aboutsummaryrefslogtreecommitdiffstats
path: root/src/pdch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdch.cpp')
-rw-r--r--src/pdch.cpp655
1 files changed, 422 insertions, 233 deletions
diff --git a/src/pdch.cpp b/src/pdch.cpp
index 0d9ebf2e..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(bts, tbf->ms(), tbf->trx->trx_no, tbf->tlli());
+ 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");
- TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS);
+ 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");
- TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS_REJ);
+ 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;
@@ -139,38 +164,46 @@ void pdch_init(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_trx *trx, uint8
/* Initialize the PTCCH/D message (Packet Timing Advance Control Channel) */
memset(pdch->ptcch_msg, PTCCH_TAI_FREE, PTCCH_TAI_NUM);
memset(pdch->ptcch_msg + PTCCH_TAI_NUM, PTCCH_PADDING, 7);
- pdch->ulc = pdch_ulc_alloc(pdch, trx->bts);
}
void gprs_rlcmac_pdch::enable()
{
- /* TODO: Check if there are still allocated resources.. */
+ 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);
+
+ OSMO_ASSERT(!this->ulc);
+ this->ulc = pdch_ulc_alloc(this, trx->bts);
+
m_is_enabled = 1;
+ bts_stat_item_inc(trx->bts, STAT_PDCH_AVAILABLE);
}
void gprs_rlcmac_pdch::disable()
{
- /* TODO.. kick free_resources once we know the TRX/TS we are on */
+ LOGPDCH(this, DRLCMAC, LOGL_INFO, "PDCH state: %s => disabled\n", m_is_enabled ? "enabled" : "disabled");
+ OSMO_ASSERT(m_is_enabled == 1);
+ this->free_resources();
+
m_is_enabled = 0;
+ bts_stat_item_dec(trx->bts, STAT_PDCH_AVAILABLE);
}
void gprs_rlcmac_pdch::free_resources()
{
struct gprs_rlcmac_paging *pag;
- /* we are not enabled. there should be no resources */
- if (!is_enabled())
- return;
-
/* 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);
talloc_free(this->ulc);
+ this->ulc = NULL;
}
struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
@@ -179,7 +212,7 @@ struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
if (llist_empty(&paging_list))
return NULL;
- pag = llist_entry(paging_list.next, struct gprs_rlcmac_paging, list);
+ pag = llist_first_entry(&paging_list, struct gprs_rlcmac_paging, list);
llist_del(&pag->list);
return pag;
@@ -304,111 +337,113 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
{
struct gprs_rlcmac_tbf *tbf, *new_tbf;
uint32_t tlli = packet->TLLI;
- GprsMs *ms = bts_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
+ GprsMs *ms;
gprs_rlcmac_ul_tbf *ul_tbf;
enum pdch_ulc_tbf_poll_reason reason;
struct pdch_ulc_node *poll;
poll = pdch_ulc_get_node(ulc, fn);
- if (!poll || poll->type !=PDCH_ULC_NODE_TBF_POLL || !poll->tbf_poll.poll_tbf) {
- 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);
+ if (!poll || poll->type != PDCH_ULC_NODE_TBF_POLL) {
+ 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);
-
- LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Control Ack\n");
- pdch_ulc_release_fn(ulc, fn);
-
- /* check if this control ack belongs to packet uplink ack */
- ul_tbf = as_ul_tbf(tbf);
- if (ul_tbf && ul_tbf->handle_ctrl_ack(reason)) {
- LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] END\n");
- if (ul_tbf->ctrl_ack_to_toggle())
- LOGPTBF(tbf, LOGL_NOTICE, "Recovered uplink ack for UL\n");
+ 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();
- tbf_free(tbf);
- return;
- }
- if (tbf->dl_ass_state_is(GPRS_RLCMAC_DL_ASS_WAIT_ACK)) {
- LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] DOWNLINK ASSIGNED\n");
- /* reset N3105 */
- tbf->n_reset(N3105);
- TBF_SET_ASS_STATE_DL(tbf, GPRS_RLCMAC_DL_ASS_NONE);
+ LOGPTBF(tbf, LOGL_DEBUG, "FN=%" PRIu32 " Rx Packet Control Ack (reason=%s)\n",
+ fn, get_value_string(pdch_ulc_tbf_poll_reason_names, reason));
- new_tbf = tbf->ms() ? ms_dl_tbf(tbf->ms()) : NULL;
- if (!new_tbf) {
- LOGPDCH(this, DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
- "TBF is gone TLLI=0x%08x\n", tlli);
+ switch (reason) {
+ case PDCH_ULC_POLL_UL_ACK:
+ 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",
+ fn, ts_no, bts_current_frame_number(tbf->bts));
return;
}
- if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE) &&
- tbf->direction == new_tbf->direction)
- tbf_free(tbf);
+ 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, 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;
- if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_CCCH)) {
- /* We now know that the PACCH really existed */
- LOGPTBF(new_tbf, LOGL_INFO,
- "The TBF has been confirmed on the PACCH, "
- "changed type from CCCH to PACCH\n");
- TBF_ASS_TYPE_SET(new_tbf, GPRS_RLCMAC_FLAG_PACCH);
+ case PDCH_ULC_POLL_UL_ASS:
+ if (!tbf->ul_ass_state_is(TBF_UL_ASS_WAIT_ACK)) {
+ LOGPTBF(tbf, LOGL_NOTICE, "FN=%d, TS=%d (curr FN %d): POLL_UL_ASS not expected! state is %s\n",
+ fn, ts_no, bts_current_frame_number(tbf->bts),
+ osmo_fsm_inst_state_name(tbf_ul_ass_fi(tbf)));
+ return;
}
- TBF_SET_STATE(new_tbf, GPRS_RLCMAC_FLOW);
- /* stop pending assignment timer */
- new_tbf->t_stop(T0, "control acked (DL-TBF)");
- if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_TO_DL_ASS))
- LOGPTBF(new_tbf, LOGL_NOTICE, "Recovered downlink assignment\n");
-
- tbf_assign_control_ts(new_tbf);
- return;
- }
- if (tbf->ul_ass_state_is(GPRS_RLCMAC_UL_ASS_WAIT_ACK)) {
LOGPTBF(tbf, LOGL_DEBUG, "[DOWNLINK] UPLINK ASSIGNED\n");
- /* reset N3105 */
- tbf->n_reset(N3105);
- TBF_SET_ASS_STATE_UL(tbf, GPRS_RLCMAC_UL_ASS_NONE);
+ pdch_ulc_release_fn(ulc, fn);
+ 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 = tbf->ms() ? ms_ul_tbf(tbf->ms()) : NULL;
+ new_tbf = ms_ul_tbf(ms);
if (!new_tbf) {
LOGPDCH(this, DRLCMAC, LOGL_ERROR, "Got ACK, but UL "
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
- if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE) &&
- tbf->direction == new_tbf->direction)
- tbf_free(tbf);
- TBF_SET_STATE(new_tbf, GPRS_RLCMAC_FLOW);
- if (new_tbf->check_n_clear(GPRS_RLCMAC_FLAG_TO_UL_ASS))
- LOGPTBF(new_tbf, LOGL_NOTICE, "Recovered uplink assignment for UL\n");
-
- tbf_assign_control_ts(new_tbf);
+ 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:
+ if (!tbf->dl_ass_state_is(TBF_DL_ASS_WAIT_ACK)) {
+ LOGPTBF(tbf, LOGL_NOTICE, "FN=%d, TS=%d (curr FN %d): POLL_DL_ASS not expected! state is %s\n",
+ fn, ts_no, bts_current_frame_number(tbf->bts),
+ osmo_fsm_inst_state_name(tbf_dl_ass_fi(tbf)));
+ return;
+ }
+ LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] DOWNLINK ASSIGNED\n");
+ pdch_ulc_release_fn(ulc, fn);
+ 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);
+ if (!new_tbf) {
+ LOGPDCH(this, DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
+ "TBF is gone TLLI=0x%08x\n", tlli);
+ return;
+ }
+ 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_fi, TBF_EV_ASSIGN_ACK_PACCH, NULL);
return;
- }
- if (ms->nacc && ms->nacc->fi->state == NACC_ST_WAIT_CELL_CHG_CONTINUE_ACK &&
- ms->nacc->continue_poll_fn == fn && ms->nacc->continue_poll_ts == ts_no) {
+
+ case PDCH_ULC_POLL_CELL_CHG_CONTINUE:
+ if (!ms->nacc || !nacc_fsm_exp_ctrl_ack(ms->nacc, fn, ts_no)) {
+ LOGPTBF(tbf, LOGL_NOTICE, "FN=%d, TS=%d (curr FN %d): POLL_CELL_CHG_CONTINUE not expected!\n",
+ fn, ts_no, bts_current_frame_number(tbf->bts));
+ return;
+ }
+ pdch_ulc_release_fn(ulc, fn);
osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_CONTINUE_ACK, NULL);
/* Don't assume MS is no longer reachable (hence don't free) after this: TS 44.060
* "When the mobile station receives the PACKET CELL CHANGE ORDER
@@ -419,9 +454,15 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
* switch to a new cell."
*/
return;
+
+ case PDCH_ULC_POLL_DL_ACK:
+ /* Handled in rcv_control_dl_ack_nack() upon receival of DL ACK/NACK as a response to our POLL. */
+ return;
+ default:
+ LOGPDCH(this, DRLCMAC, LOGL_ERROR, "FN=%" PRIu32 " "
+ "Error: received PACKET CONTROL ACK at no request (reason=%s)\n", fn,
+ get_value_string(pdch_ulc_tbf_poll_reason_names, reason));
}
- LOGPDCH(this, DRLCMAC, LOGL_ERROR,
- "Error: received PACET CONTROL ACK at no request\n");
}
void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn, struct pcu_l1_meas *meas)
@@ -438,24 +479,21 @@ void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_n
tfi = ack_nack->DOWNLINK_TFI;
poll = pdch_ulc_get_node(ulc, fn);
- if (!poll || poll->type != PDCH_ULC_NODE_TBF_POLL) {
+ if (!poll || poll->type != PDCH_ULC_NODE_TBF_POLL ||
+ poll->tbf_poll.reason != PDCH_ULC_POLL_DL_ACK) {
LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
fn, tfi, trx_no(), ts_no);
return;
}
- tbf = as_dl_tbf(poll->tbf_poll.poll_tbf);
+ OSMO_ASSERT(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);
-
- if (tbf->handle_ack_nack())
- LOGPTBF(tbf, LOGL_NOTICE, "Recovered downlink ack\n");
pdch_ulc_release_fn(ulc, fn);
LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Downlink Ack/Nack\n");
@@ -508,24 +546,21 @@ void gprs_rlcmac_pdch::rcv_control_egprs_dl_ack_nack(EGPRS_PD_AckNack_t *ack_nac
tfi = ack_nack->DOWNLINK_TFI;
poll = pdch_ulc_get_node(ulc, fn);
- if (!poll || poll->type !=PDCH_ULC_NODE_TBF_POLL) {
+ if (!poll || poll->type != PDCH_ULC_NODE_TBF_POLL ||
+ poll->tbf_poll.reason != PDCH_ULC_POLL_DL_ACK) {
LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
fn, tfi, trx_no(), ts_no);
return;
}
- tbf = as_dl_tbf(poll->tbf_poll.poll_tbf);
+ OSMO_ASSERT(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);
-
- if (tbf->handle_ack_nack())
- LOGPTBF(tbf, LOGL_NOTICE, "Recovered EGPRS downlink ack\n");
pdch_ulc_release_fn(ulc, fn);
LOGPTBF(tbf, LOGL_DEBUG,
@@ -591,111 +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)
{
+ 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;
- uint32_t tlli = request->ID.u.TLLI;
- bool ms_found = true;
- 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_found = false;
- 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);
}
- ul_tbf = ms_ul_tbf(ms); /* hence ul_tbf may be NULL */
+ } 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);
- LOGPDCH(this, DRLCMAC, LOGL_DEBUG, "MS requests UL TBF "
- "in packet resource request of single "
- "block, so we provide one:\n");
- sba = pdch_ulc_get_sba(this->ulc, fn);
- if (sba) {
- ms_set_ta(ms, sba->ta);
- sba_free(sba);
- } else if (!ul_tbf || !ul_tbf->state_is(GPRS_RLCMAC_FINISHED)) {
+ /* 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:
+ /* 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: "
+ "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;
+ }
+ 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,
- "MS requests UL TBF in PACKET RESOURCE REQ of "
- "single block, but there is no resource request "
- "scheduled!\n");
+ "Got PACKET RESOURCE REQ while TBF not finished, killing pending UL TBF\n");
+ tbf_free(ul_tbf);
+ ul_tbf = NULL;
}
- /* else: Resource Request can be received even if not scheduled
- by the network since it's used by MS to re-establish a new UL
- TBF when last one has finished. */
-
- 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);
+ /* 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 ((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;
}
-
- /* Get rid of previous finished UL TBF before providing a new one */
- if (ms_found && ul_tbf) {
- if (!ul_tbf->state_is(GPRS_RLCMAC_FINISHED))
- LOGPTBFUL(ul_tbf, LOGL_NOTICE,
- "Got PACKET RESOURCE REQ while TBF not finished, killing pending UL TBF\n");
- tbf_free(ul_tbf);
+ 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 = tbf_alloc_ul(bts(), ms, trx_no(), tlli);
- if (!ul_tbf) {
- handle_tbf_reject(bts(), ms, tlli,
- trx_no(), ts_no);
+ 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;
}
-
- /* set control ts to current MS's TS, until assignment complete */
- 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 */
- TBF_SET_ASS_STATE_UL(ul_tbf, GPRS_RLCMAC_UL_ASS_SEND_ASS);
-
- /* get measurements */
- if (ul_tbf->ms()) {
- get_meas(meas, request);
- ms_update_l1_meas(ul_tbf->ms(), meas);
+ 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;
}
-return_unref:
- ms_unref(ms);
- return;
+ /* 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);
+ }
- /* Reset N3101 counter: */
- ul_tbf->n_reset(N3101);
+ /* get measurements */
+ get_meas(meas, request);
+ ms_update_l1_meas(ms, meas);
+
+ 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)
@@ -703,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) {
@@ -730,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,
@@ -830,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;
@@ -916,15 +1047,18 @@ int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint8_t data_len, uint32_t f
if (node) {
switch (node->type) {
case PDCH_ULC_NODE_TBF_USF:
- if (tbf != node->tbf_usf.ul_tbf)
+ if (tbf == node->tbf_usf.ul_tbf)
+ pdch_ulc_release_node(ulc, node);
+ else
LOGPDCH(this, DRLCMACUL, LOGL_NOTICE, "FN=%" PRIu32 " "
"Rx UL DATA from unexpected %s vs expected %s\n",
fn, tbf_name(tbf), tbf_name(node->tbf_usf.ul_tbf));
break;
case PDCH_ULC_NODE_TBF_POLL:
LOGPDCH(this, DRLCMACUL, LOGL_NOTICE, "FN=%" PRIu32 " "
- "Rx UL DATA from unexpected %s vs expected POLL %s\n",
- fn, tbf_name(tbf), tbf_name(node->tbf_poll.poll_tbf));
+ "Rx UL DATA from unexpected %s vs expected POLL %s reason=%s\n",
+ fn, tbf_name(tbf), tbf_name(node->tbf_poll.poll_tbf),
+ get_value_string(pdch_ulc_tbf_poll_reason_names, node->tbf_poll.reason));
break;
case PDCH_ULC_NODE_SBA:
LOGPDCH(this, DRLCMACUL, LOGL_NOTICE, "FN=%" PRIu32 " "
@@ -932,7 +1066,6 @@ int gprs_rlcmac_pdch::rcv_data_block(uint8_t *data, uint8_t data_len, uint32_t f
fn, tbf_name(tbf));
break;
}
- pdch_ulc_release_node(ulc, node);
} else {
LOGPDCH(this, DRLCMACUL, LOGL_NOTICE, "FN=%" PRIu32 " "
"Rx UL DATA from unexpected %s\n", fn, tbf_name(tbf));
@@ -988,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) */
@@ -1007,14 +1140,34 @@ gprs_rlcmac_tbf *gprs_rlcmac_pdch::tbf_by_tfi(uint8_t tfi,
tbf = m_tbfs[dir][tfi];
- if (!tbf)
- return NULL;
-
- if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)) {
- return tbf;
- }
+ return tbf;
+}
- return NULL;
+void gprs_rlcmac_pdch::num_tbfs_update(gprs_rlcmac_tbf *tbf, bool is_attach)
+{
+ int threshold = is_attach ? 0 : 1;
+ int inc = is_attach ? 1 : -1;
+ uint8_t ul_dl_gprs = m_num_tbfs_gprs[GPRS_RLCMAC_UL_TBF] +
+ m_num_tbfs_gprs[GPRS_RLCMAC_DL_TBF];
+ uint8_t ul_dl_egprs = m_num_tbfs_egprs[GPRS_RLCMAC_UL_TBF] +
+ m_num_tbfs_egprs[GPRS_RLCMAC_DL_TBF];
+
+ /* Count PDCHs with at least one TBF as "occupied", as in
+ * 3GPP TS 52.402 § B.2.1.42-44. So if transitioning from 0 (threshold)
+ * TBFs in this PDCH to 1, increase the counter by 1 (inc). */
+ if (ul_dl_gprs + ul_dl_egprs == threshold)
+ bts_stat_item_add(trx->bts, STAT_PDCH_OCCUPIED, inc);
+
+ /* Update occupied GPRS/EGPRS stats (§ B.2.1.54-55) too */
+ if (tbf->is_egprs_enabled() && ul_dl_egprs == threshold)
+ bts_stat_item_add(trx->bts, STAT_PDCH_OCCUPIED_EGPRS, inc);
+ else if (!tbf->is_egprs_enabled() && ul_dl_gprs == threshold)
+ bts_stat_item_add(trx->bts, STAT_PDCH_OCCUPIED_GPRS, inc);
+
+ if (tbf->is_egprs_enabled())
+ m_num_tbfs_egprs[tbf->direction] += inc;
+ else
+ m_num_tbfs_gprs[tbf->direction] += inc;
}
void gprs_rlcmac_pdch::attach_tbf(gprs_rlcmac_tbf *tbf)
@@ -1026,9 +1179,9 @@ void gprs_rlcmac_pdch::attach_tbf(gprs_rlcmac_tbf *tbf)
"%s has not been detached, overwriting it\n",
m_tbfs[tbf->direction][tbf->tfi()]->name());
- m_num_tbfs[tbf->direction] += 1;
+ 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();
@@ -1036,7 +1189,7 @@ void gprs_rlcmac_pdch::attach_tbf(gprs_rlcmac_tbf *tbf)
LOGPDCH(this, DRLCMAC, LOGL_INFO, "Attaching %s, %d TBFs, "
"USFs = %02x, TFIs = %08x.\n",
- tbf->name(), m_num_tbfs[tbf->direction],
+ tbf->name(), num_tbfs(tbf->direction),
m_assigned_usf, m_assigned_tfi[tbf->direction]);
}
@@ -1044,38 +1197,32 @@ void gprs_rlcmac_pdch::detach_tbf(gprs_rlcmac_tbf *tbf)
{
gprs_rlcmac_ul_tbf *ul_tbf;
- OSMO_ASSERT(m_num_tbfs[tbf->direction] > 0);
+ 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 {
+ OSMO_ASSERT(m_num_tbfs_gprs[tbf->direction] > 0);
+ }
- m_num_tbfs[tbf->direction] -= 1;
+ 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(), m_num_tbfs[tbf->direction],
- m_assigned_usf, m_assigned_tfi[tbf->direction]);
}
bool gprs_rlcmac_pdch::has_gprs_only_tbf_attached() const
{
- unsigned int i;
- unsigned int j;
- for (i = 0; i < sizeof(m_assigned_tfi[0]); i++) {
- for (j = 0; j < 2; j++) {
- if (m_assigned_tfi[j] & (1UL << i)) {
- gprs_rlcmac_tbf *tbf = m_tbfs[j][i];
- if (!tbf->is_egprs_enabled())
- return true;
- }
- }
- }
- return false;
+ return (m_num_tbfs_gprs[GPRS_RLCMAC_UL_TBF] +
+ m_num_tbfs_gprs[GPRS_RLCMAC_DL_TBF]) > 0;
}
void gprs_rlcmac_pdch::reserve(enum gprs_rlcmac_tbf_direction dir)
@@ -1128,6 +1275,9 @@ void gprs_rlcmac_pdch::update_ta(uint8_t tai, uint8_t ta)
void pdch_free_all_tbf(struct gprs_rlcmac_pdch *pdch)
{
+ struct llist_item *pos;
+ struct llist_item *pos2;
+
for (uint8_t tfi = 0; tfi < 32; tfi++) {
struct gprs_rlcmac_tbf *tbf;
@@ -1138,8 +1288,47 @@ void pdch_free_all_tbf(struct gprs_rlcmac_pdch *pdch)
if (tbf)
tbf_free(tbf);
}
+
+ /* Some temporary dummy TBFs to tx ImmAssRej may be left linked to the
+ * 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 = tbf_as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
+ if (ul_tbf->control_ts == pdch)
+ tbf_free(ul_tbf);
+ }
}
-void pdch_disable(struct gprs_rlcmac_pdch *pdch) {
+void pdch_disable(struct gprs_rlcmac_pdch *pdch)
+{
pdch->disable();
}
+
+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;
+}