blob: 5bf929e1045d7799c9c20767d4cc79acdcc3616d [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
64void gprs_default_cb_ms_idle(struct GprsMs *ms)
65{
66 talloc_free(ms);
67}
68
69void gprs_default_cb_ms_active(struct GprsMs *ms)
70{
71 /* do nothing */
72}
73
74static struct gpr_ms_callback gprs_default_cb = {
75 .ms_idle = gprs_default_cb_ms_idle,
76 .ms_active = gprs_default_cb_ms_active,
77};
78
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010079static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010080{
81 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010082 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010083
84 if (ms->timer.data) {
85 ms->timer.data = NULL;
86 ms_unref(ms);
87 }
88}
89
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020090static void ms_llc_timer_cb(void *_ms)
91{
92 struct GprsMs *ms = _ms;
93 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
94
95 if (!dl_tbf)
96 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +020097 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020098 return;
99
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200100 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200101
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200102 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200103}
104
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100105static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100106struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100107{
108 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200109 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100110
111 talloc_set_destructor(ms, ms_talloc_destructor);
112
113 ms->bts = bts;
114 ms->cb = gprs_default_cb;
115 ms->tlli = tlli;
116 ms->new_ul_tlli = GSM_RESERVED_TMSI;
117 ms->new_dl_tlli = GSM_RESERVED_TMSI;
118 ms->ta = GSM48_TA_INVALID;
119 ms->current_cs_ul = UNKNOWN;
120 ms->current_cs_dl = UNKNOWN;
121 ms->is_idle = true;
122 INIT_LLIST_HEAD(&ms->list);
123 INIT_LLIST_HEAD(&ms->old_tbfs);
124
125 int codel_interval = LLC_CODEL_USE_DEFAULT;
126
127 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
128
129 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200130 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200131 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200132 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
133 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100134
135 ms_set_mode(ms, GPRS);
136
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200137 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200138 if (codel_interval == LLC_CODEL_USE_DEFAULT)
139 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
140 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100141
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142 ms->last_cs_not_low = now_msec();
143 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100144
145 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
146 if (!ms->ctrs)
147 goto free_ret;
148
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100149 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100150free_ret:
151 talloc_free(ms);
152 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100153}
154
155static int ms_talloc_destructor(struct GprsMs *ms)
156{
157 struct llist_item *pos, *tmp;
158
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100159 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100160
161 ms_set_reserved_slots(ms, NULL, 0, 0);
162
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700163 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100164
165 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200166 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100167 ms->ul_tbf = NULL;
168 }
169
170 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200171 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100172 ms->dl_tbf = NULL;
173 }
174
175 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
176 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
177 tbf_set_ms(tbf, NULL);
178 }
179
180 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200181 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100182
183 if (ms->ctrs)
184 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100185 return 0;
186}
187
188
189void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
190{
191 if (cb)
192 ms->cb = *cb;
193 else
194 ms->cb = gprs_default_cb;
195}
196
197static void ms_update_status(struct GprsMs *ms)
198{
199 if (ms->ref > 0)
200 return;
201
202 if (ms_is_idle(ms) && !ms->is_idle) {
203 ms->is_idle = true;
204 ms->cb.ms_idle(ms);
205 /* this can be deleted by now, do not access it */
206 return;
207 }
208
209 if (!ms_is_idle(ms) && ms->is_idle) {
210 ms->is_idle = false;
211 ms->cb.ms_active(ms);
212 }
213}
214
215struct GprsMs *ms_ref(struct GprsMs *ms)
216{
217 ms->ref += 1;
218 return ms;
219}
220
221void ms_unref(struct GprsMs *ms)
222{
223 OSMO_ASSERT(ms->ref >= 0);
224 ms->ref -= 1;
225 if (ms->ref == 0)
226 ms_update_status(ms);
227}
228
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100229static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100230{
231 if (ms->delay == 0)
232 return;
233
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100234 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
235
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100236 if (!ms->timer.data)
237 ms->timer.data = ms_ref(ms);
238
239 osmo_timer_schedule(&ms->timer, ms->delay, 0);
240}
241
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100242static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100243{
244 if (!ms->timer.data)
245 return;
246
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100247 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
248
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100249 osmo_timer_del(&ms->timer);
250 ms->timer.data = NULL;
251 ms_unref(ms);
252}
253
254void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
255{
256 ms->mode = mode;
257
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100258 switch (ms->mode) {
259 case GPRS:
260 if (!mcs_is_gprs(ms->current_cs_ul)) {
261 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100262 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100263 if (!mcs_is_valid(ms->current_cs_ul))
264 ms->current_cs_ul = CS1;
265 }
266 if (!mcs_is_gprs(ms->current_cs_dl)) {
267 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100268 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100269 if (!mcs_is_valid(ms->current_cs_dl))
270 ms->current_cs_dl = CS1;
271 }
272 break;
273
274 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100275 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
276 ms->current_cs_ul = mcs_get_egprs_by_num(
277 ms->bts->initial_mcs_ul);
278 if (!mcs_is_valid(ms->current_cs_ul))
279 ms->current_cs_ul = MCS1;
280 }
281 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
282 ms->current_cs_dl = mcs_get_egprs_by_num(
283 ms->bts->initial_mcs_dl);
284 if (!mcs_is_valid(ms->current_cs_dl))
285 ms->current_cs_dl = MCS1;
286 }
287 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100288 case EGPRS:
289 if (!mcs_is_edge(ms->current_cs_ul)) {
290 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100291 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100292 if (!mcs_is_valid(ms->current_cs_ul))
293 ms->current_cs_ul = MCS1;
294 }
295 if (!mcs_is_edge(ms->current_cs_dl)) {
296 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100297 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100298 if (!mcs_is_valid(ms->current_cs_dl))
299 ms->current_cs_dl = MCS1;
300 }
301 break;
302 }
303}
304
305static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
306{
307 if (ms->ul_tbf == tbf)
308 return;
309
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100310 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 +0100311
312 ms_ref(ms);
313
314 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200315 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100316
317 ms->ul_tbf = tbf;
318
319 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100320 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100321
322 ms_unref(ms);
323}
324
325static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
326{
327 if (ms->dl_tbf == tbf)
328 return;
329
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100330 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 +0100331
332 ms_ref(ms);
333
334 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200335 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100336
337 ms->dl_tbf = tbf;
338
339 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100340 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100341
342 ms_unref(ms);
343}
344
345void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
346{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200347 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200348 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200349 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200350 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100351}
352
353void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
354{
355 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
356 ms->ul_tbf = NULL;
357 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
358 ms->dl_tbf = NULL;
359 } else {
360 bool found = false;
361
362 struct llist_item *pos, *tmp;
363 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
364 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
365 if (tmp_tbf == tbf) {
366 llist_del(&pos->list);
367 found = true;
368 break;
369 }
370 }
371
372 /* Protect against recursive calls via set_ms() */
373 if (!found)
374 return;
375 }
376
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100377 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
378 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100379
380 if (tbf_ms(tbf) == ms)
381 tbf_set_ms(tbf, NULL);
382
383 if (!ms->dl_tbf && !ms->ul_tbf) {
384 ms_set_reserved_slots(ms, NULL, 0, 0);
385
386 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100387 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100388 }
389
390 ms_update_status(ms);
391}
392
393void ms_reset(struct GprsMs *ms)
394{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100395 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100396
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100397 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100398
399 ms->tlli = GSM_RESERVED_TMSI;
400 ms->new_dl_tlli = ms->tlli;
401 ms->new_ul_tlli = ms->tlli;
402 ms->imsi[0] = '\0';
403}
404
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100405/* Merge 'old_ms' object into 'ms' object.
406 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
407void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100408{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100409 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100410 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100411 ms_ref(old_ms);
412
413 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
414
415 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100416
417 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
418 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
419
420 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
421 ms_set_ms_class(ms, ms_ms_class(old_ms));
422
423 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
424 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
425
426 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
427
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100428
429 /* Clean up the old MS object */
430 /* TODO: Use timer? */
431 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
432 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
433 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
434 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
435
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100436 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100437
438 ms_unref(old_ms);
439}
440
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200441/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100442void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
443{
444 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
445 return;
446
447 if (tlli != ms->new_dl_tlli) {
448 LOGP(DRLCMAC, LOGL_INFO,
449 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
450 "not yet confirmed\n",
451 ms_tlli(ms), tlli);
452 ms->new_ul_tlli = tlli;
453 return;
454 }
455
456 LOGP(DRLCMAC, LOGL_INFO,
457 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
458 "already confirmed partly\n",
459 ms->tlli, tlli);
460
461 ms->tlli = tlli;
462 ms->new_dl_tlli = GSM_RESERVED_TMSI;
463 ms->new_ul_tlli = GSM_RESERVED_TMSI;
464}
465
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200466/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100467bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
468{
469 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
470 return false;
471
472 if (tlli != ms->new_ul_tlli) {
473 /* The MS has not sent a message with the new TLLI, which may
474 * happen according to the spec [TODO: add reference]. */
475
476 LOGP(DRLCMAC, LOGL_INFO,
477 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
478 "partly confirmed\n", tlli);
479 /* Use the network's idea of TLLI as candidate, this does not
480 * change the result value of tlli() */
481 ms->new_dl_tlli = tlli;
482 return false;
483 }
484
485 LOGP(DRLCMAC, LOGL_INFO,
486 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
487
488 ms->tlli = tlli;
489 ms->new_dl_tlli = GSM_RESERVED_TMSI;
490 ms->new_ul_tlli = GSM_RESERVED_TMSI;
491
492 return true;
493}
494
495void ms_set_imsi(struct GprsMs *ms, const char *imsi)
496{
497 if (!imsi) {
498 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
499 return;
500 }
501
502 if (imsi[0] && strlen(imsi) < 3) {
503 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
504 imsi);
505 return;
506 }
507
508 if (strcmp(imsi, ms->imsi) == 0)
509 return;
510
511 LOGP(DRLCMAC, LOGL_INFO,
512 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
513 ms_tlli(ms), ms->imsi, imsi);
514
515 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
516 /* Check if we are going to store a different MS object with already
517 existing IMSI. This is probably a bug in code calling this function,
518 since it should take care of this explicitly */
519 if (old_ms) {
520 /* We cannot find ms->ms by IMSI since we know that it has a
521 * different IMSI */
522 OSMO_ASSERT(old_ms != ms);
523
524 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
525 "IMSI '%s' was already assigned to another "
526 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
527 imsi, ms_tlli(old_ms));
528
529 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100530 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100531 }
532
533
534 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
535}
536
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100537uint16_t ms_paging_group(struct GprsMs *ms)
538{
539 uint16_t pgroup;
540 if (!ms_imsi_is_valid(ms))
541 return 0; /* 000 is the special "all paging" group */
542 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
543 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
544 return 0;
545 }
546 return pgroup;
547}
548
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100549void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
550{
551 if (ta_ == ms->ta)
552 return;
553
554 if (gsm48_ta_is_valid(ta_)) {
555 LOGP(DRLCMAC, LOGL_INFO,
556 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
557 ms_tlli(ms), ms->ta, ta_);
558 ms->ta = ta_;
559 } else
560 LOGP(DRLCMAC, LOGL_NOTICE,
561 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
562 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
563}
564
565void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
566{
567 if (ms_class_ == ms->ms_class)
568 return;
569
570 LOGP(DRLCMAC, LOGL_INFO,
571 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
572 ms_tlli(ms), ms->ms_class, ms_class_);
573
574 ms->ms_class = ms_class_;
575}
576
577void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
578{
579 if (ms_class_ == ms->egprs_ms_class)
580 return;
581
582 LOGP(DRLCMAC, LOGL_INFO,
583 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
584 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
585
586 ms->egprs_ms_class = ms_class_;
587
588 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
589 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
590 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
591 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
592 return;
593 }
594
595 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
596 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
597 ms_mode(ms) != EGPRS)
598 {
599 ms_set_mode(ms, EGPRS_GMSK);
600 } else {
601 ms_set_mode(ms, EGPRS);
602 }
603 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
604}
605
606void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
607{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100608 int64_t now;
609 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100610 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100611
612 if (error_rate < 0)
613 return;
614
615 now = now_msec();
616
617 /* TODO: Check for TBF direction */
618 /* TODO: Support different CS values for UL and DL */
619
620 ms->nack_rate_dl = error_rate;
621
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100622 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100623 if (mcs_chan_code(ms->current_cs_dl) > 0) {
624 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
625 LOGP(DRLCMACDL, LOGL_INFO,
626 "MS (IMSI %s): High error rate %d%%, "
627 "reducing CS level to %s\n",
628 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
629 ms->last_cs_not_low = now;
630 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100631 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100632 if (ms->current_cs_dl < max_cs_dl) {
633 if (now - ms->last_cs_not_low > 1000) {
634 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
635
636 LOGP(DRLCMACDL, LOGL_INFO,
637 "MS (IMSI %s): Low error rate %d%%, "
638 "increasing DL CS level to %s\n",
639 ms_imsi(ms), error_rate,
640 mcs_name(ms->current_cs_dl));
641 ms->last_cs_not_low = now;
642 } else {
643 LOGP(DRLCMACDL, LOGL_DEBUG,
644 "MS (IMSI %s): Low error rate %d%%, "
645 "ignored (within blocking period)\n",
646 ms_imsi(ms), error_rate);
647 }
648 }
649 } else {
650 LOGP(DRLCMACDL, LOGL_DEBUG,
651 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
652 ms_imsi(ms), error_rate);
653 ms->last_cs_not_low = now;
654 }
655}
656
657enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
658{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100659 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100660 OSMO_ASSERT(ms->bts != NULL);
661
662 if (mcs_is_gprs(ms->current_cs_ul)) {
663 if (!bts_max_cs_ul(ms->bts)) {
664 return CS4;
665 }
666
667 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
668 }
669
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100670 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
671 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
672 cs = MCS4;
673 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100674}
675
676void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
677{
678 ms->current_cs_dl = scheme;
679}
680
681enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
682{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100683 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100684 OSMO_ASSERT(ms->bts != NULL);
685
686 if (mcs_is_gprs(ms->current_cs_dl)) {
687 if (!bts_max_cs_dl(ms->bts)) {
688 return CS4;
689 }
690
691 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
692 }
693
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100694 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
695 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
696 cs = MCS4;
697 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100698}
699
700void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
701{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100702 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
703
704 int old_link_qual;
705 int low;
706 int high;
707 enum CodingScheme new_cs_ul = ms->current_cs_ul;
708 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
709
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100710 if (!max_cs_ul) {
711 LOGP(DRLCMACMEAS, LOGL_ERROR,
712 "max_cs_ul cannot be derived (current UL CS: %s)\n",
713 mcs_name(ms->current_cs_ul));
714 return;
715 }
716
717 if (!ms->current_cs_ul) {
718 LOGP(DRLCMACMEAS, LOGL_ERROR,
719 "Unable to update UL (M)CS because it's not set: %s\n",
720 mcs_name(ms->current_cs_ul));
721 return;
722 }
723
724 if (!meas->have_link_qual) {
725 LOGP(DRLCMACMEAS, LOGL_ERROR,
726 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
727 mcs_name(ms->current_cs_ul));
728 return;
729 }
730
731 if (mcs_is_gprs(ms->current_cs_ul)) {
732 if (current_cs >= MAX_GPRS_CS)
733 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100734 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
735 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100736 } else if (mcs_is_edge(ms->current_cs_ul)) {
737 if (current_cs >= MAX_EDGE_MCS)
738 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100739 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
740 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100741 } else {
742 LOGP(DRLCMACMEAS, LOGL_ERROR,
743 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
744 mcs_name(ms->current_cs_ul));
745 return;
746 }
747
748 /* To avoid rapid changes of the coding scheme, we also take
749 * the old link quality value into account (if present). */
750 if (ms->l1_meas.have_link_qual)
751 old_link_qual = ms->l1_meas.link_qual;
752 else
753 old_link_qual = meas->link_qual;
754
755 if (meas->link_qual < low && old_link_qual < low)
756 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
757 else if (meas->link_qual > high && old_link_qual > high &&
758 ms->current_cs_ul < max_cs_ul)
759 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
760
761 if (ms->current_cs_ul != new_cs_ul) {
762 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
763 "Link quality %ddB (old %ddB) left window [%d, %d], "
764 "modifying uplink CS level: %s -> %s\n",
765 meas->link_qual, old_link_qual,
766 low, high,
767 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
768
769 ms->current_cs_ul = new_cs_ul;
770 }
771}
772
773void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
774{
775 unsigned i;
776
777 ms_update_cs_ul(ms, meas);
778
779 if (meas->have_rssi)
780 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
781 if (meas->have_bto)
782 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
783 if (meas->have_ber)
784 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
785 if (meas->have_link_qual)
786 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
787
788 if (meas->have_ms_rx_qual)
789 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
790 if (meas->have_ms_c_value)
791 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
792 if (meas->have_ms_sign_var)
793 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
794
795 if (meas->have_ms_i_level) {
796 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
797 if (meas->ts[i].have_ms_i_level)
798 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
799 else
800 ms->l1_meas.ts[i].have_ms_i_level = 0;
801 }
802 }
803}
804
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100805/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
806enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100807{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100808 enum CodingScheme orig_cs = ms->current_cs_dl;
809 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100810 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100811 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100812
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100813 /* It could be that a TBF requests a GPRS CS despite the MS currently
814 being upgraded to EGPRS (hence reporting MCS). That could happen
815 because the TBF was created early in the process where we didn't have
816 yet enough information about the MS, and only AFTER it was created we
817 upgraded the MS to be EGPRS capable.
818 As a result, when the MS is queried for the target CS here, we could be
819 returning an MCS despite the current TBF being established as GPRS,
820 but we rather stick to the TBF type we assigned to the MS rather than
821 magically sending EGPRS data blocks to a GPRS TBF.
822 It could also be that the caller requests specific MCS kind
823 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
824 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
825 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
826 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
827 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
828 MCS1;
829 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
830 int i;
831 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
832 cs -= (MCS1 - CS1); /* MCSx -> CSx */
833 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
834 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
835 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
836 cs = CS1 + i;
837 } else {
838 cs = orig_cs;
839 }
840
841 if (orig_cs != cs)
842 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
843 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
844 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100845
846 unencoded_octets = llc_queue_octets(&ms->llc_queue);
847
848 /* If the DL TBF is active, add number of unencoded chunk octets */
849 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200850 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100851
852 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100853 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100854 return cs;
855
856 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100857 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100858 return cs;
859
860 /* The throughput would probably be better if the CS level was reduced */
861 mcs_dec_kind(&cs, ms_mode(ms));
862
863 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
864 if (cs == CS2)
865 mcs_dec_kind(&cs, ms_mode(ms));
866
867 return cs;
868}
869
870int ms_first_common_ts(const struct GprsMs *ms)
871{
872 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200873 return tbf_first_common_ts(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100874
875 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200876 return tbf_first_common_ts(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100877
878 return -1;
879}
880
881uint8_t ms_dl_slots(const struct GprsMs *ms)
882{
883 uint8_t slots = 0;
884
885 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200886 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100887
888 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200889 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100890
891 return slots;
892}
893
894uint8_t ms_ul_slots(const struct GprsMs *ms)
895{
896 uint8_t slots = 0;
897
898 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200899 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100900
901 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200902 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100903
904 return slots;
905}
906
907uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
908{
909 uint8_t slots = 0;
910
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200911 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
912 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 +0100913
914 if (!is_dl_active && !is_ul_active)
915 return 0;
916
917 /* see TS 44.060, 8.1.1.2.2 */
918 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200919 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100920 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200921 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100922 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200923 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
924 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100925
926 /* Assume a multislot class 1 device */
927 /* TODO: For class 2 devices, this could be removed */
928 slots = pcu_lsb(slots);
929
930 return slots;
931}
932
933void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
934 uint8_t ul_slots, uint8_t dl_slots)
935{
936 if (ms->current_trx) {
937 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
938 ms->reserved_dl_slots);
939 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
940 ms->reserved_ul_slots);
941 ms->reserved_dl_slots = 0;
942 ms->reserved_ul_slots = 0;
943 }
944 ms->current_trx = trx;
945 if (trx) {
946 ms->reserved_dl_slots = dl_slots;
947 ms->reserved_ul_slots = ul_slots;
948 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
949 ms->reserved_dl_slots);
950 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
951 ms->reserved_ul_slots);
952 }
953}
954
955struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
956{
957 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200958 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
959 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100960 }
961
962 return NULL;
963}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100964
Pau Espin Pedrol3547d642022-10-21 15:00:08 +0200965const char *ms_name(const struct GprsMs *ms)
966{
967 static char _ms_name_buf[128];
968 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
969}
970
971char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
972{
973 snprintf(buf, buf_size - 1,
974 "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s)",
975 ms_tlli(ms), ms_imsi(ms), ms_ta(ms),
976 ms_ms_class(ms), ms_egprs_ms_class(ms),
977 ms_ul_tbf(ms) ? " UL" : "",
978 ms_dl_tbf(ms) ? " DL" : "");
979 buf[buf_size - 1] = '\0';
980 return buf;
981}
982
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100983int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
984{
985 if (!ms->nacc)
986 ms->nacc = nacc_fsm_alloc(ms);
987 if (!ms->nacc)
988 return -EINVAL;
989 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
990}
991
992bool ms_nacc_rts(const struct GprsMs *ms)
993{
994 if (!ms->nacc)
995 return false;
996 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
997 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
998 return true;
999 return false;
1000}
1001
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001002struct 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 +01001003{
1004 int rc;
1005 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1006
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001007 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1008 .tbf = tbf,
1009 .fn = fn,
1010 .ts = ts,
1011 .msg = NULL,
1012 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001013
1014 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1015 if (rc != 0 || !data_ctx.msg)
1016 return NULL;
1017 return data_ctx.msg;
1018}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001019
1020static void ms_start_llc_timer(struct GprsMs *ms)
1021{
1022 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1023 struct timespec tv;
1024 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1025 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1026 }
1027}
1028
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001029/* Can we get to send a DL TBF ass to the MS? */
1030static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1031{
1032 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1033
1034 /* This function assumes it is called when no DL TBF is present */
1035 OSMO_ASSERT(!ms_dl_tbf(ms));
1036
1037 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1038 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1039 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1040 * completed on the mobile station side." */
1041 /* The possible uplink TBF is used to trigger downlink assignment:
1042 * - If there is no uplink TBF the MS is potentially in packet idle mode
1043 * and hence assignment will be done over CCCH (PCH)
1044 * - If there's an uplink TBF but it is finished (waiting for last PKT
1045 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1046 * have no ways to contact the MS right now. Assignment will be delayed
1047 * until PKT CTRL ACK is received and the TBF is released at the MS side
1048 * (then assignment goes through PCH).
1049 */
1050 if (!ul_tbf)
1051 return true;
1052 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1053 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1054 return true;
1055
1056 return false;
1057
1058}
1059
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001060/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1061 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1062 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1063 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1064 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1065 */
1066struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1067{
1068 const bool single_slot = false;
1069 struct gprs_rlcmac_ul_tbf *ul_tbf;
1070
1071 ul_tbf = tbf_alloc_ul_tbf(ms->bts, ms, use_trx, single_slot);
1072 if (!ul_tbf) {
1073 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1074 /* Caller will most probably send a Imm Ass Reject after return */
1075 return NULL;
1076 }
1077 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1078 /* Contention resolution is considered to be done since TLLI is known in MS */
1079 return ul_tbf;
1080}
1081
1082/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1083 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1084struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1085{
1086 const int8_t trx_no = -1;
1087 const bool single_slot = true;
1088 struct gprs_rlcmac_ul_tbf *ul_tbf;
1089
1090 ul_tbf = tbf_alloc_ul_tbf(ms->bts, ms, trx_no, single_slot);
1091 if (!ul_tbf) {
1092 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1093 /* Caller will most probably send a Imm Ass Reject after return */
1094 return NULL;
1095 }
1096 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1097 return ul_tbf;
1098}
1099
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001100/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1101 * "tbf" may be either a UL-TBF or a DL-TBF.
1102 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1103 */
1104int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001105{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001106 OSMO_ASSERT(tbf);
1107 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1108 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001109 struct gprs_rlcmac_dl_tbf *dl_tbf;
1110
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001111 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1112 if (!dl_tbf) {
1113 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1114 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001115 }
1116
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001117 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1118 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1119 return 0;
1120}
1121
1122/* A new DL-TBF is allocated and assigned through PCH.
1123 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1124 */
1125int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1126{
1127 const int8_t trx_no = -1;
1128 const bool single_slot = true;
1129 struct gprs_rlcmac_dl_tbf *dl_tbf;
1130
1131 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001132 if (!dl_tbf) {
1133 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1134 return -EBUSY;
1135 }
1136
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001137 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1138 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001139 return 0;
1140}
1141
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001142int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1143{
1144 struct timespec expire_time;
1145 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001146 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001147
1148 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1149
1150 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1151 if (!llc_msg)
1152 return -ENOMEM;
1153
1154 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1155 memcpy(msgb_put(llc_msg, len), data, len);
1156 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1157 ms_start_llc_timer(ms);
1158
1159 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001160 if (dl_tbf) {
1161 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1162 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001163 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001164 }
1165 } else {
1166 /* Check if we can create a DL TBF to start sending the enqueued
1167 * data. Otherwise it will be triggered later when it is reachable
1168 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001169 if (ms_is_reachable_for_dl_ass(ms)) {
1170 if (ms_ul_tbf(ms))
1171 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1172 else
1173 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1174 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001175 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001176 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001177}