aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am72
-rw-r--r--src/alloc_algo.cpp (renamed from src/gprs_rlcmac_ts_alloc.cpp)368
-rw-r--r--src/alloc_algo.h67
-rw-r--r--src/bts.cpp362
-rw-r--r--src/bts.h64
-rw-r--r--src/bts_pch_timer.c55
-rw-r--r--src/bts_pch_timer.h11
-rw-r--r--src/coding_scheme.c4
-rw-r--r--src/coding_scheme.h4
-rw-r--r--src/csn1.c4
-rw-r--r--src/csn1.h4
-rw-r--r--src/csn1_dec.c6
-rw-r--r--src/csn1_enc.c4
-rw-r--r--src/cxx_linuxlist.h4
-rw-r--r--src/decoding.cpp7
-rw-r--r--src/decoding.h6
-rw-r--r--src/encoding.cpp92
-rw-r--r--src/encoding.h17
-rw-r--r--src/ericsson-rbs/er_ccu_descr.h53
-rw-r--r--src/ericsson-rbs/er_ccu_if.c416
-rw-r--r--src/ericsson-rbs/er_ccu_if.h10
-rw-r--r--src/ericsson-rbs/er_ccu_l1_if.c543
-rw-r--r--src/gprs_bssgp_pcu.c46
-rw-r--r--src/gprs_bssgp_pcu.h4
-rw-r--r--src/gprs_bssgp_rim.c6
-rw-r--r--src/gprs_bssgp_rim.h4
-rw-r--r--src/gprs_codel.c4
-rw-r--r--src/gprs_codel.h4
-rw-r--r--src/gprs_debug.c (renamed from src/gprs_debug.cpp)23
-rw-r--r--src/gprs_debug.h13
-rw-r--r--src/gprs_ms.c860
-rw-r--r--src/gprs_ms.h101
-rw-r--r--src/gprs_ms_storage.cpp115
-rw-r--r--src/gprs_ms_storage.h44
-rw-r--r--src/gprs_pcu.c33
-rw-r--r--src/gprs_pcu.h28
-rw-r--r--src/gprs_rlcmac.c (renamed from src/gprs_rlcmac.cpp)22
-rw-r--r--src/gprs_rlcmac.h26
-rw-r--r--src/gprs_rlcmac_meas.cpp17
-rw-r--r--src/gprs_rlcmac_sched.cpp78
-rw-r--r--src/gsm_rlcmac.c4
-rw-r--r--src/gsm_rlcmac.h4
-rw-r--r--src/llc.c404
-rw-r--r--src/llc.cpp256
-rw-r--r--src/llc.h141
-rw-r--r--src/mslot_class.c7
-rw-r--r--src/mslot_class.h8
-rw-r--r--src/nacc_fsm.c222
-rw-r--r--src/nacc_fsm.h12
-rw-r--r--src/neigh_cache.c48
-rw-r--r--src/neigh_cache.h6
-rw-r--r--src/osmo-bts-litecell15/lc15_l1_if.c15
-rw-r--r--src/osmo-bts-litecell15/lc15bts.h20
-rw-r--r--src/osmo-bts-oc2g/oc2g_l1_if.c15
-rw-r--r--src/osmo-bts-oc2g/oc2gbts.h20
-rw-r--r--src/osmo-bts-sysmo/sysmo_l1_if.c15
-rw-r--r--src/pcu_l1_if.cpp342
-rw-r--r--src/pcu_l1_if.h25
-rw-r--r--src/pcu_l1_if_phy.h12
-rw-r--r--src/pcu_main.cpp56
-rw-r--r--src/pcu_utils.h24
-rw-r--r--src/pcu_vty.c104
-rw-r--r--src/pcu_vty_functions.cpp104
-rw-r--r--src/pcu_vty_functions.h4
-rw-r--r--src/pcuif_sock.c (renamed from src/osmobts_sock.c)14
-rw-r--r--src/pdch.cpp471
-rw-r--r--src/pdch.h11
-rw-r--r--src/pdch_ul_controller.c42
-rw-r--r--src/pdch_ul_controller.h7
-rw-r--r--src/rlc.cpp273
-rw-r--r--src/rlc.h404
-rw-r--r--src/rlc_window.cpp42
-rw-r--r--src/rlc_window.h71
-rw-r--r--src/rlc_window_dl.cpp182
-rw-r--r--src/rlc_window_dl.h205
-rw-r--r--src/rlc_window_ul.cpp125
-rw-r--r--src/rlc_window_ul.h180
-rw-r--r--src/sba.c4
-rw-r--r--src/sba.h4
-rw-r--r--src/tbf.cpp431
-rw-r--r--src/tbf.h78
-rw-r--r--src/tbf_dl.cpp517
-rw-r--r--src/tbf_dl.h76
-rw-r--r--src/tbf_dl_ass_fsm.c62
-rw-r--r--src/tbf_dl_ass_fsm.h23
-rw-r--r--src/tbf_dl_fsm.c529
-rw-r--r--src/tbf_fsm.c466
-rw-r--r--src/tbf_fsm.h66
-rw-r--r--src/tbf_ul.cpp237
-rw-r--r--src/tbf_ul.h53
-rw-r--r--src/tbf_ul_ack_fsm.c100
-rw-r--r--src/tbf_ul_ack_fsm.h27
-rw-r--r--src/tbf_ul_ass_fsm.c85
-rw-r--r--src/tbf_ul_ass_fsm.h23
-rw-r--r--src/tbf_ul_fsm.c365
-rw-r--r--src/wireshark_compat.h4
96 files changed, 6515 insertions, 4131 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 25fdab2a..8b8f31ed 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,7 +19,7 @@
#
AUTOMAKE_OPTIONS = subdir-objects
-AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS)
+AM_CPPFLAGS = -I$(top_srcdir)/include $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOTRAU_CFLAGS)
if ENABLE_SYSMODSP
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
@@ -33,25 +33,27 @@ if ENABLE_OC2GBTS_PHY
AM_CPPFLAGS += -DENABLE_DIRECT_PHY
endif
-AM_CXXFLAGS = -Wall -ldl -pthread
-AM_LDFLAGS = -lrt
+if ENABLE_ER_E1_CCU
+AM_CPPFLAGS += -DENABLE_DIRECT_PHY
+endif
+
+AM_CXXFLAGS = -Wall
noinst_LTLIBRARIES = libgprs.la
libgprs_la_SOURCES = \
- gprs_debug.cpp \
+ alloc_algo.cpp \
+ gprs_debug.c \
csn1.c \
csn1_dec.c \
csn1_enc.c \
gsm_rlcmac.c \
gprs_bssgp_pcu.c \
gprs_bssgp_rim.c \
- gprs_rlcmac.cpp \
+ gprs_rlcmac.c \
gprs_rlcmac_sched.cpp \
gprs_rlcmac_meas.cpp \
- gprs_rlcmac_ts_alloc.cpp \
gprs_ms.c \
- gprs_ms_storage.cpp \
gprs_pcu.c \
pcu_l1_if.cpp \
pcu_vty.c \
@@ -62,9 +64,11 @@ libgprs_la_SOURCES = \
tbf.cpp \
tbf_fsm.c \
tbf_ul.cpp \
+ tbf_ul_fsm.c \
tbf_ul_ack_fsm.c \
tbf_ul_ass_fsm.c \
tbf_dl.cpp \
+ tbf_dl_fsm.c \
tbf_dl_ass_fsm.c \
bts.cpp \
bts_pch_timer.c \
@@ -73,9 +77,12 @@ libgprs_la_SOURCES = \
encoding.cpp \
sba.c \
decoding.cpp \
- llc.cpp \
+ llc.c \
rlc.cpp \
- osmobts_sock.c \
+ rlc_window.cpp \
+ rlc_window_dl.cpp \
+ rlc_window_ul.cpp \
+ pcuif_sock.c \
gprs_codel.c \
coding_scheme.c \
egprs_rlc_compression.cpp \
@@ -87,6 +94,7 @@ bin_PROGRAMS = \
noinst_PROGRAMS =
noinst_HEADERS = \
+ alloc_algo.h \
gprs_debug.h \
csn1.h \
gsm_rlcmac.h \
@@ -94,9 +102,9 @@ noinst_HEADERS = \
gprs_bssgp_rim.h \
gprs_rlcmac.h \
gprs_ms.h \
- gprs_ms_storage.h \
gprs_pcu.h \
pcu_l1_if.h \
+ pcu_l1_if_phy.h \
pcu_vty.h \
pcu_vty_functions.h \
mslot_class.h \
@@ -116,6 +124,9 @@ noinst_HEADERS = \
encoding.h \
sba.h \
rlc.h \
+ rlc_window.h \
+ rlc_window_dl.h \
+ rlc_window_ul.h \
decoding.h \
llc.h \
pcu_utils.h \
@@ -130,13 +141,6 @@ osmo_pcu_SOURCES = pcu_main.cpp
if ENABLE_SYSMODSP
AM_CPPFLAGS += -I$(srcdir)/osmo-bts-sysmo -I$(SYSMOBTS_INCDIR)
-EXTRA_DIST = \
- osmo-bts-sysmo/sysmo_l1_if.c \
- osmo-bts-sysmo/sysmo_l1_if.h \
- osmo-bts-sysmo/sysmo_l1_hw.c \
- osmo-bts-sysmo/femtobts.c \
- osmo-bts-sysmo/femtobts.h
-
noinst_HEADERS += \
osmo-bts-sysmo/sysmo_l1_if.h \
osmo-bts-sysmo/femtobts.h
@@ -161,19 +165,13 @@ osmo_pcu_remote_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGSM_LIBS) \
- $(COMMON_LA)
+ -lrt \
+ $(NULL)
endif
if ENABLE_LC15BTS_PHY
AM_CPPFLAGS += $(LITECELL15_CFLAGS) -I$(srcdir)/osmo-bts-litecell15
-EXTRA_DIST = \
- osmo-bts-litecell15/lc15_l1_if.c \
- osmo-bts-litecell15/lc15_l1_if.h \
- osmo-bts-litecell15/lc15_l1_hw.c \
- osmo-bts-litecell15/lc15bts.c \
- osmo-bts-litecell15/lc15bts.h
-
noinst_HEADERS += \
osmo-bts-litecell15/lc15_l1_if.h \
osmo-bts-litecell15/lc15bts.h
@@ -187,13 +185,6 @@ endif
if ENABLE_OC2GBTS_PHY
AM_CPPFLAGS += -I$(OC2G_INCDIR) -I$(srcdir)/osmo-bts-oc2g
-EXTRA_DIST = \
- osmo-bts-oc2g/oc2g_l1_if.c \
- osmo-bts-oc2g/oc2g_l1_if.h \
- osmo-bts-oc2g/oc2g_l1_hw.c \
- osmo-bts-oc2g/oc2gbts.c \
- osmo-bts-oc2g/oc2gbts.h
-
noinst_HEADERS += \
osmo-bts-oc2g/oc2g_l1_if.h \
osmo-bts-oc2g/oc2gbts.h
@@ -204,12 +195,27 @@ osmo_pcu_SOURCES += \
osmo-bts-oc2g/oc2gbts.c
endif
+if ENABLE_ER_E1_CCU
+AM_CPPFLAGS += -I$(srcdir)/ericsson-rbs
+
+noinst_HEADERS += \
+ ericsson-rbs/er_ccu_if.h \
+ ericsson-rbs/er_ccu_descr.h
+
+osmo_pcu_SOURCES += \
+ ericsson-rbs/er_ccu_l1_if.c \
+ ericsson-rbs/er_ccu_if.c
+endif
+
osmo_pcu_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGSM_LIBS) \
- $(COMMON_LA)
+ $(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOTRAU_LIBS) \
+ -lrt \
+ $(NULL)
#MOSTLYCLEANFILES += testSource testDestination
diff --git a/src/gprs_rlcmac_ts_alloc.cpp b/src/alloc_algo.cpp
index 5b3b355a..11789c34 100644
--- a/src/gprs_rlcmac_ts_alloc.cpp
+++ b/src/alloc_algo.cpp
@@ -1,8 +1,9 @@
-/* gprs_rlcmac.cpp
+/* alloc_algo.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -13,10 +14,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_rlcmac.h>
@@ -33,6 +30,7 @@
extern "C" {
#include "mslot_class.h"
+#include "alloc_algo.h"
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
@@ -41,12 +39,12 @@ extern "C" {
/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
#define PDCH_IDLE_TBF_THRESH 1
-#define LOGPSL(tbf, level, fmt, args...) LOGP(DRLCMAC, level, "[%s] " fmt, \
- (tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL", ## args)
+#define LOGPSL(req, level, fmt, args...) LOGP(DRLCMAC, level, "[%s] " fmt, \
+ (req->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL", ## args)
-#define LOGPAL(tbf, kind, single, trx_n, level, fmt, args...) LOGPSL(tbf, level, \
+#define LOGPAL(req, kind, level, fmt, args...) LOGPSL(req, level, \
"algo %s <%s> (suggested TRX: %d): " fmt, \
- kind, single ? "single" : "multi", trx_n, ## args)
+ kind, req->single ? "single" : "multi", req->use_trx, ## args)
static char *set_flag_chars(char *buf, uint8_t val, char set_char, char unset_char = 0)
{
@@ -216,44 +214,21 @@ static int find_least_busy_pdch(const struct gprs_rlcmac_trx *trx, enum gprs_rlc
return min_ts;
}
-static void attach_tbf_to_pdch(struct gprs_rlcmac_pdch *pdch,
- struct gprs_rlcmac_tbf *tbf)
-{
- if (tbf->pdch[pdch->ts_no])
- tbf->pdch[pdch->ts_no]->detach_tbf(tbf);
-
- tbf->pdch[pdch->ts_no] = pdch;
- pdch->attach_tbf(tbf);
-}
-
-static void assign_uplink_tbf_usf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_ul_tbf *tbf, uint8_t tfi, int8_t usf)
-{
- tbf->m_tfi = tfi;
- tbf->m_usf[pdch->ts_no] = usf;
- attach_tbf_to_pdch(pdch, tbf);
-}
-
-static void assign_dlink_tbf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_dl_tbf *tbf, uint8_t tfi)
-{
- tbf->m_tfi = tfi;
- attach_tbf_to_pdch(pdch, tbf);
-}
-
-static int find_trx(const struct gprs_rlcmac_bts *bts, const GprsMs *ms, int8_t use_trx)
+static int find_trx(const struct alloc_resources_req *req)
{
unsigned trx_no;
unsigned ts;
/* We must use the TRX currently actively used by an MS */
- if (ms && ms_current_trx(ms))
- return ms_current_trx(ms)->trx_no;
+ if (req->ms && ms_current_trx(req->ms))
+ return ms_current_trx(req->ms)->trx_no;
- if (use_trx >= 0 && use_trx < 8)
- return use_trx;
+ if (req->use_trx >= 0 && req->use_trx < 8)
+ return req->use_trx;
/* Find the first TRX that has a PDCH with a free UL and DL TFI */
- for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no += 1) {
- const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
+ for (trx_no = 0; trx_no < ARRAY_SIZE(req->bts->trx); trx_no += 1) {
+ const struct gprs_rlcmac_trx *trx = &req->bts->trx[trx_no];
for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
if (!pdch->is_enabled())
@@ -297,33 +272,28 @@ static bool idle_pdch_avail(const struct gprs_rlcmac_bts *bts)
/*! Return free TFI
*
- * \param[in] bts Pointer to BTS struct
- * \param[in] trx Optional pointer to TRX struct
- * \param[in] ms Pointer to MS object
- * \param[in] dir DL or UL direction
- * \param[in] use_trx which TRX to use or -1 if it should be selected based on what MS uses
+ * \param[in] req Contains all the requested params
* \param[out] trx_no_ TRX number on which TFI was found
* \returns negative error code or 0 on success
*/
-static int tfi_find_free(const struct gprs_rlcmac_bts *bts, const gprs_rlcmac_trx *trx, const GprsMs *ms,
- enum gprs_rlcmac_tbf_direction dir, int8_t use_trx, uint8_t *trx_no_)
+static int tfi_find_free(const struct alloc_resources_req *req, uint8_t *trx_no_)
{
+ const struct gprs_rlcmac_trx *trx;
int tfi;
+ int8_t use_trx = req->use_trx;
uint8_t trx_no;
- if (trx) {
+ /* If MS is already doing stuff on a TRX, set use_trx to it: */
+ if ((trx = ms_current_trx(req->ms))) {
if (use_trx >= 0 && use_trx != trx->trx_no) {
LOGP(DRLCMAC, LOGL_ERROR, "- Requested incompatible TRX %d (current is %d)\n",
- use_trx, trx->trx_no);
+ req->use_trx, trx->trx_no);
return -EINVAL;
}
use_trx = trx->trx_no;
}
- if (use_trx == -1 && ms_current_trx(ms))
- use_trx = ms_current_trx(ms)->trx_no;
-
- tfi = bts_tfi_find_free(bts, dir, &trx_no, use_trx);
+ tfi = bts_tfi_find_free(req->bts, req->direction, &trx_no, use_trx);
if (tfi < 0)
return -EBUSY;
@@ -337,16 +307,13 @@ static int tfi_find_free(const struct gprs_rlcmac_bts *bts, const gprs_rlcmac_tr
*
* Assign single slot for uplink and downlink
*
- * \param[in,out] bts Pointer to BTS struct
- * \param[in,out] tbf Pointer to TBF struct
- * \param[in] single flag indicating if we should force single-slot allocation
- * \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
+ * \param[in] req Contains all the requested params
+ * \param[out] res The resolution/response for the allocation request
* \returns negative error code or 0 on success
*/
-int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
- int8_t use_trx)
+int alloc_algorithm_a(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res)
{
- struct gprs_rlcmac_pdch *pdch;
int ts = -1;
uint8_t ul_slots, dl_slots;
int trx_no;
@@ -354,28 +321,26 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf,
int usf = -1;
uint8_t mask = 0xff;
const char *mask_reason = NULL;
- struct GprsMs *ms = tbf->ms();
- gprs_rlcmac_trx *trx = ms_current_trx(ms);
+ gprs_rlcmac_trx *trx = ms_current_trx(req->ms);
+ struct gprs_rlcmac_pdch *first_common_ts = ms_first_common_ts(req->ms);
- LOGPAL(tbf, "A", single, use_trx, LOGL_DEBUG, "Alloc start\n");
+ LOGPAL(req, "A", LOGL_DEBUG, "Alloc start\n");
- trx_no = find_trx(bts, ms, use_trx);
+ trx_no = find_trx(req);
if (trx_no < 0) {
- LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
+ LOGPAL(req, "A", LOGL_NOTICE,
"failed to find a usable TRX (TFI exhausted)\n");
return trx_no;
}
if (!trx)
- trx = &bts->trx[trx_no];
-
- dl_slots = ms_reserved_dl_slots(ms);
- ul_slots = ms_reserved_ul_slots(ms);
+ trx = &req->bts->trx[trx_no];
- ts = ms_first_common_ts(ms);
+ dl_slots = ms_reserved_dl_slots(req->ms);
+ ul_slots = ms_reserved_ul_slots(req->ms);
- if (ts >= 0) {
+ if (first_common_ts) {
mask_reason = "need to reuse TS";
- mask = 1 << ts;
+ mask = 1 << first_common_ts->ts_no;
} else if (dl_slots || ul_slots) {
mask_reason = "need to use a reserved common TS";
mask = dl_slots & ul_slots;
@@ -385,43 +350,32 @@ int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf,
if (!mask)
return -EINVAL;
- ts = find_least_busy_pdch(trx, tbf->direction, mask,
+ ts = find_least_busy_pdch(trx, req->direction, mask,
compute_usage_for_algo_a,
&tfi, &usf);
- if (tbf->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
- LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
+ if (req->direction == GPRS_RLCMAC_UL_TBF && usf < 0) {
+ LOGPAL(req, "A", LOGL_NOTICE,
"failed to allocate a TS, no USF available\n");
return -EBUSY;
}
if (ts < 0) {
- LOGPAL(tbf, "A", single, use_trx, LOGL_NOTICE,
+ LOGPAL(req, "A", LOGL_NOTICE,
"failed to allocate a TS, no TFI available\n");
return -EBUSY;
}
- pdch = &trx->pdch[ts];
+ res->trx = trx;
+ res->first_common_ts = &trx->pdch[ts];
+ res->reserved_ul_slots = 1 << ts;
+ res->reserved_dl_slots = 1 << ts;
+ res->ass_slots_mask = 1 << ts;
+ res->upgrade_to_multislot = false;
+ res->tfi = tfi;
+ res->usf[ts] = usf;
- /* The allocation will be successful, so the system state and tbf/ms
- * may be modified from now on. */
- if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
- struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
- LOGPSL(tbf, LOGL_DEBUG, "Assign uplink TS=%d TFI=%d USF=%d\n", ts, tfi, usf);
- assign_uplink_tbf_usf(pdch, ul_tbf, tfi, usf);
- } else {
- struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
- LOGPSL(tbf, LOGL_DEBUG, "Assign downlink TS=%d TFI=%d\n", ts, tfi);
- assign_dlink_tbf(pdch, dl_tbf, tfi);
- }
-
- tbf->trx = trx;
- /* the only one TS is the common TS */
- tbf->first_ts = tbf->first_common_ts = ts;
- ms_set_reserved_slots(ms, trx, 1 << ts, 1 << ts);
-
- tbf->upgrade_to_multislot = false;
- bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_A);
+ bts_do_rate_ctr_inc(req->bts, CTR_TBF_ALLOC_ALGO_A);
return 0;
}
@@ -661,31 +615,31 @@ int find_multi_slots(struct gprs_rlcmac_trx *trx, uint8_t mslot_class, uint8_t *
* \param[in] slots Timeslots in use
* \param[in] reserved_slots Reserved timeslots
* \param[out] slotcount Number of TS in use
- * \param[out] avail_count Number of reserved TS
+ * \param[out] reserve_count Number of reserved TS
*/
-static void update_slot_counters(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *avail_count)
+static void count_slots(uint8_t slots, uint8_t reserved_slots, uint8_t *slotcount, uint8_t *reserve_count)
{
(*slotcount) = pcu_bitcount(slots);
- (*avail_count) = pcu_bitcount(reserved_slots);
+ (*reserve_count) = pcu_bitcount(reserved_slots);
}
/*! Return slot mask with single TS from a given UL/DL set according to TBF's direction, ts pointer is set to that TS
* number or to negative value on error
*
* \param[in] trx Pointer to TRX object
- * \param[in] tbf Pointer to TBF object
+ * \param[in] direction Direction of the TBF to allocate
* \param[in] dl_slots set of DL timeslots
* \param[in] ul_slots set of UL timeslots
* \param[in] ts corresponding TS or -1 for autoselection
* \returns slot mask with single UL or DL timeslot number if possible
*/
-static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, const gprs_rlcmac_tbf *tbf, uint8_t dl_slots, uint8_t ul_slots,
+static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction direction, uint8_t dl_slots, uint8_t ul_slots,
int ts)
{
uint8_t ret = dl_slots & ul_slots; /* Make sure to consider the first common slot only */
if (ts < 0)
- ts = find_least_busy_pdch(trx, tbf->direction, ret, compute_usage_by_num_tbfs, NULL, NULL);
+ ts = find_least_busy_pdch(trx, direction, ret, compute_usage_by_num_tbfs, NULL, NULL);
if (ts < 0)
return ffs(ret);
@@ -695,9 +649,8 @@ static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, const gprs_rlcmac_tbf *
/*! Find set of timeslots available for allocation
*
+ * \param[in] req Contains all the requested params
* \param[in] trx Pointer to TRX object
- * \param[in] tbf Pointer to TBF object
- * \param[in] single Flag to force the single TS allocation
* \param[in] ul_slots set of UL timeslots
* \param[in] dl_slots set of DL timeslots
* \param[in] reserved_ul_slots set of reserved UL timeslots
@@ -705,17 +658,17 @@ static uint8_t get_single_ts(const gprs_rlcmac_trx *trx, const gprs_rlcmac_tbf *
* \param[in] first_common_ts First TS common for both UL and DL or -1 if unknown
* \returns negative error code or selected TS on success
*/
-static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx *trx, bool single,
+static int tbf_select_slot_set(const struct alloc_resources_req *req, const gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots,
uint8_t reserved_ul_slots, uint8_t reserved_dl_slots,
int8_t first_common_ts)
{
- bool is_ul = tbf->direction == GPRS_RLCMAC_UL_TBF;
+ bool is_ul = req->direction == GPRS_RLCMAC_UL_TBF;
uint8_t sl = is_ul ? ul_slots : dl_slots;
char slot_info[9] = { 0 };
- if (single)
- sl = get_single_ts(trx, tbf, dl_slots, ul_slots, first_common_ts);
+ if (req->single)
+ sl = get_single_ts(trx, req->direction, dl_slots, ul_slots, first_common_ts);
if (!sl) {
LOGP(DRLCMAC, LOGL_NOTICE, "No %s slots available\n",
@@ -734,7 +687,7 @@ static int tbf_select_slot_set(const gprs_rlcmac_tbf *tbf, const gprs_rlcmac_trx
LOGPC(DRLCMAC, LOGL_DEBUG, "Selected %s slots: (TS=0)\"%s\"(TS=7), %s\n",
is_ul ? "UL" : "DL",
- slot_info, single ? "single" : "multi");
+ slot_info, req->single ? "single" : "multi");
return sl;
}
@@ -780,125 +733,56 @@ static int allocate_usf(const gprs_rlcmac_trx *trx, uint8_t selected_ul_slots, u
return ul_slots;
}
-/*! Update MS' reserved timeslots
- *
- * \param[in,out] trx Pointer to TRX struct
- * \param[in,out] ms_ Pointer to MS object
- * \param[in] tbf_ Pointer to TBF struct
- * \param[in] res_ul_slots Newly reserved UL slots
- * \param[in] res_dl_slots Newly reserved DL slots
- * \param[in] ul_slots available UL slots (for logging only)
- * \param[in] dl_slots available DL slots (for logging only)
- */
-static void update_ms_reserved_slots(gprs_rlcmac_trx *trx, GprsMs *ms, uint8_t res_ul_slots, uint8_t res_dl_slots,
- uint8_t ul_slots, uint8_t dl_slots)
-{
- char slot_info[9] = { 0 };
-
- if (res_ul_slots == ms_reserved_ul_slots(ms) && res_dl_slots == ms_reserved_dl_slots(ms))
- return;
-
- /* The reserved slots have changed, update the MS */
- ms_set_reserved_slots(ms, trx, res_ul_slots, res_dl_slots);
-
- ts_format(slot_info, dl_slots, ul_slots);
- LOGP(DRLCMAC, LOGL_DEBUG, "- Reserved DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
-}
-
-/*! Assign given UL timeslots to UL TBF
- *
- * \param[in,out] ul_tbf Pointer to UL TBF struct
- * \param[in,out] trx Pointer to TRX object
- * \param[in] ul_slots Set of slots to be assigned
- * \param[in] tfi selected TFI
- * \param[in] usf selected USF
- */
-static void assign_ul_tbf_slots(struct gprs_rlcmac_ul_tbf *ul_tbf, gprs_rlcmac_trx *trx, uint8_t ul_slots, int tfi,
- int *usf)
-{
- uint8_t ts;
-
- for (ts = 0; ts < 8; ts++) {
- if (!(ul_slots & (1 << ts)))
- continue;
-
- OSMO_ASSERT(usf[ts] >= 0);
-
- LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS %u\n", ts);
- assign_uplink_tbf_usf(&trx->pdch[ts], ul_tbf, tfi, usf[ts]);
- }
-}
-
-/*! Assign given DL timeslots to DL TBF
- *
- * \param[in,out] dl_tbf Pointer to DL TBF struct
- * \param[in,out] trx Pointer to TRX object
- * \param[in] ul_slots Set of slots to be assigned
- * \param[in] tfi selected TFI
- */
-static void assign_dl_tbf_slots(struct gprs_rlcmac_dl_tbf *dl_tbf, gprs_rlcmac_trx *trx, uint8_t dl_slots, int tfi)
-{
- uint8_t ts;
-
- for (ts = 0; ts < 8; ts++) {
- if (!(dl_slots & (1 << ts)))
- continue;
-
- LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS %u\n", ts);
- assign_dlink_tbf(&trx->pdch[ts], dl_tbf, tfi);
- }
-}
-
/*! Slot Allocation: Algorithm B
*
* Assign as many downlink slots as possible.
* Assign one uplink slot. (With free USF)
*
- * \param[in,out] bts Pointer to BTS struct
- * \param[in,out] tbf Pointer to TBF struct
- * \param[in] single flag indicating if we should force single-slot allocation
- * \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
+ * \param[in] req Contains all the requested params
+ * \param[out] res The resolution/response for the allocation request
* \returns negative error code or 0 on success
*/
-int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
- int8_t use_trx)
+int alloc_algorithm_b(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res)
{
uint8_t dl_slots;
uint8_t ul_slots;
uint8_t reserved_dl_slots;
uint8_t reserved_ul_slots;
- int8_t first_common_ts;
+ int8_t first_common_tn;
uint8_t slotcount = 0;
- uint8_t avail_count = 0, trx_no;
- int first_ts = -1;
- int usf[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
+ uint8_t reserve_count = 0, trx_no;
+ int first_ts;
int rc;
int tfi;
- struct GprsMs *ms = tbf->ms();
+ unsigned int i;
gprs_rlcmac_trx *trx;
+ char slot_info[9] = { 0 };
+ struct gprs_rlcmac_pdch *first_common_ts = ms_first_common_ts(req->ms);
- LOGPAL(tbf, "B", single, use_trx, LOGL_DEBUG, "Alloc start\n");
+ LOGPAL(req, "B", LOGL_DEBUG, "Alloc start\n");
+
+ for (i = 0; i < ARRAY_SIZE(res->usf); i++)
+ res->usf[i] = -1;
/* Step 1: Get current state from the MS object */
- reserved_dl_slots = ms_reserved_dl_slots(ms);
- reserved_ul_slots = ms_reserved_ul_slots(ms);
- first_common_ts = ms_first_common_ts(ms);
- trx = ms_current_trx(ms);
+ reserved_dl_slots = ms_reserved_dl_slots(req->ms);
+ reserved_ul_slots = ms_reserved_ul_slots(req->ms);
+ first_common_tn = first_common_ts ? first_common_ts->ts_no : -1;
/* Step 2a: Find usable TRX and TFI */
- tfi = tfi_find_free(bts, trx, ms, tbf->direction, use_trx, &trx_no);
+ tfi = tfi_find_free(req, &trx_no);
if (tfi < 0) {
- LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "failed to allocate a TFI\n");
+ LOGPAL(req, "B", LOGL_NOTICE, "failed to allocate a TFI\n");
return tfi;
}
/* Step 2b: Reserve slots on the TRX for the MS */
- if (!trx)
- trx = &bts->trx[trx_no];
+ trx = &req->bts->trx[trx_no];
if (!reserved_dl_slots || !reserved_ul_slots) {
- rc = find_multi_slots(trx, ms_ms_class(ms), &reserved_ul_slots, &reserved_dl_slots);
+ rc = find_multi_slots(trx, ms_ms_class(req->ms), &reserved_ul_slots, &reserved_dl_slots);
if (rc < 0)
return rc;
}
@@ -906,64 +790,62 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf,
ul_slots = reserved_ul_slots;
/* Step 3a: Derive the slot set for the current TBF */
- rc = tbf_select_slot_set(tbf, trx, single, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
- first_common_ts);
+ rc = tbf_select_slot_set(req, trx, ul_slots, dl_slots, reserved_ul_slots, reserved_dl_slots,
+ first_common_tn);
if (rc < 0)
return -EINVAL;
/* Step 3b: Derive the slot set for a given direction */
- if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
+ if (req->direction == GPRS_RLCMAC_DL_TBF) {
dl_slots = rc;
- update_slot_counters(dl_slots, reserved_dl_slots, &slotcount, &avail_count);
+ count_slots(dl_slots, reserved_dl_slots, &slotcount, &reserve_count);
} else {
- rc = allocate_usf(trx, rc, dl_slots, usf);
+ rc = allocate_usf(trx, rc, dl_slots, &res->usf[0]);
if (rc < 0)
return rc;
ul_slots = rc;
reserved_ul_slots = ul_slots;
- update_slot_counters(ul_slots, reserved_ul_slots, &slotcount, &avail_count);
+ count_slots(ul_slots, reserved_ul_slots, &slotcount, &reserve_count);
}
first_ts = ffs(rc) - 1;
- first_common_ts = ffs(dl_slots & ul_slots) - 1;
-
- if (first_common_ts < 0) {
- LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first common slot unavailable\n");
+ if (first_ts < 0) {
+ LOGPAL(req, "B", LOGL_NOTICE, "first slot unavailable\n");
return -EINVAL;
}
- if (first_ts < 0) {
- LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "first slot unavailable\n");
+ first_common_tn = ffs(dl_slots & ul_slots) - 1;
+ if (first_common_tn < 0) {
+ LOGPAL(req, "B", LOGL_NOTICE, "first common slot unavailable\n");
return -EINVAL;
}
-
- if (single && slotcount) {
- tbf->upgrade_to_multislot = (avail_count > slotcount);
- LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using single slot at TS %d\n", first_ts);
+ first_common_ts = &trx->pdch[first_common_tn];
+
+ res->trx = trx;
+ res->first_common_ts = first_common_ts;
+ res->reserved_ul_slots = reserved_ul_slots;
+ res->reserved_dl_slots = reserved_dl_slots;
+ res->tfi = tfi;
+ /* res->usf is already filled in above */
+ if (req->single && slotcount) {
+ res->upgrade_to_multislot = (reserve_count > slotcount);
+ LOGPAL(req, "B", LOGL_INFO, "using single slot at TS %d\n", first_ts);
} else {
- tbf->upgrade_to_multislot = false;
- LOGPAL(tbf, "B", single, use_trx, LOGL_INFO, "using %d slots\n", slotcount);
+ res->upgrade_to_multislot = false;
+ LOGPAL(req, "B", LOGL_INFO, "using %d slots\n", slotcount);
}
- /* The allocation will be successful, so the system state and tbf/ms
- * may be modified from now on. */
-
- /* Step 4: Update MS and TBF and really allocate the resources */
-
- update_ms_reserved_slots(trx, ms, reserved_ul_slots, reserved_dl_slots, ul_slots, dl_slots);
-
- tbf->trx = trx;
- tbf->first_common_ts = first_common_ts;
- tbf->first_ts = first_ts;
+ ts_format(slot_info, dl_slots, ul_slots);
+ LOGP(DRLCMAC, LOGL_DEBUG, "- Available DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
- if (tbf->direction == GPRS_RLCMAC_DL_TBF)
- assign_dl_tbf_slots(as_dl_tbf(tbf), trx, dl_slots, tfi);
+ if (req->direction == GPRS_RLCMAC_DL_TBF)
+ res->ass_slots_mask = dl_slots;
else
- assign_ul_tbf_slots(as_ul_tbf(tbf), trx, ul_slots, tfi, usf);
+ res->ass_slots_mask = ul_slots;
- bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_B);
+ bts_do_rate_ctr_inc(req->bts, CTR_TBF_ALLOC_ALGO_B);
return 0;
}
@@ -976,35 +858,33 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf,
* The goal is to support as many MS and TBF as possible. On low usage, the
* goal is to provide the highest possible bandwidth per MS.
*
- * \param[in,out] bts Pointer to BTS struct
- * \param[in,out] tbf Pointer to TBF struct
- * \param[in] single flag indicating if we should force single-slot allocation
- * \param[in] use_trx which TRX to use or -1 if it should be selected during allocation
+ * \param[in] req Contains all the requested params
+ * \param[out] res The resolution/response for the allocation request
* \returns negative error code or 0 on success
*/
-int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
- int8_t use_trx)
+int alloc_algorithm_dynamic(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res)
{
int rc;
/* Reset load_is_high if there is at least one idle PDCH */
- if (bts->multislot_disabled) {
- bts->multislot_disabled = !idle_pdch_avail(bts);
- if (!bts->multislot_disabled)
+ if (req->bts->multislot_disabled) {
+ req->bts->multislot_disabled = !idle_pdch_avail(req->bts);
+ if (!req->bts->multislot_disabled)
LOGP(DRLCMAC, LOGL_DEBUG, "Enabling algorithm B\n");
}
- if (!bts->multislot_disabled) {
- rc = alloc_algorithm_b(bts, tbf, single, use_trx);
+ if (!req->bts->multislot_disabled) {
+ rc = alloc_algorithm_b(req, res);
if (rc >= 0)
return rc;
- if (!bts->multislot_disabled)
+ if (!req->bts->multislot_disabled)
LOGP(DRLCMAC, LOGL_DEBUG, "Disabling algorithm B\n");
- bts->multislot_disabled = 1;
+ req->bts->multislot_disabled = 1;
}
- return alloc_algorithm_a(bts, tbf, single, use_trx);
+ return alloc_algorithm_a(req, res);
}
int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class)
diff --git a/src/alloc_algo.h b/src/alloc_algo.h
new file mode 100644
index 00000000..f863abd7
--- /dev/null
+++ b/src/alloc_algo.h
@@ -0,0 +1,67 @@
+/* alloc_algo.h
+ *
+ * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "tbf.h"
+
+struct gprs_rlcmac_bts;
+struct GprsMs;
+struct gprs_rlcmac_tbf;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct alloc_resources_req {
+ /* BTS where to allocate resources */
+ struct gprs_rlcmac_bts *bts;
+ /* MS for which to allocate resources */
+ const struct GprsMs *ms;
+ /* Direction of the TBF for which we are allocating resources */
+ enum gprs_rlcmac_tbf_direction direction;
+ /* Whether to allocate only a single (1) TS */
+ bool single;
+ /* Whether to allocate on a specific TRX (>=0) or not (-1) */
+ int8_t use_trx;
+};
+
+struct alloc_resources_res {
+ struct gprs_rlcmac_trx *trx;
+ struct gprs_rlcmac_pdch *first_common_ts;
+ uint8_t reserved_ul_slots;
+ uint8_t reserved_dl_slots;
+ uint8_t ass_slots_mask;
+ bool upgrade_to_multislot;
+ uint8_t tfi;
+ int usf[8];
+};
+
+int alloc_algorithm_a(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res);
+
+int alloc_algorithm_b(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res);
+
+int alloc_algorithm_dynamic(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res);
+int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/bts.cpp b/src/bts.cpp
index 50df92e2..bad06a52 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -4,8 +4,8 @@
* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -13,7 +13,7 @@
* 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
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -30,7 +30,6 @@
#include <gprs_debug.h>
#include <cxx_linuxlist.h>
#include <pdch.h>
-#include <gprs_ms_storage.h>
#include <sba.h>
#include <bts_pch_timer.h>
@@ -40,6 +39,7 @@ extern "C" {
#include <osmocom/core/stats.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm_utils.h>
+ #include <osmocom/gsm/gsm0502.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/application.h>
@@ -52,7 +52,6 @@ extern "C" {
#include <errno.h>
#include <string.h>
-#define RFN_MODULUS 42432
#define RFN_THRESHOLD RFN_MODULUS / 2
extern void *tall_pcu_ctx;
@@ -78,11 +77,26 @@ void bts_trx_free_all_tbf(struct gprs_rlcmac_trx *trx)
static struct osmo_tdef T_defs_bts[] = {
{ .T=3142, .default_val=20, .unit=OSMO_TDEF_S, .desc="Wait Indication used in Imm Ass Reject during TBF Establishment (CCCH)", .val=0, .min_val = 0, .max_val = 255 }, /* TS 44.018 10.5.2.43, TS 44.060 7.1.3.2.1 (T3172) */
+ { .T=3168, .default_val=4000, .unit=OSMO_TDEF_MS, .desc="Time MS waits for PACKET UPLINK ACK when establishing a UL TBF", .val=0 },
{ .T=3169, .default_val=5, .unit=OSMO_TDEF_S, .desc="Reuse of USF and TFI(s) after the MS uplink TBF assignment is invalid", .val=0 },
- { .T=3172, .default_val=5000,.unit=OSMO_TDEF_MS, .desc="Wait Indication used in Imm Ass Reject during TBF Establishment (PACCH)", .val=0, .min_val = 0, .max_val = 255000 }, /* TS 44.060 7.1.3.2.1 */
{ .T=3191, .default_val=5, .unit=OSMO_TDEF_S, .desc="Reuse of TFI(s) after sending (1) last RLC Data Block on TBF(s), or (2) PACKET TBF RELEASE for an MBMS radio bearer", .val=0 },
- { .T=3193, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Reuse of TFI(s) after reception of final PACKET DOWNLINK ACK/NACK from MS for TBF", .val=0 },
+ { .T=3192, .default_val=1500, .unit=OSMO_TDEF_MS, .desc="Time MS stays monitoring the PDCH after transmitting DL ACK/NACK for last DL block (FBI=1). Configured at the BSC (SI13).", .val=0 },
+ { .T=3193, .default_val=1600, .unit=OSMO_TDEF_MS, .desc="Reuse of TFI(s) after reception of final PACKET DOWNLINK ACK/NACK from MS for TBF", .val=0 },
{ .T=3195, .default_val=5, .unit=OSMO_TDEF_S, .desc="Reuse of TFI(s) upon no response from the MS (radio failure or cell change) for TBF/MBMS radio bearer", .val=0 },
+ { .T = -16, .default_val = 1000, .unit = OSMO_TDEF_MS,
+ .desc = "Granularity for *:all_allocated rate counters: amount of milliseconds that one counter increment"
+ " represents. See also X17, X18" },
+ { .T = -17, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "Rounding threshold for *:all_allocated rate counters: round up to the next counter increment"
+ " after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior:"
+ " round up after half of a granularity period. If set to 1, behave like ceil(): already"
+ " increment the counter immediately when all channels are allocated. If set >= X16, behave like"
+ " floor(): only increment after a full X16 period of all channels being occupied."
+ " See also X16, X18" },
+ { .T = -18, .default_val = 60000, .unit = OSMO_TDEF_MS,
+ .desc = "Forget-sum period for *:all_allocated rate counters:"
+ " after this amount of idle time, forget internally cumulated time remainders. Zero to always"
+ " keep remainders. See also X16, X17." },
{ .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */
};
@@ -92,6 +106,7 @@ static struct osmo_tdef T_defs_bts[] = {
* the code below.
*/
static const struct rate_ctr_desc bts_ctr_description[] = {
+ { "pdch:all_allocated", "Cumulative counter of seconds where all enabled PDCH resources were allocated"},
{ "tbf:dl:alloc", "TBF DL Allocated "},
{ "tbf:dl:freed", "TBF DL Freed "},
{ "tbf:dl:aborted", "TBF DL Aborted "},
@@ -134,14 +149,21 @@ static const struct rate_ctr_desc bts_ctr_description[] = {
{ "llc:dl_bytes", "RLC encapsulated PDUs"},
{ "llc:ul_bytes", "full PDUs received "},
{ "pch:requests", "PCH requests sent "},
+ { "pch:requests:already", "PCH requests on subscriber already being paged"},
{ "pch:requests:timeout", "PCH requests timeout "},
{ "rach:requests", "RACH requests received"},
- { "11bit_rach:requests", "11BIT_RACH requests received"},
+ { "rach:requests:11bit", "11BIT_RACH requests received"},
+ { "rach:requests:one_phase", "One phase packet access with request for single TS UL"}, /* TS 52.402 B.2.1.49 */
+ { "rach:requests:two_phase", "Single block packet request for two phase packet access"}, /* TS 52.402 B.2.1.49 */
+ { "rach:requests:unexpected", "RACH Request with unexpected content received"},
{ "spb:uplink_first_segment", "First seg of UL SPB "},
{ "spb:uplink_second_segment", "Second seg of UL SPB "},
{ "spb:downlink_first_segment", "First seg of DL SPB "},
{ "spb:downlink_second_segment","Second seg of DL SPB "},
{ "immediate:assignment_UL", "Immediate Assign UL "},
+ { "immediate:assignment_ul:one_phase", "Immediate Assign UL (one phase packet access)"}, /* TS 52.402 B.2.1.50 */
+ { "immediate:assignment_ul:two_phase", "Immediate Assign UL (two phase packet access)"}, /* TS 52.402 B.2.1.50 */
+ { "immediate:assignment_ul:contention_resolution_success", "First RLC Block (PDU) on the PDTCH from the MS received"}, /* TS 52.402 B.2.1.51 */
{ "immediate:assignment_rej", "Immediate Assign Rej "},
{ "immediate:assignment_DL", "Immediate Assign DL "},
{ "channel:request_description","Channel Request Desc "},
@@ -219,10 +241,13 @@ static const struct osmo_stat_item_group_desc bts_statg_desc = {
static int bts_talloc_destructor(struct gprs_rlcmac_bts* bts)
{
- /* this can cause counter updates and must not be left to the
- * m_ms_store's destructor */
- bts->ms_store->cleanup();
- delete bts->ms_store;
+ struct GprsMs *ms;
+ while ((ms = llist_first_entry_or_null(&bts->ms_list, struct GprsMs, list)))
+ talloc_free(ms);
+
+ gprs_bssgp_destroy(bts);
+
+ osmo_time_cc_cleanup(&bts->all_allocated_pdch);
if (bts->ratectrs) {
rate_ctr_group_free(bts->ratectrs);
@@ -256,8 +281,6 @@ struct gprs_rlcmac_bts* bts_alloc(struct gprs_pcu *pcu, uint8_t bts_nr)
bts->pcu = pcu;
bts->nr = bts_nr;
- bts->ms_store = new GprsMsStorage(bts);
-
bts->cur_fn = FN_UNSET;
bts->cur_blk_fn = -1;
bts->max_cs_dl = MAX_GPRS_CS;
@@ -293,22 +316,37 @@ struct gprs_rlcmac_bts* bts_alloc(struct gprs_pcu *pcu, uint8_t bts_nr)
bts->statg = osmo_stat_item_group_alloc(tall_pcu_ctx, &bts_statg_desc, 0);
OSMO_ASSERT(bts->statg);
+ osmo_time_cc_init(&bts->all_allocated_pdch);
+ struct osmo_time_cc_cfg *cc_cfg = &bts->all_allocated_pdch.cfg;
+ cc_cfg->gran_usec = 1*1000000,
+ cc_cfg->forget_sum_usec = 60*1000000,
+ cc_cfg->rate_ctr = rate_ctr_group_get_ctr(bts->ratectrs, CTR_PDCH_ALL_ALLOCATED),
+ cc_cfg->T_gran = -16,
+ cc_cfg->T_round_threshold = -17,
+ cc_cfg->T_forget_sum = -18,
+ cc_cfg->T_defs = T_defs_bts,
+
llist_add_tail(&bts->list, &pcu->bts_list);
INIT_LLIST_HEAD(&bts->pch_timer);
+ INIT_LLIST_HEAD(&bts->ms_list);
return bts;
}
void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, uint32_t fn)
{
+ /* See also 3GPP TS 45.002, section 4.3.3 */
+ OSMO_ASSERT(fn < GSM_TDMA_HYPERFRAME);
+
/* The UL frame numbers lag 3 behind the DL frames and the data
* indication is only sent after all 4 frames of the block have been
* received. Sometimes there is an idle frame between the end of one
* and start of another frame (every 3 blocks). */
if (fn != bts->cur_fn && bts->cur_fn != FN_UNSET && fn != fn_next_block(bts->cur_fn)) {
LOGP(DRLCMAC, LOGL_NOTICE,
- "Detected FN jump! %u -> %u\n", bts->cur_fn, fn);
+ "Detected FN jump! %u -> %u (expected %u, delta %u)\n",
+ bts->cur_fn, fn, fn_next_block(bts->cur_fn), GSM_TDMA_FN_DIFF(bts->cur_fn, fn));
}
bts->cur_fn = fn;
}
@@ -387,7 +425,7 @@ int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req,
struct gprs_rlcmac_tbf *tbf;
struct llist_head *tmp;
const struct osmo_mobile_identity *mi;
- uint8_t slot_mask[8];
+ uint8_t slot_mask[ARRAY_SIZE(bts->trx)];
int8_t first_ts; /* must be signed */
/* First, build the MI used to page on PDCH from available subscriber info: */
@@ -420,11 +458,11 @@ int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req,
* Don't mark, if TBF uses a different slot that is already marked. */
memset(slot_mask, 0, sizeof(slot_mask));
- llist_for_each(tmp, bts_ms_store(bts)->ms_list()) {
+ llist_for_each(tmp, &bts->ms_list) {
ms = llist_entry(tmp, typeof(*ms), list);
struct gprs_rlcmac_tbf *tbfs[] = { ms->ul_tbf, ms->dl_tbf };
for (l = 0; l < ARRAY_SIZE(tbfs); l++) {
- tbf = (struct gprs_rlcmac_tbf *)tbfs[l];
+ tbf = tbfs[l];
if (!tbf)
continue;
first_ts = -1;
@@ -495,7 +533,7 @@ void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
}
bts_send_gsmtap_meas(bts, categ, true, rip->trx_nr, rip->ts_nr, channel,
- bts_rfn_to_fn(bts, rip->rfn), ra_buf,
+ rip->fn, ra_buf,
rip->is_11bit ? 2 : 1, &meas);
}
@@ -595,7 +633,7 @@ int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_di
unsigned int best_cnt = 0;
uint8_t best_first_tfi = 0;
- if (use_trx >= 0 && use_trx < 8)
+ if (use_trx >= 0 && use_trx < (int8_t)ARRAY_SIZE(bts->trx))
trx_from = trx_to = use_trx;
else {
trx_from = 0;
@@ -604,7 +642,7 @@ int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_di
/* find a TFI that is unused on all PDCH */
for (trx = trx_from; trx <= trx_to; trx++) {
- uint8_t tmp_first_tfi;
+ uint8_t tmp_first_tfi = 0xff; /* make gcc happy */
unsigned int tmp_cnt;
tmp_cnt = trx_count_free_tfi(&bts->trx[trx], dir, &tmp_first_tfi);
if (tmp_cnt > best_cnt) {
@@ -628,100 +666,125 @@ int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_di
return best_first_tfi;
}
-int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn)
+static int tlli_from_imm_ass(uint32_t *tlli, const uint8_t *data)
{
- struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
+ const struct gsm48_imm_ass *imm_ass = (struct gsm48_imm_ass *)data;
uint8_t plen;
- uint32_t tlli;
- GprsMs *ms;
- /* move to IA Rest Octets */
- plen = data[0] >> 2;
+ /* Move to IA Rest Octets: TS 44.018 9.1.18 "The L2 pseudo length of
+ * this message is the sum of lengths of all information elements
+ * present in the message except the IA Rest Octets and L2 Pseudo Length
+ * information elements." */
+ /* TS 44.018 10.5.2.19 l2_plen byte lowest 2 bits are '01'B */
+ plen = imm_ass->l2_plen >> 2;
data += 1 + plen;
if ((*data & 0xf0) != 0xd0) {
- LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but rest "
+ LOGP(DTBFDL, LOGL_ERROR, "Got IMM.ASS confirm, but rest "
"octets do not start with bit sequence 'HH01' "
"(Packet Downlink Assignment)\n");
return -EINVAL;
}
/* get TLLI from downlink assignment */
- tlli = (uint32_t)((*data++) & 0xf) << 28;
- tlli |= (*data++) << 20;
- tlli |= (*data++) << 12;
- tlli |= (*data++) << 4;
- tlli |= (*data++) >> 4;
-
- ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
- if (ms)
- dl_tbf = ms_dl_tbf(ms);
+ *tlli = (uint32_t)((*data++) & 0xf) << 28;
+ *tlli |= (*data++) << 20;
+ *tlli |= (*data++) << 12;
+ *tlli |= (*data++) << 4;
+ *tlli |= (*data++) >> 4;
+
+ return 0;
+}
+
+int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t tlli)
+{
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ GprsMs *ms;
+ int rc;
+
+ /* NOTE: A confirmation for a downlink IMMEDIATE ASSIGNMENT can be received using two different methods.
+ * One way is to send the whole IMMEDIATE ASSIGNMENT back to the PCU and the TLLI, which we use as
+ * reference is extracted from the rest octets of this message. Alternatively the TLLI may be sent as
+ * confirmation directly. */
+
+ /* Extract TLLI from the presented IMMEDIATE ASSIGNMENT
+ * (if present and only when TLLI that is supplied as function parameter is valid.) */
+ if (data && tlli == GSM_RESERVED_TMSI) {
+ rc = tlli_from_imm_ass(&tlli, data);
+ if (rc != 0)
+ return -EINVAL;
+ }
+
+ /* Make sure TLLI is valid */
+ if (tlli == GSM_RESERVED_TMSI) {
+ LOGP(DTBFDL, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI is invalid!\n");
+ return -EINVAL;
+ }
+
+ /* Find related TBF and send confirmation signal to FSM */
+ ms = bts_get_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
+ if (!ms) {
+ LOGP(DTBFDL, LOGL_ERROR, "Got IMM.ASS confirm for unknown MS with TLLI=%08x\n", tlli);
+ return -EINVAL;
+ }
+ dl_tbf = ms_dl_tbf(ms);
if (!dl_tbf) {
- LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x "
- "does not exit\n", tlli);
+ LOGPMS(ms, DTBFDL, LOGL_ERROR, "Got IMM.ASS confirm, but MS has no DL TBF!\n");
return -EINVAL;
}
- LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli);
- osmo_fsm_inst_dispatch(dl_tbf->state_fsm.fi, TBF_EV_ASSIGN_PCUIF_CNF, NULL);
+ LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Got IMM.ASS confirm\n");
+ osmo_fsm_inst_dispatch(dl_tbf->state_fi, TBF_EV_ASSIGN_PCUIF_CNF, NULL);
return 0;
}
/* Determine the full frame number from a relative frame number */
-uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn)
+uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, uint32_t rfn)
{
- int32_t m_cur_rfn;
- int32_t fn;
- int32_t fn_rounded;
-
- /* double-check that relative FN is not negative and fits into int32_t */
- OSMO_ASSERT(rfn < GSM_MAX_FN);
- OSMO_ASSERT(rfn >= 0);
-
- /* Note: If a BTS is sending in a rach request it will be fully aware
- * of the frame number. If the PCU is used in a BSC-co-located setup.
- * The BSC will forward the incoming RACH request. The RACH request
- * only contains the relative frame number (Fn % 42432) in its request
- * reference. This PCU implementation has to fit both scenarios, so
- * we need to assume that Fn is a relative frame number. */
+ uint32_t m_cur_fn, m_cur_rfn;
+ uint32_t fn_rounded;
/* Ensure that all following calculations are performed with the
* relative frame number */
- if (rfn >= RFN_MODULUS)
+ OSMO_ASSERT(rfn < RFN_MODULUS);
+
+ m_cur_fn = bts_current_frame_number(bts);
+ if (OSMO_UNLIKELY(m_cur_fn == FN_UNSET)) {
+ LOGP(DRLCMAC, LOGL_ERROR, "Unable to calculate full FN from RFN %u: Current FN not known!\n",
+ rfn);
return rfn;
+ }
/* Compute an internal relative frame number from the full internal
frame number */
- m_cur_rfn = bts->cur_fn % RFN_MODULUS;
+ m_cur_rfn = fn2rfn(m_cur_fn);
/* Compute a "rounded" version of the internal frame number, which
* exactly fits in the RFN_MODULUS raster */
- fn_rounded = bts->cur_fn - m_cur_rfn;
+ fn_rounded = GSM_TDMA_FN_SUB(m_cur_fn, m_cur_rfn);
/* If the delta between the internal and the external relative frame
* number exceeds a certain limit, we need to assume that the incoming
* rach request belongs to a the previous rfn period. To correct this,
* we roll back the rounded frame number by one RFN_MODULUS */
- if (abs(rfn - m_cur_rfn) > RFN_THRESHOLD) {
+ if (GSM_TDMA_FN_DIFF(rfn, m_cur_rfn) > RFN_THRESHOLD) {
LOGP(DRLCMAC, LOGL_DEBUG,
"Race condition between rfn (%u) and m_cur_fn (%u) detected: rfn belongs to the previous modulus %u cycle, wrapping...\n",
- rfn, bts->cur_fn, RFN_MODULUS);
+ rfn, m_cur_fn, RFN_MODULUS);
if (fn_rounded < RFN_MODULUS) {
LOGP(DRLCMAC, LOGL_DEBUG,
"Cornercase detected: wrapping crosses %u border\n",
GSM_MAX_FN);
- fn_rounded = GSM_MAX_FN - (RFN_MODULUS - fn_rounded);
+ fn_rounded = GSM_TDMA_FN_SUB(GSM_MAX_FN, (GSM_TDMA_FN_SUB(RFN_MODULUS, fn_rounded)));
}
else
- fn_rounded -= RFN_MODULUS;
+ fn_rounded = GSM_TDMA_FN_SUB(fn_rounded, RFN_MODULUS);
}
/* The real frame number is the sum of the rounded frame number and the
* relative framenumber computed via RACH */
- fn = fn_rounded + rfn;
-
- return fn;
+ return GSM_TDMA_FN_SUM(fn_rounded, rfn);
}
/* 3GPP TS 44.060:
@@ -880,29 +943,42 @@ int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
bts_do_rate_ctr_inc(bts, CTR_RACH_REQUESTS);
if (rip->is_11bit)
- bts_do_rate_ctr_inc(bts, CTR_11BIT_RACH_REQUESTS);
+ bts_do_rate_ctr_inc(bts, CTR_RACH_REQUESTS_11BIT);
- /* Determine full frame number */
- uint32_t Fn = bts_rfn_to_fn(bts, rip->rfn);
uint8_t ta = qta2ta(rip->qta);
bts_send_gsmtap_rach(bts, PCU_GSMTAP_C_UL_RACH, GSMTAP_CHANNEL_RACH, rip);
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests Uplink resource on CCCH/RACH: "
"ra=0x%02x (%d bit) Fn=%u qta=%d\n", rip->ra,
- rip->is_11bit ? 11 : 8, Fn, rip->qta);
+ rip->is_11bit ? 11 : 8, rip->fn, rip->qta);
/* Parse [EGPRS Packet] Channel Request from RACH.ind */
rc = parse_rach_ind(rip, &chan_req);
- if (rc) /* Send RR Immediate Assignment Reject */
+ if (rc) {
+ bts_do_rate_ctr_inc(bts, CTR_RACH_REQUESTS_UNEXPECTED);
+ /* Send RR Immediate Assignment Reject */
goto send_imm_ass_rej;
+ }
- if (chan_req.single_block)
- LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block allocation\n");
- else if (bts->pcu->vty.force_two_phase) {
- LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block allocation, "
- "but we force two phase access\n");
- chan_req.single_block = true;
+ if (chan_req.single_block) {
+ bts_do_rate_ctr_inc(bts, CTR_RACH_REQUESTS_TWO_PHASE);
+ LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block allocation "
+ "(two phase packet access)\n");
+ } else {
+ bts_do_rate_ctr_inc(bts, CTR_RACH_REQUESTS_ONE_PHASE);
+ LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single TS uplink transmission "
+ "(one phase packet access)\n");
+ if (bts->pcu->vty.force_two_phase) {
+ /* 3GPP TS 44.018 3.5.2.1.3.1: "If the establishment cause in the
+ * CHANNEL REQUEST message indicates a request for one phase packet access,
+ * the network may grant either a one phase packet access or a single block
+ * packet access for the mobile station. If a single block packet access is
+ * granted, it forces the mobile station to perform a two phase packet access."
+ */
+ LOGP(DRLCMAC, LOGL_DEBUG, "Forcing two phase access\n");
+ chan_req.single_block = true;
+ }
}
/* TODO: handle Radio Priority (see 3GPP TS 44.060, table 11.2.5a.5) */
@@ -925,17 +1001,25 @@ int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
sb_fn = sba->fn;
LOGP(DRLCMAC, LOGL_DEBUG, "Allocated a single block at "
"SBFn=%u TRX=%u TS=%u\n", sb_fn, pdch->trx->trx_no, pdch->ts_no);
+ bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_UL_TBF_TWO_PHASE);
} else {
- GprsMs *ms = bts_alloc_ms(bts, 0, chan_req.egprs_mslot_class);
- tbf = tbf_alloc_ul_ccch(bts, ms);
+ GprsMs *ms = ms_alloc(bts, __func__);
+ ms_set_egprs_ms_class(ms, chan_req.egprs_mslot_class);
+ tbf = ms_new_ul_tbf_assigned_agch(ms);
+ /* Here either tbf was created and it holds a ref to MS, or tbf
+ * creation failed and MS will end up without references and being
+ * freed: */
+ ms_unref(ms, __func__);
if (!tbf) {
/* Send RR Immediate Assignment Reject */
rc = -EBUSY;
goto send_imm_ass_rej;
}
tbf->set_ta(ta);
- pdch = &tbf->trx->pdch[tbf->first_ts];
+ /* Only single TS can be allocated through AGCH, hence first TS is the only one: */
+ pdch = tbf_get_first_ts(tbf);
usf = tbf->m_usf[pdch->ts_no];
+ bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_UL_TBF_ONE_PHASE);
}
trx = pdch->trx;
@@ -944,12 +1028,12 @@ int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
trx->trx_no, trx->arfcn & ~ARFCN_FLAG_MASK,
pdch->ts_no, ta, pdch->tsc, tbf ? tbf->tfi() : -1, usf);
plen = Encoding::write_immediate_assignment(pdch, tbf, bv,
- false, rip->ra, Fn, ta, usf, false, sb_fn,
+ false, rip->ra, rip->rfn, ta, usf, false, fn2rfn(sb_fn),
bts_get_ms_pwr_alpha(bts), bts->pcu->vty.gamma, -1,
rip->burst_type);
bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_UL_TBF);
if (plen >= 0) {
- pcu_l1if_tx_agch(bts, bv, plen);
+ pcu_l1if_tx_agch2(bts, bv, plen, false, GSM_RESERVED_TMSI);
rc = 0;
} else {
rc = plen;
@@ -960,11 +1044,11 @@ int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
send_imm_ass_rej:
LOGP(DRLCMAC, LOGL_DEBUG, "Tx Immediate Assignment Reject on AGCH\n");
plen = Encoding::write_immediate_assignment_reject(
- bv, rip->ra, Fn, rip->burst_type,
+ bv, rip->ra, rip->rfn, rip->burst_type,
(uint8_t)osmo_tdef_get(bts->T_defs_bts, 3142, OSMO_TDEF_S, -1));
bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_REJ);
if (plen >= 0)
- pcu_l1if_tx_agch(bts, bv, plen);
+ pcu_l1if_tx_agch2(bts, bv, plen, false, GSM_RESERVED_TMSI);
bitvec_free(bv);
/* rc was already properly set before goto */
return rc;
@@ -980,7 +1064,7 @@ static uint32_t ptcch_slot_map[PTCCH_TAI_NUM] = {
int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
{
- uint32_t fn416 = bts_rfn_to_fn(bts, rip->rfn) % 416;
+ uint16_t fn416 = rip->rfn % 416;
struct gprs_rlcmac_pdch *pdch;
uint8_t ss;
@@ -1019,28 +1103,47 @@ int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params
return 0;
}
-void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, uint16_t pgroup)
+void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, const struct gprs_rlcmac_dl_tbf *tbf)
{
- uint8_t trx_no = tbf->trx->trx_no;
- uint8_t ts_no = tbf->first_ts;
int plen;
- LOGPTBF(tbf, LOGL_INFO, "TX: START Immediate Assignment Downlink (PCH)\n");
+ /* Only one TS can be assigned through PCH, hence the first one is the only one: */
+ const struct gprs_rlcmac_pdch *pdch = tbf_get_first_ts_const(tbf);
+ OSMO_ASSERT(pdch);
+
+ LOGPTBFDL(tbf, LOGL_INFO, "Tx CCCH (PCH) Immediate Assignment [PktDlAss=%s] TA=%d\n", pdch_name(pdch), tbf->ta());
+
bitvec *immediate_assignment = bitvec_alloc(22, tall_pcu_ctx); /* without plen */
bitvec_unhex(immediate_assignment, DUMMY_VEC); /* standard '2B'O padding */
- /* use request reference that has maximum distance to current time,
- * so the assignment will not conflict with possible RACH requests. */
- LOGP(DRLCMAC, LOGL_DEBUG, " - TRX=%d (%d) TS=%d TA=%d\n",
- trx_no, tbf->trx->arfcn, ts_no, tbf->ta());
- plen = Encoding::write_immediate_assignment(&bts->trx[trx_no].pdch[ts_no],
+ /* 3GPP TS 44.018, section 9.1.18.0d states that the network shall code the
+ * Request Reference IE, e.g. by using a suitably offset frame number, such
+ * that the resource reference cannot be confused with any CHANNEL REQUEST
+ * message sent by a mobile station. Use last_rts_fn + 21216 (16 TDMA
+ * super-frame periods, or ~21.3 seconds) to achieve a decent distance. */
+ plen = Encoding::write_immediate_assignment(pdch,
tbf, immediate_assignment, true, 125,
- (tbf->pdch[ts_no]->last_rts_fn + 21216) % GSM_MAX_FN,
+ fn2rfn(GSM_TDMA_FN_SUM(pdch->last_rts_fn, 21216)),
tbf->ta(), 7, false, 0,
bts_get_ms_pwr_alpha(bts), bts->pcu->vty.gamma, -1,
GSM_L1_BURST_TYPE_ACCESS_0);
if (plen >= 0) {
bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_DL_TBF);
- pcu_l1if_tx_pch(bts, immediate_assignment, plen, pgroup);
+
+ if (ms_imsi_is_valid(tbf->ms())) {
+ pcu_l1if_tx_pch2(bts, immediate_assignment, plen, true, tbf->imsi(), tbf->tlli());
+ } else {
+ /* During GMM ATTACH REQUEST, the IMSI is not yet known to the PCU or SGSN. (It is
+ * requested after the GMM ATTACH REQUEST with the GMM IDENTITY REQUEST.) When the PCU
+ * has to assign a DL TBF but the IMSI is not known, then the IMMEDIATE ASSIGNMENT is
+ * sent on the AGCH. The reason for this is that without an IMSI we can not calculate
+ * the paging group, which would be necessary for transmission on PCH. Since the IMSI
+ * is usually only unknown during the GMM ATTACH REQUEST, we may assume that the MS
+ * is in non-DRX mode and hence it is listening on all CCCH blocks, including AGCH.
+ *
+ * See also: 3gpp TS 44.060, section 5.5.1.5
+ * 3gpp TS 45.002, section 6.5.3, 6.5.6 */
+ pcu_l1if_tx_agch2(bts, immediate_assignment, plen, true, tbf->tlli());
+ }
}
bitvec_free(immediate_assignment);
@@ -1105,26 +1208,42 @@ bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts* bts, CodingScheme cs)
}
}
-GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts* bts, uint8_t ms_class, uint8_t egprs_ms_class)
+struct GprsMs *bts_get_ms(const struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli,
+ const char *imsi)
{
- GprsMs *ms;
- ms = bts_ms_store(bts)->create_ms();
+ struct llist_head *tmp;
- ms_set_timeout(ms, osmo_tdef_get(bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1));
- ms_set_ms_class(ms, ms_class);
- ms_set_egprs_ms_class(ms, egprs_ms_class);
+ if (tlli != GSM_RESERVED_TMSI || old_tlli != GSM_RESERVED_TMSI) {
+ llist_for_each(tmp, &bts->ms_list) {
+ struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
+ if (ms_check_tlli(ms, tlli))
+ return ms;
+ if (ms_check_tlli(ms, old_tlli))
+ return ms;
+ }
+ }
- return ms;
+ /* not found by TLLI */
+
+ if (imsi && imsi[0] != '\0') {
+ llist_for_each(tmp, &bts->ms_list) {
+ struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
+ if (ms_imsi_is_valid(ms) && strcmp(imsi, ms_imsi(ms)) == 0)
+ return ms;
+ }
+ }
+
+ return NULL;
}
-struct GprsMsStorage *bts_ms_store(const struct gprs_rlcmac_bts *bts)
+struct GprsMs *bts_get_ms_by_tlli(const struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli)
{
- return bts->ms_store;
+ return bts_get_ms(bts, tlli, old_tlli, NULL);
}
-struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli)
+struct GprsMs *bts_get_ms_by_imsi(const struct gprs_rlcmac_bts *bts, const char *imsi)
{
- return bts_ms_store(bts)->get_ms(tlli, old_tlli);
+ return bts_get_ms(bts, GSM_RESERVED_TMSI, GSM_RESERVED_TMSI, imsi);
}
/* update TA based on TA provided by PH-DATA-IND */
@@ -1175,7 +1294,7 @@ void bts_update_tbf_ta(struct gprs_rlcmac_bts *bts, const char *p, uint32_t fn,
poll->tbf_poll.poll_tbf->direction != GPRS_RLCMAC_UL_TBF)
goto no_tbf;
- tbf = as_ul_tbf(poll->tbf_poll.poll_tbf);
+ tbf = tbf_as_ul_tbf(poll->tbf_poll.poll_tbf);
/* we need to distinguish TA information provided by L1
* from PH-DATA-IND and PHY-RA-IND so that we can properly
* update TA for given TBF
@@ -1332,16 +1451,6 @@ void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts)
bts_set_max_mcs_ul(bts, mcs_ul);
}
-struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi)
-{
- return bts_ms_store(bts)->get_ms(0, 0, imsi);
-}
-
-const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts)
-{
- return bts_ms_store(bts)->ms_list();
-}
-
uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts)
{
if (bts->pcu->vty.force_alpha != (uint8_t)-1)
@@ -1352,3 +1461,20 @@ uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts)
* B.2 Closed loop control */
return 0;
}
+
+/* Used by counter availablePDCHAllocatedTime, TS 52.402 B.2.1.45 "All available PDCH allocated time" */
+bool bts_all_pdch_allocated(const struct gprs_rlcmac_bts *bts)
+{
+ unsigned trx_no, ts_no;
+ for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no++) {
+ const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
+ for (ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ts_no++) {
+ const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
+ if (!pdch_is_enabled(pdch))
+ continue;
+ if(!pdch_is_full(pdch))
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/src/bts.h b/src/bts.h
index d9a86ebb..ab3907fe 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -32,6 +28,7 @@ extern "C" {
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/tdef.h>
+#include <osmocom/core/time_cc.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
@@ -47,6 +44,10 @@ extern "C" {
#include "tbf.h"
#include "coding_scheme.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct GprsMs;
struct gprs_rlcmac_bts;
@@ -67,9 +68,6 @@ struct gprs_rlcmac_trx {
};
-#ifdef __cplusplus
-extern "C" {
-#endif
void bts_trx_init(struct gprs_rlcmac_trx *trx, struct gprs_rlcmac_bts *bts, uint8_t trx_no);
void bts_trx_reserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
void bts_trx_unreserve_slots(struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t slots);
@@ -77,13 +75,9 @@ void bts_trx_free_all_tbf(struct gprs_rlcmac_trx *trx);
void bts_update_tbf_ta(struct gprs_rlcmac_bts *bts, const char *p, uint32_t fn,
uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach);
-#ifdef __cplusplus
-}
-#endif
-
-
enum {
+ CTR_PDCH_ALL_ALLOCATED,
CTR_TBF_DL_ALLOCATED,
CTR_TBF_DL_FREED,
CTR_TBF_DL_ABORTED,
@@ -126,14 +120,21 @@ enum {
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_PCH_REQUESTS,
+ CTR_PCH_REQUESTS_ALREADY,
CTR_PCH_REQUESTS_TIMEDOUT,
CTR_RACH_REQUESTS,
- CTR_11BIT_RACH_REQUESTS,
+ CTR_RACH_REQUESTS_11BIT,
+ CTR_RACH_REQUESTS_ONE_PHASE,
+ CTR_RACH_REQUESTS_TWO_PHASE,
+ CTR_RACH_REQUESTS_UNEXPECTED,
CTR_SPB_UL_FIRST_SEGMENT,
CTR_SPB_UL_SECOND_SEGMENT,
CTR_SPB_DL_FIRST_SEGMENT,
CTR_SPB_DL_SECOND_SEGMENT,
CTR_IMMEDIATE_ASSIGN_UL_TBF,
+ CTR_IMMEDIATE_ASSIGN_UL_TBF_ONE_PHASE,
+ CTR_IMMEDIATE_ASSIGN_UL_TBF_TWO_PHASE,
+ CTR_IMMEDIATE_ASSIGN_UL_TBF_CONTENTION_RESOLUTION_SUCCESS,
CTR_IMMEDIATE_ASSIGN_REJ,
CTR_IMMEDIATE_ASSIGN_DL_TBF,
CTR_CHANNEL_REQUEST_DESCRIPTION,
@@ -195,7 +196,8 @@ struct rach_ind_params {
uint16_t ra;
uint8_t trx_nr;
uint8_t ts_nr;
- uint32_t rfn;
+ uint16_t rfn;
+ uint32_t fn;
int16_t qta;
};
@@ -206,7 +208,6 @@ struct chan_req_params {
bool single_block;
};
-struct GprsMsStorage;
struct pcu_l1_meas;
/**
@@ -267,15 +268,21 @@ struct gprs_rlcmac_bts {
struct rate_ctr_group *ratectrs;
struct osmo_stat_item_group *statg;
- struct GprsMsStorage *ms_store;
+ struct llist_head ms_list; /* list of struct GprsMs */
/* List of struct bts_pch_timer for active PCH pagings */
struct llist_head pch_timer;
-};
-#ifdef __cplusplus
-extern "C" {
-#endif
+ struct osmo_time_cc all_allocated_pdch;
+
+ /* BTS hardware model, see pcuif_proto.h */
+ uint8_t bts_model;
+
+ /* When the PDCH is idle, the timeslot is expected to transmit dummy blocks. Some BTS models will generate
+ * those dummy blocks automatically when no data is transmitted. In contrast, other BTS models may require a
+ * continuous stream of PDCH blocks that already has the gaps filled with dummy blocks. */
+ bool gen_idle_blocks_C0;
+};
struct paging_req_cs {
uint8_t chan_needed;
@@ -286,15 +293,14 @@ struct paging_req_cs {
struct osmo_mobile_identity mi_imsi;
};
-struct GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class, uint8_t egprs_ms_class);
int bts_add_paging(struct gprs_rlcmac_bts *bts, const struct paging_req_cs *req, struct GprsMs *ms);
-uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn);
+uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, uint32_t rfn);
struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
-void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, uint16_t pgroup);
+void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, const struct gprs_rlcmac_dl_tbf *tbf);
void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, uint32_t frame_number);
void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int frame_number);
@@ -308,7 +314,7 @@ int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_di
int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
-int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn);
+int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t tlli);
void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
@@ -322,9 +328,10 @@ void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
enum pcu_gsmtap_category categ, uint8_t channel,
const struct rach_ind_params *rip);
-struct GprsMsStorage *bts_ms_store(const struct gprs_rlcmac_bts *bts);
-
-struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
+struct GprsMs *bts_get_ms(const struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli,
+ const char *imsi);
+struct GprsMs *bts_get_ms_by_tlli(const struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
+struct GprsMs *bts_get_ms_by_imsi(const struct gprs_rlcmac_bts *bts, const char *imsi);
static inline struct rate_ctr_group *bts_rate_counters(struct gprs_rlcmac_bts *bts)
{
@@ -362,7 +369,6 @@ void bts_recalc_initial_cs(struct gprs_rlcmac_bts *bts);
void bts_recalc_initial_mcs(struct gprs_rlcmac_bts *bts);
void bts_recalc_max_cs(struct gprs_rlcmac_bts *bts);
void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts);
-struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
uint8_t bts_max_cs_dl(const struct gprs_rlcmac_bts *bts);
uint8_t bts_max_cs_ul(const struct gprs_rlcmac_bts *bts);
uint8_t bts_max_mcs_dl(const struct gprs_rlcmac_bts *bts);
@@ -372,8 +378,8 @@ void bts_set_max_cs_ul(struct gprs_rlcmac_bts *bts, uint8_t cs_ul);
void bts_set_max_mcs_dl(struct gprs_rlcmac_bts *bts, uint8_t mcs_dl);
void bts_set_max_mcs_ul(struct gprs_rlcmac_bts *bts, uint8_t mcs_ul);
bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts *bts, enum CodingScheme cs);
-const struct llist_head* bts_ms_list(struct gprs_rlcmac_bts *bts);
uint8_t bts_get_ms_pwr_alpha(const struct gprs_rlcmac_bts *bts);
+bool bts_all_pdch_allocated(const struct gprs_rlcmac_bts *bts);
#ifdef __cplusplus
}
#endif
diff --git a/src/bts_pch_timer.c b/src/bts_pch_timer.c
index 20373ac8..4b752398 100644
--- a/src/bts_pch_timer.c
+++ b/src/bts_pch_timer.c
@@ -3,8 +3,8 @@
* Author: Oliver Smith
*
* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -12,7 +12,7 @@
* 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
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -26,8 +26,22 @@
#include <gprs_debug.h>
#include <gprs_pcu.h>
#include <bts_pch_timer.h>
+#include <gprs_ms.h>
-static struct bts_pch_timer *bts_pch_timer_get(struct gprs_rlcmac_bts *bts, const char *imsi)
+static struct bts_pch_timer *bts_pch_timer_get_by_ptmsi(struct gprs_rlcmac_bts *bts, uint32_t ptmsi)
+{
+ struct bts_pch_timer *p;
+ OSMO_ASSERT(ptmsi != GSM_RESERVED_TMSI);
+
+ llist_for_each_entry(p, &bts->pch_timer, entry) {
+ if (p->ptmsi != GSM_RESERVED_TMSI && p->ptmsi == ptmsi)
+ return p;
+ }
+
+ return NULL;
+}
+
+struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi)
{
struct bts_pch_timer *p;
@@ -57,29 +71,42 @@ static void T3113_callback(void *data)
bts_pch_timer_remove(p);
}
-void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const char *imsi)
+void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
+ const char *imsi)
{
- if (bts_pch_timer_get(bts, imsi))
- return;
-
struct bts_pch_timer *p;
+ struct osmo_tdef *tdef;
+
p = talloc_zero(bts, struct bts_pch_timer);
llist_add_tail(&p->entry, &bts->pch_timer);
- osmo_strlcpy(p->imsi, imsi, sizeof(p->imsi));
p->bts = bts;
+ OSMO_STRLCPY_ARRAY(p->imsi, imsi);
+ p->ptmsi = (mi_paging->type == GSM_MI_TYPE_TMSI) ? mi_paging->tmsi : GSM_RESERVED_TMSI;
- struct osmo_tdef *tdef = osmo_tdef_get_entry(the_pcu->T_defs, 3113);
+ tdef = osmo_tdef_get_entry(the_pcu->T_defs, 3113);
OSMO_ASSERT(tdef);
osmo_timer_setup(&p->T3113, T3113_callback, p);
osmo_timer_schedule(&p->T3113, tdef->val, 0);
- LOGP(DPCU, LOGL_DEBUG, "PCH paging timer started for IMSI=%s\n", p->imsi);
+ if (log_check_level(DPCU, LOGL_DEBUG)) {
+ char str[64];
+ osmo_mobile_identity_to_str_buf(str, sizeof(str), mi_paging);
+ LOGP(DPCU, LOGL_DEBUG, "PCH paging timer started for MI=%s IMSI=%s\n", str, p->imsi);
+ }
}
-void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const char *imsi)
+void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms)
{
- struct bts_pch_timer *p = bts_pch_timer_get(bts, imsi);
-
+ struct bts_pch_timer *p = NULL;
+ uint32_t tlli = ms_tlli(ms);
+ const char *imsi = ms_imsi(ms);
+
+ /* First try matching by TMSI if available in MS */
+ if (tlli != GSM_RESERVED_TMSI)
+ p = bts_pch_timer_get_by_ptmsi(bts, tlli);
+ /* Otherwise try matching by IMSI if available in MS */
+ if (!p && imsi[0] != '\0')
+ p = bts_pch_timer_get_by_imsi(bts, imsi);
if (p)
bts_pch_timer_remove(p);
}
diff --git a/src/bts_pch_timer.h b/src/bts_pch_timer.h
index 26b89c80..c65f9fa2 100644
--- a/src/bts_pch_timer.h
+++ b/src/bts_pch_timer.h
@@ -12,7 +12,7 @@
* 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
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -32,12 +32,17 @@ struct bts_pch_timer {
struct llist_head entry;
struct gprs_rlcmac_bts *bts;
struct osmo_timer_list T3113;
+ uint32_t ptmsi; /* GSM_RESERVED_TMSI if not available */
char imsi[OSMO_IMSI_BUF_SIZE];
};
-void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const char *imsi);
-void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const char *imsi);
+struct GprsMs;
+
+void bts_pch_timer_start(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi_paging,
+ const char *imsi);
+void bts_pch_timer_stop(struct gprs_rlcmac_bts *bts, const struct GprsMs *ms);
void bts_pch_timer_stop_all(struct gprs_rlcmac_bts *bts);
+struct bts_pch_timer *bts_pch_timer_get_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
#ifdef __cplusplus
}
diff --git a/src/coding_scheme.c b/src/coding_scheme.c
index 27241d19..c370c4b6 100644
--- a/src/coding_scheme.c
+++ b/src/coding_scheme.c
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdint.h>
diff --git a/src/coding_scheme.h b/src/coding_scheme.h
index 1b212650..fdecf1c9 100644
--- a/src/coding_scheme.h
+++ b/src/coding_scheme.h
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
diff --git a/src/csn1.c b/src/csn1.c
index e165e920..a9b13430 100644
--- a/src/csn1.c
+++ b/src/csn1.c
@@ -22,10 +22,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
diff --git a/src/csn1.h b/src/csn1.h
index 285ae991..47956c34 100644
--- a/src/csn1.h
+++ b/src/csn1.h
@@ -19,10 +19,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _PACKET_CSN1_H_
diff --git a/src/csn1_dec.c b/src/csn1_dec.c
index fa1f0c39..f0d07ac7 100644
--- a/src/csn1_dec.c
+++ b/src/csn1_dec.c
@@ -22,10 +22,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
@@ -996,7 +992,7 @@ csnStreamDecoder(csnStream_t* ar, const CSN_DESCR* pDescr, struct bitvec *vector
}
}
LOGPC(DCSN1, LOGL_DEBUG, "%s = %u%s | ", pDescr->sz , (unsigned)fExist, fExist && isnull ? " (NULL)" : "");
- *pui8++ = fExist;
+ *pui8++ = !isnull;
remaining_bits_len -= 1;
bit_offset++;
diff --git a/src/csn1_enc.c b/src/csn1_enc.c
index 5518d063..e4e5e092 100644
--- a/src/csn1_enc.c
+++ b/src/csn1_enc.c
@@ -22,10 +22,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <assert.h>
diff --git a/src/cxx_linuxlist.h b/src/cxx_linuxlist.h
index 9a74b3fe..1562bfe1 100644
--- a/src/cxx_linuxlist.h
+++ b/src/cxx_linuxlist.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
diff --git a/src/decoding.cpp b/src/decoding.cpp
index 0caa8bfd..9f93f015 100644
--- a/src/decoding.cpp
+++ b/src/decoding.cpp
@@ -12,20 +12,17 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <decoding.h>
#include <rlc.h>
+#include <rlc_window.h>
#include <gprs_debug.h>
#include <egprs_rlc_compression.h>
extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/bitcomp.h>
-#include <osmocom/gprs/protocol/gsm_04_60.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
}
#include <arpa/inet.h>
diff --git a/src/decoding.h b/src/decoding.h
index 7270c3d8..c89b5b1a 100644
--- a/src/decoding.h
+++ b/src/decoding.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -36,6 +32,8 @@ struct bitvec;
#ifdef __cplusplus
+#include "rlc_window_dl.h"
+
class Decoding {
public:
/* represents (parts) LLC PDUs within one RLC Data block */
diff --git a/src/encoding.cpp b/src/encoding.cpp
index 30b0ee1d..56956665 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <encoding.h>
@@ -29,7 +25,7 @@
#include <egprs_rlc_compression.h>
extern "C" {
-#include <osmocom/gprs/protocol/gsm_04_60.h>
+#include <osmocom/gsm/protocol/gsm_44_060.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48.h>
}
@@ -67,22 +63,22 @@ static int write_alpha_gamma(bitvec *dest, uint8_t alpha, uint8_t gamma)
}
/* TBF_STARTING_TIME -- same as 3GPP TS 44.018 §10.5.2.38 Starting Time without tag: */
-static int write_tbf_start_time(bitvec *dest, uint32_t fn)
+static int write_tbf_start_time(bitvec *dest, uint16_t rfn)
{
int rc;
/* Set values according to 3GPP TS 44.018 Table 10.5.2.38.1 */
/* T1' */
- rc = bitvec_set_u64(dest, (fn / (26 * 51)) % 32, 5, false);
+ rc = bitvec_set_u64(dest, (rfn / (26 * 51)) % 32, 5, false);
CHECK(rc);
/* T3 */
- rc = bitvec_set_u64(dest, fn % 51, 6, false);
+ rc = bitvec_set_u64(dest, rfn % 51, 6, false);
CHECK(rc);
/* T2 */
- rc = bitvec_set_u64(dest, fn % 26, 5, false);
+ rc = bitvec_set_u64(dest, rfn % 26, 5, false);
CHECK(rc);
return rc;
@@ -184,7 +180,7 @@ static inline void write_ta_ie(bitvec *dest, unsigned& wp,
}
static int write_ia_rest_downlink(const gprs_rlcmac_dl_tbf *tbf, bitvec * dest, bool polling, bool ta_valid,
- uint32_t fn, uint8_t alpha, uint8_t gamma, int8_t ta_idx)
+ uint16_t rfn, uint8_t alpha, uint8_t gamma, int8_t ta_idx)
{
int rc = 0;
@@ -217,7 +213,7 @@ static int write_ia_rest_downlink(const gprs_rlcmac_dl_tbf *tbf, bitvec * dest,
if (polling) {
SET_1(dest);
- rc = write_tbf_start_time(dest, fn);
+ rc = write_tbf_start_time(dest, rfn);
CHECK(rc);
} else
SET_0(dest);
@@ -239,7 +235,7 @@ static int write_ia_rest_downlink(const gprs_rlcmac_dl_tbf *tbf, bitvec * dest,
}
/* 3GPP TS 44.018 Table 10.5.2.16.1 < Packet Uplink Assignment > -- Single Block Allocation */
-static int write_ia_rest_uplink_sba(bitvec *dest, uint32_t fn, uint8_t alpha, uint8_t gamma)
+static int write_ia_rest_uplink_sba(bitvec *dest, uint32_t rfn, uint8_t alpha, uint8_t gamma)
{
int rc = 0;
@@ -252,7 +248,7 @@ static int write_ia_rest_uplink_sba(bitvec *dest, uint32_t fn, uint8_t alpha, ui
SET_0(dest);
SET_1(dest);
- rc = write_tbf_start_time(dest, fn);
+ rc = write_tbf_start_time(dest, rfn);
CHECK(rc);
/* No P0 nor PR_MODE */
@@ -296,7 +292,7 @@ static int write_ia_rest_uplink_mba(const gprs_rlcmac_ul_tbf *tbf, bitvec *dest,
return rc;
}
-static int write_ia_rest_egprs_uplink_mba(bitvec * dest, uint32_t fn, uint8_t alpha, uint8_t gamma)
+static int write_ia_rest_egprs_uplink_mba(bitvec * dest, uint32_t rfn, uint8_t alpha, uint8_t gamma)
{
int rc = 0;
@@ -305,7 +301,7 @@ static int write_ia_rest_egprs_uplink_mba(bitvec * dest, uint32_t fn, uint8_t al
rc = write_alpha_gamma(dest, alpha, gamma);
CHECK(rc);
- rc = write_tbf_start_time(dest, fn);
+ rc = write_tbf_start_time(dest, rfn);
CHECK(rc);
SET_0(dest); /* NUMBER OF RADIO BLOCKS ALLOCATED: */
@@ -362,7 +358,7 @@ static int write_ia_rest_egprs_uplink_sba(const gprs_rlcmac_ul_tbf *tbf, bitvec
* see GSM 44.018, 9.1.20 + 10.5.2.30
*/
int Encoding::write_immediate_assignment_reject(bitvec *dest, uint16_t ra,
- uint32_t ref_fn, enum ph_burst_type burst_type, uint8_t t3142)
+ uint16_t ref_rfn, enum ph_burst_type burst_type, uint8_t t3142)
{
unsigned wp = 0;
int plen;
@@ -398,9 +394,9 @@ int Encoding::write_immediate_assignment_reject(bitvec *dest, uint16_t ra,
}
bitvec_write_field(dest, &wp,
- (ref_fn / (26 * 51)) % 32, 5); // T1'
- bitvec_write_field(dest, &wp, ref_fn % 51, 6); // T3
- bitvec_write_field(dest, &wp, ref_fn % 26, 5); // T2
+ (ref_rfn / (26 * 51)) % 32, 5); // T1'
+ bitvec_write_field(dest, &wp, ref_rfn % 51, 6); // T3
+ bitvec_write_field(dest, &wp, ref_rfn % 26, 5); // T2
/* 10.5.2.43 Wait Indication */
bitvec_write_field(dest, &wp, t3142, 8);
@@ -439,27 +435,27 @@ int Encoding::write_immediate_assignment_reject(bitvec *dest, uint16_t ra,
*/
int Encoding::write_immediate_assignment(
const struct gprs_rlcmac_pdch *pdch,
- struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_tbf *tbf,
bitvec * dest, bool downlink, uint16_t ra,
- uint32_t ref_fn, uint8_t ta,
- uint8_t usf, bool polling, uint32_t fn, uint8_t alpha,
+ uint16_t ref_rfn, uint8_t ta,
+ uint8_t usf, bool polling, uint16_t rfn, uint8_t alpha,
uint8_t gamma, int8_t ta_idx, enum ph_burst_type burst_type)
{
unsigned wp = 0;
int plen;
int rc;
- bitvec_write_field(dest, &wp,0x0,4); // Skip Indicator
- bitvec_write_field(dest, &wp,0x6,4); // Protocol Discriminator
- bitvec_write_field(dest, &wp,0x3F,8); // Immediate Assignment Message Type
+ bitvec_write_field(dest, &wp, 0x0, 4); // Skip Indicator
+ bitvec_write_field(dest, &wp, 0x6, 4); // Protocol Discriminator
+ bitvec_write_field(dest, &wp, GSM48_MT_RR_IMM_ASS, 8); // Immediate Assignment Message Type
// 10.5.2.25b Dedicated mode or TBF
- bitvec_write_field(dest, &wp,0x0,1); // spare
- bitvec_write_field(dest, &wp,0x0,1); // TMA : Two-message assignment: No meaning
- bitvec_write_field(dest, &wp,downlink,1); // Downlink : Downlink assignment to mobile in packet idle mode
- bitvec_write_field(dest, &wp,0x1,1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF).
+ bitvec_write_field(dest, &wp, 0x0, 1); // spare
+ bitvec_write_field(dest, &wp, 0x0, 1); // TMA : Two-message assignment: No meaning
+ bitvec_write_field(dest, &wp, downlink, 1); // Downlink : Downlink assignment to mobile in packet idle mode
+ bitvec_write_field(dest, &wp, 0x1, 1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF).
- bitvec_write_field(dest, &wp,0x0,4); // Page Mode
+ bitvec_write_field(dest, &wp, 0x0, 4); // Page Mode
// GSM 04.08 10.5.2.25a Packet Channel Description
bitvec_write_field(dest, &wp, 0x01, 5); // Channel type
@@ -484,13 +480,13 @@ int Encoding::write_immediate_assignment(
bitvec_write_field(dest, &wp, ra, 8); /* RACH value */
}
- bitvec_write_field(dest, &wp,(ref_fn / (26 * 51)) % 32,5); // T1'
- bitvec_write_field(dest, &wp,ref_fn % 51,6); // T3
- bitvec_write_field(dest, &wp,ref_fn % 26,5); // T2
+ bitvec_write_field(dest, &wp, (ref_rfn / (26 * 51)) % 32, 5); // T1'
+ bitvec_write_field(dest, &wp, ref_rfn % 51, 6); // T3
+ bitvec_write_field(dest, &wp, ref_rfn % 26, 5); // T2
// 10.5.2.40 Timing Advance
- bitvec_write_field(dest, &wp,0x0,2); // spare
- bitvec_write_field(dest, &wp,ta,6); // Timing Advance value
+ bitvec_write_field(dest, &wp, 0x0, 2); // spare
+ bitvec_write_field(dest, &wp, ta, 6); // Timing Advance value
/* 10.5.2.21 Mobile Allocation */
if (pdch->fh.enabled) {
@@ -510,9 +506,9 @@ int Encoding::write_immediate_assignment(
/* 3GPP TS 44.018 §10.5.2.16 IA Rest Octets */
dest->cur_bit = wp;
if (downlink) {
- OSMO_ASSERT(as_dl_tbf(tbf) != NULL);
+ OSMO_ASSERT(tbf_as_dl_tbf_const(tbf) != NULL);
- rc = write_ia_rest_downlink(as_dl_tbf(tbf), dest, polling, gsm48_ta_is_valid(ta), fn, alpha, gamma,
+ rc = write_ia_rest_downlink(tbf_as_dl_tbf_const(tbf), dest, polling, gsm48_ta_is_valid(ta), rfn, alpha, gamma,
ta_idx);
} else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) || (burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
SET_L(dest); SET_H(dest); // "LH"
@@ -522,20 +518,20 @@ int Encoding::write_immediate_assignment(
SET_0(dest); // No < Access Technologies Request struct >
- if (as_ul_tbf(tbf) != NULL)
- rc = write_ia_rest_egprs_uplink_sba(as_ul_tbf(tbf), dest, usf, alpha, gamma, ta_idx);
+ if (tbf_as_ul_tbf_const(tbf) != NULL)
+ rc = write_ia_rest_egprs_uplink_sba(tbf_as_ul_tbf_const(tbf), dest, usf, alpha, gamma, ta_idx);
else
- rc = write_ia_rest_egprs_uplink_mba(dest, fn, alpha, gamma);
+ rc = write_ia_rest_egprs_uplink_mba(dest, rfn, alpha, gamma);
} else {
OSMO_ASSERT(!tbf || !tbf->is_egprs_enabled());
SET_H(dest); SET_H(dest); // "HH"
SET_0(dest); SET_0(dest); // "00" < Packet Uplink Assignment >
- if (as_ul_tbf(tbf) != NULL)
- rc = write_ia_rest_uplink_mba(as_ul_tbf(tbf), dest, usf, alpha, gamma, ta_idx);
+ if (tbf_as_ul_tbf_const(tbf) != NULL)
+ rc = write_ia_rest_uplink_mba(tbf_as_ul_tbf_const(tbf), dest, usf, alpha, gamma, ta_idx);
else
- rc = write_ia_rest_uplink_sba(dest, fn, alpha, gamma);
+ rc = write_ia_rest_uplink_sba(dest, rfn, alpha, gamma);
}
if (rc < 0) {
@@ -557,7 +553,7 @@ static void gen_freq_params(Frequency_Parameters_t *freq_params,
Direct_encoding_1_t fh_params;
/* Check one PDCH, if it's hopping then all other should too */
- pdch = tbf->pdch[tbf->first_ts];
+ pdch = tbf_get_first_ts_const(tbf);
OSMO_ASSERT(pdch != NULL);
/* Training Sequence Code */
@@ -566,7 +562,7 @@ static void gen_freq_params(Frequency_Parameters_t *freq_params,
/* If frequency hopping is not in use, encode a single ARFCN */
if (!pdch->fh.enabled) {
freq_params->UnionType = 0x00;
- freq_params->u.ARFCN = tbf->trx->arfcn;
+ freq_params->u.ARFCN = pdch->trx->arfcn;
return;
}
@@ -577,6 +573,8 @@ static void gen_freq_params(Frequency_Parameters_t *freq_params,
fh_params.MAIO = pdch->fh.maio;
fh_params.GPRS_Mobile_Allocation.HSN = pdch->fh.hsn;
fh_params.GPRS_Mobile_Allocation.ElementsOf_RFL_NUMBER = 0;
+ memset(&fh_params.GPRS_Mobile_Allocation.RFL_NUMBER[0], 0x00,
+ sizeof(fh_params.GPRS_Mobile_Allocation.RFL_NUMBER));
/* Mobile Allocation bitmap */
fh_params.GPRS_Mobile_Allocation.UnionType = 0; /* MA bitmap */
@@ -607,7 +605,7 @@ void write_packet_uplink_assignment(RlcMacDownlink_t *block, uint8_t old_tfi,
/* See 3GPP TS 44.060, section 11.2.29 */
pua = &block->u.Packet_Uplink_Assignment;
- pua->MESSAGE_TYPE = 0x0a; // Packet Uplink Assignment
+ pua->MESSAGE_TYPE = MT_PACKET_UPLINK_ASSIGNMENT; // Packet Uplink Assignment
pua->PAGE_MODE = 0x00; // Normal Paging
/* TLLI or Global (UL/DL) TFI */
@@ -803,7 +801,7 @@ void write_packet_downlink_assignment(RlcMacDownlink_t * block,
}
/* Generate paging request. See 44.018, sections 10 and 9.1.22 */
-int Encoding::write_paging_request(bitvec * dest, const struct osmo_mobile_identity *mi)
+int write_paging_request(struct bitvec *dest, const struct osmo_mobile_identity *mi)
{
uint8_t mi_buf[GSM48_MID_MAX_SIZE];
int mi_len;
diff --git a/src/encoding.h b/src/encoding.h
index 998e69aa..89f69640 100644
--- a/src/encoding.h
+++ b/src/encoding.h
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -48,25 +44,23 @@ class Encoding {
public:
static int write_immediate_assignment(
const struct gprs_rlcmac_pdch *pdch,
- struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_tbf *tbf,
bitvec * dest, bool downlink, uint16_t ra,
- uint32_t ref_fn, uint8_t ta,
+ uint16_t ref_rfn, uint8_t ta,
uint8_t usf, bool polling,
- uint32_t fn, uint8_t alpha, uint8_t gamma,
+ uint16_t rfn, uint8_t alpha, uint8_t gamma,
int8_t ta_idx,
enum ph_burst_type burst_type);
static int write_immediate_assignment_reject(
bitvec *dest, uint16_t ra,
- uint32_t ref_fn,
+ uint16_t ref_rfn,
enum ph_burst_type burst_type,
uint8_t t3142
);
static void encode_rbb(const char *show_rbb, bitvec *rbb);
- static int write_paging_request(bitvec * dest, const struct osmo_mobile_identity *mi);
-
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
@@ -102,6 +96,9 @@ extern "C" {
#endif
void write_packet_access_reject(struct bitvec *dest, uint32_t tlli, unsigned long t3172_ms);
+
+int write_paging_request(struct bitvec *dest, const struct osmo_mobile_identity *mi);
+
void write_packet_uplink_assignment(RlcMacDownlink_t *block, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
const struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll,
diff --git a/src/ericsson-rbs/er_ccu_descr.h b/src/ericsson-rbs/er_ccu_descr.h
new file mode 100644
index 00000000..9fe1aab8
--- /dev/null
+++ b/src/ericsson-rbs/er_ccu_descr.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/trau/trau_pcu_ericsson.h>
+
+struct er_ccu_descr;
+struct e1_conn_pars;
+typedef void (er_ccu_empty) (struct er_ccu_descr *ccu_descr);
+typedef void (er_ccu_rx) (struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits);
+
+struct er_ccu_descr {
+
+ /* E1-line and timeslot (filled in by user) */
+ struct e1_conn_pars *e1_conn_pars;
+
+ /* Callback functions (provided by user) */
+ er_ccu_empty *er_ccu_empty_cb;
+ er_ccu_rx *er_ccu_rx_cb;
+
+ /* I.460 Subslot */
+ struct {
+ struct osmo_i460_schan_desc scd;
+ struct osmo_i460_subchan *schan;
+ struct osmo_fsm_inst *trau_sync_fi;
+ bool ccu_connected;
+ } link;
+
+ /* TRAU Sync state */
+ struct {
+ uint32_t pseq_ccu; /* CCU sequence counter (remote) */
+ uint32_t pseq_pcu; /* PCU sequence counter (local) */
+ uint32_t last_afn_ul; /* Adjusted frame number, uplink */
+ uint32_t last_afn_dl; /* Adjusted frame number, downlink */
+ enum time_adj_val tav; /* Last time adjustment value */
+ bool ul_frame_err; /* True when last uplink TRAU frame was bad */
+ bool ccu_synced; /* True when PCU is in sync with CCU */
+ } sync;
+
+ /* PCU related context */
+ struct {
+ uint8_t trx_no;
+ uint8_t bts_nr;
+ uint8_t ts;
+ } pcu;
+
+
+};
+
+struct er_trx_descr {
+ struct er_ccu_descr ts_ccu_descr[8];
+};
diff --git a/src/ericsson-rbs/er_ccu_if.c b/src/ericsson-rbs/er_ccu_if.c
new file mode 100644
index 00000000..98abbf7d
--- /dev/null
+++ b/src/ericsson-rbs/er_ccu_if.c
@@ -0,0 +1,416 @@
+/*
+ * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <er_ccu_if.h>
+#include <er_ccu_descr.h>
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/abis.h>
+#include <osmocom/trau/trau_sync.h>
+#include <osmocom/trau/trau_pcu_ericsson.h>
+#include <bts.h>
+#include <gprs_debug.h>
+#include <pcu_l1_if.h>
+
+#define E1_TS_BYTES 160
+#define DEBUG_BITS_MAX 1280
+#define DEBUG_BYTES_MAX 40
+
+#define LOGPCCU(ccu_descr, level, tag, fmt, args...) \
+ LOGP(DE1, level, "E1TS(%u:%u:%u) %s:" fmt, \
+ ccu_descr->e1_conn_pars->e1_nr, ccu_descr->e1_conn_pars->e1_ts, \
+ ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL ? 0 : ccu_descr->e1_conn_pars->e1_ts_ss, tag, \
+ ## args)
+
+struct e1_ts_descr {
+ uint8_t usecount;
+ bool i460_ts_initialized;
+ struct osmo_i460_timeslot i460_ts;
+};
+
+struct e1_line_descr {
+ struct e1_ts_descr e1_ts[NUM_E1_TS - 1];
+};
+
+static struct e1_line_descr e1_lines[32];
+static void *tall_ccu_ctx = NULL;
+
+static const struct e1inp_line_ops dummy_e1_line_ops = {
+ .sign_link_up = NULL,
+ .sign_link_down = NULL,
+ .sign_link = NULL,
+};
+
+/* called by trau frame synchronizer: feed received MAC blocks into PCU */
+static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits)
+{
+ struct er_ccu_descr *ccu_descr = user_data;
+
+ if (!bits || num_bits == 0)
+ return;
+
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-RX", "receiving %u TRAU frame bits from subslot (synchronized): %s...\n",
+ num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
+
+ ccu_descr->er_ccu_rx_cb(ccu_descr, bits, num_bits);
+}
+
+/* called by I.460 de-multiplexer: feed output of I.460 demux into TRAU frame sync */
+static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits,
+ unsigned int num_bits)
+{
+ struct er_ccu_descr *ccu_descr = user_data;
+
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-RX", "receiving %u TRAU frame bits from subslot: %s...\n", num_bits,
+ osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits));
+
+ OSMO_ASSERT(ccu_descr->link.trau_sync_fi);
+ osmo_trau_sync_rx_ubits(ccu_descr->link.trau_sync_fi, bits, num_bits);
+
+}
+
+/* called by I.460 de-multiplexer: ensure that sync indications are sent when mux buffer runs empty */
+static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan2, void *user_data)
+{
+ struct er_ccu_descr *ccu_descr = user_data;
+
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-TX", "demux buffer empty\n");
+ ccu_descr->er_ccu_empty_cb(ccu_descr);
+}
+
+/* handle outgoing E1 traffic */
+static void e1_send_ts_frame(struct e1inp_ts *ts)
+{
+ void *ctx = tall_ccu_ctx;
+ struct e1_ts_descr *ts_descr;
+ struct msgb *msg;
+ uint8_t *ptr;
+
+ /* The line number and ts number that arrives here should be clean. */
+ OSMO_ASSERT(ts->line->num < ARRAY_SIZE(e1_lines));
+
+ ts_descr = &e1_lines[ts->line->num].e1_ts[ts->num];
+
+ /* Do not send anything in case the E1 timeslot is not ready. */
+ if (ts_descr->usecount == 0)
+ return;
+
+ /* Get E1 frame from I.460 multiplexer */
+ msg = msgb_alloc_c(ctx, E1_TS_BYTES, "E1-TX-timeslot-bytes");
+ ptr = msgb_put(msg, E1_TS_BYTES);
+ osmo_i460_mux_out(&ts_descr->i460_ts, ptr, E1_TS_BYTES);
+
+ LOGPITS(ts, DE1, LOGL_DEBUG, "E1-TX: sending %u bytes: %s...\n",
+ msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg),
+ msgb_length(msg) >
+ DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg)));
+
+ /* Hand data over to the E1 stack */
+ msgb_enqueue(&ts->raw.tx_queue, msg);
+}
+
+/* Callback function to handle incoming E1 traffic */
+static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg)
+{
+ struct e1_ts_descr *ts_descr;
+
+ if (msg->len != E1_TS_BYTES) {
+ LOGPITS(ts, DE1, LOGL_ERROR,
+ "E1-RX: receiving bad, expected length is %u, actual length is %u!\n",
+ E1_TS_BYTES, msg->len);
+ msgb_free(msg);
+ return;
+ }
+
+ LOGPITS(ts, DE1, LOGL_DEBUG, "E1-RX: receiving %u bytes: %s ...\n",
+ msg->len, osmo_hexdump_nospc(msg->data, msg->len));
+
+ /* Note: The line number and ts number that arrives here should be clean. */
+ OSMO_ASSERT(ts->line->num < ARRAY_SIZE(e1_lines));
+ ts_descr = &e1_lines[ts->line->num].e1_ts[ts->num];
+
+ /* Hand data over to the I640 demultiplexer. */
+ osmo_i460_demux_in(&ts_descr->i460_ts, msg->data, msg->len);
+
+ /* Trigger sending of pending E1 traffic */
+ e1_send_ts_frame(ts);
+
+ /* e1inp_rx_ts(), the caller of this callback does not free() msgb. */
+ msgb_free(msg);
+}
+
+static struct e1_ts_descr *ts_descr_from_ccu_descr(struct er_ccu_descr *ccu_descr)
+{
+ /* Make sure E1 line number is valid */
+ if (ccu_descr->e1_conn_pars->e1_nr >= ARRAY_SIZE(e1_lines)) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "Invalid E1 line number!\n");
+ return NULL;
+ }
+
+ /* Make sure E1 timeslot number is valid */
+ if (ccu_descr->e1_conn_pars->e1_ts < 1 || ccu_descr->e1_conn_pars->e1_ts > NUM_E1_TS - 1) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "Invalid E1 timeslot number!\n");
+ return NULL;
+ }
+
+ /* Timeslots are only initialized once and will stay open after that. */
+ return &e1_lines[ccu_descr->e1_conn_pars->e1_nr].e1_ts[ccu_descr->e1_conn_pars->e1_ts];
+}
+
+/* Configure an I.460 subslot and add it to the CCU descriptor */
+static int add_i460_subslot(void *ctx, struct er_ccu_descr *ccu_descr)
+{
+ struct e1_ts_descr *ts_descr;
+ enum osmo_tray_sync_pat_id sync_pattern;
+
+ if (ccu_descr->link.schan) {
+ /* NOTE: This is a serious error: subslots should be removed when l1if_close_trx() is called by the
+ * PCU. This log line points towards a problem with the PDCH management inside the PCU! */
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "I.460 subslot is already configured -- will not touch it!\n");
+ return -EINVAL;
+ }
+
+ ts_descr = ts_descr_from_ccu_descr(ccu_descr);
+ if (!ts_descr)
+ return -EINVAL;
+ if (ts_descr->usecount == 0) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "E1 timeslot not ready!\n");
+ return -EINVAL;
+ }
+
+ /* Set up I.460 subchannel and connect it to the MUX on the E1 timeslot */
+ if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL) {
+ LOGPCCU(ccu_descr, LOGL_INFO, "SETUP", "using 64k subslots\n");
+ ccu_descr->link.scd.rate = OSMO_I460_RATE_64k;
+ ccu_descr->link.scd.demux.num_bits = E1_TS_BYTES * 8;
+ ccu_descr->link.scd.bit_offset = 0;
+ sync_pattern = OSMO_TRAU_SYNCP_64_ER_CCU;
+ } else {
+ LOGPCCU(ccu_descr, LOGL_INFO, "SETUP", "using 16k subslots\n");
+ if (ccu_descr->e1_conn_pars->e1_ts_ss > 3) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "Invalid I.460 subslot number!\n");
+ return -EINVAL;
+ }
+ ccu_descr->link.scd.rate = OSMO_I460_RATE_16k;
+ ccu_descr->link.scd.demux.num_bits = E1_TS_BYTES / 4 * 8;
+ ccu_descr->link.scd.bit_offset = ccu_descr->e1_conn_pars->e1_ts_ss * 2;
+ sync_pattern = OSMO_TRAU_SYNCP_16_ER_CCU;
+ }
+
+ ccu_descr->link.scd.demux.out_cb_bits = e1_i460_demux_bits_cb;
+ ccu_descr->link.scd.demux.out_cb_bytes = NULL;
+ ccu_descr->link.scd.demux.user_data = ccu_descr;
+ ccu_descr->link.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb;
+ ccu_descr->link.scd.mux.user_data = ccu_descr;
+
+ LOGPCCU(ccu_descr, LOGL_INFO, "SETUP", "adding I.460 subchannel: bit_offset=%u, num_bits=%zu\n",
+ ccu_descr->link.scd.bit_offset, ccu_descr->link.scd.demux.num_bits);
+ ccu_descr->link.schan = osmo_i460_subchan_add(ctx, &ts_descr->i460_ts, &ccu_descr->link.scd);
+ if (!ccu_descr->link.schan) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "adding I.460 subchannel: failed!\n");
+ return -EINVAL;
+ }
+
+ /* Configure TRAU synchronizer */
+ ccu_descr->link.trau_sync_fi = osmo_trau_sync_alloc(tall_ccu_ctx, "trau-sync", sync_frame_out_cb, sync_pattern, ccu_descr);
+ if (!ccu_descr->link.trau_sync_fi) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "adding I.460 TRAU frame sync: failed!\n");
+ return -EINVAL;
+ }
+
+ /* Ericsson uses a different synchronization pattern for MCS9 TRAU frames */
+ if (sync_pattern == OSMO_TRAU_SYNCP_64_ER_CCU)
+ osmo_trau_sync_set_secondary_pat(ccu_descr->link.trau_sync_fi, OSMO_TRAU_SYNCP_64_ER_CCU_MCS9, 1);
+
+ return 0;
+}
+
+/* Remove an I.460 subslot from the CCU descriptor */
+static void del_i460_subslot(struct er_ccu_descr *ccu_descr)
+{
+ if (ccu_descr->link.schan)
+ osmo_i460_subchan_del(ccu_descr->link.schan);
+ ccu_descr->link.schan = NULL;
+ if (ccu_descr->link.trau_sync_fi)
+ osmo_fsm_inst_term(ccu_descr->link.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL);
+ ccu_descr->link.trau_sync_fi = NULL;
+
+ memset(&ccu_descr->link.scd, 0, sizeof(ccu_descr->link.scd));
+}
+
+/* Configure an E1 timeslot according to the description in the ccu_descr */
+static int open_e1_timeslot(struct er_ccu_descr *ccu_descr)
+{
+ struct e1inp_line *e1_line;
+ struct e1_ts_descr *ts_descr;
+ int rc;
+
+ /* Find timeslot descriptor and check if the timeslot is already open. */
+ ts_descr = ts_descr_from_ccu_descr(ccu_descr);
+ if (!ts_descr)
+ return -EINVAL;
+ if (ts_descr->usecount > 0) {
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP", "E1 timeslot already open -- using it as it is!\n");
+ ts_descr->usecount++;
+ return 0;
+ }
+
+ /* Find and set up E1 line */
+ e1_line = e1inp_line_find(ccu_descr->e1_conn_pars->e1_nr);
+ if (!e1_line) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "no such E1 line!\n");
+ return -EINVAL;
+ }
+ e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops);
+
+ /* Set up E1 timeslot */
+ rc = e1inp_ts_config_raw(&e1_line->ts[ccu_descr->e1_conn_pars->e1_ts - 1], e1_line, e1_recv_cb);
+ if (rc < 0) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "configuration of timeslot failed!\n");
+ return -EINVAL;
+ }
+ rc = e1inp_line_update(e1_line);
+ if (rc < 0) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "line update failed!\n");
+ return -EINVAL;
+ }
+
+ /* Make sure the i460 mux is ready */
+ if (!ts_descr->i460_ts_initialized) {
+ osmo_i460_ts_init(&ts_descr->i460_ts);
+ ts_descr->i460_ts_initialized = true;
+ }
+
+ ts_descr->usecount++;
+ OSMO_ASSERT(ts_descr->usecount == 1);
+
+ return 0;
+}
+
+/* Configure an E1 timeslot according to the description in the ccu_descr */
+static int close_e1_timeslot(struct er_ccu_descr *ccu_descr)
+{
+ struct e1inp_line *e1_line;
+ struct e1_ts_descr *ts_descr;
+ int rc;
+
+ /* Find timeslot descriptor and check if the timeslot is still used by another subslot. */
+ ts_descr = ts_descr_from_ccu_descr(ccu_descr);
+ if (!ts_descr)
+ return -EINVAL;
+ if (ts_descr->usecount > 1) {
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP",
+ "E1 timeslot still in used by another subslot, leaving it open!\n");
+ ts_descr->usecount--;
+ return 0;
+ } else if (ts_descr->usecount == 0) {
+ /* This should not be as it means we close the timeslot too often. */
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "E1 timeslot already closed, leaving it as it is...\n");
+ return -EINVAL;
+ }
+
+ /* Find E1 line */
+ e1_line = e1inp_line_find(ccu_descr->e1_conn_pars->e1_nr);
+ if (!e1_line) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "no such E1 line!\n");
+ return -EINVAL;
+ }
+
+ /* Release E1 timeslot */
+ rc = e1inp_ts_config_none(&e1_line->ts[ccu_descr->e1_conn_pars->e1_ts - 1], e1_line);
+ if (rc < 0) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "failed to disable E1 timeslot!\n");
+ return -EINVAL;
+ }
+ rc = e1inp_line_update(e1_line);
+ if (rc < 0) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "failed to update E1 line!\n");
+ return -EINVAL;
+ }
+
+ ts_descr->usecount--;
+ OSMO_ASSERT(ts_descr->usecount == 0);
+
+ return 0;
+}
+
+int er_ccu_if_open(struct er_ccu_descr *ccu_descr)
+{
+ if (ccu_descr->link.ccu_connected) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP",
+ "cannot connect CCU since it is already connected -- ignored!\n");
+ return 0;
+ }
+
+ if (open_e1_timeslot(ccu_descr) < 0)
+ return -EINVAL;
+
+ if (add_i460_subslot(tall_ccu_ctx, ccu_descr) < 0)
+ return -EINVAL;
+
+ ccu_descr->link.ccu_connected = true;
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP", "CCU connected.\n");
+ return 0;
+}
+
+void er_ccu_if_close(struct er_ccu_descr *ccu_descr)
+{
+ if (!ccu_descr->link.ccu_connected) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP",
+ "cannot disconnect CCU since it is already disconnected -- ignored!\n");
+ return;
+ }
+
+ del_i460_subslot(ccu_descr);
+ close_e1_timeslot(ccu_descr);
+
+ ccu_descr->link.ccu_connected = false;
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "SETUP", "CCU disconnected.\n");
+}
+
+void er_ccu_if_tx(struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits)
+{
+ struct msgb *msg;
+ uint8_t *ptr;
+
+ if (!ccu_descr->link.ccu_connected) {
+ LOGPCCU(ccu_descr, LOGL_ERROR, "SETUP", "cannot TX block, CCU is disconnected -- ignored!\n");
+ return;
+ }
+
+ msg = msgb_alloc_c(tall_ccu_ctx, num_bits, "E1-I.460-PCU-IND-frame");
+ ptr = msgb_put(msg, num_bits);
+ memcpy(ptr, bits, num_bits);
+ LOGPCCU(ccu_descr, LOGL_DEBUG, "I.460-TX", "sending %u bits: %s...\n", msgb_length(msg),
+ osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg)));
+ osmo_i460_mux_enqueue(ccu_descr->link.schan, msg);
+}
+
+void er_ccu_if_init(void *ctx)
+{
+ libosmo_abis_init(ctx);
+ e1inp_vty_init();
+
+ tall_ccu_ctx = talloc_new(ctx);
+ memset(e1_lines, 0, sizeof(e1_lines));
+}
diff --git a/src/ericsson-rbs/er_ccu_if.h b/src/ericsson-rbs/er_ccu_if.h
new file mode 100644
index 00000000..40735301
--- /dev/null
+++ b/src/ericsson-rbs/er_ccu_if.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/abis/e1_input.h>
+#include "er_ccu_descr.h"
+
+int er_ccu_if_open(struct er_ccu_descr *ccu_descr);
+void er_ccu_if_close(struct er_ccu_descr *ccu_descr);
+void er_ccu_if_tx(struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits);
+void er_ccu_if_init(void *ctx);
diff --git a/src/ericsson-rbs/er_ccu_l1_if.c b/src/ericsson-rbs/er_ccu_l1_if.c
new file mode 100644
index 00000000..53ab7bd4
--- /dev/null
+++ b/src/ericsson-rbs/er_ccu_l1_if.c
@@ -0,0 +1,543 @@
+/*
+ * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <er_ccu_descr.h>
+#include <er_ccu_if.h>
+
+#include <string.h>
+#include <errno.h>
+
+#include <osmocom/pcu/pcuif_proto.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/abis.h>
+#include <osmocom/abis/e1_input.h>
+#include <osmocom/trau/trau_sync.h>
+#include <osmocom/trau/trau_pcu_ericsson.h>
+#include <osmocom/gsm/gsm0502.h>
+#include <osmocom/core/talloc.h>
+
+#include <bts.h>
+#include <pcu_l1_if.h>
+#include <pcu_l1_if_phy.h>
+
+extern void *tall_pcu_ctx;
+
+const uint8_t fn_inc_table[4] = { 4, 4, 5, 0 };
+const uint8_t blk_nr_table[4] = { 4, 4, 5, 0 };
+
+#define SYNC_CHECK_INTERVAL GSM_TDMA_SUPERFRAME * 8
+
+/* Subtrahend to convert Ericsson adjusted (block ending) fn to regular fn (uplink only) */
+#define AFN_SUBTRAHEND 3
+
+#define LOGPL1IF(ccu_descr, level, tag, fmt, args...) \
+ LOGP(DL1IF, level, "%s: PDCH(trx=%u,ts=%u) E1-line(line=%u,ts=%u,ss=%u) " fmt, \
+ tag, ccu_descr->pcu.trx_no, ccu_descr->pcu.ts, \
+ ccu_descr->e1_conn_pars->e1_nr, ccu_descr->e1_conn_pars->e1_ts, \
+ ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL ? 0 : ccu_descr->e1_conn_pars->e1_ts_ss, \
+ ## args)
+
+/* Calculate GPRS block number from frame number */
+static uint8_t fn_to_block_nr(uint32_t fn)
+{
+ /* Note: See also 3GPP TS 03.64 6.5.7.2.1,
+ * Mapping on the multiframe structure */
+
+ uint8_t rel_fn;
+ uint8_t super_block;
+ uint8_t local_block;
+
+ rel_fn = fn % 52;
+
+ /* Warn in case of frames that do not belong to a block */
+ if (rel_fn == 12 || rel_fn == 25 || rel_fn == 38 || rel_fn == 51)
+ LOGP(DL1IF, LOGL_ERROR, "Frame number is referencing invalid block!\n");
+
+ super_block = (rel_fn / 13);
+ local_block = rel_fn % 13 / 4;
+ return super_block * 3 + local_block;
+}
+
+static uint32_t fn_dl_advance(uint32_t fn, uint32_t n_blocks)
+{
+ uint32_t i;
+
+ uint8_t inc_fn;
+
+ for (i = 0; i < n_blocks; i++) {
+ inc_fn = fn_inc_table[(fn % 13) / 4];
+ fn = GSM_TDMA_FN_SUM(fn, inc_fn);
+ }
+
+ return fn;
+}
+
+static bool mac_block_is_noise(struct er_gprs_trau_frame *trau_frame)
+{
+ switch (trau_frame->u.ccu_data_ind.cs_hdr) {
+ case CS_OR_HDR_CS1:
+ case CS_OR_HDR_CS2:
+ case CS_OR_HDR_CS3:
+ case CS_OR_HDR_CS4:
+ if (!trau_frame->u.ccu_data_ind.u.gprs.parity_ok)
+ return true;
+ break;
+ case CS_OR_HDR_HDR1:
+ case CS_OR_HDR_HDR2:
+ case CS_OR_HDR_HDR3:
+ if (!trau_frame->u.ccu_data_ind.u.egprs.hdr_good)
+ return true;
+ if (!trau_frame->u.ccu_data_ind.u.egprs.data_good[0]
+ && !trau_frame->u.ccu_data_ind.u.egprs.data_good[1])
+ return true;
+ break;
+ case CS_OR_HDR_AB:
+ /* We are not interested in receiving access bursts. */
+ return true;
+ }
+
+ /* No noise, this block is interesting for us. */
+ return false;
+}
+
+static void log_data_ind(struct er_ccu_descr *ccu_descr, struct er_gprs_trau_frame *trau_frame, uint32_t afn_ul_comp,
+ uint32_t afn_dl_comp)
+{
+ switch (trau_frame->u.ccu_data_ind.cs_hdr) {
+ case CS_OR_HDR_CS1:
+ case CS_OR_HDR_CS2:
+ case CS_OR_HDR_CS3:
+ case CS_OR_HDR_CS4:
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-DATA-IND",
+ "tav=%u, dbe=%u, cs_hdr=%u, rx_lev=%u, est_acc_del_dev=%u,"
+ "block_qual=%u, parity_ok=%u, data=%s<==, afn_ul_comp=%u/%u\n", trau_frame->u.ccu_data_ind.tav,
+ trau_frame->u.ccu_data_ind.dbe, trau_frame->u.ccu_data_ind.cs_hdr,
+ trau_frame->u.ccu_data_ind.rx_lev, trau_frame->u.ccu_data_ind.est_acc_del_dev,
+ trau_frame->u.ccu_data_ind.u.gprs.block_qual, trau_frame->u.ccu_data_ind.u.gprs.parity_ok,
+ osmo_hexdump_nospc(trau_frame->u.ccu_data_ind.data, trau_frame->u.ccu_data_ind.data_len),
+ afn_ul_comp, afn_ul_comp % 52);
+ break;
+ case CS_OR_HDR_HDR1:
+ case CS_OR_HDR_HDR2:
+ case CS_OR_HDR_HDR3:
+ case CS_OR_HDR_AB:
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-DATA-IND",
+ "tav=%u, dbe=%u, cs_hdr=%u, rx_lev=%u, est_acc_del_dev=%u,"
+ "mean_bep=%u, cv_bep=%u, hdr_good=%u, data_good[0]=%u, data_good[1]=%u, data=%s<==, afn_ul_comp=%u/%u\n",
+ trau_frame->u.ccu_data_ind.tav, trau_frame->u.ccu_data_ind.dbe,
+ trau_frame->u.ccu_data_ind.cs_hdr, trau_frame->u.ccu_data_ind.rx_lev,
+ trau_frame->u.ccu_data_ind.est_acc_del_dev, trau_frame->u.ccu_data_ind.u.egprs.mean_bep,
+ trau_frame->u.ccu_data_ind.u.egprs.cv_bep, trau_frame->u.ccu_data_ind.u.egprs.hdr_good,
+ trau_frame->u.ccu_data_ind.u.egprs.data_good[0],
+ trau_frame->u.ccu_data_ind.u.egprs.data_good[1],
+ osmo_hexdump_nospc(trau_frame->u.ccu_data_ind.data, trau_frame->u.ccu_data_ind.data_len),
+ afn_ul_comp, afn_ul_comp % 52);
+ }
+}
+
+/* Receive block from CCU */
+static void er_ccu_rx_cb(struct er_ccu_descr *ccu_descr, const ubit_t *bits, unsigned int num_bits)
+{
+ int rc;
+ struct er_gprs_trau_frame trau_frame;
+ uint8_t inc_ul;
+ uint8_t inc_dl;
+ uint32_t afn_ul;
+ uint32_t afn_dl;
+ uint32_t afn_ul_comp;
+ uint32_t afn_dl_comp;
+ struct pcu_l1_meas meas = { 0 };
+ struct gprs_rlcmac_bts *bts;
+ struct gprs_rlcmac_pdch *pdch;
+
+ /* Compute the current frame numbers from the last frame number */
+ inc_ul = fn_inc_table[(ccu_descr->sync.last_afn_ul % 13) / 4];
+ inc_dl = fn_inc_table[(ccu_descr->sync.last_afn_dl % 13) / 4];
+ afn_ul = GSM_TDMA_FN_SUM(ccu_descr->sync.last_afn_ul, inc_ul);
+ afn_dl = GSM_TDMA_FN_SUM(ccu_descr->sync.last_afn_dl, inc_dl);
+
+ /* Compute compensated frame numbers. This will be the framenumbers we
+ * will use to exchange blocks with the PCU code. The following applies:
+ *
+ * 1. The uplink related frame numbers sent by the ericsson CCU refer to the end of a block. This is
+ * compensated by subtracting three frames.
+ * 2. The CCU downlink frame number runs one block past the uplink frame number. This needs to be
+ * compesated as well (+1).
+ * 3. The difference between the local (PCU) and the returned (CCU) pseq counter value is the number of blocks
+ * that the PCU must
+ * shift its downlink alignment in order to compensate the link latency between PCU and CCU. */
+ afn_ul_comp = GSM_TDMA_FN_SUB(afn_ul, AFN_SUBTRAHEND);
+ afn_dl_comp = afn_dl;
+ afn_dl_comp = fn_dl_advance(afn_dl_comp, GSM_TDMA_FN_DIFF(ccu_descr->sync.pseq_pcu, ccu_descr->sync.pseq_ccu) + 1);
+
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC",
+ "afn_ul=%u/%u, afn_dl=%u/%u, afn_diff=%u => afn_ul_comp=%u/%u, afn_dl_comp=%u/%u, afn_diff_comp=%u\n",
+ afn_ul, afn_ul % 52, afn_dl, afn_dl % 52, GSM_TDMA_FN_DIFF(afn_ul, afn_dl), afn_ul_comp,
+ afn_ul_comp % 52, afn_dl_comp, afn_dl_comp % 52, GSM_TDMA_FN_DIFF(afn_ul_comp, afn_dl_comp));
+
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC", "pseq_pcu=%u, pseq_ccu=%u, pseq_diff=%u\n",
+ ccu_descr->sync.pseq_pcu, ccu_descr->sync.pseq_ccu, GSM_TDMA_FN_DIFF(ccu_descr->sync.pseq_pcu, ccu_descr->sync.pseq_ccu));
+
+ /* Decode indication from CCU */
+ if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL)
+ rc = er_gprs_trau_frame_decode_64k(&trau_frame, bits);
+ else
+ rc = er_gprs_trau_frame_decode_16k(&trau_frame, bits);
+ if (rc < 0) {
+ LOGPL1IF(ccu_descr, LOGL_ERROR, "CCU-XXXX-IND",
+ "unable to decode uplink TRAU frame, afn_ul_comp=%u/%u\n", afn_ul_comp, afn_ul_comp % 52);
+
+ /* Report to the CCU that there is an issue with uplink TRAU frames, the CCU will then send
+ * a CCU-SYNC-IND within the next TRAU frame, so we can check if we are still in sync and trigger
+ * synchronization procedure if necessary. */
+ ccu_descr->sync.ul_frame_err = true;
+ goto skip;
+ }
+
+ switch (trau_frame.type) {
+ case ER_GPRS_TRAU_FT_SYNC:
+ if (trau_frame.u.ccu_sync_ind.pseq != 0x3FFFFF) {
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC-IND",
+ "tav=%u, dbe=%u, dfe=%u, pseq=%u, afn_ul=%u, afn_dl=%u\n",
+ trau_frame.u.ccu_sync_ind.tav, trau_frame.u.ccu_sync_ind.dbe,
+ trau_frame.u.ccu_sync_ind.dfe, trau_frame.u.ccu_sync_ind.pseq,
+ trau_frame.u.ccu_sync_ind.afn_ul, trau_frame.u.ccu_sync_ind.afn_dl);
+
+ /* Synchronize the current CCU PSEQ state */
+ ccu_descr->sync.pseq_ccu = trau_frame.u.ccu_sync_ind.pseq;
+ } else {
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "CCU-SYNC-IND",
+ "tav=%u, dbe=%u, dfe=%u, pseq=(none), afn_ul=%u, afn_dl=%u\n",
+ trau_frame.u.ccu_sync_ind.tav, trau_frame.u.ccu_sync_ind.dbe,
+ trau_frame.u.ccu_sync_ind.dfe, trau_frame.u.ccu_sync_ind.afn_ul,
+ trau_frame.u.ccu_sync_ind.afn_dl);
+ }
+
+ ccu_descr->sync.tav = trau_frame.u.ccu_sync_ind.tav;
+
+ /* Check if we are in sync with the CCU, if not trigger synchronization procedure */
+ if (afn_ul != trau_frame.u.ccu_sync_ind.afn_ul || afn_dl != trau_frame.u.ccu_sync_ind.afn_dl) {
+ if (afn_ul != trau_frame.u.ccu_sync_ind.afn_ul)
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND",
+ "afn_ul=%u (computed) != afn_ul=%u (sync-ind) => delta=%u\n", afn_ul,
+ trau_frame.u.ccu_sync_ind.afn_ul,
+ GSM_TDMA_FN_DIFF(afn_ul, trau_frame.u.ccu_sync_ind.afn_ul));
+ if (afn_dl != trau_frame.u.ccu_sync_ind.afn_dl)
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND",
+ "afn_dl=%u (computed) != afn_dl=%u (sync-ind) => delta=%u\n", afn_dl,
+ trau_frame.u.ccu_sync_ind.afn_dl,
+ GSM_TDMA_FN_DIFF(afn_dl, trau_frame.u.ccu_sync_ind.afn_dl));
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND",
+ "FN jump detected, lost sync with CCU -- (re)synchronizing...\n");
+ ccu_descr->sync.ccu_synced = false;
+ } else {
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "CCU-SYNC-IND", "in sync with CCU\n");
+ ccu_descr->sync.ccu_synced = true;
+ }
+
+ /* Overwrite calculated afn_ul and afn_dl with the actual values from the SYNC indication */
+ afn_ul = trau_frame.u.ccu_sync_ind.afn_ul;
+ afn_dl = trau_frame.u.ccu_sync_ind.afn_dl;
+
+ break;
+ case ER_GPRS_TRAU_FT_DATA:
+
+ ccu_descr->sync.tav = trau_frame.u.ccu_data_ind.tav;
+
+ /* Ignore all data indications that contain only noise */
+ if (mac_block_is_noise(&trau_frame))
+ break;
+
+ log_data_ind(ccu_descr, &trau_frame, afn_ul_comp, afn_dl_comp);
+
+ /* Hand received MAC block into PCU */
+ bts = gprs_pcu_get_bts_by_nr(the_pcu, ccu_descr->pcu.bts_nr);
+ if (!bts)
+ break;
+ meas.have_rssi = 1;
+ meas.rssi = rxlev2dbm(trau_frame.u.ccu_data_ind.rx_lev);
+ meas.have_link_qual = 1;
+ meas.link_qual = trau_frame.u.ccu_data_ind.u.gprs.block_qual;
+ pdch = &bts->trx[ccu_descr->pcu.trx_no].pdch[ccu_descr->pcu.ts];
+ rc = pcu_rx_data_ind_pdtch(bts, pdch, trau_frame.u.ccu_data_ind.data,
+ trau_frame.u.ccu_data_ind.data_len, afn_ul_comp, &meas);
+ break;
+ default:
+ LOGPL1IF(ccu_descr, LOGL_ERROR, "CCU-XXXX-IND", "unhandled CCU indication!\n");
+ }
+
+skip:
+ if (ccu_descr->sync.ccu_synced) {
+ bts = gprs_pcu_get_bts_by_nr(the_pcu, ccu_descr->pcu.bts_nr);
+ if (bts) {
+ /* The PCU timing is locked to the uplink fame number. The downlink frame number is advanced
+ * into the future so that the line latency is compensated and the frame arrives at the right
+ * point in time. */
+ pdch = &bts->trx[ccu_descr->pcu.trx_no].pdch[ccu_descr->pcu.ts];
+ pcu_rx_block_time(bts, pdch->trx->arfcn, afn_ul_comp, ccu_descr->pcu.ts);
+ rc = pcu_rx_rts_req_pdtch(bts, ccu_descr->pcu.trx_no, ccu_descr->pcu.ts, afn_dl_comp,
+ fn_to_block_nr(afn_dl_comp));
+ }
+ }
+
+ /* We do not receive sync indications in every cycle. When traffic is transferred we won't get frame numbers
+ * from the CCU. In this case we must update the last_afn_ul/dl values from the computed frame numbers
+ * (see above) */
+ ccu_descr->sync.last_afn_ul = afn_ul;
+ ccu_descr->sync.last_afn_dl = afn_dl;
+ ccu_descr->sync.pseq_pcu++;
+ ccu_descr->sync.pseq_ccu++;
+}
+
+static void er_ccu_empty_cb(struct er_ccu_descr *ccu_descr)
+{
+ struct er_gprs_trau_frame trau_frame;
+ ubit_t trau_frame_encoded[ER_GPRS_TRAU_FRAME_LEN_64K];
+ int rc;
+
+ memset(&trau_frame, 0, sizeof(trau_frame));
+ trau_frame.u.pcu_sync_ind.pseq = ccu_descr->sync.pseq_pcu;
+ trau_frame.u.pcu_sync_ind.tav = ccu_descr->sync.tav;
+ trau_frame.u.pcu_sync_ind.fn_ul = 0x3FFFFF;
+ trau_frame.u.pcu_sync_ind.fn_dl = 0x3FFFFF;
+ trau_frame.u.pcu_sync_ind.fn_ss = 0x3FFFFF;
+ trau_frame.u.pcu_sync_ind.ls = 0x3FFFFF;
+ trau_frame.u.pcu_sync_ind.ss = 0x3FFFFF;
+ trau_frame.type = ER_GPRS_TRAU_FT_SYNC;
+
+ if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL)
+ rc = er_gprs_trau_frame_encode_64k(trau_frame_encoded, &trau_frame);
+ else
+ rc = er_gprs_trau_frame_encode_16k(trau_frame_encoded, &trau_frame);
+ if (rc < 0) {
+ LOGPL1IF(ccu_descr, LOGL_ERROR, "PCU-SYNC-IND", "unable to encode TRAU frame\n");
+ return;
+ }
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "PCU-SYNC-IND", "pseq=%u, tav=%u\n",
+ trau_frame.u.pcu_sync_ind.pseq, trau_frame.u.pcu_sync_ind.tav);
+ er_ccu_if_tx(ccu_descr, trau_frame_encoded, rc);
+
+ /* Make sure timing adjustment value is reset after use */
+ ccu_descr->sync.tav = TIME_ADJ_NONE;
+}
+
+/* use the length of the block to determine the coding scheme */
+static int cs_hdr_from_len(uint8_t len)
+{
+ switch (len) {
+ case 23:
+ return CS_OR_HDR_CS1;
+ case 34:
+ return CS_OR_HDR_CS2;
+ case 40:
+ return CS_OR_HDR_CS3;
+ case 54:
+ return CS_OR_HDR_CS4;
+ case 27:
+ case 33:
+ case 42:
+ case 49:
+ return CS_OR_HDR_HDR3;
+ case 60:
+ case 78:
+ return CS_OR_HDR_HDR2;
+ case 118:
+ case 142:
+ case 154:
+ return CS_OR_HDR_HDR1;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* send packet data request to L1 */
+int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
+ uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
+{
+ struct er_trx_descr *trx_descr = obj;
+ struct er_ccu_descr *ccu_descr;
+ struct er_gprs_trau_frame trau_frame;
+ ubit_t trau_frame_encoded[ER_GPRS_TRAU_FRAME_LEN_64K];
+ struct gprs_rlcmac_bts *bts;
+ int rc;
+
+ /* Make sure that the CCU is synchronized and connected. */
+ if (!trx_descr) {
+ LOGP(DL1IF, LOGL_ERROR, "PCU-DATA-IND: PDCH(ts=%u, arfcn=%u) no TRX context, tossing MAC block...\n",
+ ts, arfcn);
+ return -EINVAL;
+ }
+
+ ccu_descr = &trx_descr->ts_ccu_descr[ts];
+
+ if (!ccu_descr->link.ccu_connected) {
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "PCU-DATA-IND", "CCU not connected, tossing MAC block...\n");
+ return -EINVAL;
+ }
+ if (!ccu_descr->sync.ccu_synced) {
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "PCU-DATA-IND", "CCU not synchronized, tossing MAC block...\n");
+ return -EINVAL;
+ }
+
+ /* Hand received MAC block into PCU */
+ bts = gprs_pcu_get_bts_by_nr(the_pcu, ccu_descr->pcu.bts_nr);
+ if (!bts) {
+ LOGPL1IF(ccu_descr, LOGL_NOTICE, "PCU-DATA-IND", "no BTS, tossing MAC block...\n");
+ return -EINVAL;
+ }
+
+ memset(&trau_frame, 0, sizeof(trau_frame));
+ trau_frame.type = ER_GPRS_TRAU_FT_DATA;
+
+ rc = cs_hdr_from_len(len);
+ if (rc < 0) {
+ LOGPL1IF(ccu_descr, LOGL_ERROR, "PCU-DATA-IND",
+ "unable to encode TRAU frame, invalid CS or MCS value set\n");
+ return -EINVAL;
+ }
+ trau_frame.u.pcu_data_ind.cs_hdr = (enum er_cs_or_hdr)rc;
+ trau_frame.u.pcu_data_ind.tav = ccu_descr->sync.tav;
+ trau_frame.u.pcu_data_ind.ul_frame_err = ccu_descr->sync.ul_frame_err;
+ if (bts->mcs_mask)
+ trau_frame.u.pcu_data_ind.ul_chan_mode = ER_UL_CHMOD_NB_UNKN;
+ else
+ trau_frame.u.pcu_data_ind.ul_chan_mode = ER_UL_CHMOD_NB_GMSK;
+ OSMO_ASSERT(len < sizeof(trau_frame.u.pcu_data_ind.data));
+ memcpy(trau_frame.u.pcu_data_ind.data, data, len);
+
+ /* Regulary ignore one MAC block in uplink. The CCU will then send one CCU-SYNC-IND instead. We use this
+ * indication to check whether we are still in sync with the CCU. */
+ if (fn % SYNC_CHECK_INTERVAL == 0)
+ trau_frame.u.pcu_data_ind.ul_chan_mode = ER_UL_CHMOD_VOID;
+
+ if (ccu_descr->e1_conn_pars->e1_ts_ss == E1_SUBSLOT_FULL)
+ rc = er_gprs_trau_frame_encode_64k(trau_frame_encoded, &trau_frame);
+ else
+ rc = er_gprs_trau_frame_encode_16k(trau_frame_encoded, &trau_frame);
+ if (rc < 0) {
+ LOGPL1IF(ccu_descr, LOGL_ERROR, "PCU-DATA-IND", "unable to encode TRAU frame\n");
+ return -EINVAL;
+ }
+ LOGPL1IF(ccu_descr, LOGL_DEBUG, "PCU-DATA-IND",
+ "tav=%u, ul_frame_err=%u, cs_hdr=%u, ul_chan_mode=%u, atten_db=%u, timing_offset=%u,"
+ " data=%s==>, fn=%u/%u (comp)\n", trau_frame.u.pcu_data_ind.tav,
+ trau_frame.u.pcu_data_ind.ul_frame_err, trau_frame.u.pcu_data_ind.cs_hdr,
+ trau_frame.u.pcu_data_ind.ul_chan_mode, trau_frame.u.pcu_data_ind.atten_db,
+ trau_frame.u.pcu_data_ind.timing_offset, osmo_hexdump_nospc(trau_frame.u.pcu_data_ind.data, len), fn,
+ fn % 52);
+ er_ccu_if_tx(ccu_descr, trau_frame_encoded, rc);
+
+ /* Make sure timing adjustment value is reset after use */
+ ccu_descr->sync.tav = TIME_ADJ_NONE;
+ ccu_descr->sync.ul_frame_err = false;
+
+ return 0;
+}
+
+void *l1if_open_trx(uint8_t bts_nr, uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
+{
+ struct er_trx_descr *trx_descr;
+ unsigned int i;
+
+ /* Note: We do not have enough information to really open anything at
+ * this point. We will just create the TRX context and fill it wit basic
+ * CCU context (one for each TS) */
+
+ trx_descr = talloc_zero(tall_pcu_ctx, struct er_trx_descr);
+ OSMO_ASSERT(trx_descr);
+
+ for (i = 0; i < ARRAY_SIZE(trx_descr->ts_ccu_descr); i++) {
+ trx_descr->ts_ccu_descr[i].er_ccu_rx_cb = er_ccu_rx_cb;
+ trx_descr->ts_ccu_descr[i].er_ccu_empty_cb = er_ccu_empty_cb;
+ trx_descr->ts_ccu_descr[i].pcu.trx_no = trx_no;
+ trx_descr->ts_ccu_descr[i].pcu.bts_nr = bts_nr;
+ trx_descr->ts_ccu_descr[i].pcu.ts = i;
+ }
+
+ return trx_descr;
+}
+
+int l1if_close_trx(void *obj)
+{
+ struct er_trx_descr *trx_descr = obj;
+ unsigned int i;
+
+ if (!trx_descr) {
+ LOGP(DL1IF, LOGL_ERROR, "PCU-DATA-IND: no TRX context, cannot close unknown TRX...\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(trx_descr->ts_ccu_descr); i++)
+ er_ccu_if_close(&trx_descr->ts_ccu_descr[i]);
+
+ talloc_free(trx_descr);
+ return 0;
+}
+
+int l1if_connect_pdch(void *obj, uint8_t ts)
+{
+ struct er_trx_descr *trx_descr = obj;
+ struct er_ccu_descr *ccu_descr;
+ int rc;
+
+ if (!trx_descr) {
+ LOGP(DL1IF, LOGL_ERROR, "SETUP: PDCH(ts=%u) no CCU context, TRX never opened before?\n", ts);
+ return -EINVAL;
+ }
+
+ ccu_descr = &trx_descr->ts_ccu_descr[ts];
+
+ rc = pcu_l1if_get_e1_ccu_conn_pars(&ccu_descr->e1_conn_pars, ccu_descr->pcu.bts_nr, ccu_descr->pcu.trx_no,
+ ccu_descr->pcu.ts);
+ if (rc < 0) {
+ LOGPL1IF(ccu_descr, LOGL_ERROR, "SETUP", "cannot find E1 connection parameters for CCU\n");
+ return -EINVAL;
+ }
+
+ rc = er_ccu_if_open(ccu_descr);
+ if (rc < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int l1if_disconnect_pdch(void *obj, uint8_t ts)
+{
+ struct er_trx_descr *trx_descr = obj;
+ struct er_ccu_descr *ccu_descr;
+
+ if (!trx_descr) {
+ LOGP(DL1IF, LOGL_ERROR, "SETUP: PDCH(ts=%u) no TRX context, TRX never opened before?\n", ts);
+ return -EINVAL;
+ }
+
+ ccu_descr = &trx_descr->ts_ccu_descr[ts];
+
+ er_ccu_if_close(ccu_descr);
+
+ return 0;
+}
+
+int l1if_init(void)
+{
+ er_ccu_if_init(tall_pcu_ctx);
+ return 0;
+}
diff --git a/src/gprs_bssgp_pcu.c b/src/gprs_bssgp_pcu.c
index 1fcacdb1..2c5a97a7 100644
--- a/src/gprs_bssgp_pcu.c
+++ b/src/gprs_bssgp_pcu.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_rlcmac.h>
@@ -39,6 +35,8 @@
#include "tbf_dl.h"
#include "llc.h"
#include "gprs_rlcmac.h"
+#include "bts_pch_timer.h"
+#include "alloc_algo.h"
/* Tuning parameters for BSSGP flow control */
#define FC_DEFAULT_LIFE_TIME_SECS 10 /* experimental value, 10s */
@@ -107,12 +105,8 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
uint8_t egprs_ms_class = 0;
int rc;
MS_Radio_Access_capability_t rac;
- /* TODO: is it really necessary to initialize this as a "000" IMSI? It seems, the function should just return an
- * error if no IMSI IE was found. */
- struct osmo_mobile_identity mi_imsi = {
- .type = GSM_MI_TYPE_TMSI,
- };
- OSMO_STRLCPY_ARRAY(mi_imsi.imsi, "000");
+ const char *imsi = NULL;
+ struct osmo_mobile_identity mi_imsi;
budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
tlli = ntohl(budh->tlli);
@@ -143,6 +137,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
LOGP(DBSSGP, LOGL_NOTICE, "Failed to parse IMSI IE (rc=%d)\n", rc);
return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
}
+ imsi = &mi_imsi.imsi[0];
}
/* parse ms radio access capability */
@@ -179,10 +174,11 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
"TLLI (old) IE\n");
}
- LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, mi_imsi.imsi, len);
+ LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n",
+ tlli, imsi ? : "none", len);
- return dl_tbf_handle(the_pcu->bssgp.bts, tlli, tlli_old, mi_imsi.imsi,
- ms_class, egprs_ms_class, delay_csec, data, len);
+ return dl_tbf_handle(the_pcu->bssgp.bts, tlli, tlli_old, imsi, ms_class,
+ egprs_ms_class, delay_csec, data, len);
}
/* 3GPP TS 48.018 Table 10.3.2. Returns 0 on success, suggested BSSGP cause otherwise */
@@ -248,9 +244,9 @@ static int gprs_bssgp_pcu_rx_paging_cs(struct msgb *msg, const struct tlv_parsed
* target MS is using. */
llist_for_each_entry(bts, &the_pcu->bts_list, list) {
/* TODO: Match by TMSI before IMSI if present?! */
- ms = bts_ms_by_tlli(bts, req.tlli, req.tlli);
+ ms = bts_get_ms_by_tlli(bts, req.tlli, req.tlli);
if (!ms && req.mi_imsi_present)
- ms = bts_ms_by_imsi(bts, req.mi_imsi.imsi);
+ ms = bts_get_ms_by_imsi(bts, req.mi_imsi.imsi);
bts_add_paging(bts, &req, ms);
}
@@ -293,7 +289,6 @@ static int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, const struct tlv_parsed
struct osmo_mobile_identity mi_imsi;
struct osmo_mobile_identity paging_mi;
struct gprs_rlcmac_bts *bts;
- uint16_t pgroup;
int rc;
rate_ctr_inc(rate_ctr_group_get_ctr(the_pcu->bssgp.ctrs, SGSN_CTR_RX_PAGING_PS));
@@ -308,18 +303,20 @@ static int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, const struct tlv_parsed
LOGP(DBSSGP, LOGL_NOTICE, "Failed to parse IMSI IE (rc=%d)\n", rc);
return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
}
- pgroup = imsi2paging_group(mi_imsi.imsi);
- if (pgroup > 999) {
- LOGP(DBSSGP, LOGL_NOTICE, "Failed to compute IMSI %s paging group\n", mi_imsi.imsi);
- return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg);
- }
if ((rc = get_paging_ps_mi(&paging_mi, tp)) > 0)
return bssgp_tx_status((enum gprs_bssgp_cause) rc, NULL, msg);
/* FIXME: look if MS is attached a specific BTS and then only page on that one? */
llist_for_each_entry(bts, &the_pcu->bts_list, list) {
- gprs_rlcmac_paging_request(bts, &paging_mi, pgroup);
+ if (bts_pch_timer_get_by_imsi(bts, mi_imsi.imsi)) {
+ LOGP(DBSSGP, LOGL_INFO, "PS-Paging request already pending for IMSI=%s\n", mi_imsi.imsi);
+ bts_do_rate_ctr_inc(bts, CTR_PCH_REQUESTS_ALREADY);
+ continue;
+ }
+ if (gprs_rlcmac_paging_request(bts, &paging_mi, mi_imsi.imsi) < 0)
+ continue;
+ bts_pch_timer_start(bts, &paging_mi, mi_imsi.imsi);
}
return 0;
}
@@ -780,7 +777,7 @@ static unsigned count_pdch(const struct gprs_rlcmac_bts *bts)
for (ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) {
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
- if (pdch->m_is_enabled)
+ if (pdch_is_enabled(pdch))
num_pdch += 1;
}
}
@@ -1118,7 +1115,8 @@ static int ns_configure_nse(struct gprs_rlcmac_bts *bts,
if (!(valid & (1 << i)))
continue;
- if (!gprs_ns2_ip_bind_by_sockaddr(the_pcu->nsi, &local[i])) {
+ bind[i] = gprs_ns2_ip_bind_by_sockaddr(the_pcu->nsi, &local[i]);
+ if (!bind[i]) {
snprintf(name, sizeof(name), "pcu%u", i);
rc = gprs_ns2_ip_bind(the_pcu->nsi, name, &local[i], 0, &bind[i]);
if (rc < 0) {
diff --git a/src/gprs_bssgp_pcu.h b/src/gprs_bssgp_pcu.h
index 907e6663..0c0f0919 100644
--- a/src/gprs_bssgp_pcu.h
+++ b/src/gprs_bssgp_pcu.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef GPRS_BSSGP_PCU_H
diff --git a/src/gprs_bssgp_rim.c b/src/gprs_bssgp_rim.c
index f1679a6b..aa636b66 100644
--- a/src/gprs_bssgp_rim.c
+++ b/src/gprs_bssgp_rim.c
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <osmocom/gprs/gprs_bssgp.h>
@@ -156,7 +152,7 @@ static int handle_ran_info_response_nacc(const struct bssgp_ran_inf_app_cont_nac
}
entry = si_cache_add(bts->pcu->si_cache, &nacc->reprt_cell, &val);
- llist_for_each(tmp, bts_ms_list(bts)) {
+ llist_for_each(tmp, &bts->ms_list) {
struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
if (ms->nacc && nacc_fsm_is_waiting_si_resolution(ms->nacc, &nacc->reprt_cell))
osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_SI, entry);
diff --git a/src/gprs_bssgp_rim.h b/src/gprs_bssgp_rim.h
index 5fac56f1..cf46b0ed 100644
--- a/src/gprs_bssgp_rim.h
+++ b/src/gprs_bssgp_rim.h
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
diff --git a/src/gprs_codel.c b/src/gprs_codel.c
index 7c3b2d42..9c5737f6 100644
--- a/src/gprs_codel.c
+++ b/src/gprs_codel.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "gprs_codel.h"
diff --git a/src/gprs_codel.h b/src/gprs_codel.h
index 3ad7efce..10ba91f9 100644
--- a/src/gprs_codel.h
+++ b/src/gprs_codel.h
@@ -19,10 +19,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
diff --git a/src/gprs_debug.cpp b/src/gprs_debug.c
index 0cbc4880..ba9aeec1 100644
--- a/src/gprs_debug.cpp
+++ b/src/gprs_debug.c
@@ -1,7 +1,8 @@
-/* gprs_debug.cpp
+/* gprs_debug.c
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
+ * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -12,16 +13,10 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-extern "C" {
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
-}
#include <gprs_debug.h>
@@ -84,6 +79,13 @@ static const struct log_info_cat default_categories[] = {
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
+ [DMS] = {
+ .name = "DMS",
+ .color = "\033[1;34m",
+ .description = "Mobile Station (MS)",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
[DTBF] = {
.name = "DTBF",
.color = "\033[1;34m",
@@ -133,6 +135,13 @@ static const struct log_info_cat default_categories[] = {
.loglevel = LOGL_NOTICE,
.enabled = 1,
},
+ [DE1] = {
+ .name = "DE1",
+ .color = "\033[1;31m",
+ .description = "E1 line handling",
+ .loglevel = LOGL_NOTICE,
+ .enabled = 1,
+ },
};
static int filter_fn(const struct log_context *ctx,
diff --git a/src/gprs_debug.h b/src/gprs_debug.h
index cebeabce..b66de90c 100644
--- a/src/gprs_debug.h
+++ b/src/gprs_debug.h
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -23,9 +19,6 @@
extern "C" {
#endif
#include <osmocom/core/logging.h>
-#ifdef __cplusplus
-};
-#endif
/* we used to have DBSSGP definded in each application, and applications telling
* libosmogb which sub-system to use. That creates problems and has been deprecated */
@@ -41,6 +34,7 @@ enum {
DRLCMACUL,
DRLCMACSCHED,
DRLCMACMEAS,
+ DMS,
DTBF,
DTBFDL,
DTBFUL,
@@ -48,7 +42,12 @@ enum {
DPCU,
DNACC,
DRIM,
+ DE1,
aDebug_LastEntry
};
extern const struct log_info gprs_log_info;
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/src/gprs_ms.c b/src/gprs_ms.c
index 0d6be4d5..ff7316b9 100644
--- a/src/gprs_ms.c
+++ b/src/gprs_ms.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
@@ -27,6 +23,8 @@
#include "gprs_codel.h"
#include "pcu_utils.h"
#include "nacc_fsm.h"
+#include "tbf_ul_ack_fsm.h"
+#include "alloc_algo.h"
#include <time.h>
@@ -64,72 +62,108 @@ static int64_t now_msec()
return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
}
-void gprs_default_cb_ms_idle(struct GprsMs *ms)
-{
- talloc_free(ms);
-}
+static void ms_becomes_idle(struct GprsMs *ms);
-void gprs_default_cb_ms_active(struct GprsMs *ms)
+static int ms_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
{
- /* do nothing */
-}
+ struct GprsMs *ms = e->use_count->talloc_object;
+ int32_t total;
+ int level;
+ char buf[1024];
-static struct gpr_ms_callback gprs_default_cb = {
- .ms_idle = gprs_default_cb_ms_idle,
- .ms_active = gprs_default_cb_ms_active,
-};
+ if (!e->use)
+ return -EINVAL;
+
+ total = osmo_use_count_total(&ms->use_count);
+
+ if (total == 0
+ || (total == 1 && old_use_count == 0 && e->count == 1))
+ level = LOGL_INFO;
+ else
+ level = LOGL_DEBUG;
+
+
+ LOGPSRC(DMS, level, file, line, "%s: %s %s: now used by %s\n",
+ ms_name(ms),
+ (e->count - old_use_count) > 0 ? "+" : "-", e->use,
+ (osmo_use_count_to_str_buf(buf, sizeof(buf), &ms->use_count), buf));
+
+ if (e->count < 0)
+ return -ERANGE;
+
+ if (total == 0) {
+ OSMO_ASSERT(ms_is_idle(ms));
+ ms_becomes_idle(ms);
+ }
+ return 0;
+}
static void ms_release_timer_cb(void *data)
{
struct GprsMs *ms = (struct GprsMs *) data;
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
+ LOGPMS(ms, DMS, LOGL_INFO, "Release timer expired\n");
- if (ms->timer.data) {
- ms->timer.data = NULL;
- ms_unref(ms);
- }
+ /* Finally free the MS after being idle for a while according to config */
+ talloc_free(ms);
+}
+
+static void ms_llc_timer_cb(void *_ms)
+{
+ struct GprsMs *ms = _ms;
+ struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
+
+ if (!dl_tbf)
+ return;
+ if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
+ return;
+
+ LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
+
+ dl_tbf_request_dl_ack(dl_tbf);
}
static int ms_talloc_destructor(struct GprsMs *ms);
-struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
+struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, const char *use_ref)
{
struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
+ OSMO_ASSERT(bts);
talloc_set_destructor(ms, ms_talloc_destructor);
+ llist_add(&ms->list, &bts->ms_list);
+ bts_stat_item_inc(bts, STAT_MS_PRESENT);
+
ms->bts = bts;
- ms->cb = gprs_default_cb;
- ms->tlli = tlli;
+ ms->tlli = GSM_RESERVED_TMSI;
ms->new_ul_tlli = GSM_RESERVED_TMSI;
ms->new_dl_tlli = GSM_RESERVED_TMSI;
ms->ta = GSM48_TA_INVALID;
ms->current_cs_ul = UNKNOWN;
ms->current_cs_dl = UNKNOWN;
- ms->is_idle = true;
- INIT_LLIST_HEAD(&ms->list);
INIT_LLIST_HEAD(&ms->old_tbfs);
+ ms->use_count = (struct osmo_use_count){
+ .talloc_object = ms,
+ .use_cb = ms_use_cb,
+ };
+
int codel_interval = LLC_CODEL_USE_DEFAULT;
- LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
+ LOGP(DMS, LOGL_INFO, "Creating MS object\n");
ms->imsi[0] = '\0';
- memset(&ms->timer, 0, sizeof(ms->timer));
- ms->timer.cb = ms_release_timer_cb;
- llc_queue_init(&ms->llc_queue);
+ osmo_timer_setup(&ms->release_timer, ms_release_timer_cb, ms);
+ llc_queue_init(&ms->llc_queue, ms);
+ memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
+ osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
ms_set_mode(ms, GPRS);
- if (ms->bts)
- codel_interval = the_pcu->vty.llc_codel_interval_msec;
+ codel_interval = the_pcu->vty.llc_codel_interval_msec;
+ if (codel_interval == LLC_CODEL_USE_DEFAULT)
+ codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
+ llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
- if (codel_interval) {
- if (codel_interval == LLC_CODEL_USE_DEFAULT)
- codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
- ms->codel_state = talloc(ms, struct gprs_codel);
- gprs_codel_init(ms->codel_state);
- gprs_codel_set_interval(ms->codel_state, codel_interval);
- }
ms->last_cs_not_low = now_msec();
ms->app_info_pending = false;
@@ -137,6 +171,8 @@ struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
if (!ms->ctrs)
goto free_ret;
+ if (use_ref)
+ ms_ref(ms, use_ref);
return ms;
free_ret:
talloc_free(ms);
@@ -147,99 +183,79 @@ static int ms_talloc_destructor(struct GprsMs *ms)
{
struct llist_item *pos, *tmp;
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
+ LOGPMS(ms, DMS, LOGL_INFO, "Destroying MS object\n");
+
+ bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
+ llist_del(&ms->list);
ms_set_reserved_slots(ms, NULL, 0, 0);
- if (osmo_timer_pending(&ms->timer))
- osmo_timer_del(&ms->timer);
+ osmo_timer_del(&ms->release_timer);
if (ms->ul_tbf) {
- tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
- ms->ul_tbf = NULL;
+ tbf_free(ul_tbf_as_tbf(ms->ul_tbf));
+ OSMO_ASSERT(ms->ul_tbf == NULL);
}
if (ms->dl_tbf) {
- tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
- ms->dl_tbf = NULL;
+ tbf_free(dl_tbf_as_tbf(ms->dl_tbf));
+ OSMO_ASSERT(ms->dl_tbf == NULL);
}
llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
- tbf_set_ms(tbf, NULL);
+ tbf_free(tbf);
}
llc_queue_clear(&ms->llc_queue, ms->bts);
+ osmo_timer_del(&ms->llc_timer);
if (ms->ctrs)
rate_ctr_group_free(ms->ctrs);
return 0;
}
-
-void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
+/* MS has no attached TBFs anymore. */
+static void ms_becomes_idle(struct GprsMs *ms)
{
- if (cb)
- ms->cb = *cb;
- else
- ms->cb = gprs_default_cb;
-}
+ unsigned long delay_rel_sec = osmo_tdef_get(ms->bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1);
-static void ms_update_status(struct GprsMs *ms)
-{
- if (ms->ref > 0)
- return;
+ osmo_gettimeofday(&ms->tv_idle_start, NULL);
- if (ms_is_idle(ms) && !ms->is_idle) {
- ms->is_idle = true;
- ms->cb.ms_idle(ms);
- /* this can be deleted by now, do not access it */
+ ms_set_reserved_slots(ms, NULL, 0, 0);
+ ms->first_common_ts = NULL;
+
+ /* Immediate free():
+ * Skip delaying free() through release timer if delay is configured to be 0.
+ * This is useful for synced freed during unit tests.
+ */
+ if (delay_rel_sec == 0) {
+ talloc_free(ms);
return;
}
- if (!ms_is_idle(ms) && ms->is_idle) {
- ms->is_idle = false;
- ms->cb.ms_active(ms);
- }
-}
-
-struct GprsMs *ms_ref(struct GprsMs *ms)
-{
- ms->ref += 1;
- return ms;
-}
-
-void ms_unref(struct GprsMs *ms)
-{
- OSMO_ASSERT(ms->ref >= 0);
- ms->ref -= 1;
- if (ms->ref == 0)
- ms_update_status(ms);
-}
-
-static void ms_release_timer_start(struct GprsMs *ms)
-{
- if (ms->delay == 0)
+ /* Immediate free():
+ * Skip delaying free() through release timer if TMSI is not
+ * known, since those cannot really be reused.
+ */
+ if (ms_tlli(ms) == GSM_RESERVED_TMSI) {
+ talloc_free(ms);
return;
+ }
- LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
-
- if (!ms->timer.data)
- ms->timer.data = ms_ref(ms);
-
- osmo_timer_schedule(&ms->timer, ms->delay, 0);
+ LOGPMS(ms, DMS, LOGL_INFO, "Schedule MS release in %lu secs\n", delay_rel_sec);
+ osmo_timer_schedule(&ms->release_timer, delay_rel_sec, 0);
}
-static void ms_release_timer_stop(struct GprsMs *ms)
+static void ms_becomes_active(struct GprsMs *ms)
{
- if (!ms->timer.data)
+ if (!osmo_timer_pending(&ms->release_timer))
return;
- LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
+ LOGPMS(ms, DMS, LOGL_DEBUG, "Cancel scheduled MS release\n");
- osmo_timer_del(&ms->timer);
- ms->timer.data = NULL;
- ms_unref(ms);
+ timerclear(&ms->tv_idle_start);
+ osmo_timer_del(&ms->release_timer);
}
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
@@ -293,109 +309,161 @@ void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
}
}
-static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
+/* If a TBF is attached to an MS, it is either in ms->{dl,ul}_tbf or in ms->old_tbfs list */
+static bool ms_tbf_is_attached(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf)
{
- if (ms->ul_tbf == tbf)
- return;
+ const struct llist_item *pos;
+ OSMO_ASSERT(ms);
+ OSMO_ASSERT(tbf);
+ OSMO_ASSERT(tbf_ms(tbf) == ms);
+
+ if (tbf == ul_tbf_as_tbf_const(ms->ul_tbf))
+ return true;
+
+ if (tbf == dl_tbf_as_tbf_const(ms->dl_tbf))
+ return true;
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
+ llist_for_each_entry(pos, &ms->old_tbfs, list) {
+ const struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
+ if (tmp_tbf == tbf)
+ return true;
+ }
+ return false;
+}
- ms_ref(ms);
+static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
+{
+ LOGPMS(ms, DMS, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
if (ms->ul_tbf)
- llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
+ llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
ms->ul_tbf = tbf;
- if (tbf)
- ms_release_timer_stop(ms);
-
- ms_unref(ms);
+ ms_ref(ms, MS_USE_TBF);
}
static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
{
- if (ms->dl_tbf == tbf)
- return;
-
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
-
- ms_ref(ms);
+ LOGPMS(ms, DMS, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
if (ms->dl_tbf)
- llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
+ llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
ms->dl_tbf = tbf;
- if (tbf)
- ms_release_timer_stop(ms);
-
- ms_unref(ms);
+ ms_ref(ms, MS_USE_TBF);
}
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
{
+ OSMO_ASSERT(ms);
+ OSMO_ASSERT(tbf);
+ OSMO_ASSERT(!ms_tbf_is_attached(ms, tbf));
+
if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
- ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
+ ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
else
- ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
+ ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
+
+ ms_becomes_active(ms);
}
void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
{
- if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
- ms->ul_tbf = NULL;
- } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
- ms->dl_tbf = NULL;
- } else {
- bool found = false;
-
- struct llist_item *pos, *tmp;
- llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
- struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
- if (tmp_tbf == tbf) {
- llist_del(&pos->list);
- found = true;
- break;
- }
- }
+ OSMO_ASSERT(tbf_ms(tbf) == ms);
- /* Protect against recursive calls via set_ms() */
- if (!found)
- return;
- }
+ /* In general this should not happen, but it can happen if during TBF
+ * allocation something fails before tbf->setup() called ms_attach_tbf(). */
+ if (!ms_tbf_is_attached(ms, tbf))
+ return;
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
+ LOGPMS(ms, DMS, LOGL_INFO, "Detaching TBF: %s\n",
tbf_name(tbf));
- if (tbf_ms(tbf) == ms)
- tbf_set_ms(tbf, NULL);
-
- if (!ms->dl_tbf && !ms->ul_tbf) {
- ms_set_reserved_slots(ms, NULL, 0, 0);
-
- if (ms_tlli(ms) != 0)
- ms_release_timer_start(ms);
+ if (tbf == ul_tbf_as_tbf(ms->ul_tbf)) {
+ ms->ul_tbf = NULL;
+ } else if (tbf == dl_tbf_as_tbf(ms->dl_tbf)) {
+ ms->dl_tbf = NULL;
+ } else {
+ /* We know from ms_tbf_is_attached()==true check above that tbf
+ * is in ms->old_tbfs, no need to look it up again. */
+ llist_del(tbf_ms_list(tbf));
}
- ms_update_status(ms);
+ ms_unref(ms, MS_USE_TBF);
}
-void ms_reset(struct GprsMs *ms)
+/* Cleans up old MS being merged into a new one. Should be called with a
+ * ms_ref() taken to avoid use-after-free.
+ */
+static void ms_reset(struct GprsMs *ms)
{
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
-
- ms_release_timer_stop(ms);
+ LOGPMS(ms, DMS, LOGL_INFO, "Clearing MS object\n");
+ struct llist_item *pos;
+ struct gprs_rlcmac_tbf *tbf;
+
+ tbf = ul_tbf_as_tbf(ms_ul_tbf(ms));
+ if (tbf && !tbf_timers_pending(tbf, T_MAX))
+ tbf_free(tbf);
+ tbf = dl_tbf_as_tbf(ms_dl_tbf(ms));
+ if (tbf && !tbf_timers_pending(tbf, T_MAX))
+ tbf_free(tbf);
+
+ while ((pos = llist_first_entry_or_null(&ms->old_tbfs, struct llist_item, list))) {
+ tbf = (struct gprs_rlcmac_tbf *)pos->entry;
+ if (!tbf_timers_pending(tbf, T_MAX))
+ tbf_free(tbf);
+ }
+ /* Flag it with invalid data so that it cannot be looked up anymore and
+ * shows up specially if listed in VTY. Furthermore, it will also trigger
+ * immediate free() when it becomes idle: */
ms->tlli = GSM_RESERVED_TMSI;
ms->new_dl_tlli = ms->tlli;
ms->new_ul_tlli = ms->tlli;
ms->imsi[0] = '\0';
}
-static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
+/* This function should be called on the MS object of a TBF each time an RLCMAC
+ * block is received for it with TLLI information.
+ * Besides updating the TLLI field on the MS object, it also seeks for other MS
+ * objects in the store and merges them into the current MS object. The MS
+ * duplication happened because we don't learn the TLLI of the created TBF until
+ * a later point. */
+void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
{
+ struct GprsMs *old_ms = NULL;
+
+ if (tlli == GSM_RESERVED_TMSI)
+ return;
+
+ /* When the TLLI does not match the ms, check if there is another
+ * MS object that belongs to that TLLI and if yes make sure one of them
+ * gets deleted. */
+ if (!ms_check_tlli(ms, tlli))
+ old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
+
+ ms_set_tlli(ms, tlli);
+
+ if (old_ms)
+ ms_merge_and_clear_ms(ms, old_ms);
+ /* old_ms may no longer be available here */
+}
+
+/* Merge 'old_ms' object into 'ms' object.
+ * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
+void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
+{
+ char old_ms_name[128];
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+
OSMO_ASSERT(old_ms != ms);
+ ms_ref(old_ms, __func__);
+
+ ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
+
+ LOGPMS(ms, DMS, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
@@ -406,36 +474,42 @@ static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
+
+ if ((dl_tbf = ms_dl_tbf(old_ms))) {
+ /* Move the last partially/totally unacked LLC PDU back to the LLC queue: */
+ dl_tbf_copy_unacked_pdus_to_llc_queue(dl_tbf);
+ }
+ /* Now merge the old_ms queue into the new one: */
llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
+ /* Clean up the old MS object */
ms_reset(old_ms);
+
+ ms_unref(old_ms, __func__);
}
-void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
+/* Apply changes to the TLLI directly, used interally by functions below: */
+static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
{
- OSMO_ASSERT(old_ms != ms);
-
- ms_ref(old_ms);
-
- /* Clean up the old MS object */
- /* TODO: Use timer? */
- if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
- tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
- if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
- tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
-
- ms_merge_old_ms(ms, old_ms);
+ ms->tlli = tlli;
+ ms->new_dl_tlli = GSM_RESERVED_TMSI;
+ ms->new_ul_tlli = GSM_RESERVED_TMSI;
- ms_unref(old_ms);
+ /* Update TBF FSM names: */
+ if (ms->ul_tbf)
+ tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
+ if (ms->dl_tbf)
+ tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
}
+/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
{
if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
return;
if (tlli != ms->new_dl_tlli) {
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
"not yet confirmed\n",
ms_tlli(ms), tlli);
@@ -443,16 +517,15 @@ void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
return;
}
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
"already confirmed partly\n",
ms->tlli, tlli);
- ms->tlli = tlli;
- ms->new_dl_tlli = GSM_RESERVED_TMSI;
- ms->new_ul_tlli = GSM_RESERVED_TMSI;
+ ms_apply_tlli_change(ms, tlli);
}
+/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
{
if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
@@ -462,7 +535,7 @@ bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
/* The MS has not sent a message with the new TLLI, which may
* happen according to the spec [TODO: add reference]. */
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
"partly confirmed\n", tlli);
/* Use the network's idea of TLLI as candidate, this does not
@@ -471,12 +544,10 @@ bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
return false;
}
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
- ms->tlli = tlli;
- ms->new_dl_tlli = GSM_RESERVED_TMSI;
- ms->new_ul_tlli = GSM_RESERVED_TMSI;
+ ms_apply_tlli_change(ms, tlli);
return true;
}
@@ -484,12 +555,12 @@ bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
void ms_set_imsi(struct GprsMs *ms, const char *imsi)
{
if (!imsi) {
- LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
+ LOGP(DMS, LOGL_ERROR, "Expected IMSI!\n");
return;
}
if (imsi[0] && strlen(imsi) < 3) {
- LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
+ LOGP(DMS, LOGL_ERROR, "No valid IMSI '%s'!\n",
imsi);
return;
}
@@ -497,11 +568,11 @@ void ms_set_imsi(struct GprsMs *ms, const char *imsi)
if (strcmp(imsi, ms->imsi) == 0)
return;
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
ms_tlli(ms), ms->imsi, imsi);
- struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
+ struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
/* Check if we are going to store a different MS object with already
existing IMSI. This is probably a bug in code calling this function,
since it should take care of this explicitly */
@@ -510,16 +581,23 @@ void ms_set_imsi(struct GprsMs *ms, const char *imsi)
* different IMSI */
OSMO_ASSERT(old_ms != ms);
- LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
+ LOGPMS(ms, DMS, LOGL_NOTICE,
"IMSI '%s' was already assigned to another "
"MS object: TLLI = 0x%08x, that IMSI will be removed\n",
imsi, ms_tlli(old_ms));
ms_merge_and_clear_ms(ms, old_ms);
+ /* old_ms may no longer be available here */
}
-
+ /* Store the new IMSI: */
osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
+
+ /* Update TBF FSM names: */
+ if (ms->ul_tbf)
+ tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
+ if (ms->dl_tbf)
+ tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
}
void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
@@ -528,12 +606,12 @@ void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
return;
if (gsm48_ta_is_valid(ta_)) {
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
ms_tlli(ms), ms->ta, ta_);
ms->ta = ta_;
} else
- LOGP(DRLCMAC, LOGL_NOTICE,
+ LOGP(DMS, LOGL_NOTICE,
"MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
"value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
}
@@ -543,7 +621,7 @@ void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
if (ms_class_ == ms->ms_class)
return;
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
ms_tlli(ms), ms->ms_class, ms_class_);
@@ -555,14 +633,14 @@ void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
if (ms_class_ == ms->egprs_ms_class)
return;
- LOGP(DRLCMAC, LOGL_INFO,
+ LOGP(DMS, LOGL_INFO,
"Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
ms_tlli(ms), ms->egprs_ms_class, ms_class_);
ms->egprs_ms_class = ms_class_;
if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
- LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
+ LOGPMS(ms, DMS, LOGL_DEBUG,
"Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
return;
@@ -576,7 +654,7 @@ void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
} else {
ms_set_mode(ms, EGPRS);
}
- LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
+ LOGPMS(ms, DMS, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
}
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
@@ -815,7 +893,7 @@ enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mc
}
if (orig_cs != cs)
- LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
+ LOGPMS(ms, DMS, LOGL_INFO, "MS (mode=%s) suggests transmitting "
"DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
@@ -823,7 +901,7 @@ enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mc
/* If the DL TBF is active, add number of unencoded chunk octets */
if (ms->dl_tbf)
- unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
+ unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
/* There are many unencoded octets, don't reduce */
if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
@@ -843,15 +921,15 @@ enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mc
return cs;
}
-int ms_first_common_ts(const struct GprsMs *ms)
+struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
{
- if (ms->dl_tbf)
- return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
-
- if (ms->ul_tbf)
- return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
+ return ms->first_common_ts;
+}
- return -1;
+void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
+{
+ OSMO_ASSERT(pdch);
+ ms->first_common_ts = pdch;
}
uint8_t ms_dl_slots(const struct GprsMs *ms)
@@ -859,10 +937,10 @@ uint8_t ms_dl_slots(const struct GprsMs *ms)
uint8_t slots = 0;
if (ms->dl_tbf)
- slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
+ slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
if (ms->ul_tbf)
- slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
+ slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
return slots;
}
@@ -872,10 +950,10 @@ uint8_t ms_ul_slots(const struct GprsMs *ms)
uint8_t slots = 0;
if (ms->dl_tbf)
- slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
+ slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
if (ms->ul_tbf)
- slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
+ slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
return slots;
}
@@ -884,20 +962,20 @@ uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
{
uint8_t slots = 0;
- bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
- bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
+ bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
+ bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned(ul_tbf_as_tbf(ms->ul_tbf));
if (!is_dl_active && !is_ul_active)
return 0;
/* see TS 44.060, 8.1.1.2.2 */
if (is_dl_active && !is_ul_active)
- slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
+ slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
else if (!is_dl_active && is_ul_active)
- slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
+ slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
else
- slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
- tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
+ slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
+ tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
/* Assume a multislot class 1 device */
/* TODO: For class 2 devices, this could be removed */
@@ -931,13 +1009,40 @@ void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
{
switch (dir) {
- case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
- case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
+ case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
+ case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
}
return NULL;
}
+const char *ms_name(const struct GprsMs *ms)
+{
+ static char _ms_name_buf[128];
+ return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
+}
+
+char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
+ uint32_t tlli = ms_tlli(ms);
+
+ OSMO_STRBUF_PRINTF(sb, "MS(");
+ if (ms_imsi_is_valid(ms))
+ OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
+ if (tlli != GSM_RESERVED_TMSI)
+ OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
+ OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
+ ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
+ if (ms->ul_tbf)
+ OSMO_STRBUF_PRINTF(sb, ":UL");
+ if (ms->dl_tbf)
+ OSMO_STRBUF_PRINTF(sb, ":DL");
+
+ OSMO_STRBUF_PRINTF(sb, ")");
+ return buf;
+}
+
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
{
if (!ms->nacc)
@@ -957,15 +1062,16 @@ bool ms_nacc_rts(const struct GprsMs *ms)
return false;
}
-struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
+struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
{
int rc;
struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
.tbf = tbf,
+ .pdch = pdch,
.fn = fn,
- .ts = ts,
.msg = NULL,
};
@@ -974,3 +1080,331 @@ struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf
return NULL;
return data_ctx.msg;
}
+
+static void ms_start_llc_timer(struct GprsMs *ms)
+{
+ if (the_pcu->vty.llc_idle_ack_csec > 0) {
+ struct timespec tv;
+ csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
+ osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
+ }
+}
+
+/* Can we get to send a DL TBF ass to the MS? */
+static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
+{
+ const struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
+ const struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
+
+ /* This function assumes it is called when no DL TBF is present, or
+ * alternatively if it's not really in use by the MS (TBF_ST_WAIT_REUSE_TFI) */
+ OSMO_ASSERT(!dl_tbf ||
+ tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_REUSE_TFI);
+
+ /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
+ * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
+ * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
+ * completed on the mobile station side." */
+ /* The possible uplink TBF is used to trigger downlink assignment:
+ * - If there is no uplink TBF the MS is potentially in packet idle mode
+ * and hence assignment will be done over CCCH (PCH)
+ * - If there's an uplink TBF but it is finished (waiting for last PKT
+ * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
+ * have no ways to contact the MS right now. Assignment will be delayed
+ * until PKT CTRL ACK is received and the TBF is released at the MS side
+ * (then assignment goes through PCH).
+ */
+ if (!ul_tbf)
+ return true;
+ if (ul_tbf_contention_resolution_done(ul_tbf) &&
+ !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf) &&
+ tbf_state(ul_tbf_as_tbf_const(ul_tbf)) != TBF_ST_RELEASING)
+ return true;
+
+ return false;
+
+}
+
+/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
+ * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
+ * In summary, this TBF is allocated as a consequence of receiving a "Pkt
+ * Resource Req" or "Pkt Ctrl Ack" from the MS.
+ * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
+ */
+struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
+{
+ struct gprs_rlcmac_ul_tbf *ul_tbf;
+ const struct alloc_resources_req req = {
+ .bts = ms->bts,
+ .ms = ms,
+ .direction = GPRS_RLCMAC_UL_TBF,
+ .single = false,
+ .use_trx = use_trx,
+ };
+ struct alloc_resources_res res = {};
+ int rc;
+
+ rc = the_pcu->alloc_algorithm(&req, &res);
+ if (rc < 0) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE,
+ "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
+ req.use_trx, req.single);
+ bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
+ return NULL;
+ }
+
+ ul_tbf = ul_tbf_alloc(ms->bts, ms);
+ if (!ul_tbf) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE, "ul_tbf_alloc() failed\n");
+ /* Caller will most probably send a Imm Ass Reject after return */
+ return NULL;
+ }
+
+ /* Update MS, really allocate the resources */
+ if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
+ res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
+ /* The reserved slots have changed, update the MS */
+ ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
+ }
+ ms_set_first_common_ts(ms, res.first_common_ts);
+
+ /* Apply allocated resources to TBF: */
+ ul_tbf_apply_allocated_resources(ul_tbf, &res);
+
+ ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
+
+ osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
+ /* Contention resolution is considered to be done since TLLI is known in MS */
+ return ul_tbf;
+}
+
+/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
+ * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
+struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
+{
+ struct gprs_rlcmac_ul_tbf *ul_tbf;
+ const struct alloc_resources_req req = {
+ .bts = ms->bts,
+ .ms = ms,
+ .direction = GPRS_RLCMAC_UL_TBF,
+ .single = true,
+ .use_trx = -1,
+ };
+ struct alloc_resources_res res = {};
+ int rc;
+
+ rc = the_pcu->alloc_algorithm(&req, &res);
+ if (rc < 0) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE,
+ "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
+ req.use_trx, req.single);
+ bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
+ return NULL;
+ }
+
+ ul_tbf = ul_tbf_alloc(ms->bts, ms);
+ if (!ul_tbf) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE, "ul_tbf_alloc() failed\n");
+ /* Caller will most probably send a Imm Ass Reject after return */
+ return NULL;
+ }
+
+ /* Update MS, really allocate the resources */
+ if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
+ res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
+ /* The reserved slots have changed, update the MS */
+ ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
+ }
+ ms_set_first_common_ts(ms, res.first_common_ts);
+
+ /* Apply allocated resources to TBF: */
+ ul_tbf_apply_allocated_resources(ul_tbf, &res);
+
+ ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
+
+ osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
+ return ul_tbf;
+}
+
+/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
+ * packet resource Request failed. This is similar as ul_tbf_alloc() but without
+ * calling alloc_algo (in charge of TFI/USF allocation), and reusing resources
+ * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
+struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
+{
+ struct gprs_rlcmac_ul_tbf *ul_tbf;
+ struct alloc_resources_res fake_res = {
+ .trx = pdch->trx,
+ .first_common_ts = pdch,
+ .reserved_ul_slots = 0,
+ .reserved_dl_slots = 0,
+ .ass_slots_mask = 0,
+ .upgrade_to_multislot = false,
+ .tfi = TBF_TFI_UNSET,
+ .usf = {0},
+ };
+ ul_tbf = ul_tbf_alloc(ms->bts, ms);
+ if (!ul_tbf)
+ return NULL;
+
+ /* The only one TS is the common, control TS */
+ ms_set_first_common_ts(ms, pdch);
+
+ /* Apply fake resources to TBF, to attach it to the proper TRX/PDCH: */
+ ul_tbf_apply_allocated_resources(ul_tbf, &fake_res);
+
+ ms_attach_tbf(ms, ul_tbf_as_tbf(ul_tbf));
+
+ osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
+ osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
+
+ return ul_tbf;
+}
+
+/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
+ * "tbf" may be either a UL-TBF or a DL-TBF.
+ * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
+ */
+int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
+{
+ OSMO_ASSERT(tbf);
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ const struct alloc_resources_req req = {
+ .bts = ms->bts,
+ .ms = ms,
+ .direction = GPRS_RLCMAC_DL_TBF,
+ .single = false,
+ .use_trx = tbf_get_trx(tbf)->trx_no,
+ };
+ struct alloc_resources_res res = {};
+ int rc;
+
+ rc = the_pcu->alloc_algorithm(&req, &res);
+ if (rc < 0) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE,
+ "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
+ req.use_trx, req.single);
+ bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
+ return -EBUSY;
+ }
+
+ dl_tbf = dl_tbf_alloc(ms->bts, ms);
+ if (!dl_tbf) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE, "dl_tbf_alloc() failed\n");
+ return -1;
+ }
+
+ /* Update MS, really allocate the resources */
+ if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
+ res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
+ /* The reserved slots have changed, update the MS */
+ ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
+ }
+ ms_set_first_common_ts(ms, res.first_common_ts);
+
+ /* Apply allocated resources to TBF: */
+ dl_tbf_apply_allocated_resources(dl_tbf, &res);
+
+ ms_attach_tbf(ms, dl_tbf_as_tbf(dl_tbf));
+
+ LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
+ dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
+ return 0;
+}
+
+/* A new DL-TBF is allocated and assigned through PCH.
+ * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
+ */
+int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
+{
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ const struct alloc_resources_req req = {
+ .bts = ms->bts,
+ .ms = ms,
+ .direction = GPRS_RLCMAC_DL_TBF,
+ .single = true,
+ .use_trx = -1,
+ };
+ struct alloc_resources_res res = {};
+ int rc;
+
+ rc = the_pcu->alloc_algorithm(&req, &res);
+ if (rc < 0) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE,
+ "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
+ req.use_trx, req.single);
+ bts_do_rate_ctr_inc(ms->bts, CTR_TBF_ALLOC_FAIL);
+ return -EBUSY;
+ }
+
+ dl_tbf = dl_tbf_alloc(ms->bts, ms);
+ if (!dl_tbf) {
+ LOGPMS(ms, DTBF, LOGL_NOTICE, "dl_tbf_alloc() failed\n");
+ return -1;
+ }
+
+ /* Update MS, really allocate the resources */
+ if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
+ res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
+ /* The reserved slots have changed, update the MS */
+ ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
+ }
+ ms_set_first_common_ts(ms, res.first_common_ts);
+
+ /* Apply allocated resources to TBF: */
+ dl_tbf_apply_allocated_resources(dl_tbf, &res);
+
+ ms_attach_tbf(ms, dl_tbf_as_tbf(dl_tbf));
+
+ LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
+ dl_tbf_trigger_ass_on_pch(dl_tbf);
+ return 0;
+}
+
+int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
+{
+ struct timespec expire_time;
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ int rc = 0;
+
+ LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
+
+ struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
+ if (!llc_msg)
+ return -ENOMEM;
+
+ llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
+ memcpy(msgb_put(llc_msg, len), data, len);
+ llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
+ ms_start_llc_timer(ms);
+
+ dl_tbf = ms_dl_tbf(ms);
+ if (dl_tbf) {
+ switch (tbf_state(dl_tbf_as_tbf_const(dl_tbf))) {
+ case TBF_ST_WAIT_RELEASE:
+ LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3192), so reuse TBF\n");
+ rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
+ return rc;
+ case TBF_ST_WAIT_REUSE_TFI:
+ /* According to DL TBF state it should be back to CCCH, let's check UL TBF to have more information. */
+ break;
+ case TBF_ST_RELEASING:
+ /* Something went wrong (T3195), delay for later. */
+ default:
+ /* DL TBF in working status (do nothing)*/
+ return 0;
+ }
+
+ }
+
+ /* Check if we can create a DL TBF to start sending the enqueued
+ * data. Otherwise it will be triggered later when it is reachable
+ * again. */
+ if (ms_is_reachable_for_dl_ass(ms)) {
+ if (ms_ul_tbf(ms))
+ rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
+ else
+ rc = ms_new_dl_tbf_assigned_on_pch(ms);
+ }
+ return rc;
+}
diff --git a/src/gprs_ms.h b/src/gprs_ms.h
index 4438a4c9..276733ca 100644
--- a/src/gprs_ms.h
+++ b/src/gprs_ms.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -35,6 +31,7 @@ extern "C" {
#include <osmocom/core/timer.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/use_count.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/gsm48.h>
@@ -54,20 +51,16 @@ struct gprs_rlcmac_bts;
struct gprs_rlcmac_trx;
struct GprsMs;
-struct gpr_ms_callback {
- void (*ms_idle)(struct GprsMs *);
- void (*ms_active)(struct GprsMs *);
-};
-
struct GprsMs {
struct llist_head list; /* list of all GprsMs */
- struct gpr_ms_callback cb;
bool app_info_pending;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_ul_tbf *ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf;
struct llist_head old_tbfs; /* list of gprs_rlcmac_tbf */
+ /* First common timeslot used both in UL and DL, or NULL if not set: */
+ struct gprs_rlcmac_pdch *first_common_ts;
uint32_t tlli;
uint32_t new_ul_tlli;
@@ -83,11 +76,12 @@ struct GprsMs {
enum CodingScheme current_cs_dl;
struct gprs_llc_queue llc_queue;
+ struct osmo_timer_list llc_timer;
- bool is_idle;
- int ref;
- struct osmo_timer_list timer;
- unsigned delay;
+ struct osmo_use_count use_count;
+ struct osmo_timer_list release_timer;
+ /* Time at which MS became idle and waiting to be released by release_timer: */
+ struct timeval tv_idle_start;
int64_t last_cs_not_low;
@@ -96,21 +90,18 @@ struct GprsMs {
uint8_t reserved_dl_slots;
uint8_t reserved_ul_slots;
struct gprs_rlcmac_trx *current_trx;
-
- struct gprs_codel *codel_state;
enum mcs_kind mode;
struct rate_ctr_group *ctrs;
struct nacc_fsm_ctx *nacc;
};
-struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli);
+struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, const char *use_ref);
-int ms_first_common_ts(const struct GprsMs *ms);
+struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms);
+void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch);
void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
uint8_t ul_slots, uint8_t dl_slots);
-struct GprsMs *ms_ref(struct GprsMs *ms);
-void ms_unref(struct GprsMs *ms);
void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode);
void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_);
void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_);
@@ -124,6 +115,7 @@ void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme);
void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate);
uint8_t ms_current_pacch_slots(const struct GprsMs *ms);
+void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli);
void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms);
void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
@@ -139,16 +131,25 @@ struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_dir
static inline struct gprs_rlcmac_ul_tbf *ms_ul_tbf(const struct GprsMs *ms) {return ms->ul_tbf;}
static inline struct gprs_rlcmac_dl_tbf *ms_dl_tbf(const struct GprsMs *ms) {return ms->dl_tbf;}
-
-void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb);
+const char *ms_name(const struct GprsMs *ms);
+char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size);
int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif);
bool ms_nacc_rts(const struct GprsMs *ms);
-struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
+struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_pdch *pdch, uint32_t fn);
+
+struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx);
+struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms);
+struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch);
+int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf);
+int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms);
+int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len);
static inline bool ms_is_idle(const struct GprsMs *ms)
{
- return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);
+ return !ms->ul_tbf && !ms->dl_tbf &&
+ llist_empty(&ms->old_tbfs);
}
static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
@@ -156,11 +157,30 @@ static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
return &ms->llc_queue;
}
+/* Function used in code where a ul_tbf related event occurs and hence its state
+ * changes, which in turn may change the entire MS state (eg becoming reachable
+ * over PCH or PACCH) and the caller may want to know if it is a good time to
+ * assign a DL TBF (and whether we have DL data to send) */
static inline bool ms_need_dl_tbf(struct GprsMs *ms)
{
- if (ms_dl_tbf(ms) != NULL &&
- tbf_state((const struct gprs_rlcmac_tbf *)ms_dl_tbf(ms)) != TBF_ST_WAIT_RELEASE)
- return false;
+ struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
+ if (dl_tbf) {
+ switch (tbf_state(dl_tbf_as_tbf(dl_tbf))) {
+ case TBF_ST_NEW:
+ case TBF_ST_ASSIGN:
+ case TBF_ST_FLOW:
+ case TBF_ST_FINISHED:
+ case TBF_ST_WAIT_RELEASE:
+ return false; /* TBF in use, hence no need for new DL TBF */
+ case TBF_ST_WAIT_REUSE_TFI:
+ case TBF_ST_RELEASING:
+ /* TBF cannot be used to send further data, a new one
+ * may be needed, check below */
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ }
return llc_queue_size(ms_llc_queue(ms)) > 0;
}
@@ -186,6 +206,11 @@ static inline const char *ms_imsi(const struct GprsMs *ms)
return ms->imsi;
}
+static inline bool ms_imsi_is_valid(const struct GprsMs *ms)
+{
+ return ms->imsi[0] != '\0';
+}
+
static inline uint8_t ms_ta(const struct GprsMs *ms)
{
return ms->ta;
@@ -211,16 +236,6 @@ static inline enum mcs_kind ms_mode(const struct GprsMs *ms)
return ms->mode;
}
-static inline void ms_set_timeout(struct GprsMs *ms, unsigned secs)
-{
- ms->delay = secs;
-}
-
-static inline struct gprs_codel *ms_codel_state(const struct GprsMs *ms)
-{
- return ms->codel_state;
-}
-
static inline unsigned ms_nack_rate_dl(const struct GprsMs *ms)
{
return ms->nack_rate_dl;
@@ -241,12 +256,14 @@ static inline struct gprs_rlcmac_trx *ms_current_trx(const struct GprsMs *ms)
return ms->current_trx;
}
+#define MS_USE_TBF "tbf"
+#define ms_ref(ms, use) \
+ OSMO_ASSERT(osmo_use_count_get_put(&(ms)->use_count, use, 1) == 0)
+#define ms_unref(ms, use) \
+ OSMO_ASSERT(osmo_use_count_get_put(&(ms)->use_count, use, -1) == 0)
+
#define LOGPMS(ms, category, level, fmt, args...) \
- LOGP(category, level, "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s) " fmt, \
- ms_tlli(ms), ms_imsi(ms), ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms), \
- ms_ul_tbf(ms) ? " UL": "", \
- ms_dl_tbf(ms) ? " DL": "", \
- ## args)
+ LOGP(category, level, "%s " fmt, ms_name(ms), ## args)
#ifdef __cplusplus
}
diff --git a/src/gprs_ms_storage.cpp b/src/gprs_ms_storage.cpp
deleted file mode 100644
index 6245ed9f..00000000
--- a/src/gprs_ms_storage.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/* gprs_ms_storage.cpp
- *
- * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
- * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-
-#include "gprs_ms_storage.h"
-
-#include "tbf.h"
-#include "bts.h"
-
-extern "C" {
- #include <osmocom/core/linuxlist.h>
- #include <osmocom/gsm/gsm48.h>
-}
-
-#define GPRS_UNDEFINED_IMSI "000"
-
-static void ms_storage_ms_idle_cb(struct GprsMs *ms)
-{
- llist_del(&ms->list);
- if (ms->bts)
- bts_stat_item_add(ms->bts, STAT_MS_PRESENT, -1);
- if (ms_is_idle(ms))
- talloc_free(ms);
-}
-
-static void ms_storage_ms_active_cb(struct GprsMs *ms)
-{
- /* Nothing to do */
-}
-
-static struct gpr_ms_callback ms_storage_ms_cb = {
- .ms_idle = ms_storage_ms_idle_cb,
- .ms_active = ms_storage_ms_active_cb,
-};
-
-GprsMsStorage::GprsMsStorage(struct gprs_rlcmac_bts *bts) :
- m_bts(bts)
-{
- INIT_LLIST_HEAD(&m_list);
-}
-
-GprsMsStorage::~GprsMsStorage()
-{
- cleanup();
-}
-
-void GprsMsStorage::cleanup()
-{
- struct llist_head *pos, *tmp;
-
- llist_for_each_safe(pos, tmp, &m_list) {
- struct GprsMs *ms = llist_entry(pos, typeof(*ms), list);
- ms_set_callback(ms, NULL);
- ms_storage_ms_idle_cb(ms);
- }
-}
-
-GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi) const
-{
- struct llist_head *tmp;
- GprsMs *ms;
-
- if (tlli != GSM_RESERVED_TMSI || old_tlli != GSM_RESERVED_TMSI) {
- llist_for_each(tmp, &m_list) {
- ms = llist_entry(tmp, typeof(*ms), list);
- if (ms_check_tlli(ms, tlli))
- return ms;
- if (ms_check_tlli(ms, old_tlli))
- return ms;
- }
- }
-
- /* not found by TLLI */
-
- if (imsi && imsi[0] && strcmp(imsi, GPRS_UNDEFINED_IMSI) != 0) {
- llist_for_each(tmp, &m_list) {
- ms = llist_entry(tmp, typeof(*ms), list);
- if (strcmp(imsi, ms_imsi(ms)) == 0)
- return ms;
- }
- }
-
- return NULL;
-}
-
-GprsMs *GprsMsStorage::create_ms()
-{
- GprsMs *ms;
-
- ms = ms_alloc(m_bts, GSM_RESERVED_TMSI);
-
- ms_set_callback(ms, &ms_storage_ms_cb);
- llist_add(&ms->list, &m_list);
- if (m_bts)
- bts_stat_item_add(m_bts, STAT_MS_PRESENT, 1);
-
- return ms;
-}
diff --git a/src/gprs_ms_storage.h b/src/gprs_ms_storage.h
deleted file mode 100644
index dcb6d8df..00000000
--- a/src/gprs_ms_storage.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* gprs_ms_storage.h
- *
- * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
- * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#pragma once
-
-#include "gprs_ms.h"
-#include "tbf.h"
-#include <stdint.h>
-#include <stddef.h>
-
-struct gprs_rlcmac_bts;
-
-struct GprsMsStorage {
-public:
- GprsMsStorage(struct gprs_rlcmac_bts *bts);
- ~GprsMsStorage();
-
- void cleanup();
-
- GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = GSM_RESERVED_TMSI, const char *imsi = NULL) const;
- GprsMs *create_ms();
-
- const struct llist_head* ms_list() const {return &m_list;}
-private:
- struct gprs_rlcmac_bts *m_bts;
- struct llist_head m_list; /* list of struct GprsMs */
-};
diff --git a/src/gprs_pcu.c b/src/gprs_pcu.c
index 5ed9d7d6..2c2454bb 100644
--- a/src/gprs_pcu.c
+++ b/src/gprs_pcu.c
@@ -6,8 +6,8 @@
* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -15,7 +15,7 @@
* 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
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -33,22 +33,39 @@ static struct osmo_tdef T_defs_pcu[] = {
{ .T=3113, .default_val=7, .unit=OSMO_TDEF_S, .desc="Timeout for paging", .val=0 },
{ .T=3190, .default_val=5, .unit=OSMO_TDEF_S, .desc="Return to packet idle mode after Packet DL Assignment on CCCH (s)", .val=0},
{ .T=3141, .default_val=10, .unit=OSMO_TDEF_S, .desc="Timeout for contention resolution procedure (s)", .val=0 },
+ { .T=3172, .default_val=5000, .unit=OSMO_TDEF_MS, .desc="Wait Indication used in Imm Ass Reject during TBF Establishment (PACCH)", .val=0, .min_val = 0, .max_val = 255000 }, /* TS 44.060 7.1.3.2.1 */
{ .T=PCU_TDEF_NEIGH_RESOLVE_TO, .default_val=1000, .unit=OSMO_TDEF_MS, .desc="[ARFCN+BSIC]->[RAC+CI] resolution timeout (ms)", .val=0 },
{ .T=PCU_TDEF_SI_RESOLVE_TO, .default_val=1000, .unit=OSMO_TDEF_MS, .desc="RIM RAN-INFO response timeout (ms)", .val=0 },
{ .T=PCU_TDEF_NEIGH_CACHE_ALIVE, .default_val=5, .unit=OSMO_TDEF_S, .desc="[ARFCN+BSIC]->[RAC+CI] resolution cache entry storage timeout (s)", .val=0 },
{ .T=PCU_TDEF_SI_CACHE_ALIVE, .default_val=5, .unit=OSMO_TDEF_S, .desc="[RAC+CI]->[SI] resolution cache entry storage timeout (s)", .val=0 },
{ .T=-101, .default_val=30, .unit=OSMO_TDEF_S, .desc="BSSGP (un)blocking procedures timer (s)", .val=0 },
{ .T=-102, .default_val=30, .unit=OSMO_TDEF_S, .desc="BSSGP reset procedure timer (s)", .val=0 },
- { .T=-2000, .default_val=2, .unit=OSMO_TDEF_MS, .desc="Delay release of UL TBF after tx Packet Access Reject (PACCH) (ms)", .val=0 },
- { .T=-2001, .default_val=2, .unit=OSMO_TDEF_S, .desc="PACCH assignment timeout (s)", .val=0 },
+ { .T=-2000, .default_val=0, .unit=OSMO_TDEF_MS, .desc="Delay release of UL TBF after tx Packet Access Reject (PACCH) (ms)", .val=0 },
+ { .T=-2001, .default_val=2, .unit=OSMO_TDEF_S, .desc="DL TBF PACCH assignment timeout (s)", .val=0 },
{ .T=-2002, .default_val=200, .unit=OSMO_TDEF_MS, .desc="Waiting after IMM.ASS confirm timer (ms)", .val=0 },
{ .T=-2030, .default_val=60, .unit=OSMO_TDEF_S, .desc="Time to keep an idle MS object alive (s)", .val=0 }, /* slightly above T3314 (default 44s, 24.008, 11.2.2) */
{ .T=-2031, .default_val=2000, .unit=OSMO_TDEF_MS, .desc="Time to keep an idle DL TBF alive (ms)", .val=0 },
{ .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */
};
+static void _update_stats_timer_cb(void *data)
+{
+ struct gprs_pcu *pcu = (struct gprs_pcu *)data;
+ struct gprs_rlcmac_bts *bts;
+
+ llist_for_each_entry(bts, &pcu->bts_list, list)
+ osmo_time_cc_set_flag(&bts->all_allocated_pdch, bts_all_pdch_allocated(bts));
+
+ osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
+}
+
static int gprs_pcu_talloc_destructor(struct gprs_pcu *pcu)
{
+ struct gprs_rlcmac_bts *bts;
+ while ((bts = llist_first_entry_or_null(&pcu->bts_list, struct gprs_rlcmac_bts, list)))
+ talloc_free(bts);
+
+ osmo_timer_del(&pcu->update_stats_timer);
neigh_cache_free(pcu->neigh_cache);
si_cache_free(pcu->si_cache);
return 0;
@@ -112,10 +129,9 @@ struct gprs_pcu *gprs_pcu_alloc(void *ctx)
/* TODO: increase them when CRBB decoding is implemented */
pcu->vty.ws_base = 64;
pcu->vty.ws_pdch = 0;
+ pcu->vty.msclass_default = PCU_DEFAULT_MSLOT_CLASS;
pcu->vty.llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
pcu->vty.llc_idle_ack_csec = 10;
- pcu->vty.neigh_ctrl_addr = NULL; /* don't use CTRL iface for Neigh Addr Resolution */
- pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;
pcu->T_defs = T_defs_pcu;
osmo_tdefs_reset(pcu->T_defs);
@@ -125,6 +141,9 @@ struct gprs_pcu *gprs_pcu_alloc(void *ctx)
pcu->neigh_cache = neigh_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_NEIGH_CACHE_ALIVE, OSMO_TDEF_S, -1));
pcu->si_cache = si_cache_alloc(pcu, osmo_tdef_get(pcu->T_defs, PCU_TDEF_SI_CACHE_ALIVE, OSMO_TDEF_S, -1));
+ osmo_timer_setup(&pcu->update_stats_timer, _update_stats_timer_cb, pcu);
+ osmo_timer_schedule(&pcu->update_stats_timer, 1, 0);
+
return pcu;
}
diff --git a/src/gprs_pcu.h b/src/gprs_pcu.h
index 86fe8eb4..c789d914 100644
--- a/src/gprs_pcu.h
+++ b/src/gprs_pcu.h
@@ -6,8 +6,8 @@
* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -15,7 +15,7 @@
* 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
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <osmocom/core/gsmtap_util.h>
+#include <osmocom/core/timer.h>
#include "gprs_bssgp_pcu.h"
#include "coding_scheme.h"
@@ -42,6 +43,9 @@
#define PCU_TDEF_NEIGH_CACHE_ALIVE (-10)
#define PCU_TDEF_SI_CACHE_ALIVE (-11)
+/* If Multislot Class is not known during TS allocation, assume ms_class=12: Rx=4 Tx=4 Sum=5 */
+#define PCU_DEFAULT_MSLOT_CLASS 12
+
/* see bts->gsmtap_categ_mask */
enum pcu_gsmtap_category {
PCU_GSMTAP_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */
@@ -65,10 +69,11 @@ enum pcu_gsmtap_category {
struct gprs_rlcmac_bts;
struct GprsMs;
struct gprs_rlcmac_tbf;
+struct alloc_resources_req;
+struct alloc_resources_res;
-typedef int (*alloc_algorithm_func_t)(struct gprs_rlcmac_bts *bts,
- struct gprs_rlcmac_tbf *tbf,
- bool single, int8_t use_tbf);
+typedef int (*alloc_algorithm_func_t)(const struct alloc_resources_req *req,
+ struct alloc_resources_res *res);
struct gprs_pcu {
@@ -106,19 +111,18 @@ struct gprs_pcu {
int ns_priority;
uint16_t ws_base;
uint16_t ws_pdch; /* increase WS by this value per PDCH */
+ uint8_t msclass_default; /* Assume this MSCLASS if unknown when creating TBF */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint32_t llc_discard_csec;
uint32_t llc_idle_ack_csec;
uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */
- /* Remote BSS resolution sevice (CTRL iface) */
- char *neigh_ctrl_addr;
- uint16_t neigh_ctrl_port;
} vty;
struct gsmtap_inst *gsmtap;
uint32_t gsmtap_categ_mask;
+ char *gsmtap_remote_host;
- struct llist_head bts_list; /* list of gprs_rlcmac_tbf */
+ struct llist_head bts_list; /* list of gprs_rlcmac_bts */
struct gprs_ns2_inst *nsi;
@@ -130,6 +134,10 @@ struct gprs_pcu {
struct neigh_cache *neigh_cache; /* ARFC+BSIC -> CGI PS cache */
struct si_cache *si_cache; /* ARFC+BSIC -> CGI PS cache */
+
+ struct osmo_timer_list update_stats_timer; /* Used to update some time_cc stats periodically */
+
+ uint8_t pcu_if_version;
};
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.c
index 22b12dfc..b2cbecaa 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.c
@@ -1,8 +1,9 @@
-/* gprs_rlcmac.cpp
+/* gprs_rlcmac.c
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -13,20 +14,13 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-extern "C" {
- #include <osmocom/gsm/gsm48.h>
-}
+#include <osmocom/gsm/gsm48.h>
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <bts.h>
-#include <bts_pch_timer.h>
#include <encoding.h>
#include <tbf.h>
#include <gprs_debug.h>
@@ -34,23 +28,23 @@ extern "C" {
extern void *tall_pcu_ctx;
int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi,
- uint16_t pgroup)
+ const char *imsi)
{
if (log_check_level(DRLCMAC, LOGL_NOTICE)) {
char str[64];
osmo_mobile_identity_to_str_buf(str, sizeof(str), mi);
LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH) MI=%s\n", str);
}
- bitvec *paging_request = bitvec_alloc(22, tall_pcu_ctx);
+ struct bitvec *paging_request = bitvec_alloc(22, tall_pcu_ctx);
bitvec_unhex(paging_request, DUMMY_VEC);
- int plen = Encoding::write_paging_request(paging_request, mi);
+ int plen = write_paging_request(paging_request, mi);
if (plen <= 0) {
LOGP(DRLCMAC, LOGL_ERROR, "TX: [PCU -> BTS] Failed to encode Paging Request\n");
return -1;
}
bts_do_rate_ctr_inc(bts, CTR_PCH_REQUESTS);
- bts_pch_timer_start(bts, mi->imsi);
- pcu_l1if_tx_pch(bts, paging_request, plen, pgroup);
+
+ pcu_l1if_tx_pch2(bts, paging_request, plen, false, imsi, GSM_RESERVED_TMSI);
bitvec_free(paging_request);
return 0;
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index cf95c37f..70cf63e9 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -2,6 +2,7 @@
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -12,14 +13,9 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#ifndef GPRS_RLCMAC_H
-#define GPRS_RLCMAC_H
+#pragma once
#include <stdbool.h>
#include <stdint.h>
@@ -96,28 +92,14 @@ enum gprs_rlcmac_block_type {
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
-struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
-
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts,
uint32_t fn, uint8_t block_nr);
extern "C" {
#endif
-int alloc_algorithm_a(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
- int8_t use_trx);
-
-int alloc_algorithm_b(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
- int8_t use_trx);
-
-int alloc_algorithm_dynamic(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool single,
- int8_t use_trx);
-
-int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi, uint16_t pgroup);
-int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
+int gprs_rlcmac_paging_request(struct gprs_rlcmac_bts *bts, const struct osmo_mobile_identity *mi, const char *imsi);
+struct msgb *gprs_rlcmac_app_info_msg(const struct gsm_pcu_if_app_info_req *req);
#ifdef __cplusplus
}
#endif
-
-
-#endif // GPRS_RLCMAC_H
diff --git a/src/gprs_rlcmac_meas.cpp b/src/gprs_rlcmac_meas.cpp
index b9a324fb..a3d891cc 100644
--- a/src/gprs_rlcmac_meas.cpp
+++ b/src/gprs_rlcmac_meas.cpp
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
extern "C" {
@@ -128,8 +124,8 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
if (!sum)
return -EINVAL;
- LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
- "Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
+ LOGP(DRLCMACMEAS, LOGL_DEBUG, "%s DL Loss: Received: %4d Lost: %4d Sum: %4d\n",
+ tbf_name(dl_tbf_as_tbf_const(tbf)), received, lost, sum);
tbf->m_bw.dl_loss_received += received;
tbf->m_bw.dl_loss_lost += lost;
@@ -158,9 +154,8 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
if (!sum)
return -EINVAL;
- LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
- "%d%%\n", tbf->imsi(), tbf->tlli(),
- tbf->m_bw.dl_loss_lost * 100 / sum);
+ LOGP(DRLCMACMEAS, LOGL_DEBUG, "%s DL packet loss: %d%%\n",
+ tbf_name(dl_tbf_as_tbf_const(tbf)), tbf->m_bw.dl_loss_lost * 100 / sum);
return 0;
}
@@ -183,8 +178,8 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
return 0;
tbf->m_bw.dl_throughput = (tbf->m_bw.dl_bw_octets << 10) / ((elapsed.tv_sec << 10) + (elapsed.tv_nsec >> 20));
- LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
- "%d KBits/s\n", tbf->imsi(), tbf->tlli(), tbf->m_bw.dl_throughput);
+ LOGP(DRLCMACMEAS, LOGL_INFO, "%s DL Bandwitdh: %d KBits/s\n",
+ tbf_name(dl_tbf_as_tbf_const(tbf)), tbf->m_bw.dl_throughput);
/* reset bandwidth values timestamp */
memcpy(bw_tv, &now_tv, sizeof(*bw_tv));
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index 8867d436..ac581216 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <gprs_bssgp_pcu.h>
@@ -32,6 +28,7 @@
extern "C" {
#include <osmocom/core/gsmtap.h>
+ #include "nacc_fsm.h"
}
struct tbf_sched_candidates {
@@ -49,35 +46,27 @@ static void get_ctrl_msg_tbf_candidates(const struct gprs_rlcmac_pdch *pdch,
struct llist_item *pos;
llist_for_each_entry(pos, &pdch->trx->ul_tbfs, list) {
- ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
+ ul_tbf = tbf_as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
OSMO_ASSERT(ul_tbf);
- /* this trx, this ts */
- if (!ul_tbf->is_control_ts(pdch->ts_no))
- continue;
- if (tbf_ul_ack_rts(ul_tbf))
+ if (tbf_ul_ack_rts(ul_tbf, pdch))
tbf_cand->ul_ack = ul_tbf;
- if (tbf_dl_ass_rts(ul_tbf))
+ if (tbf_dl_ass_rts(ul_tbf, pdch))
tbf_cand->dl_ass = ul_tbf;
- if (tbf_ul_ass_rts(ul_tbf))
+ if (tbf_ul_ass_rts(ul_tbf, pdch))
tbf_cand->ul_ass = ul_tbf;
- /* NACC ready to send. TFI assigned is needed to send messages */
- if (ul_tbf->is_tfi_assigned() && ms_nacc_rts(ul_tbf->ms()))
+ if (tbf_nacc_rts(ul_tbf, pdch))
tbf_cand->nacc = ul_tbf;
/* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all
states? */
}
llist_for_each_entry(pos, &pdch->trx->dl_tbfs, list) {
- dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
+ dl_tbf = tbf_as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
OSMO_ASSERT(dl_tbf);
- /* this trx, this ts */
- if (!dl_tbf->is_control_ts(pdch->ts_no))
- continue;
- if (tbf_dl_ass_rts(dl_tbf))
+ if (tbf_dl_ass_rts(dl_tbf, pdch))
tbf_cand->dl_ass = dl_tbf;
- if (tbf_ul_ass_rts(dl_tbf))
+ if (tbf_ul_ass_rts(dl_tbf, pdch))
tbf_cand->ul_ass = dl_tbf;
- /* NACC ready to send. TFI assigned is needed to send messages */
- if (dl_tbf->is_tfi_assigned() && ms_nacc_rts(dl_tbf->ms()))
+ if (tbf_nacc_rts(dl_tbf, pdch))
tbf_cand->nacc = dl_tbf;
}
}
@@ -150,7 +139,6 @@ static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_
tbfs->dl_ass,
tbfs->ul_ack,
tbfs->nacc };
- uint8_t ts = pdch->ts_no;
/* Send Packet Application Information first (ETWS primary notifications) */
msg = sched_app_info(tbfs->dl_ass);
@@ -167,16 +155,15 @@ static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_
* received, thus preventing the others from being processed.
*/
if (tbf == tbfs->ul_ass && tbf->ul_ass_state_is(TBF_UL_ASS_SEND_ASS_REJ))
- msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
+ msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, pdch, fn);
else if (tbf == tbfs->ul_ass && tbf->direction == GPRS_RLCMAC_DL_TBF)
- msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
+ msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, pdch, fn);
else if (tbf == tbfs->dl_ass && tbf->direction == GPRS_RLCMAC_UL_TBF)
- msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, fn, ts);
+ msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, pdch, fn);
else if (tbf == tbfs->ul_ack)
- msg = tbf_ul_ack_create_rlcmac_msg(tbfs->ul_ack, fn, ts);
- else if (tbf == tbfs->nacc) {
- msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf, fn, ts);
- }
+ msg = tbf_ul_ack_create_rlcmac_msg(tbfs->ul_ack, pdch, fn);
+ else if (tbf == tbfs->nacc)
+ msg = ms_nacc_create_rlcmac_msg(tbf->ms(), tbf, pdch, fn);
if (!msg) {
tbf = NULL;
@@ -196,10 +183,10 @@ static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_
*/
if (tbfs->dl_ass) {
tbf = tbfs->dl_ass;
- msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, fn, ts);
+ msg = tbf_dl_ass_create_rlcmac_msg(tbfs->dl_ass, pdch, fn);
} else if (tbfs->ul_ass) {
tbf = tbfs->ul_ass;
- msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, fn, ts);
+ msg = tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, pdch, fn);
}
}
@@ -231,24 +218,25 @@ static struct msgb *sched_select_ctrl_msg(struct gprs_rlcmac_pdch *pdch, uint32_
}
static inline enum tbf_dl_prio tbf_compute_priority(const struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_dl_tbf *tbf,
- uint8_t ts, uint32_t fn, int age)
+ const struct gprs_rlcmac_pdch *pdch, uint32_t fn, int age)
{
const gprs_rlc_dl_window *w = static_cast<gprs_rlc_dl_window *>(tbf->window());
unsigned long msecs_t3190 = osmo_tdef_get(the_pcu->T_defs, 3190, OSMO_TDEF_MS, -1);
unsigned long dl_tbf_idle_msec = osmo_tdef_get(the_pcu->T_defs, -2031, OSMO_TDEF_MS, -1);
int age_thresh1 = msecs_to_frames(200);
int age_thresh2 = msecs_to_frames(OSMO_MIN(msecs_t3190/2, dl_tbf_idle_msec));
+ const bool is_control_ts = tbf_is_control_ts(tbf, pdch);
- if (tbf->is_control_ts(ts) && tbf->need_poll_for_dl_ack_nack())
+ if (is_control_ts && tbf->need_poll_for_dl_ack_nack())
return DL_PRIO_CONTROL;
- if (tbf->is_control_ts(ts) && age > age_thresh2 && age_thresh2 > 0)
+ if (is_control_ts && age > age_thresh2 && age_thresh2 > 0)
return DL_PRIO_HIGH_AGE;
if ((tbf->state_is(TBF_ST_FLOW) && tbf->have_data()) || w->resend_needed() >= 0)
return DL_PRIO_NEW_DATA;
- if (tbf->is_control_ts(ts) && age > age_thresh1 && tbf->keep_open(fn))
+ if (is_control_ts && age > age_thresh1 && tbf->keep_open(fn))
return DL_PRIO_LOW_AGE;
if (!w->window_empty())
@@ -289,7 +277,6 @@ static struct msgb *sched_select_dl_data_msg(struct gprs_rlcmac_bts *bts, struct
struct msgb *msg = NULL;
struct gprs_rlcmac_dl_tbf *tbf, *prio_tbf = NULL;
enum tbf_dl_prio prio, max_prio = DL_PRIO_NONE;
- uint8_t ts = pdch->ts_no;
uint8_t i, tfi, prio_tfi;
int age;
@@ -316,7 +303,7 @@ static struct msgb *sched_select_dl_data_msg(struct gprs_rlcmac_bts *bts, struct
age = tbf->frames_since_last_poll(fn);
/* compute priority */
- prio = tbf_compute_priority(bts, tbf, ts, fn, age);
+ prio = tbf_compute_priority(bts, tbf, pdch, fn, age);
if (prio == DL_PRIO_NONE)
continue;
@@ -345,7 +332,7 @@ static struct msgb *sched_select_dl_data_msg(struct gprs_rlcmac_bts *bts, struct
/* next TBF to handle resource is the next one */
pdch->next_dl_tfi = (prio_tfi + 1) & 31;
/* generate DL data block */
- msg = prio_tbf->create_dl_acked_block(fn, ts, req_mcs_kind);
+ msg = prio_tbf->create_dl_acked_block(fn, pdch, req_mcs_kind);
*is_egprs = prio_tbf->is_egprs_enabled();
}
@@ -460,8 +447,8 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
* let's set its USF in the DL msg. This is not really needed,
* but it helps understand better the flow when looking at
* pcaps. */
- if (poll_tbf->direction == GPRS_RLCMAC_UL_TBF && as_ul_tbf(poll_tbf)->m_usf[ts] != USF_INVALID)
- usf_tbf = as_ul_tbf(poll_tbf);
+ if (poll_tbf->direction == GPRS_RLCMAC_UL_TBF && tbf_as_ul_tbf(poll_tbf)->m_usf[ts] != USF_INVALID)
+ usf_tbf = tbf_as_ul_tbf(poll_tbf);
/* else, search for uplink tbf */
} else if ((usf_tbf = sched_select_uplink(pdch, require_gprs_only))) {
LOGPDCH(pdch, DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: FN=%d "
@@ -478,7 +465,7 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
/* Prio 1: select control message */
if ((msg = sched_select_ctrl_msg(pdch, fn, block_nr, &tbf_cand))) {
- gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
+ gsmtap_cat = PCU_GSMTAP_C_DL_CTRL;
}
/* Prio 2: select data message for downlink */
else if((msg = sched_select_dl_data_msg(bts, pdch, fn, block_nr, req_mcs_kind, &tx_is_egprs))) {
@@ -500,11 +487,10 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
const unsigned num_tbfs = pdch->num_tbfs(GPRS_RLCMAC_DL_TBF)
+ pdch->num_tbfs(GPRS_RLCMAC_UL_TBF);
bool skip_idle = (num_tbfs == 0);
-#ifdef ENABLE_DIRECT_PHY
- /* In DIRECT_PHY mode we want to always submit something to L1 in
- * TRX0, since BTS is not preparing dummy bursts on idle TS for us */
- skip_idle = skip_idle && trx != 0;
-#endif
+
+ if (bts->gen_idle_blocks_C0)
+ skip_idle = skip_idle && trx != 0;
+
if (!skip_idle && (msg = sched_dummy())) {
/* increase counter */
gsmtap_cat = PCU_GSMTAP_C_DL_DUMMY;
diff --git a/src/gsm_rlcmac.c b/src/gsm_rlcmac.c
index 6793602c..570c97b8 100644
--- a/src/gsm_rlcmac.c
+++ b/src/gsm_rlcmac.c
@@ -23,10 +23,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <osmocom/core/utils.h>
diff --git a/src/gsm_rlcmac.h b/src/gsm_rlcmac.h
index 01e2ee63..c43d4181 100644
--- a/src/gsm_rlcmac.h
+++ b/src/gsm_rlcmac.h
@@ -23,10 +23,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __PACKET_GSM_RLCMAC_H__
diff --git a/src/llc.c b/src/llc.c
new file mode 100644
index 00000000..45b8ac49
--- /dev/null
+++ b/src/llc.c
@@ -0,0 +1,404 @@
+/* Copied from tbf.cpp
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+
+#include <stdio.h>
+
+#include <osmocom/core/msgb.h>
+
+#include "bts.h"
+#include "gprs_ms.h"
+#include "pcu_utils.h"
+#include "llc.h"
+
+void llc_init(struct gprs_llc *llc)
+{
+ llc_reset(llc);
+}
+
+/* reset LLC frame */
+void llc_reset(struct gprs_llc *llc)
+{
+ llc->index = 0;
+ llc->length = 0;
+ llc->prio = 0;
+ llc->meta_info = (struct MetaInfo){0};
+
+ memset(llc->frame, 0x42, sizeof(llc->frame));
+}
+
+void llc_reset_frame_space(struct gprs_llc *llc)
+{
+ llc->index = 0;
+}
+
+/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
+void llc_put_dummy_frame(struct gprs_llc *llc, size_t req_len)
+{
+ /* The shortest dummy command (the spec requests at least 6 octets) */
+ static const uint8_t llc_dummy_command[] = {
+ 0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
+ };
+ static const size_t max_dummy_command_len = 79;
+
+ llc_put_frame(llc, llc_dummy_command, sizeof(llc_dummy_command));
+
+ if (req_len > max_dummy_command_len)
+ req_len = max_dummy_command_len;
+
+ /* Add further stuffing, if the requested length exceeds the minimum
+ * dummy command length */
+ if (llc->length < req_len) {
+ memset(&llc->frame[llc->length], 0x2b, req_len - llc->length);
+ llc->length = req_len;
+ }
+}
+
+void llc_put_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
+{
+ /* only put frames when we are empty */
+ OSMO_ASSERT(llc->index == 0 && llc->length == 0);
+ llc_append_frame(llc, data, len);
+}
+
+void llc_append_frame(struct gprs_llc *llc, const uint8_t *data, size_t len)
+{
+ /* TODO: bounds check */
+ memcpy(llc->frame + llc->length, data, len);
+ llc->length += len;
+}
+
+static bool llc_pdu_can_be_discarded(const uint8_t *data, size_t len)
+{
+ const unsigned keep_small_thresh = 60;
+
+ /* Is the frame small, perhaps only a TCP ACK? */
+ if (len <= keep_small_thresh)
+ return false;
+
+ if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
+ return false;
+
+ if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
+ /* It is not an LLC UI frame, see TS 44.064, 6.3 */
+ return false;
+
+ return true;
+}
+
+void llc_queue_init(struct gprs_llc_queue *q, struct GprsMs *ms)
+{
+ unsigned int i;
+
+ q->ms = ms;
+ q->queue_size = 0;
+ q->queue_octets = 0;
+ q->avg_queue_delay = 0;
+ for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
+ INIT_LLIST_HEAD(&q->pq[i].queue);
+ gprs_codel_init(&q->pq[i].codel_state);
+ }
+}
+
+/* interval=0 -> don't use codel in the LLC queue */
+void llc_queue_set_codel_interval(struct gprs_llc_queue *q, unsigned int interval)
+{
+ unsigned int i;
+ if (interval == LLC_CODEL_DISABLE) {
+ q->use_codel = false;
+ return;
+ }
+ q->use_codel = true;
+ for (i = 0; i < ARRAY_SIZE(q->pq); i++)
+ gprs_codel_set_interval(&q->pq[i].codel_state, interval);
+}
+
+static enum gprs_llc_queue_prio llc_sapi2prio(uint8_t sapi)
+{
+ switch (sapi) {
+ case 1:
+ return LLC_QUEUE_PRIO_GMM;
+ case 2:
+ case 7:
+ case 8:
+ return LLC_QUEUE_PRIO_TOM_SMS;
+ default:
+ return LLC_QUEUE_PRIO_OTHER;
+ }
+}
+
+void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time)
+{
+ struct MetaInfo *meta_storage;
+ struct gprs_llc_hdr *llc_hdr = (struct gprs_llc_hdr *)msgb_data(llc_msg);
+ enum gprs_llc_queue_prio prio;
+
+ osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
+
+ prio = llc_sapi2prio(llc_hdr->sapi);
+
+ q->queue_size += 1;
+ q->queue_octets += msgb_length(llc_msg);
+
+ meta_storage = (struct MetaInfo *)&llc_msg->cb[0];
+ osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
+ meta_storage->expire_time = *expire_time;
+
+ msgb_enqueue(&q->pq[prio].queue, llc_msg);
+}
+
+void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
+{
+ struct msgb *msg;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
+ while ((msg = msgb_dequeue(&q->pq[i].queue))) {
+ if (bts)
+ bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
+ msgb_free(msg);
+ }
+ }
+
+ q->queue_size = 0;
+ q->queue_octets = 0;
+}
+
+void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
+{
+ struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
+ struct llist_head new_queue;
+ unsigned int i;
+ size_t queue_size = 0;
+ size_t queue_octets = 0;
+ INIT_LLIST_HEAD(&new_queue);
+
+ for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
+ while (1) {
+ if (msg1 == NULL)
+ msg1 = msgb_dequeue(&q->pq[i].queue);
+
+ if (msg2 == NULL)
+ msg2 = msgb_dequeue(&o->pq[i].queue);
+
+ if (msg1 == NULL && msg2 == NULL)
+ break;
+
+ if (msg1 == NULL) {
+ msg = msg2;
+ msg2 = NULL;
+ } else if (msg2 == NULL) {
+ msg = msg1;
+ msg1 = NULL;
+ } else {
+ const struct MetaInfo *mi1 = (struct MetaInfo *)&msg1->cb[0];
+ const struct MetaInfo *mi2 = (struct MetaInfo *)&msg2->cb[0];
+
+ if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
+ msg = msg1;
+ msg1 = NULL;
+ } else {
+ msg = msg2;
+ msg2 = NULL;
+ }
+ }
+
+ msgb_enqueue(&new_queue, msg);
+ queue_size += 1;
+ queue_octets += msgb_length(msg);
+ }
+
+ OSMO_ASSERT(llist_empty(&q->pq[i].queue));
+ OSMO_ASSERT(llist_empty(&o->pq[i].queue));
+ llist_splice_init(&new_queue, &q->pq[i].queue);
+ }
+
+ o->queue_size = 0;
+ o->queue_octets = 0;
+ q->queue_size = queue_size;
+ q->queue_octets = queue_octets;
+}
+
+/* Prepend / Put back a previously dequeued LLC frame (llc_queue_dequeue()) */
+void llc_queue_merge_prepend(struct gprs_llc_queue *q, struct gprs_llc *llc)
+{
+ struct MetaInfo *meta_storage;
+ unsigned int len = llc_frame_length(llc);
+ struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
+
+ OSMO_ASSERT(llc_msg);
+ memcpy(msgb_put(llc_msg, len), llc->frame, len);
+
+ q->queue_size += 1;
+ q->queue_octets += msgb_length(llc_msg);
+
+ meta_storage = (struct MetaInfo *)&llc_msg->cb[0];
+ memcpy(meta_storage, &llc->meta_info, sizeof(struct MetaInfo));
+
+ /* Prepend: */
+ llist_add(&llc_msg->list, &q->pq[llc->prio].queue);
+}
+
+#define ALPHA 0.5f
+
+static struct msgb *llc_queue_pick_msg(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *prio)
+{
+ struct msgb *msg;
+ struct timespec tv_now, tv_result;
+ uint32_t lifetime;
+ unsigned int i;
+ const struct MetaInfo *meta_storage;
+
+ for (i = 0; i < ARRAY_SIZE(q->pq); i++) {
+ if ((msg = msgb_dequeue(&q->pq[i].queue))) {
+ *prio = (enum gprs_llc_queue_prio)i;
+ break;
+ }
+ }
+ if (!msg)
+ return NULL;
+
+ meta_storage = (struct MetaInfo *)&msg->cb[0];
+
+ q->queue_size -= 1;
+ q->queue_octets -= msgb_length(msg);
+
+ /* take the second time */
+ osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
+ timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
+
+ lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
+ q->avg_queue_delay = q->avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
+
+ return msg;
+}
+
+struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *out_prio, struct MetaInfo *out_info)
+{
+ struct msgb *msg;
+ struct timespec tv_now, tv_now2;
+ uint32_t octets = 0, frames = 0;
+ struct gprs_rlcmac_bts *bts = q->ms->bts;
+ struct gprs_pcu *pcu = bts->pcu;
+ struct timespec hyst_delta = {0, 0};
+ enum gprs_llc_queue_prio prio;
+ const struct MetaInfo *info = NULL;
+
+ if (pcu->vty.llc_discard_csec)
+ csecs_to_timespec(pcu->vty.llc_discard_csec, &hyst_delta);
+
+ osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
+ timespecadd(&tv_now, &hyst_delta, &tv_now2);
+
+ while ((msg = llc_queue_pick_msg(q, &prio))) {
+ info = (const struct MetaInfo *)&msg->cb[0];
+ const struct timespec *tv_disc = &info->expire_time;
+ const struct timespec *tv_recv = &info->recv_time;
+
+ gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
+
+ if (q->use_codel) {
+ int bytes = llc_queue_octets(q);
+ if (gprs_codel_control(&q->pq[prio].codel_state, tv_recv, &tv_now, bytes))
+ goto drop_frame;
+ }
+
+ /* Is the age below the low water mark? */
+ if (!llc_queue_is_frame_expired(&tv_now2, tv_disc))
+ break;
+
+ /* Is the age below the high water mark */
+ if (!llc_queue_is_frame_expired(&tv_now, tv_disc)) {
+ /* Has the previous message not been dropped? */
+ if (frames == 0)
+ break;
+
+ /* Hysteresis mode, try to discard LLC messages until
+ * the low water mark has been reached */
+
+ /* Check whether to abort the hysteresis mode:
+ * Can the PDU be discarded according to its type? */
+ if (!llc_pdu_can_be_discarded(msg->data, msg->len))
+ break;
+ }
+
+ bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
+drop_frame:
+ frames++;
+ octets += msg->len;
+ msgb_free(msg);
+ bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
+ continue;
+ }
+
+ if (frames) {
+ LOGPMS(q->ms, DTBFDL, LOGL_NOTICE, "Discarding LLC PDU "
+ "because lifetime limit reached, "
+ "count=%u new_queue_size=%zu\n",
+ frames, llc_queue_size(q));
+ if (frames > 0xff)
+ frames = 0xff;
+ if (octets > 0xffffff)
+ octets = 0xffffff;
+ if (pcu->bssgp.bctx)
+ bssgp_tx_llc_discarded(pcu->bssgp.bctx, ms_tlli(q->ms), frames, octets);
+ }
+
+ if (!msg)
+ return NULL;
+
+ if (out_prio)
+ *out_prio = prio;
+
+ if (out_info) {
+ OSMO_ASSERT(info);
+ *out_info = *info;
+ }
+
+ return msg;
+}
+
+void llc_queue_calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
+{
+ uint16_t delay_csec;
+ if (bts->pcu->vty.force_llc_lifetime)
+ delay_csec = bts->pcu->vty.force_llc_lifetime;
+ else
+ delay_csec = pdu_delay_csec;
+
+ /* keep timestamp at 0 for infinite delay */
+ if (delay_csec == 0xffff) {
+ memset(tv, 0, sizeof(*tv));
+ return;
+ }
+
+ /* calculate timestamp of timeout */
+ struct timespec now, csec;
+ osmo_clock_gettime(CLOCK_MONOTONIC, &now);
+ csecs_to_timespec(delay_csec, &csec);
+
+ timespecadd(&now, &csec, tv);
+}
+
+bool llc_queue_is_frame_expired(const struct timespec *tv_now, const struct timespec *tv)
+{
+ /* Timeout is infinite */
+ if (tv->tv_sec == 0 && tv->tv_nsec == 0)
+ return false;
+
+ return timespeccmp(tv_now, tv, >);
+}
diff --git a/src/llc.cpp b/src/llc.cpp
deleted file mode 100644
index e508d0b1..00000000
--- a/src/llc.cpp
+++ /dev/null
@@ -1,256 +0,0 @@
-/* Copied from tbf.cpp
- *
- * Copyright (C) 2012 Ivan Klyuchnikov
- * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
- * Copyright (C) 2013 by Holger Hans Peter Freyther
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <bts.h>
-
-#include <stdio.h>
-
-extern "C" {
-#include <osmocom/core/msgb.h>
-}
-
-#include "pcu_utils.h"
-
-/* reset LLC frame */
-void gprs_llc::reset()
-{
- m_index = 0;
- m_length = 0;
-
- memset(frame, 0x42, sizeof(frame));
-}
-
-void gprs_llc::reset_frame_space()
-{
- m_index = 0;
-}
-
-/* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
-void gprs_llc::put_dummy_frame(size_t req_len)
-{
- /* The shortest dummy command (the spec requests at least 6 octets) */
- static const uint8_t llc_dummy_command[] = {
- 0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b
- };
- static const size_t max_dummy_command_len = 79;
-
- put_frame(llc_dummy_command, sizeof(llc_dummy_command));
-
- if (req_len > max_dummy_command_len)
- req_len = max_dummy_command_len;
-
- /* Add further stuffing, if the requested length exceeds the minimum
- * dummy command length */
- if (m_length < req_len) {
- memset(&frame[m_length], 0x2b, req_len - m_length);
- m_length = req_len;
- }
-}
-
-void gprs_llc::put_frame(const uint8_t *data, size_t len)
-{
- /* only put frames when we are empty */
- OSMO_ASSERT(m_index == 0 && m_length == 0);
- append_frame(data, len);
-}
-
-void gprs_llc::append_frame(const uint8_t *data, size_t len)
-{
- /* TODO: bounds check */
- memcpy(frame + m_length, data, len);
- m_length += len;
-}
-
-void gprs_llc::init()
-{
- reset();
-}
-
-bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
-{
- if (len < 2)
- return false;
-
- if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
- return false;
-
- if ((data[0] & 0xe0) != 0xc0 /* LLC UI */)
- /* It is not an LLC UI frame, see TS 44.064, 6.3 */
- return false;
-
- return true;
-}
-
-void llc_queue_init(struct gprs_llc_queue *q)
-{
- INIT_LLIST_HEAD(&q->m_queue);
- q->m_queue_size = 0;
- q->m_queue_octets = 0;
- q->m_avg_queue_delay = 0;
-}
-
-
-void gprs_llc_queue::enqueue(struct msgb *llc_msg, const struct timespec *expire_time)
-{
- MetaInfo *meta_storage;
-
- osmo_static_assert(sizeof(*meta_storage) <= sizeof(llc_msg->cb), info_does_not_fit);
-
- m_queue_size += 1;
- m_queue_octets += msgb_length(llc_msg);
-
- meta_storage = (MetaInfo *)&llc_msg->cb[0];
- osmo_clock_gettime(CLOCK_MONOTONIC, &meta_storage->recv_time);
- meta_storage->expire_time = *expire_time;
-
- msgb_enqueue(&m_queue, llc_msg);
-}
-
-void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
-{
- struct msgb *msg;
-
- while ((msg = msgb_dequeue(&q->m_queue))) {
- if (bts)
- bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
- msgb_free(msg);
- }
-
- q->m_queue_size = 0;
- q->m_queue_octets = 0;
-}
-
-void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o)
-{
- struct msgb *msg, *msg1 = NULL, *msg2 = NULL;
- struct llist_head new_queue;
- size_t queue_size = 0;
- size_t queue_octets = 0;
- INIT_LLIST_HEAD(&new_queue);
-
- while (1) {
- if (msg1 == NULL)
- msg1 = msgb_dequeue(&q->m_queue);
-
- if (msg2 == NULL)
- msg2 = msgb_dequeue(&o->m_queue);
-
- if (msg1 == NULL && msg2 == NULL)
- break;
-
- if (msg1 == NULL) {
- msg = msg2;
- msg2 = NULL;
- } else if (msg2 == NULL) {
- msg = msg1;
- msg1 = NULL;
- } else {
- const MetaInfo *mi1 = (MetaInfo *)&msg1->cb[0];
- const MetaInfo *mi2 = (MetaInfo *)&msg2->cb[0];
-
- if (timespeccmp(&mi2->recv_time, &mi1->recv_time, >)) {
- msg = msg1;
- msg1 = NULL;
- } else {
- msg = msg2;
- msg2 = NULL;
- }
- }
-
- msgb_enqueue(&new_queue, msg);
- queue_size += 1;
- queue_octets += msgb_length(msg);
- }
-
- OSMO_ASSERT(llist_empty(&q->m_queue));
- OSMO_ASSERT(llist_empty(&o->m_queue));
-
- o->m_queue_size = 0;
- o->m_queue_octets = 0;
-
- llist_splice_init(&new_queue, &q->m_queue);
- q->m_queue_size = queue_size;
- q->m_queue_octets = queue_octets;
-}
-
-#define ALPHA 0.5f
-
-struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
-{
- struct msgb *msg;
- struct timespec *tv, tv_now, tv_result;
- uint32_t lifetime;
- const MetaInfo *meta_storage;
-
- msg = msgb_dequeue(&m_queue);
- if (!msg)
- return NULL;
-
- meta_storage = (MetaInfo *)&msg->cb[0];
-
- if (info)
- *info = meta_storage;
-
- m_queue_size -= 1;
- m_queue_octets -= msgb_length(msg);
-
- /* take the second time */
- osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
- tv = (struct timespec *)&msg->data[sizeof(*tv)];
- timespecsub(&tv_now, &meta_storage->recv_time, &tv_result);
-
- lifetime = tv_result.tv_sec*1000 + tv_result.tv_nsec/1000000;
- m_avg_queue_delay = m_avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
-
- return msg;
-}
-
-void gprs_llc_queue::calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
-{
- uint16_t delay_csec;
- if (bts->pcu->vty.force_llc_lifetime)
- delay_csec = bts->pcu->vty.force_llc_lifetime;
- else
- delay_csec = pdu_delay_csec;
-
- /* keep timestamp at 0 for infinite delay */
- if (delay_csec == 0xffff) {
- memset(tv, 0, sizeof(*tv));
- return;
- }
-
- /* calculate timestamp of timeout */
- struct timespec now, csec;
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
- csecs_to_timespec(delay_csec, &csec);
-
- timespecadd(&now, &csec, tv);
-}
-
-bool gprs_llc_queue::is_frame_expired(const struct timespec *tv_now,
- const struct timespec *tv)
-{
- /* Timeout is infinite */
- if (tv->tv_sec == 0 && tv->tv_nsec == 0)
- return false;
-
- return timespeccmp(tv_now, tv, >);
-}
diff --git a/src/llc.h b/src/llc.h
index 13662d83..594f77fa 100644
--- a/src/llc.h
+++ b/src/llc.h
@@ -1,5 +1,6 @@
-/*
+/* 3GPP TS 44.064
* Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2022 by by Sysmocom s.f.m.c. GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -10,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -21,112 +18,138 @@
#ifdef __cplusplus
extern "C" {
#endif
- #include <osmocom/core/linuxlist.h>
-#ifdef __cplusplus
-}
-#endif
#include <stdint.h>
#include <string.h>
#include <time.h>
-#define LLC_MAX_LEN 1543
-
-struct gprs_rlcmac_bts;
+#include <osmocom/core/endian.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/endian.h>
-/**
- * I represent the LLC data to a MS
- */
-struct gprs_llc {
+#include "gprs_codel.h"
-#ifdef __cplusplus
- static bool is_user_data_frame(uint8_t *data, size_t len);
-
- void init();
- void reset();
- void reset_frame_space();
+#define LLC_MAX_LEN 1543
- void put_frame(const uint8_t *data, size_t len);
- void put_dummy_frame(size_t req_len);
- void append_frame(const uint8_t *data, size_t len);
+struct gprs_rlcmac_bts;
+struct GprsMs;
+
+struct gprs_llc_hdr {
+#if OSMO_IS_LITTLE_ENDIAN
+ union { /* 5.2, 6.2.0 */
+ uint8_t address;
+ uint8_t sapi:4, unused:2, c_r:1, pd:1;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ union {
+ uint8_t address;
+ uint8_t pd:1, c_r:1, unused:2, sapi:4;
#endif
-
- uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
- uint16_t m_index; /* current write/read position of frame */
- uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */
-};
+ };
+ uint8_t control[0];
+} __attribute__ ((packed));
struct MetaInfo {
struct timespec recv_time;
struct timespec expire_time;
};
+enum gprs_llc_queue_prio { /* lowest value has highest prio */
+ LLC_QUEUE_PRIO_GMM = 0, /* SAPI 1 */
+ LLC_QUEUE_PRIO_TOM_SMS, /* SAPI 2,7,8 */
+ LLC_QUEUE_PRIO_OTHER, /* Other SAPIs */
+ _LLC_QUEUE_PRIO_SIZE /* used to calculate size of enum */
+};
+
/**
- * I store the LLC frames that come from the SGSN.
+ * I represent the LLC data to a MS
*/
-struct gprs_llc_queue {
-#ifdef __cplusplus
- static void calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
- struct timespec *tv);
- static bool is_frame_expired(const struct timespec *now,
- const struct timespec *tv);
- static bool is_user_data_frame(uint8_t *data, size_t len);
+struct gprs_llc {
+ uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
+ uint16_t index; /* current write/read position of frame */
+ uint16_t length; /* len of current DL LLC_frame, 0 == no frame */
- void enqueue(struct msgb *llc_msg, const struct timespec *expire_time);
- struct msgb *dequeue(const MetaInfo **info = 0);
-#endif
- uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
- size_t m_queue_size;
- size_t m_queue_octets;
- struct llist_head m_queue; /* queued LLC DL data */
+ /* Saved when dequeue from llc_queue; allows re-enqueing in the queue if Tx fails */
+ enum gprs_llc_queue_prio prio;
+ struct MetaInfo meta_info;
};
-#ifdef __cplusplus
-extern "C" {
-#endif
-void llc_queue_init(struct gprs_llc_queue *q);
-void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
-void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
+void llc_init(struct gprs_llc *llc);
+void llc_reset(struct gprs_llc *llc);
+void llc_reset_frame_space(struct gprs_llc *llc);
+
+void llc_put_frame(struct gprs_llc *llc, const uint8_t *data, size_t len);
+void llc_put_dummy_frame(struct gprs_llc *llc, size_t req_len);
+void llc_append_frame(struct gprs_llc *llc, const uint8_t *data, size_t len);
static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
{
- return llc->m_length - llc->m_index;
+ return llc->length - llc->index;
}
static inline uint16_t llc_remaining_space(const struct gprs_llc *llc)
{
- return LLC_MAX_LEN - llc->m_length;
+ return LLC_MAX_LEN - llc->length;
}
static inline uint16_t llc_frame_length(const struct gprs_llc *llc)
{
- return llc->m_length;
+ return llc->length;
}
static inline void llc_consume(struct gprs_llc *llc, size_t len)
{
- llc->m_index += len;
+ llc->index += len;
}
static inline void llc_consume_data(struct gprs_llc *llc, uint8_t *data, size_t len)
{
/* copy and increment index */
- memcpy(data, llc->frame + llc->m_index, len);
+ memcpy(data, llc->frame + llc->index, len);
llc_consume(llc, len);
}
static inline bool llc_fits_in_current_frame(const struct gprs_llc *llc, uint8_t chunk_size)
{
- return llc->m_length + chunk_size <= LLC_MAX_LEN;
+ return llc->length + chunk_size <= LLC_MAX_LEN;
}
+/**
+ * I store the LLC frames that come from the SGSN.
+ */
+struct gprs_llc_prio_queue {
+ struct gprs_codel codel_state;
+ struct llist_head queue; /* queued LLC DL data. See enum gprs_llc_queue_prio. */
+};
+struct gprs_llc_queue {
+ struct GprsMs *ms; /* backpointer */
+ uint32_t avg_queue_delay; /* Average delay of data going through the queue */
+ size_t queue_size;
+ size_t queue_octets;
+ bool use_codel;
+ struct gprs_llc_prio_queue pq[_LLC_QUEUE_PRIO_SIZE]; /* queued LLC DL data. See enum gprs_llc_queue_prio. */
+};
+
+void llc_queue_calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
+ struct timespec *tv);
+bool llc_queue_is_frame_expired(const struct timespec *tv_now, const struct timespec *tv);
+
+void llc_queue_init(struct gprs_llc_queue *q, struct GprsMs *ms);
+void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
+void llc_queue_set_codel_interval(struct gprs_llc_queue *q, unsigned int interval);
+void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
+void llc_queue_merge_prepend(struct gprs_llc_queue *q, struct gprs_llc *llc);
+void llc_queue_enqueue(struct gprs_llc_queue *q, struct msgb *llc_msg, const struct timespec *expire_time);
+struct msgb *llc_queue_dequeue(struct gprs_llc_queue *q, enum gprs_llc_queue_prio *out_prio, struct MetaInfo *out_info);
+
static inline size_t llc_queue_size(const struct gprs_llc_queue *q)
{
- return q->m_queue_size;
+ return q->queue_size;
}
static inline size_t llc_queue_octets(const struct gprs_llc_queue *q)
{
- return q->m_queue_octets;
+ return q->queue_octets;
}
#ifdef __cplusplus
diff --git a/src/mslot_class.c b/src/mslot_class.c
index ff40f297..caab2b51 100644
--- a/src/mslot_class.c
+++ b/src/mslot_class.c
@@ -14,14 +14,11 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <mslot_class.h>
#include <gprs_debug.h>
+#include <gprs_pcu.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/utils.h>
@@ -90,7 +87,7 @@ static const struct gprs_ms_multislot_class gprs_ms_multislot_class[] = {
static inline const struct gprs_ms_multislot_class *get_mslot_table(uint8_t ms_cl)
{
- uint8_t index = ms_cl ? ms_cl : DEFAULT_MSLOT_CLASS;
+ uint8_t index = ms_cl ? ms_cl : the_pcu->vty.msclass_default;
if (ms_cl >= ARRAY_SIZE(gprs_ms_multislot_class))
index = 0;
diff --git a/src/mslot_class.h b/src/mslot_class.h
index 045fb317..0d48c83d 100644
--- a/src/mslot_class.h
+++ b/src/mslot_class.h
@@ -14,10 +14,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -26,7 +22,7 @@
#include <stddef.h>
#include <stdbool.h>
-/* 3GPP TS 05.02 Annex B.1 */
+/* 3GPP TS 45.002 Annex B.1 */
#define MS_NA 255 /* N/A */
#define MS_A 254 /* 1 with hopping, 0 without */
@@ -34,8 +30,6 @@
#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/
#define MS_TO 251 /* 31 symbol periods (this can be provided by a TA offset, i.e. a minimum TA value) */
-#define DEFAULT_MSLOT_CLASS 12
-
#define NO_FREE_TFI 0xffffffff
enum { MASK_TT = 0, MASK_TR = 1 };
diff --git a/src/nacc_fsm.c b/src/nacc_fsm.c
index ba7bbb78..24954a4e 100644
--- a/src/nacc_fsm.c
+++ b/src/nacc_fsm.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
@@ -172,7 +168,7 @@ static struct msgb *create_packet_cell_chg_continue(const struct nacc_fsm_ctx *c
struct GprsMs *ms = tbf_ms(tbf);
unsigned int rrbp;
- rc = tbf_check_polling(tbf, data->fn, data->ts, new_poll_fn, &rrbp);
+ rc = tbf_check_polling(tbf, data->pdch, data->fn, new_poll_fn, &rrbp);
if (rc < 0) {
LOGP(DTBF, LOGL_ERROR, "Failed registering poll for Pkt Cell Chg Continue (%d)\n", rc);
return NULL;
@@ -195,7 +191,7 @@ static struct msgb *create_packet_cell_chg_continue(const struct nacc_fsm_ctx *c
uint8_t tfi_is_dl = tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF;
uint8_t tfi = tbf_tfi(tbf);
uint8_t container_id = 0;
- write_packet_cell_change_continue(mac_control_block, 1, rrbp, tfi_is_dl, tfi, true,
+ write_packet_cell_change_continue(mac_control_block, 1, rrbp, tfi_is_dl, tfi, ctx->neigh_key_present,
ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic, container_id);
LOGP(DNACC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Cell Change Continue +++++++++++++++++++++++++\n");
rc = encode_gsm_rlcmac_downlink(&bv, mac_control_block);
@@ -206,9 +202,10 @@ static struct msgb *create_packet_cell_chg_continue(const struct nacc_fsm_ctx *c
LOGP(DNACC, LOGL_DEBUG, "------------------------- TX : Packet Cell Change Continue -------------------------\n");
rate_ctr_inc(rate_ctr_group_get_ctr(bts_rate_counters(ms->bts), CTR_PKT_CELL_CHG_CONTINUE));
talloc_free(mac_control_block);
- tbf_set_polling(tbf, *new_poll_fn, data->ts, PDCH_ULC_POLL_CELL_CHG_CONTINUE);
- LOGPTBFDL(tbf, LOGL_DEBUG, "Scheduled 'Packet Cell Change Continue' polling on PACCH (FN=%d, TS=%d)\n",
- *new_poll_fn, data->ts);
+ tbf_set_polling(tbf, data->pdch, *new_poll_fn, PDCH_ULC_POLL_CELL_CHG_CONTINUE);
+ LOGPTBF(tbf, LOGL_DEBUG,
+ "Scheduled 'Packet Cell Change Continue' polling on PACCH (FN=%d, TS=%d)\n",
+ *new_poll_fn, data->pdch->ts_no);
return msg;
free_ret:
@@ -273,19 +270,94 @@ static int fill_rim_ran_info_req(const struct nacc_fsm_ctx *ctx, struct bssgp_ra
return 0;
}
+/* Generate neighbour key information from a given PacketCellCHangeNotification and a BTS object (lac and cid). The
+ * ctx variable is used for logging only. */
static int fill_neigh_key_from_bts_pkt_cell_chg_not(struct neigh_cache_entry_key *neigh_key,
+ bool *neigh_key_present,
const struct gprs_rlcmac_bts *bts,
- const Packet_Cell_Change_Notification_t *notif)
+ const Packet_Cell_Change_Notification_t *notif,
+ const struct nacc_fsm_ctx *ctx)
{
+ const Target_Cell_GSM_Notif_t *notif_gsm;
+ const Target_Cell_3G_Notif_t *notif_3g;
+ const Target_Cell_4G_Notif_t *notif_4g;
+
+ memset(neigh_key, 0, sizeof(*neigh_key));
+ *neigh_key_present = false;
+
switch (notif->Target_Cell.UnionType) {
- case 0: /* GSM */
+ case 0: /* GSM */
+ notif_gsm = &notif->Target_Cell.u.Target_Cell_GSM_Notif;
+ LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell: RAT=GSM, ARFCN=%u, BSIC=%u\n",
+ notif_gsm->ARFCN, notif_gsm->BSIC);
+
neigh_key->local_lac = bts->cgi_ps.rai.lac.lac;
neigh_key->local_ci = bts->cgi_ps.cell_identity;
- neigh_key->tgt_arfcn = notif->Target_Cell.u.Target_Cell_GSM_Notif.ARFCN;
- neigh_key->tgt_bsic = notif->Target_Cell.u.Target_Cell_GSM_Notif.BSIC;
+ neigh_key->tgt_arfcn = notif_gsm->ARFCN;
+ neigh_key->tgt_bsic = notif_gsm->BSIC;
+ *neigh_key_present = true;
return 0;
default:
- return -ENOTSUP;
+ switch (notif->Target_Cell.u.Target_Other_RAT_Notif.UnionType) {
+ case 0: /* UTRAN */
+ notif_3g = &notif->Target_Cell.u.Target_Other_RAT_Notif.u.Target_Cell_3G_Notif;
+ if (notif_3g->Exist_FDD_Description) {
+ LOGPFSML(ctx->fi, LOGL_NOTICE,
+ "TargetCell: RAT=UTRAN, FDD-ARFCN=%u => no system information provided.\n",
+ notif_3g->FDD_Target_Cell_Notif.FDD_ARFCN);
+ } else if (notif_3g->Exist_TDD_Description) {
+ LOGPFSML(ctx->fi, LOGL_NOTICE,
+ "TargetCell: RAT=UTRAN, TDD-ARFCN=%u => no system information provided.\n",
+ notif_3g->TDD_Target_Cell.TDD_ARFCN);
+ }
+ return 0;
+ default:
+ switch (notif->Target_Cell.u.Target_Other_RAT_Notif.u.Target_Other_RAT_2_Notif.UnionType) {
+ case 0: /* E-UTRAN (and older RAT) */
+ notif_4g =
+ &notif->Target_Cell.u.Target_Other_RAT_Notif.u.Target_Other_RAT_2_Notif.u.Target_Cell_4G_Notif;
+ notif_3g = &notif_4g->Target_Cell_3G_Notif;
+ if (notif_4g->Exist_Arfcn) {
+ LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell: RAT=GSM, ARFCN=%u, BSIC=%u\n",
+ notif_4g->Arfcn, notif_4g->bsic);
+ neigh_key->local_lac = bts->cgi_ps.rai.lac.lac;
+ neigh_key->local_ci = bts->cgi_ps.cell_identity;
+ neigh_key->tgt_arfcn = notif_4g->Arfcn;
+ neigh_key->tgt_bsic = notif_4g->bsic;
+ *neigh_key_present = true;
+ return 0;
+ }
+ if (notif_4g->Exist_3G_Target_Cell) {
+ if (notif_3g->Exist_FDD_Description) {
+ LOGPFSML(ctx->fi, LOGL_NOTICE,
+ "TargetCell: RAT=UTRAN, FDD-ARFCN=%u => no system information provided.\n",
+ notif_3g->FDD_Target_Cell_Notif.FDD_ARFCN);
+ } else if (notif_3g->Exist_TDD_Description) {
+ LOGPFSML(ctx->fi, LOGL_NOTICE,
+ "TargetCell: RAT=UTRAN, TDD-ARFCN=%u => no system information provided.\n",
+ notif_3g->TDD_Target_Cell.TDD_ARFCN);
+ }
+ return 0;
+ }
+ if (notif_4g->Exist_Eutran_Target_Cell) {
+ LOGPFSML(ctx->fi, LOGL_NOTICE,
+ "TargetCell: RAT=E-UTRAN, EARFCN=%u, CI=%u => no system information provided.\n",
+ notif_4g->Target_EUTRAN_Cell.EARFCN,
+ notif_4g->Target_EUTRAN_Cell.Physical_Layer_Cell_Identity);
+ return 0;
+ }
+
+ /* TODO: do something meaningful with an Eutran_Ccn_Measurement_Report, in case it is
+ * provided. */
+
+ LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell: (none, invalid)\n");
+ return -EINVAL;
+ default:
+ LOGPFSML(ctx->fi, LOGL_NOTICE,
+ "TargetCell: RAT=CSG-UTRAN|CSG-EUTRAN, (not supported)\n");
+ return -ENOTSUP; /* TODO: Add support */
+ }
+ }
}
}
@@ -319,17 +391,28 @@ static void handle_retrans_pkt_cell_chg_notif(struct nacc_fsm_ctx *ctx, const Pa
{
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
struct neigh_cache_entry_key neigh_key;
+ bool neigh_key_present;
+ int rc;
- if (fill_neigh_key_from_bts_pkt_cell_chg_not(&neigh_key, bts, notif) < 0) {
- LOGPFSML(ctx->fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
- notif->Target_Cell.UnionType);
+ rc = fill_neigh_key_from_bts_pkt_cell_chg_not(&neigh_key, &neigh_key_present, bts, notif, ctx);
+ if (rc < 0) {
+ /* (see comment below) */
if (ctx->fi->state != NACC_ST_TX_CELL_CHG_CONTINUE)
nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
return;
+ } else if (!neigh_key_present) {
+ /* In case no neighbour key information is present, (This would be the case for UTRAN or EUTRAN cells)
+ * then we will not provide any system information. Instead we will send the PacketCellChangeContinue
+ * message immediately. This also applies in the case of re-transmissions. See also: 3GPP TS 48.018,
+ * section 8c.6.1. */
+ nacc_fsm_state_chg(ctx->fi, NACC_ST_TX_CELL_CHG_CONTINUE);
+ return;
}
+
/* If tgt cell changed, restart resolving it */
if (!neigh_cache_entry_key_eq(&ctx->neigh_key, &neigh_key)) {
ctx->neigh_key = neigh_key;
+ ctx->neigh_key_present = neigh_key_present;
nacc_fsm_state_chg(ctx->fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
}
/* else: ignore it, it's a dup, carry on what we were doing */
@@ -344,80 +427,32 @@ static void st_initial(struct osmo_fsm_inst *fi, uint32_t event, void *data)
struct nacc_fsm_ctx *ctx = (struct nacc_fsm_ctx *)fi->priv;
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
Packet_Cell_Change_Notification_t *notif;
+ int rc;
switch (event) {
case NACC_EV_RX_CELL_CHG_NOTIFICATION:
notif = (Packet_Cell_Change_Notification_t *)data;
- if (fill_neigh_key_from_bts_pkt_cell_chg_not(&ctx->neigh_key, bts, notif) < 0) {
- LOGPFSML(fi, LOGL_NOTICE, "TargetCell type=0x%x not supported\n",
- notif->Target_Cell.UnionType);
+ rc = fill_neigh_key_from_bts_pkt_cell_chg_not(&ctx->neigh_key, &ctx->neigh_key_present, bts, notif, ctx);
+ if (rc < 0)
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
- } else {
+ else if (!ctx->neigh_key_present) {
+ /* In case no neighbour key information is present, (This would be the case for UTRAN or EUTRAN
+ * cells) then we will not provide any system information. Instead we will send the
+ * PacketCellChangeContinue message immediately. See also: 3GPP TS 48.018, section 8c.6.1. */
+ nacc_fsm_state_chg(fi, NACC_ST_TX_CELL_CHG_CONTINUE);
+ } else
nacc_fsm_state_chg(fi, NACC_ST_WAIT_RESOLVE_RAC_CI);
- }
break;
default:
OSMO_ASSERT(0);
}
}
-static int send_neigh_addr_req_ctrl_iface(struct nacc_fsm_ctx *ctx)
-{
- struct gprs_rlcmac_bts *bts = ctx->ms->bts;
- struct gprs_pcu *pcu = bts->pcu;
- struct ctrl_cmd *cmd = NULL;
- int rc;
-
- /* We may have changed to this state previously (eg: we are handling
- * another Pkt cell Change Notify with different target). Avoid
- * re-creating the socket in that case. */
- if (ctx->neigh_ctrl_conn->write_queue.bfd.fd == -1) {
- rc = osmo_sock_init2_ofd(&ctx->neigh_ctrl_conn->write_queue.bfd,
- AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP,
- NULL, 0, pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port,
- OSMO_SOCK_F_CONNECT);
- if (rc < 0) {
- LOGPFSML(ctx->fi, LOGL_ERROR,
- "Failed to establish CTRL (neighbor resolution) connection to BSC r=%s:%u\n\n",
- pcu->vty.neigh_ctrl_addr, pcu->vty.neigh_ctrl_port);
- goto err_term;
- }
- }
-
- cmd = ctrl_cmd_create(ctx, CTRL_TYPE_GET);
- if (!cmd) {
- LOGPFSML(ctx->fi, LOGL_ERROR, "CTRL msg creation failed\n");
- goto err_term;
- }
-
- cmd->id = talloc_asprintf(cmd, "%u", arfcn_bsic_2_ctrl_id(ctx->neigh_key.tgt_arfcn,
- ctx->neigh_key.tgt_bsic));
- cmd->variable = talloc_asprintf(cmd, "neighbor_resolve_cgi_ps_from_lac_ci.%d.%d.%d.%d",
- ctx->neigh_key.local_lac, ctx->neigh_key.local_ci,
- ctx->neigh_key.tgt_arfcn, ctx->neigh_key.tgt_bsic);
- rc = ctrl_cmd_send(&ctx->neigh_ctrl_conn->write_queue, cmd);
- if (rc) {
- LOGPFSML(ctx->fi, LOGL_ERROR, "CTRL msg sent failed: %d\n", rc);
- goto err_term;
- }
-
- talloc_free(cmd);
- return 0;
-
-err_term:
- talloc_free(cmd);
- return -1;
-}
-
static int send_neigh_addr_req(struct nacc_fsm_ctx *ctx)
{
struct gprs_rlcmac_bts *bts = ctx->ms->bts;
- /* If PCU was configured to use the old CTRL interface, use it: */
- if (ctx->neigh_ctrl_conn)
- return send_neigh_addr_req_ctrl_iface(ctx);
-
- /* Otherwise, by default the new PCUIF over IPA Abis multiplex proto should be used: */
+ /* Using PCUIF over IPA Abis multiplex proto: */
return pcu_tx_neigh_addr_res_req(bts, &ctx->neigh_key);
}
@@ -437,7 +472,6 @@ static void st_wait_resolve_rac_ci_on_enter(struct osmo_fsm_inst *fi, uint32_t p
}
/* CGI-PS not in cache, resolve it using BSC Neighbor Resolution CTRL interface */
-
LOGPFSML(fi, LOGL_DEBUG, "No CGI-PS found in cache, resolving " NEIGH_CACHE_ENTRY_KEY_FMT "...\n",
NEIGH_CACHE_ENTRY_KEY_ARGS(&ctx->neigh_key));
@@ -611,7 +645,7 @@ static void st_cell_chg_continue(struct osmo_fsm_inst *fi, uint32_t event, void
data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx *)data;
data_ctx->msg = create_packet_cell_chg_continue(ctx, data_ctx, &ctx->continue_poll_fn);
if (data_ctx->msg) {
- ctx->continue_poll_ts = data_ctx->ts;
+ ctx->continue_poll_ts = data_ctx->pdch->ts_no;
nacc_fsm_state_chg(fi, NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK);
}
break;
@@ -677,7 +711,8 @@ static struct osmo_fsm_state nacc_fsm_states[] = {
.in_event_mask =
X(NACC_EV_RX_CELL_CHG_NOTIFICATION),
.out_state_mask =
- X(NACC_ST_WAIT_RESOLVE_RAC_CI),
+ X(NACC_ST_WAIT_RESOLVE_RAC_CI) |
+ X(NACC_ST_TX_CELL_CHG_CONTINUE),
.name = "INITIAL",
.action = st_initial,
},
@@ -803,6 +838,7 @@ void nacc_fsm_ctrl_reply_cb(struct ctrl_handle *ctrl, struct ctrl_cmd *cmd, void
if (!(tok = strtok_r(NULL, "-", &saveptr)))
goto free_ret;
cgi_ps.rai.lac.plmn.mnc = atoi(tok);
+ cgi_ps.rai.lac.plmn.mnc_3_digits = strlen(tok) > 2;
if (!(tok = strtok_r(NULL, "-", &saveptr)))
goto free_ret;
@@ -835,15 +871,6 @@ static int nacc_fsm_ctx_talloc_destructor(struct nacc_fsm_ctx *ctx)
ctx->fi = NULL;
}
- if (ctx->neigh_ctrl_conn) {
- if (ctx->neigh_ctrl_conn->write_queue.bfd.fd != -1) {
- osmo_wqueue_clear(&ctx->neigh_ctrl_conn->write_queue);
- osmo_fd_unregister(&ctx->neigh_ctrl_conn->write_queue.bfd);
- close(ctx->neigh_ctrl_conn->write_queue.bfd.fd);
- ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
- }
- }
-
return 0;
}
@@ -861,19 +888,6 @@ struct nacc_fsm_ctx *nacc_fsm_alloc(struct GprsMs* ms)
if (!ctx->fi)
goto free_ret;
- /* If CTRL ip present, use the old CTRL interface for neighbor resolution */
- if (ms->bts->pcu->vty.neigh_ctrl_addr) {
- ctx->neigh_ctrl = ctrl_handle_alloc(ctx, ctx, NULL);
- ctx->neigh_ctrl->reply_cb = nacc_fsm_ctrl_reply_cb;
- ctx->neigh_ctrl_conn = osmo_ctrl_conn_alloc(ctx, ctx->neigh_ctrl);
- if (!ctx->neigh_ctrl_conn)
- goto free_ret;
- /* Older versions of osmo_ctrl_conn_alloc didn't properly initialize fd to -1,
- * so make sure to do it here otherwise fd may be valid fd 0 and cause trouble */
- ctx->neigh_ctrl_conn->write_queue.bfd.fd = -1;
- llist_add(&ctx->neigh_ctrl_conn->list_entry, &ctx->neigh_ctrl->ccon_list);
- }
-
return ctx;
free_ret:
talloc_free(ctx);
@@ -902,3 +916,11 @@ bool nacc_fsm_exp_ctrl_ack(const struct nacc_fsm_ctx *ctx, uint32_t fn, uint8_t
ctx->continue_poll_fn == fn &&
ctx->continue_poll_ts == ts;
}
+
+bool tbf_nacc_rts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch)
+{
+ if (!tbf_is_control_ts(tbf, pdch))
+ return false;
+
+ return tbf_is_tfi_assigned(tbf) && ms_nacc_rts(tbf_ms(tbf));
+}
diff --git a/src/nacc_fsm.h b/src/nacc_fsm.h
index beea0f75..cab62af5 100644
--- a/src/nacc_fsm.h
+++ b/src/nacc_fsm.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -26,6 +22,7 @@
struct GprsMs;
struct gprs_rlcmac_tbf;
+struct gprs_rlcmac_pdch;
enum nacc_fsm_event {
NACC_EV_RX_CELL_CHG_NOTIFICATION, /* data: Packet_Cell_Change_Notification_t* */
@@ -49,9 +46,8 @@ enum nacc_fsm_states {
struct nacc_fsm_ctx {
struct osmo_fsm_inst *fi;
struct GprsMs* ms; /* back pointer */
- struct ctrl_handle *neigh_ctrl;
- struct ctrl_connection *neigh_ctrl_conn;
struct neigh_cache_entry_key neigh_key; /* target cell info from MS */
+ bool neigh_key_present; /* target cell info from MS is present */
struct osmo_cell_global_id_ps cgi_ps; /* target cell info resolved from req_{arfcn+bsic} */
struct si_cache_value si_info; /* SI info resolved from SGSN, to be sent to MS */
size_t si_info_bytes_sent; /* How many bytes out of si_info->si_len were already sent to MS */
@@ -63,8 +59,8 @@ struct nacc_fsm_ctx {
/* passed as data in NACC_EV_CREATE_RLCMAC_MSG */
struct nacc_ev_create_rlcmac_msg_ctx {
struct gprs_rlcmac_tbf *tbf; /* target tbf to create messages for */
+ const struct gprs_rlcmac_pdch *pdch; /* TS where the created DL ctrl block is to be sent */
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
- uint8_t ts; /* TS where the created DL ctrl block is to be sent */
struct msgb *msg; /* to be filled by FSM during event processing */
};
@@ -77,3 +73,5 @@ bool nacc_fsm_is_waiting_si_resolution(const struct nacc_fsm_ctx *ctx,
const struct osmo_cell_global_id_ps *cgi_ps);
bool nacc_fsm_exp_ctrl_ack(const struct nacc_fsm_ctx *ctx, uint32_t fn, uint8_t ts);
+
+bool tbf_nacc_rts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch);
diff --git a/src/neigh_cache.c b/src/neigh_cache.c
index 3217f408..129e40b9 100644
--- a/src/neigh_cache.c
+++ b/src/neigh_cache.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <string.h>
@@ -73,7 +69,7 @@ static void neigh_cache_schedule_cleanup(struct neigh_cache *cache)
} else {
timespecsub(&threshold, &now, &result);
}
- osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
+ osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec / 1000);
}
struct neigh_cache *neigh_cache_alloc(void *ctx, unsigned int keep_time_sec)
@@ -93,6 +89,26 @@ void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int
neigh_cache_schedule_cleanup(cache);
}
+static struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
+ const struct neigh_cache_entry_key *key)
+{
+ struct neigh_cache_entry *tmp;
+ llist_for_each_entry(tmp, &cache->list, list) {
+ if (neigh_cache_entry_key_eq(&tmp->key, key))
+ return tmp;
+ }
+ return NULL;
+}
+
+const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
+ const struct neigh_cache_entry_key *key)
+{
+ struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
+ if (it)
+ return &it->value;
+ return NULL;
+}
+
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key,
const struct osmo_cell_global_id_ps *value)
@@ -123,26 +139,6 @@ struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
return it;
}
-struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
- const struct neigh_cache_entry_key *key)
-{
- struct neigh_cache_entry *tmp;
- llist_for_each_entry(tmp, &cache->list, list) {
- if (neigh_cache_entry_key_eq(&tmp->key, key))
- return tmp;
- }
- return NULL;
-}
-
-const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
- const struct neigh_cache_entry_key *key)
-{
- struct neigh_cache_entry *it = neigh_cache_lookup_entry(cache, key);
- if (it)
- return &it->value;
- return NULL;
-}
-
void neigh_cache_free(struct neigh_cache *cache)
{
struct neigh_cache_entry *it, *tmp;
@@ -207,7 +203,7 @@ static void si_cache_schedule_cleanup(struct si_cache *cache)
} else {
timespecsub(&threshold, &now, &result);
}
- osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec*1000);
+ osmo_timer_schedule(&cache->cleanup_timer, result.tv_sec, result.tv_nsec / 1000);
}
struct si_cache *si_cache_alloc(void *ctx, unsigned int keep_time_sec)
diff --git a/src/neigh_cache.h b/src/neigh_cache.h
index 90260cd3..777a705b 100644
--- a/src/neigh_cache.h
+++ b/src/neigh_cache.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -69,8 +65,6 @@ void neigh_cache_set_keep_time_interval(struct neigh_cache *cache, unsigned int
struct neigh_cache_entry *neigh_cache_add(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key,
const struct osmo_cell_global_id_ps *value);
-struct neigh_cache_entry *neigh_cache_lookup_entry(struct neigh_cache *cache,
- const struct neigh_cache_entry_key *key);
const struct osmo_cell_global_id_ps *neigh_cache_lookup_value(struct neigh_cache *cache,
const struct neigh_cache_entry_key *key);
void neigh_cache_free(struct neigh_cache *cache);
diff --git a/src/osmo-bts-litecell15/lc15_l1_if.c b/src/osmo-bts-litecell15/lc15_l1_if.c
index 13e0fc92..3b8b21b0 100644
--- a/src/osmo-bts-litecell15/lc15_l1_if.c
+++ b/src/osmo-bts-litecell15/lc15_l1_if.c
@@ -34,6 +34,7 @@
#include <lc15_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
+#include <pcu_l1_if_phy.h>
#include <pdch.h>
#include <bts.h>
@@ -144,6 +145,11 @@ int l1if_connect_pdch(void *obj, uint8_t ts)
return l1if_req_pdch(fl1h, msg);
}
+int l1if_disconnect_pdch(void *obj, uint8_t ts)
+{
+ return 0;
+}
+
static int handle_ph_readytosend_ind(struct lc15l1_hdl *fl1h,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
@@ -361,7 +367,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
return 0;
}
-void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
+void *l1if_open_trx(uint8_t bts_nr, uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct lc15l1_hdl *fl1h;
int rc;
@@ -390,7 +396,7 @@ void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
return fl1h;
}
-int l1if_close_pdch(void *obj)
+int l1if_close_trx(void *obj)
{
struct lc15l1_hdl *fl1h = obj;
if (fl1h)
@@ -398,3 +404,8 @@ int l1if_close_pdch(void *obj)
talloc_free(fl1h);
return 0;
}
+
+int l1if_init(void)
+{
+ return 0;
+}
diff --git a/src/osmo-bts-litecell15/lc15bts.h b/src/osmo-bts-litecell15/lc15bts.h
index 4c40db0f..31ca9239 100644
--- a/src/osmo-bts-litecell15/lc15bts.h
+++ b/src/osmo-bts-litecell15/lc15bts.h
@@ -23,24 +23,24 @@ enum l1prim_type {
};
enum l1prim_type lc15bts_get_l1prim_type(GsmL1_PrimId_t id);
-const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1];
+extern const struct value_string lc15bts_l1prim_names[GsmL1_PrimId_NUM+1];
GsmL1_PrimId_t lc15bts_get_l1prim_conf(GsmL1_PrimId_t id);
enum l1prim_type lc15bts_get_sysprim_type(Litecell15_PrimId_t id);
-const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1];
+extern const struct value_string lc15bts_sysprim_names[Litecell15_PrimId_NUM+1];
Litecell15_PrimId_t lc15bts_get_sysprim_conf(Litecell15_PrimId_t id);
-const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1];
-const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1];
+extern const struct value_string lc15bts_l1sapi_names[GsmL1_Sapi_NUM+1];
+extern const struct value_string lc15bts_l1status_names[GSML1_STATUS_NUM+1];
-const struct value_string lc15bts_tracef_names[29];
-const struct value_string lc15bts_tracef_docs[29];
+extern const struct value_string lc15bts_tracef_names[29];
+extern const struct value_string lc15bts_tracef_docs[29];
-const struct value_string lc15bts_tch_pl_names[15];
+extern const struct value_string lc15bts_tch_pl_names[15];
-const struct value_string lc15bts_clksrc_names[10];
+extern const struct value_string lc15bts_clksrc_names[10];
-const struct value_string lc15bts_dir_names[6];
+extern const struct value_string lc15bts_dir_names[6];
enum pdch_cs {
PDCH_CS_1,
@@ -59,6 +59,6 @@ enum pdch_cs {
_NUM_PDCH_CS
};
-const uint8_t pdch_msu_size[_NUM_PDCH_CS];
+extern const uint8_t pdch_msu_size[_NUM_PDCH_CS];
#endif /* LC15BTS_H */
diff --git a/src/osmo-bts-oc2g/oc2g_l1_if.c b/src/osmo-bts-oc2g/oc2g_l1_if.c
index a41e7afe..2c9da725 100644
--- a/src/osmo-bts-oc2g/oc2g_l1_if.c
+++ b/src/osmo-bts-oc2g/oc2g_l1_if.c
@@ -35,6 +35,7 @@
#include <gprs_debug.h>
#include <pcu_l1_if.h>
+#include <pcu_l1_if_phy.h>
#include <osmocom/pcu/pcuif_proto.h>
#include <bts.h>
@@ -145,6 +146,11 @@ int l1if_connect_pdch(void *obj, uint8_t ts)
return l1if_req_pdch(fl1h, msg);
}
+int l1if_disconnect_pdch(void *obj, uint8_t ts)
+{
+ return 0;
+}
+
static int handle_ph_readytosend_ind(struct oc2gl1_hdl *fl1h,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
@@ -366,7 +372,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
return 0;
}
-void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
+void *l1if_open_trx(uint8_t bts_nr, uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct oc2gl1_hdl *fl1h;
int rc;
@@ -395,7 +401,7 @@ void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1)
return fl1h;
}
-int l1if_close_pdch(void *obj)
+int l1if_close_trx(void *obj)
{
struct oc2gl1_hdl *fl1h = obj;
if (fl1h)
@@ -403,3 +409,8 @@ int l1if_close_pdch(void *obj)
talloc_free(fl1h);
return 0;
}
+
+int l1if_init(void)
+{
+ return 0;
+}
diff --git a/src/osmo-bts-oc2g/oc2gbts.h b/src/osmo-bts-oc2g/oc2gbts.h
index 24deae74..ea17b2df 100644
--- a/src/osmo-bts-oc2g/oc2gbts.h
+++ b/src/osmo-bts-oc2g/oc2gbts.h
@@ -23,24 +23,24 @@ enum l1prim_type {
};
enum l1prim_type oc2gbts_get_l1prim_type(GsmL1_PrimId_t id);
-const struct value_string oc2gbts_l1prim_names[GsmL1_PrimId_NUM+1];
+extern const struct value_string oc2gbts_l1prim_names[GsmL1_PrimId_NUM+1];
GsmL1_PrimId_t oc2gbts_get_l1prim_conf(GsmL1_PrimId_t id);
enum l1prim_type oc2gbts_get_sysprim_type(Oc2g_PrimId_t id);
-const struct value_string oc2gbts_sysprim_names[Oc2g_PrimId_NUM+1];
+extern const struct value_string oc2gbts_sysprim_names[Oc2g_PrimId_NUM+1];
Oc2g_PrimId_t oc2gbts_get_sysprim_conf(Oc2g_PrimId_t id);
-const struct value_string oc2gbts_l1sapi_names[GsmL1_Sapi_NUM+1];
-const struct value_string oc2gbts_l1status_names[GSML1_STATUS_NUM+1];
+extern const struct value_string oc2gbts_l1sapi_names[GsmL1_Sapi_NUM+1];
+extern const struct value_string oc2gbts_l1status_names[GSML1_STATUS_NUM+1];
-const struct value_string oc2gbts_tracef_names[29];
-const struct value_string oc2gbts_tracef_docs[29];
+extern const struct value_string oc2gbts_tracef_names[29];
+extern const struct value_string oc2gbts_tracef_docs[29];
-const struct value_string oc2gbts_tch_pl_names[15];
+extern const struct value_string oc2gbts_tch_pl_names[15];
-const struct value_string oc2gbts_clksrc_names[10];
+extern const struct value_string oc2gbts_clksrc_names[10];
-const struct value_string oc2gbts_dir_names[6];
+extern const struct value_string oc2gbts_dir_names[6];
enum pdch_cs {
PDCH_CS_1,
@@ -59,6 +59,6 @@ enum pdch_cs {
_NUM_PDCH_CS
};
-const uint8_t pdch_msu_size[_NUM_PDCH_CS];
+extern const uint8_t pdch_msu_size[_NUM_PDCH_CS];
#endif /* LC15BTS_H */
diff --git a/src/osmo-bts-sysmo/sysmo_l1_if.c b/src/osmo-bts-sysmo/sysmo_l1_if.c
index f8b10cd8..e97b9bdf 100644
--- a/src/osmo-bts-sysmo/sysmo_l1_if.c
+++ b/src/osmo-bts-sysmo/sysmo_l1_if.c
@@ -15,6 +15,7 @@
#include <sysmo_l1_if.h>
#include <gprs_debug.h>
#include <pcu_l1_if.h>
+#include <pcu_l1_if_phy.h>
#include <pdch.h>
#include <bts.h>
@@ -127,6 +128,11 @@ int l1if_connect_pdch(void *obj, uint8_t ts)
return l1if_req_pdch(fl1h, msg);
}
+int l1if_disconnect_pdch(void *obj, uint8_t ts)
+{
+ return 0;
+}
+
static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
GsmL1_PhReadyToSendInd_t *rts_ind)
{
@@ -346,7 +352,7 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
return 0;
}
-void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
+void *l1if_open_trx(uint8_t bts_nr, uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap)
{
struct femtol1_hdl *fl1h;
int rc;
@@ -372,7 +378,7 @@ void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmta
return fl1h;
}
-int l1if_close_pdch(void *obj)
+int l1if_close_trx(void *obj)
{
struct femtol1_hdl *fl1h = obj;
if (fl1h)
@@ -380,3 +386,8 @@ int l1if_close_pdch(void *obj)
talloc_free(fl1h);
return 0;
}
+
+int l1if_init(void)
+{
+ return 0;
+}
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index 8d9defc1..fb44bd83 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
@@ -44,8 +40,10 @@ extern "C" {
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48_rest_octets.h>
#include <osmocom/gsm/sysinfo.h>
+#include <osmocom/gsm/gsm0502.h>
#include <nacc_fsm.h>
+#include <pcu_l1_if_phy.h>
}
#include <gprs_rlcmac.h>
@@ -57,41 +55,26 @@ extern "C" {
#include <pdch.h>
#include <tbf_ul.h>
#include <tbf_dl.h>
-#include <gprs_ms_storage.h>
-
-// FIXME: move this, when changed from c++ to c.
-extern "C" {
-void *l1if_open_pdch(uint8_t trx_no, uint32_t hlayer1,
- struct gsmtap_inst *gsmtap);
-int l1if_connect_pdch(void *obj, uint8_t ts);
-int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
- uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
-}
+#include <gprs_ms.h>
extern void *tall_pcu_ctx;
-#define PAGING_GROUP_LEN 3
+struct e1_ccu_conn_pars {
+ struct llist_head entry;
-/* returns [0,999] on success, > 999 on error */
-uint16_t imsi2paging_group(const char* imsi)
-{
- uint16_t pgroup = 0;
- size_t len;
-
- len = (imsi != NULL) ? strlen(imsi) : 0;
- if (len < PAGING_GROUP_LEN)
- return 0xFFFF;
- imsi += len - PAGING_GROUP_LEN;
-
- while (*imsi != '\0') {
- if (!isdigit(*imsi))
- return 0xFFFF;
- pgroup *= 10;
- pgroup += *imsi - '0';
- imsi++;
- }
- return pgroup;
-}
+ /* Related air interface */
+ uint8_t bts_nr;
+ uint8_t trx_nr;
+ uint8_t ts_nr;
+
+ /* E1 communication parameter */
+ struct e1_conn_pars e1_conn_pars;
+};
+
+/* List storage to collect E1 connection information that we receive through the pcu_sock. The collected data serves as
+ * a lookup table so that we can lookup the E1 connection information for each PDCH (trx number and timeslot number)
+ * when it is needed. */
+static LLIST_HEAD(e1_ccu_table);
/*
* PCU messages
@@ -248,7 +231,7 @@ void pcu_l1if_tx_ptcch(struct gprs_rlcmac_bts *bts,
uint32_t fn, uint8_t block_nr,
uint8_t *data, size_t data_len)
{
- if (the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PTCCH))
+ if (data_len && the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PTCCH))
gsmtap_send(the_pcu->gsmtap, arfcn, ts, GSMTAP_CHANNEL_PTCCH, 0, fn, 0, 0, data, data_len);
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
@@ -266,43 +249,49 @@ void pcu_l1if_tx_ptcch(struct gprs_rlcmac_bts *bts,
pcu_tx_data_req(bts, trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr, data, data_len);
}
-void pcu_l1if_tx_agch(struct gprs_rlcmac_bts *bts, bitvec * block, int plen)
+/* Send a MAC block via the access grant channel. This will (obviously) only work for MAC blocks that contain
+ * an IMMEDIATE ASSIGNMENT. In case the confirm flag is set, the receiving end is required to send a confirmation
+ * back when the IMMEDIATE ASSIGNMENT has been sent. */
+void pcu_l1if_tx_agch2(struct gprs_rlcmac_bts *bts, bitvec *block, int plen, bool confirm, uint32_t msg_id)
{
- uint8_t data[GSM_MACBLOCK_LEN]; /* prefix PLEN */
+ struct gsm_pcu_if_agch agch = { 0 };
- /* FIXME: why does OpenBTS has no PLEN and no fill in message? */
- bitvec_pack(block, data + 1);
- data[0] = (plen << 2) | 0x01;
+ agch.confirm = confirm;
+ agch.msg_id = msg_id;
+ agch.data[0] = (plen << 2) | 0x01;
+ bitvec_pack(block, agch.data + 1);
if (the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_AGCH))
- gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_AGCH, 0, 0, 0, 0, data, GSM_MACBLOCK_LEN);
+ gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_AGCH, 0, 0, 0, 0, agch.data, GSM_MACBLOCK_LEN);
- pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, GSM_MACBLOCK_LEN);
+ pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_AGCH_2, 0, 0, 0, (uint8_t*)&agch, sizeof(agch));
}
-void pcu_l1if_tx_pch(struct gprs_rlcmac_bts *bts, bitvec * block, int plen, uint16_t pgroup)
+/* Send a MAC block via the paging channel. This will (obviously) only work for MAC blocks that contain an
+ * IMMEDIATE ASSIGNMENT or a PAGING COMMAND message. In case the MAC block contains an IMMEDIATE ASSIGNMENT
+ * message, the receiving end is required to confirm when the IMMEDIATE ASSIGNMENT has been sent. */
+void pcu_l1if_tx_pch2(struct gprs_rlcmac_bts *bts, struct bitvec *block, int plen, bool confirm,
+ const char *imsi, uint32_t msg_id)
{
- uint8_t data[PAGING_GROUP_LEN + GSM_MACBLOCK_LEN];
- int i;
-
- /* prepend paging group */
- for (i = 0; i < PAGING_GROUP_LEN; i++) {
- data[PAGING_GROUP_LEN - 1 - i] = '0' + (char)(pgroup % 10);
- pgroup = pgroup / 10;
- }
- OSMO_ASSERT(pgroup == 0);
-
- /* block provided by upper layer comes without first byte (plen),
- * prepend it manually:
+ struct gsm_pcu_if_pch pch = { 0 };
+
+ pch.msg_id = msg_id;
+ if (imsi)
+ OSMO_STRLCPY_ARRAY(pch.imsi, imsi);
+ /* OS#6097: if strlen(pch.imsi) == 0: We assume the MS is in non-DRX
+ * mode (TS 44.060 5.5.1.5) and hence it is listening on all CCCH blocks
+ * (TS 45.002 6.5.3, 6.5.6).
*/
- OSMO_ASSERT(sizeof(data) >= PAGING_GROUP_LEN + 1 + block->data_len);
- data[3] = (plen << 2) | 0x01;
- bitvec_pack(block, data + PAGING_GROUP_LEN + 1);
+
+ pch.confirm = confirm;
+ pch.data[0] = (plen << 2) | 0x01;
+ bitvec_pack(block, pch.data + 1);
if (the_pcu->gsmtap_categ_mask & (1 << PCU_GSMTAP_C_DL_PCH))
- gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_PCH, 0, 0, 0, 0, data + 3, GSM_MACBLOCK_LEN);
+ gsmtap_send(the_pcu->gsmtap, 0, 0, GSMTAP_CHANNEL_PCH, 0, 0, 0, 0,
+ pch.data, GSM_MACBLOCK_LEN);
- pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, PAGING_GROUP_LEN + GSM_MACBLOCK_LEN);
+ pcu_tx_data_req(bts, 0, 0, PCU_IF_SAPI_PCH_2, 0, 0, 0, (uint8_t*)&pch, sizeof(pch));
}
int pcu_tx_neigh_addr_res_req(struct gprs_rlcmac_bts *bts, const struct neigh_cache_entry_key *neigh_key)
@@ -342,6 +331,9 @@ int pcu_rx_data_ind_pdtch(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_pdch *
{
int rc;
+ /* First of all, update TDMA clock: */
+ bts_set_current_frame_number(bts, fn);
+
if (!pdch->is_enabled()) {
LOGPDCH(pdch, DL1IF, LOGL_INFO, "Received DATA.ind (PDTCH) on disabled TS\n");
return -EINVAL;
@@ -434,6 +426,13 @@ static int pcu_rx_data_ind_bcch(struct gprs_rlcmac_bts *bts, uint8_t *data, uint
si_ro = ((struct gsm48_system_information_type_13*)data)->rest_octets;
if (osmo_gsm48_rest_octets_si13_decode(&bts->si13_ro_decoded, si_ro) < 0)
LOGP(DPCU, LOGL_ERROR, "Error decoding SI13\n");
+ /* Update our cached timers from it: */
+ osmo_tdef_set(bts->T_defs_bts, 3168, bts->si13_ro_decoded.cell_opts.t3168, OSMO_TDEF_MS);
+ osmo_tdef_set(bts->T_defs_bts, 3192, bts->si13_ro_decoded.cell_opts.t3192, OSMO_TDEF_MS);
+ /* Some sanity checks: */
+ if (bts->si13_ro_decoded.cell_opts.t3192 >=
+ osmo_tdef_get(bts->T_defs_bts, 3193, OSMO_TDEF_MS, -1))
+ LOGP(DL1IF, LOGL_ERROR, "Timers incorrectly configured! T3192 >= T3193\n");
break;
default:
LOGP(DL1IF, LOGL_ERROR,
@@ -504,22 +503,19 @@ static int pcu_rx_data_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data *
return rc;
}
-static int pcu_rx_data_cnf(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data *data_cnf)
+static int pcu_rx_data_cnf2(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_data_cnf *data_cnf)
{
int rc = 0;
- int current_fn = bts_current_frame_number(bts);
- LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d cur_fn=%d\n",
- data_cnf->sapi, data_cnf->fn, current_fn);
+ LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d\n", data_cnf->sapi);
switch (data_cnf->sapi) {
- case PCU_IF_SAPI_PCH:
- if (data_cnf->data[2] == 0x3f)
- bts_rcv_imm_ass_cnf(bts, data_cnf->data, data_cnf->fn);
+ case PCU_IF_SAPI_PCH_2:
+ case PCU_IF_SAPI_AGCH_2:
+ bts_rcv_imm_ass_cnf(bts, NULL, data_cnf->msg_id);
break;
default:
- LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
- "unsupported sapi %d\n", data_cnf->sapi);
+ LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with unsupported sapi %d\n", data_cnf->sapi);
rc = -EINVAL;
}
@@ -544,7 +540,7 @@ int pcu_rx_rts_req_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
/* Make sure PDCH time-slot is enabled */
pdch = &bts->trx[trx].pdch[ts];
- if (!pdch->m_is_enabled)
+ if (!pdch_is_enabled(pdch))
return -EAGAIN;
/* If there's no TBF attached to this PDCH, we can skip Tx of PTCCH
@@ -555,11 +551,10 @@ int pcu_rx_rts_req_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
const unsigned num_tbfs = pdch->num_tbfs(GPRS_RLCMAC_DL_TBF)
+ pdch->num_tbfs(GPRS_RLCMAC_UL_TBF);
bool skip_idle = (num_tbfs == 0);
-#ifdef ENABLE_DIRECT_PHY
- /* In DIRECT_PHY mode we want to always submit something to L1 in
- * TRX0, since BTS is not preparing dummy bursts on idle TS for us: */
+
+ if (bts->gen_idle_blocks_C0)
skip_idle = skip_idle && trx != 0;
-#endif
+
if (skip_idle) {
pcu_l1if_tx_ptcch(bts, trx, ts, bts->trx[trx].arfcn, fn, block_nr,
NULL, 0);
@@ -601,7 +596,7 @@ static int pcu_rx_rts_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_rts_req
return rc;
}
-/* C -> C++ adapter for direct DSP access code (e.g. osmo-bts-sysmo) */
+/* C -> C++ adapter for direct PHY access code (e.g. osmo-bts-sysmo) */
extern "C" int pcu_rx_rach_ind_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx_nr, uint8_t ts_nr, uint32_t fn, int16_t qta)
{
struct rach_ind_params rip = {
@@ -611,7 +606,8 @@ extern "C" int pcu_rx_rach_ind_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx_nr
.ra = 0x00,
.trx_nr = trx_nr,
.ts_nr = ts_nr,
- .rfn = fn,
+ .rfn = fn2rfn(fn),
+ .fn = fn,
.qta = qta,
};
@@ -621,11 +617,28 @@ extern "C" int pcu_rx_rach_ind_ptcch(struct gprs_rlcmac_bts *bts, uint8_t trx_nr
static int pcu_rx_rach_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_rach_ind *rach_ind)
{
int rc = 0;
- int current_fn = bts_current_frame_number(bts);
+ uint32_t current_fn = bts_current_frame_number(bts);
+ uint16_t rfn;
+
+ /* Note: If a BSC is sending a RACH req to us, it is actually forwarding it to
+ * us from BTS as a result of receiving an RFN (Fn % 42432) over RSL
+ * (see 3GPP TS 48.058, section 9.3.19).
+ * If a BTS is sending a RACH req to us, it may contain a full FN
+ * (current osmo-bts does that) instead of an RFN.
+ * For consistency, and taking into account the BSC case limitations,
+ * work always with RFNs here:
+ */
+ rfn = fn2rfn(rach_ind->fn);
+
+ LOGP(DL1IF, LOGL_INFO,
+ "RACH request received: sapi=%d qta=%d, ra=0x%02x, fn=%u (rfn=%u), cur_fn=%d, is_11bit=%d\n",
+ rach_ind->sapi, rach_ind->qta, rach_ind->ra, rach_ind->fn, rfn, current_fn, rach_ind->is_11bit);
- LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
- "qta=%d, ra=0x%02x, fn=%u, cur_fn=%d, is_11bit=%d\n", rach_ind->sapi, rach_ind->qta,
- rach_ind->ra, rach_ind->fn, current_fn, rach_ind->is_11bit);
+ if (OSMO_UNLIKELY(rach_ind->fn > GSM_TDMA_HYPERFRAME - 1)) {
+ LOGP(DL1IF, LOGL_ERROR, "RACH request contains fn=%u that exceeds valid limits (0-%u) -- ignored!\n",
+ rach_ind->fn, GSM_TDMA_HYPERFRAME - 1);
+ return -EINVAL;
+ }
struct rach_ind_params rip = {
.burst_type = (enum ph_burst_type) rach_ind->burst_type,
@@ -633,7 +646,8 @@ static int pcu_rx_rach_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_
.ra = rach_ind->ra,
.trx_nr = rach_ind->trx_nr,
.ts_nr = rach_ind->ts_nr,
- .rfn = rach_ind->fn,
+ .rfn = rfn,
+ .fn = bts_rfn_to_fn(bts, rfn),
.qta = rach_ind->qta,
};
@@ -709,6 +723,38 @@ static int pcu_info_ind_ns(struct gprs_rlcmac_bts *bts,
return gprs_ns_update_config(bts, info_ind->nsei, local, remote, nsvci, valid);
}
+const struct value_string gsm_pcuif_bts_model_names[] = {
+ { PCU_IF_BTS_MODEL_UNSPEC, "(unspecified)" },
+ { PCU_IF_BTS_MODEL_LC15, "osmo-bts-lc15" },
+ { PCU_IF_BTS_MODEL_OC2G, "osmo-bts-oc2g" },
+ { PCU_IF_BTS_MODEL_OCTPHY, "osmo-bts-octphy" },
+ { PCU_IF_BTS_MODEL_SYSMO, "osmo-bts-sysmo" },
+ { PCU_IF_BTS_MODEL_TRX, "osmo-bts-trx" },
+ { PCU_IF_BTS_MODEL_RBS, "ericsson-rbs" },
+ { 0, NULL }
+};
+
+static bool decide_gen_idle_blocks(struct gprs_rlcmac_bts *bts)
+{
+ switch (bts->bts_model) {
+ case PCU_IF_BTS_MODEL_UNSPEC:
+ case PCU_IF_BTS_MODEL_LC15:
+ case PCU_IF_BTS_MODEL_OC2G:
+ case PCU_IF_BTS_MODEL_OCTPHY:
+ case PCU_IF_BTS_MODEL_SYSMO:
+ case PCU_IF_BTS_MODEL_RBS:
+ /* The BTS models above do not generate dummy blocks by themselves, so OsmoPCU must fill the idle gaps in the
+ * stream of generated PDCH blocks with dummy blocks. */
+ return true;
+ case PCU_IF_BTS_MODEL_TRX:
+ /* The BTS models above generate dummy blocks by themselves, so OsmoBTS will only generate PDCH bloks that
+ * actually contain data. On idle, no blocks are generated. */
+ return false;
+ default:
+ return false;
+ }
+}
+
static int pcu_rx_info_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_info_ind *info_ind)
{
struct gprs_bssgp_pcu *pcu;
@@ -716,14 +762,18 @@ static int pcu_rx_info_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_
unsigned int trx_nr, ts_nr;
unsigned int i;
+ if (llist_count(&the_pcu->bts_list) > 1)
+ LOGP(DL1IF, LOGL_ERROR, "more than one BTS regsitered at this PCU. This PCU has only been tested with one BTS! OS#5930\n");
+
+ LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n");
+
if (info_ind->version != PCU_IF_VERSION) {
- fprintf(stderr, "PCU interface version number of BTS (%u) is "
- "different (%u).\nPlease re-compile!\n",
+ fprintf(stderr, "PCU interface version number of BTS/BSC (%u) is different (%u).\nPlease use a BTS/BSC with a compatble interface!\n",
info_ind->version, PCU_IF_VERSION);
exit(-1);
}
- LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n");
+ the_pcu->pcu_if_version = info_ind->version;
if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
@@ -735,7 +785,8 @@ bssgp_failed:
for (trx_nr = 0; trx_nr < ARRAY_SIZE(bts->trx); trx_nr++) {
bts->trx[trx_nr].arfcn = info_ind->trx[trx_nr].arfcn;
for (ts_nr = 0; ts_nr < ARRAY_SIZE(bts->trx[0].pdch); ts_nr++)
- bts->trx[trx_nr].pdch[ts_nr].disable();
+ if (bts->trx[trx_nr].pdch[ts_nr].is_enabled())
+ bts->trx[trx_nr].pdch[ts_nr].disable();
}
gprs_bssgp_destroy(bts);
exit(0);
@@ -841,23 +892,23 @@ bssgp_failed:
for (trx_nr = 0; trx_nr < ARRAY_SIZE(bts->trx); trx_nr++) {
bts->trx[trx_nr].arfcn = info_ind->trx[trx_nr].arfcn;
- if ((info_ind->flags & PCU_IF_FLAG_SYSMO)
+ if ((info_ind->flags & PCU_IF_FLAG_DIRECT_PHY)
&& info_ind->trx[trx_nr].hlayer1) {
#ifdef ENABLE_DIRECT_PHY
LOGP(DL1IF, LOGL_DEBUG, " TRX %d hlayer1=%x\n", trx_nr,
info_ind->trx[trx_nr].hlayer1);
if (!bts->trx[trx_nr].fl1h)
- bts->trx[trx_nr].fl1h = l1if_open_pdch(
- trx_nr,
+ bts->trx[trx_nr].fl1h = l1if_open_trx(
+ bts->nr, trx_nr,
info_ind->trx[trx_nr].hlayer1,
the_pcu->gsmtap);
if (!bts->trx[trx_nr].fl1h) {
LOGP(DL1IF, LOGL_FATAL, "Failed to open direct "
- "DSP access for PDCH.\n");
+ "PHY access for PDCH.\n");
exit(0);
}
#else
- LOGP(DL1IF, LOGL_FATAL, "Compiled without direct DSP "
+ LOGP(DL1IF, LOGL_FATAL, "Compiled without direct PHY "
"access for PDCH, but enabled at "
"BTS. Please deactivate it!\n");
exit(0);
@@ -865,14 +916,14 @@ bssgp_failed:
}
for (ts_nr = 0; ts_nr < ARRAY_SIZE(bts->trx[0].pdch); ts_nr++) {
- const struct gsm_pcu_if_info_ts *its = &info_ind->trx[trx_nr].ts[ts_nr];
+ const struct gsm_pcu_if_info_trx_ts *its = &info_ind->trx[trx_nr].ts[ts_nr];
struct gprs_rlcmac_pdch *pdch = &bts->trx[trx_nr].pdch[ts_nr];
if ((info_ind->trx[trx_nr].pdch_mask & (1 << ts_nr))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pdch->is_enabled()) {
#ifdef ENABLE_DIRECT_PHY
if ((info_ind->flags &
- PCU_IF_FLAG_SYSMO))
+ PCU_IF_FLAG_DIRECT_PHY))
l1if_connect_pdch(
bts->trx[trx_nr].fl1h, ts_nr);
#endif
@@ -883,7 +934,7 @@ bssgp_failed:
pdch->tsc = its->tsc;
/* (Optional) frequency hopping parameters */
- if (its->h) {
+ if (its->hopping) {
pdch->fh.enabled = true;
pdch->fh.maio = its->maio;
pdch->fh.hsn = its->hsn;
@@ -902,6 +953,10 @@ bssgp_failed:
trx_nr, ts_nr, pdch->tsc, pdch->fh.enabled ? "yes" : "no");
} else {
if (pdch->is_enabled()) {
+#ifdef ENABLE_DIRECT_PHY
+ if ((info_ind->flags & PCU_IF_FLAG_DIRECT_PHY))
+ l1if_disconnect_pdch(bts->trx[trx_nr].fl1h, ts_nr);
+#endif
pcu_tx_act_req(bts, pdch, 0);
pdch->disable();
}
@@ -909,16 +964,85 @@ bssgp_failed:
}
}
+ LOGP(DL1IF, LOGL_INFO, "BTS model: %s\n", get_value_string(gsm_pcuif_bts_model_names, info_ind->bts_model));
+ bts->bts_model = info_ind->bts_model;
+ bts->gen_idle_blocks_C0 = decide_gen_idle_blocks(bts);
+
bts->active = true;
return rc;
}
-static int pcu_rx_time_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_time_ind *time_ind)
+/* Query E1 CCU connection parameters by TS and TRX number */
+int pcu_l1if_get_e1_ccu_conn_pars(struct e1_conn_pars **e1_conn_pars, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
+{
+ struct e1_ccu_conn_pars *e1_ccu_conn_pars;
+
+ llist_for_each_entry(e1_ccu_conn_pars, &e1_ccu_table, entry) {
+ if (e1_ccu_conn_pars->bts_nr == bts_nr && e1_ccu_conn_pars->trx_nr == trx_nr
+ && e1_ccu_conn_pars->ts_nr == ts_nr) {
+ *e1_conn_pars = &e1_ccu_conn_pars->e1_conn_pars;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/* Allocate a new connection parameter struct and store connection parameters */
+static void new_e1_ccu_conn_pars(const struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind, uint8_t bts_nr)
+{
+ struct e1_ccu_conn_pars *e1_ccu_conn_pars;
+
+ e1_ccu_conn_pars = talloc_zero(tall_pcu_ctx, struct e1_ccu_conn_pars);
+ OSMO_ASSERT(e1_ccu_conn_pars);
+ e1_ccu_conn_pars->bts_nr = bts_nr;
+ e1_ccu_conn_pars->trx_nr = e1_ccu_ind->trx_nr;
+ e1_ccu_conn_pars->ts_nr = e1_ccu_ind->ts_nr;
+ e1_ccu_conn_pars->e1_conn_pars.e1_nr = e1_ccu_ind->e1_nr;
+ e1_ccu_conn_pars->e1_conn_pars.e1_ts = e1_ccu_ind->e1_ts;
+ e1_ccu_conn_pars->e1_conn_pars.e1_ts_ss = e1_ccu_ind->e1_ts_ss;
+ llist_add(&e1_ccu_conn_pars->entry, &e1_ccu_table);
+}
+
+static int pcu_rx_e1_ccu_ind(struct gprs_rlcmac_bts *bts, const struct gsm_pcu_if_e1_ccu_ind *e1_ccu_ind)
{
- uint8_t fn13 = time_ind->fn % 13;
+ struct e1_conn_pars *e1_conn_pars;
+ uint8_t rate;
+ uint8_t subslot_nr;
+ int rc;
+
+ /* only used with log statement below, no technical relevance otherwise. */
+ if (e1_ccu_ind->e1_ts_ss > 3) {
+ rate = 64;
+ subslot_nr = 0;
+ } else {
+ rate = 16;
+ subslot_nr = e1_ccu_ind->e1_ts_ss;
+ }
+
+ LOGP(DL1IF, LOGL_NOTICE,
+ "(ts=%u,trx=%u) new E1 CCU communication parameters for CCU (E1-line:%u, E1-TS:%u, E1-SS:%u, rate:%ukbps)\n",
+ e1_ccu_ind->ts_nr, e1_ccu_ind->trx_nr, e1_ccu_ind->e1_nr, e1_ccu_ind->e1_ts,
+ subslot_nr, rate);
+
+ /* Search for an existing entry, when found, update it. */
+ rc = pcu_l1if_get_e1_ccu_conn_pars(&e1_conn_pars, bts->nr, e1_ccu_ind->trx_nr, e1_ccu_ind->ts_nr);
+ if (rc == 0) {
+ e1_conn_pars->e1_nr = e1_ccu_ind->e1_nr;
+ e1_conn_pars->e1_ts = e1_ccu_ind->e1_ts;
+ e1_conn_pars->e1_ts_ss = e1_ccu_ind->e1_ts_ss;
+ return 0;
+ }
+
+ /* Create new connection parameter entry */
+ new_e1_ccu_conn_pars(e1_ccu_ind, bts->nr);
+ return 0;
+}
+static int pcu_rx_time_ind(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_time_ind *time_ind)
+{
/* omit frame numbers not starting at a MAC block */
- if (fn13 != 0 && fn13 != 4 && fn13 != 8)
+ if (!fn_valid(time_ind->fn))
return 0;
LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n", time_ind->fn % 52);
@@ -961,7 +1085,7 @@ static int pcu_rx_pag_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_pag_req
case GSM_MI_TYPE_IMSI:
req.mi_imsi = mi;
req.mi_imsi_present = true;
- ms = bts_ms_by_imsi(bts, req.mi_imsi.imsi);
+ ms = bts_get_ms_by_imsi(bts, req.mi_imsi.imsi);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Unexpected MI type %u\n", mi.type);
@@ -984,7 +1108,7 @@ static int pcu_rx_susp_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_susp_r
LOGP(DL1IF, LOGL_INFO, "GPRS Suspend request received: TLLI=0x%08x RAI=%s\n",
susp_req->tlli, osmo_rai_name(&ra_id));
- if ((ms = bts_ms_store(bts)->get_ms(susp_req->tlli))) {
+ if ((ms = bts_get_ms_by_tlli(bts, susp_req->tlli, GSM_RESERVED_TMSI))) {
/* We need to catch both pointers here since MS may become freed
after first tbf_free(dl_tbf) if only DL TBF was available */
dl_tbf = ms_dl_tbf(ms);
@@ -1009,8 +1133,8 @@ static int pcu_rx_app_info_req(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_ap
app_info_req->application_type, app_info_req->len);
bts->app_info_pending = 0;
- llist_for_each(tmp, bts_ms_store(bts)->ms_list()) {
- GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
+ llist_for_each(tmp, &bts->ms_list) {
+ struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
if (!ms_dl_tbf(ms))
continue;
bts->app_info_pending++;
@@ -1067,8 +1191,8 @@ static int pcu_rx_neigh_addr_cnf(struct gprs_rlcmac_bts *bts, struct gsm_pcu_if_
NEIGH_CACHE_ENTRY_KEY_ARGS(&neigh_key), naddr_cnf->err_code);
}
- llist_for_each(tmp, bts_ms_store(bts)->ms_list()) {
- GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
+ llist_for_each(tmp, &bts->ms_list) {
+ struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
if (ms->nacc && nacc_fsm_is_waiting_addr_resolution(ms->nacc, &neigh_key))
osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_RAC_CI, cgi_ps_ptr);
}
@@ -1125,9 +1249,9 @@ int pcu_rx(struct gsm_pcu_if *pcu_prim, size_t pcu_prim_length)
CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_ind);
rc = pcu_rx_data_ind(bts, &pcu_prim->u.data_ind);
break;
- case PCU_IF_MSG_DATA_CNF:
- CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_cnf);
- rc = pcu_rx_data_cnf(bts, &pcu_prim->u.data_cnf);
+ case PCU_IF_MSG_DATA_CNF_2:
+ CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.data_cnf2);
+ rc = pcu_rx_data_cnf2(bts, &pcu_prim->u.data_cnf2);
break;
case PCU_IF_MSG_RTS_REQ:
CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.rts_req);
@@ -1141,6 +1265,10 @@ int pcu_rx(struct gsm_pcu_if *pcu_prim, size_t pcu_prim_length)
CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.info_ind);
rc = pcu_rx_info_ind(bts, &pcu_prim->u.info_ind);
break;
+ case PCU_IF_MSG_E1_CCU_IND:
+ CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.e1_ccu_ind);
+ rc = pcu_rx_e1_ccu_ind(bts, &pcu_prim->u.e1_ccu_ind);
+ break;
case PCU_IF_MSG_TIME_IND:
CHECK_IF_MSG_SIZE(pcu_prim_length, pcu_prim->u.time_ind);
rc = pcu_rx_time_ind(bts, &pcu_prim->u.time_ind);
@@ -1167,6 +1295,8 @@ int pcu_rx(struct gsm_pcu_if *pcu_prim, size_t pcu_prim_length)
if (pcu_prim_length < exp_len) {
LOGP(DL1IF, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive container size" \
"is %zu, discarding\n", pcu_prim_length, exp_len);
+ rc = -EINVAL;
+ break;
}
rc = pcu_rx_container(bts, &pcu_prim->u.container);
break;
diff --git a/src/pcu_l1_if.h b/src/pcu_l1_if.h
index 7d48febe..b6da337b 100644
--- a/src/pcu_l1_if.h
+++ b/src/pcu_l1_if.h
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef PCU_L1_IF_H
@@ -150,9 +146,8 @@ void pcu_l1if_tx_ptcch(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr,
uint8_t *data, size_t data_len);
-void pcu_l1if_tx_agch(struct gprs_rlcmac_bts *bts, bitvec * block, int len);
-
-void pcu_l1if_tx_pch(struct gprs_rlcmac_bts *bts, bitvec * block, int plen, uint16_t pgroup);
+void pcu_l1if_tx_agch(struct gprs_rlcmac_bts *bts, bitvec *block, int len);
+void pcu_l1if_tx_agch2(struct gprs_rlcmac_bts *bts, bitvec *block, int plen, bool confirm, uint32_t msg_id);
#endif
#ifdef __cplusplus
@@ -161,7 +156,9 @@ extern "C" {
struct gprs_rlcmac_bts;
int pcu_tx_neigh_addr_res_req(struct gprs_rlcmac_bts *bts, const struct neigh_cache_entry_key *neigh_key);
-
+void pcu_l1if_tx_pch(struct gprs_rlcmac_bts *bts, struct bitvec *block, int plen, const char *imsi);
+void pcu_l1if_tx_pch2(struct gprs_rlcmac_bts *bts, struct bitvec *block, int plen, bool confirm,
+ const char *imsi, uint32_t msg_id);
int pcu_rx(struct gsm_pcu_if *pcu_prim, size_t pcu_prim_length);
int pcu_l1if_open(void);
void pcu_l1if_close(void);
@@ -179,7 +176,17 @@ int pcu_rx_data_ind_pdtch(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_pdch *
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas);
void pcu_rx_block_time(struct gprs_rlcmac_bts *bts, uint16_t arfcn, uint32_t fn, uint8_t ts_no);
-uint16_t imsi2paging_group(const char* imsi);
+
+struct e1_conn_pars {
+ /* Number of E1 line */
+ uint8_t e1_nr;
+ /* Number of E1 timeslot */
+ uint8_t e1_ts;
+ /* Number of I.460 subslot inside E1 timeslot */
+ uint8_t e1_ts_ss;
+};
+
+int pcu_l1if_get_e1_ccu_conn_pars(struct e1_conn_pars **e1_conn_pars, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr);
#define PCUIF_HDR_SIZE ( sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u) )
diff --git a/src/pcu_l1_if_phy.h b/src/pcu_l1_if_phy.h
new file mode 100644
index 00000000..08b7d238
--- /dev/null
+++ b/src/pcu_l1_if_phy.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/gsmtap_util.h>
+
+int l1if_init(void);
+void *l1if_open_trx(uint8_t bts_nr, uint8_t trx_no, uint32_t hlayer1, struct gsmtap_inst *gsmtap);
+int l1if_connect_pdch(void *obj, uint8_t ts);
+int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn, uint16_t arfcn, uint8_t block_nr, uint8_t *data,
+ uint8_t len);
+int l1if_disconnect_pdch(void *obj, uint8_t ts);
+int l1if_close_trx(void *obj);
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index 003cabb4..ec7bddd4 100644
--- a/src/pcu_main.cpp
+++ b/src/pcu_main.cpp
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <pcu_l1_if.h>
@@ -32,10 +28,12 @@
#include <bts.h>
#include <osmocom/pcu/pcuif_proto.h>
#include "gprs_bssgp_pcu.h"
+#include "alloc_algo.h"
extern "C" {
#include "pcu_vty.h"
#include "coding_scheme.h"
+#include "pcu_l1_if_phy.h"
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns2.h>
#include <osmocom/vty/telnet_interface.h>
@@ -64,7 +62,7 @@ extern void *bv_tall_ctx;
static int quit = 0;
static int rt_prio = -1;
static bool daemonize = false;
-static const char *gsmtap_addr = "localhost"; // FIXME: use gengetopt's default value instead
+static const char *gsmtap_addr;
static void print_help()
{
@@ -74,9 +72,7 @@ static void print_help()
" -m --mcc MCC Use given MCC instead of value provided by BTS\n"
" -n --mnc MNC Use given MNC instead of value provided by BTS\n"
" -V --version Print version\n"
- " -r --realtime PRIO Use SCHED_RR with the specified priority\n"
" -D --daemonize Fork the process into a background daemon\n"
- " -i --gsmtap-ip The destination IP used for GSMTAP\n"
"\nVTY reference generation:\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
@@ -162,9 +158,13 @@ static void handle_options(int argc, char **argv)
break;
case 'i':
gsmtap_addr = optarg;
+ fprintf(stderr, "Command line argument '-i' is deprecated, use VTY "
+ "parameter 'gsmtap-remote-host %s' instead.\n", gsmtap_addr);
break;
case 'r':
rt_prio = atoi(optarg);
+ fprintf(stderr, "Command line argument '-r' is deprecated, use VTY "
+ "cpu-sched node setting 'policy rr %d' instead.\n", rt_prio);
break;
case 'D':
daemonize = true;
@@ -239,6 +239,7 @@ int main(int argc, char *argv[])
pcu->pcu_sock_path = talloc_strdup(tall_pcu_ctx, PCU_SOCK_DEFAULT);
+ osmo_fsm_log_addr(false);
msgb_talloc_ctx_init(tall_pcu_ctx, 0);
osmo_stats_init(tall_pcu_ctx);
@@ -250,6 +251,12 @@ int main(int argc, char *argv[])
osmo_cpu_sched_vty_init(tall_pcu_ctx);
logging_vty_add_deprecated_subsys(tall_pcu_ctx, "bssgp");
+#ifdef ENABLE_DIRECT_PHY
+ rc = l1if_init();
+ if (rc < 0)
+ return -EINVAL;
+#endif
+
handle_options(argc, argv);
if ((!!spoof_mcc) + (!!spoof_mnc) == 1) {
fprintf(stderr, "--mcc and --mnc must be specified "
@@ -257,13 +264,6 @@ int main(int argc, char *argv[])
exit(0);
}
- pcu->gsmtap = gsmtap_source_init(gsmtap_addr, GSMTAP_UDP_PORT, 1);
-
- if (pcu->gsmtap)
- gsmtap_source_add_sink(pcu->gsmtap);
- else
- fprintf(stderr, "Failed to initialize GSMTAP for %s\n", gsmtap_addr);
-
pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
if (!pcu->nsi) {
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
@@ -282,8 +282,32 @@ int main(int argc, char *argv[])
fprintf(stderr, "No config file: '%s' Using default config.\n",
config_file);
- rc = telnet_init_dynif(tall_pcu_ctx, NULL, vty_get_bind_addr(),
- OSMO_VTY_PORT_PCU);
+ /* Accept a GSMTAP host from VTY config, but a commandline option overrides that. */
+ if (gsmtap_addr) {
+ if (pcu->gsmtap_remote_host != NULL) {
+ LOGP(DLGLOBAL, LOGL_NOTICE,
+ "Command line argument '-i %s' overrides "
+ "'gsmtap-remote-host %s' from the config file\n",
+ gsmtap_addr, pcu->gsmtap_remote_host);
+ talloc_free(pcu->gsmtap_remote_host);
+ }
+ pcu->gsmtap_remote_host = talloc_strdup(pcu, gsmtap_addr);
+ }
+
+ if (pcu->gsmtap_remote_host) {
+ LOGP(DLGLOBAL, LOGL_NOTICE,
+ "Setting up GSMTAP Um forwarding to '%s:%u'\n",
+ pcu->gsmtap_remote_host, GSMTAP_UDP_PORT);
+ pcu->gsmtap = gsmtap_source_init(pcu->gsmtap_remote_host,
+ GSMTAP_UDP_PORT, 1);
+ if (pcu->gsmtap == NULL) {
+ fprintf(stderr, "Failed during gsmtap_source_init()\n");
+ exit(1);
+ }
+ gsmtap_source_add_sink(pcu->gsmtap);
+ }
+
+ rc = telnet_init_default(tall_pcu_ctx, NULL, OSMO_VTY_PORT_PCU);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
diff --git a/src/pcu_utils.h b/src/pcu_utils.h
index e1a8572a..540f7049 100644
--- a/src/pcu_utils.h
+++ b/src/pcu_utils.h
@@ -10,10 +10,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#ifdef __cplusplus
@@ -71,23 +67,25 @@ static inline unsigned fn_next_block(unsigned fn)
return fn % GSM_MAX_FN;
}
-#ifdef __cplusplus
-template <typename T>
-inline unsigned int pcu_bitcount(T x)
+inline unsigned int pcu_bitcount(unsigned long long x)
{
- unsigned int count = 0;
- for (count = 0; x; count += 1)
- x &= x - 1;
-
- return count;
+ return __builtin_popcountll(x);
}
-#endif
+/* Trim all 1 bits except the Least Significat Bit, which is kept.
+ * equivalent of (1 << ffs(u)) / 2
+ */
static inline uint8_t pcu_lsb(uint8_t x)
{
return x & -x;
}
+#define RFN_MODULUS 42432
+static inline uint16_t fn2rfn(uint32_t fn)
+{
+ return fn % RFN_MODULUS;
+}
+
/* Used to store a C++ class in a llist used by C code */
struct llist_item {
struct llist_head list; /* item used by llist */
diff --git a/src/pcu_vty.c b/src/pcu_vty.c
index c85e324b..9de99c82 100644
--- a/src/pcu_vty.c
+++ b/src/pcu_vty.c
@@ -23,6 +23,7 @@
#include "bts.h"
#include "tbf.h"
#include "pcu_vty_functions.h"
+#include "alloc_algo.h"
#define X(x) (1 << x)
@@ -70,6 +71,49 @@ static const struct value_string pcu_gsmtap_categ_help[] = {
{ 0, NULL }
};
+DEFUN(cfg_pcu_gsmtap_remote_host,
+ cfg_pcu_gsmtap_remote_host_cmd,
+ "gsmtap-remote-host [HOSTNAME]",
+ "Enable GSMTAP Um logging (see also 'gsmtap-category')\n"
+ "Remote IP address or hostname ('localhost' if omitted)\n")
+{
+ osmo_talloc_replace_string(the_pcu, &the_pcu->gsmtap_remote_host,
+ argc > 0 ? argv[0] : "localhost");
+
+ if (vty->type != VTY_FILE)
+ vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pcu_no_gsmtap_remote_host,
+ cfg_pcu_no_gsmtap_remote_host_cmd,
+ "no gsmtap-remote-host",
+ NO_STR "Disable GSMTAP Um logging\n")
+{
+ if (the_pcu->gsmtap_remote_host)
+ TALLOC_FREE(the_pcu->gsmtap_remote_host);
+
+ if (vty->type != VTY_FILE)
+ vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pcu_gsmtap_sapi_all, pcucfg_pcu_gsmtap_categ_all_cmd,
+ "gsmtap-category (enable-all|disable-all)",
+ "Enable/disable sending of UL/DL messages over GSMTAP\n"
+ "Enable all kinds of messages (all categories)\n"
+ "Disable all kinds of messages (all categories)\n")
+{
+
+ if (strcmp(argv[0], "enable-all") == 0)
+ the_pcu->gsmtap_categ_mask = UINT32_MAX;
+ else
+ the_pcu->gsmtap_categ_mask = 0x00;
+
+ return CMD_SUCCESS;
+}
DEFUN(cfg_pcu_gsmtap_categ, cfg_pcu_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
{
@@ -235,14 +279,19 @@ static int config_write_pcu(struct vty *vty)
vty_out(vty, " gamma %d%s", the_pcu->vty.gamma * 2, VTY_NEWLINE);
if (!the_pcu->vty.dl_tbf_preemptive_retransmission)
vty_out(vty, " no dl-tbf-preemptive-retransmission%s", VTY_NEWLINE);
+ if (the_pcu->vty.msclass_default != PCU_DEFAULT_MSLOT_CLASS)
+ vty_out(vty, " multislot-class default %u%s", the_pcu->vty.msclass_default, VTY_NEWLINE);
if (strcmp(the_pcu->pcu_sock_path, PCU_SOCK_DEFAULT))
vty_out(vty, " pcu-socket %s%s", the_pcu->pcu_sock_path, VTY_NEWLINE);
+ if (the_pcu->gsmtap_remote_host)
+ vty_out(vty, " gsmtap-remote-host %s%s", the_pcu->gsmtap_remote_host, VTY_NEWLINE);
for (i = 0; i < 32; i++) {
- uint32_t cs = ((uint32_t)1 << i);
- if (the_pcu->gsmtap_categ_mask & cs) {
- vty_out(vty, " gsmtap-category %s%s",
- get_value_string(pcu_gsmtap_categ_names, i), VTY_NEWLINE);
+ if (the_pcu->gsmtap_categ_mask & ((uint32_t)1 << i)) {
+ const char *category_buf;
+ if (!(category_buf = get_value_string_or_null(pcu_gsmtap_categ_names, i)))
+ continue;
+ vty_out(vty, " gsmtap-category %s%s", category_buf, VTY_NEWLINE);
}
}
@@ -256,11 +305,6 @@ static int config_write_pcu(struct vty *vty)
if (the_pcu->vty.ns_priority != -1)
vty_out(vty, " gb socket-priority %d%s", the_pcu->vty.ns_priority, VTY_NEWLINE);
- if (the_pcu->vty.neigh_ctrl_addr) {
- vty_out(vty, " neighbor resolution %s %u%s",
- the_pcu->vty.neigh_ctrl_addr, the_pcu->vty.neigh_ctrl_port, VTY_NEWLINE);
- }
-
osmo_tdef_vty_write(vty, the_pcu->T_defs, " timer ");
return CMD_SUCCESS;
@@ -840,6 +884,19 @@ DEFUN_ATTR(cfg_pcu_no_dl_tbf_preemptive_retransmission,
return CMD_SUCCESS;
}
+DEFUN_ATTR_USRATTR(cfg_pcu_msclass_default,
+ cfg_pcu_msclass_default_cmd,
+ CMD_ATTR_HIDDEN,
+ X(PCU_VTY_ATTR_NEW_TBF),
+ "multislot-class default <1-45>",
+ "MultiSlot Class configuration\n"
+ "Set assumed default MultiSlot Class if unknown during TBF allocation\n"
+ "MultiSlot Class number to use as default (default: 12)\n")
+{
+ the_pcu->vty.msclass_default = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
#define MS_IDLE_TIME_STR "keep an idle MS object alive for the time given\n"
DEFUN_DEPRECATED(cfg_pcu_ms_idle_time,
cfg_pcu_ms_idle_time_cmd,
@@ -1060,26 +1117,6 @@ DEFUN_USRATTR(cfg_pcu_gb_priority,
return CMD_SUCCESS;
}
-DEFUN(cfg_neighbor_resolution, cfg_neighbor_resolution_cmd,
- "neighbor resolution " VTY_IPV46_CMD " [<0-65535>]",
- "Manage local and remote-BSS neighbor cells\n"
- "Connect to Neighbor Resolution Service (CTRL interface) to given ip and port\n"
- "IPv4 address to connect to\n" "IPv6 address to connect to\n"
- "Port to connect to (default 4248)\n")
-{
- vty_out(vty, "%% Warning: The CTRL interface for Neighbor Address Resolution is now deprecated."
- "Upgrade osmo-bsc and drop the 'neighbor resolution " VTY_IPV46_CMD " [<0-65535>]' VTY "
- "option in order to let osmo-pcu use the new resoluton method using the PCUIF over IPA "
- "multiplex, which will work out of the box without required configuration.%s", VTY_NEWLINE);
- osmo_talloc_replace_string(the_pcu, &the_pcu->vty.neigh_ctrl_addr, argv[0]);
- if (argc > 1)
- the_pcu->vty.neigh_ctrl_port = atoi(argv[1]);
- else
- the_pcu->vty.neigh_ctrl_port = OSMO_CTRL_PORT_BSC_NEIGH;
- return CMD_SUCCESS;
-}
-
-
DEFUN(show_bts_timer, show_bts_timer_cmd,
"show bts-timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,
SHOW_STR "Show BTS controlled timers\n"
@@ -1195,8 +1232,8 @@ DEFUN(show_ms_imsi,
}
static const char pcu_copyright[] =
- "Copyright (C) 2012 by Ivan Kluchnikov <kluchnikovi@gmail.com> and \r\n"
- " Andreas Eversberg <jolly@eversberg.eu>\r\n"
+ "Copyright (C) 2012-2013 by Ivan Kluchnikov and Andreas Eversberg\r\n"
+ "Copyright (C) 2013-2022 by sysmocom - s.f.m.c. GmbH\r\n"
"License GNU GPL version 2 or later\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
@@ -1292,15 +1329,18 @@ int pcu_vty_init(void)
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_dl_tbf_preemptive_retransmission_cmd);
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_preemptive_retransmission_cmd);
+ install_element(PCU_NODE, &cfg_pcu_msclass_default_cmd);
install_element(PCU_NODE, &cfg_pcu_ms_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_ms_idle_time_cmd);
+ install_element(PCU_NODE, &cfg_pcu_gsmtap_remote_host_cmd);
+ install_element(PCU_NODE, &cfg_pcu_no_gsmtap_remote_host_cmd);
+ install_element(PCU_NODE, &pcucfg_pcu_gsmtap_categ_all_cmd);
install_element(PCU_NODE, &cfg_pcu_gsmtap_categ_cmd);
install_element(PCU_NODE, &cfg_pcu_no_gsmtap_categ_cmd);
install_element(PCU_NODE, &cfg_pcu_sock_cmd);
install_element(PCU_NODE, &cfg_pcu_gb_dialect_cmd);
install_element(PCU_NODE, &cfg_pcu_gb_ip_dscp_cmd);
install_element(PCU_NODE, &cfg_pcu_gb_priority_cmd);
- install_element(PCU_NODE, &cfg_neighbor_resolution_cmd);
install_element(PCU_NODE, &cfg_pcu_timer_cmd);
install_element_ve(&show_bts_stats_cmd);
diff --git a/src/pcu_vty_functions.cpp b/src/pcu_vty_functions.cpp
index 657e5a16..09a1d774 100644
--- a/src/pcu_vty_functions.cpp
+++ b/src/pcu_vty_functions.cpp
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* OsmoBTS VTY interface */
@@ -24,7 +20,6 @@
#include <stdlib.h>
#include "pcu_vty_functions.h"
#include "bts.h"
-#include "gprs_ms_storage.h"
#include "gprs_ms.h"
#include "cxx_linuxlist.h"
#include <llc.h>
@@ -44,28 +39,41 @@ extern "C" {
#include "coding_scheme.h"
}
+static uint32_t tbf_state_flags(const struct gprs_rlcmac_tbf *tbf)
+{
+ const struct gprs_rlcmac_ul_tbf *ul_tbf = tbf_as_ul_tbf_const(tbf);
+ const struct gprs_rlcmac_dl_tbf *dl_tbf = tbf_as_dl_tbf_const(tbf);
+ if (ul_tbf)
+ return ul_tbf->state_fsm.state_flags;
+ return dl_tbf->state_fsm.state_flags;
+}
+
static void tbf_print_vty_info(struct vty *vty, struct gprs_rlcmac_tbf *tbf)
{
- gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
- gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
+ gprs_rlcmac_ul_tbf *ul_tbf = tbf_as_ul_tbf(tbf);
+ gprs_rlcmac_dl_tbf *dl_tbf = tbf_as_dl_tbf(tbf);
+ uint32_t state_flags = tbf_state_flags(tbf);
+ struct GprsMs *ms = tbf_ms(tbf);
+ const struct gprs_rlcmac_pdch *first_common_ts = ms_first_common_ts(ms);
vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) TA=%u DIR=%s IMSI=%s%s", tbf->tfi(),
tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid",
tbf->ta(),
tbf->direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
tbf->imsi(), VTY_NEWLINE);
- vty_out(vty, " created=%lu state=%08x [CCCH:%u, PACCH:%u] 1st_TS=%d 1st_cTS=%d ctrl_TS=%d MS_CLASS=%d/%d%s",
- tbf->created_ts(), tbf->state_fsm.state_flags,
- tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH),
- tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH),
- tbf->first_ts,
- tbf->first_common_ts, tbf->control_ts,
+ vty_out(vty, " created=%lu state=%s flags=%08x [CCCH:%u, PACCH:%u] 1st_cTS=%" PRId8 " ctrl_TS=%d MS_CLASS=%d/%d%s",
+ tbf->created_ts(), tbf->state_name(),
+ state_flags,
+ state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH),
+ state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH),
+ first_common_ts ? first_common_ts->ts_no : -1,
+ tbf->control_ts ? tbf->control_ts->ts_no : -1,
tbf->ms_class(),
- tbf->ms() ? ms_egprs_ms_class(tbf->ms()) : -1,
+ ms_egprs_ms_class(ms),
VTY_NEWLINE);
vty_out(vty, " TS_alloc=");
for (int i = 0; i < 8; i++) {
- bool is_ctrl = tbf->is_control_ts(i);
+ bool is_ctrl = tbf_is_control_ts(tbf, tbf->pdch[i]);
if (tbf->pdch[i])
vty_out(vty, "%d%s ", i, is_ctrl ? "!" : "");
}
@@ -79,11 +87,10 @@ static void tbf_print_vty_info(struct vty *vty, struct gprs_rlcmac_tbf *tbf)
ul_tbf->window_size(), win->v_q(), win->v_r());
vty_out(vty, "%s", VTY_NEWLINE);
vty_out(vty, " TBF Statistics:%s", VTY_NEWLINE);
- if (GPRS == ms_mode(tbf->ms())) {
+ if (ul_tbf->m_ul_gprs_ctrs)
vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_gprs_ctrs);
- } else {
+ if (ul_tbf->m_ul_egprs_ctrs)
vty_out_rate_ctr_group(vty, " ", ul_tbf->m_ul_egprs_ctrs);
- }
}
if (dl_tbf) {
gprs_rlc_dl_window *win = static_cast<gprs_rlc_dl_window *>(dl_tbf->window());
@@ -92,11 +99,10 @@ static void tbf_print_vty_info(struct vty *vty, struct gprs_rlcmac_tbf *tbf)
win->window_stalled() ? " STALLED" : "");
vty_out(vty, "%s", VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", tbf->m_ctrs);
- if (GPRS == ms_mode(tbf->ms())) {
+ if (dl_tbf->m_dl_gprs_ctrs)
vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_gprs_ctrs);
- } else {
+ if (dl_tbf->m_dl_egprs_ctrs)
vty_out_rate_ctr_group(vty, " ", dl_tbf->m_dl_egprs_ctrs);
- }
}
vty_out(vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
}
@@ -106,6 +112,8 @@ int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts, uint32_t
struct llist_item *iter;
const struct gprs_rlcmac_trx *trx;
struct gprs_rlcmac_tbf *tbf;
+ const struct gprs_rlcmac_ul_tbf *ul_tbf;
+ const struct gprs_rlcmac_dl_tbf *dl_tbf;
size_t trx_no;
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
@@ -113,7 +121,8 @@ int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts, uint32_t
trx = &bts->trx[trx_no];
llist_for_each_entry(iter, &trx->ul_tbfs, list) {
tbf = (struct gprs_rlcmac_tbf *)iter->entry;
- if (tbf->state_fsm.state_flags & flags)
+ ul_tbf = tbf_as_ul_tbf_const(tbf);
+ if (ul_tbf->state_fsm.state_flags & flags)
tbf_print_vty_info(vty, tbf);
}
}
@@ -123,7 +132,8 @@ int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts, uint32_t
trx = &bts->trx[trx_no];
llist_for_each_entry(iter, &trx->dl_tbfs, list) {
tbf = (struct gprs_rlcmac_tbf *)iter->entry;
- if (tbf->state_fsm.state_flags & flags)
+ dl_tbf = tbf_as_dl_tbf_const(tbf);
+ if (dl_tbf->state_fsm.state_flags & flags)
tbf_print_vty_info(vty, tbf);
}
}
@@ -138,11 +148,16 @@ static int show_ms(struct vty *vty, GprsMs *ms)
uint8_t slots;
vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms_tlli(ms), ms_imsi(ms), VTY_NEWLINE);
- vty_out(vty, " Timing advance (TA): %d%s", ms_ta(ms), VTY_NEWLINE);
- vty_out(vty, " Coding scheme uplink: %s%s", mcs_name(ms_current_cs_ul(ms)),
- VTY_NEWLINE);
- vty_out(vty, " Coding scheme downlink: %s%s", mcs_name(ms_current_cs_dl(ms, ms_mode(ms))),
- VTY_NEWLINE);
+ if (osmo_timer_pending(&ms->release_timer)) {
+ struct timeval tv_now, tv_res1, tv_res2;
+ osmo_gettimeofday(&tv_now, NULL);
+ timersub(&tv_now, &ms->tv_idle_start, &tv_res1);
+ osmo_timer_remaining(&ms->release_timer, &tv_now, &tv_res2);
+ vty_out(vty, " State: IDLE for %lus, release in %lus%s",
+ tv_res1.tv_sec, tv_res2.tv_sec, VTY_NEWLINE);
+ } else {
+ vty_out(vty, " State: ACTIVE%s", VTY_NEWLINE);
+ }
vty_out(vty, " Mode: %s%s", mode_name(ms_mode(ms)), VTY_NEWLINE);
vty_out(vty, " MS class: %d%s", ms_ms_class(ms), VTY_NEWLINE);
vty_out(vty, " EGPRS MS class: %d%s", ms_egprs_ms_class(ms), VTY_NEWLINE);
@@ -150,12 +165,17 @@ static int show_ms(struct vty *vty, GprsMs *ms)
slots = ms_current_pacch_slots(ms);
for (int i = 0; i < 8; i++)
if (slots & (1 << i))
- vty_out(vty, "%d ", i);
+ vty_out(vty, "TS%d ", i);
vty_out(vty, "%s", VTY_NEWLINE);
- vty_out(vty, " LLC queue length: %zd%s", llc_queue_size(ms_llc_queue(ms)),
+ vty_out(vty, " DL LLC queue length: %zd%s", llc_queue_size(ms_llc_queue(ms)),
VTY_NEWLINE);
- vty_out(vty, " LLC queue octets: %zd%s", llc_queue_octets(ms_llc_queue(ms)),
+ vty_out(vty, " DL LLC queue octets: %zd%s", llc_queue_octets(ms_llc_queue(ms)),
VTY_NEWLINE);
+ vty_out(vty, " DL Coding Scheme: %s%s", mcs_name(ms_current_cs_dl(ms, ms_mode(ms))),
+ VTY_NEWLINE);
+ vty_out(vty, " UL Coding Scheme: %s%s", mcs_name(ms_current_cs_ul(ms)),
+ VTY_NEWLINE);
+ vty_out(vty, " Timing advance (TA): %d%s", ms_ta(ms), VTY_NEWLINE);
if (ms->l1_meas.have_rssi)
vty_out(vty, " RSSI: %d dBm%s",
ms->l1_meas.rssi, VTY_NEWLINE);
@@ -172,7 +192,7 @@ static int show_ms(struct vty *vty, GprsMs *ms)
vty_out(vty, " Downlink NACK rate: %d %%%s",
ms_nack_rate_dl(ms), VTY_NEWLINE);
if (ms->l1_meas.have_ms_rx_qual)
- vty_out(vty, " MS RX quality: %d %%%s",
+ vty_out(vty, " MS Rx quality: %d %%%s",
ms->l1_meas.ms_rx_qual, VTY_NEWLINE);
if (ms->l1_meas.have_ms_c_value)
vty_out(vty, " MS C value: %d dB%s",
@@ -186,25 +206,25 @@ static int show_ms(struct vty *vty, GprsMs *ms)
i, ms->l1_meas.ts[i].ms_i_level, VTY_NEWLINE);
}
if (ms_ul_tbf(ms))
- vty_out(vty, " Uplink TBF: TFI=%d, state=%s%s",
+ vty_out(vty, " UL TBF: TFI=%d, state=%s%s",
ms_ul_tbf(ms)->tfi(),
ms_ul_tbf(ms)->state_name(),
VTY_NEWLINE);
if (ms_dl_tbf(ms)) {
- vty_out(vty, " Downlink TBF: TFI=%d, state=%s%s",
+ vty_out(vty, " DL TBF: TFI=%d, state=%s%s",
ms_dl_tbf(ms)->tfi(),
ms_dl_tbf(ms)->state_name(),
VTY_NEWLINE);
- vty_out(vty, " Current DL Throughput: %d Kbps %s",
+ vty_out(vty, " Current DL Throughput: %d Kbps%s",
ms_dl_tbf(ms)->m_bw.dl_throughput,
VTY_NEWLINE);
}
llist_for_each_entry(i_tbf, &ms->old_tbfs, list) {
struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)i_tbf->entry;
- vty_out(vty, " Old %-19s TFI=%d, state=%s%s",
+ vty_out(vty, " Old %s TBF: TFI=%d, state=%s%s",
tbf_direction(tbf) == GPRS_RLCMAC_UL_TBF ?
- "Uplink TBF:" : "Downlink TBF:",
+ "UL" : "DL",
tbf->tfi(),
tbf->state_name(),
VTY_NEWLINE);
@@ -218,8 +238,8 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts)
{
struct llist_head *tmp;
- llist_for_each(tmp, bts_ms_store(bts)->ms_list()) {
- GprsMs *ms_iter = llist_entry(tmp, typeof(*ms_iter), list);
+ llist_for_each(tmp, &bts->ms_list) {
+ struct GprsMs *ms_iter = llist_entry(tmp, typeof(*ms_iter), list);
show_ms(vty, ms_iter);
}
@@ -229,7 +249,7 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts)
int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts,
uint32_t tlli)
{
- GprsMs *ms = bts_ms_store(bts)->get_ms(tlli);
+ struct GprsMs *ms = bts_get_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
if (!ms) {
vty_out(vty, "Unknown TLLI %08x.%s", tlli, VTY_NEWLINE);
return CMD_WARNING;
@@ -241,7 +261,7 @@ int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts,
int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts,
const char *imsi)
{
- GprsMs *ms = bts_ms_store(bts)->get_ms(0, 0, imsi);
+ struct GprsMs *ms = bts_get_ms_by_imsi(bts, imsi);
if (!ms) {
vty_out(vty, "Unknown IMSI '%s'.%s", imsi, VTY_NEWLINE);
return CMD_WARNING;
@@ -269,7 +289,7 @@ int pcu_vty_show_bts_pdch(struct vty *vty, const struct gprs_rlcmac_bts *bts)
for (ts_nr = 0; ts_nr < ARRAY_SIZE(trx->pdch); ts_nr++) {
const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_nr];
- vty_out(vty, " TS%u: PDCH %s, %u UL TBFs, %u DL TBFs%s", pdch->ts_no,
+ vty_out(vty, " TS%u: PDCH %s, %u DL TBFs, %u UL TBFs%s", pdch->ts_no,
pdch->is_enabled() ? "enabled" : "disabled",
pdch->num_tbfs(GPRS_RLCMAC_DL_TBF),
pdch->num_tbfs(GPRS_RLCMAC_UL_TBF), VTY_NEWLINE);
diff --git a/src/pcu_vty_functions.h b/src/pcu_vty_functions.h
index a68df27c..172dffcc 100644
--- a/src/pcu_vty_functions.h
+++ b/src/pcu_vty_functions.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
diff --git a/src/osmobts_sock.c b/src/pcuif_sock.c
index 91b62a00..f8c0ecc0 100644
--- a/src/osmobts_sock.c
+++ b/src/pcuif_sock.c
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
@@ -34,6 +30,7 @@
#include <pcu_l1_if.h>
+#include <pcu_l1_if_phy.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <osmocom/pcu/pcuif_proto.h>
@@ -41,8 +38,6 @@
#include <tbf.h>
#include <pdch.h>
-int l1if_close_pdch(void *obj);
-
/*
* osmo-bts PCU socket functions
*/
@@ -101,9 +96,9 @@ static void pcu_sock_close(int lost)
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
(lost) ? "LOST" : "closed");
+ osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
- osmo_fd_unregister(bfd);
/* flush the queue */
while (!llist_empty(&pcu_sock_state.upqueue)) {
@@ -116,7 +111,7 @@ static void pcu_sock_close(int lost)
for (trx = 0; trx < 8; trx++) {
#ifdef ENABLE_DIRECT_PHY
if (bts->trx[trx].fl1h) {
- l1if_close_pdch(bts->trx[trx].fl1h);
+ l1if_close_trx(bts->trx[trx].fl1h);
bts->trx[trx].fl1h = NULL;
}
#endif
@@ -143,6 +138,7 @@ static int pcu_sock_read(struct osmo_fd *bfd)
if (rc < 0 && errno == EAGAIN)
return 0; /* Try again later */
if (rc <= 0) {
+ LOGP(DL1IF, LOGL_ERROR, "%s: recv() failed with rc=%d errno=%d\n", __func__, rc, errno);
pcu_sock_close(1);
return -EIO;
}
@@ -221,7 +217,7 @@ static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
int pcu_l1if_open(void)
{
int rc;
- LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface to OsmoBTS\n");
+ LOGP(DL1IF, LOGL_INFO, "Opening OsmoPCU L1 interface v%u to OsmoBTS/OsmoBSC\n", PCU_IF_VERSION);
memset(&pcu_sock_state, 0x00, sizeof(pcu_sock_state));
INIT_LLIST_HEAD(&pcu_sock_state.upqueue);
diff --git a/src/pdch.cpp b/src/pdch.cpp
index 5d8658af..7ff1c7e6 100644
--- a/src/pdch.cpp
+++ b/src/pdch.cpp
@@ -5,8 +5,8 @@
* 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
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -14,7 +14,7 @@
* 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
+ * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
@@ -27,7 +27,6 @@
#include <gprs_debug.h>
#include <coding_scheme.h>
#include <gprs_ms.h>
-#include <gprs_ms_storage.h>
#include <pcu_l1_if.h>
#include <rlc.h>
#include <sba.h>
@@ -119,18 +118,44 @@ static inline void sched_ul_ass_or_rej(struct gprs_rlcmac_bts *bts, struct gprs_
bts_do_rate_ctr_inc(bts, CTR_CHANNEL_REQUEST_DESCRIPTION);
/* This call will register the new TBF with the MS on success */
- gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul_pacch(bts, tbf->ms(), tbf->trx->trx_no);
+ gprs_rlcmac_ul_tbf *ul_tbf = ms_new_ul_tbf_assigned_pacch(tbf->ms(), tbf->trx->trx_no);
/* schedule uplink assignment or reject */
if (ul_tbf) {
- LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack message, so we provide one:\n");
+ LOGPTBFDL(tbf, LOGL_DEBUG, "MS requests UL TBF in ack message, so we provide one:\n");
osmo_fsm_inst_dispatch(tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_SCHED_ASS, NULL);
} else {
- LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack message, so we packet access reject:\n");
+ LOGPTBFDL(tbf, LOGL_NOTICE, "MS requests UL TBF in ack message, but alloc failed: send PktAssRej\n");
osmo_fsm_inst_dispatch(tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
}
}
+/* Make sure the PDCH vanished from the mask of reserved PDCHs for all MS, to
+ * avoid alloc_algorithm using it. */
+static void pdch_unreserve_all_ms_reserved_slots(struct gprs_rlcmac_pdch *pdch)
+{
+ struct llist_head *tmp;
+ uint8_t ts_rm_mask = (~(1 << pdch->ts_no));
+ struct gprs_rlcmac_trx *trx = pdch->trx;
+
+ llist_for_each(tmp, &trx->bts->ms_list) {
+ struct GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
+ if (ms->current_trx != trx)
+ continue;
+ uint8_t old_dl_slots = ms_reserved_dl_slots(ms);
+ uint8_t old_ul_slots = ms_reserved_ul_slots(ms);
+ uint8_t new_dl_slots = old_dl_slots & ts_rm_mask;
+ uint8_t new_ul_slots = old_ul_slots & ts_rm_mask;
+ if (old_dl_slots == new_dl_slots && old_ul_slots == new_ul_slots)
+ continue;
+ ms_set_reserved_slots(ms, trx, new_ul_slots, new_dl_slots);
+ }
+ if (pdch->num_reserved(GPRS_RLCMAC_UL_TBF) > 0 || pdch->num_reserved(GPRS_RLCMAC_DL_TBF) > 0)
+ LOGPDCH(pdch, DRLCMAC, LOGL_ERROR,
+ "Reserved TS count not zero after unreserving from all current MS in list! UL=%u DL=%u\n",
+ pdch->num_reserved(GPRS_RLCMAC_UL_TBF), pdch->num_reserved(GPRS_RLCMAC_DL_TBF));
+}
+
void pdch_init(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_trx *trx, uint8_t ts_nr)
{
pdch->ts_no = ts_nr;
@@ -143,6 +168,7 @@ void pdch_init(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_trx *trx, uint8
void gprs_rlcmac_pdch::enable()
{
+ LOGPDCH(this, DRLCMAC, LOGL_INFO, "PDCH state: %s => enabled\n", m_is_enabled ? "enabled" : "disabled");
OSMO_ASSERT(m_is_enabled == 0);
INIT_LLIST_HEAD(&paging_list);
@@ -155,6 +181,7 @@ void gprs_rlcmac_pdch::enable()
void gprs_rlcmac_pdch::disable()
{
+ LOGPDCH(this, DRLCMAC, LOGL_INFO, "PDCH state: %s => disabled\n", m_is_enabled ? "enabled" : "disabled");
OSMO_ASSERT(m_is_enabled == 1);
this->free_resources();
@@ -169,6 +196,8 @@ void gprs_rlcmac_pdch::free_resources()
/* kick all TBF on slot */
pdch_free_all_tbf(this);
+ pdch_unreserve_all_ms_reserved_slots(this);
+
/* flush all pending paging messages */
while ((pag = dequeue_paging()))
talloc_free(pag);
@@ -315,30 +344,20 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
poll = pdch_ulc_get_node(ulc, fn);
if (!poll || poll->type != PDCH_ULC_NODE_TBF_POLL) {
- LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
- "unknown FN=%u TLLI=0x%08x (TRX %d TS %d)\n",
- fn, tlli, trx_no(), ts_no);
- ms = bts_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
+ LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with unknown FN=%u TLLI=0x%08x\n",
+ fn, tlli);
+ ms = bts_get_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
if (ms)
LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
- "unknown TBF corresponds to MS with IMSI %s, TA %d, "
- "uTBF (TFI=%d, state=%s), dTBF (TFI=%d, state=%s)\n",
- ms_imsi(ms), ms_ta(ms),
- ms_ul_tbf(ms) ? ms_ul_tbf(ms)->tfi() : 0,
- ms_ul_tbf(ms) ? ms_ul_tbf(ms)->state_name() : "None",
- ms_dl_tbf(ms) ? ms_dl_tbf(ms)->tfi() : 0,
- ms_dl_tbf(ms) ? ms_dl_tbf(ms)->state_name() : "None");
+ "unknown TBF corresponds to %s\n", ms_name(ms));
return;
}
OSMO_ASSERT(poll->tbf_poll.poll_tbf);
tbf = poll->tbf_poll.poll_tbf;
reason = poll->tbf_poll.reason;
- /* Reset N3101 counter: */
- tbf->n_reset(N3101);
-
- tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF);
- /* Gather MS from TBF, since it may be NULL or may have been merged during update_ms */
+ ms_update_announced_tlli(tbf->ms(), tlli);
+ /* Gather MS from TBF again, since it may be NULL or may have been merged during ms_update_announced_tlli */
ms = tbf->ms();
LOGPTBF(tbf, LOGL_DEBUG, "FN=%" PRIu32 " Rx Packet Control Ack (reason=%s)\n",
@@ -346,7 +365,7 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
switch (reason) {
case PDCH_ULC_POLL_UL_ACK:
- ul_tbf = as_ul_tbf(tbf);
+ ul_tbf = tbf_as_ul_tbf(tbf);
OSMO_ASSERT(ul_tbf);
if (!tbf_ul_ack_exp_ctrl_ack(ul_tbf, fn, ts_no)) {
LOGPTBF(tbf, LOGL_NOTICE, "FN=%d, TS=%d (curr FN %d): POLL_UL_ACK not expected!\n",
@@ -355,9 +374,12 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
}
pdch_ulc_release_fn(ulc, fn);
osmo_fsm_inst_dispatch(ul_tbf->ul_ack_fsm.fi, TBF_UL_ACK_EV_RX_CTRL_ACK, NULL);
- /* We only set polling on final UL ACK/NACK */
- LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] END\n");
- osmo_fsm_inst_dispatch(ul_tbf->state_fsm.fi, TBF_EV_FINAL_UL_ACK_CONFIRMED, NULL);
+ /* We only set polling on final UL ACK/NACK, something is wrong... */
+ if (tbf_state(tbf) == TBF_ST_FINISHED)
+ osmo_fsm_inst_dispatch(ul_tbf->state_fi, TBF_EV_FINAL_UL_ACK_CONFIRMED, (void*)false);
+ /* ul_tbf is freed here! */
+ else
+ LOGPTBFUL(ul_tbf, LOGL_ERROR, "Received POLL_UL_ACK for UL TBF in unexpected state!\n");
return;
case PDCH_ULC_POLL_UL_ASS:
@@ -369,8 +391,8 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
}
LOGPTBF(tbf, LOGL_DEBUG, "[DOWNLINK] UPLINK ASSIGNED\n");
pdch_ulc_release_fn(ulc, fn);
- /* reset N3105 */
- tbf->n_reset(N3105);
+ if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
+ tbf->n_reset(N3105);
osmo_fsm_inst_dispatch(tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_RX_ASS_CTRL_ACK, NULL);
new_tbf = ms_ul_tbf(ms);
@@ -379,16 +401,13 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
- if (tbf->state_is(TBF_ST_WAIT_RELEASE) &&
- tbf->direction == new_tbf->direction)
- tbf_free(tbf);
- osmo_fsm_inst_dispatch(new_tbf->state_fsm.fi, TBF_EV_ASSIGN_ACK_PACCH, NULL);
+ osmo_fsm_inst_dispatch(new_tbf->state_fi, TBF_EV_ASSIGN_ACK_PACCH, NULL);
/* there might be LLC packets waiting in the queue, but the DL
* TBF might have been released while the UL TBF has been
* established */
if (ms_need_dl_tbf(new_tbf->ms()))
- new_tbf->establish_dl_tbf_on_pacch();
+ ms_new_dl_tbf_assigned_on_pacch(new_tbf->ms(), new_tbf);
return;
case PDCH_ULC_POLL_DL_ASS:
@@ -400,8 +419,8 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
}
LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] DOWNLINK ASSIGNED\n");
pdch_ulc_release_fn(ulc, fn);
- /* reset N3105 */
- tbf->n_reset(N3105);
+ if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
+ tbf->n_reset(N3105);
osmo_fsm_inst_dispatch(tbf->dl_ass_fsm.fi, TBF_DL_ASS_EV_RX_ASS_CTRL_ACK, NULL);
new_tbf = ms_dl_tbf(ms);
@@ -410,11 +429,12 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
- if (tbf->state_is(TBF_ST_WAIT_RELEASE) &&
- tbf->direction == new_tbf->direction)
+ if ((tbf->state_is(TBF_ST_WAIT_RELEASE) ||
+ tbf->state_is(TBF_ST_WAIT_REUSE_TFI)) &&
+ tbf->direction == new_tbf->direction)
tbf_free(tbf);
- osmo_fsm_inst_dispatch(new_tbf->state_fsm.fi, TBF_EV_ASSIGN_ACK_PACCH, NULL);
+ osmo_fsm_inst_dispatch(new_tbf->state_fi, TBF_EV_ASSIGN_ACK_PACCH, NULL);
return;
case PDCH_ULC_POLL_CELL_CHG_CONTINUE:
@@ -467,16 +487,13 @@ void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_n
return;
}
OSMO_ASSERT(poll->tbf_poll.poll_tbf);
- tbf = as_dl_tbf(poll->tbf_poll.poll_tbf);
+ tbf = tbf_as_dl_tbf(poll->tbf_poll.poll_tbf);
if (tbf->tfi() != tfi) {
LOGPTBFDL(tbf, LOGL_NOTICE,
"PACKET DOWNLINK ACK with wrong TFI=%d, ignoring!\n", tfi);
return;
}
- /* Reset N3101 counter: */
- tbf->n_reset(N3101);
-
pdch_ulc_release_fn(ulc, fn);
LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Downlink Ack/Nack\n");
@@ -537,15 +554,13 @@ void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nac
return;
}
OSMO_ASSERT(poll->tbf_poll.poll_tbf);
- tbf = as_dl_tbf(poll->tbf_poll.poll_tbf);
+ tbf = tbf_as_dl_tbf(poll->tbf_poll.poll_tbf);
if (tbf->tfi() != tfi) {
LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
"wrong TFI=%d, ignoring!\n", tfi);
return;
}
- /* Reset N3101 counter: */
- tbf->n_reset(N3101);
pdch_ulc_release_fn(ulc, fn);
LOGPTBF(tbf, LOGL_DEBUG,
@@ -611,157 +626,205 @@ void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nac
void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn, struct pcu_l1_meas *meas)
{
- struct gprs_rlcmac_sba *sba;
int rc;
+ struct gprs_rlcmac_bts *bts = trx->bts;
+ struct pdch_ulc_node *item;
+ char buf[128];
+ struct GprsMs *ms = NULL;
+ struct gprs_rlcmac_sba *sba;
+ struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
+ struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
+ struct gprs_rlcmac_ul_tbf *new_ul_tbf = NULL;
- if (request->ID.UnionType) {
- struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
- struct pdch_ulc_node *item;
- uint32_t tlli = request->ID.u.TLLI;
- GprsMs *ms = bts_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
+ if (!(item = pdch_ulc_get_node(ulc, fn))) {
+ LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
+ "UL block not reserved\n", fn);
+ return;
+ }
+
+ /* First gather MS from TLLI/DL_TFI/UL_TFI:*/
+ if (request->ID.UnionType) { /* ID_TYPE = TLLI */
+ uint32_t tlli = request->ID.u.TLLI;
+ ms = bts_get_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
if (!ms) {
- ms = bts_alloc_ms(bts(), 0, 0); /* ms class updated later */
+ /* A reference is already hold immediately below in all cases, no
+ * need to hold and extra one, pass use_ref=NULL: */
+ ms = ms_alloc(bts, NULL);
ms_set_tlli(ms, tlli);
}
+ } else if (request->ID.u.Global_TFI.UnionType) { /* ID_TYPE = DL_TFI */
+ int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI;
+ dl_tbf = bts_dl_tbf_by_tfi(bts, tfi, trx_no(), ts_no);
+ if (!dl_tbf) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESOURCE REQ unknown downlink TFI=%d\n", tfi);
+ return;
+ }
+ ms = tbf_ms(dl_tbf_as_tbf(dl_tbf));
+ dl_tbf = NULL;
+ } else { /* ID_TYPE = UL_TFI */
+ int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI;
+ ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no(), ts_no);
+ if (!ul_tbf) {
+ LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESOURCE REQ unknown uplink TFI=%d\n", tfi);
+ return;
+ }
+ ms = tbf_ms(ul_tbf_as_tbf(ul_tbf));
+ ul_tbf = NULL;
+ }
- /* Keep the ms, even if it gets idle temporarily */
- ms_ref(ms);
- if (!(item = pdch_ulc_get_node(ulc, fn))) {
- LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
- "UL block not reserved\n", fn);
- goto return_unref;
- }
+ /* Here ms is available (non-null). Keep the ms, even if it gets idle
+ * temporarily, in order to avoid it being freed if we free any of its
+ * resources (TBF). */
+ OSMO_ASSERT(ms);
+ ms_ref(ms, __func__);
- switch (item->type) {
- case PDCH_ULC_NODE_TBF_USF:
- /* Is it actually valid for an MS to send a PKT Res Req during USF? */
- ul_tbf = item->tbf_usf.ul_tbf;
+
+ switch (item->type) {
+ case PDCH_ULC_NODE_TBF_USF:
+ /* TS 44.060 to 8.1.1.1.2: An MS can send a PKT Res Req during USF, for instance to change its Radio Priority. */
+ ul_tbf = item->tbf_usf.ul_tbf;
+ if (tbf_ms(ul_tbf_as_tbf(ul_tbf)) != ms) {
LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
- "Unexpectedly received, waiting USF of %s\n",
- fn, tbf_name(item->tbf_usf.ul_tbf));
- /* Ignore it, let common path expire related ULC entry */
+ "Received from unexpected %s vs exp %s\n", fn,
+ ms_name_buf(ms, buf, sizeof(buf)), ms_name(tbf_ms(ul_tbf_as_tbf(ul_tbf))));
+ /* let common path expire the poll */
goto return_unref;
- case PDCH_ULC_NODE_SBA:
- sba = item->sba.sba;
- LOGPDCH(this, DRLCMAC, LOGL_DEBUG, "FN=%u PKT RESOURCE REQ: "
- "MS requests UL TBF throguh SBA\n", fn);
- ms_set_ta(ms, sba->ta);
- sba_free(sba);
- break;
- case PDCH_ULC_NODE_TBF_POLL:
- if (item->tbf_poll.poll_tbf->direction != GPRS_RLCMAC_UL_TBF) {
- LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
- "Unexpectedly received for DL TBF %s\n", fn,
- tbf_name(item->tbf_poll.poll_tbf));
- /* let common path expire the poll */
- goto return_unref;
- }
- ul_tbf = (struct gprs_rlcmac_ul_tbf *)item->tbf_poll.poll_tbf;
- if (item->tbf_poll.reason != PDCH_ULC_POLL_UL_ACK) {
- LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
- "Unexpectedly received, waiting for poll reason %d\n",
- fn, item->tbf_poll.reason);
- /* let common path expire the poll */
- goto return_unref;
- }
- if (ul_tbf != ms_ul_tbf(ms)) {
- LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
- "Unexpected TLLI 0x%08x received vs exp 0x%08x\n",
- fn, tlli, ul_tbf->tlli());
- /* let common path expire the poll */
- goto return_unref;
- }
- /* 3GPP TS 44.060 $ 9.3.3.3 */
- LOGPTBFUL(ul_tbf, LOGL_DEBUG, "FN=%u PKT RESOURCE REQ: "
- "MS requests reuse of finished UL TBF in RRBP "
- "block of final UL ACK/NACK\n", fn);
- ul_tbf->n_reset(N3103);
- pdch_ulc_release_node(ulc, item);
- rc = osmo_fsm_inst_dispatch(ul_tbf->state_fsm.fi, TBF_EV_FINAL_UL_ACK_CONFIRMED, NULL);
- if (rc) {
- /* FSM failed handling, get rid of previous finished UL TBF before providing a new one */
- LOGPTBFUL(ul_tbf, LOGL_NOTICE,
- "Got PACKET RESOURCE REQ while TBF not finished, killing pending UL TBF\n");
- tbf_free(ul_tbf);
- } /* else: ul_tbf has been freed by state_fsm */
+ }
+ LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
+ "MS requests UL TBF upgrade through USF of %s\n",
+ fn, tbf_name(item->tbf_usf.ul_tbf));
+ pdch_ulc_release_node(ulc, item);
+ /* FIXME OS#4947: Currenty we free the UL TBF and create a new one below in order to trigger the Pkt UL Ass.
+ * We should instead keep the same UL TBF working and upgrading it (modify ul_tbf->ul_ass_fsm.fi to
+ * handle the PktUlAss + PktCtrlAck).
+ * This is required by spec, and MS are know to continue using the TBF (due to delay in between DL and
+ * UL scheduling).
+ * TS 44.060 to 8.1.1.1.2:
+ * "If the mobile station or the network does not support multiple TBF procedures, then after the
+ * transmission of the PACKET RESOURCE REQUEST message with the reason for changing PFI, the radio
+ * priority or peak throughput class of an assigned uplink TBF the mobile station continue to use the
+ * currently assigned uplink TBF assuming that the requested radio priority or peak throughput class is
+ * already assigned to that TBF."
+ */
+ tbf_free(ul_tbf);
+ ul_tbf = NULL;
+ break;
+ case PDCH_ULC_NODE_SBA:
+ sba = item->sba.sba;
+ LOGPDCH(this, DRLCMAC, LOGL_DEBUG, "FN=%u PKT RESOURCE REQ: "
+ "MS requests UL TBF throguh SBA\n", fn);
+ ms_set_ta(ms, sba->ta);
+ sba_free(sba);
+ /* If MS identified by TLLI sent us a PktResReq through SBA, it means it came
+ * from CCCH, so it's for sure not using previous UL BF; drop it if it still
+ * exits on our end:
+ */
+ if ((ul_tbf = ms_ul_tbf(ms))) {
+ /* Get rid of previous finished UL TBF before providing a new one */
+ LOGPTBFUL(ul_tbf, LOGL_NOTICE,
+ "Got PACKET RESOURCE REQ while TBF not finished, killing pending UL TBF\n");
+ tbf_free(ul_tbf);
ul_tbf = NULL;
- break;
- default:
- OSMO_ASSERT(0);
}
-
- /* Here ul_tbf is NULL:
- * - SBA case: no previous TBF) and in
- * - POLL case: PktResReq is a final ACk confirmation and ul_tbf was freed
+ /* Similarly, it is for sure not using any DL-TBF. We still may have some because
+ * we were trying to assign a DL-TBF over CCCH when the MS proactively requested
+ * for a UL-TBF. In that case we'll need to re-assigna new DL-TBF through PACCH when
+ * contention resolution is done:
*/
-
- if (request->Exist_MS_Radio_Access_capability2) {
- uint8_t ms_class, egprs_ms_class;
- ms_class = get_ms_class_by_capability(&request->MS_Radio_Access_capability2);
- egprs_ms_class = get_egprs_ms_class_by_capability(&request->MS_Radio_Access_capability2);
- if (ms_class)
- ms_set_ms_class(ms, ms_class);
- if (egprs_ms_class)
- ms_set_egprs_ms_class(ms, egprs_ms_class);
+ if ((dl_tbf = ms_dl_tbf(ms))) {
+ LOGPTBFDL(dl_tbf, LOGL_NOTICE,
+ "Got PACKET RESOURCE REQ while DL-TBF pending, killing it\n");
+ tbf_free(dl_tbf);
+ dl_tbf = NULL;
}
-
- ul_tbf = tbf_alloc_ul_pacch(bts(), ms, trx_no());
- if (!ul_tbf) {
- handle_tbf_reject(bts(), ms, trx_no(), ts_no);
+ break;
+ case PDCH_ULC_NODE_TBF_POLL:
+ if (item->tbf_poll.poll_tbf->direction != GPRS_RLCMAC_UL_TBF) {
+ LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
+ "Unexpectedly received for DL TBF %s\n", fn,
+ tbf_name(item->tbf_poll.poll_tbf));
+ /* let common path expire the poll */
goto return_unref;
}
-
- /* Set control TS to the TS where this PktResReq was received,
- * which in practice happens to be the control_ts from the
- * previous UL-TBF or SBA. When CTRL ACK is received as RRBP of the Pkt
- * UL Ass scheduled below, then TBF_EV_ASSIGN_ACK_PACCH will be
- * sent to tbf_fsm which will call tbf_assign_control_ts(),
- * effectively setting back control_ts to
- * tbf->initial_common_ts. */
- LOGPTBF(ul_tbf, LOGL_DEBUG, "change control TS %d -> %d until assignment is complete.\n",
- ul_tbf->control_ts, ts_no);
-
- ul_tbf->control_ts = ts_no;
- /* schedule uplink assignment */
- osmo_fsm_inst_dispatch(ul_tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_SCHED_ASS, NULL);
-
- /* get measurements */
- get_meas(meas, request);
- ms_update_l1_meas(ul_tbf->ms(), meas);
-return_unref:
- ms_unref(ms);
- return;
+ ul_tbf = tbf_as_ul_tbf(item->tbf_poll.poll_tbf);
+ if (item->tbf_poll.reason != PDCH_ULC_POLL_UL_ACK) {
+ LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
+ "Unexpectedly received, waiting for poll reason %d\n",
+ fn, item->tbf_poll.reason);
+ /* let common path expire the poll */
+ goto return_unref;
+ }
+ if (tbf_ms(ul_tbf_as_tbf(ul_tbf)) != ms) {
+ LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "FN=%u PKT RESOURCE REQ: "
+ "Received from unexpected %s vs exp %s\n", fn,
+ ms_name_buf(ms, buf, sizeof(buf)), ms_name(tbf_ms(ul_tbf_as_tbf(ul_tbf))));
+ /* let common path expire the poll */
+ goto return_unref;
+ }
+ /* 3GPP TS 44.060 $ 9.3.3.3 */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "FN=%u PKT RESOURCE REQ: "
+ "MS requests reuse of finished UL TBF in RRBP "
+ "block of final UL ACK/NACK\n", fn);
+ ul_tbf->n_reset(N3103);
+ pdch_ulc_release_node(ulc, item);
+ rc = osmo_fsm_inst_dispatch(ul_tbf->state_fi, TBF_EV_FINAL_UL_ACK_CONFIRMED, (void*)true);
+ if (rc) {
+ /* FSM failed handling, get rid of previous finished UL TBF before providing a new one */
+ LOGPTBFUL(ul_tbf, LOGL_NOTICE,
+ "Got PACKET RESOURCE REQ while TBF not finished, killing pending UL TBF\n");
+ tbf_free(ul_tbf);
+ } /* else: ul_tbf has been freed by state_fsm */
+ ul_tbf = NULL;
+ break;
+ default:
+ OSMO_ASSERT(0);
}
- if (request->ID.u.Global_TFI.UnionType) {
- struct gprs_rlcmac_dl_tbf *dl_tbf;
- int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI;
- dl_tbf = bts_dl_tbf_by_tfi(bts(), tfi, trx_no(), ts_no);
- if (!dl_tbf) {
- LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESOURCE REQ unknown downlink TFI=%d\n", tfi);
- return;
- }
- LOGPTBFDL(dl_tbf, LOGL_ERROR,
- "RX: [PCU <- BTS] FIXME: Packet resource request\n");
+ /* Here ul_tbf is NULL:
+ * - USF case: Previous TBF is explicitly freed (FIXME: this is incorrect, old TBF should be kept in this case)
+ * - SBA case: no previous TBF
+ * - POLL case: PktResReq is a final ACk confirmation and ul_tbf was freed
+ */
- /* Reset N3101 counter: */
- dl_tbf->n_reset(N3101);
- } else {
- struct gprs_rlcmac_ul_tbf *ul_tbf;
- int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI;
- ul_tbf = bts_ul_tbf_by_tfi(bts(), tfi, trx_no(), ts_no);
- if (!ul_tbf) {
- LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESOURCE REQ unknown uplink TFI=%d\n", tfi);
- return;
- }
- LOGPTBFUL(ul_tbf, LOGL_ERROR,
- "RX: [PCU <- BTS] FIXME: Packet resource request\n");
+ if (request->Exist_MS_Radio_Access_capability2) {
+ uint8_t ms_class, egprs_ms_class;
+ ms_class = get_ms_class_by_capability(&request->MS_Radio_Access_capability2);
+ egprs_ms_class = get_egprs_ms_class_by_capability(&request->MS_Radio_Access_capability2);
+ if (ms_class)
+ ms_set_ms_class(ms, ms_class);
+ if (egprs_ms_class)
+ ms_set_egprs_ms_class(ms, egprs_ms_class);
+ }
+
+ /* get measurements */
+ get_meas(meas, request);
+ ms_update_l1_meas(ms, meas);
- /* Reset N3101 counter: */
- ul_tbf->n_reset(N3101);
+ new_ul_tbf = ms_new_ul_tbf_assigned_pacch(ms, trx_no());
+ if (!new_ul_tbf) {
+ ms_new_ul_tbf_rejected_pacch(ms, this);
+ goto return_unref;
}
+
+ /* Set control TS to the TS where this PktResReq was received,
+ * which in practice happens to be the control_ts from the
+ * previous UL-TBF or SBA. When CTRL ACK is received as RRBP of the Pkt
+ * UL Ass scheduled below, then TBF_EV_ASSIGN_ACK_PACCH will be
+ * sent to tbf_fsm which will call tbf_assign_control_ts(),
+ * effectively setting back control_ts to tbf->initial_common_ts.
+ */
+ LOGPTBF(new_ul_tbf, LOGL_INFO, "Change control TS %s -> %s until assignment is complete.\n",
+ new_ul_tbf->control_ts ? pdch_name_buf(new_ul_tbf->control_ts, buf, sizeof(buf)) : "(none)",
+ pdch_name(this));
+
+ new_ul_tbf->control_ts = this;
+ /* schedule uplink assignment */
+ osmo_fsm_inst_dispatch(new_ul_tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_SCHED_ASS, NULL);
+return_unref:
+ ms_unref(ms, __func__);
+ return;
}
void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
@@ -769,13 +832,16 @@ void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *repor
struct gprs_rlcmac_sba *sba;
struct pdch_ulc_node *poll;
GprsMs *ms;
+ bool ms_allocated = false;
- ms = bts_ms_by_tlli(bts(), report->TLLI, GSM_RESERVED_TMSI);
+ ms = bts_get_ms_by_tlli(bts(), report->TLLI, GSM_RESERVED_TMSI);
if (!ms) {
LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "MS send measurement "
"but TLLI 0x%08x is unknown\n", report->TLLI);
- ms = bts_alloc_ms(bts(), 0, 0);
+ ms = ms_alloc(bts(), __func__);
ms_set_tlli(ms, report->TLLI);
+ /* Track that we need to free ref at the end: */
+ ms_allocated = true;
}
if ((poll = pdch_ulc_get_node(ulc, fn))) {
switch (poll->type) {
@@ -796,6 +862,8 @@ void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *repor
}
}
gprs_rlcmac_meas_rep(ms, report);
+ if (ms_allocated)
+ ms_unref(ms, __func__);
}
void gprs_rlcmac_pdch::rcv_cell_change_notification(Packet_Cell_Change_Notification_t *notif,
@@ -896,9 +964,6 @@ free_ret:
int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
struct pcu_l1_meas *meas)
{
- /* First of all, update TDMA clock: */
- bts_set_current_frame_number(trx->bts, fn);
-
/* No successfully decoded UL block was received during this FN: */
if (len == 0)
return 0;
@@ -1056,12 +1121,12 @@ gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_from_list_by_tfi(
gprs_rlcmac_ul_tbf *gprs_rlcmac_pdch::ul_tbf_by_tfi(uint8_t tfi)
{
- return as_ul_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_UL_TBF));
+ return tbf_as_ul_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_UL_TBF));
}
gprs_rlcmac_dl_tbf *gprs_rlcmac_pdch::dl_tbf_by_tfi(uint8_t tfi)
{
- return as_dl_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF));
+ return tbf_as_dl_tbf(tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF));
}
/* lookup TBF Entity (by TFI) */
@@ -1116,7 +1181,7 @@ void gprs_rlcmac_pdch::attach_tbf(gprs_rlcmac_tbf *tbf)
num_tbfs_update(tbf, true);
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
- ul_tbf = as_ul_tbf(tbf);
+ ul_tbf = tbf_as_ul_tbf(tbf);
m_assigned_usf |= 1 << ul_tbf->m_usf[ts_no];
}
m_assigned_tfi[tbf->direction] |= 1UL << tbf->tfi();
@@ -1132,6 +1197,11 @@ void gprs_rlcmac_pdch::detach_tbf(gprs_rlcmac_tbf *tbf)
{
gprs_rlcmac_ul_tbf *ul_tbf;
+ LOGPDCH(this, DRLCMAC, LOGL_INFO, "Detaching %s, %d TBFs, "
+ "USFs = %02x, TFIs = %08x.\n",
+ tbf->name(), num_tbfs(tbf->direction),
+ m_assigned_usf, m_assigned_tfi[tbf->direction]);
+
if (tbf->is_egprs_enabled()) {
OSMO_ASSERT(m_num_tbfs_egprs[tbf->direction] > 0);
} else {
@@ -1140,18 +1210,13 @@ void gprs_rlcmac_pdch::detach_tbf(gprs_rlcmac_tbf *tbf)
num_tbfs_update(tbf, false);
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
- ul_tbf = as_ul_tbf(tbf);
+ ul_tbf = tbf_as_ul_tbf(tbf);
m_assigned_usf &= ~(1 << ul_tbf->m_usf[ts_no]);
}
m_assigned_tfi[tbf->direction] &= ~(1UL << tbf->tfi());
m_tbfs[tbf->direction][tbf->tfi()] = NULL;
pdch_ulc_release_tbf(ulc, tbf);
-
- LOGPDCH(this, DRLCMAC, LOGL_INFO, "Detaching %s, %d TBFs, "
- "USFs = %02x, TFIs = %08x.\n",
- tbf->name(), num_tbfs(tbf->direction),
- m_assigned_usf, m_assigned_tfi[tbf->direction]);
}
bool gprs_rlcmac_pdch::has_gprs_only_tbf_attached() const
@@ -1228,8 +1293,8 @@ void pdch_free_all_tbf(struct gprs_rlcmac_pdch *pdch)
* PDCH, since they have no TFI assigned (see handle_tbf_reject()).
* Get rid of them too: */
llist_for_each_entry_safe(pos, pos2, &pdch->trx->ul_tbfs, list) {
- struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
- if (ul_tbf->control_ts == pdch->ts_no)
+ struct gprs_rlcmac_ul_tbf *ul_tbf = tbf_as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
+ if (ul_tbf->control_ts == pdch)
tbf_free(ul_tbf);
}
}
@@ -1243,3 +1308,27 @@ bool pdch_is_enabled(const struct gprs_rlcmac_pdch *pdch)
{
return pdch->is_enabled();
}
+
+/* To be called only on enabled PDCHs. Used to gather information on whether the
+ * PDCH is currently unable to allocate more TBFs due to any resource being
+ * full. Used by bts_all_pdch_allocated() for counting purposes. */
+bool pdch_is_full(const struct gprs_rlcmac_pdch *pdch)
+{
+ return pdch->assigned_tfi(GPRS_RLCMAC_UL_TBF) == NO_FREE_TFI ||
+ pdch->assigned_tfi(GPRS_RLCMAC_DL_TBF) == NO_FREE_TFI ||
+ find_free_usf(pdch->assigned_usf()) < 0;
+}
+
+const char *pdch_name(const struct gprs_rlcmac_pdch *pdch)
+{
+ static char _pdch_name_buf[128];
+ return pdch_name_buf(pdch, _pdch_name_buf, sizeof(_pdch_name_buf));
+}
+
+char *pdch_name_buf(const struct gprs_rlcmac_pdch *pdch, char *buf, size_t buf_size)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
+ OSMO_STRBUF_PRINTF(sb, "PDCH(bts=%" PRIu8 ",trx=%" PRIu8 ",ts=%" PRIu8 ")",
+ pdch->trx->bts->nr, pdch->trx->trx_no, pdch->ts_no);
+ return buf;
+}
diff --git a/src/pdch.h b/src/pdch.h
index 94056069..2ca37212 100644
--- a/src/pdch.h
+++ b/src/pdch.h
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -194,11 +190,12 @@ void pdch_init(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_trx *trx, uint8
void pdch_free_all_tbf(struct gprs_rlcmac_pdch *pdch);
void pdch_disable(struct gprs_rlcmac_pdch *pdch);
bool pdch_is_enabled(const struct gprs_rlcmac_pdch *pdch);
+bool pdch_is_full(const struct gprs_rlcmac_pdch *pdch);
+const char *pdch_name(const struct gprs_rlcmac_pdch *pdch);
+char *pdch_name_buf(const struct gprs_rlcmac_pdch *pdch, char *buf, size_t buf_size);
#ifdef __cplusplus
}
#endif
#define LOGPDCH(pdch, category, level, fmt, args...) \
- LOGP(category, level, "PDCH(bts=%" PRIu8 ",trx=%" PRIu8 ",ts=%" PRIu8 ") " fmt, \
- (pdch)->trx->bts->nr, (pdch)->trx->trx_no, (pdch)->ts_no, \
- ## args)
+ LOGP(category, level, "%s " fmt, pdch_name(pdch), ## args)
diff --git a/src/pdch_ul_controller.c b/src/pdch_ul_controller.c
index 79a03391..661957bb 100644
--- a/src/pdch_ul_controller.c
+++ b/src/pdch_ul_controller.c
@@ -12,15 +12,13 @@
* 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 General Public License
- * along with it program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
#include <talloc.h>
+#include <osmocom/gsm/gsm0502.h>
+
#include "pdch_ul_controller.h"
#include "bts.h"
#include "sba.h"
@@ -47,20 +45,6 @@ const struct value_string pdch_ulc_tbf_poll_reason_names[] = {
{ 0, NULL }
};
-#define GSM_MAX_FN_THRESH (GSM_MAX_FN >> 1)
-/* 0: equal, -1: fn1 BEFORE fn2, 1: fn1 AFTER fn2 */
-static inline int fn_cmp(uint32_t fn1, uint32_t fn2)
-{
- if (fn1 == fn2)
- return 0;
- /* FN1 goes before FN2: */
- if ((fn1 < fn2 && (fn2 - fn1) < GSM_MAX_FN_THRESH) ||
- (fn1 > fn2 && (fn1 - fn2) > GSM_MAX_FN_THRESH))
- return -1;
- /* FN1 goes after FN2: */
- return 1;
-}
-
struct pdch_ulc *pdch_ulc_alloc(struct gprs_rlcmac_pdch *pdch, void *ctx)
{
struct pdch_ulc* ulc;
@@ -75,13 +59,14 @@ struct pdch_ulc *pdch_ulc_alloc(struct gprs_rlcmac_pdch *pdch, void *ctx)
struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn)
{
+ OSMO_ASSERT(ulc);
struct rb_node *node = ulc->tree_root.rb_node;
struct pdch_ulc_node *it;
int res;
while (node) {
it = rb_entry(node, struct pdch_ulc_node, node);
- res = fn_cmp(it->fn, fn);
+ res = gsm0502_fncmp(it->fn, fn);
if (res > 0) /* it->fn AFTER fn */
node = node->rb_left;
else if (res < 0) /* it->fn BEFORE fn */
@@ -161,7 +146,7 @@ int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *
}
/* Get next free (unreserved) FN which is not located in time before "start_fn" */
-uint32_t pdch_ulc_get_next_free_fn(struct pdch_ulc *ulc, uint32_t start_fn)
+uint32_t pdch_ulc_get_next_free_fn(const struct pdch_ulc *ulc, uint32_t start_fn)
{
struct rb_node *node;
struct pdch_ulc_node *it;
@@ -170,12 +155,12 @@ uint32_t pdch_ulc_get_next_free_fn(struct pdch_ulc *ulc, uint32_t start_fn)
for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
it = container_of(node, struct pdch_ulc_node, node);
- res = fn_cmp(it->fn, check_fn);
+ res = gsm0502_fncmp(it->fn, check_fn);
if (res > 0) { /* it->fn AFTER check_fn */
/* Next reserved FN is passed check_fn, hence it means check_fn is free */
return check_fn;
}
- /* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
+
if (res == 0)/* it->fn == fn */
check_fn = fn_next_block(check_fn);
/* if it->fn < check_fn, simply continue iterating, we want to reach at least check_fn */
@@ -196,6 +181,9 @@ static int pdch_ulc_add_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
struct rb_node **n = &(ulc->tree_root.rb_node);
struct rb_node *parent = NULL;
+ LOGPDCH(ulc->pdch, DRLCMAC, LOGL_DEBUG, "Reserving FN %u for type %s\n",
+ item->fn, get_value_string(pdch_ul_node_names, item->type));
+
while (*n) {
struct pdch_ulc_node *it;
int res;
@@ -203,7 +191,7 @@ static int pdch_ulc_add_node(struct pdch_ulc *ulc, struct pdch_ulc_node *item)
it = container_of(*n, struct pdch_ulc_node, node);
parent = *n;
- res = fn_cmp(item->fn, it->fn);
+ res = gsm0502_fncmp(item->fn, it->fn);
if (res < 0) { /* item->fn "BEFORE" it->fn */
n = &((*n)->rb_left);
} else if (res > 0) { /* item->fn "AFTER" it->fn */
@@ -279,7 +267,7 @@ void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tb
item_tbf = item->tbf_poll.poll_tbf;
break;
case PDCH_ULC_NODE_TBF_USF:
- item_tbf = (struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf;
+ item_tbf = ul_tbf_as_tbf_const(item->tbf_usf.ul_tbf);
break;
default:
OSMO_ASSERT(0);
@@ -303,9 +291,9 @@ void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
int res;
struct rb_node *first;
- while((first = rb_first(&ulc->tree_root))) {
+ while ((first = rb_first(&ulc->tree_root))) {
item = container_of(first, struct pdch_ulc_node, node);
- res = fn_cmp(item->fn, fn);
+ res = gsm0502_fncmp(item->fn, fn);
if (res > 0) /* item->fn AFTER fn */
break;
if (res < 0) { /* item->fn BEFORE fn */
@@ -320,7 +308,7 @@ void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
case PDCH_ULC_NODE_TBF_USF:
LOGPDCH(ulc->pdch, DRLCMAC, LOGL_INFO,
"Timeout for registered USF (FN=%u): %s\n",
- item->fn, tbf_name((struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf));
+ item->fn, tbf_name(ul_tbf_as_tbf_const(item->tbf_usf.ul_tbf)));
tbf_usf_timeout(item->tbf_usf.ul_tbf);
break;
case PDCH_ULC_NODE_TBF_POLL:
diff --git a/src/pdch_ul_controller.h b/src/pdch_ul_controller.h
index c56945f1..a488e9b9 100644
--- a/src/pdch_ul_controller.h
+++ b/src/pdch_ul_controller.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -40,7 +36,6 @@ enum rrbp_field {
struct pdch_ulc {
struct gprs_rlcmac_pdch *pdch; /* back pointer */
- uint32_t last_fn; /* last FN rx from TDMA clock */
struct rb_root tree_root;
void *pool_ctx; /* talloc pool of struct pdch_ulc_node */
};
@@ -89,7 +84,7 @@ int pdch_ulc_reserve_sba(struct pdch_ulc *ulc, struct gprs_rlcmac_sba *sba);
bool pdch_ulc_fn_is_free(struct pdch_ulc *ulc, uint32_t fn);
int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp);
-uint32_t pdch_ulc_get_next_free_fn(struct pdch_ulc *ulc, uint32_t start_fn);
+uint32_t pdch_ulc_get_next_free_fn(const struct pdch_ulc *ulc, uint32_t start_fn);
struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn);
struct pdch_ulc_node *pdch_ulc_pop_node(struct pdch_ulc *ulc, uint32_t fn);
diff --git a/src/rlc.cpp b/src/rlc.cpp
index a2cc52c2..2166e4d9 100644
--- a/src/rlc.cpp
+++ b/src/rlc.cpp
@@ -10,10 +10,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "bts.h"
@@ -45,275 +41,6 @@ uint8_t *prepare(struct gprs_rlc_data *rlc, size_t block_data_len)
return rlc->block;
}
-void gprs_rlc_v_b::reset()
-{
- for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
- mark_invalid(i);
-}
-
-void gprs_rlc_dl_window::reset()
-{
- m_v_s = 0;
- m_v_a = 0;
- m_v_b.reset();
-}
-
-int gprs_rlc_dl_window::resend_needed() const
-{
- for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
- if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
- return bsn;
- }
-
- return -1;
-}
-
-int gprs_rlc_dl_window::mark_for_resend()
-{
- int resend = 0;
-
- for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
- if (m_v_b.is_unacked(bsn)) {
- /* mark to be re-send */
- m_v_b.mark_resend(bsn);
- resend += 1;
- }
- }
-
- return resend;
-}
-
-/* Update the receive block bitmap */
-uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
-{
- uint16_t i;
- uint16_t bsn;
- uint16_t bitmask = 0x80;
- int8_t pos = 0;
- int8_t bit_pos = 0;
- for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
- bsn = this->mod_sns(bsn + 1)) {
- if (m_v_n.is_received(bsn)) {
- rbb[pos] = rbb[pos] | bitmask;
- } else {
- rbb[pos] = rbb[pos] & (~bitmask);
- }
- bitmask = bitmask >> 1;
- bit_pos++;
- bit_pos = bit_pos % 8;
- if (bit_pos == 0) {
- pos++;
- bitmask = 0x80;
- }
- }
- return i;
-}
-
-int gprs_rlc_dl_window::count_unacked()
-{
- uint16_t unacked = 0;
- uint16_t bsn;
-
- for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
- if (!m_v_b.is_acked(bsn))
- unacked += 1;
- }
-
- return unacked;
-}
-
-static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
-{
- return (ssn - 1 - bitnum);
-}
-
-void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
- uint16_t first_bsn, uint16_t *lost,
- uint16_t *received)
-{
- unsigned dist = distance();
- unsigned num_blocks = rbb->cur_bit > dist
- ? dist : rbb->cur_bit;
- unsigned bsn;
-
- /* first_bsn is in range V(A)..V(S) */
-
- for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
- bool is_ack;
- bsn = mod_sns(first_bsn + bitpos);
- if (bsn == mod_sns(v_a() - 1))
- break;
-
- is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
-
- if (is_ack) {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
- if (!m_v_b.is_acked(bsn))
- *received += 1;
- m_v_b.mark_acked(bsn);
- } else {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
- m_v_b.mark_nacked(bsn);
- bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
- *lost += 1;
- }
- }
-}
-
-void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
- uint16_t *lost, uint16_t *received)
-{
- /* SSN - 1 is in range V(A)..V(S)-1 */
- for (int bitpos = 0; bitpos < ws(); bitpos++) {
- uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
-
- if (bsn == mod_sns(v_a() - 1))
- break;
-
- if (show_rbb[ws() - 1 - bitpos] == 'R') {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
- if (!m_v_b.is_acked(bsn))
- *received += 1;
- m_v_b.mark_acked(bsn);
- } else {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
- m_v_b.mark_nacked(bsn);
- bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
- *lost += 1;
- }
- }
-}
-
-int gprs_rlc_dl_window::move_window()
-{
- int i;
- uint16_t bsn;
- int moved = 0;
-
- for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
- if (m_v_b.is_acked(bsn)) {
- m_v_b.mark_invalid(bsn);
- moved += 1;
- } else
- break;
- }
-
- return moved;
-}
-
-void gprs_rlc_dl_window::show_state(char *show_v_b)
-{
- int i;
- uint16_t bsn;
-
- for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
- uint16_t index = bsn & mod_sns_half();
- switch(m_v_b.get_state(index)) {
- case GPRS_RLC_DL_BSN_INVALID:
- show_v_b[i] = 'I';
- break;
- case GPRS_RLC_DL_BSN_ACKED:
- show_v_b[i] = 'A';
- break;
- case GPRS_RLC_DL_BSN_RESEND:
- show_v_b[i] = 'X';
- break;
- case GPRS_RLC_DL_BSN_NACKED:
- show_v_b[i] = 'N';
- break;
- default:
- show_v_b[i] = '?';
- }
- }
- show_v_b[i] = '\0';
-}
-
-void gprs_rlc_v_n::reset()
-{
- for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
- m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
-}
-
-void gprs_rlc_window::set_sns(uint16_t sns)
-{
- OSMO_ASSERT(sns >= RLC_GPRS_SNS);
- OSMO_ASSERT(sns <= RLC_MAX_SNS);
- /* check for 2^n */
- OSMO_ASSERT((sns & (-sns)) == sns);
- m_sns = sns;
-}
-
-void gprs_rlc_window::set_ws(uint16_t ws)
-{
- LOGP(DRLCMAC, LOGL_INFO, "ws(%d)\n",
- ws);
- OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
- OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
- m_ws = ws;
-}
-
-/* Update the receive block bitmap */
-void gprs_rlc_ul_window::update_rbb(char *rbb)
-{
- int i;
- for (i=0; i < ws(); i++) {
- if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
- rbb[ws()-1-i] = 'R';
- else
- rbb[ws()-1-i] = 'I';
- }
-}
-
-/* Raise V(R) to highest received sequence number not received. */
-void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
-{
- uint16_t offset_v_r;
- offset_v_r = mod_sns(bsn + 1 - v_r());
- /* Positive offset, so raise. */
- if (offset_v_r < (sns() >> 1)) {
- while (offset_v_r--) {
- if (offset_v_r) /* all except the received block */
- m_v_n.mark_missing(v_r());
- raise_v_r_to(1);
- }
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
- }
-}
-
-/*
- * Raise V(Q) if possible. This is looped until there is a gap
- * (non received block) or the window is empty.
- */
-uint16_t gprs_rlc_ul_window::raise_v_q()
-{
- uint16_t count = 0;
-
- while (v_q() != v_r()) {
- if (!m_v_n.is_received(v_q()))
- break;
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
- "V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
- raise_v_q(1);
- count += 1;
- }
-
- return count;
-}
-
-void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
-{
- m_v_n.mark_received(bsn);
- raise_v_r(bsn);
-}
-
-bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
-{
- bool was_valid = m_v_n.is_received(bsn);
- m_v_n.mark_missing(bsn);
-
- return was_valid;
-}
-
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
enum CodingScheme cs, bool with_padding, unsigned int header_bits,
const unsigned int spb)
diff --git a/src/rlc.h b/src/rlc.h
index 45cbb95f..dd2f0c7c 100644
--- a/src/rlc.h
+++ b/src/rlc.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
#ifdef __cplusplus
@@ -33,34 +29,10 @@ extern "C" {
#include <string.h>
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
-#define RLC_GPRS_WS 64 /* max window size */
-#define RLC_EGPRS_MIN_WS 64 /* min window size */
-#define RLC_EGPRS_MAX_WS 1024 /* min window size */
#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
-#define RLC_EGPRS_MAX_BSN_DELTA 512
#define RLC_MAX_SNS RLC_EGPRS_SNS
-#define RLC_MAX_WS RLC_EGPRS_MAX_WS
#define RLC_MAX_LEN 74 /* MCS-9 data unit */
-struct gprs_rlcmac_bts;
-
-/* The state of a BSN in the send/receive window */
-enum gprs_rlc_ul_bsn_state {
- GPRS_RLC_UL_BSN_INVALID,
- GPRS_RLC_UL_BSN_RECEIVED,
- GPRS_RLC_UL_BSN_MISSING,
- GPRS_RLC_UL_BSN_MAX,
-};
-
-enum gprs_rlc_dl_bsn_state {
- GPRS_RLC_DL_BSN_INVALID,
- GPRS_RLC_DL_BSN_NACKED,
- GPRS_RLC_DL_BSN_ACKED,
- GPRS_RLC_DL_BSN_UNACKED,
- GPRS_RLC_DL_BSN_RESEND,
- GPRS_RLC_DL_BSN_MAX,
-};
-
/*
* EGPRS resegment status information for UL
* When only first split block is received bsn state
@@ -243,138 +215,6 @@ struct gprs_rlc {
gprs_rlc_data m_blocks[RLC_MAX_SNS/2];
};
-/**
- * TODO: for GPRS/EDGE maybe make sns a template parameter
- * so we create specialized versions...
- */
-struct gprs_rlc_v_b {
- /* Check for an individual frame */
- bool is_unacked(int bsn) const;
- bool is_nacked(int bsn) const;
- bool is_acked(int bsn) const;
- bool is_resend(int bsn) const;
- bool is_invalid(int bsn) const;
- gprs_rlc_dl_bsn_state get_state(int bsn) const;
-
- /* Mark a RLC frame for something */
- void mark_unacked(int bsn);
- void mark_nacked(int bsn);
- void mark_acked(int bsn);
- void mark_resend(int bsn);
- void mark_invalid(int bsn);
-
- void reset();
-
-
-private:
- bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
- void mark(int bsn, const gprs_rlc_dl_bsn_state state);
-
- gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
-};
-
-
-/**
- * TODO: The UL/DL code could/should share a base class.
- */
-class gprs_rlc_window {
-public:
- gprs_rlc_window();
-
- const uint16_t mod_sns() const;
- const uint16_t mod_sns(uint16_t bsn) const;
- const uint16_t sns() const;
- const uint16_t ws() const;
-
- void set_sns(uint16_t sns);
- void set_ws(uint16_t ws);
-
-protected:
- uint16_t m_sns;
- uint16_t m_ws;
-};
-
-struct gprs_rlc_dl_window: public gprs_rlc_window {
- void reset();
-
- bool window_stalled() const;
- bool window_empty() const;
-
- void increment_send();
- void raise(int moves);
-
- const uint16_t v_s() const;
- const uint16_t v_s_mod(int offset) const;
- const uint16_t v_a() const;
- const uint16_t distance() const;
-
- /* Methods to manage reception */
- int resend_needed() const;
- int mark_for_resend();
- void update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
- uint16_t *lost, uint16_t *received);
- void update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
- uint16_t first_bsn, uint16_t *lost,
- uint16_t *received);
- int move_window();
- void show_state(char *show_rbb);
- int count_unacked();
-
- uint16_t m_v_s; /* send state */
- uint16_t m_v_a; /* ack state */
-
- gprs_rlc_v_b m_v_b;
-
- gprs_rlc_dl_window();
-};
-
-struct gprs_rlc_v_n {
- void reset();
-
- void mark_received(int bsn);
- void mark_missing(int bsn);
-
- bool is_received(int bsn) const;
-
- gprs_rlc_ul_bsn_state state(int bsn) const;
-private:
- bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
- void mark(int bsn, const gprs_rlc_ul_bsn_state state);
- gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
-};
-
-struct gprs_rlc_ul_window: public gprs_rlc_window {
- const uint16_t v_r() const;
- const uint16_t v_q() const;
-
- const void set_v_r(int);
- const void set_v_q(int);
- void reset_state();
-
- const uint16_t ssn() const;
-
- bool is_in_window(uint16_t bsn) const;
- bool is_received(uint16_t bsn) const;
-
- void update_rbb(char *rbb);
- uint16_t update_egprs_rbb(uint8_t *rbb);
- void raise_v_r_to(int moves);
- void raise_v_r(const uint16_t bsn);
- uint16_t raise_v_q();
-
- void raise_v_q(int);
-
- void receive_bsn(const uint16_t bsn);
- bool invalidate_bsn(const uint16_t bsn);
-
- uint16_t m_v_r; /* receive state */
- uint16_t m_v_q; /* receive window state */
-
- gprs_rlc_v_n m_v_n;
-
- gprs_rlc_ul_window();
-};
-
extern "C" {
/* TS 44.060 10.2.2 */
struct rlc_ul_header {
@@ -390,7 +230,7 @@ struct rlc_ul_header {
uint8_t e:1,
bsn:7;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t pt:2, cv:4, si:1, r:1;
uint8_t spare:1, pi:1, tfi:5, ti:1;
uint8_t bsn:7, e:1;
@@ -409,7 +249,7 @@ struct rlc_dl_header {
uint8_t e:1,
bsn:7;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t pt:2, rrbp:2, s_p:1, usf:3;
uint8_t pr:2, tfi:5, fbi:1;
uint8_t bsn:7, e:1;
@@ -422,7 +262,7 @@ struct rlc_li_field {
m:1,
li:6;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t li:6, m:1, e:1;
#endif
} __attribute__ ((packed));
@@ -432,248 +272,12 @@ struct rlc_li_field_egprs {
uint8_t e:1,
li:7;
#elif OSMO_IS_BIG_ENDIAN
-/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t li:7, e:1;
#endif
} __attribute__ ((packed));
}
-inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
-{
- return m_v_b[bsn & mod_sns_half()] == type;
-}
-
-inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
-{
- m_v_b[bsn & mod_sns_half()] = type;
-}
-
-inline bool gprs_rlc_v_b::is_nacked(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
-}
-
-inline bool gprs_rlc_v_b::is_acked(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
-}
-
-inline bool gprs_rlc_v_b::is_unacked(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
-}
-
-inline bool gprs_rlc_v_b::is_resend(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
-}
-
-inline bool gprs_rlc_v_b::is_invalid(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
-}
-
-inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
-{
- return m_v_b[bsn & mod_sns_half()];
-}
-
-inline void gprs_rlc_v_b::mark_resend(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
-}
-
-inline void gprs_rlc_v_b::mark_unacked(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
-}
-
-inline void gprs_rlc_v_b::mark_acked(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
-}
-
-inline void gprs_rlc_v_b::mark_nacked(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
-}
-
-inline void gprs_rlc_v_b::mark_invalid(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
-}
-
-inline gprs_rlc_window::gprs_rlc_window()
- : m_sns(RLC_GPRS_SNS)
- , m_ws(RLC_GPRS_WS)
-{
-}
-
-inline const uint16_t gprs_rlc_window::sns() const
-{
- return m_sns;
-}
-
-inline const uint16_t gprs_rlc_window::ws() const
-{
- return m_ws;
-}
-
-inline const uint16_t gprs_rlc_window::mod_sns() const
-{
- return sns() - 1;
-}
-
-inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
-{
- return bsn & mod_sns();
-}
-
-inline gprs_rlc_dl_window::gprs_rlc_dl_window()
- : m_v_s(0)
- , m_v_a(0)
-{
- reset();
-}
-
-inline const uint16_t gprs_rlc_dl_window::v_s() const
-{
- return m_v_s;
-}
-
-inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
-{
- return mod_sns(m_v_s + offset);
-}
-
-inline const uint16_t gprs_rlc_dl_window::v_a() const
-{
- return m_v_a;
-}
-
-inline bool gprs_rlc_dl_window::window_stalled() const
-{
- return (mod_sns(m_v_s - m_v_a)) == ws();
-}
-
-inline bool gprs_rlc_dl_window::window_empty() const
-{
- return m_v_s == m_v_a;
-}
-
-inline void gprs_rlc_dl_window::increment_send()
-{
- m_v_s = (m_v_s + 1) & mod_sns();
-}
-
-inline void gprs_rlc_dl_window::raise(int moves)
-{
- m_v_a = (m_v_a + moves) & mod_sns();
-}
-
-inline const uint16_t gprs_rlc_dl_window::distance() const
-{
- return (m_v_s - m_v_a) & mod_sns();
-}
-
-inline gprs_rlc_ul_window::gprs_rlc_ul_window()
- : m_v_r(0)
- , m_v_q(0)
-{
- m_v_n.reset();
-}
-
-inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
-{
- uint16_t offset_v_q;
-
- /* current block relative to lowest unreceived block */
- offset_v_q = (bsn - m_v_q) & mod_sns();
- /* If out of window (may happen if blocks below V(Q) are received
- * again. */
- return offset_v_q < ws();
-}
-
-inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
-{
- uint16_t offset_v_r;
-
- /* Offset to the end of the received window */
- offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
- return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
-}
-
-inline void gprs_rlc_ul_window::reset_state()
-{
- m_v_r = 0;
- m_v_q = 0;
-}
-
-inline const void gprs_rlc_ul_window::set_v_r(int v_r)
-{
- m_v_r = v_r;
-}
-
-inline const void gprs_rlc_ul_window::set_v_q(int v_q)
-{
- m_v_q = v_q;
-}
-
-inline const uint16_t gprs_rlc_ul_window::v_r() const
-{
- return m_v_r;
-}
-
-inline const uint16_t gprs_rlc_ul_window::v_q() const
-{
- return m_v_q;
-}
-
-inline const uint16_t gprs_rlc_ul_window::ssn() const
-{
- return m_v_r;
-}
-
-inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
-{
- m_v_r = mod_sns(m_v_r + moves);
-}
-
-inline void gprs_rlc_ul_window::raise_v_q(int incr)
-{
- m_v_q = mod_sns(m_v_q + incr);
-}
-
-inline void gprs_rlc_v_n::mark_received(int bsn)
-{
- return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
-}
-
-inline void gprs_rlc_v_n::mark_missing(int bsn)
-{
- return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
-}
-
-inline bool gprs_rlc_v_n::is_received(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
-}
-
-inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
-{
- return m_v_n[bsn & mod_sns_half()] == type;
-}
-
-inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
-{
- m_v_n[bsn & mod_sns_half()] = type;
-}
-
-inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
-{
- return m_v_n[bsn & mod_sns_half()];
-}
-
inline void gprs_rlc::init()
{
memset(m_blocks, 0, sizeof(m_blocks));
diff --git a/src/rlc_window.cpp b/src/rlc_window.cpp
new file mode 100644
index 00000000..c056170f
--- /dev/null
+++ b/src/rlc_window.cpp
@@ -0,0 +1,42 @@
+/* RLC Window (common for both UL/DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "gprs_debug.h"
+#include "rlc_window.h"
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/logging.h>
+}
+
+void gprs_rlc_window::set_sns(uint16_t sns)
+{
+ OSMO_ASSERT(sns >= RLC_GPRS_SNS);
+ OSMO_ASSERT(sns <= RLC_MAX_SNS);
+ /* check for 2^n */
+ OSMO_ASSERT((sns & (-sns)) == sns);
+ m_sns = sns;
+}
+
+void gprs_rlc_window::set_ws(uint16_t ws)
+{
+ LOGP(DRLCMAC, LOGL_INFO, "ws(%d)\n",
+ ws);
+ OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
+ OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
+ m_ws = ws;
+} \ No newline at end of file
diff --git a/src/rlc_window.h b/src/rlc_window.h
new file mode 100644
index 00000000..1b0227f4
--- /dev/null
+++ b/src/rlc_window.h
@@ -0,0 +1,71 @@
+/* RLC Window (common for both UL/DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include "rlc.h"
+
+#define RLC_GPRS_WS 64 /* max window size */
+#define RLC_EGPRS_MIN_WS 64 /* min window size */
+#define RLC_EGPRS_MAX_WS 1024 /* min window size */
+#define RLC_EGPRS_MAX_BSN_DELTA 512
+#define RLC_MAX_WS RLC_EGPRS_MAX_WS
+
+class gprs_rlc_window {
+public:
+ gprs_rlc_window();
+
+ const uint16_t mod_sns(void) const;
+ const uint16_t mod_sns(uint16_t bsn) const;
+ const uint16_t sns(void) const;
+ const uint16_t ws(void) const;
+
+ void set_sns(uint16_t sns);
+ void set_ws(uint16_t ws);
+
+protected:
+ uint16_t m_sns;
+ uint16_t m_ws;
+};
+
+
+inline gprs_rlc_window::gprs_rlc_window(void)
+ : m_sns(RLC_GPRS_SNS)
+ , m_ws(RLC_GPRS_WS)
+{
+}
+
+inline const uint16_t gprs_rlc_window::sns(void) const
+{
+ return m_sns;
+}
+
+inline const uint16_t gprs_rlc_window::ws(void) const
+{
+ return m_ws;
+}
+
+inline const uint16_t gprs_rlc_window::mod_sns(void) const
+{
+ return sns() - 1;
+}
+
+inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
+{
+ return bsn & mod_sns();
+}
diff --git a/src/rlc_window_dl.cpp b/src/rlc_window_dl.cpp
new file mode 100644
index 00000000..41f9887d
--- /dev/null
+++ b/src/rlc_window_dl.cpp
@@ -0,0 +1,182 @@
+/* RLC Window (DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "gprs_debug.h"
+#include "bts.h"
+#include "rlc_window_dl.h"
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/logging.h>
+}
+
+static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
+{
+ return (ssn - 1 - bitnum);
+}
+
+void gprs_rlc_v_b::reset()
+{
+ for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
+ mark_invalid(i);
+}
+
+void gprs_rlc_dl_window::reset()
+{
+ m_v_s = 0;
+ m_v_a = 0;
+ m_v_b.reset();
+}
+
+int gprs_rlc_dl_window::resend_needed() const
+{
+ for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
+ if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
+ return bsn;
+ }
+
+ return -1;
+}
+
+int gprs_rlc_dl_window::mark_for_resend()
+{
+ int resend = 0;
+
+ for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
+ if (m_v_b.is_unacked(bsn)) {
+ /* mark to be re-send */
+ m_v_b.mark_resend(bsn);
+ resend += 1;
+ }
+ }
+
+ return resend;
+}
+
+int gprs_rlc_dl_window::count_unacked()
+{
+ uint16_t unacked = 0;
+ uint16_t bsn;
+
+ for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
+ if (!m_v_b.is_acked(bsn))
+ unacked += 1;
+ }
+
+ return unacked;
+}
+
+void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
+ uint16_t first_bsn, uint16_t *lost,
+ uint16_t *received)
+{
+ unsigned dist = distance();
+ unsigned num_blocks = rbb->cur_bit > dist
+ ? dist : rbb->cur_bit;
+ unsigned bsn;
+
+ /* first_bsn is in range V(A)..V(S) */
+
+ for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
+ bool is_ack;
+ bsn = mod_sns(first_bsn + bitpos);
+ if (bsn == mod_sns(v_a() - 1))
+ break;
+
+ is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
+
+ if (is_ack) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
+ if (!m_v_b.is_acked(bsn))
+ *received += 1;
+ m_v_b.mark_acked(bsn);
+ } else {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
+ m_v_b.mark_nacked(bsn);
+ bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
+ *lost += 1;
+ }
+ }
+}
+
+void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
+ uint16_t *lost, uint16_t *received)
+{
+ /* SSN - 1 is in range V(A)..V(S)-1 */
+ for (int bitpos = 0; bitpos < ws(); bitpos++) {
+ uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
+
+ if (bsn == mod_sns(v_a() - 1))
+ break;
+
+ if (show_rbb[ws() - 1 - bitpos] == 'R') {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
+ if (!m_v_b.is_acked(bsn))
+ *received += 1;
+ m_v_b.mark_acked(bsn);
+ } else {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
+ m_v_b.mark_nacked(bsn);
+ bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
+ *lost += 1;
+ }
+ }
+}
+
+int gprs_rlc_dl_window::move_window()
+{
+ int i;
+ uint16_t bsn;
+ int moved = 0;
+
+ for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
+ if (m_v_b.is_acked(bsn)) {
+ m_v_b.mark_invalid(bsn);
+ moved += 1;
+ } else
+ break;
+ }
+
+ return moved;
+}
+
+void gprs_rlc_dl_window::show_state(char *show_v_b)
+{
+ int i;
+ uint16_t bsn;
+
+ for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
+ uint16_t index = bsn & mod_sns_half();
+ switch(m_v_b.get_state(index)) {
+ case GPRS_RLC_DL_BSN_INVALID:
+ show_v_b[i] = 'I';
+ break;
+ case GPRS_RLC_DL_BSN_ACKED:
+ show_v_b[i] = 'A';
+ break;
+ case GPRS_RLC_DL_BSN_RESEND:
+ show_v_b[i] = 'X';
+ break;
+ case GPRS_RLC_DL_BSN_NACKED:
+ show_v_b[i] = 'N';
+ break;
+ default:
+ show_v_b[i] = '?';
+ }
+ }
+ show_v_b[i] = '\0';
+} \ No newline at end of file
diff --git a/src/rlc_window_dl.h b/src/rlc_window_dl.h
new file mode 100644
index 00000000..e2da6145
--- /dev/null
+++ b/src/rlc_window_dl.h
@@ -0,0 +1,205 @@
+/* RLC Window (DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+#pragma once
+
+#include "rlc.h"
+#include "rlc_window.h"
+
+enum gprs_rlc_dl_bsn_state {
+ GPRS_RLC_DL_BSN_INVALID,
+ GPRS_RLC_DL_BSN_NACKED,
+ GPRS_RLC_DL_BSN_ACKED,
+ GPRS_RLC_DL_BSN_UNACKED,
+ GPRS_RLC_DL_BSN_RESEND,
+ GPRS_RLC_DL_BSN_MAX,
+};
+
+/**
+ * TODO: for GPRS/EDGE maybe make sns a template parameter
+ * so we create specialized versions...
+ */
+struct gprs_rlc_v_b {
+ /* Check for an individual frame */
+ bool is_unacked(int bsn) const;
+ bool is_nacked(int bsn) const;
+ bool is_acked(int bsn) const;
+ bool is_resend(int bsn) const;
+ bool is_invalid(int bsn) const;
+ gprs_rlc_dl_bsn_state get_state(int bsn) const;
+
+ /* Mark a RLC frame for something */
+ void mark_unacked(int bsn);
+ void mark_nacked(int bsn);
+ void mark_acked(int bsn);
+ void mark_resend(int bsn);
+ void mark_invalid(int bsn);
+
+ void reset(void);
+
+
+private:
+ bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
+ void mark(int bsn, const gprs_rlc_dl_bsn_state state);
+
+ gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
+};
+
+inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
+{
+ return m_v_b[bsn & mod_sns_half()] == type;
+}
+
+inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
+{
+ m_v_b[bsn & mod_sns_half()] = type;
+}
+
+inline bool gprs_rlc_v_b::is_nacked(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
+}
+
+inline bool gprs_rlc_v_b::is_acked(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
+}
+
+inline bool gprs_rlc_v_b::is_unacked(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
+}
+
+inline bool gprs_rlc_v_b::is_resend(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
+}
+
+inline bool gprs_rlc_v_b::is_invalid(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
+}
+
+inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
+{
+ return m_v_b[bsn & mod_sns_half()];
+}
+
+inline void gprs_rlc_v_b::mark_resend(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
+}
+
+inline void gprs_rlc_v_b::mark_unacked(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
+}
+
+inline void gprs_rlc_v_b::mark_acked(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
+}
+
+inline void gprs_rlc_v_b::mark_nacked(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
+}
+
+inline void gprs_rlc_v_b::mark_invalid(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
+}
+
+struct gprs_rlc_dl_window : public gprs_rlc_window {
+ void reset(void);
+
+ bool window_stalled(void) const;
+ bool window_empty(void) const;
+
+ void increment_send(void);
+ void raise(int moves);
+
+ const uint16_t v_s(void) const;
+ const uint16_t v_s_mod(int offset) const;
+ const uint16_t v_a(void) const;
+ const uint16_t distance(void) const;
+
+ /* Methods to manage reception */
+ int resend_needed(void) const;
+ int mark_for_resend(void);
+ void update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
+ uint16_t *lost, uint16_t *received);
+ void update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
+ uint16_t first_bsn, uint16_t *lost,
+ uint16_t *received);
+ int move_window(void);
+ void show_state(char *show_v_b);
+ int count_unacked(void);
+
+ uint16_t m_v_s; /* send state */
+ uint16_t m_v_a; /* ack state */
+
+ gprs_rlc_v_b m_v_b;
+
+ gprs_rlc_dl_window(void);
+};
+
+inline gprs_rlc_dl_window::gprs_rlc_dl_window(void)
+ : m_v_s(0)
+ , m_v_a(0)
+{
+ reset();
+}
+
+inline const uint16_t gprs_rlc_dl_window::v_s(void) const
+{
+ return m_v_s;
+}
+
+inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
+{
+ return mod_sns(m_v_s + offset);
+}
+
+inline const uint16_t gprs_rlc_dl_window::v_a(void) const
+{
+ return m_v_a;
+}
+
+inline bool gprs_rlc_dl_window::window_stalled(void) const
+{
+ return (mod_sns(m_v_s - m_v_a)) == ws();
+}
+
+inline bool gprs_rlc_dl_window::window_empty(void) const
+{
+ return m_v_s == m_v_a;
+}
+
+inline void gprs_rlc_dl_window::increment_send(void)
+{
+ m_v_s = (m_v_s + 1) & mod_sns();
+}
+
+inline void gprs_rlc_dl_window::raise(int moves)
+{
+ m_v_a = (m_v_a + moves) & mod_sns();
+}
+
+inline const uint16_t gprs_rlc_dl_window::distance(void) const
+{
+ return (m_v_s - m_v_a) & mod_sns();
+}
diff --git a/src/rlc_window_ul.cpp b/src/rlc_window_ul.cpp
new file mode 100644
index 00000000..32810d1b
--- /dev/null
+++ b/src/rlc_window_ul.cpp
@@ -0,0 +1,125 @@
+/* RLC Window (UL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "gprs_debug.h"
+#include "rlc_window_ul.h"
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/logging.h>
+}
+
+void gprs_rlc_v_n::reset()
+{
+ for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
+ m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
+}
+
+/* Raise V(R) to highest received sequence number not received. */
+void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
+{
+ uint16_t offset_v_r;
+ offset_v_r = mod_sns(bsn + 1 - v_r());
+ /* Positive offset, so raise. */
+ if (offset_v_r < (sns() >> 1)) {
+ while (offset_v_r--) {
+ const uint16_t _v_r = v_r();
+ const uint16_t bsn_no_longer_in_ws = mod_sns(_v_r - ws());
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Mark BSN %u as INVALID\n", bsn_no_longer_in_ws);
+ m_v_n.mark_invalid(bsn_no_longer_in_ws);
+ if (offset_v_r) {/* all except the received block */
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Mark BSN %u as MISSING\n", _v_r);
+ m_v_n.mark_missing(_v_r);
+ }
+ raise_v_r_to(1);
+ }
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
+ }
+}
+
+/*
+ * Raise V(Q) if possible. This is looped until there is a gap
+ * (non received block) or the window is empty.
+ */
+uint16_t gprs_rlc_ul_window::raise_v_q()
+{
+ uint16_t count = 0;
+
+ while (v_q() != v_r()) {
+ if (!m_v_n.is_received(v_q()))
+ break;
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
+ "V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
+ raise_v_q(1);
+ count += 1;
+ }
+
+ return count;
+}
+
+void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
+{
+ m_v_n.mark_received(bsn);
+ raise_v_r(bsn);
+}
+
+bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
+{
+ bool was_valid = m_v_n.is_received(bsn);
+ m_v_n.mark_missing(bsn);
+
+ return was_valid;
+}
+
+
+/* Update the receive block bitmap */
+uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
+{
+ uint16_t i;
+ uint16_t bsn;
+ uint16_t bitmask = 0x80;
+ int8_t pos = 0;
+ int8_t bit_pos = 0;
+ for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
+ bsn = this->mod_sns(bsn + 1)) {
+ if (m_v_n.is_received(bsn)) {
+ rbb[pos] = rbb[pos] | bitmask;
+ } else {
+ rbb[pos] = rbb[pos] & (~bitmask);
+ }
+ bitmask = bitmask >> 1;
+ bit_pos++;
+ bit_pos = bit_pos % 8;
+ if (bit_pos == 0) {
+ pos++;
+ bitmask = 0x80;
+ }
+ }
+ return i;
+}
+
+/* Update the receive block bitmap */
+void gprs_rlc_ul_window::update_rbb(char *rbb)
+{
+ int i;
+ for (i=0; i < ws(); i++) {
+ if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
+ rbb[ws()-1-i] = 'R';
+ else
+ rbb[ws()-1-i] = 'I';
+ }
+} \ No newline at end of file
diff --git a/src/rlc_window_ul.h b/src/rlc_window_ul.h
new file mode 100644
index 00000000..8a48af92
--- /dev/null
+++ b/src/rlc_window_ul.h
@@ -0,0 +1,180 @@
+/* RLC Window (UL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+#pragma once
+
+#include "rlc.h"
+#include "rlc_window.h"
+
+/* The state of a BSN in the send/receive window */
+enum gprs_rlc_ul_bsn_state {
+ GPRS_RLC_UL_BSN_INVALID,
+ GPRS_RLC_UL_BSN_RECEIVED,
+ GPRS_RLC_UL_BSN_MISSING,
+ GPRS_RLC_UL_BSN_MAX,
+};
+
+struct gprs_rlc_v_n {
+ void reset(void);
+
+ void mark_received(int bsn);
+ void mark_missing(int bsn);
+ void mark_invalid(int bsn);
+
+ bool is_received(int bsn) const;
+
+ gprs_rlc_ul_bsn_state state(int bsn) const;
+private:
+ bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
+ void mark(int bsn, const gprs_rlc_ul_bsn_state state);
+ gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
+};
+
+
+inline void gprs_rlc_v_n::mark_received(int bsn)
+{
+ return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
+}
+
+inline void gprs_rlc_v_n::mark_missing(int bsn)
+{
+ return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
+}
+
+inline void gprs_rlc_v_n::mark_invalid(int bsn)
+{
+ return mark(bsn, GPRS_RLC_UL_BSN_INVALID);
+}
+
+inline bool gprs_rlc_v_n::is_received(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
+}
+
+inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
+{
+ return m_v_n[bsn & mod_sns_half()] == type;
+}
+
+inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
+{
+ m_v_n[bsn & mod_sns_half()] = type;
+}
+
+inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
+{
+ return m_v_n[bsn & mod_sns_half()];
+}
+
+struct gprs_rlc_ul_window : public gprs_rlc_window {
+ const uint16_t v_r(void) const;
+ const uint16_t v_q(void) const;
+
+ const void set_v_r(int v_r);
+ const void set_v_q(int v_q);
+ void reset_state(void);
+
+ const uint16_t ssn(void) const;
+
+ bool is_in_window(uint16_t bsn) const;
+ bool is_received(uint16_t bsn) const;
+
+ void update_rbb(char *rbb);
+ uint16_t update_egprs_rbb(uint8_t *rbb);
+ void raise_v_r_to(int moves);
+ void raise_v_r(const uint16_t bsn);
+ uint16_t raise_v_q(void);
+
+ void raise_v_q(int incr);
+
+ void receive_bsn(const uint16_t bsn);
+ bool invalidate_bsn(const uint16_t bsn);
+
+ uint16_t m_v_r; /* receive state */
+ uint16_t m_v_q; /* receive window state */
+
+ gprs_rlc_v_n m_v_n;
+
+ gprs_rlc_ul_window(void);
+};
+
+
+inline gprs_rlc_ul_window::gprs_rlc_ul_window(void)
+{
+ reset_state();
+}
+
+inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
+{
+ uint16_t offset_v_q;
+
+ /* current block relative to lowest unreceived block */
+ offset_v_q = (bsn - m_v_q) & mod_sns();
+ /* If out of window (may happen if blocks below V(Q) are received
+ * again. */
+ return offset_v_q < ws();
+}
+
+inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
+{
+ uint16_t offset_v_r;
+
+ /* Offset to the end of the received window */
+ offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
+ return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
+}
+
+inline void gprs_rlc_ul_window::reset_state(void)
+{
+ m_v_r = 0;
+ m_v_q = 0;
+ m_v_n.reset();
+}
+
+inline const void gprs_rlc_ul_window::set_v_r(int v_r)
+{
+ m_v_r = v_r;
+}
+
+inline const void gprs_rlc_ul_window::set_v_q(int v_q)
+{
+ m_v_q = v_q;
+}
+
+inline const uint16_t gprs_rlc_ul_window::v_r(void) const
+{
+ return m_v_r;
+}
+
+inline const uint16_t gprs_rlc_ul_window::v_q(void) const
+{
+ return m_v_q;
+}
+
+inline const uint16_t gprs_rlc_ul_window::ssn(void) const
+{
+ return m_v_r;
+}
+
+inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
+{
+ m_v_r = mod_sns(m_v_r + moves);
+}
+
+inline void gprs_rlc_ul_window::raise_v_q(int incr)
+{
+ m_v_q = mod_sns(m_v_q + incr);
+}
diff --git a/src/sba.c b/src/sba.c
index d293784c..94c3c564 100644
--- a/src/sba.c
+++ b/src/sba.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <sba.h>
diff --git a/src/sba.h b/src/sba.h
index 39625764..749b1457 100644
--- a/src/sba.h
+++ b/src/sba.h
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 07732dc4..840a1f71 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <new>
@@ -32,9 +28,9 @@
#include <gprs_debug.h>
#include <gprs_ms.h>
#include <pcu_utils.h>
-#include <gprs_ms_storage.h>
#include <sba.h>
#include <pdch.h>
+#include <alloc_algo.h>
extern "C" {
#include <osmocom/core/msgb.h>
@@ -94,14 +90,12 @@ gprs_rlcmac_tbf::Meas::Meas() :
gprs_rlcmac_tbf::gprs_rlcmac_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms, gprs_rlcmac_tbf_direction dir) :
direction(dir),
trx(NULL),
- first_ts(0),
- first_common_ts(0),
- control_ts(TBF_CONTROL_TS_UNSET),
+ control_ts(NULL),
fT(0),
num_fT_exp(0),
upgrade_to_multislot(false),
bts(bts_),
- m_tfi(0),
+ m_tfi(TBF_TFI_UNSET),
m_created_ts(0),
m_ctrs(NULL),
m_ms(ms),
@@ -119,11 +113,6 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms, gprs_
memset(&m_trx_list, 0, sizeof(m_trx_list));
m_trx_list.entry = this;
- memset(&state_fsm, 0, sizeof(state_fsm));
- state_fsm.tbf = this;
- state_fsm.fi = osmo_fsm_inst_alloc(&tbf_fsm, this, &state_fsm, LOGL_INFO, NULL);
- OSMO_ASSERT(state_fsm.fi);
-
memset(&ul_ass_fsm, 0, sizeof(ul_ass_fsm));
ul_ass_fsm.tbf = this;
ul_ass_fsm.fi = osmo_fsm_inst_alloc(&tbf_ul_ass_fsm, this, &ul_ass_fsm, LOGL_INFO, NULL);
@@ -134,16 +123,23 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms, gprs_
OSMO_ASSERT(dl_ass_fsm.fi);
m_rlc.init();
- m_llc.init();
+ llc_init(&m_llc);
m_name_buf[0] = '\0';
+
+ m_created_ts = time(NULL);
+ /* set timestamp */
+ osmo_clock_gettime(CLOCK_MONOTONIC, &meas.rssi_tv);
+
+ m_ctrs = rate_ctr_group_alloc(this, &tbf_ctrg_desc, next_tbf_ctr_group_id++);
+ OSMO_ASSERT(m_ctrs);
}
gprs_rlcmac_tbf::~gprs_rlcmac_tbf()
{
- osmo_fsm_inst_free(state_fsm.fi);
- state_fsm.fi = NULL;
+ osmo_fsm_inst_free(state_fi);
+ state_fi = NULL;
osmo_fsm_inst_free(ul_ass_fsm.fi);
ul_ass_fsm.fi = NULL;
@@ -215,30 +211,7 @@ void gprs_rlcmac_tbf::set_ms(GprsMs *ms)
ms_attach_tbf(m_ms, this);
}
-void gprs_rlcmac_tbf::update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
-{
- if (tlli == GSM_RESERVED_TMSI)
- return;
-
- /* TODO: When the TLLI does not match the ms, check if there is another
- * MS object that belongs to that TLLI and if yes make sure one of them
- * gets deleted. This is the same problem that can arise with
- * IMSI in gprs_rlcmac_dl_tbf::handle() so there should be a unified solution */
- if (!ms_check_tlli(ms(), tlli)) {
- GprsMs *old_ms;
-
- old_ms = bts_ms_store(bts)->get_ms(tlli, 0, NULL);
- if (old_ms)
- ms_merge_and_clear_ms(ms(), old_ms);
- }
-
- if (dir == GPRS_RLCMAC_UL_TBF)
- ms_set_tlli(ms(), tlli);
- else
- ms_confirm_tlli(ms(), tlli);
-}
-
-static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
+void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
{
int ts;
@@ -249,8 +222,10 @@ static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
* confirmation from the MS and goes through the FLOW state. Hence, we
* may have ULC pollings ongoing and we need to make sure we drop all
* reserved nodes there: */
- if (tbf->control_ts != TBF_CONTROL_TS_UNSET && !tbf->pdch[tbf->control_ts])
- pdch_ulc_release_tbf(tbf->trx->pdch[tbf->control_ts].ulc, tbf);
+ if (tbf->control_ts) {
+ pdch_ulc_release_tbf(tbf->control_ts->ulc, tbf);
+ tbf->control_ts = NULL;
+ }
/* Now simply detach from all attached PDCHs */
for (ts = 0; ts < 8; ts++) {
@@ -260,6 +235,12 @@ static void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf)
tbf->pdch[ts]->detach_tbf(tbf);
tbf->pdch[ts] = NULL;
}
+
+ /* Detach from TRX: */
+ if (tbf->trx) {
+ llist_del(tbf_trx_list(tbf));
+ tbf->trx = NULL;
+ }
}
void tbf_free(struct gprs_rlcmac_tbf *tbf)
@@ -270,7 +251,7 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
if (tbf->state_is(TBF_ST_FLOW))
bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_UL_ABORTED);
} else {
- gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(tbf);
+ gprs_rlcmac_dl_tbf *dl_tbf = tbf_as_dl_tbf(tbf);
gprs_rlc_dl_window *win = static_cast<gprs_rlc_dl_window *>(dl_tbf->window());
bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_DL_FREED);
@@ -291,7 +272,6 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
tbf->stop_timers("freeing TBF");
/* TODO: Could/Should generate bssgp_tx_llc_discarded */
tbf_unlink_pdch(tbf);
- llist_del(tbf_trx_list(tbf));
if (tbf->ms())
tbf->set_ms(NULL);
@@ -309,50 +289,38 @@ uint16_t egprs_window_size(const struct gprs_rlcmac_bts *bts, uint8_t slots)
OSMO_MAX(64, (the_pcu->vty.ws_base + num_pdch * the_pcu->vty.ws_pdch) / 32 * 32));
}
-int gprs_rlcmac_tbf::update()
-{
- int rc;
-
- LOGP(DTBF, LOGL_DEBUG, "********** DL-TBF update **********\n");
- OSMO_ASSERT(direction == GPRS_RLCMAC_DL_TBF);
-
- tbf_unlink_pdch(this);
- rc = the_pcu->alloc_algorithm(bts, this, false, -1);
- /* if no resource */
- if (rc < 0) {
- LOGPTBF(this, LOGL_ERROR, "No resource after update???\n");
- bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_FAIL);
- return rc;
- }
-
- if (is_egprs_enabled()) {
- gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this);
- if (dl_tbf)
- dl_tbf->set_window_size();
- }
-
- tbf_update_state_fsm_name(this);
-
- return 0;
-}
-
void tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf)
{
- if (tbf->control_ts == TBF_CONTROL_TS_UNSET)
- LOGPTBF(tbf, LOGL_INFO, "Setting Control TS %d\n",
- tbf->first_common_ts);
- else if (tbf->control_ts != tbf->first_common_ts)
- LOGPTBF(tbf, LOGL_INFO, "Changing Control TS %d -> %d\n",
- tbf->control_ts, tbf->first_common_ts);
- tbf->control_ts = tbf->first_common_ts;
+ char buf[128];
+ struct gprs_rlcmac_pdch *first_common = ms_first_common_ts(tbf_ms(tbf));
+ OSMO_ASSERT(first_common);
+
+ if (!tbf->control_ts)
+ LOGPTBF(tbf, LOGL_INFO, "Setting Control TS %s\n",
+ pdch_name(first_common));
+ else if (tbf->control_ts != first_common)
+ LOGPTBF(tbf, LOGL_INFO, "Changing Control TS %s -> %s\n",
+ pdch_name_buf(tbf->control_ts, buf, sizeof(buf)),
+ pdch_name(first_common));
+ tbf->control_ts = first_common;
}
void gprs_rlcmac_tbf::n_reset(enum tbf_counters n)
{
- if (n >= N_MAX) {
- LOGPTBF(this, LOGL_ERROR, "attempting to reset unknown counter %s\n",
- get_value_string(tbf_counters_names, n));
- return;
+ OSMO_ASSERT(n < N_MAX);
+
+ switch(n) {
+ case N3101:
+ OSMO_ASSERT(direction == GPRS_RLCMAC_UL_TBF);
+ break;
+ case N3103:
+ OSMO_ASSERT(direction == GPRS_RLCMAC_UL_TBF);
+ break;
+ case N3105:
+ OSMO_ASSERT(direction == GPRS_RLCMAC_DL_TBF);
+ break;
+ default:
+ break;
}
Narr[n] = 0;
@@ -363,22 +331,21 @@ bool gprs_rlcmac_tbf::n_inc(enum tbf_counters n)
{
uint8_t chk;
- if (n >= N_MAX) {
- LOGPTBF(this, LOGL_ERROR, "attempting to increment unknown counter %s\n",
- get_value_string(tbf_counters_names, n));
- return true;
- }
+ OSMO_ASSERT(n < N_MAX);
Narr[n]++;
switch(n) {
case N3101:
+ OSMO_ASSERT(direction == GPRS_RLCMAC_UL_TBF);
chk = bts->n3101;
break;
case N3103:
+ OSMO_ASSERT(direction == GPRS_RLCMAC_UL_TBF);
chk = bts->n3103;
break;
case N3105:
+ OSMO_ASSERT(direction == GPRS_RLCMAC_DL_TBF);
chk = bts->n3105;
break;
default:
@@ -491,8 +458,8 @@ void gprs_rlcmac_tbf::t_start(enum tbf_timers t, int T, const char *reason, bool
OSMO_ASSERT(false);
}
- LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s %sstarting timer %s [%s] with %u sec. %u microsec, cur_fn=%d\n",
- tbf_name(this), osmo_timer_pending(&Tarr[t]) ? "re" : "",
+ LOGPSRC(DTBF, LOGL_DEBUG, file, line, "%s %starting timer %s [%s] with %u sec. %u microsec, cur_fn=%d\n",
+ tbf_name(this), osmo_timer_pending(&Tarr[t]) ? "Res" : "S",
get_value_string(tbf_timers_names, t), reason, sec, microsec, current_fn);
Tarr[t].data = this;
@@ -512,46 +479,18 @@ void gprs_rlcmac_tbf::t_start(enum tbf_timers t, int T, const char *reason, bool
osmo_timer_schedule(&Tarr[t], sec, microsec);
}
-int gprs_rlcmac_tbf::check_polling(uint32_t fn, uint8_t ts,
- uint32_t *poll_fn_, unsigned int *rrbp_) const
-{
- int rc;
- if (!is_control_ts(ts)) {
- LOGPTBF(this, LOGL_DEBUG, "Polling cannot be "
- "scheduled in this TS %d (first control TS %d)\n",
- ts, control_ts);
- return -EINVAL;
- }
-
- if ((rc = pdch_ulc_get_next_free_rrbp_fn(trx->pdch[ts].ulc, fn, poll_fn_, rrbp_)) < 0) {
- LOGPTBF(this, LOGL_DEBUG,
- "(bts=%u,trx=%u,ts=%u) FN=%u No suitable free RRBP offset found!\n",
- trx->bts->nr, trx->trx_no, ts, fn);
- return rc;
- }
-
- return 0;
-}
-
-void gprs_rlcmac_tbf::set_polling(uint32_t new_poll_fn, uint8_t ts, enum pdch_ulc_tbf_poll_reason reason)
-{
- /* schedule polling */
- if (pdch_ulc_reserve_tbf_poll(trx->pdch[ts].ulc, new_poll_fn, this, reason) < 0)
- LOGPTBFDL(this, LOGL_ERROR, "Failed scheduling poll on PACCH (FN=%d, TS=%d)\n",
- new_poll_fn, ts);
-}
-
void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_fn, enum pdch_ulc_tbf_poll_reason reason)
{
gprs_rlcmac_ul_tbf *ul_tbf;
gprs_rlcmac_dl_tbf *dl_tbf;
- LOGPTBF(this, LOGL_NOTICE, "poll timeout for FN=%d, TS=%d (curr FN %d)\n",
- poll_fn, pdch->ts_no, bts_current_frame_number(bts));
+ LOGPTBF(this, LOGL_NOTICE, "poll timeout for FN=%d, TS=%d (curr FN %d), reason=%s\n",
+ poll_fn, pdch->ts_no, bts_current_frame_number(bts),
+ get_value_string(pdch_ulc_tbf_poll_reason_names, reason));
switch (reason) {
case PDCH_ULC_POLL_UL_ACK:
- ul_tbf = as_ul_tbf(this);
+ ul_tbf = tbf_as_ul_tbf(this);
OSMO_ASSERT(ul_tbf);
if (!tbf_ul_ack_exp_ctrl_ack(ul_tbf, poll_fn, pdch->ts_no)) {
LOGPTBF(this, LOGL_NOTICE, "FN=%d, TS=%d (curr FN %d): POLL_UL_ACK not expected!\n",
@@ -563,7 +502,7 @@ void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_
if (state_is(TBF_ST_FINISHED)) {
if (ul_tbf->n_inc(N3103)) {
bts_do_rate_ctr_inc(bts, CTR_PUAN_POLL_FAILED);
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_MAX_N3103, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_MAX_N3103, NULL);
return;
}
}
@@ -579,8 +518,8 @@ void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_
}
bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_TIMEDOUT);
bts_do_rate_ctr_inc(bts, CTR_PUA_POLL_TIMEDOUT);
- if (n_inc(N3105)) {
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_MAX_N3105, NULL);
+ if (this->direction == GPRS_RLCMAC_DL_TBF && n_inc(N3105)) {
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_MAX_N3105, NULL);
bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_FAILED);
bts_do_rate_ctr_inc(bts, CTR_PUA_POLL_FAILED);
return;
@@ -598,8 +537,8 @@ void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_
}
bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_TIMEDOUT);
bts_do_rate_ctr_inc(bts, CTR_PDA_POLL_TIMEDOUT);
- if (n_inc(N3105)) {
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_MAX_N3105, NULL);
+ if (this->direction == GPRS_RLCMAC_DL_TBF && n_inc(N3105)) {
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_MAX_N3105, NULL);
bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_FAILED);
bts_do_rate_ctr_inc(bts, CTR_PDA_POLL_FAILED);
return;
@@ -619,14 +558,14 @@ void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_
break;
case PDCH_ULC_POLL_DL_ACK:
- dl_tbf = as_dl_tbf(this);
+ dl_tbf = tbf_as_dl_tbf(this);
/* POLL Timeout expecting DL ACK/NACK: implies direction == GPRS_RLCMAC_DL_TBF */
OSMO_ASSERT(dl_tbf);
- if (!(dl_tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
+ if (!dl_tbf->m_last_dl_poll_ack_lost) {
LOGPTBF(this, LOGL_NOTICE,
"Timeout for polling PACKET DOWNLINK ACK: %s\n",
tbf_rlcmac_diag(dl_tbf));
- dl_tbf->state_fsm.state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
+ dl_tbf->m_last_dl_poll_ack_lost = true;
}
if (dl_tbf->state_is(TBF_ST_RELEASING))
bts_do_rate_ctr_inc(bts, CTR_RLC_REL_TIMEDOUT);
@@ -635,13 +574,13 @@ void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_
bts_do_rate_ctr_inc(bts, CTR_PDAN_POLL_TIMEDOUT);
}
if (dl_tbf->n_inc(N3105)) {
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_MAX_N3105, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_MAX_N3105, NULL);
bts_do_rate_ctr_inc(bts, CTR_PDAN_POLL_FAILED);
bts_do_rate_ctr_inc(bts, CTR_RLC_ACK_FAILED);
return;
}
/* resend IMM.ASS on CCCH on timeout */
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_DL_ACKNACK_MISS, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_DL_ACKNACK_MISS, NULL);
break;
default:
@@ -650,99 +589,46 @@ void gprs_rlcmac_tbf::poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_
}
}
-int gprs_rlcmac_tbf::setup(int8_t use_trx, bool single_slot)
-{
- int rc;
-
- if (ms_mode(m_ms) != GPRS)
- enable_egprs();
-
- m_created_ts = time(NULL);
- /* select algorithm */
- rc = the_pcu->alloc_algorithm(bts, this, single_slot, use_trx);
- /* if no resource */
- if (rc < 0) {
- LOGPTBF(this, LOGL_NOTICE,
- "Timeslot Allocation failed: trx = %d, single_slot = %d\n",
- use_trx, single_slot);
- bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_FAIL);
- return -1;
- }
- /* assign initial control ts */
- tbf_assign_control_ts(this);
-
- /* set timestamp */
- osmo_clock_gettime(CLOCK_MONOTONIC, &meas.rssi_tv);
-
- LOGPTBF(this, LOGL_INFO,
- "Allocated: trx = %d, ul_slots = %02x, dl_slots = %02x\n",
- this->trx->trx_no, ul_slots(), dl_slots());
-
- m_ctrs = rate_ctr_group_alloc(this, &tbf_ctrg_desc, next_tbf_ctr_group_id++);
- if (!m_ctrs) {
- LOGPTBF(this, LOGL_ERROR, "Couldn't allocate TBF counters\n");
- return -1;
- }
-
- tbf_update_state_fsm_name(this);
-
- ms_attach_tbf(m_ms, this);
-
- return 0;
-}
-
-int gprs_rlcmac_tbf::establish_dl_tbf_on_pacch()
-{
- struct gprs_rlcmac_dl_tbf *new_tbf = NULL;
-
- bts_do_rate_ctr_inc(bts, CTR_TBF_REUSED);
-
- new_tbf = tbf_alloc_dl_tbf(bts, ms(),
- this->trx->trx_no, false);
-
- if (!new_tbf) {
- LOGP(DTBF, LOGL_NOTICE, "No PDCH resource\n");
- return -1;
- }
-
- LOGPTBF(this, LOGL_DEBUG, "Trigger downlink assignment on PACCH\n");
- new_tbf->trigger_ass(this);
-
- return 0;
-}
-
const char *tbf_name(const gprs_rlcmac_tbf *tbf)
{
return tbf ? tbf->name() : "(no TBF)";
}
-const char *gprs_rlcmac_tbf::name() const
+const char *gprs_rlcmac_tbf::name(bool enclousure) const
{
- snprintf(m_name_buf, sizeof(m_name_buf) - 1,
- "TBF(TFI=%d TLLI=0x%08x DIR=%s STATE=%s%s)",
- m_tfi, tlli(),
- direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
- state_name(),
- is_egprs_enabled() ? " EGPRS" : ""
- );
- m_name_buf[sizeof(m_name_buf) - 1] = '\0';
+ struct osmo_strbuf sb = { .buf = m_name_buf, .len = sizeof(m_name_buf) };
+
+ if (enclousure)
+ OSMO_STRBUF_PRINTF(sb, "TBF(");
+ OSMO_STRBUF_PRINTF(sb, "%s", direction == GPRS_RLCMAC_UL_TBF ? "UL" : "DL");
+ if (this->trx) { /* This may not be available during TBF alloc func time */
+ int8_t tfi = (m_tfi == TBF_TFI_UNSET) ? -1 : m_tfi;
+ OSMO_STRBUF_PRINTF(sb, ":TFI-%u-%u-%d",
+ this->trx->bts->nr, this->trx->trx_no, tfi);
+ }
+ OSMO_STRBUF_PRINTF(sb, ":%c", is_egprs_enabled() ? 'E' : 'G');
+ if (m_ms) {
+ uint32_t tlli = ms_tlli(m_ms);
+ if (ms_imsi_is_valid(m_ms))
+ OSMO_STRBUF_PRINTF(sb, ":IMSI-%s", ms_imsi(m_ms));
+ if (tlli != GSM_RESERVED_TMSI)
+ OSMO_STRBUF_PRINTF(sb, ":TLLI-0x%08x", tlli);
+ }
+ if (enclousure)
+ OSMO_STRBUF_PRINTF(sb, "){%s}", state_name());
return m_name_buf;
}
void tbf_update_state_fsm_name(struct gprs_rlcmac_tbf *tbf)
{
- char buf[64];
- snprintf(buf, sizeof(buf), "%s-TFI_%d",
- tbf_direction(tbf) == GPRS_RLCMAC_UL_TBF ? "UL" : "DL",
- tbf_tfi(tbf));
- osmo_identifier_sanitize_buf(buf, NULL, '_');
+ const char *buf = tbf->name(false);
- osmo_fsm_inst_update_id(tbf->state_fsm.fi, buf);
+ osmo_fsm_inst_update_id(tbf->state_fi, buf);
osmo_fsm_inst_update_id(tbf->ul_ass_fsm.fi, buf);
osmo_fsm_inst_update_id(tbf->dl_ass_fsm.fi, buf);
if (tbf_direction(tbf) == GPRS_RLCMAC_UL_TBF) {
- struct gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
+ struct gprs_rlcmac_ul_tbf *ul_tbf = tbf_as_ul_tbf(tbf);
osmo_fsm_inst_update_id(ul_tbf->ul_ack_fsm.fi, buf);
}
@@ -776,12 +662,14 @@ uint8_t gprs_rlcmac_tbf::ul_slots() const
{
uint8_t slots = 0;
size_t i;
+ struct gprs_rlcmac_pdch *first_common;
if (direction == GPRS_RLCMAC_DL_TBF) {
- if (control_ts < 8)
- slots |= 1 << control_ts;
- if (first_common_ts < 8)
- slots |= 1 << first_common_ts;
+ if (control_ts)
+ slots |= 1 << control_ts->ts_no;
+ first_common = ms_first_common_ts(tbf_ms(this));
+ if (first_common)
+ slots |= 1 << first_common->ts_no;
return slots;
}
@@ -793,11 +681,6 @@ uint8_t gprs_rlcmac_tbf::ul_slots() const
return slots;
}
-bool gprs_rlcmac_tbf::is_control_ts(uint8_t ts) const
-{
- return ts == control_ts;
-}
-
void gprs_rlcmac_tbf::enable_egprs()
{
/* Decrease GPRS TBF count of attached PDCHs */
@@ -819,7 +702,12 @@ void gprs_rlcmac_tbf::enable_egprs()
/* C API */
enum tbf_fsm_states tbf_state(const struct gprs_rlcmac_tbf *tbf)
{
- return (enum tbf_fsm_states)tbf->state_fsm.fi->state;
+ return (enum tbf_fsm_states)tbf->state_fi->state;
+}
+
+struct osmo_fsm_inst *tbf_state_fi(const struct gprs_rlcmac_tbf *tbf)
+{
+ return tbf->state_fi;
}
struct osmo_fsm_inst *tbf_ul_ass_fi(const struct gprs_rlcmac_tbf *tbf)
@@ -867,11 +755,6 @@ struct gprs_llc *tbf_llc(struct gprs_rlcmac_tbf *tbf)
return &tbf->m_llc;
}
-uint8_t tbf_first_common_ts(const struct gprs_rlcmac_tbf *tbf)
-{
- return tbf->first_common_ts;
-}
-
uint8_t tbf_dl_slots(const struct gprs_rlcmac_tbf *tbf)
{
return tbf->dl_slots();
@@ -883,7 +766,19 @@ uint8_t tbf_ul_slots(const struct gprs_rlcmac_tbf *tbf)
bool tbf_is_tfi_assigned(const struct gprs_rlcmac_tbf *tbf)
{
- return tbf->is_tfi_assigned();
+ const struct gprs_rlcmac_dl_tbf *dl_tbf;
+
+ /* The TBF is established: */
+ if (tbf->state_fi->state > TBF_ST_ASSIGN)
+ return true;
+
+ /* The DL TBF has been assigned by a IMM.ASS: */
+ dl_tbf = tbf_as_dl_tbf_const(tbf);
+ if (tbf->state_fi->state == TBF_ST_ASSIGN && dl_tbf &&
+ (dl_tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)))
+ return true;
+
+ return false;
}
uint8_t tbf_tfi(const struct gprs_rlcmac_tbf *tbf)
@@ -896,14 +791,37 @@ bool tbf_is_egprs_enabled(const struct gprs_rlcmac_tbf *tbf)
return tbf->is_egprs_enabled();
}
-int tbf_check_polling(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts, uint32_t *poll_fn, unsigned int *rrbp)
+int tbf_check_polling(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp)
{
- return tbf->check_polling(fn, ts, poll_fn, rrbp);
+ int rc;
+ OSMO_ASSERT(pdch);
+
+ if (!tbf_is_control_ts(tbf, pdch)) {
+ char buf[128];
+ LOGPTBF(tbf, LOGL_NOTICE, "Polling cannot be "
+ "scheduled in this TS %s (control TS %s)\n",
+ pdch_name(pdch),
+ tbf->control_ts ? pdch_name_buf(tbf->control_ts, buf, sizeof(buf)) : "none");
+ return -EINVAL;
+ }
+
+ if ((rc = pdch_ulc_get_next_free_rrbp_fn(pdch->ulc, fn, poll_fn, rrbp)) < 0) {
+ LOGPTBF(tbf, LOGL_NOTICE,
+ "FN=%u No suitable free RRBP offset found on %s!\n",
+ fn, pdch_name(pdch));
+ return rc;
+ }
+
+ return 0;
}
-void tbf_set_polling(struct gprs_rlcmac_tbf *tbf, uint32_t new_poll_fn, uint8_t ts, enum pdch_ulc_tbf_poll_reason t)
+void tbf_set_polling(struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch, uint32_t new_poll_fn, enum pdch_ulc_tbf_poll_reason t)
{
- return tbf->set_polling(new_poll_fn, ts, t);
+ /* schedule polling */
+ if (pdch_ulc_reserve_tbf_poll(pdch->ulc, new_poll_fn, tbf, t) < 0)
+ LOGPTBF(tbf, LOGL_ERROR, "FN=%u Failed scheduling poll on PACCH %s\n",
+ new_poll_fn, pdch_name(pdch));
}
void tbf_poll_timeout(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_pdch *pdch, uint32_t poll_fn, enum pdch_ulc_tbf_poll_reason reason)
@@ -911,19 +829,39 @@ void tbf_poll_timeout(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_pdch *pdch
tbf->poll_timeout(pdch, poll_fn, reason);
}
-bool tbf_is_control_ts(const struct gprs_rlcmac_tbf *tbf, uint8_t ts)
+bool tbf_is_control_ts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch)
{
- return tbf->is_control_ts(ts);
+ return tbf->control_ts == pdch;
}
bool tbf_can_upgrade_to_multislot(const struct gprs_rlcmac_tbf *tbf)
{
return tbf->upgrade_to_multislot;
}
+/* first TS used by TBF */
+struct gprs_rlcmac_pdch *tbf_get_first_ts(struct gprs_rlcmac_tbf *tbf)
+{
+ unsigned int i;
-int tbf_update(struct gprs_rlcmac_tbf *tbf)
+ for (i = 0; i < ARRAY_SIZE(tbf->pdch); i++) {
+ struct gprs_rlcmac_pdch *pdch;
+ pdch = tbf->pdch[i];
+ if (pdch)
+ return pdch;
+ }
+ return NULL;
+}
+const struct gprs_rlcmac_pdch *tbf_get_first_ts_const(const struct gprs_rlcmac_tbf *tbf)
{
- return tbf->update();
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(tbf->pdch); i++) {
+ const struct gprs_rlcmac_pdch *pdch;
+ pdch = tbf->pdch[i];
+ if (pdch)
+ return pdch;
+ }
+ return NULL;
}
const char* tbf_rlcmac_diag(const struct gprs_rlcmac_tbf *tbf)
@@ -932,18 +870,23 @@ const char* tbf_rlcmac_diag(const struct gprs_rlcmac_tbf *tbf)
struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
OSMO_STRBUF_PRINTF(sb, "|");
- if (tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
- OSMO_STRBUF_PRINTF(sb, "Assignment was on CCCH|");
- if (tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))
- OSMO_STRBUF_PRINTF(sb, "Assignment was on PACCH|");
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
- const struct gprs_rlcmac_ul_tbf *ul_tbf = static_cast<const gprs_rlcmac_ul_tbf *>(tbf);
+ const struct gprs_rlcmac_ul_tbf *ul_tbf = tbf_as_ul_tbf_const(tbf);
+ if (ul_tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
+ OSMO_STRBUF_PRINTF(sb, "Assignment was on CCCH|");
+ if (ul_tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))
+ OSMO_STRBUF_PRINTF(sb, "Assignment was on PACCH|");
if (ul_tbf->m_rx_counter)
OSMO_STRBUF_PRINTF(sb, "Uplink data was received|");
else
OSMO_STRBUF_PRINTF(sb, "No uplink data received yet|");
} else {
- if (tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))
+ const struct gprs_rlcmac_dl_tbf *dl_tbf = tbf_as_dl_tbf_const(tbf);
+ if (dl_tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
+ OSMO_STRBUF_PRINTF(sb, "Assignment was on CCCH|");
+ if (dl_tbf->state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))
+ OSMO_STRBUF_PRINTF(sb, "Assignment was on PACCH|");
+ if (dl_tbf->m_first_dl_ack_rcvd)
OSMO_STRBUF_PRINTF(sb, "Downlink ACK was received|");
else
OSMO_STRBUF_PRINTF(sb, "No downlink ACK received yet|");
@@ -951,3 +894,13 @@ const char* tbf_rlcmac_diag(const struct gprs_rlcmac_tbf *tbf)
return buf;
}
+
+struct gprs_rlcmac_trx *tbf_get_trx(struct gprs_rlcmac_tbf *tbf)
+{
+ return tbf->trx;
+}
+
+void tbf_stop_timers(struct gprs_rlcmac_tbf *tbf, const char *reason)
+{
+ tbf->stop_timers(reason);
+} \ No newline at end of file
diff --git a/src/tbf.h b/src/tbf.h
index 4013ab05..4170ca1b 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -10,10 +10,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -24,6 +20,7 @@
#include "llc.h"
#include "rlc.h"
+#include "rlc_window.h"
#include "cxx_linuxlist.h"
#include "pcu_utils.h"
#include <gprs_debug.h>
@@ -31,6 +28,7 @@
struct bssgp_bvc_ctx;
struct gprs_rlcmac_bts;
+struct alloc_resources_req;
#endif
@@ -112,11 +110,7 @@ enum tbf_counters { /* TBF counters from 3GPP TS 44.060 §13.4 */
#define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */
#define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */
-#define GPRS_RLCMAC_FLAG_DL_ACK 2 /* DL TBF: At least one DL ACK/NACK was recieved since it was assigned */
-#define GPRS_RLCMAC_FLAG_TO_DL_ACK 3 /* DL TBF: Failed to receive last polled DL ACK/NACK */
-#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
-
-#define TBF_CONTROL_TS_UNSET 0xff
+#define TBF_TFI_UNSET 0xff
#define T_START(tbf, t, T, r, f) tbf->t_start(t, T, r, f, __FILE__, __LINE__)
@@ -128,6 +122,7 @@ const char *tbf_name(const struct gprs_rlcmac_tbf *tbf);
enum tbf_fsm_states tbf_state(const struct gprs_rlcmac_tbf *tbf);
struct osmo_fsm_inst *tbf_ul_ass_fi(const struct gprs_rlcmac_tbf *tbf);
struct osmo_fsm_inst *tbf_dl_ass_fi(const struct gprs_rlcmac_tbf *tbf);
+struct osmo_fsm_inst *tbf_state_fi(const struct gprs_rlcmac_tbf *tbf);
enum gprs_rlcmac_tbf_direction tbf_direction(const struct gprs_rlcmac_tbf *tbf);
void tbf_set_ms(struct gprs_rlcmac_tbf *tbf, struct GprsMs *ms);
struct llist_head *tbf_ms_list(struct gprs_rlcmac_tbf *tbf);
@@ -136,21 +131,24 @@ struct GprsMs *tbf_ms(const struct gprs_rlcmac_tbf *tbf);
bool tbf_timers_pending(struct gprs_rlcmac_tbf *tbf, enum tbf_timers t);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
struct gprs_llc *tbf_llc(struct gprs_rlcmac_tbf *tbf);
-uint8_t tbf_first_common_ts(const struct gprs_rlcmac_tbf *tbf);
uint8_t tbf_dl_slots(const struct gprs_rlcmac_tbf *tbf);
uint8_t tbf_ul_slots(const struct gprs_rlcmac_tbf *tbf);
bool tbf_is_tfi_assigned(const struct gprs_rlcmac_tbf *tbf);
uint8_t tbf_tfi(const struct gprs_rlcmac_tbf *tbf);
bool tbf_is_egprs_enabled(const struct gprs_rlcmac_tbf *tbf);
void tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
-int tbf_check_polling(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts, uint32_t *poll_fn, unsigned int *rrbp);
-void tbf_set_polling(struct gprs_rlcmac_tbf *tbf, uint32_t new_poll_fn, uint8_t ts, enum pdch_ulc_tbf_poll_reason t);
+int tbf_check_polling(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp);
+void tbf_set_polling(struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch, uint32_t new_poll_fn, enum pdch_ulc_tbf_poll_reason t);
void tbf_poll_timeout(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_pdch *pdch, uint32_t poll_fn, enum pdch_ulc_tbf_poll_reason reason);
void tbf_update_state_fsm_name(struct gprs_rlcmac_tbf *tbf);
const char* tbf_rlcmac_diag(const struct gprs_rlcmac_tbf *tbf);
-bool tbf_is_control_ts(const struct gprs_rlcmac_tbf *tbf, uint8_t ts);
+bool tbf_is_control_ts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch);
bool tbf_can_upgrade_to_multislot(const struct gprs_rlcmac_tbf *tbf);
-int tbf_update(struct gprs_rlcmac_tbf *tbf);
+struct gprs_rlcmac_pdch *tbf_get_first_ts(struct gprs_rlcmac_tbf *tbf);
+const struct gprs_rlcmac_pdch *tbf_get_first_ts_const(const struct gprs_rlcmac_tbf *tbf);
+struct gprs_rlcmac_trx *tbf_get_trx(struct gprs_rlcmac_tbf *tbf);
+void tbf_unlink_pdch(struct gprs_rlcmac_tbf *tbf);
+void tbf_stop_timers(struct gprs_rlcmac_tbf *tbf, const char *reason);
#ifdef __cplusplus
}
#endif
@@ -162,18 +160,17 @@ struct gprs_rlcmac_tbf {
virtual ~gprs_rlcmac_tbf();
virtual gprs_rlc_window *window() = 0;
+ virtual void apply_allocated_resources(const struct alloc_resources_res *res) = 0;
- int setup(int8_t use_trx, bool single_slot);
bool state_is(enum tbf_fsm_states rhs) const;
bool state_is_not(enum tbf_fsm_states rhs) const;
bool dl_ass_state_is(enum tbf_dl_ass_fsm_states rhs) const;
bool ul_ass_state_is(enum tbf_ul_ass_fsm_states rhs) const;
void poll_sched_set(const char *file, int line);
void poll_sched_unset(const char *file, int line);
- bool check_n_clear(uint8_t state_flag);
const char *state_name() const;
- const char *name() const;
+ const char *name(bool enclousure = true) const;
struct msgb *create_dl_ass(uint32_t fn, uint8_t ts);
@@ -183,29 +180,19 @@ struct gprs_rlcmac_tbf {
bool n_inc(enum tbf_counters n);
void n_reset(enum tbf_counters n);
- int update();
void handle_timeout();
void stop_timers(const char *reason);
bool timers_pending(enum tbf_timers t);
void t_stop(enum tbf_timers t, const char *reason);
void t_start(enum tbf_timers t, int T, const char *reason, bool force,
const char *file, unsigned line);
- int establish_dl_tbf_on_pacch();
-
- int check_polling(uint32_t fn, uint8_t ts,
- uint32_t *poll_fn, unsigned int *rrbp) const;
- void set_polling(uint32_t poll_fn, uint8_t ts, enum pdch_ulc_tbf_poll_reason reason);
void poll_timeout(struct gprs_rlcmac_pdch *pdch, uint32_t poll_fn, enum pdch_ulc_tbf_poll_reason reason);
/** tlli handling */
uint32_t tlli() const;
bool is_tlli_valid() const;
- /** MS updating */
- void update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction);
-
uint8_t tfi() const;
- bool is_tfi_assigned() const;
const char *imsi() const;
uint8_t ta() const;
@@ -217,8 +204,6 @@ struct gprs_rlcmac_tbf {
uint8_t dl_slots() const;
uint8_t ul_slots() const;
- bool is_control_ts(uint8_t ts) const;
-
/* EGPRS */
bool is_egprs_enabled() const;
@@ -227,10 +212,7 @@ struct gprs_rlcmac_tbf {
enum gprs_rlcmac_tbf_direction direction;
struct gprs_rlcmac_trx *trx;
- uint8_t first_ts; /* first TS used by TBF */
- uint8_t first_common_ts; /* first TS where the phone can send and
- receive simultaniously */
- uint8_t control_ts; /* timeslot control messages and polling */
+ struct gprs_rlcmac_pdch *control_ts; /* timeslot control messages and polling */
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
gprs_llc m_llc;
@@ -261,26 +243,26 @@ struct gprs_rlcmac_tbf {
time_t m_created_ts;
struct rate_ctr_group *m_ctrs;
- struct tbf_fsm_ctx state_fsm;
+ struct osmo_fsm_inst *state_fi;
struct tbf_ul_ass_fsm_ctx ul_ass_fsm;
- struct tbf_ul_ass_fsm_ctx dl_ass_fsm;
+ struct tbf_dl_ass_fsm_ctx dl_ass_fsm;
struct llist_item m_ms_list;
struct llist_item m_trx_list;
protected:
void merge_and_clear_ms(GprsMs *old_ms);
+ void enable_egprs(void);
gprs_llc_queue *llc_queue();
const gprs_llc_queue *llc_queue() const;
struct GprsMs *m_ms;
private:
- void enable_egprs();
bool m_egprs_enabled;
struct osmo_timer_list Tarr[T_MAX];
uint8_t Narr[N_MAX];
- mutable char m_name_buf[60];
+ mutable char m_name_buf[128];
};
inline bool gprs_rlcmac_tbf::state_is(enum tbf_fsm_states rhs) const
@@ -306,17 +288,7 @@ inline bool gprs_rlcmac_tbf::state_is_not(enum tbf_fsm_states rhs) const
inline const char *gprs_rlcmac_tbf::state_name() const
{
- return osmo_fsm_inst_state_name(state_fsm.fi);
-}
-
-inline bool gprs_rlcmac_tbf::check_n_clear(uint8_t state_flag)
-{
- if ((state_fsm.state_flags & (1 << state_flag))) {
- state_fsm.state_flags &= ~(1 << state_flag);
- return true;
- }
-
- return false;
+ return osmo_fsm_inst_state_name(state_fi);
}
inline GprsMs *gprs_rlcmac_tbf::ms() const
@@ -329,16 +301,6 @@ inline bool gprs_rlcmac_tbf::is_tlli_valid() const
return tlli() != GSM_RESERVED_TMSI;
}
-inline bool gprs_rlcmac_tbf::is_tfi_assigned() const
-{
- /* The TBF is established or has been assigned by a IMM.ASS for
- * download */
- return state_fsm.fi->state > TBF_ST_ASSIGN ||
- (direction == GPRS_RLCMAC_DL_TBF &&
- state_fsm.fi->state == TBF_ST_ASSIGN &&
- (state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)));
-}
-
inline uint8_t gprs_rlcmac_tbf::tfi() const
{
return m_tfi;
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 05d5ad36..dc35b844 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <bts.h>
@@ -31,9 +27,9 @@
#include <decoding.h>
#include <encoding.h>
#include <gprs_ms.h>
-#include <gprs_ms_storage.h>
#include <llc.h>
#include "pcu_utils.h"
+#include "alloc_algo.h"
extern "C" {
#include <osmocom/core/msgb.h>
@@ -94,26 +90,14 @@ static const struct rate_ctr_group_desc tbf_dl_egprs_ctrg_desc = {
tbf_dl_egprs_ctr_description,
};
-static void llc_timer_cb(void *_tbf)
-{
- struct gprs_rlcmac_dl_tbf *tbf = (struct gprs_rlcmac_dl_tbf *)_tbf;
-
- if (tbf->state_is_not(TBF_ST_FLOW))
- return;
-
- LOGPTBFDL(tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
-
- tbf->request_dl_ack();
-}
-
gprs_rlcmac_dl_tbf::BandWidth::BandWidth() :
dl_bw_octets(0),
dl_throughput(0),
dl_loss_lost(0),
dl_loss_received(0)
{
- timespecclear(&dl_bw_tv);
- timespecclear(&dl_loss_tv);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &dl_bw_tv);
+ osmo_clock_gettime(CLOCK_MONOTONIC, &dl_loss_tv);
}
static int dl_tbf_dtor(struct gprs_rlcmac_dl_tbf *tbf)
@@ -122,10 +106,9 @@ static int dl_tbf_dtor(struct gprs_rlcmac_dl_tbf *tbf)
return 0;
}
-struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, bool single_slot)
+struct gprs_rlcmac_dl_tbf *dl_tbf_alloc(struct gprs_rlcmac_bts *bts, struct GprsMs *ms)
{
struct gprs_rlcmac_dl_tbf *tbf;
- int rc;
OSMO_ASSERT(ms != NULL);
@@ -140,15 +123,7 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs
talloc_set_destructor(tbf, dl_tbf_dtor);
new (tbf) gprs_rlcmac_dl_tbf(bts, ms);
- rc = tbf->setup(use_trx, single_slot);
- /* if no resource */
- if (rc < 0) {
- talloc_free(tbf);
- return NULL;
- }
-
if (tbf->is_egprs_enabled()) {
- tbf->set_window_size();
tbf->m_dl_egprs_ctrs = rate_ctr_group_alloc(tbf,
&tbf_dl_egprs_ctrg_desc,
tbf->m_ctrs->idx);
@@ -167,19 +142,13 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs
return NULL;
}
}
-
- llist_add(tbf_trx_list((struct gprs_rlcmac_tbf *)tbf), &tbf->trx->dl_tbfs);
bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_DL_ALLOCATED);
- osmo_clock_gettime(CLOCK_MONOTONIC, &tbf->m_bw.dl_bw_tv);
- osmo_clock_gettime(CLOCK_MONOTONIC, &tbf->m_bw.dl_loss_tv);
-
return tbf;
}
gprs_rlcmac_dl_tbf::~gprs_rlcmac_dl_tbf()
{
- osmo_timer_del(&m_llc_timer);
if (is_egprs_enabled()) {
rate_ctr_group_free(m_dl_egprs_ctrs);
} else {
@@ -193,91 +162,23 @@ gprs_rlcmac_dl_tbf::gprs_rlcmac_dl_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms)
m_tx_counter(0),
m_dl_ack_requested(false),
m_last_dl_poll_fn(-1),
+ m_last_dl_poll_ack_lost(false),
m_last_dl_drained_fn(-1),
+ m_first_dl_ack_rcvd(false),
m_dl_gprs_ctrs(NULL),
m_dl_egprs_ctrs(NULL)
{
- memset(&m_llc_timer, 0, sizeof(m_llc_timer));
- osmo_timer_setup(&m_llc_timer, llc_timer_cb, this);
-}
-
-void gprs_rlcmac_dl_tbf::start_llc_timer()
-{
- if (the_pcu->vty.llc_idle_ack_csec > 0) {
- struct timespec tv;
- csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
- osmo_timer_schedule(&m_llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
- }
-}
-
-int gprs_rlcmac_dl_tbf::append_data(uint16_t pdu_delay_csec,
- const uint8_t *data, uint16_t len)
-{
- struct timespec expire_time;
-
- LOGPTBFDL(this, LOGL_DEBUG, "appending %u bytes\n", len);
-
- struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
- if (!llc_msg)
- return -ENOMEM;
-
- gprs_llc_queue::calc_pdu_lifetime(bts, pdu_delay_csec, &expire_time);
- memcpy(msgb_put(llc_msg, len), data, len);
- llc_queue()->enqueue(llc_msg, &expire_time);
- start_llc_timer();
-
- if (state_is(TBF_ST_WAIT_RELEASE)) {
- LOGPTBFDL(this, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
- establish_dl_tbf_on_pacch();
- }
-
- return 0;
-}
-
-static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, GprsMs *ms,
- struct gprs_rlcmac_dl_tbf **tbf)
-{
- bool ss;
- int8_t use_trx;
- struct gprs_rlcmac_ul_tbf *ul_tbf = NULL, *old_ul_tbf;
- struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
-
- ul_tbf = ms_ul_tbf(ms);
-
- /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
- * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
- * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
- * completed on the mobile station side." */
- if (ul_tbf && ul_tbf->m_contention_resolution_done
- && !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf)) {
- use_trx = ul_tbf->trx->trx_no;
- ss = false;
- old_ul_tbf = ul_tbf;
- } else {
- use_trx = -1;
- ss = true; /* PCH assignment only allows one timeslot */
- old_ul_tbf = NULL;
- }
+ memset(&state_fsm, 0, sizeof(state_fsm));
+ state_fsm.dl_tbf = this;
+ state_fi = osmo_fsm_inst_alloc(&tbf_dl_fsm, this, &state_fsm, LOGL_INFO, NULL);
+ OSMO_ASSERT(state_fi);
- // Create new TBF (any TRX)
-/* FIXME: Copy and paste with alloc_ul_tbf */
- /* set number of downlink slots according to multislot class */
- dl_tbf = tbf_alloc_dl_tbf(bts, ms, use_trx, ss);
+ INIT_LLIST_HEAD(&this->tx_llc_until_first_dl_ack_rcvd);
- if (!dl_tbf) {
- LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
- return -EBUSY;
- }
-
- LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START\n");
-
- /* trigger downlink assignment and set state to ASSIGN.
- * we don't use old_downlink, so the possible uplink is used
- * to trigger downlink assignment. if there is no uplink,
- * AGCH is used. */
- dl_tbf->trigger_ass(old_ul_tbf);
- *tbf = dl_tbf;
- return 0;
+ /* This has to be called in child constructor because enable_egprs()
+ * uses the window() virtual function which is dependent on subclass. */
+ if (ms_mode(m_ms) != GPRS)
+ enable_egprs();
}
/**
@@ -289,41 +190,35 @@ int dl_tbf_handle(struct gprs_rlcmac_bts *bts,
const uint16_t delay_csec,
const uint8_t *data, const uint16_t len)
{
- struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
int rc;
GprsMs *ms, *ms_old;
+ bool ms_allocated = false;
/* check for existing TBF */
- ms = bts_ms_store(bts)->get_ms(tlli, tlli_old, imsi);
-
- if (ms && strlen(ms_imsi(ms)) == 0) {
- ms_old = bts_ms_store(bts)->get_ms(0, 0, imsi);
- if (ms_old && ms_old != ms) {
- /* The TLLI has changed (RAU), so there are two MS
- * objects for the same MS */
- LOGP(DTBF, LOGL_NOTICE,
- "There is a new MS object for the same MS: (0x%08x, '%s') -> (0x%08x, '%s')\n",
- ms_tlli(ms_old), ms_imsi(ms_old), ms_tlli(ms), ms_imsi(ms));
-
- ms_ref(ms_old);
-
- if (!ms_dl_tbf(ms) && ms_dl_tbf(ms_old)) {
+ ms = bts_get_ms(bts, tlli, tlli_old, imsi);
+
+ /* If we got MS by TLLI above let's see if we already have another MS
+ * object identified by IMSI and merge them */
+ if (ms) {
+ if (!ms_imsi_is_valid(ms) && imsi) {
+ ms_old = bts_get_ms_by_imsi(bts, imsi);
+ if (ms_old && ms_old != ms) {
+ /* The TLLI has changed (RAU), so there are two MS
+ * objects for the same MS */
LOGP(DTBF, LOGL_NOTICE,
- "IMSI %s, old TBF %s: moving DL TBF to new MS object\n",
- imsi, ms_dl_tbf(ms_old)->name());
- dl_tbf = ms_dl_tbf(ms_old);
- /* Move the DL TBF to the new MS */
- dl_tbf->set_ms(ms);
+ "There is a new MS object for the same MS: (0x%08x, '%s') -> (0x%08x, '%s')\n",
+ ms_tlli(ms_old), ms_imsi(ms_old), ms_tlli(ms), ms_imsi(ms));
+ ms_merge_and_clear_ms(ms, ms_old);
+ /* old_ms may no longer be available here */
}
- ms_merge_and_clear_ms(ms, ms_old);
-
- ms_unref(ms_old);
}
+ } else {
+ ms = ms_alloc(bts, __func__);
+ /* Remember we have to unref the alloc reference at the end: */
+ ms_allocated = true;
}
-
- if (!ms)
- ms = bts_alloc_ms(bts, ms_class, egprs_ms_class);
- ms_set_imsi(ms, imsi);
+ if (imsi)
+ ms_set_imsi(ms, imsi);
ms_confirm_tlli(ms, tlli);
if (!ms_ms_class(ms) && ms_class) {
ms_set_ms_class(ms, ms_class);
@@ -332,95 +227,15 @@ int dl_tbf_handle(struct gprs_rlcmac_bts *bts,
ms_set_egprs_ms_class(ms, egprs_ms_class);
}
- dl_tbf = ms_dl_tbf(ms);
- if (!dl_tbf) {
- rc = tbf_new_dl_assignment(bts, ms, &dl_tbf);
- if (rc < 0)
- return rc;
+ rc = ms_append_llc_dl_data(ms, delay_csec, data, len);
+ if (ms_allocated) {
+ ms_unref(ms, __func__);
+ /* Here "ms" may be freed if ms_append_llc_dl_data() failed to
+ * allocate a DL TBF and it has no more TBFs attached */
}
-
- rc = dl_tbf->append_data(delay_csec, data, len);
-
return rc;
}
-struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
-{
- struct msgb *msg;
- struct timespec tv_now, tv_now2;
- uint32_t octets = 0, frames = 0;
- struct timespec hyst_delta = {0, 0};
- const unsigned keep_small_thresh = 60;
- const MetaInfo *info;
-
- if (the_pcu->vty.llc_discard_csec)
- csecs_to_timespec(the_pcu->vty.llc_discard_csec, &hyst_delta);
-
- osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now);
- timespecadd(&tv_now, &hyst_delta, &tv_now2);
-
- while ((msg = llc_queue()->dequeue(&info))) {
- const struct timespec *tv_disc = &info->expire_time;
- const struct timespec *tv_recv = &info->recv_time;
-
- gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
-
- if (ms() && ms_codel_state(ms())) {
- int bytes = llc_queue_octets(llc_queue());
- if (gprs_codel_control(ms_codel_state(ms()),
- tv_recv, &tv_now, bytes))
- goto drop_frame;
- }
-
- /* Is the age below the low water mark? */
- if (!gprs_llc_queue::is_frame_expired(&tv_now2, tv_disc))
- break;
-
- /* Is the age below the high water mark */
- if (!gprs_llc_queue::is_frame_expired(&tv_now, tv_disc)) {
- /* Has the previous message not been dropped? */
- if (frames == 0)
- break;
-
- /* Hysteresis mode, try to discard LLC messages until
- * the low water mark has been reached */
-
- /* Check whether to abort the hysteresis mode */
-
- /* Is the frame small, perhaps only a TCP ACK? */
- if (msg->len <= keep_small_thresh)
- break;
-
- /* Is it a GMM message? */
- if (!gprs_llc::is_user_data_frame(msg->data, msg->len))
- break;
- }
-
- bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
-drop_frame:
- frames++;
- octets += msg->len;
- msgb_free(msg);
- bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
- continue;
- }
-
- if (frames) {
- LOGPTBFDL(this, LOGL_NOTICE, "Discarding LLC PDU "
- "because lifetime limit reached, "
- "count=%u new_queue_size=%zu\n",
- frames, llc_queue_size(llc_queue()));
- if (frames > 0xff)
- frames = 0xff;
- if (octets > 0xffffff)
- octets = 0xffffff;
- if (bctx)
- bssgp_tx_llc_discarded(bctx, tlli(), frames, octets);
- }
-
- return msg;
-}
-
bool gprs_rlcmac_dl_tbf::restart_bsn_cycle()
{
/* If V(S) == V(A) and finished state, we would have received
@@ -578,7 +393,7 @@ int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
* Create DL data block
* The messages are fragmented and forwarded as data blocks.
*/
-struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts, enum mcs_kind req_mcs_kind)
+struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, const struct gprs_rlcmac_pdch *pdch, enum mcs_kind req_mcs_kind)
{
int bsn, bsn2 = -1;
bool may_combine;
@@ -593,35 +408,132 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts,
if (may_combine)
bsn2 = take_next_bsn(fn, bsn, req_mcs_kind, &may_combine);
- return create_dl_acked_block(fn, ts, bsn, bsn2);
+ return create_dl_acked_block(fn, pdch, bsn, bsn2);
+}
+
+void gprs_rlcmac_dl_tbf::apply_allocated_resources(const struct alloc_resources_res *res)
+{
+ uint8_t ts;
+
+ if (this->trx)
+ llist_del(&this->m_trx_list.list);
+
+ llist_add(&this->m_trx_list.list, &res->trx->dl_tbfs);
+
+ this->trx = res->trx;
+ this->upgrade_to_multislot = res->upgrade_to_multislot;
+
+ for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
+ struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
+ OSMO_ASSERT(!this->pdch[pdch->ts_no]);
+ if (!(res->ass_slots_mask & (1 << ts)))
+ continue;
+ LOGPTBFDL(this, LOGL_DEBUG, "Assigning TS=%u TFI=%d\n",
+ ts, res->tfi);
+
+ this->m_tfi = res->tfi;
+
+ this->pdch[pdch->ts_no] = pdch;
+ pdch->attach_tbf(this);
+ }
+
+ /* assign initial control ts */
+ tbf_assign_control_ts(this);
+
+ LOGPTBF(this, LOGL_INFO,
+ "Allocated: trx = %d, ul_slots = %02x, dl_slots = %02x\n",
+ this->trx->trx_no, ul_slots(), dl_slots());
+
+ if (tbf_is_egprs_enabled(this))
+ this->set_window_size();
+
+ tbf_update_state_fsm_name(this);
}
-/* depending on the current TBF, we assign on PACCH or AGCH */
-void gprs_rlcmac_dl_tbf::trigger_ass(struct gprs_rlcmac_tbf *old_tbf)
+void dl_tbf_apply_allocated_resources(struct gprs_rlcmac_dl_tbf *dl_tbf, const struct alloc_resources_res *res)
{
- uint16_t pgroup;
+ dl_tbf->apply_allocated_resources(res);
+}
+
+/* old_tbf (UL TBF or DL TBF) will send a Pkt Dl Ass on PACCH to assign tbf.
+ * Note: It is possible that "tbf == old_tbf" if the TBF is being updated. This can
+ * happen when we first assign over PCH (only single slot is possible) and we want
+ * to upgrade the DL-TBF to be multislot. See code calling tbf_update() for more
+ * information.
+ */
+void dl_tbf_trigger_ass_on_pacch(struct gprs_rlcmac_dl_tbf *tbf, struct gprs_rlcmac_tbf *old_tbf)
+{
+ OSMO_ASSERT(tbf);
+ OSMO_ASSERT(old_tbf);
/* stop pending timer */
- stop_timers("assignment (DL-TBF)");
+ tbf_stop_timers(tbf, "DL assignment (PACCH)");
- /* check for downlink tbf: */
- if (old_tbf) {
- LOGPTBFDL(this, LOGL_DEBUG, "Send downlink assignment on PACCH, because %s exists\n", old_tbf->name());
- osmo_fsm_inst_dispatch(old_tbf->dl_ass_fsm.fi, TBF_DL_ASS_EV_SCHED_ASS, NULL);
+ LOGPTBFDL(tbf, LOGL_DEBUG, "Send downlink assignment on PACCH, because %s exists\n", old_tbf->name());
+ osmo_fsm_inst_dispatch(old_tbf->dl_ass_fsm.fi, TBF_DL_ASS_EV_SCHED_ASS, NULL);
- /* change state */
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_ASSIGN_ADD_PACCH, NULL);
- } else {
- LOGPTBFDL(this, LOGL_DEBUG, "Send downlink assignment on PCH, no TBF exist (IMSI=%s)\n",
- imsi());
+ /* change state */
+ osmo_fsm_inst_dispatch(tbf->state_fi, TBF_EV_ASSIGN_ADD_PACCH, NULL);
+
+}
+
+void dl_tbf_trigger_ass_on_pch(struct gprs_rlcmac_dl_tbf *tbf)
+{
+ /* stop pending timer */
+ struct GprsMs *ms = tbf_ms(tbf);
+
+ tbf_stop_timers(tbf, "DL assignment (PCH)");
+
+ LOGPTBFDL(tbf, LOGL_DEBUG, "Send downlink assignment on PCH, no TBF exist (IMSI=%s)\n", ms_imsi(ms));
+
+ /* change state */
+ osmo_fsm_inst_dispatch(tbf->state_fi, TBF_EV_ASSIGN_ADD_CCCH, NULL);
+}
+
+int dl_tbf_upgrade_to_multislot(struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ int rc;
+ struct gprs_rlcmac_tbf *tbf = dl_tbf_as_tbf(dl_tbf);
+ struct gprs_rlcmac_trx *trx = tbf_get_trx(dl_tbf);
+ struct gprs_rlcmac_bts *bts = trx->bts;
+ struct GprsMs *ms = tbf->ms();
- /* change state */
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_ASSIGN_ADD_CCCH, NULL);
+ LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Upgrade to multislot\n");
- /* send immediate assignment */
- if ((pgroup = imsi2paging_group(imsi())) > 999)
- LOGPTBFDL(this, LOGL_ERROR, "IMSI to paging group failed! (%s)\n", imsi());
- bts_snd_dl_ass(bts, this, pgroup);
+ tbf_unlink_pdch(dl_tbf);
+
+ const struct alloc_resources_req req = {
+ .bts = bts,
+ .ms = ms,
+ .direction = tbf_direction(tbf),
+ .single = false,
+ .use_trx = -1,
+ };
+ struct alloc_resources_res res = {};
+
+ rc = the_pcu->alloc_algorithm(&req, &res);
+ /* if no resource */
+ if (rc < 0) {
+ LOGPTBFDL(dl_tbf, LOGL_ERROR, "No resources allocated during upgrade to multislot!\n");
+ bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_FAIL);
+ return rc;
}
+
+ /* Update MS, really allocate the resources */
+ if (res.reserved_ul_slots != ms_reserved_ul_slots(ms) ||
+ res.reserved_dl_slots != ms_reserved_dl_slots(ms)) {
+ /* The reserved slots have changed, update the MS */
+ ms_set_reserved_slots(ms, res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
+ }
+ ms_set_first_common_ts(ms, res.first_common_ts);
+
+ /* Apply allocated resources to TBF: */
+ dl_tbf_apply_allocated_resources(dl_tbf, &res);
+
+ /* Note: No need to call ms_attach_tbf(), tbf is already attached to the MS */
+
+ /* Now trigger the assignment using the pre-existing TBF: */
+ dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
+ return 0;
}
void gprs_rlcmac_dl_tbf::schedule_next_frame()
@@ -632,13 +544,13 @@ void gprs_rlcmac_dl_tbf::schedule_next_frame()
return;
/* dequeue next LLC frame, if any */
- msg = llc_dequeue(bts->pcu->bssgp.bctx);
+ msg = llc_queue_dequeue(llc_queue(), &m_llc.prio, &m_llc.meta_info);
if (!msg)
return;
LOGPTBFDL(this, LOGL_DEBUG, "Dequeue next LLC (len=%d)\n", msg->len);
- m_llc.put_frame(msg->data, msg->len);
+ llc_put_frame(&m_llc, msg->data, msg->len);
bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_SCHED);
msgb_free(msg);
m_last_dl_drained_fn = -1;
@@ -710,7 +622,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, enum CodingScheme cs)
is_final = llc_queue_size(llc_queue()) == 0 && !keep_open(fn);
if (is_final) {
rdbi->cv = 0;
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_LAST_DL_DATA_SENT, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_LAST_DL_DATA_SENT, NULL);
}
if (mcs_is_edge(cs)) {
@@ -730,7 +642,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, enum CodingScheme cs)
* "Delayed release of downlink Temporary Block Flow" */
/* A header will need to by added, so we just need
* space-1 octets */
- m_llc.put_dummy_frame(space - 1);
+ llc_put_dummy_frame(&m_llc, space - 1);
LOGPTBFDL(this, LOGL_DEBUG,
"Empty chunk, added LLC dummy command of size %d, drained_since=%d\n",
@@ -751,11 +663,20 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, enum CodingScheme cs)
LOGPTBFDL(this, LOGL_DEBUG, "Complete DL frame, len=%d\n", llc_frame_length(&m_llc));
gprs_rlcmac_dl_bw(this, llc_frame_length(&m_llc));
bts_do_rate_ctr_add(bts, CTR_LLC_DL_BYTES, llc_frame_length(&m_llc));
- m_llc.reset();
+
+ /* Keep transmitted LLC PDUs until first ACK to avoid lossing them if MS is not there. */
+ if (!this->m_first_dl_ack_rcvd) {
+ struct gprs_dl_llc_llist_item *llc_it = talloc(this, struct gprs_dl_llc_llist_item);
+ memcpy(&llc_it->llc, &m_llc, sizeof(llc_it->llc));
+ /* Prepend to list to store them in inverse order of transmission, see
+ * dl_tbf_copy_unacked_pdus_to_llc_queue() for the complete picture. */
+ llist_add(&llc_it->list, &this->tx_llc_until_first_dl_ack_rcvd);
+ }
+ llc_reset(&m_llc);
if (is_final) {
request_dl_ack();
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_LAST_DL_DATA_SENT, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_LAST_DL_DATA_SENT, NULL);
}
/* dequeue next LLC frame, if any */
@@ -773,7 +694,7 @@ int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, enum CodingScheme cs)
}
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
- const uint32_t fn, const uint8_t ts,
+ const uint32_t fn, const struct gprs_rlcmac_pdch *pdch,
int index, int index2)
{
uint8_t *msg_data;
@@ -940,13 +861,14 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
m_last_dl_poll_fn = fn;
/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx or
- * when last polled DL ACK/NACK was lost. */
- if (need_poll_for_dl_ack_nack()) {
+ * when last polled DL ACK/NACK was lost. Always do so in the control TS. */
+ if (tbf_is_control_ts(dl_tbf_as_tbf(this), pdch) &&
+ need_poll_for_dl_ack_nack()) {
if (m_dl_ack_requested) {
LOGPTBFDL(this, LOGL_DEBUG,
"Scheduling Ack/Nack polling, because it was requested explicitly "
"(e.g. first final block sent).\n");
- } else if (state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK)) {
+ } else if (m_last_dl_poll_ack_lost) {
LOGPTBFDL(this, LOGL_DEBUG,
"Scheduling Ack/Nack polling, because polling timed out.\n");
} else {
@@ -955,22 +877,22 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
POLL_ACK_AFTER_FRAMES);
}
- rc = check_polling(fn, ts, &new_poll_fn, &rrbp);
+ rc = tbf_check_polling(this, pdch, fn, &new_poll_fn, &rrbp);
if (rc >= 0) {
- set_polling(new_poll_fn, ts, PDCH_ULC_POLL_DL_ACK);
+ tbf_set_polling(this, pdch, new_poll_fn, PDCH_ULC_POLL_DL_ACK);
LOGPTBFDL(this, LOGL_DEBUG,
"Scheduled DL Acknowledgement polling on PACCH (FN=%d, TS=%d)\n",
- new_poll_fn, ts);
+ new_poll_fn, pdch->ts_no);
m_tx_counter = 0;
/* start timer whenever we send the final block */
if (is_final)
T_START(this, T3191, 3191, "final block (DL-TBF)", true);
- state_fsm.state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); /* clear poll timeout flag */
-
/* Clear request flag */
m_dl_ack_requested = false;
+ /* clear poll timeout flag */
+ m_last_dl_poll_ack_lost = false;
/* set polling in header */
rlc.rrbp = rrbp;
@@ -980,7 +902,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
LOGPTBFDL(this, LOGL_INFO,
"Scheduled Ack/Nack polling on FN=%d, TS=%d\n",
- new_poll_fn, ts);
+ new_poll_fn, pdch->ts_no);
}
}
@@ -1142,8 +1064,7 @@ int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn,
int gprs_rlcmac_dl_tbf::rcvd_dl_final_ack()
{
uint16_t received;
-
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_FINAL_ACK_RECVD, NULL);
+ int rc;
/* range V(A)..V(S)-1 */
received = m_window.count_unacked();
@@ -1152,12 +1073,9 @@ int gprs_rlcmac_dl_tbf::rcvd_dl_final_ack()
m_tx_counter = 0;
m_window.reset();
- /* check for LLC PDU in the LLC Queue */
- if (llc_queue_size(llc_queue()) > 0)
- /* we have more data so we will re-use this tbf */
- establish_dl_tbf_on_pacch();
+ rc = osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_FINAL_ACK_RECVD, NULL);
- return 0;
+ return rc;
}
int gprs_rlcmac_dl_tbf::rcvd_dl_ack(bool final_ack, unsigned first_bsn,
@@ -1166,8 +1084,16 @@ int gprs_rlcmac_dl_tbf::rcvd_dl_ack(bool final_ack, unsigned first_bsn,
int rc;
LOGPTBFDL(this, LOGL_DEBUG, "downlink acknowledge\n");
- state_fsm.state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK);
- state_fsm.state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
+ if (m_first_dl_ack_rcvd == false) {
+ /* MS is there, free temporarily stored transmitted LLC PDUs */
+ struct gprs_dl_llc_llist_item *llc_it;
+ while ((llc_it = llist_first_entry_or_null(&this->tx_llc_until_first_dl_ack_rcvd, struct gprs_dl_llc_llist_item, list))) {
+ llist_del(&llc_it->list);
+ talloc_free(llc_it);
+ }
+ m_first_dl_ack_rcvd = true;
+ }
+ m_last_dl_poll_ack_lost = false;
/* reset N3105 */
n_reset(N3105);
@@ -1196,12 +1122,48 @@ void gprs_rlcmac_dl_tbf::request_dl_ack()
m_dl_ack_requested = true;
}
+void dl_tbf_request_dl_ack(struct gprs_rlcmac_dl_tbf *dl_tbf) {
+ dl_tbf->request_dl_ack();
+}
+
+bool dl_tbf_first_dl_ack_rcvd(const struct gprs_rlcmac_dl_tbf *tbf)
+{
+ return tbf->m_first_dl_ack_rcvd;
+}
+
+/* Copy back to GprsMs' llc_queue the LLC PDUs previously dequeued and never
+ * fully ACKED at the MS side.
+ * FIXME: For now, only blocks transmitted and without first ever DL ACK are
+ * copied, because we have no way yet to track LLC PDUs once they are converted
+ * to RLC blocks. This is however enough to cover the case where a DL TBF is
+ * assigned over PCH and the MS never answers.
+ */
+void dl_tbf_copy_unacked_pdus_to_llc_queue(struct gprs_rlcmac_dl_tbf *tbf)
+{
+ struct GprsMs *ms = tbf_ms(dl_tbf_as_tbf(tbf));
+ struct gprs_dl_llc_llist_item *llc_it;
+
+ /* If we have LLC PDU still being transmitted, prepend it first to the queue: */
+ if (llc_frame_length(&tbf->m_llc) > 0)
+ llc_queue_merge_prepend(&ms->llc_queue, &tbf->m_llc);
+
+ /* Iterate over the list of totally transmitted LLC PDUs and merge them
+ * into the queue. The items in the list are in inverse order of
+ * transmission, hence when popping from here and enqueueing (prepending)
+ * back to the llc_queue it ends up in the exact same initial order. */
+ while ((llc_it = llist_first_entry_or_null(&tbf->tx_llc_until_first_dl_ack_rcvd, struct gprs_dl_llc_llist_item, list))) {
+ llist_del(&llc_it->list);
+ llc_queue_merge_prepend(&ms->llc_queue, &llc_it->llc);
+ talloc_free(llc_it);
+ }
+}
+
/* Does this DL TBF require to poll the MS for DL ACK/NACK? */
bool gprs_rlcmac_dl_tbf::need_poll_for_dl_ack_nack() const
{
/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx or
* when last polled DL ACK/NACK was lost. */
- return state_fsm.state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK) ||
+ return m_last_dl_poll_ack_lost ||
m_tx_counter >= POLL_ACK_AFTER_FRAMES ||
m_dl_ack_requested;
}
@@ -1442,7 +1404,7 @@ void gprs_rlcmac_dl_tbf::update_coding_scheme_counter_dl(enum CodingScheme cs)
}
}
-struct gprs_rlcmac_dl_tbf *as_dl_tbf(struct gprs_rlcmac_tbf *tbf)
+struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf(struct gprs_rlcmac_tbf *tbf)
{
if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
@@ -1450,7 +1412,10 @@ struct gprs_rlcmac_dl_tbf *as_dl_tbf(struct gprs_rlcmac_tbf *tbf)
return NULL;
}
-void tbf_dl_trigger_ass(struct gprs_rlcmac_dl_tbf *tbf, struct gprs_rlcmac_tbf *old_tbf)
+const struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf_const(const struct gprs_rlcmac_tbf *tbf)
{
- return tbf->trigger_ass(old_tbf);
+ if (tbf && tbf->direction == GPRS_RLCMAC_DL_TBF)
+ return static_cast<const gprs_rlcmac_dl_tbf *>(tbf);
+ else
+ return NULL;
}
diff --git a/src/tbf_dl.h b/src/tbf_dl.h
index d20ad75f..59a03565 100644
--- a/src/tbf_dl.h
+++ b/src/tbf_dl.h
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -22,9 +18,18 @@
#ifdef __cplusplus
#include "tbf.h"
+#include "rlc_window_dl.h"
#include <stdint.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <tbf_fsm.h>
+#ifdef __cplusplus
+}
+#endif
+
/*
* TBF instance
*/
@@ -38,17 +43,20 @@ enum tbf_dl_prio {
DL_PRIO_CONTROL, /* a control block needs to be sent */
};
+struct gprs_dl_llc_llist_item {
+ struct llist_head list; /* this item added in dl_tbf->tx_llc_until_first_dl_ack_rcvd */
+ struct gprs_llc llc;
+};
+
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
gprs_rlcmac_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms);
~gprs_rlcmac_dl_tbf();
gprs_rlc_window *window();
-
- int append_data(uint16_t pdu_delay_csec,
- const uint8_t *data, uint16_t len);
+ void apply_allocated_resources(const struct alloc_resources_res *res);
int rcvd_dl_ack(bool final_ack, unsigned first_bsn, struct bitvec *rbb);
- struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts, enum mcs_kind req_mcs_kind = EGPRS);
- void trigger_ass(struct gprs_rlcmac_tbf *old_tbf);
+ struct msgb *create_dl_acked_block(uint32_t fn, const gprs_rlcmac_pdch *pdch,
+ enum mcs_kind req_mcs_kind = EGPRS);
void request_dl_ack();
bool need_poll_for_dl_ack_nack() const;
@@ -61,8 +69,6 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
void set_window_size();
void update_coding_scheme_counter_dl(enum CodingScheme cs);
- struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
-
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
@@ -71,7 +77,17 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
int32_t m_tx_counter; /* count all transmitted blocks */
bool m_dl_ack_requested;
int32_t m_last_dl_poll_fn;
+ /* Whether we failed to receive ("poll timeout") last PKT CTRL ACK from
+ * MS polled during DL ACK/NACK with RRBP set in "m_last_dl_poll_fn": */
+ bool m_last_dl_poll_ack_lost;
int32_t m_last_dl_drained_fn;
+ /* Whether we receive at least one PKT DL ACK/NACK from MS since this DL TBF was assigned: */
+ bool m_first_dl_ack_rcvd;
+
+ /* Keep transmitted LLC PDUs until first ACK to avoid losing them if MS is not there.
+ * list of gprs_dl_llc_llist_item, stored in inverse order of transmission (last transmitted
+ * is first in the list ) */
+ struct llist_head tx_llc_until_first_dl_ack_rcvd;
struct BandWidth {
struct timespec dl_bw_tv; /* timestamp for dl bw calculation */
@@ -88,6 +104,8 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
struct rate_ctr_group *m_dl_gprs_ctrs;
struct rate_ctr_group *m_dl_egprs_ctrs;
+ struct tbf_dl_fsm_ctx state_fsm;
+
protected:
struct ana_result {
unsigned received_packets;
@@ -100,13 +118,12 @@ protected:
bool *may_combine);
bool restart_bsn_cycle();
int create_new_bsn(const uint32_t fn, enum CodingScheme cs);
- struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
- int index, int index2 = -1);
+ struct msgb *create_dl_acked_block(const uint32_t fn, const struct gprs_rlcmac_pdch *pdch,
+ int index, int index2 = -1);
int update_window(unsigned first_bsn, const struct bitvec *rbb);
int rcvd_dl_final_ack();
bool dl_window_stalled() const;
void reuse_tbf();
- void start_llc_timer();
int analyse_errors(char *show_rbb, uint8_t ssn, ana_result *res);
void schedule_next_frame();
@@ -115,8 +132,6 @@ protected:
unsigned int get_egprs_dl_spb_status(int bsn);
enum egprs_rlcmac_dl_spb get_egprs_dl_spb(int bsn);
- struct osmo_timer_list m_llc_timer;
-
/* Please note that all variables below will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
@@ -130,9 +145,6 @@ inline uint16_t gprs_rlcmac_dl_tbf::window_size() const
return m_window.ws();
}
-struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms,
- int8_t use_trx, bool single_slot);
-
#else /* ifdef __cplusplus */
struct gprs_rlcmac_dl_tbf;
#endif
@@ -142,7 +154,10 @@ extern "C" {
#endif
struct gprs_rlcmac_bts;
-struct gprs_rlcmac_dl_tbf *as_dl_tbf(struct gprs_rlcmac_tbf *tbf);
+struct gprs_rlcmac_dl_tbf *dl_tbf_alloc(struct gprs_rlcmac_bts *bts, struct GprsMs *ms);
+
+struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf(struct gprs_rlcmac_tbf *tbf);
+const struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf_const(const struct gprs_rlcmac_tbf *tbf);
/* dispatch Unitdata.DL messages */
int dl_tbf_handle(struct gprs_rlcmac_bts *bts,
const uint32_t tlli, const uint32_t old_tlli,
@@ -150,9 +165,26 @@ int dl_tbf_handle(struct gprs_rlcmac_bts *bts,
const uint8_t egprs_ms_class, const uint16_t delay_csec,
const uint8_t *data, const uint16_t len);
-void tbf_dl_trigger_ass(struct gprs_rlcmac_dl_tbf *tbf, struct gprs_rlcmac_tbf *old_tbf);
+void dl_tbf_apply_allocated_resources(struct gprs_rlcmac_dl_tbf *dl_tbf, const struct alloc_resources_res *res);
+void dl_tbf_trigger_ass_on_pacch(struct gprs_rlcmac_dl_tbf *tbf, struct gprs_rlcmac_tbf *old_tbf);
+void dl_tbf_trigger_ass_on_pch(struct gprs_rlcmac_dl_tbf *tbf);
+void dl_tbf_request_dl_ack(struct gprs_rlcmac_dl_tbf *tbf);
+bool dl_tbf_first_dl_ack_rcvd(const struct gprs_rlcmac_dl_tbf *tbf);
+int dl_tbf_upgrade_to_multislot(struct gprs_rlcmac_dl_tbf *tbf);
+
+void dl_tbf_copy_unacked_pdus_to_llc_queue(struct gprs_rlcmac_dl_tbf *tbf);
+
+static inline struct gprs_rlcmac_tbf *dl_tbf_as_tbf(struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ return (struct gprs_rlcmac_tbf *)dl_tbf;
+}
+
+static inline const struct gprs_rlcmac_tbf *dl_tbf_as_tbf_const(const struct gprs_rlcmac_dl_tbf *dl_tbf)
+{
+ return (const struct gprs_rlcmac_tbf *)dl_tbf;
+}
-#define LOGPTBFDL(tbf, level, fmt, args...) LOGP(DTBFDL, level, "%s " fmt, tbf_name(tbf), ## args)
+#define LOGPTBFDL(dl_tbf, level, fmt, args...) LOGP(DTBFDL, level, "%s " fmt, tbf_name(dl_tbf_as_tbf_const(dl_tbf)), ## args)
#ifdef __cplusplus
}
#endif
diff --git a/src/tbf_dl_ass_fsm.c b/src/tbf_dl_ass_fsm.c
index 37ced616..d811b711 100644
--- a/src/tbf_dl_ass_fsm.c
+++ b/src/tbf_dl_ass_fsm.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
@@ -35,13 +31,13 @@
#define X(s) (1 << (s))
-const struct osmo_tdef_state_timeout tbf_dl_ass_fsm_timeouts[32] = {
+static const struct osmo_tdef_state_timeout tbf_dl_ass_fsm_timeouts[32] = {
[TBF_DL_ASS_NONE] = {},
[TBF_DL_ASS_SEND_ASS] = {},
[TBF_DL_ASS_WAIT_ACK] = {},
};
-const struct value_string tbf_dl_ass_fsm_event_names[] = {
+static const struct value_string tbf_dl_ass_fsm_event_names[] = {
{ TBF_DL_ASS_EV_SCHED_ASS, "SCHED_ASS" },
{ TBF_DL_ASS_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
{ TBF_DL_ASS_EV_RX_ASS_CTRL_ACK, "RX_ASS_CTRL_ACK" },
@@ -49,7 +45,16 @@ const struct value_string tbf_dl_ass_fsm_event_names[] = {
{ 0, NULL }
};
-struct msgb *create_packet_dl_assign(const struct tbf_dl_ass_fsm_ctx *ctx,
+/* Transition to a state, using the T timer defined in tbf_dl_ass_fsm_timeouts.
+ * The actual timeout value is in turn obtained from conn->T_defs.
+ * Assumes local variable fi exists. */
+#define tbf_dl_ass_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
+ tbf_dl_ass_fsm_timeouts, \
+ the_pcu->T_defs, \
+ -1)
+
+static struct msgb *create_packet_dl_assign(const struct tbf_dl_ass_fsm_ctx *ctx,
const struct tbf_dl_ass_ev_create_rlcmac_msg_ctx *d)
{
struct msgb *msg;
@@ -63,9 +68,9 @@ struct msgb *create_packet_dl_assign(const struct tbf_dl_ass_fsm_ctx *ctx,
bool old_tfi_is_valid = tbf_is_tfi_assigned(ctx->tbf);
/* We only use this function in control TS (PACCH) so that MS can always answer the poll */
- OSMO_ASSERT(tbf_is_control_ts(ctx->tbf, d->ts));
+ OSMO_ASSERT(tbf_is_control_ts(ctx->tbf, d->pdch));
- rc = tbf_check_polling(ctx->tbf, d->fn, d->ts, &new_poll_fn, &rrbp);
+ rc = tbf_check_polling(ctx->tbf, d->pdch, d->fn, &new_poll_fn, &rrbp);
if (rc < 0)
return NULL;
@@ -77,9 +82,6 @@ struct msgb *create_packet_dl_assign(const struct tbf_dl_ass_fsm_ctx *ctx,
return NULL;
}
- if (new_dl_tbf == as_dl_tbf(ctx->tbf))
- LOGPTBF(ctx->tbf, LOGL_DEBUG, "New and old TBF are the same.\n");
-
if (old_tfi_is_valid && ms_tlli(ms) == GSM_RESERVED_TMSI) {
LOGPTBF(ctx->tbf, LOGL_ERROR,
"The old TFI is not assigned and there is no TLLI. New TBF %s\n",
@@ -99,7 +101,12 @@ struct msgb *create_packet_dl_assign(const struct tbf_dl_ass_fsm_ctx *ctx,
};
bitvec_unhex(&bv, DUMMY_VEC);
- LOGPTBF((struct gprs_rlcmac_tbf *)new_dl_tbf, LOGL_INFO, "start Packet Downlink Assignment (PACCH)\n");
+ if (ctx->tbf != (struct gprs_rlcmac_tbf *)new_dl_tbf)
+ LOGPTBF(ctx->tbf, LOGL_INFO, "start Packet Downlink Assignment (PACCH) for %s\n",
+ tbf_name((const struct gprs_rlcmac_tbf *)new_dl_tbf));
+ else
+ LOGPTBF(ctx->tbf, LOGL_INFO, "start Packet Downlink Assignment (PACCH)\n");
+
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->tbf, RlcMacDownlink_t);
write_packet_downlink_assignment(mac_control_block, old_tfi_is_valid,
tbf_tfi(ctx->tbf), (tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF),
@@ -115,9 +122,9 @@ struct msgb *create_packet_dl_assign(const struct tbf_dl_ass_fsm_ctx *ctx,
LOGP(DTBF, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
bts_do_rate_ctr_inc(ms->bts, CTR_PKT_DL_ASSIGNMENT);
- tbf_set_polling(ctx->tbf, new_poll_fn, d->ts, PDCH_ULC_POLL_DL_ASS);
- LOGPTBFDL(ctx->tbf, LOGL_INFO, "Scheduled DL Assignment polling on PACCH (FN=%d, TS=%d)\n",
- new_poll_fn, d->ts);
+ tbf_set_polling(ctx->tbf, d->pdch, new_poll_fn, PDCH_ULC_POLL_DL_ASS);
+ LOGPTBF(ctx->tbf, LOGL_INFO, "Scheduled DL Assignment polling on PACCH (FN=%d, TS=%d)\n",
+ new_poll_fn, d->pdch->ts_no);
talloc_free(mac_control_block);
return msg;
@@ -183,11 +190,7 @@ static void st_wait_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
static int tbf_dl_ass_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
- struct tbf_dl_ass_fsm_ctx *ctx = (struct tbf_dl_ass_fsm_ctx *)fi->priv;
switch (fi->T) {
- case -2000:
- tbf_free(ctx->tbf);
- break;
default:
OSMO_ASSERT(0);
}
@@ -206,7 +209,9 @@ static struct osmo_fsm_state tbf_dl_ass_fsm_states[] = {
},
[TBF_DL_ASS_SEND_ASS] = {
.in_event_mask = X(TBF_DL_ASS_EV_CREATE_RLCMAC_MSG),
- .out_state_mask = X(TBF_DL_ASS_WAIT_ACK),
+ .out_state_mask =
+ X(TBF_DL_ASS_WAIT_ACK) |
+ X(TBF_DL_ASS_NONE),
.name = "SEND_ASS",
.action = st_send_ass,
},
@@ -237,12 +242,14 @@ static __attribute__((constructor)) void tbf_dl_ass_fsm_init(void)
}
-struct msgb *tbf_dl_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf* tbf, uint32_t fn, uint8_t ts)
+struct msgb *tbf_dl_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn)
{
int rc;
struct tbf_dl_ass_ev_create_rlcmac_msg_ctx data_ctx = {
+ .pdch = pdch,
.fn = fn,
- .ts = ts,
.msg = NULL,
};
@@ -252,9 +259,14 @@ struct msgb *tbf_dl_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf* tbf, uin
return data_ctx.msg;
}
-bool tbf_dl_ass_rts(const struct gprs_rlcmac_tbf *tbf)
+bool tbf_dl_ass_rts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch)
{
- struct osmo_fsm_inst *fi = tbf_dl_ass_fi(tbf);
+ struct osmo_fsm_inst *fi;
+
+ if (!tbf_is_control_ts(tbf, pdch))
+ return false;
+
+ fi = tbf_dl_ass_fi(tbf);
if (fi->state != TBF_DL_ASS_SEND_ASS)
return false;
diff --git a/src/tbf_dl_ass_fsm.h b/src/tbf_dl_ass_fsm.h
index 94c9062d..cb7be8d3 100644
--- a/src/tbf_dl_ass_fsm.h
+++ b/src/tbf_dl_ass_fsm.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -25,6 +21,7 @@
#include <gprs_pcu.h>
struct gprs_rlcmac_tbf;
+struct gprs_rlcmac_pdch;
enum tbf_dl_ass_fsm_event {
TBF_DL_ASS_EV_SCHED_ASS, /* Tx Uplink Assignment is pending */
@@ -44,26 +41,18 @@ struct tbf_dl_ass_fsm_ctx {
struct gprs_rlcmac_tbf* tbf; /* back pointer */
};
-extern const struct osmo_tdef_state_timeout tbf_dl_ass_fsm_timeouts[32];
-/* Transition to a state, using the T timer defined in tbf_dl_ass_fsm_timeouts.
- * The actual timeout value is in turn obtained from conn->T_defs.
- * Assumes local variable fi exists. */
-#define tbf_dl_ass_fsm_state_chg(fi, NEXT_STATE) \
- osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
- tbf_dl_ass_fsm_timeouts, \
- the_pcu->T_defs, \
- -1)
-
extern struct osmo_fsm tbf_dl_ass_fsm;
/* passed as data in TBF_DL_ASS_EV_CREATE_RLCMAC_MSG */
struct tbf_dl_ass_ev_create_rlcmac_msg_ctx {
+ const struct gprs_rlcmac_pdch *pdch; /* TS where the created DL ctrl block is to be sent */
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
- uint8_t ts; /* TS where the created DL ctrl block is to be sent */
struct msgb *msg; /* to be filled by FSM during event processing */
};
-struct msgb *tbf_dl_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf* tbf, uint32_t fn, uint8_t ts);
-bool tbf_dl_ass_rts(const struct gprs_rlcmac_tbf* tbf);
+struct msgb *tbf_dl_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn);
+bool tbf_dl_ass_rts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch);
diff --git a/src/tbf_dl_fsm.c b/src/tbf_dl_fsm.c
new file mode 100644
index 00000000..917c4b15
--- /dev/null
+++ b/src/tbf_dl_fsm.c
@@ -0,0 +1,529 @@
+/* tbf_dl_fsm.c
+ *
+ * Copyright (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <talloc.h>
+
+#include <tbf_fsm.h>
+#include <gprs_rlcmac.h>
+#include <gprs_debug.h>
+#include <gprs_ms.h>
+#include <encoding.h>
+#include <bts.h>
+
+#include <bts_pch_timer.h>
+
+#define X(s) (1 << (s))
+
+static const struct osmo_tdef_state_timeout tbf_dl_fsm_timeouts[32] = {
+ [TBF_ST_NEW] = {},
+ [TBF_ST_ASSIGN] = {},
+ [TBF_ST_FLOW] = {},
+ [TBF_ST_FINISHED] = {},
+ [TBF_ST_WAIT_RELEASE] = { .T = 3192 },
+ [TBF_ST_WAIT_REUSE_TFI] = { /* .T = 3193 set manually onenter subtracting T3192 */ },
+ [TBF_ST_RELEASING] = { .T = 3195 },
+};
+
+/* Transition to a state, using the T timer defined in tbf_dl_fsm_timeouts.
+ * The actual timeout value is in turn obtained from T_defs_bts.
+ */
+#define tbf_dl_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
+ tbf_dl_fsm_timeouts, \
+ tbf_ms(((struct tbf_dl_fsm_ctx *)(fi->priv))->tbf)->bts->T_defs_bts, \
+ -1)
+
+static void mod_ass_type(struct tbf_dl_fsm_ctx *ctx, uint8_t t, bool set)
+{
+ const char *ch = "UNKNOWN";
+ bool prev_set = ctx->state_flags & (1 << t);
+
+ switch (t) {
+ case GPRS_RLCMAC_FLAG_CCCH:
+ ch = "CCCH";
+ break;
+ case GPRS_RLCMAC_FLAG_PACCH:
+ ch = "PACCH";
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ LOGPTBFDL(ctx->dl_tbf, LOGL_INFO, "%sset ass. type %s [prev CCCH:%u, PACCH:%u]\n",
+ set ? "" : "un", ch,
+ !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)),
+ !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)));
+
+ if (set && prev_set) {
+ LOGPTBFDL(ctx->dl_tbf, LOGL_ERROR,
+ "Attempted to set ass. type %s which is already set\n", ch);
+ return;
+ }
+
+ if (!set && !prev_set)
+ return;
+
+ if (set)
+ ctx->state_flags |= (1 << t);
+ else
+ ctx->state_flags &= ~(1 << t);
+}
+
+
+static void st_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ switch (event) {
+ case TBF_EV_ASSIGN_ADD_CCCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
+ tbf_dl_fsm_state_chg(fi, TBF_ST_ASSIGN);
+ break;
+ case TBF_EV_ASSIGN_ADD_PACCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
+ tbf_dl_fsm_state_chg(fi, TBF_ST_ASSIGN);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_assign_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ struct GprsMs *ms = tbf_ms(ctx->tbf);
+ unsigned long val;
+ unsigned int sec, micro;
+
+ /* If assignment for this TBF is happening on PACCH, that means the
+ * actual Assignment procedure (tx/rx) is happening on another TBF (eg
+ * Ul TBF vs DL TBF). Hence we add a security timer here to free it in
+ * case the other TBF doesn't succeed in informing (assigning) the MS
+ * about this TBF, or simply because the scheduler takes too long to
+ * schedule it. This timer can probably be dropped once we make the
+ * other TBF always signal us assignment failure (we already get
+ * assignment success through TBF_EV_ASSIGN_ACK_PACCH) */
+ if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)) {
+ fi->T = -2001;
+ val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
+ sec = val / 1000;
+ micro = (val % 1000) * 1000;
+ LOGPTBFDL(ctx->dl_tbf, LOGL_DEBUG,
+ "Starting timer X2001 [assignment (PACCH)] with %u sec. %u microsec\n",
+ sec, micro);
+ osmo_timer_schedule(&fi->timer, sec, micro);
+ } else {
+ /* GPRS_RLCMAC_FLAG_CCCH is set, so here we submit a DL Ass
+ * through PCUIF on CCCH */
+ OSMO_ASSERT(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH));
+ /* Send CCCH (PCH) Immediate Assignment over PCUIF: */
+ bts_snd_dl_ass(ms->bts, ctx->dl_tbf);
+ }
+}
+
+static void st_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ struct GprsMs *ms;
+ unsigned long val;
+ unsigned int sec, micro;
+
+ switch (event) {
+ case TBF_EV_ASSIGN_ADD_CCCH:
+ /* Note: This code path is not really used nowadays, since ADD_CCCH is
+ * only dispatched during dl_tbf allocation (st=NEW) */
+ ms = tbf_ms(ctx->tbf);
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
+ /* Re-send CCCH (PCH) Immediate Assignment over PCUIF: */
+ bts_snd_dl_ass(ms->bts, ctx->dl_tbf);
+ break;
+ case TBF_EV_ASSIGN_ADD_PACCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
+ break;
+ case TBF_EV_ASSIGN_ACK_PACCH:
+ tbf_assign_control_ts(ctx->tbf);
+ if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) {
+ /* We now know that the PACCH really existed */
+ LOGPTBFDL(ctx->dl_tbf, LOGL_INFO,
+ "The TBF has been confirmed on the PACCH, "
+ "changed type from CCCH to PACCH\n");
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
+ }
+ tbf_dl_fsm_state_chg(fi, TBF_ST_FLOW);
+ break;
+ case TBF_EV_ASSIGN_PCUIF_CNF:
+ /* BTS informs us it sent Imm Ass for DL TBF over CCCH. We now
+ * have to wait for X2002 to trigger (meaning MS is already
+ * listening on PDCH) in order to move to FLOW state and start
+ * transmitting data to it. When X2002 triggers (see cb timer
+ * end of the file) it will send TBF_EV_ASSIGN_READY_CCCH back
+ * to us here.
+ */
+ if (!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) {
+ /* This can happen if we initiated a CCCH DlAss from an
+ * older TBF object (same TLLI) towards BTS, and the DL-TBF
+ * was recreated and is now trying to be assigned through
+ * PACCH.
+ */
+ LOGPTBFDL(ctx->dl_tbf, LOGL_INFO,
+ "Ignoring event ASSIGN_PCUIF_CNF from BTS "
+ "(CCCH was not requested on current assignment)\n");
+ break;
+ }
+ fi->T = -2002;
+ val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
+ sec = val / 1000;
+ micro = (val % 1000) * 1000;
+ LOGPTBFDL(ctx->dl_tbf, LOGL_DEBUG,
+ "Starting timer X2002 [assignment (PCH)] with %u sec. %u microsec\n",
+ sec, micro);
+ osmo_timer_schedule(&fi->timer, sec, micro);
+ break;
+ case TBF_EV_ASSIGN_READY_CCCH:
+ /* change state to FLOW, so scheduler will start transmission */
+ tbf_dl_fsm_state_chg(fi, TBF_ST_FLOW);
+ break;
+ case TBF_EV_MAX_N3105:
+ /* We are going to release, so abort any Pkt Ul Ass pending to be scheduled: */
+ osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ctx->tbf), TBF_UL_ASS_EV_ABORT, NULL);
+ tbf_dl_fsm_state_chg(fi, TBF_ST_RELEASING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_flow(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+
+ switch (event) {
+ case TBF_EV_ASSIGN_PCUIF_CNF:
+ if (!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) {
+ /* This can happen if we initiated a CCCH DlAss from an
+ * older TBF object (same TLLI) towards BTS, and the DL-TBF
+ * was recreated (this one) and was successfully assigned over PACCH.
+ * This is usually the case if MS requests 2phase access
+ * to get an UL TBF while we were waiting for a DL TBF
+ * assignment for that same MS over PCH.
+ */
+ LOGPTBFDL(ctx->dl_tbf, LOGL_INFO,
+ "Ignoring event ASSIGN_PCUIF_CNF from BTS "
+ "(CCCH was not requested on current assignment)\n");
+ }
+ break;
+ case TBF_EV_DL_ACKNACK_MISS:
+ /* DL TBF: we missed a DL ACK/NACK. If we started assignment
+ * over CCCH and never received any DL ACK/NACK yet, it means we
+ * don't even know if the MS successfully received the Imm Ass on
+ * CCCH and hence is listening on PDCH. Let's better refrain
+ * from continuing and start assignment on CCCH again */
+ if ((ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) &&
+ !dl_tbf_first_dl_ack_rcvd(ctx->dl_tbf)) {
+ LOGPTBFDL(ctx->dl_tbf, LOGL_DEBUG, "Retransmit ImmAss[PktDlAss] on PCH\n");
+ tbf_dl_fsm_state_chg(fi, TBF_ST_ASSIGN);
+ }
+ break;
+ case TBF_EV_LAST_DL_DATA_SENT:
+ /* All data has been sent or received, change state to FINISHED */
+ tbf_dl_fsm_state_chg(fi, TBF_ST_FINISHED);
+ break;
+ case TBF_EV_MAX_N3105:
+ tbf_dl_fsm_state_chg(fi, TBF_ST_RELEASING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_finished(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case TBF_EV_DL_ACKNACK_MISS:
+ break;
+ case TBF_EV_FINAL_ACK_RECVD:
+ /* We received Final Ack (DL ACK/NACK) from MS. move to
+ * WAIT_RELEASE, where MS stays monitoring PDCH over T3192 span,
+ * where we can use this DL TBF to assign a new one in case we
+ * receive more DL data to Tx */
+ tbf_dl_fsm_state_chg(fi, TBF_ST_WAIT_RELEASE);
+ break;
+ case TBF_EV_MAX_N3105:
+ tbf_dl_fsm_state_chg(fi, TBF_ST_RELEASING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_wait_release_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ struct GprsMs *ms = tbf_ms(ctx->tbf);
+
+ /* This state was entered because FinalACK was received; now T3192 is
+ * running on the MS and has also been armed by this FSM.
+ * During that time, it is possible to reach the MS over PACCH to assign
+ * new DL TBF.
+ * Upon T3192 expiration, FSM will transition to TBF_ST_WAIT_REUSE_TFI
+ * for some more time (T3193 - T3192) until internally freeing the TBF
+ * object, at which time the resources can be reused.
+ */
+
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
+
+ /* check for LLC PDU in the LLC Queue */
+ if (llc_queue_size(ms_llc_queue(ms)) > 0) {
+ /* we have more data so we will re-use this tbf */
+ ms_new_dl_tbf_assigned_on_pacch(ms, ctx->tbf);
+ }
+}
+
+static void st_wait_release(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case TBF_EV_FINAL_ACK_RECVD:
+ /* ignore, duplicate ACK, we already know about since we left ST_FINISHED */
+ break;
+ case TBF_EV_DL_ACKNACK_MISS:
+ /* ignore, miss for retransmitted ACK, but a previous one was
+ * already ACKED since we left ST_FINISHED. This happens due to
+ * fn-advance scheduling several DL blocks in advance. */
+ break;
+ case TBF_EV_MAX_N3105:
+ /* Triggered potentially by a poll timeout of PKT UL/DL TBF ASS.
+ * Reached N3105, the MS is not responding, so stop attempting
+ * using the TBF for DL assignment and change to ST_RELEASING in
+ * order to simply wait until resources can be reused (see
+ * st_releasing_on_enter()). */
+ tbf_dl_fsm_state_chg(fi, TBF_ST_RELEASING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_wait_reuse_tfi_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ struct GprsMs *ms = tbf_ms(ctx->tbf);
+ unsigned long t3192, t3193, res;
+
+ /* T3192 has expired, so the MS is not listening on that PACCH anymore.
+ * Still, wait until remaining of T3193 expiration (>T3192) to internally
+ * free the TBF, at which point the TFI and other allocated resources
+ * will be freed and can then be reused.
+ */
+
+ t3192 = osmo_tdef_get(ms->bts->T_defs_bts, 3192, OSMO_TDEF_MS, -1);
+ t3193 = osmo_tdef_get(ms->bts->T_defs_bts, 3193, OSMO_TDEF_MS, -1);
+ /* As per spec T3193 shall be greater than T3192, but let's be safe against wrong configs: */
+ res = (t3193 >= t3192) ? (t3193 - t3192) : 0;
+ fi->T = 3193;
+ LOGPTBF(ctx->tbf, LOGL_DEBUG, "Waiting %lu sec. %lu microsec (T3193 - T3192) [REUSE TFI]\n",
+ res / 1000, (res % 1000) * 1000);
+ osmo_timer_schedule(&fi->timer, res / 1000, (res % 1000) * 1000);
+}
+
+static void st_wait_reuse_tfi(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ /* Simply wait for T3193 timeout, it will tbf_free() */
+ switch (event) {
+ case TBF_EV_FINAL_ACK_RECVD:
+ /* ignore, duplicate ACK, we already know about since we left ST_FINISHED */
+ break;
+ case TBF_EV_DL_ACKNACK_MISS:
+ /* ignore, miss for retransmitted ACK, but a previous one was
+ * already ACKED since we left ST_FINISHED. This happens due to
+ * fn-advance scheduling several DL blocks in advance. */
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_releasing_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ /* T3195 has been set entering this state: Wait for reuse of TFI(s) when
+ * there is no response from the MS (radio failure or cell change) for this
+ * TBF/MBMS radio bearer. Upon timeout, the timer_cb does tbf_free().
+ */
+}
+
+static void st_releasing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case TBF_EV_DL_ACKNACK_MISS:
+ /* Ignore, we don't care about missed DL ACK/NACK poll timeouts
+ * anymore, we are already releasing the TBF */
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void handle_timeout_X2002(struct osmo_fsm_inst *fi)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ int rc;
+
+ if (fi->state != TBF_ST_ASSIGN) {
+ LOGPTBFDL(ctx->dl_tbf, LOGL_NOTICE, "Continue flow after IMM.ASS confirm\n");
+ return;
+ }
+
+ /* state TBF_ST_ASSIGN: */
+ tbf_assign_control_ts(ctx->tbf);
+
+ if (!tbf_can_upgrade_to_multislot(ctx->tbf)) {
+ /* change state to FLOW, so scheduler will start transmission */
+ osmo_fsm_inst_dispatch(fi, TBF_EV_ASSIGN_READY_CCCH, NULL);
+ return;
+ }
+
+ /* This tbf can be upgraded to use multiple DL timeslots and now that there is already
+ * one slot assigned send another DL assignment via PDCH.
+ */
+
+ /* Reset state flags */
+ ctx->state_flags = 0x00;
+
+ rc = dl_tbf_upgrade_to_multislot(ctx->dl_tbf);
+ if (rc < 0)
+ tbf_free(ctx->tbf);
+}
+
+static int tbf_dl_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct tbf_dl_fsm_ctx *ctx = (struct tbf_dl_fsm_ctx *)fi->priv;
+ switch (fi->T) {
+ case -2002:
+ handle_timeout_X2002(fi);
+ break;
+ case 3192:
+ tbf_dl_fsm_state_chg(fi, TBF_ST_WAIT_REUSE_TFI);
+ break;
+ case -2001:
+ LOGPTBFDL(ctx->dl_tbf, LOGL_NOTICE, "releasing due to PACCH assignment timeout.\n");
+ /* fall-through */
+ case 3193:
+ case 3195:
+ tbf_free(ctx->tbf);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return 0;
+}
+
+static struct osmo_fsm_state tbf_dl_fsm_states[] = {
+ [TBF_ST_NEW] = {
+ .in_event_mask =
+ X(TBF_EV_ASSIGN_ADD_CCCH) |
+ X(TBF_EV_ASSIGN_ADD_PACCH),
+ .out_state_mask =
+ X(TBF_ST_ASSIGN),
+ .name = "NEW",
+ .action = st_new,
+ },
+ [TBF_ST_ASSIGN] = {
+ .in_event_mask =
+ X(TBF_EV_ASSIGN_ADD_CCCH) |
+ X(TBF_EV_ASSIGN_ADD_PACCH) |
+ X(TBF_EV_ASSIGN_ACK_PACCH) |
+ X(TBF_EV_ASSIGN_PCUIF_CNF) |
+ X(TBF_EV_ASSIGN_READY_CCCH) |
+ X(TBF_EV_MAX_N3105),
+ .out_state_mask =
+ X(TBF_ST_FLOW) |
+ X(TBF_ST_FINISHED) |
+ X(TBF_ST_RELEASING),
+ .name = "ASSIGN",
+ .action = st_assign,
+ .onenter = st_assign_on_enter,
+ },
+ [TBF_ST_FLOW] = {
+ .in_event_mask =
+ X(TBF_EV_ASSIGN_PCUIF_CNF) |
+ X(TBF_EV_DL_ACKNACK_MISS) |
+ X(TBF_EV_LAST_DL_DATA_SENT) |
+ X(TBF_EV_MAX_N3105),
+ .out_state_mask =
+ X(TBF_ST_ASSIGN) |
+ X(TBF_ST_FINISHED) |
+ X(TBF_ST_RELEASING),
+ .name = "FLOW",
+ .action = st_flow,
+ },
+ [TBF_ST_FINISHED] = {
+ .in_event_mask =
+ X(TBF_EV_DL_ACKNACK_MISS) |
+ X(TBF_EV_FINAL_ACK_RECVD) |
+ X(TBF_EV_MAX_N3105),
+ .out_state_mask =
+ X(TBF_ST_WAIT_RELEASE) |
+ X(TBF_ST_RELEASING),
+ .name = "FINISHED",
+ .action = st_finished,
+ },
+ [TBF_ST_WAIT_RELEASE] = {
+ .in_event_mask =
+ X(TBF_EV_DL_ACKNACK_MISS) |
+ X(TBF_EV_FINAL_ACK_RECVD) |
+ X(TBF_EV_MAX_N3105),
+ .out_state_mask =
+ X(TBF_ST_WAIT_REUSE_TFI) |
+ X(TBF_ST_RELEASING),
+ .name = "WAIT_RELEASE",
+ .action = st_wait_release,
+ .onenter = st_wait_release_on_enter,
+ },
+ [TBF_ST_WAIT_REUSE_TFI] = {
+ .in_event_mask =
+ X(TBF_EV_DL_ACKNACK_MISS) |
+ X(TBF_EV_FINAL_ACK_RECVD),
+ .out_state_mask =
+ X(TBF_ST_RELEASING),
+ .name = "WAIT_REUSE_TFI",
+ .action = st_wait_reuse_tfi,
+ .onenter = st_wait_reuse_tfi_on_enter,
+ },
+ [TBF_ST_RELEASING] = {
+ .in_event_mask =
+ X(TBF_EV_DL_ACKNACK_MISS),
+ .out_state_mask =
+ 0,
+ .name = "RELEASING",
+ .action = st_releasing,
+ .onenter = st_releasing_on_enter,
+ },
+};
+
+struct osmo_fsm tbf_dl_fsm = {
+ .name = "DL_TBF",
+ .states = tbf_dl_fsm_states,
+ .num_states = ARRAY_SIZE(tbf_dl_fsm_states),
+ .timer_cb = tbf_dl_fsm_timer_cb,
+ .log_subsys = DTBFDL,
+ .event_names = tbf_fsm_event_names,
+};
+
+static __attribute__((constructor)) void tbf_dl_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&tbf_dl_fsm) == 0);
+}
diff --git a/src/tbf_fsm.c b/src/tbf_fsm.c
index af2b34ef..f6dafc3c 100644
--- a/src/tbf_fsm.c
+++ b/src/tbf_fsm.c
@@ -1,6 +1,6 @@
/* tbf_fsm.c
*
- * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Copyright (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* This program is free software; you can redistribute it and/or
@@ -12,33 +12,14 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <unistd.h>
-
-#include <talloc.h>
+#include <osmocom/core/utils.h>
#include <tbf_fsm.h>
-#include <gprs_rlcmac.h>
-#include <gprs_debug.h>
-#include <gprs_ms.h>
-#include <encoding.h>
-#include <bts.h>
-#define X(s) (1 << (s))
-
-const struct osmo_tdef_state_timeout tbf_fsm_timeouts[32] = {
- [TBF_ST_NEW] = {},
- [TBF_ST_ASSIGN] = { },
- [TBF_ST_FLOW] = { },
- [TBF_ST_FINISHED] = {},
- [TBF_ST_WAIT_RELEASE] = {},
- [TBF_ST_RELEASING] = {},
-};
+/* Note: This file contains shared code for UL/DL TBF FSM. See tbf_dl_fsm.c and
+ * tbf_ul_fsm.c for the actual implementations of the FSM */
const struct value_string tbf_fsm_event_names[] = {
{ TBF_EV_ASSIGN_ADD_CCCH, "ASSIGN_ADD_CCCH" },
@@ -46,6 +27,8 @@ const struct value_string tbf_fsm_event_names[] = {
{ TBF_EV_ASSIGN_ACK_PACCH, "ASSIGN_ACK_PACCH" },
{ TBF_EV_ASSIGN_READY_CCCH, "ASSIGN_READY_CCCH" },
{ TBF_EV_ASSIGN_PCUIF_CNF, "ASSIGN_PCUIF_CNF" },
+ { TBF_EV_FIRST_UL_DATA_RECVD, "FIRST_UL_DATA_RECVD" },
+ { TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS, "CONTENTION_RESOLUTION_MS_SUCCESS" },
{ TBF_EV_DL_ACKNACK_MISS, "DL_ACKNACK_MISS" },
{ TBF_EV_LAST_DL_DATA_SENT, "LAST_DL_DATA_SENT" },
{ TBF_EV_LAST_UL_DATA_RECVD, "LAST_UL_DATA_RECVD" },
@@ -56,440 +39,3 @@ const struct value_string tbf_fsm_event_names[] = {
{ TBF_EV_MAX_N3105 , "MAX_N3105" },
{ 0, NULL }
};
-
-static void mod_ass_type(struct tbf_fsm_ctx *ctx, uint8_t t, bool set)
-{
- const char *ch = "UNKNOWN";
- bool prev_set = ctx->state_flags & (1 << t);
-
- switch (t) {
- case GPRS_RLCMAC_FLAG_CCCH:
- ch = "CCCH";
- break;
- case GPRS_RLCMAC_FLAG_PACCH:
- ch = "PACCH";
- break;
- default:
- LOGPTBF(ctx->tbf, LOGL_ERROR,
- "attempted to %sset unexpected ass. type %d - FIXME!\n",
- set ? "" : "un", t);
- return;
- }
-
- if (set && prev_set) {
- LOGPTBF(ctx->tbf, LOGL_ERROR,
- "attempted to set ass. type %s which is already set.\n", ch);
- } else if (!set && !prev_set) {
- return;
- }
-
- LOGPTBF(ctx->tbf, LOGL_INFO, "%sset ass. type %s [prev CCCH:%u, PACCH:%u]\n",
- set ? "" : "un", ch,
- !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)),
- !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)));
-
- if (set) {
- ctx->state_flags |= (1 << t);
- } else {
- ctx->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep to flags */
- ctx->state_flags &= ~(1 << t);
- }
-}
-
-
-static void st_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- switch (event) {
- case TBF_EV_ASSIGN_ADD_CCCH:
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
- tbf_fsm_state_chg(fi, tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF ?
- TBF_ST_ASSIGN : TBF_ST_FLOW);
- break;
- case TBF_EV_ASSIGN_ADD_PACCH:
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
- tbf_fsm_state_chg(fi, TBF_ST_ASSIGN);
- break;
- default:
- OSMO_ASSERT(0);
- }
-}
-
-static void st_assign_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- unsigned long val;
- unsigned int sec, micro;
-
- /* If assignment for this TBF is happening on PACCH, that means the
- * actual Assignment procedure (tx/rx) is happening on another TBF (eg
- * Ul TBF vs DL TBF). Hence we add a security timer here to free it in
- * case the other TBF doesn't succeed in informing (assigning) the MS
- * about this TBF, or simply because the scheduler takes too long to
- * schedule it. This timer can probably be dropped once we make the
- * other TBF always signal us assignment failure (we already get
- * assignment success through TBF_EV_ASSIGN_ACK_PACCH) */
- if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)) {
- fi->T = -2001;
- val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
- sec = val / 1000;
- micro = (val % 1000) * 1000;
- LOGPTBF(ctx->tbf, LOGL_DEBUG,
- "Starting timer X2001 [assignment (PACCH)] with %u sec. %u microsec\n",
- sec, micro);
- osmo_timer_schedule(&fi->timer, sec, micro);
- } else if (tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF) {
- /* GPRS_RLCMAC_FLAG_CCCH is set, so here we submitted an DL Ass through PCUIF on CCCH */
- }
-}
-
-static void st_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- unsigned long val;
- unsigned int sec, micro;
-
- switch (event) {
- case TBF_EV_ASSIGN_ADD_CCCH:
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
- break;
- case TBF_EV_ASSIGN_ADD_PACCH:
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
- break;
- case TBF_EV_ASSIGN_ACK_PACCH:
- tbf_assign_control_ts(ctx->tbf);
- if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) {
- /* We now know that the PACCH really existed */
- LOGPTBF(ctx->tbf, LOGL_INFO,
- "The TBF has been confirmed on the PACCH, "
- "changed type from CCCH to PACCH\n");
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
- }
- tbf_fsm_state_chg(fi, TBF_ST_FLOW);
- break;
- case TBF_EV_ASSIGN_PCUIF_CNF:
- /* BTS informs us it sent Imm Ass for DL TBF over CCCH. We now
- * have to wait for X2002 to trigger (meaning MS is already
- * listening on PDCH) in order to move to FLOW state and start
- * transmitting data to it. When X2002 triggers (see cb timer
- * end of the file) it will send TBF_EV_ASSIGN_READY_CCCH back
- * to us here. */
- OSMO_ASSERT(tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF);
- fi->T = -2002;
- val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
- sec = val / 1000;
- micro = (val % 1000) * 1000;
- LOGPTBF(ctx->tbf, LOGL_DEBUG,
- "Starting timer X2002 [assignment (AGCH)] with %u sec. %u microsec\n",
- sec, micro);
- osmo_timer_schedule(&fi->timer, sec, micro);
- break;
- case TBF_EV_ASSIGN_READY_CCCH:
- /* change state to FLOW, so scheduler will start transmission */
- tbf_fsm_state_chg(fi, TBF_ST_FLOW);
- break;
- case TBF_EV_MAX_N3105:
- /* We are going to release, so abort any Pkt Ul Ass pending to be scheduled: */
- osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ctx->tbf), TBF_UL_ASS_EV_ABORT, NULL);
- ctx->T_release = 3195;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- default:
- OSMO_ASSERT(0);
- }
-}
-
-static void st_flow(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
-
- switch (event) {
- case TBF_EV_DL_ACKNACK_MISS:
- /* DL TBF: we missed a DL ACK/NACK. If we started assignment
- * over CCCH and never received any DL ACK/NACK yet, it means we
- * don't even know if the MS successfuly received the Imm Ass on
- * CCCH and hence is listening on PDCH. Let's better refrain
- * from continuing and start assignment on CCCH again */
- if ((ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
- && !(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) {
- struct GprsMs *ms = tbf_ms(ctx->tbf);
- const char *imsi = ms_imsi(ms);
- uint16_t pgroup;
- LOGPTBF(ctx->tbf, LOGL_DEBUG, "Re-send downlink assignment on PCH (IMSI=%s)\n",
- imsi);
- tbf_fsm_state_chg(fi, TBF_ST_ASSIGN);
- /* send immediate assignment */
- if ((pgroup = imsi2paging_group(imsi)) > 999)
- LOGPTBF(ctx->tbf, LOGL_ERROR, "IMSI to paging group failed! (%s)\n", imsi);
- bts_snd_dl_ass(ms->bts, ctx->tbf, pgroup);
- }
- break;
- case TBF_EV_LAST_DL_DATA_SENT:
- case TBF_EV_LAST_UL_DATA_RECVD:
- /* All data has been sent or received, change state to FINISHED */
- tbf_fsm_state_chg(fi, TBF_ST_FINISHED);
- break;
- case TBF_EV_FINAL_ACK_RECVD:
- /* We received Final Ack (DL ACK/NACK) from MS. move to
- WAIT_RELEASE, we wait there for release or re-use the TBF in
- case we receive more DL data to tx */
- tbf_fsm_state_chg(fi, TBF_ST_WAIT_RELEASE);
- break;
- case TBF_EV_MAX_N3101:
- ctx->T_release = 3169;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- case TBF_EV_MAX_N3105:
- ctx->T_release = 3195;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- default:
- OSMO_ASSERT(0);
- }
-}
-
-static void st_finished(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- switch (event) {
- case TBF_EV_DL_ACKNACK_MISS:
- break;
- case TBF_EV_FINAL_ACK_RECVD:
- /* We received Final Ack (DL ACK/NACK) from MS. move to
- WAIT_RELEASE, we wait there for release or re-use the TBF in
- case we receive more DL data to tx */
- tbf_fsm_state_chg(fi, TBF_ST_WAIT_RELEASE);
- break;
- case TBF_EV_FINAL_UL_ACK_CONFIRMED:
- /* UL TBF ACKed our transmitted UL ACK/NACK with final Ack
- * Indicator set to '1' t. We can free the TBF right away. */
- tbf_free(ctx->tbf);
- break;
- case TBF_EV_MAX_N3103:
- ctx->T_release = 3169;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- case TBF_EV_MAX_N3105:
- ctx->T_release = 3195;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- default:
- OSMO_ASSERT(0);
- }
-}
-
-static void st_wait_release_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- unsigned long val_s, val_ms, val_us;
- OSMO_ASSERT(tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF);
-
- fi->T = 3193;
- val_ms = osmo_tdef_get(tbf_ms(ctx->tbf)->bts->T_defs_bts, fi->T, OSMO_TDEF_MS, -1);
- val_s = val_ms / 1000;
- val_us = (val_ms % 1000) * 1000;
- LOGPTBF(ctx->tbf, LOGL_DEBUG, "starting timer T%u with %lu sec. %lu microsec\n",
- fi->T, val_s, val_us);
- osmo_timer_schedule(&fi->timer, val_s, val_us);
-
- mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
-}
-
-static void st_wait_release(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- switch (event) {
- case TBF_EV_FINAL_ACK_RECVD:
- /* ignore, duplicate ACK, we already know about since we are in WAIT_RELEASE */
- break;
- case TBF_EV_MAX_N3101:
- ctx->T_release = 3169;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- case TBF_EV_MAX_N3105:
- ctx->T_release = 3195;
- tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
- break;
- default:
- OSMO_ASSERT(0);
- }
-}
-
-static void st_releasing_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- unsigned long val;
-
- if (!ctx->T_release)
- return;
-
- /* In general we should end up here with an assigned timer in ctx->T_release. Possible values are:
- * T3195: Wait for reuse of TFI(s) when there is no response from the MS
- * (radio failure or cell change) for this TBF/MBMS radio bearer.
- * T3169: Wait for reuse of USF and TFI(s) after the MS uplink assignment for this TBF is invalid.
- */
- val = osmo_tdef_get(tbf_ms(ctx->tbf)->bts->T_defs_bts, ctx->T_release, OSMO_TDEF_S, -1);
- fi->T = ctx->T_release;
- LOGPTBF(ctx->tbf, LOGL_DEBUG, "starting timer T%u with %lu sec. %u microsec\n",
- ctx->T_release, val, 0);
- osmo_timer_schedule(&fi->timer, val, 0);
-}
-
-static void st_releasing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- switch (event) {
- case TBF_EV_DL_ACKNACK_MISS:
- /* Ignore, we don't care about missed DL ACK/NACK poll timeouts
- * anymore, we are already releasing the TBF */
- break;
- default:
- OSMO_ASSERT(0);
- }
-}
-
-static void handle_timeout_X2002(struct tbf_fsm_ctx *ctx)
-{
- struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(ctx->tbf);
-
- /* X2002 is used only for DL TBF */
- OSMO_ASSERT(dl_tbf);
-
- if (ctx->fi->state == TBF_ST_ASSIGN) {
- tbf_assign_control_ts(ctx->tbf);
-
- if (!tbf_can_upgrade_to_multislot(ctx->tbf)) {
- /* change state to FLOW, so scheduler
- * will start transmission */
- osmo_fsm_inst_dispatch(ctx->fi, TBF_EV_ASSIGN_READY_CCCH, NULL);
- return;
- }
-
- /* This tbf can be upgraded to use multiple DL
- * timeslots and now that there is already one
- * slot assigned send another DL assignment via
- * PDCH. */
-
- /* keep to flags */
- ctx->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
-
- tbf_update(ctx->tbf);
-
- tbf_dl_trigger_ass(dl_tbf, ctx->tbf);
- } else
- LOGPTBF(ctx->tbf, LOGL_NOTICE, "Continue flow after IMM.ASS confirm\n");
-}
-
-static int tbf_fsm_timer_cb(struct osmo_fsm_inst *fi)
-{
- struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
- switch (fi->T) {
- case -2002:
- handle_timeout_X2002(ctx);
- break;
- case -2001:
- LOGPTBF(ctx->tbf, LOGL_NOTICE, "releasing due to PACCH assignment timeout.\n");
- /* fall-through */
- case 3169:
- case 3193:
- case 3195:
- tbf_free(ctx->tbf);
- break;
- default:
- OSMO_ASSERT(0);
- }
- return 0;
-}
-
-static struct osmo_fsm_state tbf_fsm_states[] = {
- [TBF_ST_NEW] = {
- .in_event_mask =
- X(TBF_EV_ASSIGN_ADD_CCCH) |
- X(TBF_EV_ASSIGN_ADD_PACCH),
- .out_state_mask =
- X(TBF_ST_ASSIGN) |
- X(TBF_ST_FLOW) |
- X(TBF_ST_RELEASING),
- .name = "NEW",
- .action = st_new,
- },
- [TBF_ST_ASSIGN] = {
- .in_event_mask =
- X(TBF_EV_ASSIGN_ADD_CCCH) |
- X(TBF_EV_ASSIGN_ADD_PACCH) |
- X(TBF_EV_ASSIGN_ACK_PACCH) |
- X(TBF_EV_ASSIGN_PCUIF_CNF) |
- X(TBF_EV_ASSIGN_READY_CCCH) |
- X(TBF_EV_MAX_N3105),
- .out_state_mask =
- X(TBF_ST_FLOW) |
- X(TBF_ST_FINISHED) |
- X(TBF_ST_RELEASING),
- .name = "ASSIGN",
- .action = st_assign,
- .onenter = st_assign_on_enter,
- },
- [TBF_ST_FLOW] = {
- .in_event_mask =
- X(TBF_EV_DL_ACKNACK_MISS) |
- X(TBF_EV_LAST_DL_DATA_SENT) |
- X(TBF_EV_LAST_UL_DATA_RECVD) |
- X(TBF_EV_FINAL_ACK_RECVD) |
- X(TBF_EV_MAX_N3101) |
- X(TBF_EV_MAX_N3105),
- .out_state_mask =
- X(TBF_ST_ASSIGN) |
- X(TBF_ST_FINISHED) |
- X(TBF_ST_WAIT_RELEASE) |
- X(TBF_ST_RELEASING),
- .name = "FLOW",
- .action = st_flow,
- },
- [TBF_ST_FINISHED] = {
- .in_event_mask =
- X(TBF_EV_DL_ACKNACK_MISS) |
- X(TBF_EV_FINAL_ACK_RECVD) |
- X(TBF_EV_FINAL_UL_ACK_CONFIRMED) |
- X(TBF_EV_MAX_N3103) |
- X(TBF_EV_MAX_N3105),
- .out_state_mask =
- X(TBF_ST_WAIT_RELEASE) |
- X(TBF_ST_RELEASING),
- .name = "FINISHED",
- .action = st_finished,
- },
- [TBF_ST_WAIT_RELEASE] = {
- .in_event_mask =
- X(TBF_EV_FINAL_ACK_RECVD) |
- X(TBF_EV_MAX_N3101) |
- X(TBF_EV_MAX_N3105),
- .out_state_mask =
- X(TBF_ST_RELEASING),
- .name = "WAIT_RELEASE",
- .action = st_wait_release,
- .onenter = st_wait_release_on_enter,
- },
- [TBF_ST_RELEASING] = {
- .in_event_mask =
- X(TBF_EV_DL_ACKNACK_MISS),
- .out_state_mask =
- 0,
- .name = "RELEASING",
- .action = st_releasing,
- .onenter = st_releasing_on_enter,
- },
-};
-
-struct osmo_fsm tbf_fsm = {
- .name = "TBF",
- .states = tbf_fsm_states,
- .num_states = ARRAY_SIZE(tbf_fsm_states),
- .timer_cb = tbf_fsm_timer_cb,
- .log_subsys = DTBF,
- .event_names = tbf_fsm_event_names,
-};
-
-static __attribute__((constructor)) void tbf_fsm_init(void)
-{
- OSMO_ASSERT(osmo_fsm_register(&tbf_fsm) == 0);
-}
diff --git a/src/tbf_fsm.h b/src/tbf_fsm.h
index 614c319d..fd6ffa6f 100644
--- a/src/tbf_fsm.h
+++ b/src/tbf_fsm.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -27,45 +23,55 @@
struct gprs_rlcmac_tbf;
enum tbf_fsm_event {
- TBF_EV_ASSIGN_ADD_CCCH, /* An assignment is sent over CCCH and confirmation from MS is pending */
+ /* For both UL/DL TBF: */
+ TBF_EV_ASSIGN_ADD_CCCH, /* An assignment is sent over CCCH and confirmation from MS is pending */
TBF_EV_ASSIGN_ADD_PACCH, /* An assignment is sent over PACCH and confirmation from MS is pending */
- TBF_EV_ASSIGN_ACK_PACCH, /* We received a CTRL ACK confirming assignment started on PACCH */
+ TBF_EV_ASSIGN_ACK_PACCH, /* We received a CTRL ACK confirming assignment started on PACCH */
+ TBF_EV_MAX_N3105, /* MAX N3105 (max poll timeout) reached */
+
+ /* Only for DL TBF: */
TBF_EV_ASSIGN_READY_CCCH, /* TBF Start Time timer triggered */
- TBF_EV_ASSIGN_PCUIF_CNF, /* Transmission of IMM.ASS for DL TBF to the MS confirmed by BTS over PCUIF */
- TBF_EV_DL_ACKNACK_MISS, /* DL TBF: We polled for DL ACK/NACK but we received none (POLL timeout) */
- TBF_EV_LAST_DL_DATA_SENT, /* DL TBF sends RLCMAC block containing last DL avilable data buffered */
- TBF_EV_LAST_UL_DATA_RECVD, /* UL TBF sends RLCMAC block containing last UL data (cv=0) */
+ TBF_EV_ASSIGN_PCUIF_CNF, /* Transmission of IMM.ASS for to the MS confirmed by BTS over PCUIF */
+ TBF_EV_DL_ACKNACK_MISS, /* We polled for DL ACK/NACK but we received none (POLL timeout) */
+ TBF_EV_LAST_DL_DATA_SENT, /* Network sends RLCMAC block containing last DL avilable data buffered */
TBF_EV_FINAL_ACK_RECVD, /* DL ACK/NACK with FINAL_ACK=1 received from MS */
- TBF_EV_FINAL_UL_ACK_CONFIRMED, /* UL TBF: MS ACKs (CtrlAck or PktResReq) our UL ACK/NACK w/ FinalAckInd=1 */
- TBF_EV_MAX_N3101, /* MAX N3101 (max usf timeout) reached (UL TBF) */
- TBF_EV_MAX_N3103, /* MAX N3103 (max Pkt Ctrl Ack for last UL ACK/NACK timeout) reached (UL TBF) */
- TBF_EV_MAX_N3105, /* MAX N3105 (max poll timeout) reached (UL/DL TBF) */
+
+ /* Only for UL TBF: */
+ TBF_EV_FIRST_UL_DATA_RECVD, /* Received first UL data from MS. Equals to Contention Resolution completed on the network side */
+ TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS, /* Contention resolution success at the mobile station side (first UL_ACK_NACK confirming TLLI is received at the MS) */
+ TBF_EV_LAST_UL_DATA_RECVD, /* MS ends RLCMAC block containing last UL data (cv=0) */
+ TBF_EV_FINAL_UL_ACK_CONFIRMED, /* MS ACKs (CtrlAck or PktResReq) our UL ACK/NACK w/ FinalAckInd=1. data = (bool) MS requests establishment of a new UL-TBF. */
+ TBF_EV_MAX_N3101, /* MAX N3101 (max usf timeout) reached */
+ TBF_EV_MAX_N3103, /* MAX N3103 (max Pkt Ctrl Ack for last UL ACK/NACK timeout) reached */
};
+extern const struct value_string tbf_fsm_event_names[];
+
enum tbf_fsm_states {
TBF_ST_NEW = 0, /* new created TBF */
TBF_ST_ASSIGN, /* wait for downlink assignment */
TBF_ST_FLOW, /* RLC/MAC flow, resource needed */
TBF_ST_FINISHED, /* flow finished, wait for release */
- TBF_ST_WAIT_RELEASE,/* wait for release or restart of DL TBF */
- TBF_ST_RELEASING, /* releasing, wait to free TBI/USF */
+ TBF_ST_WAIT_RELEASE, /* DL TBF: wait for release or restart */
+ TBF_ST_WAIT_REUSE_TFI, /* DL TBF: wait to reuse TFI after last PKT ACK/NACK */
+ TBF_ST_RELEASING, /* releasing, wait to free TFI/USF */
};
-struct tbf_fsm_ctx {
- struct osmo_fsm_inst *fi;
- struct gprs_rlcmac_tbf* tbf; /* back pointer */
+struct tbf_dl_fsm_ctx {
+ union { /* back pointer. union used to easily access superclass from ctx */
+ struct gprs_rlcmac_tbf *tbf;
+ struct gprs_rlcmac_dl_tbf *dl_tbf;
+ };
uint32_t state_flags;
- unsigned int T_release; /* Timer to be used to end release: T3169 or T3195 */
};
-extern const struct osmo_tdef_state_timeout tbf_fsm_timeouts[32];
-/* Transition to a state, using the T timer defined in tbf_fsm_timeouts.
- * The actual timeout value is in turn obtained from conn->T_defs.
- * Assumes local variable fi exists. */
-#define tbf_fsm_state_chg(fi, NEXT_STATE) \
- osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
- tbf_fsm_timeouts, \
- the_pcu->T_defs, \
- -1)
+struct tbf_ul_fsm_ctx {
+ union { /* back pointer. union used to easily access superclass from ctx */
+ struct gprs_rlcmac_tbf *tbf;
+ struct gprs_rlcmac_ul_tbf *ul_tbf;
+ };
+ uint32_t state_flags;
+};
-extern struct osmo_fsm tbf_fsm;
+extern struct osmo_fsm tbf_dl_fsm;
+extern struct osmo_fsm tbf_ul_fsm;
diff --git a/src/tbf_ul.cpp b/src/tbf_ul.cpp
index 74b2636b..3746130f 100644
--- a/src/tbf_ul.cpp
+++ b/src/tbf_ul.cpp
@@ -13,10 +13,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <bts.h>
@@ -33,6 +29,7 @@
#include <gprs_ms.h>
#include <llc.h>
#include "pcu_utils.h"
+#include "alloc_algo.h"
extern "C" {
#include <osmocom/core/msgb.h>
@@ -108,10 +105,9 @@ static int ul_tbf_dtor(struct gprs_rlcmac_ul_tbf *tbf)
}
/* Generic function to alloc a UL TBF, later configured to be assigned either over CCCH or PACCH */
-struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, bool single_slot)
+struct gprs_rlcmac_ul_tbf *ul_tbf_alloc(struct gprs_rlcmac_bts *bts, struct GprsMs *ms)
{
struct gprs_rlcmac_ul_tbf *tbf;
- int rc;
OSMO_ASSERT(ms != NULL);
@@ -124,122 +120,38 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs
talloc_set_destructor(tbf, ul_tbf_dtor);
new (tbf) gprs_rlcmac_ul_tbf(bts, ms);
- rc = tbf->setup(use_trx, single_slot);
-
- /* if no resource */
- if (rc < 0) {
- talloc_free(tbf);
- return NULL;
- }
-
- if (tbf->is_egprs_enabled())
- tbf->set_window_size();
-
- tbf->m_ul_egprs_ctrs = rate_ctr_group_alloc(tbf,
- &tbf_ul_egprs_ctrg_desc, tbf->m_ctrs->idx);
- tbf->m_ul_gprs_ctrs = rate_ctr_group_alloc(tbf,
- &tbf_ul_gprs_ctrg_desc, tbf->m_ctrs->idx);
- if (!tbf->m_ul_egprs_ctrs || !tbf->m_ul_gprs_ctrs) {
- LOGPTBF(tbf, LOGL_ERROR, "Couldn't allocate TBF UL counters\n");
- talloc_free(tbf);
- return NULL;
- }
-
- llist_add_tail(tbf_trx_list(tbf), &tbf->trx->ul_tbfs);
bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_UL_ALLOCATED);
return tbf;
}
-/* Alloc a UL TBF to be assigned over PACCH */
-gprs_rlcmac_ul_tbf *tbf_alloc_ul_pacch(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx)
-{
- struct gprs_rlcmac_ul_tbf *tbf;
-
- tbf = tbf_alloc_ul_tbf(bts, ms, use_trx, false);
- if (!tbf) {
- LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
- /* Caller will most probably send a Imm Ass Reject after return */
- return NULL;
- }
- tbf->m_contention_resolution_done = 1;
- osmo_fsm_inst_dispatch(tbf->state_fsm.fi, TBF_EV_ASSIGN_ADD_PACCH, NULL);
-
- return tbf;
-}
-
-/* Alloc a UL TBF to be assigned over CCCH */
-struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_ccch(struct gprs_rlcmac_bts *bts, struct GprsMs *ms)
-{
- struct gprs_rlcmac_ul_tbf *tbf;
-
- tbf = tbf_alloc_ul_tbf(bts, ms, -1, true);
- if (!tbf) {
- LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
- /* Caller will most probably send a Imm Ass Reject after return */
- return NULL;
- }
- osmo_fsm_inst_dispatch(tbf->state_fsm.fi, TBF_EV_ASSIGN_ADD_CCCH, NULL);
- tbf->contention_resolution_start();
- OSMO_ASSERT(tbf->ms());
-
- return tbf;
-}
-
-/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
- * packet resource Request failed. This is similar as tbf_alloc_ul() but without
- * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
- * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
-struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
- GprsMs *ms, uint8_t trx_no, uint8_t ts)
-{
- struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
- struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
- OSMO_ASSERT(ms);
-
- ul_tbf = talloc(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
- if (!ul_tbf)
- return ul_tbf;
- talloc_set_destructor(ul_tbf, ul_tbf_dtor);
- new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
-
- ul_tbf->control_ts = ts;
- ul_tbf->trx = trx;
- ul_tbf->m_ctrs = rate_ctr_group_alloc(ul_tbf, &tbf_ctrg_desc, next_tbf_ctr_group_id++);
- ul_tbf->m_ul_egprs_ctrs = rate_ctr_group_alloc(ul_tbf,
- &tbf_ul_egprs_ctrg_desc,
- ul_tbf->m_ctrs->idx);
- ul_tbf->m_ul_gprs_ctrs = rate_ctr_group_alloc(ul_tbf,
- &tbf_ul_gprs_ctrg_desc,
- ul_tbf->m_ctrs->idx);
- if (!ul_tbf->m_ctrs || !ul_tbf->m_ul_egprs_ctrs || !ul_tbf->m_ul_gprs_ctrs) {
- LOGPTBF(ul_tbf, LOGL_ERROR, "Cound not allocate TBF UL rate counters\n");
- talloc_free(ul_tbf);
- return NULL;
- }
-
- ms_attach_tbf(ms, ul_tbf);
- llist_add(tbf_trx_list((struct gprs_rlcmac_tbf *)ul_tbf), &trx->ul_tbfs);
- bts_do_rate_ctr_inc(ul_tbf->bts, CTR_TBF_UL_ALLOCATED);
- osmo_fsm_inst_dispatch(ul_tbf->state_fsm.fi, TBF_EV_ASSIGN_ADD_PACCH, NULL);
- osmo_fsm_inst_dispatch(ul_tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
-
- return ul_tbf;
-}
-
gprs_rlcmac_ul_tbf::gprs_rlcmac_ul_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms) :
gprs_rlcmac_tbf(bts_, ms, GPRS_RLCMAC_UL_TBF),
m_rx_counter(0),
- m_contention_resolution_done(0),
+ m_contention_resolution_done(true),
m_ul_gprs_ctrs(NULL),
m_ul_egprs_ctrs(NULL)
{
memset(&m_usf, USF_INVALID, sizeof(m_usf));
+ memset(&state_fsm, 0, sizeof(state_fsm));
+ state_fsm.ul_tbf = this;
+ state_fi = osmo_fsm_inst_alloc(&tbf_ul_fsm, this, &state_fsm, LOGL_INFO, NULL);
+ OSMO_ASSERT(state_fi);
+
memset(&ul_ack_fsm, 0, sizeof(ul_ack_fsm));
ul_ack_fsm.tbf = this;
ul_ack_fsm.fi = osmo_fsm_inst_alloc(&tbf_ul_ack_fsm, this, &ul_ack_fsm, LOGL_INFO, NULL);
+ m_ul_egprs_ctrs = rate_ctr_group_alloc(this, &tbf_ul_egprs_ctrg_desc, m_ctrs->idx);
+ OSMO_ASSERT(m_ul_egprs_ctrs);
+ m_ul_gprs_ctrs = rate_ctr_group_alloc(this, &tbf_ul_gprs_ctrg_desc, m_ctrs->idx);
+ OSMO_ASSERT(m_ul_gprs_ctrs);
+
+ /* This has to be called in child constructor because enable_egprs()
+ * uses the window() virtual function which is dependent on subclass. */
+ if (ms_mode(m_ms) != GPRS)
+ enable_egprs();
}
/*
@@ -276,7 +188,7 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
i + 1, frame->offset, frame->length,
frame->is_complete);
- m_llc.append_frame(data + frame->offset, frame->length);
+ llc_append_frame(&m_llc, data + frame->offset, frame->length);
llc_consume(&m_llc, frame->length);
}
@@ -285,13 +197,14 @@ int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
LOGPTBFUL(this, LOGL_DEBUG, "complete UL frame len=%d\n", llc_frame_length(&m_llc));
snd_ul_ud();
bts_do_rate_ctr_add(bts, CTR_LLC_UL_BYTES, llc_frame_length(&m_llc));
- m_llc.reset();
+ llc_reset(&m_llc);
}
}
return 0;
}
+/* 3GPP TS 44.060 sec 7a.2.1 Contention Resolution */
void gprs_rlcmac_ul_tbf::contention_resolution_start()
{
/* 3GPP TS 44.018 sec 11.1.2 Timers on the network side: "This timer is
@@ -304,23 +217,29 @@ void gprs_rlcmac_ul_tbf::contention_resolution_start()
* timeout only for one-phase packet access, since two-phase is handled
* through SBA structs, which are freed by the PDCH UL Controller if the
* single allocated block is lost. */
+ m_contention_resolution_done = false;
T_START(this, T3141, 3141, "Contention resolution (UL-TBF, CCCH)", true);
}
void gprs_rlcmac_ul_tbf::contention_resolution_success()
{
- if (m_contention_resolution_done)
- return;
+ /* now we must set this flag, so we are allowed to assign downlink
+ * TBF on PACCH. it is only allowed when TLLI is acknowledged
+ * (3GPP TS 44.060 sec 7.1.3.1). */
+ m_contention_resolution_done = true;
- /* 3GPP TS 44.060 sec 7a.2.1 Contention Resolution */
/* 3GPP TS 44.018 3.5.2.1.4 Packet access completion: The one phase
packet access procedure is completed at a successful contention
resolution. The mobile station has entered the packet transfer mode.
Timer T3141 is stopped on the network side */
t_stop(T3141, "Contention resolution success (UL-TBF, CCCH)");
- /* now we must set this flag, so we are allowed to assign downlink
- * TBF on PACCH. it is only allowed when TLLI is acknowledged. */
- m_contention_resolution_done = 1;
+ bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_UL_TBF_CONTENTION_RESOLUTION_SUCCESS);
+
+ /* Check if we can create a DL TBF to start sending the enqueued
+ * data. Otherwise it will be triggered later when it is reachable
+ * again. */
+ if (ms_need_dl_tbf(ms()) && !tbf_ul_ack_waiting_cnf_final_ack(this))
+ ms_new_dl_tbf_assigned_on_pacch(ms(), this);
}
/*! \brief receive data from PDCH/L1 */
@@ -339,6 +258,19 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
"V(R)=%d)\n", rlc->tfi, this->m_window.v_q(),
this->m_window.v_r());
+ if (tbf_state(this) == TBF_ST_RELEASING) {
+ /* This may happen if MAX_N3101 is hit previously, moving the UL
+ * TBF to RELEASING state. Since we have an fn-advance where DL
+ * blocks are scheduled in advance, we may have requested USF for
+ * this UL TBF before triggering and hence we are now receiving a
+ * UL block from it. If this is the case, simply ignore the block.
+ */
+ LOGPTBFUL(this, LOGL_INFO,
+ "UL DATA TFI=%d received (V(Q)=%d .. V(R)=%d) while in RELEASING state, discarding\n",
+ rlc->tfi, this->m_window.v_q(), this->m_window.v_r());
+ return 0;
+ }
+
/* process RSSI */
gprs_rlcmac_rssi(this, rssi);
@@ -371,7 +303,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
rdbi->bsn,
m_window.v_q(), m_window.mod_sns(m_window.v_q() + ws - 1));
continue;
- } else if (m_window.is_received(rdbi->bsn)) {
+ } else if (m_window.m_v_n.is_received(rdbi->bsn)) {
LOGPTBFUL(this, LOGL_DEBUG,
"BSN %d already received\n", rdbi->bsn);
continue;
@@ -429,8 +361,8 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
LOGPTBFUL(this, LOGL_INFO,
"Decoded premier TLLI=0x%08x of UL DATA TFI=%d.\n",
new_tlli, rlc->tfi);
- update_ms(new_tlli, GPRS_RLCMAC_UL_TBF);
- bts_pch_timer_stop(bts, ms_imsi(ms()));
+ ms_update_announced_tlli(ms(), new_tlli);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_FIRST_UL_DATA_RECVD, NULL);
} else if (new_tlli != GSM_RESERVED_TMSI && new_tlli != tlli()) {
LOGPTBFUL(this, LOGL_NOTICE,
"Decoded TLLI=%08x mismatch on UL DATA TFI=%d. (Ignoring due to contention resolution)\n",
@@ -472,7 +404,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
rdbi->bsn, rdbi->cv);
if (rdbi->cv == 0) {
LOGPTBFUL(this, LOGL_DEBUG, "Finished with UL TBF\n");
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_LAST_UL_DATA_RECVD, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_LAST_UL_DATA_RECVD, NULL);
/* Reset N3103 counter. */
this->n_reset(N3103);
}
@@ -535,7 +467,7 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), llc_frame_length(&m_llc));
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
- m_llc.reset_frame_space();
+ llc_reset_frame_space(&m_llc);
return -EIO;
}
@@ -547,7 +479,7 @@ int gprs_rlcmac_ul_tbf::snd_ul_ud()
qos_profile[2] = QOS_PROFILE;
bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu);
- m_llc.reset_frame_space();
+ llc_reset_frame_space(&m_llc);
return 0;
}
@@ -743,13 +675,63 @@ gprs_rlc_window *gprs_rlcmac_ul_tbf::window()
return &m_window;
}
+void gprs_rlcmac_ul_tbf::apply_allocated_resources(const struct alloc_resources_res *res)
+{
+ uint8_t ts;
+
+ if (this->trx)
+ llist_del(&this->m_trx_list.list);
+
+ llist_add(&this->m_trx_list.list, &res->trx->ul_tbfs);
+
+ this->trx = res->trx;
+ this->upgrade_to_multislot = res->upgrade_to_multislot;
+
+ for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
+ struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
+ OSMO_ASSERT(!this->pdch[pdch->ts_no]);
+ if (!(res->ass_slots_mask & (1 << ts)))
+ continue;
+ LOGPTBFUL(this, LOGL_DEBUG, "Assigning TS=%u TFI=%d USF=%u\n",
+ ts, res->tfi, res->usf[ts]);
+ OSMO_ASSERT(res->usf[ts] >= 0);
+
+ this->m_tfi = res->tfi;
+ this->m_usf[pdch->ts_no] = res->usf[ts];
+
+ this->pdch[pdch->ts_no] = pdch;
+ pdch->attach_tbf(this);
+ }
+
+ /* assign initial control ts */
+ tbf_assign_control_ts(this);
+
+ /* res.ass_slots_mask == 0 -> special case for Rejected UL TBFs,
+ * see ms_new_ul_tbf_rejected_pacch() */
+ if (res->ass_slots_mask != 0) {
+ LOGPTBF(this, LOGL_INFO,
+ "Allocated: trx = %d, ul_slots = %02x, dl_slots = %02x\n",
+ this->trx->trx_no, ul_slots(), dl_slots());
+
+ if (tbf_is_egprs_enabled(this))
+ this->set_window_size();
+ }
+
+ tbf_update_state_fsm_name(this);
+}
+
+void ul_tbf_apply_allocated_resources(struct gprs_rlcmac_ul_tbf *ul_tbf, const struct alloc_resources_res *res)
+{
+ ul_tbf->apply_allocated_resources(res);
+}
+
void gprs_rlcmac_ul_tbf::usf_timeout()
{
if (n_inc(N3101))
- osmo_fsm_inst_dispatch(this->state_fsm.fi, TBF_EV_MAX_N3101, NULL);
+ osmo_fsm_inst_dispatch(this->state_fi, TBF_EV_MAX_N3101, NULL);
}
-struct gprs_rlcmac_ul_tbf *as_ul_tbf(struct gprs_rlcmac_tbf *tbf)
+struct gprs_rlcmac_ul_tbf *tbf_as_ul_tbf(struct gprs_rlcmac_tbf *tbf)
{
if (tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
return static_cast<gprs_rlcmac_ul_tbf *>(tbf);
@@ -757,6 +739,14 @@ struct gprs_rlcmac_ul_tbf *as_ul_tbf(struct gprs_rlcmac_tbf *tbf)
return NULL;
}
+const struct gprs_rlcmac_ul_tbf *tbf_as_ul_tbf_const(const struct gprs_rlcmac_tbf *tbf)
+{
+ if (tbf && tbf->direction == GPRS_RLCMAC_UL_TBF)
+ return static_cast<const gprs_rlcmac_ul_tbf *>(tbf);
+ else
+ return NULL;
+}
+
void tbf_usf_timeout(struct gprs_rlcmac_ul_tbf *tbf)
{
tbf->usf_timeout();
@@ -773,6 +763,11 @@ struct osmo_fsm_inst *tbf_ul_ack_fi(const struct gprs_rlcmac_ul_tbf *tbf)
return tbf->ul_ack_fsm.fi;
}
+void ul_tbf_contention_resolution_start(struct gprs_rlcmac_ul_tbf *tbf)
+{
+ tbf->contention_resolution_start();
+}
+
void ul_tbf_contention_resolution_success(struct gprs_rlcmac_ul_tbf *tbf)
{
return tbf->contention_resolution_success();
diff --git a/src/tbf_ul.h b/src/tbf_ul.h
index 0dc2336d..7a4c70c7 100644
--- a/src/tbf_ul.h
+++ b/src/tbf_ul.h
@@ -11,10 +11,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -24,10 +20,12 @@
#include <stdbool.h>
#include "tbf.h"
+#include "rlc_window_ul.h"
#ifdef __cplusplus
extern "C" {
#endif
+#include <tbf_fsm.h>
#include <tbf_ul_ack_fsm.h>
#ifdef __cplusplus
}
@@ -61,8 +59,9 @@ enum tbf_egprs_ul_counters {
struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
gprs_rlcmac_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms);
- ~gprs_rlcmac_ul_tbf();
- gprs_rlc_window *window();
+ ~gprs_rlcmac_ul_tbf(void);
+ gprs_rlc_window *window(void);
+ void apply_allocated_resources(const struct alloc_resources_res *res);
/* blocks were acked */
int rcv_data_block_acknowledged(
const struct gprs_rlc_data_info *rlc,
@@ -71,7 +70,7 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
/* TODO: extract LLC class? */
int assemble_forward_llc(const gprs_rlc_data *data);
- int snd_ul_ud();
+ int snd_ul_ud(void);
egprs_rlc_ul_reseg_bsn_state handle_egprs_ul_spb(
const struct gprs_rlc_data_info *rlc,
@@ -88,12 +87,12 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
struct gprs_rlc_data *block,
uint8_t *data, const uint8_t block_idx);
- uint16_t window_size() const;
- void set_window_size();
+ uint16_t window_size(void) const;
+ void set_window_size(void);
void update_coding_scheme_counter_ul(enum CodingScheme cs);
- void usf_timeout();
- void contention_resolution_start();
- void contention_resolution_success();
+ void usf_timeout(void);
+ void contention_resolution_start(void);
+ void contention_resolution_success(void);
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
@@ -102,12 +101,13 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
*/
int32_t m_rx_counter; /* count all received blocks */
uint8_t m_usf[8]; /* list USFs per PDCH (timeslot), initialized to USF_INVALID */
- uint8_t m_contention_resolution_done; /* set after done */
+ bool m_contention_resolution_done; /* set after done */
struct rate_ctr_group *m_ul_gprs_ctrs;
struct rate_ctr_group *m_ul_egprs_ctrs;
- struct tbf_ul_ass_fsm_ctx ul_ack_fsm;
+ struct tbf_ul_fsm_ctx state_fsm;
+ struct tbf_ul_ack_fsm_ctx ul_ack_fsm;
protected:
void maybe_schedule_uplink_acknack(const gprs_rlc_data_info *rlc, bool countdown_finished);
@@ -125,11 +125,6 @@ inline uint16_t gprs_rlcmac_ul_tbf::window_size() const
return m_window.ws();
}
-struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx, bool single_slot);
-struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_pacch(struct gprs_rlcmac_bts *bts, GprsMs *ms, int8_t use_trx);
-struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_ccch(struct gprs_rlcmac_bts *bts, struct GprsMs *ms);
-struct gprs_rlcmac_ul_tbf *handle_tbf_reject(struct gprs_rlcmac_bts *bts,
- GprsMs *ms, uint8_t trx_no, uint8_t ts_no);
#else /* ifdef __cplusplus */
struct gprs_rlcmac_ul_tbf;
@@ -139,15 +134,29 @@ struct gprs_rlcmac_ul_tbf;
#ifdef __cplusplus
extern "C" {
#endif
+struct gprs_rlcmac_ul_tbf *ul_tbf_alloc(struct gprs_rlcmac_bts *bts, struct GprsMs *ms);
void update_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, int8_t ta_delta);
void set_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, uint8_t ta);
-struct gprs_rlcmac_ul_tbf *as_ul_tbf(struct gprs_rlcmac_tbf *tbf);
+struct gprs_rlcmac_ul_tbf *tbf_as_ul_tbf(struct gprs_rlcmac_tbf *tbf);
+const struct gprs_rlcmac_ul_tbf *tbf_as_ul_tbf_const(const struct gprs_rlcmac_tbf *tbf);
void tbf_usf_timeout(struct gprs_rlcmac_ul_tbf *tbf);
+void ul_tbf_apply_allocated_resources(struct gprs_rlcmac_ul_tbf *ul_tbf, const struct alloc_resources_res *res);
+void ul_tbf_contention_resolution_start(struct gprs_rlcmac_ul_tbf *tbf);
+void ul_tbf_contention_resolution_success(struct gprs_rlcmac_ul_tbf *tbf);
bool ul_tbf_contention_resolution_done(const struct gprs_rlcmac_ul_tbf *tbf);
struct osmo_fsm_inst *tbf_ul_ack_fi(const struct gprs_rlcmac_ul_tbf *tbf);
-void ul_tbf_contention_resolution_success(struct gprs_rlcmac_ul_tbf *tbf);
-#define LOGPTBFUL(tbf, level, fmt, args...) LOGP(DTBFUL, level, "%s " fmt, tbf_name(tbf), ## args)
+static inline struct gprs_rlcmac_tbf *ul_tbf_as_tbf(struct gprs_rlcmac_ul_tbf *ul_tbf)
+{
+ return (struct gprs_rlcmac_tbf *)ul_tbf;
+}
+
+static inline const struct gprs_rlcmac_tbf *ul_tbf_as_tbf_const(const struct gprs_rlcmac_ul_tbf *ul_tbf)
+{
+ return (const struct gprs_rlcmac_tbf *)ul_tbf;
+}
+
+#define LOGPTBFUL(ul_tbf, level, fmt, args...) LOGP(DTBFUL, level, "%s " fmt, tbf_name(ul_tbf_as_tbf_const(ul_tbf)), ## args)
#ifdef __cplusplus
}
#endif
diff --git a/src/tbf_ul_ack_fsm.c b/src/tbf_ul_ack_fsm.c
index 32e3533f..2fa20d70 100644
--- a/src/tbf_ul_ack_fsm.c
+++ b/src/tbf_ul_ack_fsm.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
@@ -37,13 +33,13 @@
#define X(s) (1 << (s))
-const struct osmo_tdef_state_timeout tbf_ul_ack_fsm_timeouts[32] = {
+static const struct osmo_tdef_state_timeout tbf_ul_ack_fsm_timeouts[32] = {
[TBF_UL_ACK_ST_NONE] = {},
[TBF_UL_ACK_ST_SCHED_UL_ACK] = {},
[TBF_UL_ACK_ST_WAIT_ACK] = {},
};
-const struct value_string tbf_ul_ack_fsm_event_names[] = {
+static const struct value_string tbf_ul_ack_fsm_event_names[] = {
{ TBF_UL_ACK_EV_SCHED_ACK, "SCHED_ACK" },
{ TBF_UL_ACK_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
{ TBF_UL_ACK_EV_RX_CTRL_ACK, "RX_CTRL_ACK" },
@@ -51,6 +47,15 @@ const struct value_string tbf_ul_ack_fsm_event_names[] = {
{ 0, NULL }
};
+/* Transition to a state, using the T timer defined in tbf_ul_ack_fsm_timeouts.
+ * The actual timeout value is in turn obtained from conn->T_defs.
+ * Assumes local variable fi exists. */
+#define tbf_ul_ack_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
+ tbf_ul_ack_fsm_timeouts, \
+ the_pcu->T_defs, \
+ -1)
+
static struct msgb *create_ul_ack_nack(const struct tbf_ul_ack_fsm_ctx *ctx,
const struct tbf_ul_ack_ev_create_rlcmac_msg_ctx *d,
bool final)
@@ -59,11 +64,10 @@ static struct msgb *create_ul_ack_nack(const struct tbf_ul_ack_fsm_ctx *ctx,
int rc;
unsigned int rrbp = 0;
uint32_t new_poll_fn = 0;
- struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)ctx->tbf;
- struct GprsMs *ms = tbf_ms(tbf);
+ struct gprs_rlcmac_tbf *tbf = ul_tbf_as_tbf(ctx->tbf);
if (final) {
- rc = tbf_check_polling(tbf, d->fn, d->ts, &new_poll_fn, &rrbp);
+ rc = tbf_check_polling(tbf, d->pdch, d->fn, &new_poll_fn, &rrbp);
if (rc < 0)
return NULL;
}
@@ -81,21 +85,11 @@ static struct msgb *create_ul_ack_nack(const struct tbf_ul_ack_fsm_ctx *ctx,
bitvec_pack(ack_vec, msgb_put(msg, 23));
bitvec_free(ack_vec);
- /* TS 44.060 7a.2.1.1: "The contention resolution is completed on
- * the network side when the network receives an RLC data block that
- * comprises the TLLI value that identifies the mobile station and the
- * TFI value associated with the TBF."
- * However, it's handier for us to mark contention resolution success
- * here since according to spec upon rx UL ACK is the time at which MS
- * realizes contention resolution succeeds. */
- if (ms_tlli(ms) != GSM_RESERVED_TMSI)
- ul_tbf_contention_resolution_success(ctx->tbf);
-
if (final) {
- tbf_set_polling(tbf, new_poll_fn, d->ts, PDCH_ULC_POLL_UL_ACK);
- LOGPTBFUL(tbf, LOGL_DEBUG,
- "Scheduled UL Acknowledgement polling on PACCH (FN=%d, TS=%d)\n",
- new_poll_fn, d->ts);
+ tbf_set_polling(tbf, d->pdch, new_poll_fn, PDCH_ULC_POLL_UL_ACK);
+ LOGPTBFUL(ctx->tbf, LOGL_DEBUG,
+ "Scheduled UL Acknowledgement polling on PACCH (FN=%d, TS=%d)\n",
+ new_poll_fn, d->pdch->ts_no);
}
return msg;
@@ -115,7 +109,8 @@ static void st_none(struct osmo_fsm_inst *fi, uint32_t event, void *data)
static void st_sched_ul_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct tbf_ul_ack_fsm_ctx *ctx = (struct tbf_ul_ack_fsm_ctx *)fi->priv;
- struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)ctx->tbf;
+ struct gprs_rlcmac_ul_tbf *tbf = ctx->tbf;
+ struct GprsMs *ms = tbf_ms(ul_tbf_as_tbf(tbf));
struct tbf_ul_ack_ev_create_rlcmac_msg_ctx *data_ctx;
bool final;
@@ -126,7 +121,7 @@ static void st_sched_ul_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data
break;
case TBF_UL_ACK_EV_CREATE_RLCMAC_MSG:
data_ctx = (struct tbf_ul_ack_ev_create_rlcmac_msg_ctx *)data;
- final = tbf_state(tbf) == TBF_ST_FINISHED;
+ final = tbf_state(ul_tbf_as_tbf(tbf)) == TBF_ST_FINISHED;
data_ctx->msg = create_ul_ack_nack(ctx, data_ctx, final);
if (!data_ctx->msg)
return;
@@ -134,6 +129,28 @@ static void st_sched_ul_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data
tbf_ul_ack_fsm_state_chg(fi, TBF_UL_ACK_ST_WAIT_ACK);
else
tbf_ul_ack_fsm_state_chg(fi, TBF_UL_ACK_ST_NONE);
+
+ /* TS 44.060 7a.2.1.1: "The contention resolution is completed on
+ * the network side when the network receives an RLC data block that
+ * comprises the TLLI value that identifies the mobile station and the
+ * TFI value associated with the TBF." (see TBF_EV_FIRST_UL_DATA_RECVD).
+ *
+ * However, it's handier for us to mark contention resolution success here
+ * since upon rx UL ACK is the time at which MS realizes contention resolution
+ * succeeds:
+ * TS 44.060 7.1.2.3: "The contention resolution is successfully completed
+ * on the mobile station side when the mobile station receives a
+ * PACKET UPLINK ACK/NACK"
+ *
+ * This event must be triggered here *after* potentially transitioning
+ * to ST_WAIT_ACK above, so that gprs_ms knows whether it can create a
+ * DL TBF on PACCH of the UL_TBF or not (not possible if we are in
+ * ST_WAIT_ACK, since UL TBF is terminating sending the final PKT CTRL
+ * ACK).
+ */
+ if (ms_tlli(ms) != GSM_RESERVED_TMSI && !ul_tbf_contention_resolution_done(ctx->tbf))
+ osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ctx->tbf)), TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS, NULL);
+
break;
default:
OSMO_ASSERT(0);
@@ -143,7 +160,7 @@ static void st_sched_ul_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data
static void st_wait_ctrl_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct tbf_ul_ack_fsm_ctx *ctx = (struct tbf_ul_ack_fsm_ctx *)fi->priv;
- struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)ctx->tbf;
+ struct gprs_rlcmac_ul_tbf *tbf = ctx->tbf;
switch (event) {
case TBF_UL_ACK_EV_SCHED_ACK:
@@ -153,9 +170,9 @@ static void st_wait_ctrl_ack(struct osmo_fsm_inst *fi, uint32_t event, void *dat
tbf_ul_ack_fsm_state_chg(fi, TBF_UL_ACK_ST_NONE);
break;
case TBF_UL_ACK_EV_POLL_TIMEOUT:
- LOGPTBF(tbf, LOGL_NOTICE,
+ LOGPTBFUL(tbf, LOGL_NOTICE,
"Timeout for polling PACKET CONTROL ACK for PACKET UPLINK ACK: %s\n",
- tbf_rlcmac_diag(tbf));
+ tbf_rlcmac_diag(ul_tbf_as_tbf(tbf)));
/* Reschedule Ul Ack/NAck */
tbf_ul_ack_fsm_state_chg(fi, TBF_UL_ACK_ST_SCHED_UL_ACK);
break;
@@ -220,39 +237,44 @@ static __attribute__((constructor)) void tbf_ul_ack_fsm_init(void)
}
-struct msgb *tbf_ul_ack_create_rlcmac_msg(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
+struct msgb *tbf_ul_ack_create_rlcmac_msg(const struct gprs_rlcmac_ul_tbf *ul_tbf,
+ const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn)
{
int rc;
struct tbf_ul_ack_ev_create_rlcmac_msg_ctx data_ctx = {
+ .pdch = pdch,
.fn = fn,
- .ts = ts,
.msg = NULL,
};
- OSMO_ASSERT(tbf_direction(tbf) == GPRS_RLCMAC_UL_TBF);
- rc = osmo_fsm_inst_dispatch(tbf_ul_ack_fi((const struct gprs_rlcmac_ul_tbf *)tbf), TBF_UL_ACK_EV_CREATE_RLCMAC_MSG, &data_ctx);
+ rc = osmo_fsm_inst_dispatch(tbf_ul_ack_fi(ul_tbf), TBF_UL_ACK_EV_CREATE_RLCMAC_MSG, &data_ctx);
if (rc != 0 || !data_ctx.msg)
return NULL;
return data_ctx.msg;
}
-bool tbf_ul_ack_rts(const struct gprs_rlcmac_tbf *tbf)
+bool tbf_ul_ack_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_pdch *pdch)
{
- struct osmo_fsm_inst *fi = tbf_ul_ack_fi((const struct gprs_rlcmac_ul_tbf *)tbf);
+ struct osmo_fsm_inst *fi;
+
+ if (!tbf_is_control_ts(ul_tbf_as_tbf_const(ul_tbf), pdch))
+ return false;
+
+ fi = tbf_ul_ack_fi(ul_tbf);
return fi->state == TBF_UL_ACK_ST_SCHED_UL_ACK;
}
/* Did we already send the Final ACK and we are waiting for its confirmation (CTRL ACK) ? */
-bool tbf_ul_ack_waiting_cnf_final_ack(const struct gprs_rlcmac_tbf* tbf)
+bool tbf_ul_ack_waiting_cnf_final_ack(const struct gprs_rlcmac_ul_tbf *ul_tbf)
{
- OSMO_ASSERT(tbf_direction(tbf) == GPRS_RLCMAC_UL_TBF);
- struct osmo_fsm_inst *fi = tbf_ul_ack_fi((const struct gprs_rlcmac_ul_tbf *)tbf);
+ struct osmo_fsm_inst *fi = tbf_ul_ack_fi(ul_tbf);
return fi->state == TBF_UL_ACK_ST_WAIT_ACK;
}
-bool tbf_ul_ack_exp_ctrl_ack(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
+bool tbf_ul_ack_exp_ctrl_ack(const struct gprs_rlcmac_ul_tbf *ul_tbf, uint32_t fn, uint8_t ts)
{
- struct osmo_fsm_inst *fi = tbf_ul_ack_fi((const struct gprs_rlcmac_ul_tbf *)tbf);
+ struct osmo_fsm_inst *fi = tbf_ul_ack_fi(ul_tbf);
return fi->state == TBF_UL_ACK_ST_WAIT_ACK;
/* FIXME: validate FN and TS match: && ctx->poll_fn = fn && ctx->poll_ts == ts */
}
diff --git a/src/tbf_ul_ack_fsm.h b/src/tbf_ul_ack_fsm.h
index aaee5a3e..3f34f31a 100644
--- a/src/tbf_ul_ack_fsm.h
+++ b/src/tbf_ul_ack_fsm.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -26,6 +22,7 @@
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_ul_tbf;
+struct gprs_rlcmac_pdch;
enum tbf_ul_ack_fsm_event {
TBF_UL_ACK_EV_SCHED_ACK, /* Tx UL ACK/NACK is pending */
@@ -45,28 +42,20 @@ struct tbf_ul_ack_fsm_ctx {
struct gprs_rlcmac_ul_tbf *tbf; /* back pointer */
};
-extern const struct osmo_tdef_state_timeout tbf_ul_ack_fsm_timeouts[32];
-/* Transition to a state, using the T timer defined in tbf_ul_ack_fsm_timeouts.
- * The actual timeout value is in turn obtained from conn->T_defs.
- * Assumes local variable fi exists. */
-#define tbf_ul_ack_fsm_state_chg(fi, NEXT_STATE) \
- osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
- tbf_ul_ack_fsm_timeouts, \
- the_pcu->T_defs, \
- -1)
-
extern struct osmo_fsm tbf_ul_ack_fsm;
/* passed as data in TBF_UL_ACK_EV_CREATE_RLCMAC_MSG */
struct tbf_ul_ack_ev_create_rlcmac_msg_ctx {
+ const struct gprs_rlcmac_pdch *pdch; /* TS where the created DL ctrl block is to be sent */
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
- uint8_t ts; /* TS where the created DL ctrl block is to be sent */
struct msgb *msg; /* to be filled by FSM during event processing */
};
-struct msgb *tbf_ul_ack_create_rlcmac_msg(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
-bool tbf_ul_ack_rts(const struct gprs_rlcmac_tbf *tbf);
-bool tbf_ul_ack_waiting_cnf_final_ack(const struct gprs_rlcmac_tbf *tbf);
-bool tbf_ul_ack_exp_ctrl_ack(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
+struct msgb *tbf_ul_ack_create_rlcmac_msg(const struct gprs_rlcmac_ul_tbf *ul_tbf,
+ const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn);
+bool tbf_ul_ack_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_pdch *pdch);
+bool tbf_ul_ack_waiting_cnf_final_ack(const struct gprs_rlcmac_ul_tbf *ul_tbf);
+bool tbf_ul_ack_exp_ctrl_ack(const struct gprs_rlcmac_ul_tbf *ul_tbf, uint32_t fn, uint8_t ts);
diff --git a/src/tbf_ul_ass_fsm.c b/src/tbf_ul_ass_fsm.c
index ab23fbdb..6f88f112 100644
--- a/src/tbf_ul_ass_fsm.c
+++ b/src/tbf_ul_ass_fsm.c
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <unistd.h>
@@ -35,14 +31,14 @@
#define X(s) (1 << (s))
-const struct osmo_tdef_state_timeout tbf_ul_ass_fsm_timeouts[32] = {
+static const struct osmo_tdef_state_timeout tbf_ul_ass_fsm_timeouts[32] = {
[TBF_UL_ASS_NONE] = {},
- [TBF_UL_ASS_SEND_ASS] = {},
+ [TBF_UL_ASS_SEND_ASS] = { .keep_timer = true },
[TBF_UL_ASS_SEND_ASS_REJ] = {},
- [TBF_UL_ASS_WAIT_ACK] = {},
+ [TBF_UL_ASS_WAIT_ACK] = { .keep_timer = true },
};
-const struct value_string tbf_ul_ass_fsm_event_names[] = {
+static const struct value_string tbf_ul_ass_fsm_event_names[] = {
{ TBF_UL_ASS_EV_SCHED_ASS, "SCHED_ASS" },
{ TBF_UL_ASS_EV_SCHED_ASS_REJ, "SCHED_ASS_REJ" },
{ TBF_UL_ASS_EV_CREATE_RLCMAC_MSG, "CREATE_RLCMAC_MSG" },
@@ -52,6 +48,15 @@ const struct value_string tbf_ul_ass_fsm_event_names[] = {
{ 0, NULL }
};
+/* Transition to a state, using the T timer defined in tbf_ul_ass_fsm_timeouts.
+ * The actual timeout value is in turn obtained from conn->T_defs.
+ * Assumes local variable fi exists. */
+#define tbf_ul_ass_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
+ tbf_ul_ass_fsm_timeouts, \
+ the_pcu->T_defs, \
+ -1)
+
static struct msgb *create_packet_access_reject(const struct tbf_ul_ass_fsm_ctx *ctx)
{
struct msgb *msg;
@@ -64,7 +69,7 @@ static struct msgb *create_packet_access_reject(const struct tbf_ul_ass_fsm_ctx
bitvec_unhex(packet_access_rej, DUMMY_VEC);
write_packet_access_reject(packet_access_rej, ms_tlli(ms),
- osmo_tdef_get(ms->bts->T_defs_bts, 3172, OSMO_TDEF_MS, -1));
+ osmo_tdef_get(ms->bts->pcu->T_defs, 3172, OSMO_TDEF_MS, -1));
bts_do_rate_ctr_inc(ms->bts, CTR_PKT_ACCESS_REJ);
@@ -75,7 +80,7 @@ static struct msgb *create_packet_access_reject(const struct tbf_ul_ass_fsm_ctx
}
-struct msgb *create_packet_ul_assign(const struct tbf_ul_ass_fsm_ctx *ctx,
+static struct msgb *create_packet_ul_assign(const struct tbf_ul_ass_fsm_ctx *ctx,
const struct tbf_ul_ass_ev_create_rlcmac_msg_ctx *d)
{
struct msgb *msg = NULL;
@@ -87,7 +92,7 @@ struct msgb *create_packet_ul_assign(const struct tbf_ul_ass_fsm_ctx *ctx,
unsigned int rrbp;
uint32_t new_poll_fn;
- rc = tbf_check_polling(ctx->tbf, d->fn, d->ts, &new_poll_fn, &rrbp);
+ rc = tbf_check_polling(ctx->tbf, d->pdch, d->fn, &new_poll_fn, &rrbp);
if (rc < 0)
return NULL;
@@ -110,7 +115,12 @@ struct msgb *create_packet_ul_assign(const struct tbf_ul_ass_fsm_ctx *ctx,
};
bitvec_unhex(&bv, DUMMY_VEC);
- LOGPTBFUL((const struct gprs_rlcmac_tbf *)new_tbf, LOGL_INFO, "start Packet Uplink Assignment (PACCH)\n");
+ if (ctx->tbf != ul_tbf_as_tbf_const(new_tbf))
+ LOGPTBF(ctx->tbf, LOGL_INFO, "start Packet Uplink Assignment (PACCH) for %s\n",
+ tbf_name(ul_tbf_as_tbf_const(new_tbf)));
+ else
+ LOGPTBF(ctx->tbf, LOGL_INFO, "start Packet Uplink Assignment (PACCH)\n");
+
mac_control_block = (RlcMacDownlink_t *)talloc_zero(ctx->tbf, RlcMacDownlink_t);
tlli = ms_tlli(ms);
write_packet_uplink_assignment(mac_control_block, tbf_tfi(ctx->tbf),
@@ -127,9 +137,9 @@ struct msgb *create_packet_ul_assign(const struct tbf_ul_ass_fsm_ctx *ctx,
LOGP(DTBF, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n");
bts_do_rate_ctr_inc(ms->bts, CTR_PKT_UL_ASSIGNMENT);
- tbf_set_polling(ctx->tbf, new_poll_fn, d->ts, PDCH_ULC_POLL_UL_ASS);
- LOGPTBFUL(ctx->tbf, LOGL_INFO, "Scheduled UL Assignment polling on PACCH (FN=%d, TS=%d)\n",
- new_poll_fn, d->ts);
+ tbf_set_polling(ctx->tbf, d->pdch, new_poll_fn, PDCH_ULC_POLL_UL_ASS);
+ LOGPTBF(ctx->tbf, LOGL_INFO, "Scheduled UL Assignment polling on PACCH (FN=%d, TS=%d)\n",
+ new_poll_fn, d->pdch->ts_no);
talloc_free(mac_control_block);
return msg;
@@ -154,7 +164,7 @@ static void st_none_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
sec = val / 1000;
micro = (val % 1000) * 1000;
- LOGPTBF(ctx->tbf, LOGL_DEBUG, "starting timer X2000 [reject (PACCH)] with %u sec. %u microsec\n",
+ LOGPTBF(ctx->tbf, LOGL_DEBUG, "Starting timer X2000 [reject (PACCH)] with %u sec. %u microsec\n",
sec, micro);
osmo_timer_schedule(&fi->timer, sec, micro);
}
@@ -178,6 +188,32 @@ static void st_none(struct osmo_fsm_inst *fi, uint32_t event, void *data)
}
}
+static void st_send_ass_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct tbf_ul_ass_fsm_ctx *ctx = (struct tbf_ul_ass_fsm_ctx *)fi->priv;
+ unsigned long val;
+ unsigned int sec, micro;
+ struct GprsMs *ms = tbf_ms(ctx->tbf);
+
+ /* Here it's point in time where we received PKT RES REQ or DL ACK/NACK to request a new UL TBF,
+ * so MS will be gone after T3168 (* 4 retrans, 8.1.1.1.2) if we are unable to seize it.
+ * Hence, attempt re-scheduling PKT UL ASS (states SEND_ASS<->WAIT_ACK ping-pong) until T3168 we
+ * announced (SI13) to the MS expires:
+ */
+ if (prev_state == TBF_UL_ASS_NONE) {
+ /* tbf_free() called upon trigger */
+ fi->T = 3168;
+ val = osmo_tdef_get(ms->bts->T_defs_bts, fi->T, OSMO_TDEF_MS, -1);
+ val *= 4; /* 4 PKT RES REQ retransmit */
+ sec = val / 1000;
+ micro = (val % 1000) * 1000;
+ LOGPTBF(ctx->tbf, LOGL_DEBUG, "Starting timer T3168 [PKT UL ASS PACCH] with %u sec. %u microsec\n",
+ sec, micro);
+ osmo_timer_schedule(&fi->timer, sec, micro);
+ }
+
+}
+
static void st_send_ass(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct tbf_ul_ass_fsm_ctx *ctx = (struct tbf_ul_ass_fsm_ctx *)fi->priv;
@@ -258,6 +294,7 @@ static int tbf_ul_ass_fsm_timer_cb(struct osmo_fsm_inst *fi)
struct tbf_ul_ass_fsm_ctx *ctx = (struct tbf_ul_ass_fsm_ctx *)fi->priv;
switch (fi->T) {
case -2000:
+ case 3168:
tbf_free(ctx->tbf);
break;
default:
@@ -288,6 +325,7 @@ static struct osmo_fsm_state tbf_ul_ass_fsm_states[] = {
X(TBF_UL_ASS_NONE),
.name = "SEND_ASS",
.action = st_send_ass,
+ .onenter = st_send_ass_on_enter,
},
[TBF_UL_ASS_SEND_ASS_REJ] = {
.in_event_mask =
@@ -325,12 +363,14 @@ static __attribute__((constructor)) void tbf_ul_ass_fsm_init(void)
}
-struct msgb *tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf* tbf, uint32_t fn, uint8_t ts)
+struct msgb *tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn)
{
int rc;
struct tbf_ul_ass_ev_create_rlcmac_msg_ctx data_ctx = {
+ .pdch = pdch,
.fn = fn,
- .ts = ts,
.msg = NULL,
};
@@ -340,8 +380,13 @@ struct msgb *tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf* tbf, uin
return data_ctx.msg;
}
-bool tbf_ul_ass_rts(const struct gprs_rlcmac_tbf* tbf)
+bool tbf_ul_ass_rts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch)
{
- struct osmo_fsm_inst *fi = tbf_ul_ass_fi(tbf);
+ struct osmo_fsm_inst *fi;
+
+ if (!tbf_is_control_ts(tbf, pdch))
+ return false;
+
+ fi = tbf_ul_ass_fi(tbf);
return fi->state == TBF_UL_ASS_SEND_ASS || fi->state == TBF_UL_ASS_SEND_ASS_REJ;
}
diff --git a/src/tbf_ul_ass_fsm.h b/src/tbf_ul_ass_fsm.h
index 34b81a7b..28a09dcd 100644
--- a/src/tbf_ul_ass_fsm.h
+++ b/src/tbf_ul_ass_fsm.h
@@ -12,10 +12,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#pragma once
@@ -25,6 +21,7 @@
#include <gprs_pcu.h>
struct gprs_rlcmac_tbf;
+struct gprs_rlcmac_pdch;
enum tbf_ul_ass_fsm_event {
TBF_UL_ASS_EV_SCHED_ASS, /* Tx Uplink Assignment is pending */
@@ -47,26 +44,18 @@ struct tbf_ul_ass_fsm_ctx {
struct gprs_rlcmac_tbf* tbf; /* back pointer */
};
-extern const struct osmo_tdef_state_timeout tbf_ul_ass_fsm_timeouts[32];
-/* Transition to a state, using the T timer defined in tbf_ul_ass_fsm_timeouts.
- * The actual timeout value is in turn obtained from conn->T_defs.
- * Assumes local variable fi exists. */
-#define tbf_ul_ass_fsm_state_chg(fi, NEXT_STATE) \
- osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
- tbf_ul_ass_fsm_timeouts, \
- the_pcu->T_defs, \
- -1)
-
extern struct osmo_fsm tbf_ul_ass_fsm;
/* passed as data in TBF_UL_ASS_EV_CREATE_RLCMAC_MSG */
struct tbf_ul_ass_ev_create_rlcmac_msg_ctx {
+ const struct gprs_rlcmac_pdch *pdch; /* TS where the created DL ctrl block is to be sent */
uint32_t fn; /* FN where the created DL ctrl block is to be sent */
- uint8_t ts; /* TS where the created DL ctrl block is to be sent */
struct msgb *msg; /* to be filled by FSM during event processing */
};
-struct msgb *tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf* tbf, uint32_t fn, uint8_t ts);
-bool tbf_ul_ass_rts(const struct gprs_rlcmac_tbf* tbf);
+struct msgb *tbf_ul_ass_create_rlcmac_msg(const struct gprs_rlcmac_tbf *tbf,
+ const struct gprs_rlcmac_pdch *pdch,
+ uint32_t fn);
+bool tbf_ul_ass_rts(const struct gprs_rlcmac_tbf *tbf, const struct gprs_rlcmac_pdch *pdch);
diff --git a/src/tbf_ul_fsm.c b/src/tbf_ul_fsm.c
new file mode 100644
index 00000000..7dff3599
--- /dev/null
+++ b/src/tbf_ul_fsm.c
@@ -0,0 +1,365 @@
+/* tbf_ul_fsm.c
+ *
+ * Copyright (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <talloc.h>
+
+#include <tbf_fsm.h>
+#include <gprs_rlcmac.h>
+#include <gprs_debug.h>
+#include <gprs_ms.h>
+#include <encoding.h>
+#include <bts.h>
+
+#include <bts_pch_timer.h>
+
+#define X(s) (1 << (s))
+
+static const struct osmo_tdef_state_timeout tbf_ul_fsm_timeouts[32] = {
+ [TBF_ST_NEW] = {},
+ [TBF_ST_ASSIGN] = {},
+ [TBF_ST_FLOW] = {},
+ [TBF_ST_FINISHED] = {},
+ [TBF_ST_RELEASING] = { .T = 3169 },
+};
+
+/* Transition to a state, using the T timer defined in tbf_fsm_timeouts.
+ * The actual timeout value is in turn obtained from conn->T_defs.
+ * Assumes local variable fi exists. */
+#define tbf_ul_fsm_state_chg(fi, NEXT_STATE) \
+ osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \
+ tbf_ul_fsm_timeouts, \
+ tbf_ms(((struct tbf_ul_fsm_ctx *)(fi->priv))->tbf)->bts->T_defs_bts, \
+ -1)
+
+static void mod_ass_type(struct tbf_ul_fsm_ctx *ctx, uint8_t t, bool set)
+{
+ const char *ch = "UNKNOWN";
+ bool prev_set = ctx->state_flags & (1 << t);
+
+ switch (t) {
+ case GPRS_RLCMAC_FLAG_CCCH:
+ ch = "CCCH";
+ break;
+ case GPRS_RLCMAC_FLAG_PACCH:
+ ch = "PACCH";
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ LOGPTBFUL(ctx->ul_tbf, LOGL_INFO, "%sset ass. type %s [prev CCCH:%u, PACCH:%u]\n",
+ set ? "" : "un", ch,
+ !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)),
+ !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)));
+
+ if (set && prev_set) {
+ LOGPTBFUL(ctx->ul_tbf, LOGL_ERROR,
+ "Attempted to set ass. type %s which is already set\n", ch);
+ return;
+ }
+
+ if (!set && !prev_set)
+ return;
+
+ if (set)
+ ctx->state_flags |= (1 << t);
+ else
+ ctx->state_flags &= ~(1 << t);
+}
+
+
+static void st_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_ul_fsm_ctx *ctx = (struct tbf_ul_fsm_ctx *)fi->priv;
+ switch (event) {
+ case TBF_EV_ASSIGN_ADD_CCCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
+ tbf_ul_fsm_state_chg(fi, TBF_ST_ASSIGN);
+ ul_tbf_contention_resolution_start(ctx->ul_tbf);
+ break;
+ case TBF_EV_ASSIGN_ADD_PACCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
+ tbf_ul_fsm_state_chg(fi, TBF_ST_ASSIGN);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_assign_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct tbf_ul_fsm_ctx *ctx = (struct tbf_ul_fsm_ctx *)fi->priv;
+ struct GprsMs *ms = tbf_ms(ctx->tbf);
+ unsigned long val;
+ unsigned int sec, micro;
+
+ /* If assignment for this TBF is happening on PACCH, that means the
+ * actual Assignment procedure (tx/rx) is happening on another TBF (eg
+ * Ul TBF vs DL TBF). Hence we add a security timer here to free it in
+ * case the other TBF doesn't succeed in informing (assigning) the MS
+ * about this TBF, or simply because the scheduler takes too long to
+ * schedule it. This timer can probably be dropped once we make the
+ * other TBF always signal us assignment failure (we already get
+ * assignment success through TBF_EV_ASSIGN_ACK_PACCH) */
+ if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)) {
+ fi->T = 3168;
+ val = osmo_tdef_get(ms->bts->T_defs_bts, fi->T, OSMO_TDEF_MS, -1);
+ val *= 4; /* 4 PKT RES REQ retransmit */
+ sec = val / 1000;
+ micro = (val % 1000) * 1000;
+ LOGPTBFUL(ctx->ul_tbf, LOGL_DEBUG,
+ "Starting timer T3168 [UL TBF Ass (PACCH)] with %u sec. %u microsec\n",
+ sec, micro);
+ osmo_timer_schedule(&fi->timer, sec, micro);
+ } else if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) {
+ /* Wait a bit for the AGCH ImmAss[PktUlAss] sent BSC->BTS to
+ * arrive at the MS, and for the MS to jump and starting
+ * listening on USFs in the assigned PDCH.
+ * Ideally we would first wait for TBF_EV_ASSIGN_PCUIF_CNF to
+ * account for queueing time, but that's only sent for data on PCH
+ * so far, while ImmAss for UL TBF is sent on AGCH.
+ */
+ fi->T = -2002;
+ val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
+ sec = val / 1000;
+ micro = (val % 1000) * 1000;
+ LOGPTBFUL(ctx->ul_tbf, LOGL_DEBUG,
+ "Starting timer X2002 [assignment (AGCH)] with %u sec. %u microsec\n",
+ sec, micro);
+ osmo_timer_schedule(&fi->timer, sec, micro);
+ }
+}
+
+static void st_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_ul_fsm_ctx *ctx = (struct tbf_ul_fsm_ctx *)fi->priv;
+
+ switch (event) {
+ case TBF_EV_ASSIGN_ADD_CCCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
+ break;
+ case TBF_EV_ASSIGN_ADD_PACCH:
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
+ break;
+ case TBF_EV_ASSIGN_ACK_PACCH:
+ tbf_assign_control_ts(ctx->tbf);
+ if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) {
+ /* We now know that the PACCH really existed */
+ LOGPTBFUL(ctx->ul_tbf, LOGL_INFO,
+ "The TBF has been confirmed on the PACCH, "
+ "changed type from CCCH to PACCH\n");
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
+ mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
+ }
+ tbf_ul_fsm_state_chg(fi, TBF_ST_FLOW);
+ break;
+ case TBF_EV_ASSIGN_READY_CCCH:
+ /* change state to FLOW, so scheduler will start requesting USF */
+ tbf_ul_fsm_state_chg(fi, TBF_ST_FLOW);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_flow(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_ul_fsm_ctx *ctx = (struct tbf_ul_fsm_ctx *)fi->priv;
+ struct GprsMs *ms = tbf_ms(ctx->tbf);
+ struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
+
+ switch (event) {
+ case TBF_EV_FIRST_UL_DATA_RECVD:
+ /* TS 44.060 7a.2.1.1: "The contention resolution is completed on
+ * the network side when the network receives an RLC data block that
+ * comprises the TLLI value that identifies the mobile station and the
+ * TFI value associated with the TBF." */
+ bts_pch_timer_stop(ms->bts, ms);
+ /* We may still have some DL-TBF waiting for assignment in PCH,
+ * which clearly won't happen since the MS is on PDCH now. Get rid
+ * of it, it will be re-assigned on PACCH when contention
+ * resolution at the MS side is done (1st UL ACK/NACK sent) */
+ if ((dl_tbf = ms_dl_tbf(ms))) {
+ /* Get rid of previous finished UL TBF before providing a new one */
+ LOGPTBFDL(dl_tbf, LOGL_NOTICE,
+ "Got first UL data while DL-TBF pending, killing it\n");
+ tbf_free(dl_tbf_as_tbf(dl_tbf));
+ dl_tbf = NULL;
+ }
+ break;
+ case TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS:
+ ul_tbf_contention_resolution_success(tbf_as_ul_tbf(ctx->tbf));
+ break;
+ case TBF_EV_LAST_UL_DATA_RECVD:
+ /* All data has been sent or received, change state to FINISHED */
+ tbf_ul_fsm_state_chg(fi, TBF_ST_FINISHED);
+ break;
+ case TBF_EV_MAX_N3101:
+ tbf_ul_fsm_state_chg(fi, TBF_ST_RELEASING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_finished(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct tbf_ul_fsm_ctx *ctx = (struct tbf_ul_fsm_ctx *)fi->priv;
+ struct GprsMs *ms;
+ bool new_ul_tbf_requested;
+
+ switch (event) {
+ case TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS:
+ /* UL TBF: If MS only sends 1 RLCMAC UL block, it can be that we
+ * end up in FINISHED state before sending the first UL ACK/NACK */
+ ul_tbf_contention_resolution_success(tbf_as_ul_tbf(ctx->tbf));
+ break;
+ case TBF_EV_FINAL_UL_ACK_CONFIRMED:
+ new_ul_tbf_requested = (bool)data;
+ /* Ref the MS, otherwise it may be freed after ul_tbf is
+ * detached when sending event below. */
+ ms = tbf_ms(ctx->tbf);
+ ms_ref(ms, __func__);
+ /* UL TBF ACKed our transmitted UL ACK/NACK with final Ack
+ * Indicator set to '1'. We can free the TBF right away, the MS
+ * also just released its TBF on its side. */
+ LOGPTBFUL(tbf_as_ul_tbf(ctx->tbf), LOGL_DEBUG, "[UPLINK] END\n");
+ tbf_free(ctx->tbf);
+ /* Here fi, ctx and ctx->tbf are already freed! */
+ /* TS 44.060 9.3.3.3.2: There might be LLC packets waiting in
+ * the queue but the DL TBF assignment might have been delayed
+ * because there was no way to reach the MS (because ul_tbf was
+ * in packet-active mode with FINISHED state). If MS is going
+ * back to packet-idle mode then we can assign the DL TBF on PCH
+ * now. */
+ if (!new_ul_tbf_requested && ms_need_dl_tbf(ms))
+ ms_new_dl_tbf_assigned_on_pch(ms);
+ ms_unref(ms, __func__);
+ break;
+ case TBF_EV_MAX_N3103:
+ tbf_ul_fsm_state_chg(fi, TBF_ST_RELEASING);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void st_releasing_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ /* T3169 has been set entering this state: Wait for reuse of USF and
+ * TFI(s) after the MS uplink assignment for this TBF is invalid. Upon
+ * timeout, the timer_cb does tbf_free().
+ */
+}
+
+static void st_releasing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static int tbf_ul_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct tbf_ul_fsm_ctx *ctx = (struct tbf_ul_fsm_ctx *)fi->priv;
+ switch (fi->T) {
+ case -2002:
+ osmo_fsm_inst_dispatch(fi, TBF_EV_ASSIGN_READY_CCCH, NULL);
+ break;
+ case 3168:
+ LOGPTBFUL(ctx->ul_tbf, LOGL_NOTICE, "Releasing due to UL TBF PACCH assignment timeout\n");
+ /* fall-through */
+ case 3169:
+ tbf_free(ctx->tbf);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+ return 0;
+}
+
+static struct osmo_fsm_state tbf_ul_fsm_states[] = {
+ [TBF_ST_NEW] = {
+ .in_event_mask =
+ X(TBF_EV_ASSIGN_ADD_CCCH) |
+ X(TBF_EV_ASSIGN_ADD_PACCH),
+ .out_state_mask =
+ X(TBF_ST_ASSIGN) |
+ X(TBF_ST_FLOW),
+ .name = "NEW",
+ .action = st_new,
+ },
+ [TBF_ST_ASSIGN] = {
+ .in_event_mask =
+ X(TBF_EV_ASSIGN_ADD_CCCH) |
+ X(TBF_EV_ASSIGN_ADD_PACCH) |
+ X(TBF_EV_ASSIGN_ACK_PACCH) |
+ X(TBF_EV_ASSIGN_READY_CCCH),
+ .out_state_mask =
+ X(TBF_ST_FLOW) |
+ X(TBF_ST_FINISHED),
+ .name = "ASSIGN",
+ .action = st_assign,
+ .onenter = st_assign_on_enter,
+ },
+ [TBF_ST_FLOW] = {
+ .in_event_mask =
+ X(TBF_EV_FIRST_UL_DATA_RECVD) |
+ X(TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS) |
+ X(TBF_EV_LAST_UL_DATA_RECVD) |
+ X(TBF_EV_MAX_N3101),
+ .out_state_mask =
+ X(TBF_ST_ASSIGN) |
+ X(TBF_ST_FINISHED) |
+ X(TBF_ST_RELEASING),
+ .name = "FLOW",
+ .action = st_flow,
+ },
+ [TBF_ST_FINISHED] = {
+ .in_event_mask =
+ X(TBF_EV_CONTENTION_RESOLUTION_MS_SUCCESS) |
+ X(TBF_EV_FINAL_UL_ACK_CONFIRMED) |
+ X(TBF_EV_MAX_N3103),
+ .out_state_mask =
+ X(TBF_ST_RELEASING),
+ .name = "FINISHED",
+ .action = st_finished,
+ },
+ [TBF_ST_RELEASING] = {
+ .in_event_mask = 0,
+ .out_state_mask = 0,
+ .name = "RELEASING",
+ .action = st_releasing,
+ .onenter = st_releasing_on_enter,
+ },
+};
+
+struct osmo_fsm tbf_ul_fsm = {
+ .name = "UL_TBF",
+ .states = tbf_ul_fsm_states,
+ .num_states = ARRAY_SIZE(tbf_ul_fsm_states),
+ .timer_cb = tbf_ul_fsm_timer_cb,
+ .log_subsys = DTBFUL,
+ .event_names = tbf_fsm_event_names,
+};
+
+static __attribute__((constructor)) void tbf_ul_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&tbf_ul_fsm) == 0);
+}
diff --git a/src/wireshark_compat.h b/src/wireshark_compat.h
index 13b165a0..2840124f 100644
--- a/src/wireshark_compat.h
+++ b/src/wireshark_compat.h
@@ -10,10 +10,6 @@
* 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* This header contains a few definitions required by rlcmac and csn1 files