blob: edb6fba954a36e761db852cc4539e1157281ee4b [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 Pedrol8abe13c2022-10-21 18:49:48 +020027#include "gprs_ms_storage.h"
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010028
29#include <time.h>
30
31#include <osmocom/core/talloc.h>
32#include <osmocom/core/utils.h>
33#include <osmocom/core/timer.h>
34#include <osmocom/gsm/protocol/gsm_04_08.h>
35#include <osmocom/gsm/gsm48.h>
36#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010037#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010038#include "coding_scheme.h"
39
40#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
41
42extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010043static unsigned int next_ms_ctr_group_id;
44
45static const struct rate_ctr_desc ms_ctr_description[] = {
46 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
47};
48
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010049static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010050 .group_name_prefix = "pcu:ms",
51 .group_description = "MS Statistics",
52 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
53 .num_ctr = ARRAY_SIZE(ms_ctr_description),
54 .ctr_desc = ms_ctr_description,
55};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010056
57static int64_t now_msec()
58{
59 struct timespec ts;
60 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
61
62 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
63}
64
65void gprs_default_cb_ms_idle(struct GprsMs *ms)
66{
67 talloc_free(ms);
68}
69
70void gprs_default_cb_ms_active(struct GprsMs *ms)
71{
72 /* do nothing */
73}
74
75static struct gpr_ms_callback gprs_default_cb = {
76 .ms_idle = gprs_default_cb_ms_idle,
77 .ms_active = gprs_default_cb_ms_active,
78};
79
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010080static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010081{
82 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010083 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010084
85 if (ms->timer.data) {
86 ms->timer.data = NULL;
87 ms_unref(ms);
88 }
89}
90
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020091static void ms_llc_timer_cb(void *_ms)
92{
93 struct GprsMs *ms = _ms;
94 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
95
96 if (!dl_tbf)
97 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +020098 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020099 return;
100
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200101 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200102
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200103 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200104}
105
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100106static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100107struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100108{
109 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200110 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100111
112 talloc_set_destructor(ms, ms_talloc_destructor);
113
114 ms->bts = bts;
115 ms->cb = gprs_default_cb;
116 ms->tlli = tlli;
117 ms->new_ul_tlli = GSM_RESERVED_TMSI;
118 ms->new_dl_tlli = GSM_RESERVED_TMSI;
119 ms->ta = GSM48_TA_INVALID;
120 ms->current_cs_ul = UNKNOWN;
121 ms->current_cs_dl = UNKNOWN;
122 ms->is_idle = true;
123 INIT_LLIST_HEAD(&ms->list);
124 INIT_LLIST_HEAD(&ms->old_tbfs);
125
126 int codel_interval = LLC_CODEL_USE_DEFAULT;
127
128 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
129
130 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200131 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200132 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200133 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
134 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100135
136 ms_set_mode(ms, GPRS);
137
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200138 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200139 if (codel_interval == LLC_CODEL_USE_DEFAULT)
140 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
141 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100143 ms->last_cs_not_low = now_msec();
144 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100145
146 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
147 if (!ms->ctrs)
148 goto free_ret;
149
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100150 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100151free_ret:
152 talloc_free(ms);
153 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100154}
155
156static int ms_talloc_destructor(struct GprsMs *ms)
157{
158 struct llist_item *pos, *tmp;
159
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100160 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100161
162 ms_set_reserved_slots(ms, NULL, 0, 0);
163
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700164 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100165
166 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200167 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100168 ms->ul_tbf = NULL;
169 }
170
171 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200172 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100173 ms->dl_tbf = NULL;
174 }
175
176 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
177 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
178 tbf_set_ms(tbf, NULL);
179 }
180
181 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200182 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100183
184 if (ms->ctrs)
185 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100186 return 0;
187}
188
189
190void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
191{
192 if (cb)
193 ms->cb = *cb;
194 else
195 ms->cb = gprs_default_cb;
196}
197
198static void ms_update_status(struct GprsMs *ms)
199{
200 if (ms->ref > 0)
201 return;
202
203 if (ms_is_idle(ms) && !ms->is_idle) {
204 ms->is_idle = true;
205 ms->cb.ms_idle(ms);
206 /* this can be deleted by now, do not access it */
207 return;
208 }
209
210 if (!ms_is_idle(ms) && ms->is_idle) {
211 ms->is_idle = false;
212 ms->cb.ms_active(ms);
213 }
214}
215
216struct GprsMs *ms_ref(struct GprsMs *ms)
217{
218 ms->ref += 1;
219 return ms;
220}
221
222void ms_unref(struct GprsMs *ms)
223{
224 OSMO_ASSERT(ms->ref >= 0);
225 ms->ref -= 1;
226 if (ms->ref == 0)
227 ms_update_status(ms);
228}
229
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100230static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100231{
232 if (ms->delay == 0)
233 return;
234
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100235 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
236
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100237 if (!ms->timer.data)
238 ms->timer.data = ms_ref(ms);
239
240 osmo_timer_schedule(&ms->timer, ms->delay, 0);
241}
242
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100243static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100244{
245 if (!ms->timer.data)
246 return;
247
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100248 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
249
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250 osmo_timer_del(&ms->timer);
251 ms->timer.data = NULL;
252 ms_unref(ms);
253}
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
306static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
307{
308 if (ms->ul_tbf == tbf)
309 return;
310
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100311 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 +0100312
313 ms_ref(ms);
314
315 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200316 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100317
318 ms->ul_tbf = tbf;
319
320 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100321 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100322
323 ms_unref(ms);
324}
325
326static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
327{
328 if (ms->dl_tbf == tbf)
329 return;
330
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100331 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 +0100332
333 ms_ref(ms);
334
335 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200336 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100337
338 ms->dl_tbf = tbf;
339
340 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100341 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100342
343 ms_unref(ms);
344}
345
346void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
347{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200348 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200349 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200350 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200351 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100352}
353
354void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
355{
356 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
357 ms->ul_tbf = NULL;
358 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
359 ms->dl_tbf = NULL;
360 } else {
361 bool found = false;
362
363 struct llist_item *pos, *tmp;
364 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
365 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
366 if (tmp_tbf == tbf) {
367 llist_del(&pos->list);
368 found = true;
369 break;
370 }
371 }
372
373 /* Protect against recursive calls via set_ms() */
374 if (!found)
375 return;
376 }
377
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100378 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
379 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100380
381 if (tbf_ms(tbf) == ms)
382 tbf_set_ms(tbf, NULL);
383
384 if (!ms->dl_tbf && !ms->ul_tbf) {
385 ms_set_reserved_slots(ms, NULL, 0, 0);
386
387 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100388 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100389 }
390
391 ms_update_status(ms);
392}
393
394void ms_reset(struct GprsMs *ms)
395{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100396 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100397
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100398 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100399
400 ms->tlli = GSM_RESERVED_TMSI;
401 ms->new_dl_tlli = ms->tlli;
402 ms->new_ul_tlli = ms->tlli;
403 ms->imsi[0] = '\0';
404}
405
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200406/* This function should be called on the MS object of a TBF each time an RLCMAC
407 * block is received for it with TLLI information.
408 * Besides updating the TLLI field on the MS object, it also seeks for other MS
409 * objects in the store and merges them into the current MS object. The MS
410 * duplication happened because we don't learn the TLLI of the created TBF until
411 * a later point. */
412void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
413{
414 struct GprsMs *old_ms = NULL;
415
416 if (tlli == GSM_RESERVED_TMSI)
417 return;
418
419 /* When the TLLI does not match the ms, check if there is another
420 * MS object that belongs to that TLLI and if yes make sure one of them
421 * gets deleted. */
422 if (!ms_check_tlli(ms, tlli))
423 old_ms = ms_store_get_ms(bts_ms_store(ms->bts), tlli, GSM_RESERVED_TMSI, NULL);
424
425 ms_set_tlli(ms, tlli);
426
427 if (old_ms)
428 ms_merge_and_clear_ms(ms, old_ms);
429 /* old_ms may no longer be available here */
430}
431
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100432/* Merge 'old_ms' object into 'ms' object.
433 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
434void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100435{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100436 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100437 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100438 ms_ref(old_ms);
439
440 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
441
442 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100443
444 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
445 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
446
447 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
448 ms_set_ms_class(ms, ms_ms_class(old_ms));
449
450 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
451 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
452
453 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
454
Pau Espin Pedrol677bebb2022-10-31 13:56:33 +0100455 if (!ms_dl_tbf(ms) && ms_dl_tbf(old_ms)) {
456 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(old_ms);
457 LOGPTBFDL(dl_tbf, LOGL_NOTICE,
458 "Merge MS: Move DL TBF: %s => %s\n",
459 old_ms_name, ms_name(ms));
460 /* Move the DL TBF to the new MS */
461 tbf_set_ms(dl_tbf_as_tbf(dl_tbf), ms);
462 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100463
464 /* Clean up the old MS object */
465 /* TODO: Use timer? */
466 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
467 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
468 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
469 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
470
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100471 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100472
473 ms_unref(old_ms);
474}
475
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100476/* Apply changes to the TLLI directly, used interally by functions below: */
477static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
478{
479 ms->tlli = tlli;
480 ms->new_dl_tlli = GSM_RESERVED_TMSI;
481 ms->new_ul_tlli = GSM_RESERVED_TMSI;
482
483 /* Update TBF FSM names: */
484 if (ms->ul_tbf)
485 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
486 if (ms->dl_tbf)
487 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
488}
489
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200490/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100491void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
492{
493 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
494 return;
495
496 if (tlli != ms->new_dl_tlli) {
497 LOGP(DRLCMAC, LOGL_INFO,
498 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
499 "not yet confirmed\n",
500 ms_tlli(ms), tlli);
501 ms->new_ul_tlli = tlli;
502 return;
503 }
504
505 LOGP(DRLCMAC, LOGL_INFO,
506 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
507 "already confirmed partly\n",
508 ms->tlli, tlli);
509
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100510 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100511}
512
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200513/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100514bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
515{
516 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
517 return false;
518
519 if (tlli != ms->new_ul_tlli) {
520 /* The MS has not sent a message with the new TLLI, which may
521 * happen according to the spec [TODO: add reference]. */
522
523 LOGP(DRLCMAC, LOGL_INFO,
524 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
525 "partly confirmed\n", tlli);
526 /* Use the network's idea of TLLI as candidate, this does not
527 * change the result value of tlli() */
528 ms->new_dl_tlli = tlli;
529 return false;
530 }
531
532 LOGP(DRLCMAC, LOGL_INFO,
533 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
534
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100535 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100536
537 return true;
538}
539
540void ms_set_imsi(struct GprsMs *ms, const char *imsi)
541{
542 if (!imsi) {
543 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
544 return;
545 }
546
547 if (imsi[0] && strlen(imsi) < 3) {
548 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
549 imsi);
550 return;
551 }
552
553 if (strcmp(imsi, ms->imsi) == 0)
554 return;
555
556 LOGP(DRLCMAC, LOGL_INFO,
557 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
558 ms_tlli(ms), ms->imsi, imsi);
559
560 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
561 /* Check if we are going to store a different MS object with already
562 existing IMSI. This is probably a bug in code calling this function,
563 since it should take care of this explicitly */
564 if (old_ms) {
565 /* We cannot find ms->ms by IMSI since we know that it has a
566 * different IMSI */
567 OSMO_ASSERT(old_ms != ms);
568
569 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
570 "IMSI '%s' was already assigned to another "
571 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
572 imsi, ms_tlli(old_ms));
573
574 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100575 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100576 }
577
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100578 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100579 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100580
581 /* Update TBF FSM names: */
582 if (ms->ul_tbf)
583 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
584 if (ms->dl_tbf)
585 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100586}
587
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100588uint16_t ms_paging_group(struct GprsMs *ms)
589{
590 uint16_t pgroup;
591 if (!ms_imsi_is_valid(ms))
592 return 0; /* 000 is the special "all paging" group */
593 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
594 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
595 return 0;
596 }
597 return pgroup;
598}
599
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100600void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
601{
602 if (ta_ == ms->ta)
603 return;
604
605 if (gsm48_ta_is_valid(ta_)) {
606 LOGP(DRLCMAC, LOGL_INFO,
607 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
608 ms_tlli(ms), ms->ta, ta_);
609 ms->ta = ta_;
610 } else
611 LOGP(DRLCMAC, LOGL_NOTICE,
612 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
613 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
614}
615
616void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
617{
618 if (ms_class_ == ms->ms_class)
619 return;
620
621 LOGP(DRLCMAC, LOGL_INFO,
622 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
623 ms_tlli(ms), ms->ms_class, ms_class_);
624
625 ms->ms_class = ms_class_;
626}
627
628void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
629{
630 if (ms_class_ == ms->egprs_ms_class)
631 return;
632
633 LOGP(DRLCMAC, LOGL_INFO,
634 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
635 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
636
637 ms->egprs_ms_class = ms_class_;
638
639 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
640 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
641 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
642 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
643 return;
644 }
645
646 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
647 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
648 ms_mode(ms) != EGPRS)
649 {
650 ms_set_mode(ms, EGPRS_GMSK);
651 } else {
652 ms_set_mode(ms, EGPRS);
653 }
654 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
655}
656
657void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
658{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100659 int64_t now;
660 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100661 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100662
663 if (error_rate < 0)
664 return;
665
666 now = now_msec();
667
668 /* TODO: Check for TBF direction */
669 /* TODO: Support different CS values for UL and DL */
670
671 ms->nack_rate_dl = error_rate;
672
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100673 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100674 if (mcs_chan_code(ms->current_cs_dl) > 0) {
675 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
676 LOGP(DRLCMACDL, LOGL_INFO,
677 "MS (IMSI %s): High error rate %d%%, "
678 "reducing CS level to %s\n",
679 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
680 ms->last_cs_not_low = now;
681 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100682 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100683 if (ms->current_cs_dl < max_cs_dl) {
684 if (now - ms->last_cs_not_low > 1000) {
685 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
686
687 LOGP(DRLCMACDL, LOGL_INFO,
688 "MS (IMSI %s): Low error rate %d%%, "
689 "increasing DL CS level to %s\n",
690 ms_imsi(ms), error_rate,
691 mcs_name(ms->current_cs_dl));
692 ms->last_cs_not_low = now;
693 } else {
694 LOGP(DRLCMACDL, LOGL_DEBUG,
695 "MS (IMSI %s): Low error rate %d%%, "
696 "ignored (within blocking period)\n",
697 ms_imsi(ms), error_rate);
698 }
699 }
700 } else {
701 LOGP(DRLCMACDL, LOGL_DEBUG,
702 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
703 ms_imsi(ms), error_rate);
704 ms->last_cs_not_low = now;
705 }
706}
707
708enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
709{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100710 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100711 OSMO_ASSERT(ms->bts != NULL);
712
713 if (mcs_is_gprs(ms->current_cs_ul)) {
714 if (!bts_max_cs_ul(ms->bts)) {
715 return CS4;
716 }
717
718 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
719 }
720
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100721 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
722 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
723 cs = MCS4;
724 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100725}
726
727void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
728{
729 ms->current_cs_dl = scheme;
730}
731
732enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
733{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100734 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100735 OSMO_ASSERT(ms->bts != NULL);
736
737 if (mcs_is_gprs(ms->current_cs_dl)) {
738 if (!bts_max_cs_dl(ms->bts)) {
739 return CS4;
740 }
741
742 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
743 }
744
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100745 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
746 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
747 cs = MCS4;
748 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100749}
750
751void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
752{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100753 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
754
755 int old_link_qual;
756 int low;
757 int high;
758 enum CodingScheme new_cs_ul = ms->current_cs_ul;
759 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
760
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100761 if (!max_cs_ul) {
762 LOGP(DRLCMACMEAS, LOGL_ERROR,
763 "max_cs_ul cannot be derived (current UL CS: %s)\n",
764 mcs_name(ms->current_cs_ul));
765 return;
766 }
767
768 if (!ms->current_cs_ul) {
769 LOGP(DRLCMACMEAS, LOGL_ERROR,
770 "Unable to update UL (M)CS because it's not set: %s\n",
771 mcs_name(ms->current_cs_ul));
772 return;
773 }
774
775 if (!meas->have_link_qual) {
776 LOGP(DRLCMACMEAS, LOGL_ERROR,
777 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
778 mcs_name(ms->current_cs_ul));
779 return;
780 }
781
782 if (mcs_is_gprs(ms->current_cs_ul)) {
783 if (current_cs >= MAX_GPRS_CS)
784 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100785 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
786 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100787 } else if (mcs_is_edge(ms->current_cs_ul)) {
788 if (current_cs >= MAX_EDGE_MCS)
789 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100790 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
791 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100792 } else {
793 LOGP(DRLCMACMEAS, LOGL_ERROR,
794 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
795 mcs_name(ms->current_cs_ul));
796 return;
797 }
798
799 /* To avoid rapid changes of the coding scheme, we also take
800 * the old link quality value into account (if present). */
801 if (ms->l1_meas.have_link_qual)
802 old_link_qual = ms->l1_meas.link_qual;
803 else
804 old_link_qual = meas->link_qual;
805
806 if (meas->link_qual < low && old_link_qual < low)
807 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
808 else if (meas->link_qual > high && old_link_qual > high &&
809 ms->current_cs_ul < max_cs_ul)
810 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
811
812 if (ms->current_cs_ul != new_cs_ul) {
813 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
814 "Link quality %ddB (old %ddB) left window [%d, %d], "
815 "modifying uplink CS level: %s -> %s\n",
816 meas->link_qual, old_link_qual,
817 low, high,
818 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
819
820 ms->current_cs_ul = new_cs_ul;
821 }
822}
823
824void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
825{
826 unsigned i;
827
828 ms_update_cs_ul(ms, meas);
829
830 if (meas->have_rssi)
831 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
832 if (meas->have_bto)
833 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
834 if (meas->have_ber)
835 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
836 if (meas->have_link_qual)
837 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
838
839 if (meas->have_ms_rx_qual)
840 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
841 if (meas->have_ms_c_value)
842 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
843 if (meas->have_ms_sign_var)
844 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
845
846 if (meas->have_ms_i_level) {
847 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
848 if (meas->ts[i].have_ms_i_level)
849 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
850 else
851 ms->l1_meas.ts[i].have_ms_i_level = 0;
852 }
853 }
854}
855
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100856/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
857enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100858{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100859 enum CodingScheme orig_cs = ms->current_cs_dl;
860 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100861 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100862 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100863
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100864 /* It could be that a TBF requests a GPRS CS despite the MS currently
865 being upgraded to EGPRS (hence reporting MCS). That could happen
866 because the TBF was created early in the process where we didn't have
867 yet enough information about the MS, and only AFTER it was created we
868 upgraded the MS to be EGPRS capable.
869 As a result, when the MS is queried for the target CS here, we could be
870 returning an MCS despite the current TBF being established as GPRS,
871 but we rather stick to the TBF type we assigned to the MS rather than
872 magically sending EGPRS data blocks to a GPRS TBF.
873 It could also be that the caller requests specific MCS kind
874 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
875 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
876 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
877 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
878 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
879 MCS1;
880 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
881 int i;
882 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
883 cs -= (MCS1 - CS1); /* MCSx -> CSx */
884 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
885 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
886 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
887 cs = CS1 + i;
888 } else {
889 cs = orig_cs;
890 }
891
892 if (orig_cs != cs)
893 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
894 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
895 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100896
897 unencoded_octets = llc_queue_octets(&ms->llc_queue);
898
899 /* If the DL TBF is active, add number of unencoded chunk octets */
900 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200901 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100902
903 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100904 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100905 return cs;
906
907 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100908 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100909 return cs;
910
911 /* The throughput would probably be better if the CS level was reduced */
912 mcs_dec_kind(&cs, ms_mode(ms));
913
914 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
915 if (cs == CS2)
916 mcs_dec_kind(&cs, ms_mode(ms));
917
918 return cs;
919}
920
921int ms_first_common_ts(const struct GprsMs *ms)
922{
923 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200924 return tbf_first_common_ts(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100925
926 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200927 return tbf_first_common_ts(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100928
929 return -1;
930}
931
932uint8_t ms_dl_slots(const struct GprsMs *ms)
933{
934 uint8_t slots = 0;
935
936 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200937 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100938
939 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200940 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100941
942 return slots;
943}
944
945uint8_t ms_ul_slots(const struct GprsMs *ms)
946{
947 uint8_t slots = 0;
948
949 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200950 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100951
952 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200953 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100954
955 return slots;
956}
957
958uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
959{
960 uint8_t slots = 0;
961
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200962 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
963 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 +0100964
965 if (!is_dl_active && !is_ul_active)
966 return 0;
967
968 /* see TS 44.060, 8.1.1.2.2 */
969 if (is_dl_active && !is_ul_active)
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 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200972 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100973 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200974 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
975 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100976
977 /* Assume a multislot class 1 device */
978 /* TODO: For class 2 devices, this could be removed */
979 slots = pcu_lsb(slots);
980
981 return slots;
982}
983
984void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
985 uint8_t ul_slots, uint8_t dl_slots)
986{
987 if (ms->current_trx) {
988 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
989 ms->reserved_dl_slots);
990 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
991 ms->reserved_ul_slots);
992 ms->reserved_dl_slots = 0;
993 ms->reserved_ul_slots = 0;
994 }
995 ms->current_trx = trx;
996 if (trx) {
997 ms->reserved_dl_slots = dl_slots;
998 ms->reserved_ul_slots = ul_slots;
999 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
1000 ms->reserved_dl_slots);
1001 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
1002 ms->reserved_ul_slots);
1003 }
1004}
1005
1006struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1007{
1008 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001009 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1010 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001011 }
1012
1013 return NULL;
1014}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001015
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001016const char *ms_name(const struct GprsMs *ms)
1017{
1018 static char _ms_name_buf[128];
1019 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1020}
1021
1022char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1023{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001024 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1025 uint32_t tlli = ms_tlli(ms);
1026
1027 OSMO_STRBUF_PRINTF(sb, "MS(");
1028 if (ms_imsi_is_valid(ms))
1029 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1030 if (tlli != GSM_RESERVED_TMSI)
1031 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1032 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1033 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1034 if (ms->ul_tbf)
1035 OSMO_STRBUF_PRINTF(sb, ":UL");
1036 if (ms->dl_tbf)
1037 OSMO_STRBUF_PRINTF(sb, ":DL");
1038
1039 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001040 return buf;
1041}
1042
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001043int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1044{
1045 if (!ms->nacc)
1046 ms->nacc = nacc_fsm_alloc(ms);
1047 if (!ms->nacc)
1048 return -EINVAL;
1049 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1050}
1051
1052bool ms_nacc_rts(const struct GprsMs *ms)
1053{
1054 if (!ms->nacc)
1055 return false;
1056 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1057 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1058 return true;
1059 return false;
1060}
1061
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001062struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001063{
1064 int rc;
1065 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1066
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001067 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1068 .tbf = tbf,
1069 .fn = fn,
1070 .ts = ts,
1071 .msg = NULL,
1072 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001073
1074 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1075 if (rc != 0 || !data_ctx.msg)
1076 return NULL;
1077 return data_ctx.msg;
1078}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001079
1080static void ms_start_llc_timer(struct GprsMs *ms)
1081{
1082 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1083 struct timespec tv;
1084 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1085 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1086 }
1087}
1088
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001089/* Can we get to send a DL TBF ass to the MS? */
1090static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1091{
1092 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1093
1094 /* This function assumes it is called when no DL TBF is present */
1095 OSMO_ASSERT(!ms_dl_tbf(ms));
1096
1097 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1098 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1099 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1100 * completed on the mobile station side." */
1101 /* The possible uplink TBF is used to trigger downlink assignment:
1102 * - If there is no uplink TBF the MS is potentially in packet idle mode
1103 * and hence assignment will be done over CCCH (PCH)
1104 * - If there's an uplink TBF but it is finished (waiting for last PKT
1105 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1106 * have no ways to contact the MS right now. Assignment will be delayed
1107 * until PKT CTRL ACK is received and the TBF is released at the MS side
1108 * (then assignment goes through PCH).
1109 */
1110 if (!ul_tbf)
1111 return true;
1112 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1113 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1114 return true;
1115
1116 return false;
1117
1118}
1119
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001120/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1121 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1122 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1123 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1124 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1125 */
1126struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1127{
1128 const bool single_slot = false;
1129 struct gprs_rlcmac_ul_tbf *ul_tbf;
1130
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001131 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001132 if (!ul_tbf) {
1133 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1134 /* Caller will most probably send a Imm Ass Reject after return */
1135 return NULL;
1136 }
1137 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1138 /* Contention resolution is considered to be done since TLLI is known in MS */
1139 return ul_tbf;
1140}
1141
1142/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1143 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1144struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1145{
1146 const int8_t trx_no = -1;
1147 const bool single_slot = true;
1148 struct gprs_rlcmac_ul_tbf *ul_tbf;
1149
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001150 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001151 if (!ul_tbf) {
1152 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1153 /* Caller will most probably send a Imm Ass Reject after return */
1154 return NULL;
1155 }
1156 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1157 return ul_tbf;
1158}
1159
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001160/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1161 * "tbf" may be either a UL-TBF or a DL-TBF.
1162 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1163 */
1164int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001165{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001166 OSMO_ASSERT(tbf);
1167 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1168 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001169 struct gprs_rlcmac_dl_tbf *dl_tbf;
1170
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001171 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1172 if (!dl_tbf) {
1173 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1174 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001175 }
1176
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001177 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1178 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1179 return 0;
1180}
1181
1182/* A new DL-TBF is allocated and assigned through PCH.
1183 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1184 */
1185int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1186{
1187 const int8_t trx_no = -1;
1188 const bool single_slot = true;
1189 struct gprs_rlcmac_dl_tbf *dl_tbf;
1190
1191 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001192 if (!dl_tbf) {
1193 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1194 return -EBUSY;
1195 }
1196
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001197 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1198 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001199 return 0;
1200}
1201
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001202int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1203{
1204 struct timespec expire_time;
1205 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001206 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001207
1208 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1209
1210 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1211 if (!llc_msg)
1212 return -ENOMEM;
1213
1214 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1215 memcpy(msgb_put(llc_msg, len), data, len);
1216 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1217 ms_start_llc_timer(ms);
1218
1219 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001220 if (dl_tbf) {
1221 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1222 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001223 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001224 }
1225 } else {
1226 /* Check if we can create a DL TBF to start sending the enqueued
1227 * data. Otherwise it will be triggered later when it is reachable
1228 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001229 if (ms_is_reachable_for_dl_ass(ms)) {
1230 if (ms_ul_tbf(ms))
1231 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1232 else
1233 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1234 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001235 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001236 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001237}