blob: 0dbf04c80d33ccc33219f6d420baa0a309d96e0b [file] [log] [blame]
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +02001/* tbf_fsm.c
2 *
3 * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
4 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <unistd.h>
22
23#include <talloc.h>
24
25#include <tbf_fsm.h>
26#include <gprs_rlcmac.h>
27#include <gprs_debug.h>
28#include <gprs_ms.h>
29#include <encoding.h>
30#include <bts.h>
31
32#define X(s) (1 << (s))
33
34const struct osmo_tdef_state_timeout tbf_fsm_timeouts[32] = {
35 [TBF_ST_NULL] = {},
36 [TBF_ST_ASSIGN] = { },
37 [TBF_ST_FLOW] = { },
38 [TBF_ST_FINISHED] = {},
39 [TBF_ST_WAIT_RELEASE] = {},
40 [TBF_ST_RELEASING] = {},
41};
42
43const struct value_string tbf_fsm_event_names[] = {
Pau Espin Pedrol33e80072021-07-22 19:20:50 +020044 { TBF_EV_ASSIGN_ADD_CCCH, "ASSIGN_ADD_CCCH" },
45 { TBF_EV_ASSIGN_ADD_PACCH, "ASSIGN_ADD_PACCH" },
46 { TBF_EV_ASSIGN_DEL_CCCH, "ASSIGN_DEL_CCCH" },
Pau Espin Pedrol720e19e2021-07-22 19:56:37 +020047 { TBF_EV_ASSIGN_ACK_PACCH, "ASSIGN_ACK_PACCH" },
48 { TBF_EV_ASSIGN_READY_CCCH, "ASSIGN_READY_CCCH" },
Pau Espin Pedrolc32c4a32021-07-23 18:27:57 +020049 { TBF_EV_LAST_DL_DATA_SENT, "LAST_DL_DATA_SENT" },
50 { TBF_EV_LAST_UL_DATA_RECVD, "LAST_UL_DATA_RECVD" },
Pau Espin Pedrolefcb0462021-07-26 12:33:39 +020051 { TBF_EV_FINAL_ACK_RECVD, "FINAL_ACK_RECVD" },
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +020052 { TBF_EV_MAX_N3101 , "MAX_N3101" },
53 { TBF_EV_MAX_N3103 , "MAX_N3103" },
54 { TBF_EV_MAX_N3105 , "MAX_N3105" },
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +020055 { 0, NULL }
56};
57
Pau Espin Pedrol33e80072021-07-22 19:20:50 +020058static void mod_ass_type(struct tbf_fsm_ctx *ctx, uint8_t t, bool set)
59{
60 const char *ch = "UNKNOWN";
61 bool prev_set = ctx->state_flags & (1 << t);
62
63 switch (t) {
64 case GPRS_RLCMAC_FLAG_CCCH:
65 ch = "CCCH";
66 break;
67 case GPRS_RLCMAC_FLAG_PACCH:
68 ch = "PACCH";
69 break;
70 default:
71 LOGPTBF(ctx->tbf, LOGL_ERROR,
72 "attempted to %sset unexpected ass. type %d - FIXME!\n",
73 set ? "" : "un", t);
74 return;
75 }
76
77 if (set && prev_set) {
78 LOGPTBF(ctx->tbf, LOGL_ERROR,
79 "attempted to set ass. type %s which is already set.\n", ch);
80 } else if (!set && !prev_set) {
81 return;
82 }
83
84 LOGPTBF(ctx->tbf, LOGL_INFO, "%sset ass. type %s [prev CCCH:%u, PACCH:%u]\n",
85 set ? "" : "un", ch,
86 !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)),
87 !!(ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)));
88
89 if (set) {
90 ctx->state_flags |= (1 << t);
91 } else {
92 ctx->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep to flags */
93 ctx->state_flags &= ~(1 << t);
94 }
95}
96
97
98static void st_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
99{
100 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
101 switch (event) {
102 case TBF_EV_ASSIGN_ADD_CCCH:
103 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
104 tbf_fsm_state_chg(fi, tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF ?
105 TBF_ST_ASSIGN : TBF_ST_FLOW);
106 break;
107 case TBF_EV_ASSIGN_ADD_PACCH:
108 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
109 tbf_fsm_state_chg(fi, TBF_ST_ASSIGN);
110 break;
111 default:
112 OSMO_ASSERT(0);
113 }
114}
115
Pau Espin Pedrol9d67e722021-07-28 19:25:38 +0200116static void st_assign_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
117{
118 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
119 unsigned long val;
120 unsigned int sec, micro;
121
122 /* If assignment for this TBF is happening on PACCH, that means the
123 * actual Assignment procedure (tx/rx) is happening on another TBF (eg
124 * Ul TBF vs DL TBF). Hence we add a security timer here to free it in
125 * case the other TBF doesn't succeed in informing (assigning) the MS
126 * about this TBF, or simply because the scheduler takes too long to
127 * schedule it. This timer can probably be dropped once we make the
128 * other TBF always signal us assignment failure (we already get
129 * assignment success through TBF_EV_ASSIGN_ACK_PACCH) */
130 if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH)) {
131 fi->T = -2001;
132 val = osmo_tdef_get(the_pcu->T_defs, fi->T, OSMO_TDEF_MS, -1);
133 sec = val / 1000;
134 micro = (val % 1000) * 1000;
135 LOGPTBF(ctx->tbf, LOGL_DEBUG,
136 "Starting timer X2001 [assignment (PACCH)] with %u sec. %u microsec\n",
137 sec, micro);
138 osmo_timer_schedule(&fi->timer, sec, micro);
139 }
140}
141
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200142static void st_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
143{
144 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
145 switch (event) {
146 case TBF_EV_ASSIGN_ADD_CCCH:
147 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, true);
148 break;
149 case TBF_EV_ASSIGN_ADD_PACCH:
150 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
151 break;
Pau Espin Pedrol720e19e2021-07-22 19:56:37 +0200152 case TBF_EV_ASSIGN_ACK_PACCH:
153 if (ctx->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH)) {
154 /* We now know that the PACCH really existed */
155 LOGPTBF(ctx->tbf, LOGL_INFO,
156 "The TBF has been confirmed on the PACCH, "
157 "changed type from CCCH to PACCH\n");
158 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
159 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_PACCH, true);
160 }
161 tbf_fsm_state_chg(fi, TBF_ST_FLOW);
162 break;
163 case TBF_EV_ASSIGN_READY_CCCH:
164 /* change state to FLOW, so scheduler will start transmission */
165 tbf_fsm_state_chg(fi, TBF_ST_FLOW);
166 break;
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200167 default:
168 OSMO_ASSERT(0);
169 }
170}
171
Pau Espin Pedrolc32c4a32021-07-23 18:27:57 +0200172static void st_flow(struct osmo_fsm_inst *fi, uint32_t event, void *data)
173{
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200174 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
Pau Espin Pedrolc32c4a32021-07-23 18:27:57 +0200175 switch (event) {
176 case TBF_EV_LAST_DL_DATA_SENT:
177 case TBF_EV_LAST_UL_DATA_RECVD:
178 /* All data has been sent or received, change state to FINISHED */
179 tbf_fsm_state_chg(fi, TBF_ST_FINISHED);
180 break;
Pau Espin Pedrolefcb0462021-07-26 12:33:39 +0200181 case TBF_EV_FINAL_ACK_RECVD:
182 /* We received Final Ack (DL ACK/NACK) from MS. move to
183 WAIT_RELEASE, we wait there for release or re-use the TBF in
184 case we receive more DL data to tx */
185 tbf_fsm_state_chg(fi, TBF_ST_WAIT_RELEASE);
186 break;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200187 case TBF_EV_MAX_N3101:
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200188 ctx->T_release = 3169;
189 tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
190 break;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200191 case TBF_EV_MAX_N3105:
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200192 ctx->T_release = 3195;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200193 tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
194 break;
Pau Espin Pedrolefcb0462021-07-26 12:33:39 +0200195 default:
196 OSMO_ASSERT(0);
197 }
198}
199
200static void st_finished(struct osmo_fsm_inst *fi, uint32_t event, void *data)
201{
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200202 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
Pau Espin Pedrolefcb0462021-07-26 12:33:39 +0200203 switch (event) {
204 case TBF_EV_FINAL_ACK_RECVD:
205 /* We received Final Ack (DL ACK/NACK) from MS. move to
206 WAIT_RELEASE, we wait there for release or re-use the TBF in
207 case we receive more DL data to tx */
208 tbf_fsm_state_chg(fi, TBF_ST_WAIT_RELEASE);
209 break;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200210 case TBF_EV_MAX_N3103:
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200211 ctx->T_release = 3169;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200212 tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
213 break;
214 case TBF_EV_MAX_N3105:
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200215 ctx->T_release = 3195;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200216 tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
217 break;
218 default:
219 OSMO_ASSERT(0);
220 }
221}
222
223static void st_wait_release(struct osmo_fsm_inst *fi, uint32_t event, void *data)
224{
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200225 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200226 switch (event) {
227 case TBF_EV_FINAL_ACK_RECVD:
228 /* ignore, duplicate ACK, we already know about since we are in WAIT_RELEASE */
229 break;
230 case TBF_EV_MAX_N3101:
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200231 ctx->T_release = 3169;
232 tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
233 break;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200234 case TBF_EV_MAX_N3105:
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200235 ctx->T_release = 3195;
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200236 tbf_fsm_state_chg(fi, TBF_ST_RELEASING);
237 break;
Pau Espin Pedrolc32c4a32021-07-23 18:27:57 +0200238 default:
239 OSMO_ASSERT(0);
240 }
241}
242
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200243static void st_releasing_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
244{
245 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
246 unsigned long val;
247
248 if (!ctx->T_release)
249 return;
250
251 /* In general we should end up here with an assigned timer in ctx->T_release. Possible values are:
252 * T3195: Wait for reuse of TFI(s) when there is no response from the MS
253 * (radio failure or cell change) for this TBF/MBMS radio bearer.
254 * T3169: Wait for reuse of USF and TFI(s) after the MS uplink assignment for this TBF is invalid.
255 */
256 val = osmo_tdef_get(tbf_ms(ctx->tbf)->bts->T_defs_bts, ctx->T_release, OSMO_TDEF_S, -1);
257 fi->T = ctx->T_release;
258 LOGPTBF(ctx->tbf, LOGL_DEBUG, "starting timer T%u with %lu sec. %u microsec\n",
259 ctx->T_release, val, 0);
260 osmo_timer_schedule(&fi->timer, val, 0);
261}
262
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200263static void tbf_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
264{
265 /* TODO: needed ?
266 * struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
267 */
268}
269
270static int tbf_fsm_timer_cb(struct osmo_fsm_inst *fi)
271{
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200272 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200273 switch (fi->T) {
Pau Espin Pedrol9d67e722021-07-28 19:25:38 +0200274 case -2001:
275 LOGPTBF(ctx->tbf, LOGL_NOTICE, "releasing due to PACCH assignment timeout.\n");
276 /* fall-through */
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200277 case 3169:
278 case 3195:
279 tbf_free(ctx->tbf);
280 break;
281 default:
282 OSMO_ASSERT(0);
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200283 }
284 return 0;
285}
286
287static struct osmo_fsm_state tbf_fsm_states[] = {
288 [TBF_ST_NULL] = {
289 .in_event_mask =
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200290 X(TBF_EV_ASSIGN_ADD_CCCH) |
291 X(TBF_EV_ASSIGN_ADD_PACCH),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200292 .out_state_mask =
293 X(TBF_ST_ASSIGN) |
294 X(TBF_ST_FLOW) |
295 X(TBF_ST_RELEASING),
296 .name = "NULL",
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200297 .action = st_null,
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200298 },
299 [TBF_ST_ASSIGN] = {
300 .in_event_mask =
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200301 X(TBF_EV_ASSIGN_ADD_CCCH) |
Pau Espin Pedrol720e19e2021-07-22 19:56:37 +0200302 X(TBF_EV_ASSIGN_ADD_PACCH) |
303 X(TBF_EV_ASSIGN_ACK_PACCH) |
304 X(TBF_EV_ASSIGN_READY_CCCH),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200305 .out_state_mask =
306 X(TBF_ST_FLOW) |
307 X(TBF_ST_FINISHED) |
308 X(TBF_ST_RELEASING),
309 .name = "ASSIGN",
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200310 .action = st_assign,
Pau Espin Pedrol9d67e722021-07-28 19:25:38 +0200311 .onenter = st_assign_on_enter,
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200312 },
313 [TBF_ST_FLOW] = {
314 .in_event_mask =
Pau Espin Pedrolc32c4a32021-07-23 18:27:57 +0200315 X(TBF_EV_LAST_DL_DATA_SENT) |
Pau Espin Pedrolefcb0462021-07-26 12:33:39 +0200316 X(TBF_EV_LAST_UL_DATA_RECVD) |
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200317 X(TBF_EV_FINAL_ACK_RECVD) |
318 X(TBF_EV_MAX_N3101) |
319 X(TBF_EV_MAX_N3105),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200320 .out_state_mask =
321 X(TBF_ST_FINISHED) |
322 X(TBF_ST_WAIT_RELEASE) |
323 X(TBF_ST_RELEASING),
324 .name = "FLOW",
Pau Espin Pedrolc32c4a32021-07-23 18:27:57 +0200325 .action = st_flow,
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200326 },
327 [TBF_ST_FINISHED] = {
328 .in_event_mask =
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200329 X(TBF_EV_FINAL_ACK_RECVD) |
330 X(TBF_EV_MAX_N3103) |
331 X(TBF_EV_MAX_N3105),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200332 .out_state_mask =
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200333 X(TBF_ST_WAIT_RELEASE) |
334 X(TBF_ST_RELEASING),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200335 .name = "FINISHED",
Pau Espin Pedrolefcb0462021-07-26 12:33:39 +0200336 .action = st_finished,
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200337 },
338 [TBF_ST_WAIT_RELEASE] = {
339 .in_event_mask =
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200340 X(TBF_EV_FINAL_ACK_RECVD) |
341 X(TBF_EV_MAX_N3101) |
342 X(TBF_EV_MAX_N3105),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200343 .out_state_mask =
344 X(TBF_ST_RELEASING),
345 .name = "WAIT_RELEASE",
Pau Espin Pedrol55f600b2021-07-26 15:54:39 +0200346 .action = st_wait_release,
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200347 },
348 [TBF_ST_RELEASING] = {
349 .in_event_mask =
350 0,
351 .out_state_mask =
352 0,
353 .name = "RELEASING",
Pau Espin Pedrolcfb61d92021-07-26 17:17:02 +0200354 .onenter = st_releasing_on_enter,
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200355 },
356};
357
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200358void tbf_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
359{
360 struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
361 switch (event) {
362 case TBF_EV_ASSIGN_DEL_CCCH:
363 mod_ass_type(ctx, GPRS_RLCMAC_FLAG_CCCH, false);
364 break;
365 default:
366 OSMO_ASSERT(0);
367 }
368}
369
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200370struct osmo_fsm tbf_fsm = {
371 .name = "TBF",
372 .states = tbf_fsm_states,
373 .num_states = ARRAY_SIZE(tbf_fsm_states),
374 .timer_cb = tbf_fsm_timer_cb,
375 .cleanup = tbf_fsm_cleanup,
376 .log_subsys = DTBF,
377 .event_names = tbf_fsm_event_names,
Pau Espin Pedrol33e80072021-07-22 19:20:50 +0200378 .allstate_action = tbf_fsm_allstate_action,
379 .allstate_event_mask = X(TBF_EV_ASSIGN_DEL_CCCH),
Pau Espin Pedroldc2aaac2021-05-14 12:50:46 +0200380};
381
382static __attribute__((constructor)) void tbf_fsm_init(void)
383{
384 OSMO_ASSERT(osmo_fsm_register(&tbf_fsm) == 0);
385}