blob: 6c345fbce9bc4961948a9b4ac110a5ee320ec413 [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
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200351/* If a TBF is attached to an MS, it is either in ms->{dl,ul}_tbf or in ms->old_tbfs list */
352static bool ms_tbf_is_attached(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf)
353{
354 const struct llist_item *pos;
355 OSMO_ASSERT(ms);
356 OSMO_ASSERT(tbf);
357 OSMO_ASSERT(tbf_ms(tbf) == ms);
358
359 if (tbf == ul_tbf_as_tbf_const(ms->ul_tbf))
360 return true;
361
362 if (tbf == dl_tbf_as_tbf_const(ms->dl_tbf))
363 return true;
364
365 llist_for_each_entry(pos, &ms->old_tbfs, list) {
366 const struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
367 if (tmp_tbf == tbf)
368 return true;
369 }
370 return false;
371}
372
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100373static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
374{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100375 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 +0100376
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200377 ms_ref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100378
379 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200380 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100381
382 ms->ul_tbf = tbf;
383
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200384 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100385
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200386 ms_unref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100387}
388
389static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
390{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100391 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 +0100392
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200393 ms_ref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100394
395 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200396 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100397
398 ms->dl_tbf = tbf;
399
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200400 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100401
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200402 ms_unref(ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100403}
404
405void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
406{
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200407 OSMO_ASSERT(ms);
408 OSMO_ASSERT(tbf);
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200409 OSMO_ASSERT(!ms_tbf_is_attached(ms, tbf));
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200410
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200411 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200412 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200413 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200414 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100415}
416
417void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
418{
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200419 OSMO_ASSERT(tbf_ms(tbf) == ms);
420
421 /* In general this should not happen, but it can happen if during TBF
422 * allocation something fails before tbf->setup() called ms_attach_tbf(). */
423 if (!ms_tbf_is_attached(ms, tbf))
424 return;
425
Pau Espin Pedroldf6684f2023-04-18 17:50:34 +0200426 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
427 tbf_name(tbf));
428
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200429 if (tbf == ul_tbf_as_tbf(ms->ul_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100430 ms->ul_tbf = NULL;
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200431 } else if (tbf == dl_tbf_as_tbf(ms->dl_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100432 ms->dl_tbf = NULL;
433 } else {
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200434 /* We know from ms_tbf_is_attached()==true check above that tbf
435 * is in ms->old_tbfs, no need to look it up again. */
436 llist_del(tbf_ms_list(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100437 }
438
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100439 if (!ms->dl_tbf && !ms->ul_tbf) {
440 ms_set_reserved_slots(ms, NULL, 0, 0);
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100441 ms->first_common_ts = NULL;
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200442 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100443 }
444
445 ms_update_status(ms);
446}
447
448void ms_reset(struct GprsMs *ms)
449{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100450 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100451
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100452 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100453
454 ms->tlli = GSM_RESERVED_TMSI;
455 ms->new_dl_tlli = ms->tlli;
456 ms->new_ul_tlli = ms->tlli;
457 ms->imsi[0] = '\0';
458}
459
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200460/* This function should be called on the MS object of a TBF each time an RLCMAC
461 * block is received for it with TLLI information.
462 * Besides updating the TLLI field on the MS object, it also seeks for other MS
463 * objects in the store and merges them into the current MS object. The MS
464 * duplication happened because we don't learn the TLLI of the created TBF until
465 * a later point. */
466void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
467{
468 struct GprsMs *old_ms = NULL;
469
470 if (tlli == GSM_RESERVED_TMSI)
471 return;
472
473 /* When the TLLI does not match the ms, check if there is another
474 * MS object that belongs to that TLLI and if yes make sure one of them
475 * gets deleted. */
476 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200477 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200478
479 ms_set_tlli(ms, tlli);
480
481 if (old_ms)
482 ms_merge_and_clear_ms(ms, old_ms);
483 /* old_ms may no longer be available here */
484}
485
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100486/* Merge 'old_ms' object into 'ms' object.
487 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
488void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100489{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100490 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100491 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200492 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100493
494 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
495
496 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100497
498 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
499 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
500
501 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
502 ms_set_ms_class(ms, ms_ms_class(old_ms));
503
504 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
505 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
506
507 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
508
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100509 /* Clean up the old MS object */
510 /* TODO: Use timer? */
511 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
512 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
513 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
514 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
515
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100516 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100517
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200518 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100519}
520
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100521/* Apply changes to the TLLI directly, used interally by functions below: */
522static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
523{
524 ms->tlli = tlli;
525 ms->new_dl_tlli = GSM_RESERVED_TMSI;
526 ms->new_ul_tlli = GSM_RESERVED_TMSI;
527
528 /* Update TBF FSM names: */
529 if (ms->ul_tbf)
530 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
531 if (ms->dl_tbf)
532 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
533}
534
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200535/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100536void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
537{
538 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
539 return;
540
541 if (tlli != ms->new_dl_tlli) {
542 LOGP(DRLCMAC, LOGL_INFO,
543 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
544 "not yet confirmed\n",
545 ms_tlli(ms), tlli);
546 ms->new_ul_tlli = tlli;
547 return;
548 }
549
550 LOGP(DRLCMAC, LOGL_INFO,
551 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
552 "already confirmed partly\n",
553 ms->tlli, tlli);
554
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100555 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100556}
557
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200558/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100559bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
560{
561 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
562 return false;
563
564 if (tlli != ms->new_ul_tlli) {
565 /* The MS has not sent a message with the new TLLI, which may
566 * happen according to the spec [TODO: add reference]. */
567
568 LOGP(DRLCMAC, LOGL_INFO,
569 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
570 "partly confirmed\n", tlli);
571 /* Use the network's idea of TLLI as candidate, this does not
572 * change the result value of tlli() */
573 ms->new_dl_tlli = tlli;
574 return false;
575 }
576
577 LOGP(DRLCMAC, LOGL_INFO,
578 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
579
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100580 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100581
582 return true;
583}
584
585void ms_set_imsi(struct GprsMs *ms, const char *imsi)
586{
587 if (!imsi) {
588 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
589 return;
590 }
591
592 if (imsi[0] && strlen(imsi) < 3) {
593 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
594 imsi);
595 return;
596 }
597
598 if (strcmp(imsi, ms->imsi) == 0)
599 return;
600
601 LOGP(DRLCMAC, LOGL_INFO,
602 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
603 ms_tlli(ms), ms->imsi, imsi);
604
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200605 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100606 /* Check if we are going to store a different MS object with already
607 existing IMSI. This is probably a bug in code calling this function,
608 since it should take care of this explicitly */
609 if (old_ms) {
610 /* We cannot find ms->ms by IMSI since we know that it has a
611 * different IMSI */
612 OSMO_ASSERT(old_ms != ms);
613
614 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
615 "IMSI '%s' was already assigned to another "
616 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
617 imsi, ms_tlli(old_ms));
618
619 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100620 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100621 }
622
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100623 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100624 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100625
626 /* Update TBF FSM names: */
627 if (ms->ul_tbf)
628 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
629 if (ms->dl_tbf)
630 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100631}
632
633void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
634{
635 if (ta_ == ms->ta)
636 return;
637
638 if (gsm48_ta_is_valid(ta_)) {
639 LOGP(DRLCMAC, LOGL_INFO,
640 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
641 ms_tlli(ms), ms->ta, ta_);
642 ms->ta = ta_;
643 } else
644 LOGP(DRLCMAC, LOGL_NOTICE,
645 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
646 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
647}
648
649void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
650{
651 if (ms_class_ == ms->ms_class)
652 return;
653
654 LOGP(DRLCMAC, LOGL_INFO,
655 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
656 ms_tlli(ms), ms->ms_class, ms_class_);
657
658 ms->ms_class = ms_class_;
659}
660
661void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
662{
663 if (ms_class_ == ms->egprs_ms_class)
664 return;
665
666 LOGP(DRLCMAC, LOGL_INFO,
667 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
668 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
669
670 ms->egprs_ms_class = ms_class_;
671
672 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
673 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
674 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
675 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
676 return;
677 }
678
679 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
680 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
681 ms_mode(ms) != EGPRS)
682 {
683 ms_set_mode(ms, EGPRS_GMSK);
684 } else {
685 ms_set_mode(ms, EGPRS);
686 }
687 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
688}
689
690void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
691{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100692 int64_t now;
693 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100694 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100695
696 if (error_rate < 0)
697 return;
698
699 now = now_msec();
700
701 /* TODO: Check for TBF direction */
702 /* TODO: Support different CS values for UL and DL */
703
704 ms->nack_rate_dl = error_rate;
705
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100706 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100707 if (mcs_chan_code(ms->current_cs_dl) > 0) {
708 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
709 LOGP(DRLCMACDL, LOGL_INFO,
710 "MS (IMSI %s): High error rate %d%%, "
711 "reducing CS level to %s\n",
712 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
713 ms->last_cs_not_low = now;
714 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100715 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100716 if (ms->current_cs_dl < max_cs_dl) {
717 if (now - ms->last_cs_not_low > 1000) {
718 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
719
720 LOGP(DRLCMACDL, LOGL_INFO,
721 "MS (IMSI %s): Low error rate %d%%, "
722 "increasing DL CS level to %s\n",
723 ms_imsi(ms), error_rate,
724 mcs_name(ms->current_cs_dl));
725 ms->last_cs_not_low = now;
726 } else {
727 LOGP(DRLCMACDL, LOGL_DEBUG,
728 "MS (IMSI %s): Low error rate %d%%, "
729 "ignored (within blocking period)\n",
730 ms_imsi(ms), error_rate);
731 }
732 }
733 } else {
734 LOGP(DRLCMACDL, LOGL_DEBUG,
735 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
736 ms_imsi(ms), error_rate);
737 ms->last_cs_not_low = now;
738 }
739}
740
741enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
742{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100743 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100744 OSMO_ASSERT(ms->bts != NULL);
745
746 if (mcs_is_gprs(ms->current_cs_ul)) {
747 if (!bts_max_cs_ul(ms->bts)) {
748 return CS4;
749 }
750
751 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
752 }
753
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100754 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
755 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
756 cs = MCS4;
757 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100758}
759
760void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
761{
762 ms->current_cs_dl = scheme;
763}
764
765enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
766{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100767 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100768 OSMO_ASSERT(ms->bts != NULL);
769
770 if (mcs_is_gprs(ms->current_cs_dl)) {
771 if (!bts_max_cs_dl(ms->bts)) {
772 return CS4;
773 }
774
775 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
776 }
777
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100778 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
779 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
780 cs = MCS4;
781 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100782}
783
784void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
785{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100786 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
787
788 int old_link_qual;
789 int low;
790 int high;
791 enum CodingScheme new_cs_ul = ms->current_cs_ul;
792 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
793
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100794 if (!max_cs_ul) {
795 LOGP(DRLCMACMEAS, LOGL_ERROR,
796 "max_cs_ul cannot be derived (current UL CS: %s)\n",
797 mcs_name(ms->current_cs_ul));
798 return;
799 }
800
801 if (!ms->current_cs_ul) {
802 LOGP(DRLCMACMEAS, LOGL_ERROR,
803 "Unable to update UL (M)CS because it's not set: %s\n",
804 mcs_name(ms->current_cs_ul));
805 return;
806 }
807
808 if (!meas->have_link_qual) {
809 LOGP(DRLCMACMEAS, LOGL_ERROR,
810 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
811 mcs_name(ms->current_cs_ul));
812 return;
813 }
814
815 if (mcs_is_gprs(ms->current_cs_ul)) {
816 if (current_cs >= MAX_GPRS_CS)
817 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100818 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
819 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100820 } else if (mcs_is_edge(ms->current_cs_ul)) {
821 if (current_cs >= MAX_EDGE_MCS)
822 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100823 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
824 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100825 } else {
826 LOGP(DRLCMACMEAS, LOGL_ERROR,
827 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
828 mcs_name(ms->current_cs_ul));
829 return;
830 }
831
832 /* To avoid rapid changes of the coding scheme, we also take
833 * the old link quality value into account (if present). */
834 if (ms->l1_meas.have_link_qual)
835 old_link_qual = ms->l1_meas.link_qual;
836 else
837 old_link_qual = meas->link_qual;
838
839 if (meas->link_qual < low && old_link_qual < low)
840 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
841 else if (meas->link_qual > high && old_link_qual > high &&
842 ms->current_cs_ul < max_cs_ul)
843 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
844
845 if (ms->current_cs_ul != new_cs_ul) {
846 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
847 "Link quality %ddB (old %ddB) left window [%d, %d], "
848 "modifying uplink CS level: %s -> %s\n",
849 meas->link_qual, old_link_qual,
850 low, high,
851 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
852
853 ms->current_cs_ul = new_cs_ul;
854 }
855}
856
857void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
858{
859 unsigned i;
860
861 ms_update_cs_ul(ms, meas);
862
863 if (meas->have_rssi)
864 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
865 if (meas->have_bto)
866 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
867 if (meas->have_ber)
868 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
869 if (meas->have_link_qual)
870 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
871
872 if (meas->have_ms_rx_qual)
873 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
874 if (meas->have_ms_c_value)
875 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
876 if (meas->have_ms_sign_var)
877 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
878
879 if (meas->have_ms_i_level) {
880 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
881 if (meas->ts[i].have_ms_i_level)
882 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
883 else
884 ms->l1_meas.ts[i].have_ms_i_level = 0;
885 }
886 }
887}
888
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100889/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
890enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100891{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100892 enum CodingScheme orig_cs = ms->current_cs_dl;
893 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100894 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100895 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100896
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100897 /* It could be that a TBF requests a GPRS CS despite the MS currently
898 being upgraded to EGPRS (hence reporting MCS). That could happen
899 because the TBF was created early in the process where we didn't have
900 yet enough information about the MS, and only AFTER it was created we
901 upgraded the MS to be EGPRS capable.
902 As a result, when the MS is queried for the target CS here, we could be
903 returning an MCS despite the current TBF being established as GPRS,
904 but we rather stick to the TBF type we assigned to the MS rather than
905 magically sending EGPRS data blocks to a GPRS TBF.
906 It could also be that the caller requests specific MCS kind
907 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
908 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
909 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
910 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
911 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
912 MCS1;
913 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
914 int i;
915 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
916 cs -= (MCS1 - CS1); /* MCSx -> CSx */
917 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
918 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
919 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
920 cs = CS1 + i;
921 } else {
922 cs = orig_cs;
923 }
924
925 if (orig_cs != cs)
926 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
927 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
928 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100929
930 unencoded_octets = llc_queue_octets(&ms->llc_queue);
931
932 /* If the DL TBF is active, add number of unencoded chunk octets */
933 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200934 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100935
936 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100937 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100938 return cs;
939
940 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100941 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100942 return cs;
943
944 /* The throughput would probably be better if the CS level was reduced */
945 mcs_dec_kind(&cs, ms_mode(ms));
946
947 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
948 if (cs == CS2)
949 mcs_dec_kind(&cs, ms_mode(ms));
950
951 return cs;
952}
953
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100954struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100955{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100956 return ms->first_common_ts;
957}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100958
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100959void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100960{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100961 OSMO_ASSERT(pdch);
962 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100963}
964
965uint8_t ms_dl_slots(const struct GprsMs *ms)
966{
967 uint8_t slots = 0;
968
969 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200970 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100971
972 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200973 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100974
975 return slots;
976}
977
978uint8_t ms_ul_slots(const struct GprsMs *ms)
979{
980 uint8_t slots = 0;
981
982 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200983 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100984
985 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200986 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100987
988 return slots;
989}
990
991uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
992{
993 uint8_t slots = 0;
994
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200995 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
996 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 +0100997
998 if (!is_dl_active && !is_ul_active)
999 return 0;
1000
1001 /* see TS 44.060, 8.1.1.2.2 */
1002 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001003 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001004 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001005 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001006 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001007 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
1008 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001009
1010 /* Assume a multislot class 1 device */
1011 /* TODO: For class 2 devices, this could be removed */
1012 slots = pcu_lsb(slots);
1013
1014 return slots;
1015}
1016
1017void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
1018 uint8_t ul_slots, uint8_t dl_slots)
1019{
1020 if (ms->current_trx) {
1021 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1022 ms->reserved_dl_slots);
1023 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1024 ms->reserved_ul_slots);
1025 ms->reserved_dl_slots = 0;
1026 ms->reserved_ul_slots = 0;
1027 }
1028 ms->current_trx = trx;
1029 if (trx) {
1030 ms->reserved_dl_slots = dl_slots;
1031 ms->reserved_ul_slots = ul_slots;
1032 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1033 ms->reserved_dl_slots);
1034 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1035 ms->reserved_ul_slots);
1036 }
1037}
1038
1039struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1040{
1041 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001042 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1043 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001044 }
1045
1046 return NULL;
1047}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001048
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001049const char *ms_name(const struct GprsMs *ms)
1050{
1051 static char _ms_name_buf[128];
1052 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1053}
1054
1055char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1056{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001057 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1058 uint32_t tlli = ms_tlli(ms);
1059
1060 OSMO_STRBUF_PRINTF(sb, "MS(");
1061 if (ms_imsi_is_valid(ms))
1062 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1063 if (tlli != GSM_RESERVED_TMSI)
1064 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1065 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1066 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1067 if (ms->ul_tbf)
1068 OSMO_STRBUF_PRINTF(sb, ":UL");
1069 if (ms->dl_tbf)
1070 OSMO_STRBUF_PRINTF(sb, ":DL");
1071
1072 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001073 return buf;
1074}
1075
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001076int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1077{
1078 if (!ms->nacc)
1079 ms->nacc = nacc_fsm_alloc(ms);
1080 if (!ms->nacc)
1081 return -EINVAL;
1082 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1083}
1084
1085bool ms_nacc_rts(const struct GprsMs *ms)
1086{
1087 if (!ms->nacc)
1088 return false;
1089 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1090 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1091 return true;
1092 return false;
1093}
1094
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001095struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1096 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001097{
1098 int rc;
1099 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1100
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001101 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1102 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001103 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001104 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001105 .msg = NULL,
1106 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001107
1108 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1109 if (rc != 0 || !data_ctx.msg)
1110 return NULL;
1111 return data_ctx.msg;
1112}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001113
1114static void ms_start_llc_timer(struct GprsMs *ms)
1115{
1116 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1117 struct timespec tv;
1118 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1119 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1120 }
1121}
1122
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001123/* Can we get to send a DL TBF ass to the MS? */
1124static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1125{
1126 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1127
1128 /* This function assumes it is called when no DL TBF is present */
1129 OSMO_ASSERT(!ms_dl_tbf(ms));
1130
1131 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1132 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1133 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1134 * completed on the mobile station side." */
1135 /* The possible uplink TBF is used to trigger downlink assignment:
1136 * - If there is no uplink TBF the MS is potentially in packet idle mode
1137 * and hence assignment will be done over CCCH (PCH)
1138 * - If there's an uplink TBF but it is finished (waiting for last PKT
1139 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1140 * have no ways to contact the MS right now. Assignment will be delayed
1141 * until PKT CTRL ACK is received and the TBF is released at the MS side
1142 * (then assignment goes through PCH).
1143 */
1144 if (!ul_tbf)
1145 return true;
1146 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1147 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1148 return true;
1149
1150 return false;
1151
1152}
1153
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001154/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1155 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1156 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1157 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1158 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1159 */
1160struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1161{
1162 const bool single_slot = false;
1163 struct gprs_rlcmac_ul_tbf *ul_tbf;
1164
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001165 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001166 if (!ul_tbf) {
1167 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1168 /* Caller will most probably send a Imm Ass Reject after return */
1169 return NULL;
1170 }
1171 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1172 /* Contention resolution is considered to be done since TLLI is known in MS */
1173 return ul_tbf;
1174}
1175
1176/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1177 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1178struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1179{
1180 const int8_t trx_no = -1;
1181 const bool single_slot = true;
1182 struct gprs_rlcmac_ul_tbf *ul_tbf;
1183
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001184 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001185 if (!ul_tbf) {
1186 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1187 /* Caller will most probably send a Imm Ass Reject after return */
1188 return NULL;
1189 }
1190 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1191 return ul_tbf;
1192}
1193
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001194/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1195 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1196 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1197 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1198struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1199{
1200 struct gprs_rlcmac_ul_tbf *ul_tbf;
1201 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1202 if (!ul_tbf)
1203 return NULL;
1204 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1205 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1206
1207 return ul_tbf;
1208}
1209
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001210/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1211 * "tbf" may be either a UL-TBF or a DL-TBF.
1212 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1213 */
1214int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001215{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001216 OSMO_ASSERT(tbf);
1217 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1218 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001219 struct gprs_rlcmac_dl_tbf *dl_tbf;
1220
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001221 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1222 if (!dl_tbf) {
1223 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1224 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001225 }
1226
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001227 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1228 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1229 return 0;
1230}
1231
1232/* A new DL-TBF is allocated and assigned through PCH.
1233 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1234 */
1235int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1236{
1237 const int8_t trx_no = -1;
1238 const bool single_slot = true;
1239 struct gprs_rlcmac_dl_tbf *dl_tbf;
1240
1241 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001242 if (!dl_tbf) {
1243 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1244 return -EBUSY;
1245 }
1246
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001247 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1248 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001249 return 0;
1250}
1251
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001252int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1253{
1254 struct timespec expire_time;
1255 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001256 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001257
1258 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1259
1260 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1261 if (!llc_msg)
1262 return -ENOMEM;
1263
1264 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1265 memcpy(msgb_put(llc_msg, len), data, len);
1266 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1267 ms_start_llc_timer(ms);
1268
1269 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001270 if (dl_tbf) {
1271 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1272 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001273 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001274 }
1275 } else {
1276 /* Check if we can create a DL TBF to start sending the enqueued
1277 * data. Otherwise it will be triggered later when it is reachable
1278 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001279 if (ms_is_reachable_for_dl_ass(ms)) {
1280 if (ms_ul_tbf(ms))
1281 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1282 else
1283 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1284 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001285 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001286 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001287}