aboutsummaryrefslogtreecommitdiffstats
path: root/src/bts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bts.cpp')
-rw-r--r--src/bts.cpp362
1 files changed, 244 insertions, 118 deletions
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;
+}