blob: 1b51d4d3c7f0617950326c97677e049f83da914b [file] [log] [blame]
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001/* gprs_ms.c
2 *
3 * Copyright (C) 2015-2020 by Sysmocom s.f.m.c. GmbH
4 * Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010015 */
16
17
18#include "gprs_ms.h"
19#include "bts.h"
20#include "tbf.h"
21#include "tbf_ul.h"
22#include "gprs_debug.h"
23#include "gprs_codel.h"
24#include "pcu_utils.h"
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +010025#include "nacc_fsm.h"
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +020026#include "tbf_ul_ack_fsm.h"
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010027
28#include <time.h>
29
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32#include <osmocom/core/timer.h>
33#include <osmocom/gsm/protocol/gsm_04_08.h>
34#include <osmocom/gsm/gsm48.h>
35#include <osmocom/core/logging.h>
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010036#include <osmocom/core/stats.h>
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010037#include "coding_scheme.h"
38
39#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
40
41extern void *tall_pcu_ctx;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010042static unsigned int next_ms_ctr_group_id;
43
44static const struct rate_ctr_desc ms_ctr_description[] = {
45 [MS_CTR_DL_CTRL_MSG_SCHED] = { "ms:dl_ctrl_msg_sched", "Amount of DL CTRL messages scheduled" },
46};
47
Pau Espin Pedrolf5a251b2021-01-12 20:57:56 +010048static const struct rate_ctr_group_desc ms_ctrg_desc = {
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +010049 .group_name_prefix = "pcu:ms",
50 .group_description = "MS Statistics",
51 .class_id = OSMO_STATS_CLASS_SUBSCRIBER,
52 .num_ctr = ARRAY_SIZE(ms_ctr_description),
53 .ctr_desc = ms_ctr_description,
54};
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +010055
56static int64_t now_msec()
57{
58 struct timespec ts;
59 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
60
61 return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
62}
63
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +020064static void ms_becomes_idle(struct GprsMs *ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020065
66static int ms_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
67{
68 struct GprsMs *ms = e->use_count->talloc_object;
69 int32_t total;
70 int level;
71 char buf[1024];
72
73 if (!e->use)
74 return -EINVAL;
75
76 total = osmo_use_count_total(&ms->use_count);
77
78 if (total == 0
79 || (total == 1 && old_use_count == 0 && e->count == 1))
80 level = LOGL_INFO;
81 else
82 level = LOGL_DEBUG;
83
84
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +020085 LOGPSRC(DMS, level, file, line, "%s: %s %s: now used by %s\n",
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020086 ms_name(ms),
87 (e->count - old_use_count) > 0 ? "+" : "-", e->use,
88 (osmo_use_count_to_str_buf(buf, sizeof(buf), &ms->use_count), buf));
89
90 if (e->count < 0)
91 return -ERANGE;
92
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +020093 if (total == 0) {
94 OSMO_ASSERT(ms_is_idle(ms));
95 ms_becomes_idle(ms);
96 }
Pau Espin Pedrol403e0482023-04-17 20:28:10 +020097 return 0;
98}
99
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100100static void ms_release_timer_cb(void *data)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100101{
102 struct GprsMs *ms = (struct GprsMs *) data;
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200103 LOGPMS(ms, DMS, LOGL_INFO, "Release timer expired\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100104
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200105 /* Finally free the MS after being idle for a while according to config */
106 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100107}
108
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200109static void ms_llc_timer_cb(void *_ms)
110{
111 struct GprsMs *ms = _ms;
112 struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(ms);
113
114 if (!dl_tbf)
115 return;
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200116 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) != TBF_ST_FLOW)
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200117 return;
118
Pau Espin Pedrolbd1f01f2022-10-27 15:19:39 +0200119 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "LLC receive timeout, requesting DL ACK\n");
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200120
Pau Espin Pedrol8fa3e062022-10-28 17:38:52 +0200121 dl_tbf_request_dl_ack(dl_tbf);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200122}
123
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100124static int ms_talloc_destructor(struct GprsMs *ms);
Pau Espin Pedroleae91472023-04-19 19:42:05 +0200125struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, const char *use_ref)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100126{
127 struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200128 OSMO_ASSERT(bts);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100129
130 talloc_set_destructor(ms, ms_talloc_destructor);
131
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200132 llist_add(&ms->list, &bts->ms_list);
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200133 bts_stat_item_inc(bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200134
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100135 ms->bts = bts;
Pau Espin Pedrolf80cc5b2023-04-17 14:25:51 +0200136 ms->tlli = GSM_RESERVED_TMSI;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100137 ms->new_ul_tlli = GSM_RESERVED_TMSI;
138 ms->new_dl_tlli = GSM_RESERVED_TMSI;
139 ms->ta = GSM48_TA_INVALID;
140 ms->current_cs_ul = UNKNOWN;
141 ms->current_cs_dl = UNKNOWN;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100142 INIT_LLIST_HEAD(&ms->old_tbfs);
143
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200144 ms->use_count = (struct osmo_use_count){
145 .talloc_object = ms,
146 .use_cb = ms_use_cb,
147 };
148
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100149 int codel_interval = LLC_CODEL_USE_DEFAULT;
150
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200151 LOGP(DMS, LOGL_INFO, "Creating MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100152
153 ms->imsi[0] = '\0';
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200154 osmo_timer_setup(&ms->timer, ms_release_timer_cb, ms);
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200155 llc_queue_init(&ms->llc_queue, ms);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200156 memset(&ms->llc_timer, 0, sizeof(ms->llc_timer));
157 osmo_timer_setup(&ms->llc_timer, ms_llc_timer_cb, ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100158
159 ms_set_mode(ms, GPRS);
160
Pau Espin Pedrolb4987462022-04-01 17:21:08 +0200161 codel_interval = the_pcu->vty.llc_codel_interval_msec;
Pau Espin Pedrol6b1e9512022-04-04 13:45:56 +0200162 if (codel_interval == LLC_CODEL_USE_DEFAULT)
163 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
164 llc_queue_set_codel_interval(&ms->llc_queue, codel_interval);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100165
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100166 ms->last_cs_not_low = now_msec();
167 ms->app_info_pending = false;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100168
169 ms->ctrs = rate_ctr_group_alloc(ms, &ms_ctrg_desc, next_ms_ctr_group_id++);
170 if (!ms->ctrs)
171 goto free_ret;
172
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200173 ms_set_timeout(ms, osmo_tdef_get(bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1));
174
Pau Espin Pedroleae91472023-04-19 19:42:05 +0200175 if (use_ref)
176 ms_ref(ms, use_ref);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100177 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100178free_ret:
179 talloc_free(ms);
180 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100181}
182
183static int ms_talloc_destructor(struct GprsMs *ms)
184{
185 struct llist_item *pos, *tmp;
186
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200187 LOGPMS(ms, DMS, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100188
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200189 bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200190 llist_del(&ms->list);
191
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100192 ms_set_reserved_slots(ms, NULL, 0, 0);
193
Vadim Yanitskiye33c2362022-07-22 03:44:44 +0700194 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100195
196 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200197 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100198 ms->ul_tbf = NULL;
199 }
200
201 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200202 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100203 ms->dl_tbf = NULL;
204 }
205
206 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
207 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
208 tbf_set_ms(tbf, NULL);
209 }
210
211 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200212 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100213
214 if (ms->ctrs)
215 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100216 return 0;
217}
218
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200219/* MS has no attached TBFs anymore. */
220static void ms_becomes_idle(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100221{
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200222 ms_set_reserved_slots(ms, NULL, 0, 0);
223 ms->first_common_ts = NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100224
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200225 /* Immediate free():
226 * Skip delaying free() through release timer if delay is configured to be 0.
227 * This is useful for synced freed during unit tests.
228 */
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200229 if (ms->delay == 0) {
230 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100231 return;
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200232 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100233
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200234 /* Immediate free():
235 * Skip delaying free() through release timer if TMSI is not
236 * known, since those cannot really be reused.
237 */
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200238 if (ms_tlli(ms) == GSM_RESERVED_TMSI) {
239 talloc_free(ms);
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200240 return;
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200241 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100242
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200243 LOGPMS(ms, DMS, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100244 osmo_timer_schedule(&ms->timer, ms->delay, 0);
245}
246
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200247static void ms_becomes_active(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100248{
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200249 if (!osmo_timer_pending(&ms->timer))
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250 return;
251
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200252 LOGPMS(ms, DMS, LOGL_DEBUG, "Cancel scheduled MS release\n");
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100253
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100254 osmo_timer_del(&ms->timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100255}
256
257void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
258{
259 ms->mode = mode;
260
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100261 switch (ms->mode) {
262 case GPRS:
263 if (!mcs_is_gprs(ms->current_cs_ul)) {
264 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100265 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100266 if (!mcs_is_valid(ms->current_cs_ul))
267 ms->current_cs_ul = CS1;
268 }
269 if (!mcs_is_gprs(ms->current_cs_dl)) {
270 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100271 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100272 if (!mcs_is_valid(ms->current_cs_dl))
273 ms->current_cs_dl = CS1;
274 }
275 break;
276
277 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100278 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
279 ms->current_cs_ul = mcs_get_egprs_by_num(
280 ms->bts->initial_mcs_ul);
281 if (!mcs_is_valid(ms->current_cs_ul))
282 ms->current_cs_ul = MCS1;
283 }
284 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
285 ms->current_cs_dl = mcs_get_egprs_by_num(
286 ms->bts->initial_mcs_dl);
287 if (!mcs_is_valid(ms->current_cs_dl))
288 ms->current_cs_dl = MCS1;
289 }
290 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100291 case EGPRS:
292 if (!mcs_is_edge(ms->current_cs_ul)) {
293 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100294 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100295 if (!mcs_is_valid(ms->current_cs_ul))
296 ms->current_cs_ul = MCS1;
297 }
298 if (!mcs_is_edge(ms->current_cs_dl)) {
299 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100300 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100301 if (!mcs_is_valid(ms->current_cs_dl))
302 ms->current_cs_dl = MCS1;
303 }
304 break;
305 }
306}
307
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200308/* If a TBF is attached to an MS, it is either in ms->{dl,ul}_tbf or in ms->old_tbfs list */
309static bool ms_tbf_is_attached(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf)
310{
311 const struct llist_item *pos;
312 OSMO_ASSERT(ms);
313 OSMO_ASSERT(tbf);
314 OSMO_ASSERT(tbf_ms(tbf) == ms);
315
316 if (tbf == ul_tbf_as_tbf_const(ms->ul_tbf))
317 return true;
318
319 if (tbf == dl_tbf_as_tbf_const(ms->dl_tbf))
320 return true;
321
322 llist_for_each_entry(pos, &ms->old_tbfs, list) {
323 const struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
324 if (tmp_tbf == tbf)
325 return true;
326 }
327 return false;
328}
329
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100330static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
331{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200332 LOGPMS(ms, DMS, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100333
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100334 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200335 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100336
337 ms->ul_tbf = tbf;
338
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200339 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100340}
341
342static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
343{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200344 LOGPMS(ms, DMS, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100345
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100346 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200347 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100348
349 ms->dl_tbf = tbf;
350
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200351 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100352}
353
354void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
355{
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200356 OSMO_ASSERT(ms);
357 OSMO_ASSERT(tbf);
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200358 OSMO_ASSERT(!ms_tbf_is_attached(ms, tbf));
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200359
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200360 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200361 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200362 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200363 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200364
365 ms_becomes_active(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100366}
367
368void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
369{
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200370 OSMO_ASSERT(tbf_ms(tbf) == ms);
371
372 /* In general this should not happen, but it can happen if during TBF
373 * allocation something fails before tbf->setup() called ms_attach_tbf(). */
374 if (!ms_tbf_is_attached(ms, tbf))
375 return;
376
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200377 LOGPMS(ms, DMS, LOGL_INFO, "Detaching TBF: %s\n",
Pau Espin Pedroldf6684f2023-04-18 17:50:34 +0200378 tbf_name(tbf));
379
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200380 if (tbf == ul_tbf_as_tbf(ms->ul_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100381 ms->ul_tbf = NULL;
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200382 } else if (tbf == dl_tbf_as_tbf(ms->dl_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100383 ms->dl_tbf = NULL;
384 } else {
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200385 /* We know from ms_tbf_is_attached()==true check above that tbf
386 * is in ms->old_tbfs, no need to look it up again. */
387 llist_del(tbf_ms_list(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100388 }
389
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200390 ms_unref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100391}
392
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200393/* Cleans up old MS being merged into a new one. Should be called with a
394ms_ref() taken to avoid use-after-free. */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100395void ms_reset(struct GprsMs *ms)
396{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200397 LOGPMS(ms, DMS, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200398 struct llist_item *pos;
399 struct gprs_rlcmac_tbf *tbf;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100400
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200401 /* free immediately when it becomes idle: */
402 ms->delay = 0;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100403
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200404 tbf = ul_tbf_as_tbf(ms_ul_tbf(ms));
405 if (tbf && !tbf_timers_pending(tbf, T_MAX))
406 tbf_free(tbf);
407 tbf = dl_tbf_as_tbf(ms_dl_tbf(ms));
408 if (tbf && !tbf_timers_pending(tbf, T_MAX))
409 tbf_free(tbf);
410
411 llist_for_each_entry(pos, &ms->old_tbfs, list) {
412 tbf = (struct gprs_rlcmac_tbf *)pos->entry;
413 if (!tbf_timers_pending(tbf, T_MAX))
414 tbf_free(tbf);
415 }
416
417 /* Flag it with invalid data so that it cannot be looked up anymore and
418 * shows up specially if listed in VTY: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100419 ms->tlli = GSM_RESERVED_TMSI;
420 ms->new_dl_tlli = ms->tlli;
421 ms->new_ul_tlli = ms->tlli;
422 ms->imsi[0] = '\0';
423}
424
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200425/* This function should be called on the MS object of a TBF each time an RLCMAC
426 * block is received for it with TLLI information.
427 * Besides updating the TLLI field on the MS object, it also seeks for other MS
428 * objects in the store and merges them into the current MS object. The MS
429 * duplication happened because we don't learn the TLLI of the created TBF until
430 * a later point. */
431void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
432{
433 struct GprsMs *old_ms = NULL;
434
435 if (tlli == GSM_RESERVED_TMSI)
436 return;
437
438 /* When the TLLI does not match the ms, check if there is another
439 * MS object that belongs to that TLLI and if yes make sure one of them
440 * gets deleted. */
441 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200442 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200443
444 ms_set_tlli(ms, tlli);
445
446 if (old_ms)
447 ms_merge_and_clear_ms(ms, old_ms);
448 /* old_ms may no longer be available here */
449}
450
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100451/* Merge 'old_ms' object into 'ms' object.
452 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
453void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100454{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100455 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100456 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200457 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100458
459 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
460
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200461 LOGPMS(ms, DMS, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100462
463 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
464 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
465
466 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
467 ms_set_ms_class(ms, ms_ms_class(old_ms));
468
469 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
470 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
471
472 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
473
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100474 /* Clean up the old MS object */
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100475 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100476
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200477 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100478}
479
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100480/* Apply changes to the TLLI directly, used interally by functions below: */
481static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
482{
483 ms->tlli = tlli;
484 ms->new_dl_tlli = GSM_RESERVED_TMSI;
485 ms->new_ul_tlli = GSM_RESERVED_TMSI;
486
487 /* Update TBF FSM names: */
488 if (ms->ul_tbf)
489 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
490 if (ms->dl_tbf)
491 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
492}
493
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200494/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100495void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
496{
497 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
498 return;
499
500 if (tlli != ms->new_dl_tlli) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200501 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100502 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
503 "not yet confirmed\n",
504 ms_tlli(ms), tlli);
505 ms->new_ul_tlli = tlli;
506 return;
507 }
508
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200509 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100510 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
511 "already confirmed partly\n",
512 ms->tlli, tlli);
513
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100514 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100515}
516
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200517/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100518bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
519{
520 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
521 return false;
522
523 if (tlli != ms->new_ul_tlli) {
524 /* The MS has not sent a message with the new TLLI, which may
525 * happen according to the spec [TODO: add reference]. */
526
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200527 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100528 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
529 "partly confirmed\n", tlli);
530 /* Use the network's idea of TLLI as candidate, this does not
531 * change the result value of tlli() */
532 ms->new_dl_tlli = tlli;
533 return false;
534 }
535
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200536 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100537 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
538
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100539 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100540
541 return true;
542}
543
544void ms_set_imsi(struct GprsMs *ms, const char *imsi)
545{
546 if (!imsi) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200547 LOGP(DMS, LOGL_ERROR, "Expected IMSI!\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100548 return;
549 }
550
551 if (imsi[0] && strlen(imsi) < 3) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200552 LOGP(DMS, LOGL_ERROR, "No valid IMSI '%s'!\n",
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100553 imsi);
554 return;
555 }
556
557 if (strcmp(imsi, ms->imsi) == 0)
558 return;
559
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200560 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100561 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
562 ms_tlli(ms), ms->imsi, imsi);
563
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200564 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100565 /* Check if we are going to store a different MS object with already
566 existing IMSI. This is probably a bug in code calling this function,
567 since it should take care of this explicitly */
568 if (old_ms) {
569 /* We cannot find ms->ms by IMSI since we know that it has a
570 * different IMSI */
571 OSMO_ASSERT(old_ms != ms);
572
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200573 LOGPMS(ms, DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100574 "IMSI '%s' was already assigned to another "
575 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
576 imsi, ms_tlli(old_ms));
577
578 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100579 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100580 }
581
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100582 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100583 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100584
585 /* Update TBF FSM names: */
586 if (ms->ul_tbf)
587 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
588 if (ms->dl_tbf)
589 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100590}
591
592void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
593{
594 if (ta_ == ms->ta)
595 return;
596
597 if (gsm48_ta_is_valid(ta_)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200598 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100599 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
600 ms_tlli(ms), ms->ta, ta_);
601 ms->ta = ta_;
602 } else
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200603 LOGP(DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100604 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
605 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
606}
607
608void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
609{
610 if (ms_class_ == ms->ms_class)
611 return;
612
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200613 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100614 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
615 ms_tlli(ms), ms->ms_class, ms_class_);
616
617 ms->ms_class = ms_class_;
618}
619
620void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
621{
622 if (ms_class_ == ms->egprs_ms_class)
623 return;
624
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200625 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100626 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
627 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
628
629 ms->egprs_ms_class = ms_class_;
630
631 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200632 LOGPMS(ms, DMS, LOGL_DEBUG,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100633 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
634 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
635 return;
636 }
637
638 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
639 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
640 ms_mode(ms) != EGPRS)
641 {
642 ms_set_mode(ms, EGPRS_GMSK);
643 } else {
644 ms_set_mode(ms, EGPRS);
645 }
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200646 LOGPMS(ms, DMS, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100647}
648
649void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
650{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100651 int64_t now;
652 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100653 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100654
655 if (error_rate < 0)
656 return;
657
658 now = now_msec();
659
660 /* TODO: Check for TBF direction */
661 /* TODO: Support different CS values for UL and DL */
662
663 ms->nack_rate_dl = error_rate;
664
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100665 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100666 if (mcs_chan_code(ms->current_cs_dl) > 0) {
667 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
668 LOGP(DRLCMACDL, LOGL_INFO,
669 "MS (IMSI %s): High error rate %d%%, "
670 "reducing CS level to %s\n",
671 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
672 ms->last_cs_not_low = now;
673 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100674 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100675 if (ms->current_cs_dl < max_cs_dl) {
676 if (now - ms->last_cs_not_low > 1000) {
677 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
678
679 LOGP(DRLCMACDL, LOGL_INFO,
680 "MS (IMSI %s): Low error rate %d%%, "
681 "increasing DL CS level to %s\n",
682 ms_imsi(ms), error_rate,
683 mcs_name(ms->current_cs_dl));
684 ms->last_cs_not_low = now;
685 } else {
686 LOGP(DRLCMACDL, LOGL_DEBUG,
687 "MS (IMSI %s): Low error rate %d%%, "
688 "ignored (within blocking period)\n",
689 ms_imsi(ms), error_rate);
690 }
691 }
692 } else {
693 LOGP(DRLCMACDL, LOGL_DEBUG,
694 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
695 ms_imsi(ms), error_rate);
696 ms->last_cs_not_low = now;
697 }
698}
699
700enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
701{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100702 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100703 OSMO_ASSERT(ms->bts != NULL);
704
705 if (mcs_is_gprs(ms->current_cs_ul)) {
706 if (!bts_max_cs_ul(ms->bts)) {
707 return CS4;
708 }
709
710 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
711 }
712
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100713 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
714 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
715 cs = MCS4;
716 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100717}
718
719void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
720{
721 ms->current_cs_dl = scheme;
722}
723
724enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
725{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100726 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100727 OSMO_ASSERT(ms->bts != NULL);
728
729 if (mcs_is_gprs(ms->current_cs_dl)) {
730 if (!bts_max_cs_dl(ms->bts)) {
731 return CS4;
732 }
733
734 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
735 }
736
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100737 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
738 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
739 cs = MCS4;
740 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100741}
742
743void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
744{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100745 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
746
747 int old_link_qual;
748 int low;
749 int high;
750 enum CodingScheme new_cs_ul = ms->current_cs_ul;
751 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
752
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100753 if (!max_cs_ul) {
754 LOGP(DRLCMACMEAS, LOGL_ERROR,
755 "max_cs_ul cannot be derived (current UL CS: %s)\n",
756 mcs_name(ms->current_cs_ul));
757 return;
758 }
759
760 if (!ms->current_cs_ul) {
761 LOGP(DRLCMACMEAS, LOGL_ERROR,
762 "Unable to update UL (M)CS because it's not set: %s\n",
763 mcs_name(ms->current_cs_ul));
764 return;
765 }
766
767 if (!meas->have_link_qual) {
768 LOGP(DRLCMACMEAS, LOGL_ERROR,
769 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
770 mcs_name(ms->current_cs_ul));
771 return;
772 }
773
774 if (mcs_is_gprs(ms->current_cs_ul)) {
775 if (current_cs >= MAX_GPRS_CS)
776 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100777 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
778 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100779 } else if (mcs_is_edge(ms->current_cs_ul)) {
780 if (current_cs >= MAX_EDGE_MCS)
781 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100782 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
783 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100784 } else {
785 LOGP(DRLCMACMEAS, LOGL_ERROR,
786 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
787 mcs_name(ms->current_cs_ul));
788 return;
789 }
790
791 /* To avoid rapid changes of the coding scheme, we also take
792 * the old link quality value into account (if present). */
793 if (ms->l1_meas.have_link_qual)
794 old_link_qual = ms->l1_meas.link_qual;
795 else
796 old_link_qual = meas->link_qual;
797
798 if (meas->link_qual < low && old_link_qual < low)
799 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
800 else if (meas->link_qual > high && old_link_qual > high &&
801 ms->current_cs_ul < max_cs_ul)
802 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
803
804 if (ms->current_cs_ul != new_cs_ul) {
805 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
806 "Link quality %ddB (old %ddB) left window [%d, %d], "
807 "modifying uplink CS level: %s -> %s\n",
808 meas->link_qual, old_link_qual,
809 low, high,
810 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
811
812 ms->current_cs_ul = new_cs_ul;
813 }
814}
815
816void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
817{
818 unsigned i;
819
820 ms_update_cs_ul(ms, meas);
821
822 if (meas->have_rssi)
823 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
824 if (meas->have_bto)
825 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
826 if (meas->have_ber)
827 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
828 if (meas->have_link_qual)
829 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
830
831 if (meas->have_ms_rx_qual)
832 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
833 if (meas->have_ms_c_value)
834 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
835 if (meas->have_ms_sign_var)
836 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
837
838 if (meas->have_ms_i_level) {
839 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
840 if (meas->ts[i].have_ms_i_level)
841 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
842 else
843 ms->l1_meas.ts[i].have_ms_i_level = 0;
844 }
845 }
846}
847
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100848/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
849enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100850{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100851 enum CodingScheme orig_cs = ms->current_cs_dl;
852 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100853 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100854 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100855
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100856 /* It could be that a TBF requests a GPRS CS despite the MS currently
857 being upgraded to EGPRS (hence reporting MCS). That could happen
858 because the TBF was created early in the process where we didn't have
859 yet enough information about the MS, and only AFTER it was created we
860 upgraded the MS to be EGPRS capable.
861 As a result, when the MS is queried for the target CS here, we could be
862 returning an MCS despite the current TBF being established as GPRS,
863 but we rather stick to the TBF type we assigned to the MS rather than
864 magically sending EGPRS data blocks to a GPRS TBF.
865 It could also be that the caller requests specific MCS kind
866 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
867 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
868 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
869 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
870 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
871 MCS1;
872 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
873 int i;
874 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
875 cs -= (MCS1 - CS1); /* MCSx -> CSx */
876 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
877 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
878 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
879 cs = CS1 + i;
880 } else {
881 cs = orig_cs;
882 }
883
884 if (orig_cs != cs)
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200885 LOGPMS(ms, DMS, LOGL_INFO, "MS (mode=%s) suggests transmitting "
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100886 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
887 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100888
889 unencoded_octets = llc_queue_octets(&ms->llc_queue);
890
891 /* If the DL TBF is active, add number of unencoded chunk octets */
892 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200893 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100894
895 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100896 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100897 return cs;
898
899 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100900 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100901 return cs;
902
903 /* The throughput would probably be better if the CS level was reduced */
904 mcs_dec_kind(&cs, ms_mode(ms));
905
906 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
907 if (cs == CS2)
908 mcs_dec_kind(&cs, ms_mode(ms));
909
910 return cs;
911}
912
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100913struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100914{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100915 return ms->first_common_ts;
916}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100917
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100918void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100919{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100920 OSMO_ASSERT(pdch);
921 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100922}
923
924uint8_t ms_dl_slots(const struct GprsMs *ms)
925{
926 uint8_t slots = 0;
927
928 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200929 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100930
931 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200932 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100933
934 return slots;
935}
936
937uint8_t ms_ul_slots(const struct GprsMs *ms)
938{
939 uint8_t slots = 0;
940
941 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200942 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100943
944 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200945 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100946
947 return slots;
948}
949
950uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
951{
952 uint8_t slots = 0;
953
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200954 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
955 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 +0100956
957 if (!is_dl_active && !is_ul_active)
958 return 0;
959
960 /* see TS 44.060, 8.1.1.2.2 */
961 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200962 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100963 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200964 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100965 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200966 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
967 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100968
969 /* Assume a multislot class 1 device */
970 /* TODO: For class 2 devices, this could be removed */
971 slots = pcu_lsb(slots);
972
973 return slots;
974}
975
976void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
977 uint8_t ul_slots, uint8_t dl_slots)
978{
979 if (ms->current_trx) {
980 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
981 ms->reserved_dl_slots);
982 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
983 ms->reserved_ul_slots);
984 ms->reserved_dl_slots = 0;
985 ms->reserved_ul_slots = 0;
986 }
987 ms->current_trx = trx;
988 if (trx) {
989 ms->reserved_dl_slots = dl_slots;
990 ms->reserved_ul_slots = ul_slots;
991 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
992 ms->reserved_dl_slots);
993 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
994 ms->reserved_ul_slots);
995 }
996}
997
998struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
999{
1000 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001001 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1002 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001003 }
1004
1005 return NULL;
1006}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001007
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001008const char *ms_name(const struct GprsMs *ms)
1009{
1010 static char _ms_name_buf[128];
1011 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1012}
1013
1014char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1015{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001016 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1017 uint32_t tlli = ms_tlli(ms);
1018
1019 OSMO_STRBUF_PRINTF(sb, "MS(");
1020 if (ms_imsi_is_valid(ms))
1021 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1022 if (tlli != GSM_RESERVED_TMSI)
1023 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1024 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1025 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1026 if (ms->ul_tbf)
1027 OSMO_STRBUF_PRINTF(sb, ":UL");
1028 if (ms->dl_tbf)
1029 OSMO_STRBUF_PRINTF(sb, ":DL");
1030
1031 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001032 return buf;
1033}
1034
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001035int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1036{
1037 if (!ms->nacc)
1038 ms->nacc = nacc_fsm_alloc(ms);
1039 if (!ms->nacc)
1040 return -EINVAL;
1041 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1042}
1043
1044bool ms_nacc_rts(const struct GprsMs *ms)
1045{
1046 if (!ms->nacc)
1047 return false;
1048 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1049 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1050 return true;
1051 return false;
1052}
1053
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001054struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1055 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001056{
1057 int rc;
1058 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1059
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001060 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1061 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001062 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001063 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001064 .msg = NULL,
1065 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001066
1067 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1068 if (rc != 0 || !data_ctx.msg)
1069 return NULL;
1070 return data_ctx.msg;
1071}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001072
1073static void ms_start_llc_timer(struct GprsMs *ms)
1074{
1075 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1076 struct timespec tv;
1077 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1078 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1079 }
1080}
1081
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001082/* Can we get to send a DL TBF ass to the MS? */
1083static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1084{
1085 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1086
1087 /* This function assumes it is called when no DL TBF is present */
1088 OSMO_ASSERT(!ms_dl_tbf(ms));
1089
1090 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1091 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1092 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1093 * completed on the mobile station side." */
1094 /* The possible uplink TBF is used to trigger downlink assignment:
1095 * - If there is no uplink TBF the MS is potentially in packet idle mode
1096 * and hence assignment will be done over CCCH (PCH)
1097 * - If there's an uplink TBF but it is finished (waiting for last PKT
1098 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1099 * have no ways to contact the MS right now. Assignment will be delayed
1100 * until PKT CTRL ACK is received and the TBF is released at the MS side
1101 * (then assignment goes through PCH).
1102 */
1103 if (!ul_tbf)
1104 return true;
1105 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1106 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1107 return true;
1108
1109 return false;
1110
1111}
1112
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001113/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1114 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1115 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1116 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1117 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1118 */
1119struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1120{
1121 const bool single_slot = false;
1122 struct gprs_rlcmac_ul_tbf *ul_tbf;
1123
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001124 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001125 if (!ul_tbf) {
1126 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1127 /* Caller will most probably send a Imm Ass Reject after return */
1128 return NULL;
1129 }
1130 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1131 /* Contention resolution is considered to be done since TLLI is known in MS */
1132 return ul_tbf;
1133}
1134
1135/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1136 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1137struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1138{
1139 const int8_t trx_no = -1;
1140 const bool single_slot = true;
1141 struct gprs_rlcmac_ul_tbf *ul_tbf;
1142
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001143 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001144 if (!ul_tbf) {
1145 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1146 /* Caller will most probably send a Imm Ass Reject after return */
1147 return NULL;
1148 }
1149 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1150 return ul_tbf;
1151}
1152
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001153/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1154 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1155 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1156 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1157struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1158{
1159 struct gprs_rlcmac_ul_tbf *ul_tbf;
1160 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1161 if (!ul_tbf)
1162 return NULL;
1163 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1164 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1165
1166 return ul_tbf;
1167}
1168
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001169/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1170 * "tbf" may be either a UL-TBF or a DL-TBF.
1171 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1172 */
1173int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001174{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001175 OSMO_ASSERT(tbf);
1176 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1177 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001178 struct gprs_rlcmac_dl_tbf *dl_tbf;
1179
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001180 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1181 if (!dl_tbf) {
1182 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1183 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001184 }
1185
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001186 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1187 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1188 return 0;
1189}
1190
1191/* A new DL-TBF is allocated and assigned through PCH.
1192 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1193 */
1194int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1195{
1196 const int8_t trx_no = -1;
1197 const bool single_slot = true;
1198 struct gprs_rlcmac_dl_tbf *dl_tbf;
1199
1200 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001201 if (!dl_tbf) {
1202 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1203 return -EBUSY;
1204 }
1205
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001206 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1207 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001208 return 0;
1209}
1210
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001211int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1212{
1213 struct timespec expire_time;
1214 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001215 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001216
1217 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1218
1219 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1220 if (!llc_msg)
1221 return -ENOMEM;
1222
1223 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1224 memcpy(msgb_put(llc_msg, len), data, len);
1225 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1226 ms_start_llc_timer(ms);
1227
1228 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001229 if (dl_tbf) {
1230 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1231 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001232 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001233 }
1234 } else {
1235 /* Check if we can create a DL TBF to start sending the enqueued
1236 * data. Otherwise it will be triggered later when it is reachable
Pau Espin Pedrol84b0ebc2023-04-19 18:45:21 +02001237 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001238 if (ms_is_reachable_for_dl_ass(ms)) {
1239 if (ms_ul_tbf(ms))
1240 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1241 else
1242 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1243 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001244 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001245 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001246}