blob: 0018252031526ccf16f3b69061210d6a84f2fe02 [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 Pedrolf086fed2023-04-20 16:52:40 +0200154 osmo_timer_setup(&ms->release_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 Pedroleae91472023-04-19 19:42:05 +0200173 if (use_ref)
174 ms_ref(ms, use_ref);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100175 return ms;
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100176free_ret:
177 talloc_free(ms);
178 return NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100179}
180
181static int ms_talloc_destructor(struct GprsMs *ms)
182{
183 struct llist_item *pos, *tmp;
184
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200185 LOGPMS(ms, DMS, LOGL_INFO, "Destroying MS object\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100186
Pau Espin Pedrol9da06862023-04-17 19:00:04 +0200187 bts_stat_item_dec(ms->bts, STAT_MS_PRESENT);
Pau Espin Pedrolfe7aee92023-04-17 18:23:56 +0200188 llist_del(&ms->list);
189
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100190 ms_set_reserved_slots(ms, NULL, 0, 0);
191
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200192 osmo_timer_del(&ms->release_timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100193
194 if (ms->ul_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200195 tbf_set_ms(ul_tbf_as_tbf(ms->ul_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100196 ms->ul_tbf = NULL;
197 }
198
199 if (ms->dl_tbf) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200200 tbf_set_ms(dl_tbf_as_tbf(ms->dl_tbf), NULL);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100201 ms->dl_tbf = NULL;
202 }
203
204 llist_for_each_entry_safe(pos, tmp, &ms->old_tbfs, list) {
205 struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)pos->entry;
206 tbf_set_ms(tbf, NULL);
207 }
208
209 llc_queue_clear(&ms->llc_queue, ms->bts);
Pau Espin Pedrol14beef62022-10-26 19:44:07 +0200210 osmo_timer_del(&ms->llc_timer);
Pau Espin Pedrolbed48cc2021-01-11 17:32:18 +0100211
212 if (ms->ctrs)
213 rate_ctr_group_free(ms->ctrs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100214 return 0;
215}
216
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200217/* MS has no attached TBFs anymore. */
218static void ms_becomes_idle(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100219{
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200220 unsigned long delay_rel_sec = osmo_tdef_get(ms->bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1);
221
Pau Espin Pedrol4673eb02023-04-20 17:18:34 +0200222 osmo_gettimeofday(&ms->tv_idle_start, NULL);
223
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200224 ms_set_reserved_slots(ms, NULL, 0, 0);
225 ms->first_common_ts = NULL;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100226
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200227 /* Immediate free():
228 * Skip delaying free() through release timer if delay is configured to be 0.
229 * This is useful for synced freed during unit tests.
230 */
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200231 if (delay_rel_sec == 0) {
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200232 talloc_free(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100233 return;
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200234 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100235
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200236 /* Immediate free():
237 * Skip delaying free() through release timer if TMSI is not
238 * known, since those cannot really be reused.
239 */
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200240 if (ms_tlli(ms) == GSM_RESERVED_TMSI) {
241 talloc_free(ms);
Pau Espin Pedrol14379ef2023-04-17 20:43:26 +0200242 return;
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200243 }
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100244
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200245 LOGPMS(ms, DMS, LOGL_INFO, "Schedule MS release in %lu secs\n", delay_rel_sec);
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200246 osmo_timer_schedule(&ms->release_timer, delay_rel_sec, 0);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100247}
248
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200249static void ms_becomes_active(struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100250{
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200251 if (!osmo_timer_pending(&ms->release_timer))
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100252 return;
253
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200254 LOGPMS(ms, DMS, LOGL_DEBUG, "Cancel scheduled MS release\n");
Pau Espin Pedrol4fe09012021-03-03 17:30:50 +0100255
Pau Espin Pedrol4673eb02023-04-20 17:18:34 +0200256 timerclear(&ms->tv_idle_start);
Pau Espin Pedrolf086fed2023-04-20 16:52:40 +0200257 osmo_timer_del(&ms->release_timer);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100258}
259
260void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
261{
262 ms->mode = mode;
263
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100264 switch (ms->mode) {
265 case GPRS:
266 if (!mcs_is_gprs(ms->current_cs_ul)) {
267 ms->current_cs_ul = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100268 ms->bts->initial_cs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100269 if (!mcs_is_valid(ms->current_cs_ul))
270 ms->current_cs_ul = CS1;
271 }
272 if (!mcs_is_gprs(ms->current_cs_dl)) {
273 ms->current_cs_dl = mcs_get_gprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100274 ms->bts->initial_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100275 if (!mcs_is_valid(ms->current_cs_dl))
276 ms->current_cs_dl = CS1;
277 }
278 break;
279
280 case EGPRS_GMSK:
Pau Espin Pedrol7bb8cd62021-01-25 15:08:35 +0100281 if (!mcs_is_edge_gmsk(ms->current_cs_ul)) {
282 ms->current_cs_ul = mcs_get_egprs_by_num(
283 ms->bts->initial_mcs_ul);
284 if (!mcs_is_valid(ms->current_cs_ul))
285 ms->current_cs_ul = MCS1;
286 }
287 if (!mcs_is_edge_gmsk(ms->current_cs_dl)) {
288 ms->current_cs_dl = mcs_get_egprs_by_num(
289 ms->bts->initial_mcs_dl);
290 if (!mcs_is_valid(ms->current_cs_dl))
291 ms->current_cs_dl = MCS1;
292 }
293 break;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100294 case EGPRS:
295 if (!mcs_is_edge(ms->current_cs_ul)) {
296 ms->current_cs_ul = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100297 ms->bts->initial_mcs_ul);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100298 if (!mcs_is_valid(ms->current_cs_ul))
299 ms->current_cs_ul = MCS1;
300 }
301 if (!mcs_is_edge(ms->current_cs_dl)) {
302 ms->current_cs_dl = mcs_get_egprs_by_num(
Pau Espin Pedrol2182e622021-01-14 16:48:38 +0100303 ms->bts->initial_mcs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100304 if (!mcs_is_valid(ms->current_cs_dl))
305 ms->current_cs_dl = MCS1;
306 }
307 break;
308 }
309}
310
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200311/* If a TBF is attached to an MS, it is either in ms->{dl,ul}_tbf or in ms->old_tbfs list */
312static bool ms_tbf_is_attached(const struct GprsMs *ms, const struct gprs_rlcmac_tbf *tbf)
313{
314 const struct llist_item *pos;
315 OSMO_ASSERT(ms);
316 OSMO_ASSERT(tbf);
317 OSMO_ASSERT(tbf_ms(tbf) == ms);
318
319 if (tbf == ul_tbf_as_tbf_const(ms->ul_tbf))
320 return true;
321
322 if (tbf == dl_tbf_as_tbf_const(ms->dl_tbf))
323 return true;
324
325 llist_for_each_entry(pos, &ms->old_tbfs, list) {
326 const struct gprs_rlcmac_tbf *tmp_tbf = (struct gprs_rlcmac_tbf *)pos->entry;
327 if (tmp_tbf == tbf)
328 return true;
329 }
330 return false;
331}
332
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100333static void ms_attach_ul_tbf(struct GprsMs *ms, struct gprs_rlcmac_ul_tbf *tbf)
334{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200335 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 +0100336
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100337 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200338 llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100339
340 ms->ul_tbf = tbf;
341
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200342 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100343}
344
345static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
346{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200347 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 +0100348
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100349 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200350 llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100351
352 ms->dl_tbf = tbf;
353
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200354 ms_ref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100355}
356
357void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
358{
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200359 OSMO_ASSERT(ms);
360 OSMO_ASSERT(tbf);
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200361 OSMO_ASSERT(!ms_tbf_is_attached(ms, tbf));
Pau Espin Pedrol3ea467d2023-04-18 16:33:34 +0200362
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200363 if (tbf_direction(tbf) == GPRS_RLCMAC_DL_TBF)
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200364 ms_attach_dl_tbf(ms, tbf_as_dl_tbf(tbf));
Pau Espin Pedrolb5fece92021-08-23 16:58:04 +0200365 else
Pau Espin Pedrolcc30b052022-10-27 15:25:55 +0200366 ms_attach_ul_tbf(ms, tbf_as_ul_tbf(tbf));
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200367
368 ms_becomes_active(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100369}
370
371void ms_detach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
372{
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200373 OSMO_ASSERT(tbf_ms(tbf) == ms);
374
375 /* In general this should not happen, but it can happen if during TBF
376 * allocation something fails before tbf->setup() called ms_attach_tbf(). */
377 if (!ms_tbf_is_attached(ms, tbf))
378 return;
379
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200380 LOGPMS(ms, DMS, LOGL_INFO, "Detaching TBF: %s\n",
Pau Espin Pedroldf6684f2023-04-18 17:50:34 +0200381 tbf_name(tbf));
382
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200383 if (tbf == ul_tbf_as_tbf(ms->ul_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100384 ms->ul_tbf = NULL;
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200385 } else if (tbf == dl_tbf_as_tbf(ms->dl_tbf)) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100386 ms->dl_tbf = NULL;
387 } else {
Pau Espin Pedrol6efa3812023-04-18 17:31:02 +0200388 /* We know from ms_tbf_is_attached()==true check above that tbf
389 * is in ms->old_tbfs, no need to look it up again. */
390 llist_del(tbf_ms_list(tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100391 }
392
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200393 ms_unref(ms, MS_USE_TBF);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100394}
395
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200396/* Cleans up old MS being merged into a new one. Should be called with a
397ms_ref() taken to avoid use-after-free. */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100398void ms_reset(struct GprsMs *ms)
399{
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200400 LOGPMS(ms, DMS, LOGL_INFO, "Clearing MS object\n");
Pau Espin Pedrolac4d4a62023-04-18 19:02:55 +0200401 struct llist_item *pos;
402 struct gprs_rlcmac_tbf *tbf;
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
Pau Espin Pedrolb53230a2023-04-20 16:42:54 +0200418 * shows up specially if listed in VTY. Furthermore, it will also trigger
419 * immediate free() when it becomes idle: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100420 ms->tlli = GSM_RESERVED_TMSI;
421 ms->new_dl_tlli = ms->tlli;
422 ms->new_ul_tlli = ms->tlli;
423 ms->imsi[0] = '\0';
424}
425
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200426/* This function should be called on the MS object of a TBF each time an RLCMAC
427 * block is received for it with TLLI information.
428 * Besides updating the TLLI field on the MS object, it also seeks for other MS
429 * objects in the store and merges them into the current MS object. The MS
430 * duplication happened because we don't learn the TLLI of the created TBF until
431 * a later point. */
432void ms_update_announced_tlli(struct GprsMs *ms, uint32_t tlli)
433{
434 struct GprsMs *old_ms = NULL;
435
436 if (tlli == GSM_RESERVED_TMSI)
437 return;
438
439 /* When the TLLI does not match the ms, check if there is another
440 * MS object that belongs to that TLLI and if yes make sure one of them
441 * gets deleted. */
442 if (!ms_check_tlli(ms, tlli))
Pau Espin Pedroleb0a0522023-04-17 16:33:35 +0200443 old_ms = bts_get_ms_by_tlli(ms->bts, tlli, GSM_RESERVED_TMSI);
Pau Espin Pedrol8abe13c2022-10-21 18:49:48 +0200444
445 ms_set_tlli(ms, tlli);
446
447 if (old_ms)
448 ms_merge_and_clear_ms(ms, old_ms);
449 /* old_ms may no longer be available here */
450}
451
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100452/* Merge 'old_ms' object into 'ms' object.
453 * 'old_ms' may be freed during the call to this function, don't use the pointer to it afterwards */
454void ms_merge_and_clear_ms(struct GprsMs *ms, struct GprsMs *old_ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100455{
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100456 char old_ms_name[128];
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100457 OSMO_ASSERT(old_ms != ms);
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200458 ms_ref(old_ms, __func__);
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100459
460 ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
461
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200462 LOGPMS(ms, DMS, LOGL_INFO, "Merge MS: %s\n", old_ms_name);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100463
464 if (strlen(ms_imsi(ms)) == 0 && strlen(ms_imsi(old_ms)) != 0)
465 osmo_strlcpy(ms->imsi, ms_imsi(old_ms), sizeof(ms->imsi));
466
467 if (!ms_ms_class(ms) && ms_ms_class(old_ms))
468 ms_set_ms_class(ms, ms_ms_class(old_ms));
469
470 if (!ms_egprs_ms_class(ms) && ms_egprs_ms_class(old_ms))
471 ms_set_egprs_ms_class(ms, ms_egprs_ms_class(old_ms));
472
473 llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
474
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100475 /* Clean up the old MS object */
Pau Espin Pedrol32416fd2022-10-31 12:55:03 +0100476 ms_reset(old_ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100477
Pau Espin Pedrol403e0482023-04-17 20:28:10 +0200478 ms_unref(old_ms, __func__);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100479}
480
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100481/* Apply changes to the TLLI directly, used interally by functions below: */
482static void ms_apply_tlli_change(struct GprsMs *ms, uint32_t tlli)
483{
484 ms->tlli = tlli;
485 ms->new_dl_tlli = GSM_RESERVED_TMSI;
486 ms->new_ul_tlli = GSM_RESERVED_TMSI;
487
488 /* Update TBF FSM names: */
489 if (ms->ul_tbf)
490 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
491 if (ms->dl_tbf)
492 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
493}
494
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200495/* Set/update the MS object TLLI based on knowledge gained from the MS side (Uplink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100496void ms_set_tlli(struct GprsMs *ms, uint32_t tlli)
497{
498 if (tlli == ms->tlli || tlli == ms->new_ul_tlli)
499 return;
500
501 if (tlli != ms->new_dl_tlli) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200502 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100503 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
504 "not yet confirmed\n",
505 ms_tlli(ms), tlli);
506 ms->new_ul_tlli = tlli;
507 return;
508 }
509
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200510 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100511 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
512 "already confirmed partly\n",
513 ms->tlli, tlli);
514
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100515 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100516}
517
Pau Espin Pedrol0b5997e2022-10-21 14:10:41 +0200518/* Set/update the MS object TLLI based on knowledge gained from the SGSN side (Downlink direction) */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100519bool ms_confirm_tlli(struct GprsMs *ms, uint32_t tlli)
520{
521 if (tlli == ms->tlli || tlli == ms->new_dl_tlli)
522 return false;
523
524 if (tlli != ms->new_ul_tlli) {
525 /* The MS has not sent a message with the new TLLI, which may
526 * happen according to the spec [TODO: add reference]. */
527
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200528 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100529 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
530 "partly confirmed\n", tlli);
531 /* Use the network's idea of TLLI as candidate, this does not
532 * change the result value of tlli() */
533 ms->new_dl_tlli = tlli;
534 return false;
535 }
536
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200537 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100538 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
539
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100540 ms_apply_tlli_change(ms, tlli);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100541
542 return true;
543}
544
545void ms_set_imsi(struct GprsMs *ms, const char *imsi)
546{
547 if (!imsi) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200548 LOGP(DMS, LOGL_ERROR, "Expected IMSI!\n");
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100549 return;
550 }
551
552 if (imsi[0] && strlen(imsi) < 3) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200553 LOGP(DMS, LOGL_ERROR, "No valid IMSI '%s'!\n",
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100554 imsi);
555 return;
556 }
557
558 if (strcmp(imsi, ms->imsi) == 0)
559 return;
560
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200561 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100562 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
563 ms_tlli(ms), ms->imsi, imsi);
564
Pau Espin Pedrolcde18c52023-04-17 18:16:48 +0200565 struct GprsMs *old_ms = bts_get_ms_by_imsi(ms->bts, imsi);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100566 /* Check if we are going to store a different MS object with already
567 existing IMSI. This is probably a bug in code calling this function,
568 since it should take care of this explicitly */
569 if (old_ms) {
570 /* We cannot find ms->ms by IMSI since we know that it has a
571 * different IMSI */
572 OSMO_ASSERT(old_ms != ms);
573
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200574 LOGPMS(ms, DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100575 "IMSI '%s' was already assigned to another "
576 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
577 imsi, ms_tlli(old_ms));
578
579 ms_merge_and_clear_ms(ms, old_ms);
Pau Espin Pedrolb8ff74b2022-10-31 12:58:46 +0100580 /* old_ms may no longer be available here */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100581 }
582
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100583 /* Store the new IMSI: */
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100584 osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi));
Pau Espin Pedrol57843c52022-11-03 13:35:19 +0100585
586 /* Update TBF FSM names: */
587 if (ms->ul_tbf)
588 tbf_update_state_fsm_name(ul_tbf_as_tbf(ms->ul_tbf));
589 if (ms->dl_tbf)
590 tbf_update_state_fsm_name(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100591}
592
593void ms_set_ta(struct GprsMs *ms, uint8_t ta_)
594{
595 if (ta_ == ms->ta)
596 return;
597
598 if (gsm48_ta_is_valid(ta_)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200599 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100600 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
601 ms_tlli(ms), ms->ta, ta_);
602 ms->ta = ta_;
603 } else
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200604 LOGP(DMS, LOGL_NOTICE,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100605 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
606 "value %d kept)\n", ms_tlli(ms), ta_, ms->ta);
607}
608
609void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_)
610{
611 if (ms_class_ == ms->ms_class)
612 return;
613
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200614 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100615 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
616 ms_tlli(ms), ms->ms_class, ms_class_);
617
618 ms->ms_class = ms_class_;
619}
620
621void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_)
622{
623 if (ms_class_ == ms->egprs_ms_class)
624 return;
625
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200626 LOGP(DMS, LOGL_INFO,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100627 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
628 ms_tlli(ms), ms->egprs_ms_class, ms_class_);
629
630 ms->egprs_ms_class = ms_class_;
631
632 if (!bts_max_mcs_ul(ms->bts) || !bts_max_mcs_dl(ms->bts)) {
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200633 LOGPMS(ms, DMS, LOGL_DEBUG,
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100634 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
635 bts_max_mcs_ul(ms->bts), bts_max_mcs_dl(ms->bts));
636 return;
637 }
638
639 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts))) &&
640 mcs_is_edge_gmsk(mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts))) &&
641 ms_mode(ms) != EGPRS)
642 {
643 ms_set_mode(ms, EGPRS_GMSK);
644 } else {
645 ms_set_mode(ms, EGPRS);
646 }
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200647 LOGPMS(ms, DMS, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(ms_mode(ms)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100648}
649
650void ms_update_error_rate(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf, int error_rate)
651{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100652 int64_t now;
653 enum CodingScheme max_cs_dl = ms_max_cs_dl(ms);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100654 OSMO_ASSERT(max_cs_dl);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100655
656 if (error_rate < 0)
657 return;
658
659 now = now_msec();
660
661 /* TODO: Check for TBF direction */
662 /* TODO: Support different CS values for UL and DL */
663
664 ms->nack_rate_dl = error_rate;
665
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100666 if (error_rate > the_pcu->vty.cs_adj_upper_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100667 if (mcs_chan_code(ms->current_cs_dl) > 0) {
668 mcs_dec_kind(&ms->current_cs_dl, ms_mode(ms));
669 LOGP(DRLCMACDL, LOGL_INFO,
670 "MS (IMSI %s): High error rate %d%%, "
671 "reducing CS level to %s\n",
672 ms_imsi(ms), error_rate, mcs_name(ms->current_cs_dl));
673 ms->last_cs_not_low = now;
674 }
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100675 } else if (error_rate < the_pcu->vty.cs_adj_lower_limit) {
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100676 if (ms->current_cs_dl < max_cs_dl) {
677 if (now - ms->last_cs_not_low > 1000) {
678 mcs_inc_kind(&ms->current_cs_dl, ms_mode(ms));
679
680 LOGP(DRLCMACDL, LOGL_INFO,
681 "MS (IMSI %s): Low error rate %d%%, "
682 "increasing DL CS level to %s\n",
683 ms_imsi(ms), error_rate,
684 mcs_name(ms->current_cs_dl));
685 ms->last_cs_not_low = now;
686 } else {
687 LOGP(DRLCMACDL, LOGL_DEBUG,
688 "MS (IMSI %s): Low error rate %d%%, "
689 "ignored (within blocking period)\n",
690 ms_imsi(ms), error_rate);
691 }
692 }
693 } else {
694 LOGP(DRLCMACDL, LOGL_DEBUG,
695 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
696 ms_imsi(ms), error_rate);
697 ms->last_cs_not_low = now;
698 }
699}
700
701enum CodingScheme ms_max_cs_ul(const struct GprsMs *ms)
702{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100703 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100704 OSMO_ASSERT(ms->bts != NULL);
705
706 if (mcs_is_gprs(ms->current_cs_ul)) {
707 if (!bts_max_cs_ul(ms->bts)) {
708 return CS4;
709 }
710
711 return mcs_get_gprs_by_num(bts_max_cs_ul(ms->bts));
712 }
713
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100714 cs = mcs_get_egprs_by_num(bts_max_mcs_ul(ms->bts));
715 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
716 cs = MCS4;
717 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100718}
719
720void ms_set_current_cs_dl(struct GprsMs *ms, enum CodingScheme scheme)
721{
722 ms->current_cs_dl = scheme;
723}
724
725enum CodingScheme ms_max_cs_dl(const struct GprsMs *ms)
726{
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100727 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100728 OSMO_ASSERT(ms->bts != NULL);
729
730 if (mcs_is_gprs(ms->current_cs_dl)) {
731 if (!bts_max_cs_dl(ms->bts)) {
732 return CS4;
733 }
734
735 return mcs_get_gprs_by_num(bts_max_cs_dl(ms->bts));
736 }
737
Pau Espin Pedrol201da4e2021-01-25 15:10:33 +0100738 cs = mcs_get_egprs_by_num(bts_max_mcs_dl(ms->bts));
739 if (ms_mode(ms) == EGPRS_GMSK && cs > MCS4)
740 cs = MCS4;
741 return cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100742}
743
744void ms_update_cs_ul(struct GprsMs *ms, const struct pcu_l1_meas *meas)
745{
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100746 enum CodingScheme max_cs_ul = ms_max_cs_ul(ms);
747
748 int old_link_qual;
749 int low;
750 int high;
751 enum CodingScheme new_cs_ul = ms->current_cs_ul;
752 uint8_t current_cs = mcs_chan_code(ms->current_cs_ul);
753
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100754 if (!max_cs_ul) {
755 LOGP(DRLCMACMEAS, LOGL_ERROR,
756 "max_cs_ul cannot be derived (current UL CS: %s)\n",
757 mcs_name(ms->current_cs_ul));
758 return;
759 }
760
761 if (!ms->current_cs_ul) {
762 LOGP(DRLCMACMEAS, LOGL_ERROR,
763 "Unable to update UL (M)CS because it's not set: %s\n",
764 mcs_name(ms->current_cs_ul));
765 return;
766 }
767
768 if (!meas->have_link_qual) {
769 LOGP(DRLCMACMEAS, LOGL_ERROR,
770 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
771 mcs_name(ms->current_cs_ul));
772 return;
773 }
774
775 if (mcs_is_gprs(ms->current_cs_ul)) {
776 if (current_cs >= MAX_GPRS_CS)
777 current_cs = MAX_GPRS_CS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100778 low = the_pcu->vty.cs_lqual_ranges[current_cs].low;
779 high = the_pcu->vty.cs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100780 } else if (mcs_is_edge(ms->current_cs_ul)) {
781 if (current_cs >= MAX_EDGE_MCS)
782 current_cs = MAX_EDGE_MCS - 1;
Pau Espin Pedrol54b159a2021-01-14 13:30:04 +0100783 low = the_pcu->vty.mcs_lqual_ranges[current_cs].low;
784 high = the_pcu->vty.mcs_lqual_ranges[current_cs].high;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100785 } else {
786 LOGP(DRLCMACMEAS, LOGL_ERROR,
787 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
788 mcs_name(ms->current_cs_ul));
789 return;
790 }
791
792 /* To avoid rapid changes of the coding scheme, we also take
793 * the old link quality value into account (if present). */
794 if (ms->l1_meas.have_link_qual)
795 old_link_qual = ms->l1_meas.link_qual;
796 else
797 old_link_qual = meas->link_qual;
798
799 if (meas->link_qual < low && old_link_qual < low)
800 mcs_dec_kind(&new_cs_ul, ms_mode(ms));
801 else if (meas->link_qual > high && old_link_qual > high &&
802 ms->current_cs_ul < max_cs_ul)
803 mcs_inc_kind(&new_cs_ul, ms_mode(ms));
804
805 if (ms->current_cs_ul != new_cs_ul) {
806 LOGPMS(ms, DRLCMACMEAS, LOGL_INFO,
807 "Link quality %ddB (old %ddB) left window [%d, %d], "
808 "modifying uplink CS level: %s -> %s\n",
809 meas->link_qual, old_link_qual,
810 low, high,
811 mcs_name(ms->current_cs_ul), mcs_name(new_cs_ul));
812
813 ms->current_cs_ul = new_cs_ul;
814 }
815}
816
817void ms_update_l1_meas(struct GprsMs *ms, const struct pcu_l1_meas *meas)
818{
819 unsigned i;
820
821 ms_update_cs_ul(ms, meas);
822
823 if (meas->have_rssi)
824 pcu_l1_meas_set_rssi(&ms->l1_meas, meas->rssi);
825 if (meas->have_bto)
826 pcu_l1_meas_set_bto(&ms->l1_meas, meas->bto);
827 if (meas->have_ber)
828 pcu_l1_meas_set_ber(&ms->l1_meas, meas->ber);
829 if (meas->have_link_qual)
830 pcu_l1_meas_set_link_qual(&ms->l1_meas, meas->link_qual);
831
832 if (meas->have_ms_rx_qual)
833 pcu_l1_meas_set_ms_rx_qual(&ms->l1_meas, meas->ms_rx_qual);
834 if (meas->have_ms_c_value)
835 pcu_l1_meas_set_ms_c_value(&ms->l1_meas, meas->ms_c_value);
836 if (meas->have_ms_sign_var)
837 pcu_l1_meas_set_ms_sign_var(&ms->l1_meas, meas->ms_sign_var);
838
839 if (meas->have_ms_i_level) {
840 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
841 if (meas->ts[i].have_ms_i_level)
842 pcu_l1_meas_set_ms_i_level(&ms->l1_meas, i, meas->ts[i].ms_i_level);
843 else
844 ms->l1_meas.ts[i].have_ms_i_level = 0;
845 }
846 }
847}
848
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100849/* req_mcs_kind acts as a set filter, where EGPRS means any and GPRS is the most restrictive */
850enum CodingScheme ms_current_cs_dl(const struct GprsMs *ms, enum mcs_kind req_mcs_kind)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100851{
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100852 enum CodingScheme orig_cs = ms->current_cs_dl;
853 struct gprs_rlcmac_bts *bts = ms->bts;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100854 size_t unencoded_octets;
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100855 enum CodingScheme cs;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100856
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100857 /* It could be that a TBF requests a GPRS CS despite the MS currently
858 being upgraded to EGPRS (hence reporting MCS). That could happen
859 because the TBF was created early in the process where we didn't have
860 yet enough information about the MS, and only AFTER it was created we
861 upgraded the MS to be EGPRS capable.
862 As a result, when the MS is queried for the target CS here, we could be
863 returning an MCS despite the current TBF being established as GPRS,
864 but we rather stick to the TBF type we assigned to the MS rather than
865 magically sending EGPRS data blocks to a GPRS TBF.
866 It could also be that the caller requests specific MCS kind
867 explicitly too due to scheduling restrictions (GPRS+EGPRS multiplexing). */
868 if (req_mcs_kind == EGPRS_GMSK && mcs_is_edge(orig_cs) && orig_cs > MCS4) {
869 cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
870 bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
871 bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
872 MCS1;
873 } else if (req_mcs_kind == GPRS && mcs_is_edge(orig_cs)) { /* GPRS */
874 int i;
875 cs = orig_cs > MCS4 ? MCS4 : orig_cs;
876 cs -= (MCS1 - CS1); /* MCSx -> CSx */
877 /* Find suitable CS starting from equivalent MCS which is supported by BTS: */
878 for (i = mcs_chan_code(cs); !bts_cs_dl_is_supported(bts, CS1 + i); i--);
879 OSMO_ASSERT(i >= 0 && i <= 3); /* CS1 is always supported */
880 cs = CS1 + i;
881 } else {
882 cs = orig_cs;
883 }
884
885 if (orig_cs != cs)
Pau Espin Pedrolfe4d2f72023-04-19 20:38:40 +0200886 LOGPMS(ms, DMS, LOGL_INFO, "MS (mode=%s) suggests transmitting "
Pau Espin Pedrolfc464932021-01-25 12:05:32 +0100887 "DL %s, downgrade to %s in order to match TBF & scheduler requirements\n",
888 mode_name(ms_mode(ms)), mcs_name(orig_cs), mcs_name(cs));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100889
890 unencoded_octets = llc_queue_octets(&ms->llc_queue);
891
892 /* If the DL TBF is active, add number of unencoded chunk octets */
893 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200894 unencoded_octets += llc_chunk_size(tbf_llc(dl_tbf_as_tbf(ms->dl_tbf)));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100895
896 /* There are many unencoded octets, don't reduce */
Pau Espin Pedrolad79b852021-01-14 13:20:55 +0100897 if (unencoded_octets >= the_pcu->vty.cs_downgrade_threshold)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100898 return cs;
899
900 /* RF conditions are good, don't reduce */
Pau Espin Pedrole8dcf642021-01-14 13:17:01 +0100901 if (ms->nack_rate_dl < the_pcu->vty.cs_adj_lower_limit)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100902 return cs;
903
904 /* The throughput would probably be better if the CS level was reduced */
905 mcs_dec_kind(&cs, ms_mode(ms));
906
907 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
908 if (cs == CS2)
909 mcs_dec_kind(&cs, ms_mode(ms));
910
911 return cs;
912}
913
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100914struct gprs_rlcmac_pdch *ms_first_common_ts(const struct GprsMs *ms)
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100915{
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100916 return ms->first_common_ts;
917}
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100918
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100919void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
Pau Espin Pedrol345d9ad2022-12-12 19:22:44 +0100920{
Pau Espin Pedrol9935d0d2022-12-13 18:29:25 +0100921 OSMO_ASSERT(pdch);
922 ms->first_common_ts = pdch;
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100923}
924
925uint8_t ms_dl_slots(const struct GprsMs *ms)
926{
927 uint8_t slots = 0;
928
929 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200930 slots |= tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100931
932 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200933 slots |= tbf_dl_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100934
935 return slots;
936}
937
938uint8_t ms_ul_slots(const struct GprsMs *ms)
939{
940 uint8_t slots = 0;
941
942 if (ms->dl_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200943 slots |= tbf_ul_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100944
945 if (ms->ul_tbf)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200946 slots |= tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100947
948 return slots;
949}
950
951uint8_t ms_current_pacch_slots(const struct GprsMs *ms)
952{
953 uint8_t slots = 0;
954
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200955 bool is_dl_active = ms->dl_tbf && tbf_is_tfi_assigned(dl_tbf_as_tbf(ms->dl_tbf));
956 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 +0100957
958 if (!is_dl_active && !is_ul_active)
959 return 0;
960
961 /* see TS 44.060, 8.1.1.2.2 */
962 if (is_dl_active && !is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200963 slots = tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100964 else if (!is_dl_active && is_ul_active)
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200965 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100966 else
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +0200967 slots = tbf_ul_slots(ul_tbf_as_tbf(ms->ul_tbf)) &
968 tbf_dl_slots(dl_tbf_as_tbf(ms->dl_tbf));
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +0100969
970 /* Assume a multislot class 1 device */
971 /* TODO: For class 2 devices, this could be removed */
972 slots = pcu_lsb(slots);
973
974 return slots;
975}
976
977void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
978 uint8_t ul_slots, uint8_t dl_slots)
979{
980 if (ms->current_trx) {
981 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
982 ms->reserved_dl_slots);
983 bts_trx_unreserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
984 ms->reserved_ul_slots);
985 ms->reserved_dl_slots = 0;
986 ms->reserved_ul_slots = 0;
987 }
988 ms->current_trx = trx;
989 if (trx) {
990 ms->reserved_dl_slots = dl_slots;
991 ms->reserved_ul_slots = ul_slots;
992 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_DL_TBF,
993 ms->reserved_dl_slots);
994 bts_trx_reserve_slots(ms->current_trx, GPRS_RLCMAC_UL_TBF,
995 ms->reserved_ul_slots);
996 }
997}
998
999struct gprs_rlcmac_tbf *ms_tbf(const struct GprsMs *ms, enum gprs_rlcmac_tbf_direction dir)
1000{
1001 switch (dir) {
Pau Espin Pedrol1e16e1d2022-10-27 15:40:20 +02001002 case GPRS_RLCMAC_DL_TBF: return dl_tbf_as_tbf(ms->dl_tbf);
1003 case GPRS_RLCMAC_UL_TBF: return ul_tbf_as_tbf(ms->ul_tbf);
Pau Espin Pedrolda971ee2020-12-16 15:59:45 +01001004 }
1005
1006 return NULL;
1007}
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001008
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001009const char *ms_name(const struct GprsMs *ms)
1010{
1011 static char _ms_name_buf[128];
1012 return ms_name_buf(ms, _ms_name_buf, sizeof(_ms_name_buf));
1013}
1014
1015char *ms_name_buf(const struct GprsMs *ms, char *buf, unsigned int buf_size)
1016{
Pau Espin Pedrol94f82582022-11-03 14:16:17 +01001017 struct osmo_strbuf sb = { .buf = buf, .len = buf_size };
1018 uint32_t tlli = ms_tlli(ms);
1019
1020 OSMO_STRBUF_PRINTF(sb, "MS(");
1021 if (ms_imsi_is_valid(ms))
1022 OSMO_STRBUF_PRINTF(sb, "IMSI-%s:", ms_imsi(ms));
1023 if (tlli != GSM_RESERVED_TMSI)
1024 OSMO_STRBUF_PRINTF(sb, "TLLI-0x%08x:", tlli);
1025 OSMO_STRBUF_PRINTF(sb, "TA-%" PRIu8 ":MSCLS-%" PRIu8 "-%" PRIu8,
1026 ms_ta(ms), ms_ms_class(ms), ms_egprs_ms_class(ms));
1027 if (ms->ul_tbf)
1028 OSMO_STRBUF_PRINTF(sb, ":UL");
1029 if (ms->dl_tbf)
1030 OSMO_STRBUF_PRINTF(sb, ":DL");
1031
1032 OSMO_STRBUF_PRINTF(sb, ")");
Pau Espin Pedrol3547d642022-10-21 15:00:08 +02001033 return buf;
1034}
1035
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001036int ms_nacc_start(struct GprsMs *ms, Packet_Cell_Change_Notification_t *notif)
1037{
1038 if (!ms->nacc)
1039 ms->nacc = nacc_fsm_alloc(ms);
1040 if (!ms->nacc)
1041 return -EINVAL;
1042 return osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_RX_CELL_CHG_NOTIFICATION, notif);
1043}
1044
1045bool ms_nacc_rts(const struct GprsMs *ms)
1046{
1047 if (!ms->nacc)
1048 return false;
1049 if (ms->nacc->fi->state == NACC_ST_TX_NEIGHBOUR_DATA ||
1050 ms->nacc->fi->state == NACC_ST_TX_CELL_CHG_CONTINUE)
1051 return true;
1052 return false;
1053}
1054
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001055struct msgb *ms_nacc_create_rlcmac_msg(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf,
1056 const struct gprs_rlcmac_pdch *pdch, uint32_t fn)
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001057{
1058 int rc;
1059 struct nacc_ev_create_rlcmac_msg_ctx data_ctx;
1060
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001061 data_ctx = (struct nacc_ev_create_rlcmac_msg_ctx) {
1062 .tbf = tbf,
Pau Espin Pedrol5ba3ef92022-12-12 18:02:25 +01001063 .pdch = pdch,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001064 .fn = fn,
Pau Espin Pedrol952cb3d2021-02-01 14:52:48 +01001065 .msg = NULL,
1066 };
Pau Espin Pedrolc0a250d2021-01-21 18:46:13 +01001067
1068 rc = osmo_fsm_inst_dispatch(ms->nacc->fi, NACC_EV_CREATE_RLCMAC_MSG, &data_ctx);
1069 if (rc != 0 || !data_ctx.msg)
1070 return NULL;
1071 return data_ctx.msg;
1072}
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001073
1074static void ms_start_llc_timer(struct GprsMs *ms)
1075{
1076 if (the_pcu->vty.llc_idle_ack_csec > 0) {
1077 struct timespec tv;
1078 csecs_to_timespec(the_pcu->vty.llc_idle_ack_csec, &tv);
1079 osmo_timer_schedule(&ms->llc_timer, tv.tv_sec, tv.tv_nsec / 1000);
1080 }
1081}
1082
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001083/* Can we get to send a DL TBF ass to the MS? */
1084static bool ms_is_reachable_for_dl_ass(const struct GprsMs *ms)
1085{
1086 struct gprs_rlcmac_ul_tbf *ul_tbf = ms_ul_tbf(ms);
1087
1088 /* This function assumes it is called when no DL TBF is present */
1089 OSMO_ASSERT(!ms_dl_tbf(ms));
1090
1091 /* 3GPP TS 44.060 sec 7.1.3.1 Initiation of the Packet resource request procedure:
1092 * "Furthermore, the mobile station shall not respond to PACKET DOWNLINK ASSIGNMENT
1093 * or MULTIPLE TBF DOWNLINK ASSIGNMENT messages before contention resolution is
1094 * completed on the mobile station side." */
1095 /* The possible uplink TBF is used to trigger downlink assignment:
1096 * - If there is no uplink TBF the MS is potentially in packet idle mode
1097 * and hence assignment will be done over CCCH (PCH)
1098 * - If there's an uplink TBF but it is finished (waiting for last PKT
1099 * CTRL ACK after sending last Pkt UL ACK/NACK with FINAL_ACK=1, then we
1100 * have no ways to contact the MS right now. Assignment will be delayed
1101 * until PKT CTRL ACK is received and the TBF is released at the MS side
1102 * (then assignment goes through PCH).
1103 */
1104 if (!ul_tbf)
1105 return true;
1106 if (ul_tbf_contention_resolution_done(ul_tbf) &&
1107 !tbf_ul_ack_waiting_cnf_final_ack(ul_tbf))
1108 return true;
1109
1110 return false;
1111
1112}
1113
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001114/* Alloc a UL TBF to be assigned over PACCH. Called when an MS requests to
1115 * create a new UL TBF during the end of life of a previous UL TBF (or an SBA).
1116 * In summary, this TBF is allocated as a consequence of receiving a "Pkt
1117 * Resource Req" or "Pkt Ctrl Ack" from the MS.
1118 * See TS 44.060 9.3.2.4.2 "Non-extended uplink TBF mode".
1119 */
1120struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_pacch(struct GprsMs *ms, int8_t use_trx)
1121{
1122 const bool single_slot = false;
1123 struct gprs_rlcmac_ul_tbf *ul_tbf;
1124
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001125 ul_tbf = ul_tbf_alloc(ms->bts, ms, use_trx, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001126 if (!ul_tbf) {
1127 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1128 /* Caller will most probably send a Imm Ass Reject after return */
1129 return NULL;
1130 }
1131 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1132 /* Contention resolution is considered to be done since TLLI is known in MS */
1133 return ul_tbf;
1134}
1135
1136/* Alloc a UL TBF to be assigned over AGCH. Used by request of a "One phase
1137 * packet access", where MS requested only 1 PDCH TS (TS 44.018 Table 9.1.8.1). */
1138struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_assigned_agch(struct GprsMs *ms)
1139{
1140 const int8_t trx_no = -1;
1141 const bool single_slot = true;
1142 struct gprs_rlcmac_ul_tbf *ul_tbf;
1143
Pau Espin Pedrolbda7bb72022-10-31 14:33:09 +01001144 ul_tbf = ul_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol94386132022-10-28 19:50:09 +02001145 if (!ul_tbf) {
1146 LOGP(DTBF, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
1147 /* Caller will most probably send a Imm Ass Reject after return */
1148 return NULL;
1149 }
1150 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_CCCH, NULL);
1151 return ul_tbf;
1152}
1153
Pau Espin Pedrola621d592022-12-13 17:35:04 +01001154/* Create a temporary dummy TBF to Tx a ImmAssReject if allocating a new one during
1155 * packet resource Request failed. This is similar as ul_tbf_alloc() but without
1156 * calling tbf->setup() (in charge of TFI/USF allocation), and reusing resources
1157 * from Packet Resource Request we received. See TS 44.060 sec 7.1.3.2.1 */
1158struct gprs_rlcmac_ul_tbf *ms_new_ul_tbf_rejected_pacch(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch)
1159{
1160 struct gprs_rlcmac_ul_tbf *ul_tbf;
1161 ul_tbf = ul_tbf_alloc_rejected(ms->bts, ms, pdch);
1162 if (!ul_tbf)
1163 return NULL;
1164 osmo_fsm_inst_dispatch(tbf_state_fi(ul_tbf_as_tbf(ul_tbf)), TBF_EV_ASSIGN_ADD_PACCH, NULL);
1165 osmo_fsm_inst_dispatch(tbf_ul_ass_fi(ul_tbf_as_tbf(ul_tbf)), TBF_UL_ASS_EV_SCHED_ASS_REJ, NULL);
1166
1167 return ul_tbf;
1168}
1169
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001170/* A new DL-TBF is allocated and assigned through PACCH using "tbf".
1171 * "tbf" may be either a UL-TBF or a DL-TBF.
1172 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1173 */
1174int ms_new_dl_tbf_assigned_on_pacch(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001175{
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001176 OSMO_ASSERT(tbf);
1177 const int8_t trx_no = tbf_get_trx(tbf)->trx_no;
1178 const bool single_slot = false;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001179 struct gprs_rlcmac_dl_tbf *dl_tbf;
1180
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001181 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
1182 if (!dl_tbf) {
1183 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1184 return -EBUSY;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001185 }
1186
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001187 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PACCH)\n");
1188 dl_tbf_trigger_ass_on_pacch(dl_tbf, tbf);
1189 return 0;
1190}
1191
1192/* A new DL-TBF is allocated and assigned through PCH.
1193 * Note: This should be called only when MS is reachable, see ms_is_reachable_for_dl_ass().
1194 */
1195int ms_new_dl_tbf_assigned_on_pch(struct GprsMs *ms)
1196{
1197 const int8_t trx_no = -1;
1198 const bool single_slot = true;
1199 struct gprs_rlcmac_dl_tbf *dl_tbf;
1200
1201 dl_tbf = dl_tbf_alloc(ms->bts, ms, trx_no, single_slot);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001202 if (!dl_tbf) {
1203 LOGPMS(ms, DTBF, LOGL_NOTICE, "No PDCH resource\n");
1204 return -EBUSY;
1205 }
1206
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001207 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "[DOWNLINK] START (PCH)\n");
1208 dl_tbf_trigger_ass_on_pch(dl_tbf);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001209 return 0;
1210}
1211
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001212int ms_append_llc_dl_data(struct GprsMs *ms, uint16_t pdu_delay_csec, const uint8_t *data, uint16_t len)
1213{
1214 struct timespec expire_time;
1215 struct gprs_rlcmac_dl_tbf *dl_tbf;
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001216 int rc = 0;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001217
1218 LOGPMS(ms, DTBFDL, LOGL_DEBUG, "appending %u bytes to DL LLC queue\n", len);
1219
1220 struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
1221 if (!llc_msg)
1222 return -ENOMEM;
1223
1224 llc_queue_calc_pdu_lifetime(ms->bts, pdu_delay_csec, &expire_time);
1225 memcpy(msgb_put(llc_msg, len), data, len);
1226 llc_queue_enqueue(ms_llc_queue(ms), llc_msg, &expire_time);
1227 ms_start_llc_timer(ms);
1228
1229 dl_tbf = ms_dl_tbf(ms);
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001230 if (dl_tbf) {
1231 if (tbf_state(dl_tbf_as_tbf_const(dl_tbf)) == TBF_ST_WAIT_RELEASE) {
1232 LOGPTBFDL(dl_tbf, LOGL_DEBUG, "in WAIT RELEASE state (T3193), so reuse TBF\n");
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001233 rc = ms_new_dl_tbf_assigned_on_pacch(ms, dl_tbf_as_tbf(dl_tbf));
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001234 }
1235 } else {
1236 /* Check if we can create a DL TBF to start sending the enqueued
1237 * data. Otherwise it will be triggered later when it is reachable
Pau Espin Pedrol84b0ebc2023-04-19 18:45:21 +02001238 * again. */
Pau Espin Pedrol091642a2022-10-28 18:01:31 +02001239 if (ms_is_reachable_for_dl_ass(ms)) {
1240 if (ms_ul_tbf(ms))
1241 rc = ms_new_dl_tbf_assigned_on_pacch(ms, ul_tbf_as_tbf(ms_ul_tbf(ms)));
1242 else
1243 rc = ms_new_dl_tbf_assigned_on_pch(ms);
1244 }
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001245 }
Pau Espin Pedrol22b26d82022-10-26 15:44:14 +02001246 return rc;
Pau Espin Pedrol14beef62022-10-26 19:44:07 +02001247}