Move timer X2002 to tbf_fsm
Related: OS#2709
Change-Id: I94b71c60ed49d51ebdf6d6b428056b4b94354676
diff --git a/src/tbf_fsm.c b/src/tbf_fsm.c
index 0dbf04c..42c5118 100644
--- a/src/tbf_fsm.c
+++ b/src/tbf_fsm.c
@@ -46,6 +46,8 @@
{ TBF_EV_ASSIGN_DEL_CCCH, "ASSIGN_DEL_CCCH" },
{ 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_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" },
{ TBF_EV_FINAL_ACK_RECVD, "FINAL_ACK_RECVD" },
@@ -136,12 +138,17 @@
"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);
@@ -160,6 +167,23 @@
}
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);
@@ -172,7 +196,28 @@
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 dowlink 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 */
@@ -201,6 +246,8 @@
{
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
@@ -267,10 +314,42 @@
*/
}
+static void handle_timeout_X2002(struct tbf_fsm_ctx *ctx)
+{
+ struct gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(ctx->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 */
@@ -301,6 +380,7 @@
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),
.out_state_mask =
X(TBF_ST_FLOW) |
@@ -312,12 +392,14 @@
},
[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),
@@ -326,6 +408,7 @@
},
[TBF_ST_FINISHED] = {
.in_event_mask =
+ X(TBF_EV_DL_ACKNACK_MISS) |
X(TBF_EV_FINAL_ACK_RECVD) |
X(TBF_EV_MAX_N3103) |
X(TBF_EV_MAX_N3105),