blob: 77a8f2ea46d943d2306cf5e16c3de4b692815a6e [file] [log] [blame]
Jacob Erlbecke04e0b02015-05-06 18:30:48 +02001/* gprs_ms.cpp
2 *
3 * Copyright (C) 2015 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21
22#include "gprs_ms.h"
Max1187a772018-01-26 13:31:42 +010023#include <gprs_coding_scheme.h>
Jacob Erlbecka700dd92015-06-02 16:00:41 +020024#include "bts.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020025#include "tbf.h"
26#include "gprs_debug.h"
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020027#include "gprs_codel.h"
Jacob Erlbeck7c72aca2016-01-22 17:06:14 +010028#include "pcu_utils.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020029
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020030#include <time.h>
31
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020032extern "C" {
33 #include <osmocom/core/talloc.h>
34 #include <osmocom/core/utils.h>
Max9bbe1602016-07-18 12:50:18 +020035 #include <osmocom/gsm/protocol/gsm_04_08.h>
Max1187a772018-01-26 13:31:42 +010036 #include <osmocom/core/logging.h>
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020037}
38
Jacob Erlbeck1c3b8992015-08-14 16:01:36 +020039#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020040
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020041extern void *tall_pcu_ctx;
42
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020043static int64_t now_msec()
44{
45 struct timespec ts;
46 clock_gettime(CLOCK_MONOTONIC, &ts);
47
48 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
49}
50
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020051struct GprsMsDefaultCallback: public GprsMs::Callback {
52 virtual void ms_idle(class GprsMs *ms) {
53 delete ms;
54 }
55 virtual void ms_active(class GprsMs *) {}
56};
57
58static GprsMsDefaultCallback gprs_default_cb;
59
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020060GprsMs::Guard::Guard(GprsMs *ms) :
61 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020062{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020063}
64
65GprsMs::Guard::~Guard()
66{
67 if (m_ms)
68 m_ms->unref();
69}
70
Jacob Erlbeckb2439bb2015-07-13 14:23:32 +020071bool GprsMs::Guard::is_idle() const
72{
73 if (!m_ms)
74 return true;
75
76 return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
77}
78
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020079void GprsMs::timeout(void *priv_)
80{
81 GprsMs *ms = static_cast<GprsMs *>(priv_);
82
83 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
84 ms->tlli());
85
86 if (ms->m_timer.data) {
87 ms->m_timer.data = NULL;
88 ms->unref();
89 }
90}
91
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020092GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
93 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020094 m_cb(&gprs_default_cb),
95 m_ul_tbf(NULL),
96 m_dl_tbf(NULL),
97 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020098 m_new_ul_tlli(0),
99 m_new_dl_tlli(0),
Max9bbe1602016-07-18 12:50:18 +0200100 m_ta(GSM48_TA_INVALID),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200101 m_ms_class(0),
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200102 m_egprs_ms_class(0),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200103 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +0200104 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +0200105 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200106 m_delay(0),
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200107 m_nack_rate_dl(0),
108 m_reserved_dl_slots(0),
109 m_reserved_ul_slots(0),
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200110 m_current_trx(NULL),
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100111 m_codel_state(NULL),
sivasankarida7250a2016-12-16 12:57:18 +0530112 m_mode(GprsCodingScheme::GPRS),
113 m_dl_ctrl_msg(0)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200114{
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200115 int codel_interval = LLC_CODEL_USE_DEFAULT;
116
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200117 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200118
119 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200120 memset(&m_timer, 0, sizeof(m_timer));
121 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200122 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200123
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100124 set_mode(m_mode);
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200125
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100126 if (m_bts)
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200127 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200128
129 if (codel_interval) {
130 if (codel_interval == LLC_CODEL_USE_DEFAULT)
131 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
132 m_codel_state = talloc(this, struct gprs_codel);
133 gprs_codel_init(m_codel_state);
134 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200135 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200136 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200137}
138
139GprsMs::~GprsMs()
140{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200141 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
142
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200143 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200144
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200145 set_reserved_slots(NULL, 0, 0);
146
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200147 if (osmo_timer_pending(&m_timer))
148 osmo_timer_del(&m_timer);
149
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200150 if (m_ul_tbf) {
151 m_ul_tbf->set_ms(NULL);
152 m_ul_tbf = NULL;
153 }
154
155 if (m_dl_tbf) {
156 m_dl_tbf->set_ms(NULL);
157 m_dl_tbf = NULL;
158 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200159
160 llist_for_each_safe(pos, tmp, &m_old_tbfs)
161 pos->entry()->set_ms(NULL);
162
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200163 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200164}
165
166void* GprsMs::operator new(size_t size)
167{
168 static void *tall_ms_ctx = NULL;
169 if (!tall_ms_ctx)
170 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
171
172 return talloc_size(tall_ms_ctx, size);
173}
174
175void GprsMs::operator delete(void* p)
176{
177 talloc_free(p);
178}
179
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200180GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200181{
182 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200183 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200184}
185
186void GprsMs::unref()
187{
188 OSMO_ASSERT(m_ref >= 0);
189 m_ref -= 1;
190 if (m_ref == 0)
191 update_status();
192}
193
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200194void GprsMs::start_timer()
195{
196 if (m_delay == 0)
197 return;
198
199 if (!m_timer.data)
200 m_timer.data = ref();
201
202 osmo_timer_schedule(&m_timer, m_delay, 0);
203}
204
205void GprsMs::stop_timer()
206{
207 if (!m_timer.data)
208 return;
209
210 osmo_timer_del(&m_timer);
211 m_timer.data = NULL;
212 unref();
213}
214
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100215void GprsMs::set_mode(GprsCodingScheme::Mode mode)
216{
217 m_mode = mode;
218
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100219 if (!m_bts)
220 return;
221
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100222 switch (m_mode) {
223 case GprsCodingScheme::GPRS:
224 if (!m_current_cs_ul.isGprs()) {
225 m_current_cs_ul = GprsCodingScheme::getGprsByNum(
226 m_bts->bts_data()->initial_cs_ul);
227 if (!m_current_cs_ul.isValid())
Maxbea2edb2019-03-06 17:04:59 +0100228 m_current_cs_ul = CS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100229 }
230 if (!m_current_cs_dl.isGprs()) {
231 m_current_cs_dl = GprsCodingScheme::getGprsByNum(
232 m_bts->bts_data()->initial_cs_dl);
233 if (!m_current_cs_dl.isValid())
Maxbea2edb2019-03-06 17:04:59 +0100234 m_current_cs_dl = CS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100235 }
236 break;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100237
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100238 case GprsCodingScheme::EGPRS_GMSK:
239 case GprsCodingScheme::EGPRS:
240 if (!m_current_cs_ul.isEgprs()) {
241 m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
242 m_bts->bts_data()->initial_mcs_ul);
Holger Hans Peter Freytherfd263b02016-03-04 18:24:50 +0100243 if (!m_current_cs_ul.isValid())
Maxbea2edb2019-03-06 17:04:59 +0100244 m_current_cs_ul = MCS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100245 }
246 if (!m_current_cs_dl.isEgprs()) {
247 m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
248 m_bts->bts_data()->initial_mcs_dl);
249 if (!m_current_cs_dl.isValid())
Maxbea2edb2019-03-06 17:04:59 +0100250 m_current_cs_dl = MCS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100251 }
252 break;
253 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100254}
255
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200256void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
257{
258 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100259 attach_dl_tbf(as_dl_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200260 else
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100261 attach_ul_tbf(as_ul_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200262}
263
264void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
265{
266 if (m_ul_tbf == tbf)
267 return;
268
269 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
270 tlli(), tbf->name());
271
272 Guard guard(this);
273
274 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200275 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200276
277 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200278
279 if (tbf)
280 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200281}
282
283void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
284{
285 if (m_dl_tbf == tbf)
286 return;
287
288 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
289 tlli(), tbf->name());
290
291 Guard guard(this);
292
293 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200294 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200295
296 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200297
298 if (tbf)
299 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200300}
301
302void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
303{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200304 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200305 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200306 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200307 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200308 } else {
309 bool found = false;
310
311 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
312 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
313 if (pos->entry() == tbf) {
314 llist_del(pos);
315 found = true;
316 break;
317 }
318 }
319
320 /* Protect against recursive calls via set_ms() */
321 if (!found)
322 return;
323 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200324
325 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
326 tlli(), tbf->name());
327
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200328 if (tbf->ms() == this)
329 tbf->set_ms(NULL);
330
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200331 if (!m_dl_tbf && !m_ul_tbf) {
332 set_reserved_slots(NULL, 0, 0);
333
334 if (tlli() != 0)
335 start_timer();
336 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200337
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200338 update_status();
339}
340
341void GprsMs::update_status()
342{
343 if (m_ref > 0)
344 return;
345
346 if (is_idle() && !m_is_idle) {
347 m_is_idle = true;
348 m_cb->ms_idle(this);
349 /* this can be deleted by now, do not access it */
350 return;
351 }
352
353 if (!is_idle() && m_is_idle) {
354 m_is_idle = false;
355 m_cb->ms_active(this);
356 }
357}
358
Jacob Erlbeckac289052015-08-14 12:50:54 +0200359void GprsMs::reset()
360{
361 LOGP(DRLCMAC, LOGL_INFO,
362 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
363 tlli(), imsi());
364
365 stop_timer();
366
367 m_tlli = 0;
368 m_new_dl_tlli = 0;
369 m_new_ul_tlli = 0;
370 m_imsi[0] = '\0';
371}
372
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200373void GprsMs::merge_old_ms(GprsMs *old_ms)
374{
375 if (old_ms == this)
376 return;
377
378 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
379 set_imsi(old_ms->imsi());
380
381 if (!ms_class() && old_ms->ms_class())
382 set_ms_class(old_ms->ms_class());
383
Jacob Erlbecke0b21f42015-08-21 18:30:05 +0200384 m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
385
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200386 old_ms->reset();
387}
388
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200389void GprsMs::set_tlli(uint32_t tlli)
390{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200391 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200392 return;
393
Jacob Erlbeck93990462015-05-15 15:50:43 +0200394 if (tlli != m_new_dl_tlli) {
395 LOGP(DRLCMAC, LOGL_INFO,
396 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
397 "not yet confirmed\n",
398 this->tlli(), tlli);
399 m_new_ul_tlli = tlli;
400 return;
401 }
402
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200403 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200404 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
405 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200406 m_tlli, tlli);
407
408 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200409 m_new_dl_tlli = 0;
410 m_new_ul_tlli = 0;
411}
412
413bool GprsMs::confirm_tlli(uint32_t tlli)
414{
415 if (tlli == m_tlli || tlli == m_new_dl_tlli)
416 return false;
417
418 if (tlli != m_new_ul_tlli) {
419 /* The MS has not sent a message with the new TLLI, which may
420 * happen according to the spec [TODO: add reference]. */
421
422 LOGP(DRLCMAC, LOGL_INFO,
423 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
424 "partly confirmed\n", tlli);
425 /* Use the network's idea of TLLI as candidate, this does not
426 * change the result value of tlli() */
427 m_new_dl_tlli = tlli;
428 return false;
429 }
430
431 LOGP(DRLCMAC, LOGL_INFO,
432 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
433
434 m_tlli = tlli;
435 m_new_dl_tlli = 0;
436 m_new_ul_tlli = 0;
437
438 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200439}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200440
441void GprsMs::set_imsi(const char *imsi)
442{
443 if (!imsi) {
444 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
445 return;
446 }
447
448 if (imsi[0] && strlen(imsi) < 3) {
449 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
450 imsi);
451 return;
452 }
453
454 if (strcmp(imsi, m_imsi) == 0)
455 return;
456
457 LOGP(DRLCMAC, LOGL_INFO,
458 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
459 tlli(), m_imsi, imsi);
460
461 strncpy(m_imsi, imsi, sizeof(m_imsi));
462 m_imsi[sizeof(m_imsi) - 1] = '\0';
463}
464
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200465void GprsMs::set_ta(uint8_t ta_)
466{
467 if (ta_ == m_ta)
468 return;
469
Max9bbe1602016-07-18 12:50:18 +0200470 if (gsm48_ta_is_valid(ta_)) {
471 LOGP(DRLCMAC, LOGL_INFO,
472 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
473 tlli(), m_ta, ta_);
474 m_ta = ta_;
475 } else
476 LOGP(DRLCMAC, LOGL_NOTICE,
477 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
478 "value %d kept)\n", tlli(), ta_, m_ta);
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200479}
480
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200481void GprsMs::set_ms_class(uint8_t ms_class_)
482{
483 if (ms_class_ == m_ms_class)
484 return;
485
486 LOGP(DRLCMAC, LOGL_INFO,
487 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
488 tlli(), m_ms_class, ms_class_);
489
490 m_ms_class = ms_class_;
491}
492
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200493void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
494{
495 if (ms_class_ == m_egprs_ms_class)
496 return;
497
498 LOGP(DRLCMAC, LOGL_INFO,
499 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
500 tlli(), m_egprs_ms_class, ms_class_);
501
502 m_egprs_ms_class = ms_class_;
503}
504
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200505void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
506{
507 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200508 int64_t now;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100509 GprsCodingScheme max_cs_dl = this->max_cs_dl();
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200510
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100511 OSMO_ASSERT(max_cs_dl);
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200512 bts_data = m_bts->bts_data();
513
514 if (error_rate < 0)
515 return;
516
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200517 now = now_msec();
518
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200519 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200520 /* TODO: Support different CS values for UL and DL */
521
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200522 m_nack_rate_dl = error_rate;
523
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200524 if (error_rate > bts_data->cs_adj_upper_limit) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100525 if (m_current_cs_dl.to_num() > 1) {
526 m_current_cs_dl.dec(mode());
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200527 LOGP(DRLCMACDL, LOGL_INFO,
528 "MS (IMSI %s): High error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100529 "reducing CS level to %s\n",
530 imsi(), error_rate, m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200531 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200532 }
533 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200534 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200535 if (now - m_last_cs_not_low > 1000) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100536 m_current_cs_dl.inc(mode());
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200537
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200538 LOGP(DRLCMACDL, LOGL_INFO,
539 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100540 "increasing DL CS level to %s\n",
541 imsi(), error_rate,
542 m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200543 m_last_cs_not_low = now;
544 } else {
545 LOGP(DRLCMACDL, LOGL_DEBUG,
546 "MS (IMSI %s): Low error rate %d%%, "
547 "ignored (within blocking period)\n",
548 imsi(), error_rate);
549 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200550 }
551 } else {
552 LOGP(DRLCMACDL, LOGL_DEBUG,
553 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
554 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200555 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200556 }
557}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200558
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100559GprsCodingScheme GprsMs::max_cs_ul() const
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200560{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200561 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200562
563 OSMO_ASSERT(m_bts != NULL);
564 bts_data = m_bts->bts_data();
565
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100566 if (m_current_cs_ul.isGprs()) {
567 if (!bts_data->max_cs_ul)
Maxbea2edb2019-03-06 17:04:59 +0100568 return GprsCodingScheme(CS4);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200569
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100570 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200571 }
572
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100573 if (!m_current_cs_ul.isEgprs())
574 return GprsCodingScheme(); /* UNKNOWN */
575
576 if (bts_data->max_mcs_ul)
577 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
578 else if (bts_data->max_cs_ul)
579 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
580
Maxbea2edb2019-03-06 17:04:59 +0100581 return GprsCodingScheme(MCS4);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100582}
583
Maxbea2edb2019-03-06 17:04:59 +0100584void GprsMs::set_current_cs_dl(CodingScheme scheme)
Aravind Sirsikare8ccafc2016-07-13 11:37:47 +0530585{
586 m_current_cs_dl = scheme;
587}
588
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100589GprsCodingScheme GprsMs::max_cs_dl() const
590{
591 struct gprs_rlcmac_bts *bts_data;
592
593 OSMO_ASSERT(m_bts != NULL);
594 bts_data = m_bts->bts_data();
595
596 if (m_current_cs_dl.isGprs()) {
597 if (!bts_data->max_cs_dl)
Maxbea2edb2019-03-06 17:04:59 +0100598 return GprsCodingScheme(CS4);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100599
600 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
601 }
602
603 if (!m_current_cs_dl.isEgprs())
604 return GprsCodingScheme(); /* UNKNOWN */
605
606 if (bts_data->max_mcs_dl)
607 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
608 else if (bts_data->max_cs_dl)
609 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
610
Maxbea2edb2019-03-06 17:04:59 +0100611 return GprsCodingScheme(MCS4);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100612}
613
614void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
615{
616 struct gprs_rlcmac_bts *bts_data;
617 GprsCodingScheme max_cs_ul = this->max_cs_ul();
618
619 int old_link_qual;
620 int low;
621 int high;
622 GprsCodingScheme new_cs_ul = m_current_cs_ul;
Max8119ecd2019-03-06 19:03:01 +0100623 uint8_t current_cs_num = m_current_cs_ul.to_num();
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100624
625 bts_data = m_bts->bts_data();
626
627 if (!max_cs_ul) {
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400628 LOGP(DRLCMACMEAS, LOGL_ERROR,
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100629 "max_cs_ul cannot be derived (current UL CS: %s)\n",
630 m_current_cs_ul.name());
631 return;
632 }
633
Max19621362017-09-08 12:33:34 +0200634 OSMO_ASSERT(current_cs_num > 0);
635
Maxfa3085b2019-03-07 11:46:43 +0100636 if (!m_current_cs_ul) {
637 LOGP(DRLCMACMEAS, LOGL_ERROR,
638 "Unable to update UL (M)CS because it's not set: %s\n",
639 m_current_cs_ul.name());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100640 return;
Maxfa3085b2019-03-07 11:46:43 +0100641 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100642
Maxfa3085b2019-03-07 11:46:43 +0100643 if (!meas->have_link_qual) {
644 LOGP(DRLCMACMEAS, LOGL_ERROR,
645 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
646 m_current_cs_ul.name());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100647 return;
Maxfa3085b2019-03-07 11:46:43 +0100648 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100649
650 old_link_qual = meas->link_qual;
651
652 if (m_current_cs_ul.isGprs()) {
Max8119ecd2019-03-06 19:03:01 +0100653 if (current_cs_num > MAX_GPRS_CS)
654 current_cs_num = MAX_GPRS_CS;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100655 low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
656 high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
657 } else if (m_current_cs_ul.isEgprs()) {
Max8119ecd2019-03-06 19:03:01 +0100658 if (current_cs_num > MAX_EDGE_MCS)
659 current_cs_num = MAX_EDGE_MCS;
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400660 low = bts_data->mcs_lqual_ranges[current_cs_num-1].low;
661 high = bts_data->mcs_lqual_ranges[current_cs_num-1].high;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100662 } else {
Maxfa3085b2019-03-07 11:46:43 +0100663 LOGP(DRLCMACMEAS, LOGL_ERROR,
664 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
665 m_current_cs_ul.name());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100666 return;
667 }
668
669 if (m_l1_meas.have_link_qual)
670 old_link_qual = m_l1_meas.link_qual;
671
672 if (meas->link_qual < low && old_link_qual < low)
673 new_cs_ul.dec(mode());
674 else if (meas->link_qual > high && old_link_qual > high &&
675 m_current_cs_ul < max_cs_ul)
676 new_cs_ul.inc(mode());
677
678 if (m_current_cs_ul != new_cs_ul) {
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400679 LOGP(DRLCMACMEAS, LOGL_INFO,
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100680 "MS (IMSI %s): "
681 "Link quality %ddB (%ddB) left window [%d, %d], "
682 "modifying uplink CS level: %s -> %s\n",
683 imsi(), meas->link_qual, old_link_qual,
684 low, high,
685 m_current_cs_ul.name(), new_cs_ul.name());
686
687 m_current_cs_ul = new_cs_ul;
688 }
689}
690
691void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
692{
693 unsigned i;
694
695 update_cs_ul(meas);
696
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200697 if (meas->have_rssi)
698 m_l1_meas.set_rssi(meas->rssi);
699 if (meas->have_bto)
700 m_l1_meas.set_bto(meas->bto);
701 if (meas->have_ber)
702 m_l1_meas.set_ber(meas->ber);
703 if (meas->have_link_qual)
704 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200705
706 if (meas->have_ms_rx_qual)
707 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
708 if (meas->have_ms_c_value)
709 m_l1_meas.set_ms_c_value(meas->ms_c_value);
710 if (meas->have_ms_sign_var)
711 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
712
713 if (meas->have_ms_i_level) {
714 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
715 if (meas->ts[i].have_ms_i_level)
716 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
717 else
718 m_l1_meas.ts[i].have_ms_i_level = 0;
719 }
720 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200721}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200722
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100723GprsCodingScheme GprsMs::current_cs_dl() const
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200724{
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100725 GprsCodingScheme cs = m_current_cs_dl;
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200726 size_t unencoded_octets;
727
728 if (!m_bts)
729 return cs;
730
731 unencoded_octets = m_llc_queue.octets();
732
733 /* If the DL TBF is active, add number of unencoded chunk octets */
734 if (m_dl_tbf)
Jacob Erlbeck14e26cb2016-02-03 15:26:29 +0100735 unencoded_octets += m_dl_tbf->m_llc.chunk_size();
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200736
737 /* There are many unencoded octets, don't reduce */
738 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
739 return cs;
740
741 /* RF conditions are good, don't reduce */
742 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
743 return cs;
744
745 /* The throughput would probably be better if the CS level was reduced */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100746 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200747
748 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
Maxbea2edb2019-03-06 17:04:59 +0100749 if (cs == GprsCodingScheme(CS2))
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100750 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200751
752 return cs;
753}
754
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200755int GprsMs::first_common_ts() const
756{
757 if (m_dl_tbf)
758 return m_dl_tbf->first_common_ts;
759
760 if (m_ul_tbf)
761 return m_ul_tbf->first_common_ts;
762
763 return -1;
764}
765
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200766uint8_t GprsMs::dl_slots() const
767{
768 uint8_t slots = 0;
769
770 if (m_dl_tbf)
771 slots |= m_dl_tbf->dl_slots();
772
773 if (m_ul_tbf)
774 slots |= m_ul_tbf->dl_slots();
775
776 return slots;
777}
778
779uint8_t GprsMs::ul_slots() const
780{
781 uint8_t slots = 0;
782
783 if (m_dl_tbf)
784 slots |= m_dl_tbf->ul_slots();
785
786 if (m_ul_tbf)
787 slots |= m_ul_tbf->ul_slots();
788
789 return slots;
790}
791
Jacob Erlbeck7c72aca2016-01-22 17:06:14 +0100792uint8_t GprsMs::current_pacch_slots() const
793{
794 uint8_t slots = 0;
795
796 bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
797 bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
798
799 if (!is_dl_active && !is_ul_active)
800 return 0;
801
802 /* see TS 44.060, 8.1.1.2.2 */
803 if (is_dl_active && !is_ul_active)
804 slots = m_dl_tbf->dl_slots();
805 else if (!is_dl_active && is_ul_active)
806 slots = m_ul_tbf->ul_slots();
807 else
808 slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
809
810 /* Assume a multislot class 1 device */
811 /* TODO: For class 2 devices, this could be removed */
812 slots = pcu_lsb(slots);
813
814 return slots;
815}
816
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200817void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
818 uint8_t ul_slots, uint8_t dl_slots)
819{
820 if (m_current_trx) {
821 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
822 m_reserved_dl_slots);
823 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
824 m_reserved_ul_slots);
825 m_reserved_dl_slots = 0;
826 m_reserved_ul_slots = 0;
827 }
828 m_current_trx = trx;
829 if (trx) {
830 m_reserved_dl_slots = dl_slots;
831 m_reserved_ul_slots = ul_slots;
832 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
833 m_reserved_dl_slots);
834 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
835 m_reserved_ul_slots);
836 }
837}
838
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200839gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
840{
841 switch (dir) {
842 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
843 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
844 }
845
846 return NULL;
847}