diff options
Diffstat (limited to 'src/bts.cpp')
-rw-r--r-- | src/bts.cpp | 362 |
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; +} |