blob: caa0cf50dc77327fabbe354427c4e9e99988ea63 [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 Pedrolac4d4a62023-04-18 19:02:55 +020064static void ms_becomes_idle(struct GprsMs *ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020065
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
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +020093 if (total == 0) {
94 OSMO_ASSERT(ms_is_idle(ms));
95 ms_becomes_idle(ms);
96 }
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020097 return 0;
98}
99
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100100static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100101{
102 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100103 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100104
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200105 /* Finally free the MS after being idle for a while according to config */
106 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100107}
108
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200109static void ms_llc_timer_cb(void *_ms)
110{
111 struct GprsMs *ms = _ms;
112 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
113
114 if (!dl_tbf)
115 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200116 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200117 return;
118
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200119 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200120
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200121 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200122}
123
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100124static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200125struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100126{
127 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200128 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100129
130 talloc_set_destructor(ms, ms_talloc_destructor);
131
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200132 llist_add(&ms->list, &bts->ms_list);
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200133 bts_stat_item_inc(bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200134
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100135 ms->bts = bts;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200136 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100137 ms->new_ul_tlli = GSM_RESERVED_TMSI;
138 ms->new_dl_tlli = GSM_RESERVED_TMSI;
139 ms->ta = GSM48_TA_INVALID;
140 ms->current_cs_ul = UNKNOWN;
141 ms->current_cs_dl = UNKNOWN;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142 INIT_LLIST_HEAD(&ms->old_tbfs);
143
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200144 ms->use_count = (struct osmo_use_count){
145 .talloc_object = ms,
146 .use_cb = ms_use_cb,
147 };
148
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100149 int codel_interval = LLC_CODEL_USE_DEFAULT;
150
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200151 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100152
153 ms->imsi[0] = '\0';
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200154 osmo_timer_setup(&ms->timer, ms_release_timer_cb, ms);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200155 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200156 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
157 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100158
159 ms_set_mode(ms, GPRS);
160
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200161 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200162 if (codel_interval == LLC_CODEL_USE_DEFAULT)
163 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
164 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100165
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100166 ms->last_cs_not_low = now_msec();
167 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100168
169 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
170 if (!ms->ctrs)
171 goto free_ret;
172
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200173 ms_set_timeout(ms, osmo_tdef_get(bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1));
174
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100175 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100176free_ret:
177 talloc_free(ms);
178 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100179}
180
181static int ms_talloc_destructor(struct GprsMs *ms)
182{
183 struct llist_item *pos, *tmp;
184
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100185 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100186
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200187 bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200188 llist_del(&ms->list);
189
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100190 ms_set_reserved_slots(ms, NULL, 0, 0);
191
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700192 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100193
194 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200195 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100196 ms->ul_tbf = NULL;
197 }
198
199 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200200 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100201 ms->dl_tbf = NULL;
202 }
203
204 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
205 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
206 tbf_set_ms(tbf, NULL);
207 }
208
209 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200210 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100211
212 if (ms->ctrs)
213 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100214 return 0;
215}
216
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200217/* MS has no attached TBFs anymore. */
218static void ms_becomes_idle(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100219{
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200220 ms_set_reserved_slots(ms, NULL, 0, 0);
221 ms->first_common_ts = NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100222
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200223 /* Immediate free():
224 * Skip delaying free() through release timer if delay is configured to be 0.
225 * This is useful for synced freed during unit tests.
226 */
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200227 if (ms->delay == 0) {
228 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100229 return;
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200230 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100231
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200232 /* Immediate free():
233 * Skip delaying free() through release timer if TMSI is not
234 * known, since those cannot really be reused.
235 */
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200236 if (ms_tlli(ms) == GSM_RESERVED_TMSI) {
237 talloc_free(ms);
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200238 return;
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200239 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100240
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200241 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100242 osmo_timer_schedule(&ms->timer, ms->delay, 0);
243}
244
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200245static void ms_becomes_active(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100246{
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200247 if (!osmo_timer_pending(&ms->timer))
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100248 return;
249
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100250 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
251
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100252 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100253}
254
255void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
256{
257 ms->mode = mode;
258
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100259 switch (ms->mode) {
260 case GPRS:
261 if (!mcs_is_gprs(ms->current_cs_ul)) {
262 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100263 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100264 if (!mcs_is_valid(ms->current_cs_ul))
265 ms->current_cs_ul = CS1;
266 }
267 if (!mcs_is_gprs(ms->current_cs_dl)) {
268 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100269 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100270 if (!mcs_is_valid(ms->current_cs_dl))
271 ms->current_cs_dl = CS1;
272 }
273 break;
274
275 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100276 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
277 ms->current_cs_ul = mcs_get_egprs_by_num(
278 ms->bts->initial_mcs_ul);
279 if (!mcs_is_valid(ms->current_cs_ul))
280 ms->current_cs_ul = MCS1;
281 }
282 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
283 ms->current_cs_dl = mcs_get_egprs_by_num(
284 ms->bts->initial_mcs_dl);
285 if (!mcs_is_valid(ms->current_cs_dl))
286 ms->current_cs_dl = MCS1;
287 }
288 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100289 case EGPRS:
290 if (!mcs_is_edge(ms->current_cs_ul)) {
291 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100292 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100293 if (!mcs_is_valid(ms->current_cs_ul))
294 ms->current_cs_ul = MCS1;
295 }
296 if (!mcs_is_edge(ms->current_cs_dl)) {
297 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100298 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100299 if (!mcs_is_valid(ms->current_cs_dl))
300 ms->current_cs_dl = MCS1;
301 }
302 break;
303 }
304}
305
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200306/* If a TBF is attached to an MS, it is either in ms->{dl,ul}_tbf or in ms->old_tbfs list */
307static bool ms_tbf_is_attached(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf)
308{
309 const struct llist_item *pos;
310 OSMO_ASSERT(ms);
311 OSMO_ASSERT(tbf);
312 OSMO_ASSERT(tbf_ms(tbf) == ms);
313
314 if (tbf == ul_tbf_as_tbf_const(ms->ul_tbf))
315 return true;
316
317 if (tbf == dl_tbf_as_tbf_const(ms->dl_tbf))
318 return true;
319
320 llist_for_each_entry(pos, &ms->old_tbfs, list) {
321 const struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
322 if (tmp_tbf == tbf)
323 return true;
324 }
325 return false;
326}
327
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100328static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
329{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100330 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 +0100331
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100332 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200333 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100334
335 ms->ul_tbf = tbf;
336
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200337 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100338}
339
340static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
341{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100342 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 +0100343
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100344 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200345 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100346
347 ms->dl_tbf = tbf;
348
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200349 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100350}
351
352void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
353{
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200354 OSMO_ASSERT(ms);
355 OSMO_ASSERT(tbf);
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200356 OSMO_ASSERT(!ms_tbf_is_attached(ms, tbf));
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200357
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200358 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200359 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200360 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200361 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200362
363 ms_becomes_active(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100364}
365
366void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
367{
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200368 OSMO_ASSERT(tbf_ms(tbf) == ms);
369
370 /* In general this should not happen, but it can happen if during TBF
371 * allocation something fails before tbf->setup() called ms_attach_tbf(). */
372 if (!ms_tbf_is_attached(ms, tbf))
373 return;
374
Pau Espin Pedroldf6684f2023-04-18 17:50:34 +0200375 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
376 tbf_name(tbf));
377
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200378 if (tbf == ul_tbf_as_tbf(ms->ul_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100379 ms->ul_tbf = NULL;
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200380 } else if (tbf == dl_tbf_as_tbf(ms->dl_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100381 ms->dl_tbf = NULL;
382 } else {
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200383 /* We know from ms_tbf_is_attached()==true check above that tbf
384 * is in ms->old_tbfs, no need to look it up again. */
385 llist_del(tbf_ms_list(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100386 }
387
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200388 ms_unref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100389}
390
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200391/* Cleans up old MS being merged into a new one. Should be called with a
392ms_ref() taken to avoid use-after-free. */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100393void ms_reset(struct GprsMs *ms)
394{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100395 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200396 struct llist_item *pos;
397 struct gprs_rlcmac_tbf *tbf;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100398
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200399 /* free immediately when it becomes idle: */
400 ms->delay = 0;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100401
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200402 tbf = ul_tbf_as_tbf(ms_ul_tbf(ms));
403 if (tbf && !tbf_timers_pending(tbf, T_MAX))
404 tbf_free(tbf);
405 tbf = dl_tbf_as_tbf(ms_dl_tbf(ms));
406 if (tbf && !tbf_timers_pending(tbf, T_MAX))
407 tbf_free(tbf);
408
409 llist_for_each_entry(pos, &ms->old_tbfs, list) {
410 tbf = (struct gprs_rlcmac_tbf *)pos->entry;
411 if (!tbf_timers_pending(tbf, T_MAX))
412 tbf_free(tbf);
413 }
414
415 /* Flag it with invalid data so that it cannot be looked up anymore and
416 * shows up specially if listed in VTY: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100417 ms->tlli = GSM_RESERVED_TMSI;
418 ms->new_dl_tlli = ms->tlli;
419 ms->new_ul_tlli = ms->tlli;
420 ms->imsi[0] = '\0';
421}
422
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200423/* This function should be called on the MS object of a TBF each time an RLCMAC
424 * block is received for it with TLLI information.
425 * Besides updating the TLLI field on the MS object, it also seeks for other MS
426 * objects in the store and merges them into the current MS object. The MS
427 * duplication happened because we don't learn the TLLI of the created TBF until
428 * a later point. */
429void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
430{
431 struct GprsMs *old_ms = NULL;
432
433 if (tlli == GSM_RESERVED_TMSI)
434 return;
435
436 /* When the TLLI does not match the ms, check if there is another
437 * MS object that belongs to that TLLI and if yes make sure one of them
438 * gets deleted. */
439 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200440 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200441
442 ms_set_tlli(ms, tlli);
443
444 if (old_ms)
445 ms_merge_and_clear_ms(ms, old_ms);
446 /* old_ms may no longer be available here */
447}
448
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100449/* Merge 'old_ms' object into 'ms' object.
450 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
451void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100452{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100453 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100454 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200455 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100456
457 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
458
459 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100460
461 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
462 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
463
464 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
465 ms_set_ms_class(ms, ms_ms_class(old_ms));
466
467 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
468 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
469
470 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
471
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100472 /* Clean up the old MS object */
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100473 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100474
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200475 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100476}
477
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100478/* Apply changes to the TLLI directly, used interally by functions below: */
479static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
480{
481 ms->tlli = tlli;
482 ms->new_dl_tlli = GSM_RESERVED_TMSI;
483 ms->new_ul_tlli = GSM_RESERVED_TMSI;
484
485 /* Update TBF FSM names: */
486 if (ms->ul_tbf)
487 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
488 if (ms->dl_tbf)
489 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
490}
491
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200492/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100493void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
494{
495 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
496 return;
497
498 if (tlli != ms->new_dl_tlli) {
499 LOGP(DRLCMAC, LOGL_INFO,
500 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
501 "not yet confirmed\n",
502 ms_tlli(ms), tlli);
503 ms->new_ul_tlli = tlli;
504 return;
505 }
506
507 LOGP(DRLCMAC, LOGL_INFO,
508 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
509 "already confirmed partly\n",
510 ms->tlli, tlli);
511
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100512 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100513}
514
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200515/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100516bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
517{
518 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
519 return false;
520
521 if (tlli != ms->new_ul_tlli) {
522 /* The MS has not sent a message with the new TLLI, which may
523 * happen according to the spec [TODO: add reference]. */
524
525 LOGP(DRLCMAC, LOGL_INFO,
526 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
527 "partly confirmed\n", tlli);
528 /* Use the network's idea of TLLI as candidate, this does not
529 * change the result value of tlli() */
530 ms->new_dl_tlli = tlli;
531 return false;
532 }
533
534 LOGP(DRLCMAC, LOGL_INFO,
535 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
536
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100537 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100538
539 return true;
540}
541
542void ms_set_imsi(struct GprsMs *ms, const char *imsi)
543{
544 if (!imsi) {
545 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
546 return;
547 }
548
549 if (imsi[0] && strlen(imsi) < 3) {
550 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
551 imsi);
552 return;
553 }
554
555 if (strcmp(imsi, ms->imsi) == 0)
556 return;
557
558 LOGP(DRLCMAC, LOGL_INFO,
559 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
560 ms_tlli(ms), ms->imsi, imsi);
561
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200562 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100563 /* Check if we are going to store a different MS object with already
564 existing IMSI. This is probably a bug in code calling this function,
565 since it should take care of this explicitly */
566 if (old_ms) {
567 /* We cannot find ms->ms by IMSI since we know that it has a
568 * different IMSI */
569 OSMO_ASSERT(old_ms != ms);
570
571 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
572 "IMSI '%s' was already assigned to another "
573 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
574 imsi, ms_tlli(old_ms));
575
576 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100577 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100578 }
579
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100580 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100581 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100582
583 /* Update TBF FSM names: */
584 if (ms->ul_tbf)
585 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
586 if (ms->dl_tbf)
587 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100588}
589
590void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
591{
592 if (ta_ == ms->ta)
593 return;
594
595 if (gsm48_ta_is_valid(ta_)) {
596 LOGP(DRLCMAC, LOGL_INFO,
597 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
598 ms_tlli(ms), ms->ta, ta_);
599 ms->ta = ta_;
600 } else
601 LOGP(DRLCMAC, LOGL_NOTICE,
602 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
603 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
604}
605
606void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
607{
608 if (ms_class_ == ms->ms_class)
609 return;
610
611 LOGP(DRLCMAC, LOGL_INFO,
612 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
613 ms_tlli(ms), ms->ms_class, ms_class_);
614
615 ms->ms_class = ms_class_;
616}
617
618void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
619{
620 if (ms_class_ == ms->egprs_ms_class)
621 return;
622
623 LOGP(DRLCMAC, LOGL_INFO,
624 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
625 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
626
627 ms->egprs_ms_class = ms_class_;
628
629 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
630 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
631 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
632 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
633 return;
634 }
635
636 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
637 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
638 ms_mode(ms) != EGPRS)
639 {
640 ms_set_mode(ms, EGPRS_GMSK);
641 } else {
642 ms_set_mode(ms, EGPRS);
643 }
644 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
645}
646
647void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
648{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100649 int64_t now;
650 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100651 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100652
653 if (error_rate < 0)
654 return;
655
656 now = now_msec();
657
658 /* TODO: Check for TBF direction */
659 /* TODO: Support different CS values for UL and DL */
660
661 ms->nack_rate_dl = error_rate;
662
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100663 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100664 if (mcs_chan_code(ms->current_cs_dl) > 0) {
665 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
666 LOGP(DRLCMACDL, LOGL_INFO,
667 "MS (IMSI %s): High error rate %d%%, "
668 "reducing CS level to %s\n",
669 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
670 ms->last_cs_not_low = now;
671 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100672 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100673 if (ms->current_cs_dl < max_cs_dl) {
674 if (now - ms->last_cs_not_low > 1000) {
675 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
676
677 LOGP(DRLCMACDL, LOGL_INFO,
678 "MS (IMSI %s): Low error rate %d%%, "
679 "increasing DL CS level to %s\n",
680 ms_imsi(ms), error_rate,
681 mcs_name(ms->current_cs_dl));
682 ms->last_cs_not_low = now;
683 } else {
684 LOGP(DRLCMACDL, LOGL_DEBUG,
685 "MS (IMSI %s): Low error rate %d%%, "
686 "ignored (within blocking period)\n",
687 ms_imsi(ms), error_rate);
688 }
689 }
690 } else {
691 LOGP(DRLCMACDL, LOGL_DEBUG,
692 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
693 ms_imsi(ms), error_rate);
694 ms->last_cs_not_low = now;
695 }
696}
697
698enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
699{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100700 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100701 OSMO_ASSERT(ms->bts != NULL);
702
703 if (mcs_is_gprs(ms->current_cs_ul)) {
704 if (!bts_max_cs_ul(ms->bts)) {
705 return CS4;
706 }
707
708 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
709 }
710
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100711 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
712 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
713 cs = MCS4;
714 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100715}
716
717void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
718{
719 ms->current_cs_dl = scheme;
720}
721
722enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
723{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100724 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100725 OSMO_ASSERT(ms->bts != NULL);
726
727 if (mcs_is_gprs(ms->current_cs_dl)) {
728 if (!bts_max_cs_dl(ms->bts)) {
729 return CS4;
730 }
731
732 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
733 }
734
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100735 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
736 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
737 cs = MCS4;
738 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100739}
740
741void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
742{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100743 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
744
745 int old_link_qual;
746 int low;
747 int high;
748 enum CodingScheme new_cs_ul = ms->current_cs_ul;
749 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
750
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100751 if (!max_cs_ul) {
752 LOGP(DRLCMACMEAS, LOGL_ERROR,
753 "max_cs_ul cannot be derived (current UL CS: %s)\n",
754 mcs_name(ms->current_cs_ul));
755 return;
756 }
757
758 if (!ms->current_cs_ul) {
759 LOGP(DRLCMACMEAS, LOGL_ERROR,
760 "Unable to update UL (M)CS because it's not set: %s\n",
761 mcs_name(ms->current_cs_ul));
762 return;
763 }
764
765 if (!meas->have_link_qual) {
766 LOGP(DRLCMACMEAS, LOGL_ERROR,
767 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
768 mcs_name(ms->current_cs_ul));
769 return;
770 }
771
772 if (mcs_is_gprs(ms->current_cs_ul)) {
773 if (current_cs >= MAX_GPRS_CS)
774 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100775 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
776 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100777 } else if (mcs_is_edge(ms->current_cs_ul)) {
778 if (current_cs >= MAX_EDGE_MCS)
779 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100780 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
781 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100782 } else {
783 LOGP(DRLCMACMEAS, LOGL_ERROR,
784 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
785 mcs_name(ms->current_cs_ul));
786 return;
787 }
788
789 /* To avoid rapid changes of the coding scheme, we also take
790 * the old link quality value into account (if present). */
791 if (ms->l1_meas.have_link_qual)
792 old_link_qual = ms->l1_meas.link_qual;
793 else
794 old_link_qual = meas->link_qual;
795
796 if (meas->link_qual < low && old_link_qual < low)
797 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
798 else if (meas->link_qual > high && old_link_qual > high &&
799 ms->current_cs_ul < max_cs_ul)
800 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
801
802 if (ms->current_cs_ul != new_cs_ul) {
803 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
804 "Link quality %ddB (old %ddB) left window [%d, %d], "
805 "modifying uplink CS level: %s -> %s\n",
806 meas->link_qual, old_link_qual,
807 low, high,
808 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
809
810 ms->current_cs_ul = new_cs_ul;
811 }
812}
813
814void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
815{
816 unsigned i;
817
818 ms_update_cs_ul(ms, meas);
819
820 if (meas->have_rssi)
821 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
822 if (meas->have_bto)
823 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
824 if (meas->have_ber)
825 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
826 if (meas->have_link_qual)
827 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
828
829 if (meas->have_ms_rx_qual)
830 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
831 if (meas->have_ms_c_value)
832 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
833 if (meas->have_ms_sign_var)
834 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
835
836 if (meas->have_ms_i_level) {
837 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
838 if (meas->ts[i].have_ms_i_level)
839 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
840 else
841 ms->l1_meas.ts[i].have_ms_i_level = 0;
842 }
843 }
844}
845
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100846/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
847enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100848{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100849 enum CodingScheme orig_cs = ms->current_cs_dl;
850 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100851 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100852 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100853
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100854 /* It could be that a TBF requests a GPRS CS despite the MS currently
855 being upgraded to EGPRS (hence reporting MCS). That could happen
856 because the TBF was created early in the process where we didn't have
857 yet enough information about the MS, and only AFTER it was created we
858 upgraded the MS to be EGPRS capable.
859 As a result, when the MS is queried for the target CS here, we could be
860 returning an MCS despite the current TBF being established as GPRS,
861 but we rather stick to the TBF type we assigned to the MS rather than
862 magically sending EGPRS data blocks to a GPRS TBF.
863 It could also be that the caller requests specific MCS kind
864 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
865 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
866 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
867 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
868 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
869 MCS1;
870 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
871 int i;
872 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
873 cs -= (MCS1 - CS1); /* MCSx -> CSx */
874 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
875 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
876 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
877 cs = CS1 + i;
878 } else {
879 cs = orig_cs;
880 }
881
882 if (orig_cs != cs)
883 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
884 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
885 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100886
887 unencoded_octets = llc_queue_octets(&ms->llc_queue);
888
889 /* If the DL TBF is active, add number of unencoded chunk octets */
890 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200891 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100892
893 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100894 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100895 return cs;
896
897 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100898 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100899 return cs;
900
901 /* The throughput would probably be better if the CS level was reduced */
902 mcs_dec_kind(&cs, ms_mode(ms));
903
904 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
905 if (cs == CS2)
906 mcs_dec_kind(&cs, ms_mode(ms));
907
908 return cs;
909}
910
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100911struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100912{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100913 return ms->first_common_ts;
914}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100915
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100916void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100917{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100918 OSMO_ASSERT(pdch);
919 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100920}
921
922uint8_t ms_dl_slots(const struct GprsMs *ms)
923{
924 uint8_t slots = 0;
925
926 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200927 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100928
929 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200930 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100931
932 return slots;
933}
934
935uint8_t ms_ul_slots(const struct GprsMs *ms)
936{
937 uint8_t slots = 0;
938
939 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200940 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100941
942 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200943 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100944
945 return slots;
946}
947
948uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
949{
950 uint8_t slots = 0;
951
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200952 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
953 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 +0100954
955 if (!is_dl_active && !is_ul_active)
956 return 0;
957
958 /* see TS 44.060, 8.1.1.2.2 */
959 if (is_dl_active && !is_ul_active)
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 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200962 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100963 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200964 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
965 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100966
967 /* Assume a multislot class 1 device */
968 /* TODO: For class 2 devices, this could be removed */
969 slots = pcu_lsb(slots);
970
971 return slots;
972}
973
974void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
975 uint8_t ul_slots, uint8_t dl_slots)
976{
977 if (ms->current_trx) {
978 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
979 ms->reserved_dl_slots);
980 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
981 ms->reserved_ul_slots);
982 ms->reserved_dl_slots = 0;
983 ms->reserved_ul_slots = 0;
984 }
985 ms->current_trx = trx;
986 if (trx) {
987 ms->reserved_dl_slots = dl_slots;
988 ms->reserved_ul_slots = ul_slots;
989 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
990 ms->reserved_dl_slots);
991 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
992 ms->reserved_ul_slots);
993 }
994}
995
996struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
997{
998 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200999 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1000 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001001 }
1002
1003 return NULL;
1004}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001005
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001006const char *ms_name(const struct GprsMs *ms)
1007{
1008 static char _ms_name_buf[128];
1009 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1010}
1011
1012char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1013{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001014 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1015 uint32_t tlli = ms_tlli(ms);
1016
1017 OSMO_STRBUF_PRINTF(sb, "MS(");
1018 if (ms_imsi_is_valid(ms))
1019 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1020 if (tlli != GSM_RESERVED_TMSI)
1021 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1022 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1023 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1024 if (ms->ul_tbf)
1025 OSMO_STRBUF_PRINTF(sb, ":UL");
1026 if (ms->dl_tbf)
1027 OSMO_STRBUF_PRINTF(sb, ":DL");
1028
1029 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001030 return buf;
1031}
1032
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001033int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1034{
1035 if (!ms->nacc)
1036 ms->nacc = nacc_fsm_alloc(ms);
1037 if (!ms->nacc)
1038 return -EINVAL;
1039 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1040}
1041
1042bool ms_nacc_rts(const struct GprsMs *ms)
1043{
1044 if (!ms->nacc)
1045 return false;
1046 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1047 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1048 return true;
1049 return false;
1050}
1051
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001052struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1053 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001054{
1055 int rc;
1056 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1057
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001058 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1059 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001060 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001061 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001062 .msg = NULL,
1063 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001064
1065 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1066 if (rc != 0 || !data_ctx.msg)
1067 return NULL;
1068 return data_ctx.msg;
1069}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001070
1071static void ms_start_llc_timer(struct GprsMs *ms)
1072{
1073 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1074 struct timespec tv;
1075 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1076 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1077 }
1078}
1079
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001080/* Can we get to send a DL TBF ass to the MS? */
1081static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1082{
1083 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1084
1085 /* This function assumes it is called when no DL TBF is present */
1086 OSMO_ASSERT(!ms_dl_tbf(ms));
1087
1088 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1089 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1090 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1091 * completed on the mobile station side." */
1092 /* The possible uplink TBF is used to trigger downlink assignment:
1093 * - If there is no uplink TBF the MS is potentially in packet idle mode
1094 * and hence assignment will be done over CCCH (PCH)
1095 * - If there's an uplink TBF but it is finished (waiting for last PKT
1096 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1097 * have no ways to contact the MS right now. Assignment will be delayed
1098 * until PKT CTRL ACK is received and the TBF is released at the MS side
1099 * (then assignment goes through PCH).
1100 */
1101 if (!ul_tbf)
1102 return true;
1103 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1104 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1105 return true;
1106
1107 return false;
1108
1109}
1110
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001111/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1112 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1113 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1114 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1115 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1116 */
1117struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1118{
1119 const bool single_slot = false;
1120 struct gprs_rlcmac_ul_tbf *ul_tbf;
1121
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001122 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001123 if (!ul_tbf) {
1124 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1125 /* Caller will most probably send a Imm Ass Reject after return */
1126 return NULL;
1127 }
1128 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1129 /* Contention resolution is considered to be done since TLLI is known in MS */
1130 return ul_tbf;
1131}
1132
1133/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1134 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1135struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1136{
1137 const int8_t trx_no = -1;
1138 const bool single_slot = true;
1139 struct gprs_rlcmac_ul_tbf *ul_tbf;
1140
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001141 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001142 if (!ul_tbf) {
1143 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1144 /* Caller will most probably send a Imm Ass Reject after return */
1145 return NULL;
1146 }
1147 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1148 return ul_tbf;
1149}
1150
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001151/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1152 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1153 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1154 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1155struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1156{
1157 struct gprs_rlcmac_ul_tbf *ul_tbf;
1158 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1159 if (!ul_tbf)
1160 return NULL;
1161 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1162 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1163
1164 return ul_tbf;
1165}
1166
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001167/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1168 * "tbf" may be either a UL-TBF or a DL-TBF.
1169 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1170 */
1171int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001172{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001173 OSMO_ASSERT(tbf);
1174 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1175 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001176 struct gprs_rlcmac_dl_tbf *dl_tbf;
1177
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001178 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1179 if (!dl_tbf) {
1180 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1181 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001182 }
1183
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001184 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1185 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1186 return 0;
1187}
1188
1189/* A new DL-TBF is allocated and assigned through PCH.
1190 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1191 */
1192int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1193{
1194 const int8_t trx_no = -1;
1195 const bool single_slot = true;
1196 struct gprs_rlcmac_dl_tbf *dl_tbf;
1197
1198 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001199 if (!dl_tbf) {
1200 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1201 return -EBUSY;
1202 }
1203
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001204 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1205 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001206 return 0;
1207}
1208
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001209int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1210{
1211 struct timespec expire_time;
1212 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001213 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001214
1215 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1216
1217 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1218 if (!llc_msg)
1219 return -ENOMEM;
1220
1221 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1222 memcpy(msgb_put(llc_msg, len), data, len);
1223 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1224 ms_start_llc_timer(ms);
1225
1226 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001227 if (dl_tbf) {
1228 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1229 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001230 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001231 }
1232 } else {
1233 /* Check if we can create a DL TBF to start sending the enqueued
1234 * data. Otherwise it will be triggered later when it is reachable
1235 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001236 if (ms_is_reachable_for_dl_ass(ms)) {
1237 if (ms_ul_tbf(ms))
1238 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1239 else
1240 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1241 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001242 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001243 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001244}