blob: f1b0c6a8d250dc8936b799a5a0c426978a2387ef [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);
530 }
531
532
533 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
534}
535
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100536uint16_t ms_paging_group(struct GprsMs *ms)
537{
538 uint16_t pgroup;
539 if (!ms_imsi_is_valid(ms))
540 return 0; /* 000 is the special "all paging" group */
541 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
542 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
543 return 0;
544 }
545 return pgroup;
546}
547
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100548void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
549{
550 if (ta_ == ms->ta)
551 return;
552
553 if (gsm48_ta_is_valid(ta_)) {
554 LOGP(DRLCMAC, LOGL_INFO,
555 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
556 ms_tlli(ms), ms->ta, ta_);
557 ms->ta = ta_;
558 } else
559 LOGP(DRLCMAC, LOGL_NOTICE,
560 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
561 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
562}
563
564void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
565{
566 if (ms_class_ == ms->ms_class)
567 return;
568
569 LOGP(DRLCMAC, LOGL_INFO,
570 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
571 ms_tlli(ms), ms->ms_class, ms_class_);
572
573 ms->ms_class = ms_class_;
574}
575
576void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
577{
578 if (ms_class_ == ms->egprs_ms_class)
579 return;
580
581 LOGP(DRLCMAC, LOGL_INFO,
582 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
583 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
584
585 ms->egprs_ms_class = ms_class_;
586
587 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
588 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
589 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
590 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
591 return;
592 }
593
594 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
595 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
596 ms_mode(ms) != EGPRS)
597 {
598 ms_set_mode(ms, EGPRS_GMSK);
599 } else {
600 ms_set_mode(ms, EGPRS);
601 }
602 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
603}
604
605void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
606{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100607 int64_t now;
608 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100609 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100610
611 if (error_rate < 0)
612 return;
613
614 now = now_msec();
615
616 /* TODO: Check for TBF direction */
617 /* TODO: Support different CS values for UL and DL */
618
619 ms->nack_rate_dl = error_rate;
620
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100621 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100622 if (mcs_chan_code(ms->current_cs_dl) > 0) {
623 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
624 LOGP(DRLCMACDL, LOGL_INFO,
625 "MS (IMSI %s): High error rate %d%%, "
626 "reducing CS level to %s\n",
627 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
628 ms->last_cs_not_low = now;
629 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100630 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100631 if (ms->current_cs_dl < max_cs_dl) {
632 if (now - ms->last_cs_not_low > 1000) {
633 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
634
635 LOGP(DRLCMACDL, LOGL_INFO,
636 "MS (IMSI %s): Low error rate %d%%, "
637 "increasing DL CS level to %s\n",
638 ms_imsi(ms), error_rate,
639 mcs_name(ms->current_cs_dl));
640 ms->last_cs_not_low = now;
641 } else {
642 LOGP(DRLCMACDL, LOGL_DEBUG,
643 "MS (IMSI %s): Low error rate %d%%, "
644 "ignored (within blocking period)\n",
645 ms_imsi(ms), error_rate);
646 }
647 }
648 } else {
649 LOGP(DRLCMACDL, LOGL_DEBUG,
650 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
651 ms_imsi(ms), error_rate);
652 ms->last_cs_not_low = now;
653 }
654}
655
656enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
657{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100658 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100659 OSMO_ASSERT(ms->bts != NULL);
660
661 if (mcs_is_gprs(ms->current_cs_ul)) {
662 if (!bts_max_cs_ul(ms->bts)) {
663 return CS4;
664 }
665
666 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
667 }
668
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100669 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
670 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
671 cs = MCS4;
672 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100673}
674
675void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
676{
677 ms->current_cs_dl = scheme;
678}
679
680enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
681{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100682 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100683 OSMO_ASSERT(ms->bts != NULL);
684
685 if (mcs_is_gprs(ms->current_cs_dl)) {
686 if (!bts_max_cs_dl(ms->bts)) {
687 return CS4;
688 }
689
690 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
691 }
692
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100693 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
694 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
695 cs = MCS4;
696 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100697}
698
699void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
700{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100701 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
702
703 int old_link_qual;
704 int low;
705 int high;
706 enum CodingScheme new_cs_ul = ms->current_cs_ul;
707 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
708
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100709 if (!max_cs_ul) {
710 LOGP(DRLCMACMEAS, LOGL_ERROR,
711 "max_cs_ul cannot be derived (current UL CS: %s)\n",
712 mcs_name(ms->current_cs_ul));
713 return;
714 }
715
716 if (!ms->current_cs_ul) {
717 LOGP(DRLCMACMEAS, LOGL_ERROR,
718 "Unable to update UL (M)CS because it's not set: %s\n",
719 mcs_name(ms->current_cs_ul));
720 return;
721 }
722
723 if (!meas->have_link_qual) {
724 LOGP(DRLCMACMEAS, LOGL_ERROR,
725 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
726 mcs_name(ms->current_cs_ul));
727 return;
728 }
729
730 if (mcs_is_gprs(ms->current_cs_ul)) {
731 if (current_cs >= MAX_GPRS_CS)
732 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100733 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
734 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100735 } else if (mcs_is_edge(ms->current_cs_ul)) {
736 if (current_cs >= MAX_EDGE_MCS)
737 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100738 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
739 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100740 } else {
741 LOGP(DRLCMACMEAS, LOGL_ERROR,
742 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
743 mcs_name(ms->current_cs_ul));
744 return;
745 }
746
747 /* To avoid rapid changes of the coding scheme, we also take
748 * the old link quality value into account (if present). */
749 if (ms->l1_meas.have_link_qual)
750 old_link_qual = ms->l1_meas.link_qual;
751 else
752 old_link_qual = meas->link_qual;
753
754 if (meas->link_qual < low && old_link_qual < low)
755 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
756 else if (meas->link_qual > high && old_link_qual > high &&
757 ms->current_cs_ul < max_cs_ul)
758 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
759
760 if (ms->current_cs_ul != new_cs_ul) {
761 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
762 "Link quality %ddB (old %ddB) left window [%d, %d], "
763 "modifying uplink CS level: %s -> %s\n",
764 meas->link_qual, old_link_qual,
765 low, high,
766 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
767
768 ms->current_cs_ul = new_cs_ul;
769 }
770}
771
772void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
773{
774 unsigned i;
775
776 ms_update_cs_ul(ms, meas);
777
778 if (meas->have_rssi)
779 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
780 if (meas->have_bto)
781 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
782 if (meas->have_ber)
783 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
784 if (meas->have_link_qual)
785 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
786
787 if (meas->have_ms_rx_qual)
788 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
789 if (meas->have_ms_c_value)
790 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
791 if (meas->have_ms_sign_var)
792 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
793
794 if (meas->have_ms_i_level) {
795 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
796 if (meas->ts[i].have_ms_i_level)
797 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
798 else
799 ms->l1_meas.ts[i].have_ms_i_level = 0;
800 }
801 }
802}
803
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100804/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
805enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100806{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100807 enum CodingScheme orig_cs = ms->current_cs_dl;
808 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100809 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100810 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100811
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100812 /* It could be that a TBF requests a GPRS CS despite the MS currently
813 being upgraded to EGPRS (hence reporting MCS). That could happen
814 because the TBF was created early in the process where we didn't have
815 yet enough information about the MS, and only AFTER it was created we
816 upgraded the MS to be EGPRS capable.
817 As a result, when the MS is queried for the target CS here, we could be
818 returning an MCS despite the current TBF being established as GPRS,
819 but we rather stick to the TBF type we assigned to the MS rather than
820 magically sending EGPRS data blocks to a GPRS TBF.
821 It could also be that the caller requests specific MCS kind
822 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
823 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
824 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
825 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
826 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
827 MCS1;
828 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
829 int i;
830 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
831 cs -= (MCS1 - CS1); /* MCSx -> CSx */
832 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
833 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
834 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
835 cs = CS1 + i;
836 } else {
837 cs = orig_cs;
838 }
839
840 if (orig_cs != cs)
841 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
842 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
843 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100844
845 unencoded_octets = llc_queue_octets(&ms->llc_queue);
846
847 /* If the DL TBF is active, add number of unencoded chunk octets */
848 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200849 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100850
851 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100852 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100853 return cs;
854
855 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100856 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100857 return cs;
858
859 /* The throughput would probably be better if the CS level was reduced */
860 mcs_dec_kind(&cs, ms_mode(ms));
861
862 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
863 if (cs == CS2)
864 mcs_dec_kind(&cs, ms_mode(ms));
865
866 return cs;
867}
868
869int ms_first_common_ts(const struct GprsMs *ms)
870{
871 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200872 return tbf_first_common_ts(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100873
874 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200875 return tbf_first_common_ts(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100876
877 return -1;
878}
879
880uint8_t ms_dl_slots(const struct GprsMs *ms)
881{
882 uint8_t slots = 0;
883
884 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200885 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100886
887 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200888 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100889
890 return slots;
891}
892
893uint8_t ms_ul_slots(const struct GprsMs *ms)
894{
895 uint8_t slots = 0;
896
897 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200898 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100899
900 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200901 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100902
903 return slots;
904}
905
906uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
907{
908 uint8_t slots = 0;
909
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200910 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
911 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 +0100912
913 if (!is_dl_active && !is_ul_active)
914 return 0;
915
916 /* see TS 44.060, 8.1.1.2.2 */
917 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200918 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100919 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200920 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100921 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200922 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
923 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100924
925 /* Assume a multislot class 1 device */
926 /* TODO: For class 2 devices, this could be removed */
927 slots = pcu_lsb(slots);
928
929 return slots;
930}
931
932void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
933 uint8_t ul_slots, uint8_t dl_slots)
934{
935 if (ms->current_trx) {
936 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
937 ms->reserved_dl_slots);
938 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
939 ms->reserved_ul_slots);
940 ms->reserved_dl_slots = 0;
941 ms->reserved_ul_slots = 0;
942 }
943 ms->current_trx = trx;
944 if (trx) {
945 ms->reserved_dl_slots = dl_slots;
946 ms->reserved_ul_slots = ul_slots;
947 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
948 ms->reserved_dl_slots);
949 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
950 ms->reserved_ul_slots);
951 }
952}
953
954struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
955{
956 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200957 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
958 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100959 }
960
961 return NULL;
962}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100963
Pau Espin Pedrol3547d642022-10-21 15:00:08 +0200964const char *ms_name(const struct GprsMs *ms)
965{
966 static char _ms_name_buf[128];
967 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
968}
969
970char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
971{
972 snprintf(buf, buf_size - 1,
973 "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s)",
974 ms_tlli(ms), ms_imsi(ms), ms_ta(ms),
975 ms_ms_class(ms), ms_egprs_ms_class(ms),
976 ms_ul_tbf(ms) ? " UL" : "",
977 ms_dl_tbf(ms) ? " DL" : "");
978 buf[buf_size - 1] = '\0';
979 return buf;
980}
981
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100982int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
983{
984 if (!ms->nacc)
985 ms->nacc = nacc_fsm_alloc(ms);
986 if (!ms->nacc)
987 return -EINVAL;
988 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
989}
990
991bool ms_nacc_rts(const struct GprsMs *ms)
992{
993 if (!ms->nacc)
994 return false;
995 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
996 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
997 return true;
998 return false;
999}
1000
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001001struct 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 +01001002{
1003 int rc;
1004 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1005
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001006 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1007 .tbf = tbf,
1008 .fn = fn,
1009 .ts = ts,
1010 .msg = NULL,
1011 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001012
1013 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1014 if (rc != 0 || !data_ctx.msg)
1015 return NULL;
1016 return data_ctx.msg;
1017}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001018
1019static void ms_start_llc_timer(struct GprsMs *ms)
1020{
1021 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1022 struct timespec tv;
1023 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1024 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1025 }
1026}
1027
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001028/* Can we get to send a DL TBF ass to the MS? */
1029static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1030{
1031 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1032
1033 /* This function assumes it is called when no DL TBF is present */
1034 OSMO_ASSERT(!ms_dl_tbf(ms));
1035
1036 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1037 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1038 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1039 * completed on the mobile station side." */
1040 /* The possible uplink TBF is used to trigger downlink assignment:
1041 * - If there is no uplink TBF the MS is potentially in packet idle mode
1042 * and hence assignment will be done over CCCH (PCH)
1043 * - If there's an uplink TBF but it is finished (waiting for last PKT
1044 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1045 * have no ways to contact the MS right now. Assignment will be delayed
1046 * until PKT CTRL ACK is received and the TBF is released at the MS side
1047 * (then assignment goes through PCH).
1048 */
1049 if (!ul_tbf)
1050 return true;
1051 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1052 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1053 return true;
1054
1055 return false;
1056
1057}
1058
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001059/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1060 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1061 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1062 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1063 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1064 */
1065struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1066{
1067 const bool single_slot = false;
1068 struct gprs_rlcmac_ul_tbf *ul_tbf;
1069
1070 ul_tbf = tbf_alloc_ul_tbf(ms->bts, ms, use_trx, single_slot);
1071 if (!ul_tbf) {
1072 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1073 /* Caller will most probably send a Imm Ass Reject after return */
1074 return NULL;
1075 }
1076 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1077 /* Contention resolution is considered to be done since TLLI is known in MS */
1078 return ul_tbf;
1079}
1080
1081/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1082 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1083struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1084{
1085 const int8_t trx_no = -1;
1086 const bool single_slot = true;
1087 struct gprs_rlcmac_ul_tbf *ul_tbf;
1088
1089 ul_tbf = tbf_alloc_ul_tbf(ms->bts, ms, trx_no, single_slot);
1090 if (!ul_tbf) {
1091 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1092 /* Caller will most probably send a Imm Ass Reject after return */
1093 return NULL;
1094 }
1095 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1096 return ul_tbf;
1097}
1098
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001099/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1100 * "tbf" may be either a UL-TBF or a DL-TBF.
1101 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1102 */
1103int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001104{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001105 OSMO_ASSERT(tbf);
1106 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1107 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001108 struct gprs_rlcmac_dl_tbf *dl_tbf;
1109
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001110 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1111 if (!dl_tbf) {
1112 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1113 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001114 }
1115
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001116 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1117 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1118 return 0;
1119}
1120
1121/* A new DL-TBF is allocated and assigned through PCH.
1122 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1123 */
1124int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1125{
1126 const int8_t trx_no = -1;
1127 const bool single_slot = true;
1128 struct gprs_rlcmac_dl_tbf *dl_tbf;
1129
1130 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001131 if (!dl_tbf) {
1132 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1133 return -EBUSY;
1134 }
1135
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001136 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1137 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001138 return 0;
1139}
1140
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001141int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1142{
1143 struct timespec expire_time;
1144 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001145 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001146
1147 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1148
1149 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1150 if (!llc_msg)
1151 return -ENOMEM;
1152
1153 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1154 memcpy(msgb_put(llc_msg, len), data, len);
1155 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1156 ms_start_llc_timer(ms);
1157
1158 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001159 if (dl_tbf) {
1160 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1161 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001162 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001163 }
1164 } else {
1165 /* Check if we can create a DL TBF to start sending the enqueued
1166 * data. Otherwise it will be triggered later when it is reachable
1167 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001168 if (ms_is_reachable_for_dl_ass(ms)) {
1169 if (ms_ul_tbf(ms))
1170 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1171 else
1172 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1173 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001174 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001175 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001176}