blob: f552d938b8be831b36899e5adc908ec98d890bc7 [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 Pedrolda971ee2020-12-16 15:59:45 +010026
27#include <time.h>
28
29#include <osmocom/core/talloc.h>
30#include <osmocom/core/utils.h>
31#include <osmocom/core/timer.h>
32#include <osmocom/gsm/protocol/gsm_04_08.h>
33#include <osmocom/gsm/gsm48.h>
34#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010035#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010036#include "coding_scheme.h"
37
38#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
39
40extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010041static unsigned int next_ms_ctr_group_id;
42
43static const struct rate_ctr_desc ms_ctr_description[] = {
44 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
45};
46
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010047static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010048 .group_name_prefix = "pcu:ms",
49 .group_description = "MS Statistics",
50 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
51 .num_ctr = ARRAY_SIZE(ms_ctr_description),
52 .ctr_desc = ms_ctr_description,
53};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010054
55static int64_t now_msec()
56{
57 struct timespec ts;
58 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
59
60 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
61}
62
63void gprs_default_cb_ms_idle(struct GprsMs *ms)
64{
65 talloc_free(ms);
66}
67
68void gprs_default_cb_ms_active(struct GprsMs *ms)
69{
70 /* do nothing */
71}
72
73static struct gpr_ms_callback gprs_default_cb = {
74 .ms_idle = gprs_default_cb_ms_idle,
75 .ms_active = gprs_default_cb_ms_active,
76};
77
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010078static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010079{
80 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +010081 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010082
83 if (ms->timer.data) {
84 ms->timer.data = NULL;
85 ms_unref(ms);
86 }
87}
88
Pau Espin Pedrol14beef62022-10-26 19:44:07 +020089static void ms_llc_timer_cb(void *_ms)
90{
91 struct GprsMs *ms = _ms;
92 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
93
94 if (!dl_tbf)
95 return;
96 if (tbf_state((const struct gprs_rlcmac_tbf *)dl_tbf) != TBF_ST_FLOW)
97 return;
98
99 LOGPTBFDL((const struct gprs_rlcmac_tbf *)dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
100
101 tbf_dl_request_dl_ack(dl_tbf);
102}
103
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100104static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100105struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100106{
107 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200108 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100109
110 talloc_set_destructor(ms, ms_talloc_destructor);
111
112 ms->bts = bts;
113 ms->cb = gprs_default_cb;
114 ms->tlli = tlli;
115 ms->new_ul_tlli = GSM_RESERVED_TMSI;
116 ms->new_dl_tlli = GSM_RESERVED_TMSI;
117 ms->ta = GSM48_TA_INVALID;
118 ms->current_cs_ul = UNKNOWN;
119 ms->current_cs_dl = UNKNOWN;
120 ms->is_idle = true;
121 INIT_LLIST_HEAD(&ms->list);
122 INIT_LLIST_HEAD(&ms->old_tbfs);
123
124 int codel_interval = LLC_CODEL_USE_DEFAULT;
125
126 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
127
128 ms->imsi[0] = '\0';
Pau Espin Pedrolc7802d92022-05-09 16:07:52 +0200129 osmo_timer_setup(&ms->timer, ms_release_timer_cb, NULL);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200130 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200131 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
132 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100133
134 ms_set_mode(ms, GPRS);
135
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200136 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200137 if (codel_interval == LLC_CODEL_USE_DEFAULT)
138 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
139 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100140
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100141 ms->last_cs_not_low = now_msec();
142 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100143
144 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
145 if (!ms->ctrs)
146 goto free_ret;
147
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100148 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100149free_ret:
150 talloc_free(ms);
151 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100152}
153
154static int ms_talloc_destructor(struct GprsMs *ms)
155{
156 struct llist_item *pos, *tmp;
157
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100158 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100159
160 ms_set_reserved_slots(ms, NULL, 0, 0);
161
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700162 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100163
164 if (ms->ul_tbf) {
165 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->ul_tbf, NULL);
166 ms->ul_tbf = NULL;
167 }
168
169 if (ms->dl_tbf) {
170 tbf_set_ms((struct gprs_rlcmac_tbf *)ms->dl_tbf, NULL);
171 ms->dl_tbf = NULL;
172 }
173
174 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
175 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
176 tbf_set_ms(tbf, NULL);
177 }
178
179 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200180 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100181
182 if (ms->ctrs)
183 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100184 return 0;
185}
186
187
188void ms_set_callback(struct GprsMs *ms, struct gpr_ms_callback *cb)
189{
190 if (cb)
191 ms->cb = *cb;
192 else
193 ms->cb = gprs_default_cb;
194}
195
196static void ms_update_status(struct GprsMs *ms)
197{
198 if (ms->ref > 0)
199 return;
200
201 if (ms_is_idle(ms) && !ms->is_idle) {
202 ms->is_idle = true;
203 ms->cb.ms_idle(ms);
204 /* this can be deleted by now, do not access it */
205 return;
206 }
207
208 if (!ms_is_idle(ms) && ms->is_idle) {
209 ms->is_idle = false;
210 ms->cb.ms_active(ms);
211 }
212}
213
214struct GprsMs *ms_ref(struct GprsMs *ms)
215{
216 ms->ref += 1;
217 return ms;
218}
219
220void ms_unref(struct GprsMs *ms)
221{
222 OSMO_ASSERT(ms->ref >= 0);
223 ms->ref -= 1;
224 if (ms->ref == 0)
225 ms_update_status(ms);
226}
227
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100228static void ms_release_timer_start(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100229{
230 if (ms->delay == 0)
231 return;
232
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100233 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
234
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100235 if (!ms->timer.data)
236 ms->timer.data = ms_ref(ms);
237
238 osmo_timer_schedule(&ms->timer, ms->delay, 0);
239}
240
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100241static void ms_release_timer_stop(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100242{
243 if (!ms->timer.data)
244 return;
245
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100246 LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Cancel scheduled MS release\n");
247
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100248 osmo_timer_del(&ms->timer);
249 ms->timer.data = NULL;
250 ms_unref(ms);
251}
252
253void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
254{
255 ms->mode = mode;
256
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100257 switch (ms->mode) {
258 case GPRS:
259 if (!mcs_is_gprs(ms->current_cs_ul)) {
260 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100261 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100262 if (!mcs_is_valid(ms->current_cs_ul))
263 ms->current_cs_ul = CS1;
264 }
265 if (!mcs_is_gprs(ms->current_cs_dl)) {
266 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100267 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100268 if (!mcs_is_valid(ms->current_cs_dl))
269 ms->current_cs_dl = CS1;
270 }
271 break;
272
273 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100274 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
275 ms->current_cs_ul = mcs_get_egprs_by_num(
276 ms->bts->initial_mcs_ul);
277 if (!mcs_is_valid(ms->current_cs_ul))
278 ms->current_cs_ul = MCS1;
279 }
280 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
281 ms->current_cs_dl = mcs_get_egprs_by_num(
282 ms->bts->initial_mcs_dl);
283 if (!mcs_is_valid(ms->current_cs_dl))
284 ms->current_cs_dl = MCS1;
285 }
286 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100287 case EGPRS:
288 if (!mcs_is_edge(ms->current_cs_ul)) {
289 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100290 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100291 if (!mcs_is_valid(ms->current_cs_ul))
292 ms->current_cs_ul = MCS1;
293 }
294 if (!mcs_is_edge(ms->current_cs_dl)) {
295 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100296 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100297 if (!mcs_is_valid(ms->current_cs_dl))
298 ms->current_cs_dl = MCS1;
299 }
300 break;
301 }
302}
303
304static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
305{
306 if (ms->ul_tbf == tbf)
307 return;
308
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100309 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 +0100310
311 ms_ref(ms);
312
313 if (ms->ul_tbf)
314 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->ul_tbf), &ms->old_tbfs);
315
316 ms->ul_tbf = tbf;
317
318 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100319 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100320
321 ms_unref(ms);
322}
323
324static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
325{
326 if (ms->dl_tbf == tbf)
327 return;
328
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100329 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 +0100330
331 ms_ref(ms);
332
333 if (ms->dl_tbf)
334 llist_add_tail(tbf_ms_list((struct gprs_rlcmac_tbf *)ms->dl_tbf), &ms->old_tbfs);
335
336 ms->dl_tbf = tbf;
337
338 if (tbf)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100339 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100340
341 ms_unref(ms);
342}
343
344void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
345{
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200346 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100347 ms_attach_dl_tbf(ms, as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200348 else
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100349 ms_attach_ul_tbf(ms, as_ul_tbf(tbf));
350}
351
352void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
353{
354 if (tbf == (struct gprs_rlcmac_tbf *)(ms->ul_tbf)) {
355 ms->ul_tbf = NULL;
356 } else if (tbf == (struct gprs_rlcmac_tbf *)(ms->dl_tbf)) {
357 ms->dl_tbf = NULL;
358 } else {
359 bool found = false;
360
361 struct llist_item *pos, *tmp;
362 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
363 struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
364 if (tmp_tbf == tbf) {
365 llist_del(&pos->list);
366 found = true;
367 break;
368 }
369 }
370
371 /* Protect against recursive calls via set_ms() */
372 if (!found)
373 return;
374 }
375
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100376 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Detaching TBF: %s\n",
377 tbf_name(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100378
379 if (tbf_ms(tbf) == ms)
380 tbf_set_ms(tbf, NULL);
381
382 if (!ms->dl_tbf && !ms->ul_tbf) {
383 ms_set_reserved_slots(ms, NULL, 0, 0);
384
385 if (ms_tlli(ms) != 0)
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100386 ms_release_timer_start(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100387 }
388
389 ms_update_status(ms);
390}
391
392void ms_reset(struct GprsMs *ms)
393{
Pau Espin Pedrol11f01102021-03-03 20:37:38 +0100394 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100395
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100396 ms_release_timer_stop(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100397
398 ms->tlli = GSM_RESERVED_TMSI;
399 ms->new_dl_tlli = ms->tlli;
400 ms->new_ul_tlli = ms->tlli;
401 ms->imsi[0] = '\0';
402}
403
404static void ms_merge_old_ms(struct GprsMs *ms, struct GprsMs *old_ms)
405{
406 OSMO_ASSERT(old_ms != ms);
407
408 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
409 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
410
411 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
412 ms_set_ms_class(ms, ms_ms_class(old_ms));
413
414 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
415 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
416
417 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
418
419 ms_reset(old_ms);
420}
421
422void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
423{
424 OSMO_ASSERT(old_ms != ms);
425
426 ms_ref(old_ms);
427
428 /* Clean up the old MS object */
429 /* TODO: Use timer? */
430 if (ms_ul_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms), T_MAX))
431 tbf_free((struct gprs_rlcmac_tbf *)ms_ul_tbf(old_ms));
432 if (ms_dl_tbf(old_ms) && !tbf_timers_pending((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms), T_MAX))
433 tbf_free((struct gprs_rlcmac_tbf *)ms_dl_tbf(old_ms));
434
435 ms_merge_old_ms(ms, old_ms);
436
437 ms_unref(old_ms);
438}
439
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200440/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100441void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
442{
443 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
444 return;
445
446 if (tlli != ms->new_dl_tlli) {
447 LOGP(DRLCMAC, LOGL_INFO,
448 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
449 "not yet confirmed\n",
450 ms_tlli(ms), tlli);
451 ms->new_ul_tlli = tlli;
452 return;
453 }
454
455 LOGP(DRLCMAC, LOGL_INFO,
456 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
457 "already confirmed partly\n",
458 ms->tlli, tlli);
459
460 ms->tlli = tlli;
461 ms->new_dl_tlli = GSM_RESERVED_TMSI;
462 ms->new_ul_tlli = GSM_RESERVED_TMSI;
463}
464
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200465/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100466bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
467{
468 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
469 return false;
470
471 if (tlli != ms->new_ul_tlli) {
472 /* The MS has not sent a message with the new TLLI, which may
473 * happen according to the spec [TODO: add reference]. */
474
475 LOGP(DRLCMAC, LOGL_INFO,
476 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
477 "partly confirmed\n", tlli);
478 /* Use the network's idea of TLLI as candidate, this does not
479 * change the result value of tlli() */
480 ms->new_dl_tlli = tlli;
481 return false;
482 }
483
484 LOGP(DRLCMAC, LOGL_INFO,
485 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
486
487 ms->tlli = tlli;
488 ms->new_dl_tlli = GSM_RESERVED_TMSI;
489 ms->new_ul_tlli = GSM_RESERVED_TMSI;
490
491 return true;
492}
493
494void ms_set_imsi(struct GprsMs *ms, const char *imsi)
495{
496 if (!imsi) {
497 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
498 return;
499 }
500
501 if (imsi[0] && strlen(imsi) < 3) {
502 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
503 imsi);
504 return;
505 }
506
507 if (strcmp(imsi, ms->imsi) == 0)
508 return;
509
510 LOGP(DRLCMAC, LOGL_INFO,
511 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
512 ms_tlli(ms), ms->imsi, imsi);
513
514 struct GprsMs *old_ms = bts_ms_by_imsi(ms->bts, imsi);
515 /* Check if we are going to store a different MS object with already
516 existing IMSI. This is probably a bug in code calling this function,
517 since it should take care of this explicitly */
518 if (old_ms) {
519 /* We cannot find ms->ms by IMSI since we know that it has a
520 * different IMSI */
521 OSMO_ASSERT(old_ms != ms);
522
523 LOGPMS(ms, DRLCMAC, LOGL_NOTICE,
524 "IMSI '%s' was already assigned to another "
525 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
526 imsi, ms_tlli(old_ms));
527
528 ms_merge_and_clear_ms(ms, old_ms);
529 }
530
531
532 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
533}
534
Pau Espin Pedrol5deac142021-11-12 18:01:50 +0100535uint16_t ms_paging_group(struct GprsMs *ms)
536{
537 uint16_t pgroup;
538 if (!ms_imsi_is_valid(ms))
539 return 0; /* 000 is the special "all paging" group */
540 if ((pgroup = imsi2paging_group(ms_imsi(ms))) > 999) {
541 LOGPMS(ms, DRLCMAC, LOGL_ERROR, "IMSI to paging group failed!\n");
542 return 0;
543 }
544 return pgroup;
545}
546
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100547void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
548{
549 if (ta_ == ms->ta)
550 return;
551
552 if (gsm48_ta_is_valid(ta_)) {
553 LOGP(DRLCMAC, LOGL_INFO,
554 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
555 ms_tlli(ms), ms->ta, ta_);
556 ms->ta = ta_;
557 } else
558 LOGP(DRLCMAC, LOGL_NOTICE,
559 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
560 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
561}
562
563void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
564{
565 if (ms_class_ == ms->ms_class)
566 return;
567
568 LOGP(DRLCMAC, LOGL_INFO,
569 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
570 ms_tlli(ms), ms->ms_class, ms_class_);
571
572 ms->ms_class = ms_class_;
573}
574
575void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
576{
577 if (ms_class_ == ms->egprs_ms_class)
578 return;
579
580 LOGP(DRLCMAC, LOGL_INFO,
581 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
582 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
583
584 ms->egprs_ms_class = ms_class_;
585
586 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
587 LOGPMS(ms, DRLCMAC, LOGL_DEBUG,
588 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
589 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
590 return;
591 }
592
593 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
594 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
595 ms_mode(ms) != EGPRS)
596 {
597 ms_set_mode(ms, EGPRS_GMSK);
598 } else {
599 ms_set_mode(ms, EGPRS);
600 }
601 LOGPMS(ms, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
602}
603
604void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
605{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100606 int64_t now;
607 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100608 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100609
610 if (error_rate < 0)
611 return;
612
613 now = now_msec();
614
615 /* TODO: Check for TBF direction */
616 /* TODO: Support different CS values for UL and DL */
617
618 ms->nack_rate_dl = error_rate;
619
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100620 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100621 if (mcs_chan_code(ms->current_cs_dl) > 0) {
622 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
623 LOGP(DRLCMACDL, LOGL_INFO,
624 "MS (IMSI %s): High error rate %d%%, "
625 "reducing CS level to %s\n",
626 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
627 ms->last_cs_not_low = now;
628 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100629 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100630 if (ms->current_cs_dl < max_cs_dl) {
631 if (now - ms->last_cs_not_low > 1000) {
632 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
633
634 LOGP(DRLCMACDL, LOGL_INFO,
635 "MS (IMSI %s): Low error rate %d%%, "
636 "increasing DL CS level to %s\n",
637 ms_imsi(ms), error_rate,
638 mcs_name(ms->current_cs_dl));
639 ms->last_cs_not_low = now;
640 } else {
641 LOGP(DRLCMACDL, LOGL_DEBUG,
642 "MS (IMSI %s): Low error rate %d%%, "
643 "ignored (within blocking period)\n",
644 ms_imsi(ms), error_rate);
645 }
646 }
647 } else {
648 LOGP(DRLCMACDL, LOGL_DEBUG,
649 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
650 ms_imsi(ms), error_rate);
651 ms->last_cs_not_low = now;
652 }
653}
654
655enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
656{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100657 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100658 OSMO_ASSERT(ms->bts != NULL);
659
660 if (mcs_is_gprs(ms->current_cs_ul)) {
661 if (!bts_max_cs_ul(ms->bts)) {
662 return CS4;
663 }
664
665 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
666 }
667
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100668 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
669 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
670 cs = MCS4;
671 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100672}
673
674void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
675{
676 ms->current_cs_dl = scheme;
677}
678
679enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
680{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100681 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100682 OSMO_ASSERT(ms->bts != NULL);
683
684 if (mcs_is_gprs(ms->current_cs_dl)) {
685 if (!bts_max_cs_dl(ms->bts)) {
686 return CS4;
687 }
688
689 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
690 }
691
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100692 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
693 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
694 cs = MCS4;
695 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100696}
697
698void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
699{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100700 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
701
702 int old_link_qual;
703 int low;
704 int high;
705 enum CodingScheme new_cs_ul = ms->current_cs_ul;
706 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
707
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100708 if (!max_cs_ul) {
709 LOGP(DRLCMACMEAS, LOGL_ERROR,
710 "max_cs_ul cannot be derived (current UL CS: %s)\n",
711 mcs_name(ms->current_cs_ul));
712 return;
713 }
714
715 if (!ms->current_cs_ul) {
716 LOGP(DRLCMACMEAS, LOGL_ERROR,
717 "Unable to update UL (M)CS because it's not set: %s\n",
718 mcs_name(ms->current_cs_ul));
719 return;
720 }
721
722 if (!meas->have_link_qual) {
723 LOGP(DRLCMACMEAS, LOGL_ERROR,
724 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
725 mcs_name(ms->current_cs_ul));
726 return;
727 }
728
729 if (mcs_is_gprs(ms->current_cs_ul)) {
730 if (current_cs >= MAX_GPRS_CS)
731 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100732 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
733 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100734 } else if (mcs_is_edge(ms->current_cs_ul)) {
735 if (current_cs >= MAX_EDGE_MCS)
736 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100737 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
738 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100739 } else {
740 LOGP(DRLCMACMEAS, LOGL_ERROR,
741 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
742 mcs_name(ms->current_cs_ul));
743 return;
744 }
745
746 /* To avoid rapid changes of the coding scheme, we also take
747 * the old link quality value into account (if present). */
748 if (ms->l1_meas.have_link_qual)
749 old_link_qual = ms->l1_meas.link_qual;
750 else
751 old_link_qual = meas->link_qual;
752
753 if (meas->link_qual < low && old_link_qual < low)
754 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
755 else if (meas->link_qual > high && old_link_qual > high &&
756 ms->current_cs_ul < max_cs_ul)
757 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
758
759 if (ms->current_cs_ul != new_cs_ul) {
760 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
761 "Link quality %ddB (old %ddB) left window [%d, %d], "
762 "modifying uplink CS level: %s -> %s\n",
763 meas->link_qual, old_link_qual,
764 low, high,
765 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
766
767 ms->current_cs_ul = new_cs_ul;
768 }
769}
770
771void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
772{
773 unsigned i;
774
775 ms_update_cs_ul(ms, meas);
776
777 if (meas->have_rssi)
778 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
779 if (meas->have_bto)
780 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
781 if (meas->have_ber)
782 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
783 if (meas->have_link_qual)
784 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
785
786 if (meas->have_ms_rx_qual)
787 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
788 if (meas->have_ms_c_value)
789 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
790 if (meas->have_ms_sign_var)
791 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
792
793 if (meas->have_ms_i_level) {
794 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
795 if (meas->ts[i].have_ms_i_level)
796 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
797 else
798 ms->l1_meas.ts[i].have_ms_i_level = 0;
799 }
800 }
801}
802
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100803/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
804enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100805{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100806 enum CodingScheme orig_cs = ms->current_cs_dl;
807 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100808 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100809 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100810
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100811 /* It could be that a TBF requests a GPRS CS despite the MS currently
812 being upgraded to EGPRS (hence reporting MCS). That could happen
813 because the TBF was created early in the process where we didn't have
814 yet enough information about the MS, and only AFTER it was created we
815 upgraded the MS to be EGPRS capable.
816 As a result, when the MS is queried for the target CS here, we could be
817 returning an MCS despite the current TBF being established as GPRS,
818 but we rather stick to the TBF type we assigned to the MS rather than
819 magically sending EGPRS data blocks to a GPRS TBF.
820 It could also be that the caller requests specific MCS kind
821 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
822 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
823 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
824 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
825 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
826 MCS1;
827 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
828 int i;
829 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
830 cs -= (MCS1 - CS1); /* MCSx -> CSx */
831 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
832 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
833 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
834 cs = CS1 + i;
835 } else {
836 cs = orig_cs;
837 }
838
839 if (orig_cs != cs)
840 LOGPMS(ms, DRLCMACDL, LOGL_INFO, "MS (mode=%s) suggests transmitting "
841 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
842 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100843
844 unencoded_octets = llc_queue_octets(&ms->llc_queue);
845
846 /* If the DL TBF is active, add number of unencoded chunk octets */
847 if (ms->dl_tbf)
848 unencoded_octets += llc_chunk_size(tbf_llc((struct gprs_rlcmac_tbf *)ms->dl_tbf));
849
850 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100851 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100852 return cs;
853
854 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100855 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100856 return cs;
857
858 /* The throughput would probably be better if the CS level was reduced */
859 mcs_dec_kind(&cs, ms_mode(ms));
860
861 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
862 if (cs == CS2)
863 mcs_dec_kind(&cs, ms_mode(ms));
864
865 return cs;
866}
867
868int ms_first_common_ts(const struct GprsMs *ms)
869{
870 if (ms->dl_tbf)
871 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->dl_tbf);
872
873 if (ms->ul_tbf)
874 return tbf_first_common_ts((struct gprs_rlcmac_tbf *)ms->ul_tbf);
875
876 return -1;
877}
878
879uint8_t ms_dl_slots(const struct GprsMs *ms)
880{
881 uint8_t slots = 0;
882
883 if (ms->dl_tbf)
884 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
885
886 if (ms->ul_tbf)
887 slots |= tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
888
889 return slots;
890}
891
892uint8_t ms_ul_slots(const struct GprsMs *ms)
893{
894 uint8_t slots = 0;
895
896 if (ms->dl_tbf)
897 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
898
899 if (ms->ul_tbf)
900 slots |= tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
901
902 return slots;
903}
904
905uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
906{
907 uint8_t slots = 0;
908
909 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->dl_tbf);
910 bool is_ul_active = ms->ul_tbf && tbf_is_tfi_assigned((struct gprs_rlcmac_tbf *)ms->ul_tbf);
911
912 if (!is_dl_active && !is_ul_active)
913 return 0;
914
915 /* see TS 44.060, 8.1.1.2.2 */
916 if (is_dl_active && !is_ul_active)
917 slots = tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
918 else if (!is_dl_active && is_ul_active)
919 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf);
920 else
921 slots = tbf_ul_slots((struct gprs_rlcmac_tbf *)ms->ul_tbf) &
922 tbf_dl_slots((struct gprs_rlcmac_tbf *)ms->dl_tbf);
923
924 /* Assume a multislot class 1 device */
925 /* TODO: For class 2 devices, this could be removed */
926 slots = pcu_lsb(slots);
927
928 return slots;
929}
930
931void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
932 uint8_t ul_slots, uint8_t dl_slots)
933{
934 if (ms->current_trx) {
935 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
936 ms->reserved_dl_slots);
937 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
938 ms->reserved_ul_slots);
939 ms->reserved_dl_slots = 0;
940 ms->reserved_ul_slots = 0;
941 }
942 ms->current_trx = trx;
943 if (trx) {
944 ms->reserved_dl_slots = dl_slots;
945 ms->reserved_ul_slots = ul_slots;
946 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
947 ms->reserved_dl_slots);
948 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
949 ms->reserved_ul_slots);
950 }
951}
952
953struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
954{
955 switch (dir) {
956 case GPRS_RLCMAC_DL_TBF: return (struct gprs_rlcmac_tbf *)ms->dl_tbf;
957 case GPRS_RLCMAC_UL_TBF: return (struct gprs_rlcmac_tbf *)ms->ul_tbf;
958 }
959
960 return NULL;
961}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100962
Pau Espin Pedrol3547d642022-10-21 15:00:08 +0200963const char *ms_name(const struct GprsMs *ms)
964{
965 static char _ms_name_buf[128];
966 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
967}
968
969char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
970{
971 snprintf(buf, buf_size - 1,
972 "MS(TLLI=0x%08x, IMSI=%s, TA=%" PRIu8 ", %" PRIu8 "/%" PRIu8 ",%s%s)",
973 ms_tlli(ms), ms_imsi(ms), ms_ta(ms),
974 ms_ms_class(ms), ms_egprs_ms_class(ms),
975 ms_ul_tbf(ms) ? " UL" : "",
976 ms_dl_tbf(ms) ? " DL" : "");
977 buf[buf_size - 1] = '\0';
978 return buf;
979}
980
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +0100981int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
982{
983 if (!ms->nacc)
984 ms->nacc = nacc_fsm_alloc(ms);
985 if (!ms->nacc)
986 return -EINVAL;
987 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
988}
989
990bool ms_nacc_rts(const struct GprsMs *ms)
991{
992 if (!ms->nacc)
993 return false;
994 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
995 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
996 return true;
997 return false;
998}
999
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001000struct 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 +01001001{
1002 int rc;
1003 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1004
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001005 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1006 .tbf = tbf,
1007 .fn = fn,
1008 .ts = ts,
1009 .msg = NULL,
1010 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001011
1012 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1013 if (rc != 0 || !data_ctx.msg)
1014 return NULL;
1015 return data_ctx.msg;
1016}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001017
1018static void ms_start_llc_timer(struct GprsMs *ms)
1019{
1020 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1021 struct timespec tv;
1022 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1023 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1024 }
1025}
1026
1027int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1028{
1029 struct timespec expire_time;
1030 struct gprs_rlcmac_dl_tbf *dl_tbf;
1031
1032 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1033
1034 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1035 if (!llc_msg)
1036 return -ENOMEM;
1037
1038 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1039 memcpy(msgb_put(llc_msg, len), data, len);
1040 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1041 ms_start_llc_timer(ms);
1042
1043 dl_tbf = ms_dl_tbf(ms);
1044 if (dl_tbf && tbf_state((const struct gprs_rlcmac_tbf *)dl_tbf) == TBF_ST_WAIT_RELEASE) {
1045 LOGPTBFDL((const struct gprs_rlcmac_tbf *)dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
1046 tbf_establish_dl_tbf_on_pacch((struct gprs_rlcmac_tbf *)dl_tbf);
1047 }
1048
1049 return 0;
1050}