blob: f65ffb863cf88c192ee3e1f4f20ba0f321b93725 [file] [log] [blame]
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001/* gprs_ms.c
2 *
3 * Copyright (C) 2015-2020 by Sysmocom s.f.m.c. GmbH
4 * Author: Jacob Erlbeck <jerlbeck@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.
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010015 */
16
17
18#include "gprs_ms.h"
19#include "bts.h"
20#include "tbf.h"
21#include "tbf_ul.h"
22#include "gprs_debug.h"
23#include "gprs_codel.h"
24#include "pcu_utils.h"
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010025#include "nacc_fsm.h"
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +020026#include "tbf_ul_ack_fsm.h"
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010027
28#include <time.h>
29
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32#include <osmocom/core/timer.h>
33#include <osmocom/gsm/protocol/gsm_04_08.h>
34#include <osmocom/gsm/gsm48.h>
35#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010036#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010037#include "coding_scheme.h"
38
39#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
40
41extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010042static unsigned int next_ms_ctr_group_id;
43
44static const struct rate_ctr_desc ms_ctr_description[] = {
45 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
46};
47
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010048static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010049 .group_name_prefix = "pcu:ms",
50 .group_description = "MS Statistics",
51 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
52 .num_ctr = ARRAY_SIZE(ms_ctr_description),
53 .ctr_desc = ms_ctr_description,
54};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010055
56static int64_t now_msec()
57{
58 struct timespec ts;
59 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
60
61 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
62}
63
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020064static void ms_update_status(struct GprsMs *ms);
65
66static int ms_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
67{
68 struct GprsMs *ms = e->use_count->talloc_object;
69 int32_t total;
70 int level;
71 char buf[1024];
72
73 if (!e->use)
74 return -EINVAL;
75
76 total = osmo_use_count_total(&ms->use_count);
77
78 if (total == 0
79 || (total == 1 && old_use_count == 0 && e->count == 1))
80 level = LOGL_INFO;
81 else
82 level = LOGL_DEBUG;
83
84
85 LOGPSRC(DRLCMAC, level, file, line, "%s: %s %s: now used by %s\n",
86 ms_name(ms),
87 (e->count - old_use_count) > 0 ? "+" : "-", e->use,
88 (osmo_use_count_to_str_buf(buf, sizeof(buf), &ms->use_count), buf));
89
90 if (e->count < 0)
91 return -ERANGE;
92
93 if (total == 0)
94 ms_update_status(ms);
95 return 0;
96}
97
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010098void gprs_default_cb_ms_idle(struct GprsMs *ms)
99{
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200100 if (ms_is_idle(ms))
101 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100102}
103
104void gprs_default_cb_ms_active(struct GprsMs *ms)
105{
106 /* do nothing */
107}
108
109static struct gpr_ms_callback gprs_default_cb = {
110 .ms_idle = gprs_default_cb_ms_idle,
111 .ms_active = gprs_default_cb_ms_active,
112};
113
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100114static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100115{
116 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100117 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100118
119 if (ms->timer.data) {
120 ms->timer.data = NULL;
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200121 ms_unref(ms, MS_USE_RELEASE_TIMER);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100122 }
123}
124
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200125static void ms_llc_timer_cb(void *_ms)
126{
127 struct GprsMs *ms = _ms;
128 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
129
130 if (!dl_tbf)
131 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200132 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200133 return;
134
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200135 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200136
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200137 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200138}
139
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100140static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200141struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142{
143 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200144 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100145
146 talloc_set_destructor(ms, ms_talloc_destructor);
147
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200148 llist_add(&ms->list, &bts->ms_list);
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200149 bts_stat_item_inc(bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200150
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100151 ms->bts = bts;
152 ms->cb = gprs_default_cb;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200153 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100154 ms->new_ul_tlli = GSM_RESERVED_TMSI;
155 ms->new_dl_tlli = GSM_RESERVED_TMSI;
156 ms->ta = GSM48_TA_INVALID;
157 ms->current_cs_ul = UNKNOWN;
158 ms->current_cs_dl = UNKNOWN;
159 ms->is_idle = true;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100160 INIT_LLIST_HEAD(&ms->old_tbfs);
161
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200162 ms->use_count = (struct osmo_use_count){
163 .talloc_object = ms,
164 .use_cb = ms_use_cb,
165 };
166
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100167 int codel_interval = LLC_CODEL_USE_DEFAULT;
168
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200169 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100170
171 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200172 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200173 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200174 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
175 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100176
177 ms_set_mode(ms, GPRS);
178
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200179 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200180 if (codel_interval == LLC_CODEL_USE_DEFAULT)
181 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
182 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100183
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100184 ms->last_cs_not_low = now_msec();
185 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100186
187 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
188 if (!ms->ctrs)
189 goto free_ret;
190
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200191 ms_set_timeout(ms, osmo_tdef_get(bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1));
192
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100193 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100194free_ret:
195 talloc_free(ms);
196 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100197}
198
199static int ms_talloc_destructor(struct GprsMs *ms)
200{
201 struct llist_item *pos, *tmp;
202
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100203 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100204
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200205 bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200206 llist_del(&ms->list);
207
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100208 ms_set_reserved_slots(ms, NULL, 0, 0);
209
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700210 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100211
212 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200213 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100214 ms->ul_tbf = NULL;
215 }
216
217 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200218 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100219 ms->dl_tbf = NULL;
220 }
221
222 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
223 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
224 tbf_set_ms(tbf, NULL);
225 }
226
227 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200228 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100229
230 if (ms->ctrs)
231 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100232 return 0;
233}
234
235
236void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
237{
238 if (cb)
239 ms->cb = *cb;
240 else
241 ms->cb = gprs_default_cb;
242}
243
244static void ms_update_status(struct GprsMs *ms)
245{
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200246 if (osmo_use_count_total(&ms->use_count) > 0)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100247 return;
248
249 if (ms_is_idle(ms) && !ms->is_idle) {
250 ms->is_idle = true;
251 ms->cb.ms_idle(ms);
252 /* this can be deleted by now, do not access it */
253 return;
254 }
255
256 if (!ms_is_idle(ms) && ms->is_idle) {
257 ms->is_idle = false;
258 ms->cb.ms_active(ms);
259 }
260}
261
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100262static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100263{
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200264 /* Immediate free():
265 * Skip delaying free() through release timer if delay is configured to be 0.
266 * This is useful for synced freed during unit tests.
267 */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100268 if (ms->delay == 0)
269 return;
270
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200271 /* Immediate free():
272 * Skip delaying free() through release timer if TMSI is not
273 * known, since those cannot really be reused.
274 */
275 if (ms_tlli(ms) == GSM_RESERVED_TMSI)
276 return;
277
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100278 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
279
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200280 if (!ms->timer.data) {
281 ms_ref(ms, MS_USE_RELEASE_TIMER);
282 ms->timer.data = ms;
283 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100284
285 osmo_timer_schedule(&ms->timer, ms->delay, 0);
286}
287
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100288static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100289{
290 if (!ms->timer.data)
291 return;
292
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100293 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
294
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100295 osmo_timer_del(&ms->timer);
296 ms->timer.data = NULL;
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200297 ms_unref(ms, MS_USE_RELEASE_TIMER);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100298}
299
300void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
301{
302 ms->mode = mode;
303
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100304 switch (ms->mode) {
305 case GPRS:
306 if (!mcs_is_gprs(ms->current_cs_ul)) {
307 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100308 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100309 if (!mcs_is_valid(ms->current_cs_ul))
310 ms->current_cs_ul = CS1;
311 }
312 if (!mcs_is_gprs(ms->current_cs_dl)) {
313 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100314 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100315 if (!mcs_is_valid(ms->current_cs_dl))
316 ms->current_cs_dl = CS1;
317 }
318 break;
319
320 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100321 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
322 ms->current_cs_ul = mcs_get_egprs_by_num(
323 ms->bts->initial_mcs_ul);
324 if (!mcs_is_valid(ms->current_cs_ul))
325 ms->current_cs_ul = MCS1;
326 }
327 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
328 ms->current_cs_dl = mcs_get_egprs_by_num(
329 ms->bts->initial_mcs_dl);
330 if (!mcs_is_valid(ms->current_cs_dl))
331 ms->current_cs_dl = MCS1;
332 }
333 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100334 case EGPRS:
335 if (!mcs_is_edge(ms->current_cs_ul)) {
336 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100337 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100338 if (!mcs_is_valid(ms->current_cs_ul))
339 ms->current_cs_ul = MCS1;
340 }
341 if (!mcs_is_edge(ms->current_cs_dl)) {
342 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100343 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100344 if (!mcs_is_valid(ms->current_cs_dl))
345 ms->current_cs_dl = MCS1;
346 }
347 break;
348 }
349}
350
351static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
352{
353 if (ms->ul_tbf == tbf)
354 return;
355
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100356 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100357
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200358 ms_ref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100359
360 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200361 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100362
363 ms->ul_tbf = tbf;
364
365 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100366 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100367
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200368 ms_unref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100369}
370
371static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
372{
373 if (ms->dl_tbf == tbf)
374 return;
375
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100376 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100377
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200378 ms_ref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100379
380 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200381 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100382
383 ms->dl_tbf = tbf;
384
385 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100386 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100387
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200388 ms_unref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100389}
390
391void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
392{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200393 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200394 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200395 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200396 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100397}
398
399void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
400{
401 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
402 ms->ul_tbf = NULL;
403 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
404 ms->dl_tbf = NULL;
405 } else {
406 bool found = false;
407
408 struct llist_item *pos, *tmp;
409 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
410 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
411 if (tmp_tbf == tbf) {
412 llist_del(&pos->list);
413 found = true;
414 break;
415 }
416 }
417
418 /* Protect against recursive calls via set_ms() */
419 if (!found)
420 return;
421 }
422
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100423 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
424 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100425
426 if (tbf_ms(tbf) == ms)
427 tbf_set_ms(tbf, NULL);
428
429 if (!ms->dl_tbf && !ms->ul_tbf) {
430 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100431 ms->first_common_ts = NULL;
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200432 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100433 }
434
435 ms_update_status(ms);
436}
437
438void ms_reset(struct GprsMs *ms)
439{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100440 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100441
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100442 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100443
444 ms->tlli = GSM_RESERVED_TMSI;
445 ms->new_dl_tlli = ms->tlli;
446 ms->new_ul_tlli = ms->tlli;
447 ms->imsi[0] = '\0';
448}
449
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200450/* This function should be called on the MS object of a TBF each time an RLCMAC
451 * block is received for it with TLLI information.
452 * Besides updating the TLLI field on the MS object, it also seeks for other MS
453 * objects in the store and merges them into the current MS object. The MS
454 * duplication happened because we don't learn the TLLI of the created TBF until
455 * a later point. */
456void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
457{
458 struct GprsMs *old_ms = NULL;
459
460 if (tlli == GSM_RESERVED_TMSI)
461 return;
462
463 /* When the TLLI does not match the ms, check if there is another
464 * MS object that belongs to that TLLI and if yes make sure one of them
465 * gets deleted. */
466 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200467 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200468
469 ms_set_tlli(ms, tlli);
470
471 if (old_ms)
472 ms_merge_and_clear_ms(ms, old_ms);
473 /* old_ms may no longer be available here */
474}
475
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100476/* Merge 'old_ms' object into 'ms' object.
477 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
478void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100479{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100480 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100481 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200482 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100483
484 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
485
486 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100487
488 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
489 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
490
491 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
492 ms_set_ms_class(ms, ms_ms_class(old_ms));
493
494 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
495 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
496
497 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
498
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100499 /* Clean up the old MS object */
500 /* TODO: Use timer? */
501 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
502 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
503 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
504 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
505
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100506 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100507
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200508 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100509}
510
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100511/* Apply changes to the TLLI directly, used interally by functions below: */
512static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
513{
514 ms->tlli = tlli;
515 ms->new_dl_tlli = GSM_RESERVED_TMSI;
516 ms->new_ul_tlli = GSM_RESERVED_TMSI;
517
518 /* Update TBF FSM names: */
519 if (ms->ul_tbf)
520 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
521 if (ms->dl_tbf)
522 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
523}
524
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200525/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100526void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
527{
528 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
529 return;
530
531 if (tlli != ms->new_dl_tlli) {
532 LOGP(DRLCMAC, LOGL_INFO,
533 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
534 "not yet confirmed\n",
535 ms_tlli(ms), tlli);
536 ms->new_ul_tlli = tlli;
537 return;
538 }
539
540 LOGP(DRLCMAC, LOGL_INFO,
541 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
542 "already confirmed partly\n",
543 ms->tlli, tlli);
544
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100545 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100546}
547
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200548/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100549bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
550{
551 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
552 return false;
553
554 if (tlli != ms->new_ul_tlli) {
555 /* The MS has not sent a message with the new TLLI, which may
556 * happen according to the spec [TODO: add reference]. */
557
558 LOGP(DRLCMAC, LOGL_INFO,
559 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
560 "partly confirmed\n", tlli);
561 /* Use the network's idea of TLLI as candidate, this does not
562 * change the result value of tlli() */
563 ms->new_dl_tlli = tlli;
564 return false;
565 }
566
567 LOGP(DRLCMAC, LOGL_INFO,
568 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
569
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100570 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100571
572 return true;
573}
574
575void ms_set_imsi(struct GprsMs *ms, const char *imsi)
576{
577 if (!imsi) {
578 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
579 return;
580 }
581
582 if (imsi[0] && strlen(imsi) < 3) {
583 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
584 imsi);
585 return;
586 }
587
588 if (strcmp(imsi, ms->imsi) == 0)
589 return;
590
591 LOGP(DRLCMAC, LOGL_INFO,
592 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
593 ms_tlli(ms), ms->imsi, imsi);
594
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200595 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100596 /* Check if we are going to store a different MS object with already
597 existing IMSI. This is probably a bug in code calling this function,
598 since it should take care of this explicitly */
599 if (old_ms) {
600 /* We cannot find ms->ms by IMSI since we know that it has a
601 * different IMSI */
602 OSMO_ASSERT(old_ms != ms);
603
604 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
605 "IMSI '%s' was already assigned to another "
606 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
607 imsi, ms_tlli(old_ms));
608
609 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100610 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100611 }
612
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100613 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100614 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100615
616 /* Update TBF FSM names: */
617 if (ms->ul_tbf)
618 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
619 if (ms->dl_tbf)
620 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100621}
622
623void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
624{
625 if (ta_ == ms->ta)
626 return;
627
628 if (gsm48_ta_is_valid(ta_)) {
629 LOGP(DRLCMAC, LOGL_INFO,
630 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
631 ms_tlli(ms), ms->ta, ta_);
632 ms->ta = ta_;
633 } else
634 LOGP(DRLCMAC, LOGL_NOTICE,
635 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
636 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
637}
638
639void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
640{
641 if (ms_class_ == ms->ms_class)
642 return;
643
644 LOGP(DRLCMAC, LOGL_INFO,
645 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
646 ms_tlli(ms), ms->ms_class, ms_class_);
647
648 ms->ms_class = ms_class_;
649}
650
651void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
652{
653 if (ms_class_ == ms->egprs_ms_class)
654 return;
655
656 LOGP(DRLCMAC, LOGL_INFO,
657 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
658 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
659
660 ms->egprs_ms_class = ms_class_;
661
662 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
663 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
664 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
665 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
666 return;
667 }
668
669 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
670 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
671 ms_mode(ms) != EGPRS)
672 {
673 ms_set_mode(ms, EGPRS_GMSK);
674 } else {
675 ms_set_mode(ms, EGPRS);
676 }
677 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
678}
679
680void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
681{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100682 int64_t now;
683 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100684 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100685
686 if (error_rate < 0)
687 return;
688
689 now = now_msec();
690
691 /* TODO: Check for TBF direction */
692 /* TODO: Support different CS values for UL and DL */
693
694 ms->nack_rate_dl = error_rate;
695
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100696 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100697 if (mcs_chan_code(ms->current_cs_dl) > 0) {
698 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
699 LOGP(DRLCMACDL, LOGL_INFO,
700 "MS (IMSI %s): High error rate %d%%, "
701 "reducing CS level to %s\n",
702 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
703 ms->last_cs_not_low = now;
704 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100705 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100706 if (ms->current_cs_dl < max_cs_dl) {
707 if (now - ms->last_cs_not_low > 1000) {
708 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
709
710 LOGP(DRLCMACDL, LOGL_INFO,
711 "MS (IMSI %s): Low error rate %d%%, "
712 "increasing DL CS level to %s\n",
713 ms_imsi(ms), error_rate,
714 mcs_name(ms->current_cs_dl));
715 ms->last_cs_not_low = now;
716 } else {
717 LOGP(DRLCMACDL, LOGL_DEBUG,
718 "MS (IMSI %s): Low error rate %d%%, "
719 "ignored (within blocking period)\n",
720 ms_imsi(ms), error_rate);
721 }
722 }
723 } else {
724 LOGP(DRLCMACDL, LOGL_DEBUG,
725 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
726 ms_imsi(ms), error_rate);
727 ms->last_cs_not_low = now;
728 }
729}
730
731enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
732{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100733 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100734 OSMO_ASSERT(ms->bts != NULL);
735
736 if (mcs_is_gprs(ms->current_cs_ul)) {
737 if (!bts_max_cs_ul(ms->bts)) {
738 return CS4;
739 }
740
741 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
742 }
743
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100744 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
745 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
746 cs = MCS4;
747 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100748}
749
750void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
751{
752 ms->current_cs_dl = scheme;
753}
754
755enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
756{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100757 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100758 OSMO_ASSERT(ms->bts != NULL);
759
760 if (mcs_is_gprs(ms->current_cs_dl)) {
761 if (!bts_max_cs_dl(ms->bts)) {
762 return CS4;
763 }
764
765 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
766 }
767
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100768 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
769 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
770 cs = MCS4;
771 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100772}
773
774void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
775{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100776 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
777
778 int old_link_qual;
779 int low;
780 int high;
781 enum CodingScheme new_cs_ul = ms->current_cs_ul;
782 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
783
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100784 if (!max_cs_ul) {
785 LOGP(DRLCMACMEAS, LOGL_ERROR,
786 "max_cs_ul cannot be derived (current UL CS: %s)\n",
787 mcs_name(ms->current_cs_ul));
788 return;
789 }
790
791 if (!ms->current_cs_ul) {
792 LOGP(DRLCMACMEAS, LOGL_ERROR,
793 "Unable to update UL (M)CS because it's not set: %s\n",
794 mcs_name(ms->current_cs_ul));
795 return;
796 }
797
798 if (!meas->have_link_qual) {
799 LOGP(DRLCMACMEAS, LOGL_ERROR,
800 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
801 mcs_name(ms->current_cs_ul));
802 return;
803 }
804
805 if (mcs_is_gprs(ms->current_cs_ul)) {
806 if (current_cs >= MAX_GPRS_CS)
807 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100808 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
809 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100810 } else if (mcs_is_edge(ms->current_cs_ul)) {
811 if (current_cs >= MAX_EDGE_MCS)
812 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100813 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
814 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100815 } else {
816 LOGP(DRLCMACMEAS, LOGL_ERROR,
817 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
818 mcs_name(ms->current_cs_ul));
819 return;
820 }
821
822 /* To avoid rapid changes of the coding scheme, we also take
823 * the old link quality value into account (if present). */
824 if (ms->l1_meas.have_link_qual)
825 old_link_qual = ms->l1_meas.link_qual;
826 else
827 old_link_qual = meas->link_qual;
828
829 if (meas->link_qual < low && old_link_qual < low)
830 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
831 else if (meas->link_qual > high && old_link_qual > high &&
832 ms->current_cs_ul < max_cs_ul)
833 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
834
835 if (ms->current_cs_ul != new_cs_ul) {
836 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
837 "Link quality %ddB (old %ddB) left window [%d, %d], "
838 "modifying uplink CS level: %s -> %s\n",
839 meas->link_qual, old_link_qual,
840 low, high,
841 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
842
843 ms->current_cs_ul = new_cs_ul;
844 }
845}
846
847void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
848{
849 unsigned i;
850
851 ms_update_cs_ul(ms, meas);
852
853 if (meas->have_rssi)
854 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
855 if (meas->have_bto)
856 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
857 if (meas->have_ber)
858 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
859 if (meas->have_link_qual)
860 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
861
862 if (meas->have_ms_rx_qual)
863 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
864 if (meas->have_ms_c_value)
865 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
866 if (meas->have_ms_sign_var)
867 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
868
869 if (meas->have_ms_i_level) {
870 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
871 if (meas->ts[i].have_ms_i_level)
872 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
873 else
874 ms->l1_meas.ts[i].have_ms_i_level = 0;
875 }
876 }
877}
878
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100879/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
880enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100881{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100882 enum CodingScheme orig_cs = ms->current_cs_dl;
883 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100884 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100885 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100886
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100887 /* It could be that a TBF requests a GPRS CS despite the MS currently
888 being upgraded to EGPRS (hence reporting MCS). That could happen
889 because the TBF was created early in the process where we didn't have
890 yet enough information about the MS, and only AFTER it was created we
891 upgraded the MS to be EGPRS capable.
892 As a result, when the MS is queried for the target CS here, we could be
893 returning an MCS despite the current TBF being established as GPRS,
894 but we rather stick to the TBF type we assigned to the MS rather than
895 magically sending EGPRS data blocks to a GPRS TBF.
896 It could also be that the caller requests specific MCS kind
897 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
898 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
899 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
900 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
901 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
902 MCS1;
903 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
904 int i;
905 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
906 cs -= (MCS1 - CS1); /* MCSx -> CSx */
907 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
908 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
909 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
910 cs = CS1 + i;
911 } else {
912 cs = orig_cs;
913 }
914
915 if (orig_cs != cs)
916 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
917 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
918 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100919
920 unencoded_octets = llc_queue_octets(&ms->llc_queue);
921
922 /* If the DL TBF is active, add number of unencoded chunk octets */
923 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200924 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100925
926 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100927 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100928 return cs;
929
930 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100931 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100932 return cs;
933
934 /* The throughput would probably be better if the CS level was reduced */
935 mcs_dec_kind(&cs, ms_mode(ms));
936
937 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
938 if (cs == CS2)
939 mcs_dec_kind(&cs, ms_mode(ms));
940
941 return cs;
942}
943
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100944struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100945{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100946 return ms->first_common_ts;
947}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100948
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100949void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100950{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100951 OSMO_ASSERT(pdch);
952 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100953}
954
955uint8_t ms_dl_slots(const struct GprsMs *ms)
956{
957 uint8_t slots = 0;
958
959 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200960 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100961
962 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200963 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100964
965 return slots;
966}
967
968uint8_t ms_ul_slots(const struct GprsMs *ms)
969{
970 uint8_t slots = 0;
971
972 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200973 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100974
975 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200976 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100977
978 return slots;
979}
980
981uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
982{
983 uint8_t slots = 0;
984
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200985 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
986 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100987
988 if (!is_dl_active && !is_ul_active)
989 return 0;
990
991 /* see TS 44.060, 8.1.1.2.2 */
992 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200993 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100994 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200995 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100996 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200997 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
998 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100999
1000 /* Assume a multislot class 1 device */
1001 /* TODO: For class 2 devices, this could be removed */
1002 slots = pcu_lsb(slots);
1003
1004 return slots;
1005}
1006
1007void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
1008 uint8_t ul_slots, uint8_t dl_slots)
1009{
1010 if (ms->current_trx) {
1011 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1012 ms->reserved_dl_slots);
1013 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1014 ms->reserved_ul_slots);
1015 ms->reserved_dl_slots = 0;
1016 ms->reserved_ul_slots = 0;
1017 }
1018 ms->current_trx = trx;
1019 if (trx) {
1020 ms->reserved_dl_slots = dl_slots;
1021 ms->reserved_ul_slots = ul_slots;
1022 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1023 ms->reserved_dl_slots);
1024 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1025 ms->reserved_ul_slots);
1026 }
1027}
1028
1029struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1030{
1031 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001032 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1033 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001034 }
1035
1036 return NULL;
1037}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001038
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001039const char *ms_name(const struct GprsMs *ms)
1040{
1041 static char _ms_name_buf[128];
1042 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1043}
1044
1045char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1046{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001047 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1048 uint32_t tlli = ms_tlli(ms);
1049
1050 OSMO_STRBUF_PRINTF(sb, "MS(");
1051 if (ms_imsi_is_valid(ms))
1052 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1053 if (tlli != GSM_RESERVED_TMSI)
1054 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1055 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1056 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1057 if (ms->ul_tbf)
1058 OSMO_STRBUF_PRINTF(sb, ":UL");
1059 if (ms->dl_tbf)
1060 OSMO_STRBUF_PRINTF(sb, ":DL");
1061
1062 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001063 return buf;
1064}
1065
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001066int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1067{
1068 if (!ms->nacc)
1069 ms->nacc = nacc_fsm_alloc(ms);
1070 if (!ms->nacc)
1071 return -EINVAL;
1072 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1073}
1074
1075bool ms_nacc_rts(const struct GprsMs *ms)
1076{
1077 if (!ms->nacc)
1078 return false;
1079 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1080 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1081 return true;
1082 return false;
1083}
1084
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001085struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1086 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001087{
1088 int rc;
1089 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1090
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001091 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1092 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001093 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001094 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001095 .msg = NULL,
1096 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001097
1098 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1099 if (rc != 0 || !data_ctx.msg)
1100 return NULL;
1101 return data_ctx.msg;
1102}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001103
1104static void ms_start_llc_timer(struct GprsMs *ms)
1105{
1106 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1107 struct timespec tv;
1108 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1109 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1110 }
1111}
1112
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001113/* Can we get to send a DL TBF ass to the MS? */
1114static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1115{
1116 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1117
1118 /* This function assumes it is called when no DL TBF is present */
1119 OSMO_ASSERT(!ms_dl_tbf(ms));
1120
1121 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1122 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1123 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1124 * completed on the mobile station side." */
1125 /* The possible uplink TBF is used to trigger downlink assignment:
1126 * - If there is no uplink TBF the MS is potentially in packet idle mode
1127 * and hence assignment will be done over CCCH (PCH)
1128 * - If there's an uplink TBF but it is finished (waiting for last PKT
1129 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1130 * have no ways to contact the MS right now. Assignment will be delayed
1131 * until PKT CTRL ACK is received and the TBF is released at the MS side
1132 * (then assignment goes through PCH).
1133 */
1134 if (!ul_tbf)
1135 return true;
1136 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1137 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1138 return true;
1139
1140 return false;
1141
1142}
1143
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001144/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1145 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1146 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1147 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1148 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1149 */
1150struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1151{
1152 const bool single_slot = false;
1153 struct gprs_rlcmac_ul_tbf *ul_tbf;
1154
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001155 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001156 if (!ul_tbf) {
1157 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1158 /* Caller will most probably send a Imm Ass Reject after return */
1159 return NULL;
1160 }
1161 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1162 /* Contention resolution is considered to be done since TLLI is known in MS */
1163 return ul_tbf;
1164}
1165
1166/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1167 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1168struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1169{
1170 const int8_t trx_no = -1;
1171 const bool single_slot = true;
1172 struct gprs_rlcmac_ul_tbf *ul_tbf;
1173
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001174 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001175 if (!ul_tbf) {
1176 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1177 /* Caller will most probably send a Imm Ass Reject after return */
1178 return NULL;
1179 }
1180 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1181 return ul_tbf;
1182}
1183
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001184/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1185 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1186 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1187 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1188struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1189{
1190 struct gprs_rlcmac_ul_tbf *ul_tbf;
1191 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1192 if (!ul_tbf)
1193 return NULL;
1194 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1195 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1196
1197 return ul_tbf;
1198}
1199
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001200/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1201 * "tbf" may be either a UL-TBF or a DL-TBF.
1202 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1203 */
1204int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001205{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001206 OSMO_ASSERT(tbf);
1207 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1208 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001209 struct gprs_rlcmac_dl_tbf *dl_tbf;
1210
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001211 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1212 if (!dl_tbf) {
1213 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1214 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001215 }
1216
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001217 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1218 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1219 return 0;
1220}
1221
1222/* A new DL-TBF is allocated and assigned through PCH.
1223 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1224 */
1225int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1226{
1227 const int8_t trx_no = -1;
1228 const bool single_slot = true;
1229 struct gprs_rlcmac_dl_tbf *dl_tbf;
1230
1231 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001232 if (!dl_tbf) {
1233 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1234 return -EBUSY;
1235 }
1236
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001237 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1238 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001239 return 0;
1240}
1241
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001242int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1243{
1244 struct timespec expire_time;
1245 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001246 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001247
1248 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1249
1250 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1251 if (!llc_msg)
1252 return -ENOMEM;
1253
1254 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1255 memcpy(msgb_put(llc_msg, len), data, len);
1256 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1257 ms_start_llc_timer(ms);
1258
1259 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001260 if (dl_tbf) {
1261 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1262 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001263 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001264 }
1265 } else {
1266 /* Check if we can create a DL TBF to start sending the enqueued
1267 * data. Otherwise it will be triggered later when it is reachable
1268 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001269 if (ms_is_reachable_for_dl_ass(ms)) {
1270 if (ms_ul_tbf(ms))
1271 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1272 else
1273 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1274 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001275 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001276 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001277}