blob: 66312fa9a4df75ab32c040ca7e04e89b29e03079 [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"
23
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>
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020036}
37
Jacob Erlbeck1c3b8992015-08-14 16:01:36 +020038#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020039
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020040extern void *tall_pcu_ctx;
41
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020042static int64_t now_msec()
43{
44 struct timespec ts;
45 clock_gettime(CLOCK_MONOTONIC, &ts);
46
47 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
48}
49
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020050struct GprsMsDefaultCallback: public GprsMs::Callback {
51 virtual void ms_idle(class GprsMs *ms) {
52 delete ms;
53 }
54 virtual void ms_active(class GprsMs *) {}
55};
56
57static GprsMsDefaultCallback gprs_default_cb;
58
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020059GprsMs::Guard::Guard(GprsMs *ms) :
60 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020061{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020062}
63
64GprsMs::Guard::~Guard()
65{
66 if (m_ms)
67 m_ms->unref();
68}
69
Jacob Erlbeckb2439bb2015-07-13 14:23:32 +020070bool GprsMs::Guard::is_idle() const
71{
72 if (!m_ms)
73 return true;
74
75 return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
76}
77
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020078void GprsMs::timeout(void *priv_)
79{
80 GprsMs *ms = static_cast<GprsMs *>(priv_);
81
82 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
83 ms->tlli());
84
85 if (ms->m_timer.data) {
86 ms->m_timer.data = NULL;
87 ms->unref();
88 }
89}
90
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020091GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
92 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020093 m_cb(&gprs_default_cb),
94 m_ul_tbf(NULL),
95 m_dl_tbf(NULL),
96 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020097 m_new_ul_tlli(0),
98 m_new_dl_tlli(0),
Max9bbe1602016-07-18 12:50:18 +020099 m_ta(GSM48_TA_INVALID),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200100 m_ms_class(0),
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200101 m_egprs_ms_class(0),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200102 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +0200103 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +0200104 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200105 m_delay(0),
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200106 m_nack_rate_dl(0),
107 m_reserved_dl_slots(0),
108 m_reserved_ul_slots(0),
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200109 m_current_trx(NULL),
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100110 m_codel_state(NULL),
sivasankarida7250a2016-12-16 12:57:18 +0530111 m_mode(GprsCodingScheme::GPRS),
112 m_dl_ctrl_msg(0)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200113{
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200114 int codel_interval = LLC_CODEL_USE_DEFAULT;
115
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200116 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200117
118 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200119 memset(&m_timer, 0, sizeof(m_timer));
120 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200121 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200122
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100123 set_mode(m_mode);
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200124
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100125 if (m_bts)
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200126 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200127
128 if (codel_interval) {
129 if (codel_interval == LLC_CODEL_USE_DEFAULT)
130 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
131 m_codel_state = talloc(this, struct gprs_codel);
132 gprs_codel_init(m_codel_state);
133 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200134 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200135 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200136}
137
138GprsMs::~GprsMs()
139{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200140 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
141
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200142 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200143
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200144 set_reserved_slots(NULL, 0, 0);
145
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200146 if (osmo_timer_pending(&m_timer))
147 osmo_timer_del(&m_timer);
148
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200149 if (m_ul_tbf) {
150 m_ul_tbf->set_ms(NULL);
151 m_ul_tbf = NULL;
152 }
153
154 if (m_dl_tbf) {
155 m_dl_tbf->set_ms(NULL);
156 m_dl_tbf = NULL;
157 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200158
159 llist_for_each_safe(pos, tmp, &m_old_tbfs)
160 pos->entry()->set_ms(NULL);
161
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200162 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200163}
164
165void* GprsMs::operator new(size_t size)
166{
167 static void *tall_ms_ctx = NULL;
168 if (!tall_ms_ctx)
169 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
170
171 return talloc_size(tall_ms_ctx, size);
172}
173
174void GprsMs::operator delete(void* p)
175{
176 talloc_free(p);
177}
178
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200179GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200180{
181 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200182 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200183}
184
185void GprsMs::unref()
186{
187 OSMO_ASSERT(m_ref >= 0);
188 m_ref -= 1;
189 if (m_ref == 0)
190 update_status();
191}
192
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200193void GprsMs::start_timer()
194{
195 if (m_delay == 0)
196 return;
197
198 if (!m_timer.data)
199 m_timer.data = ref();
200
201 osmo_timer_schedule(&m_timer, m_delay, 0);
202}
203
204void GprsMs::stop_timer()
205{
206 if (!m_timer.data)
207 return;
208
209 osmo_timer_del(&m_timer);
210 m_timer.data = NULL;
211 unref();
212}
213
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100214void GprsMs::set_mode(GprsCodingScheme::Mode mode)
215{
216 m_mode = mode;
217
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100218 if (!m_bts)
219 return;
220
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100221 switch (m_mode) {
222 case GprsCodingScheme::GPRS:
223 if (!m_current_cs_ul.isGprs()) {
224 m_current_cs_ul = GprsCodingScheme::getGprsByNum(
225 m_bts->bts_data()->initial_cs_ul);
226 if (!m_current_cs_ul.isValid())
227 m_current_cs_ul = GprsCodingScheme::CS1;
228 }
229 if (!m_current_cs_dl.isGprs()) {
230 m_current_cs_dl = GprsCodingScheme::getGprsByNum(
231 m_bts->bts_data()->initial_cs_dl);
232 if (!m_current_cs_dl.isValid())
233 m_current_cs_dl = GprsCodingScheme::CS1;
234 }
235 break;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100236
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100237 case GprsCodingScheme::EGPRS_GMSK:
238 case GprsCodingScheme::EGPRS:
239 if (!m_current_cs_ul.isEgprs()) {
240 m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
241 m_bts->bts_data()->initial_mcs_ul);
Holger Hans Peter Freytherfd263b02016-03-04 18:24:50 +0100242 if (!m_current_cs_ul.isValid())
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100243 m_current_cs_ul = GprsCodingScheme::MCS1;
244 }
245 if (!m_current_cs_dl.isEgprs()) {
246 m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
247 m_bts->bts_data()->initial_mcs_dl);
248 if (!m_current_cs_dl.isValid())
249 m_current_cs_dl = GprsCodingScheme::MCS1;
250 }
251 break;
252 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100253}
254
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200255void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
256{
257 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100258 attach_dl_tbf(as_dl_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200259 else
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100260 attach_ul_tbf(as_ul_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200261}
262
263void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
264{
265 if (m_ul_tbf == tbf)
266 return;
267
268 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
269 tlli(), tbf->name());
270
271 Guard guard(this);
272
273 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200274 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200275
276 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200277
278 if (tbf)
279 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200280}
281
282void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
283{
284 if (m_dl_tbf == tbf)
285 return;
286
287 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
288 tlli(), tbf->name());
289
290 Guard guard(this);
291
292 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200293 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200294
295 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200296
297 if (tbf)
298 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200299}
300
301void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
302{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200303 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200304 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200305 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200306 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200307 } else {
308 bool found = false;
309
310 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
311 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
312 if (pos->entry() == tbf) {
313 llist_del(pos);
314 found = true;
315 break;
316 }
317 }
318
319 /* Protect against recursive calls via set_ms() */
320 if (!found)
321 return;
322 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200323
324 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
325 tlli(), tbf->name());
326
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200327 if (tbf->ms() == this)
328 tbf->set_ms(NULL);
329
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200330 if (!m_dl_tbf && !m_ul_tbf) {
331 set_reserved_slots(NULL, 0, 0);
332
333 if (tlli() != 0)
334 start_timer();
335 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200336
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200337 update_status();
338}
339
340void GprsMs::update_status()
341{
342 if (m_ref > 0)
343 return;
344
345 if (is_idle() && !m_is_idle) {
346 m_is_idle = true;
347 m_cb->ms_idle(this);
348 /* this can be deleted by now, do not access it */
349 return;
350 }
351
352 if (!is_idle() && m_is_idle) {
353 m_is_idle = false;
354 m_cb->ms_active(this);
355 }
356}
357
Jacob Erlbeckac289052015-08-14 12:50:54 +0200358void GprsMs::reset()
359{
360 LOGP(DRLCMAC, LOGL_INFO,
361 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
362 tlli(), imsi());
363
364 stop_timer();
365
366 m_tlli = 0;
367 m_new_dl_tlli = 0;
368 m_new_ul_tlli = 0;
369 m_imsi[0] = '\0';
370}
371
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200372void GprsMs::merge_old_ms(GprsMs *old_ms)
373{
374 if (old_ms == this)
375 return;
376
377 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
378 set_imsi(old_ms->imsi());
379
380 if (!ms_class() && old_ms->ms_class())
381 set_ms_class(old_ms->ms_class());
382
Jacob Erlbecke0b21f42015-08-21 18:30:05 +0200383 m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
384
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200385 old_ms->reset();
386}
387
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200388void GprsMs::set_tlli(uint32_t tlli)
389{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200390 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200391 return;
392
Jacob Erlbeck93990462015-05-15 15:50:43 +0200393 if (tlli != m_new_dl_tlli) {
394 LOGP(DRLCMAC, LOGL_INFO,
395 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
396 "not yet confirmed\n",
397 this->tlli(), tlli);
398 m_new_ul_tlli = tlli;
399 return;
400 }
401
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200402 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200403 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
404 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200405 m_tlli, tlli);
406
407 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200408 m_new_dl_tlli = 0;
409 m_new_ul_tlli = 0;
410}
411
412bool GprsMs::confirm_tlli(uint32_t tlli)
413{
414 if (tlli == m_tlli || tlli == m_new_dl_tlli)
415 return false;
416
417 if (tlli != m_new_ul_tlli) {
418 /* The MS has not sent a message with the new TLLI, which may
419 * happen according to the spec [TODO: add reference]. */
420
421 LOGP(DRLCMAC, LOGL_INFO,
422 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
423 "partly confirmed\n", tlli);
424 /* Use the network's idea of TLLI as candidate, this does not
425 * change the result value of tlli() */
426 m_new_dl_tlli = tlli;
427 return false;
428 }
429
430 LOGP(DRLCMAC, LOGL_INFO,
431 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
432
433 m_tlli = tlli;
434 m_new_dl_tlli = 0;
435 m_new_ul_tlli = 0;
436
437 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200438}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200439
440void GprsMs::set_imsi(const char *imsi)
441{
442 if (!imsi) {
443 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
444 return;
445 }
446
447 if (imsi[0] && strlen(imsi) < 3) {
448 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
449 imsi);
450 return;
451 }
452
453 if (strcmp(imsi, m_imsi) == 0)
454 return;
455
456 LOGP(DRLCMAC, LOGL_INFO,
457 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
458 tlli(), m_imsi, imsi);
459
460 strncpy(m_imsi, imsi, sizeof(m_imsi));
461 m_imsi[sizeof(m_imsi) - 1] = '\0';
462}
463
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200464void GprsMs::set_ta(uint8_t ta_)
465{
466 if (ta_ == m_ta)
467 return;
468
Max9bbe1602016-07-18 12:50:18 +0200469 if (gsm48_ta_is_valid(ta_)) {
470 LOGP(DRLCMAC, LOGL_INFO,
471 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
472 tlli(), m_ta, ta_);
473 m_ta = ta_;
474 } else
475 LOGP(DRLCMAC, LOGL_NOTICE,
476 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
477 "value %d kept)\n", tlli(), ta_, m_ta);
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200478}
479
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200480void GprsMs::set_ms_class(uint8_t ms_class_)
481{
482 if (ms_class_ == m_ms_class)
483 return;
484
485 LOGP(DRLCMAC, LOGL_INFO,
486 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
487 tlli(), m_ms_class, ms_class_);
488
489 m_ms_class = ms_class_;
490}
491
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200492void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
493{
494 if (ms_class_ == m_egprs_ms_class)
495 return;
496
497 LOGP(DRLCMAC, LOGL_INFO,
498 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
499 tlli(), m_egprs_ms_class, ms_class_);
500
501 m_egprs_ms_class = ms_class_;
502}
503
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200504void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
505{
506 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200507 int64_t now;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100508 GprsCodingScheme max_cs_dl = this->max_cs_dl();
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200509
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100510 OSMO_ASSERT(max_cs_dl);
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200511 bts_data = m_bts->bts_data();
512
513 if (error_rate < 0)
514 return;
515
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200516 now = now_msec();
517
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200518 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200519 /* TODO: Support different CS values for UL and DL */
520
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200521 m_nack_rate_dl = error_rate;
522
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200523 if (error_rate > bts_data->cs_adj_upper_limit) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100524 if (m_current_cs_dl.to_num() > 1) {
525 m_current_cs_dl.dec(mode());
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200526 LOGP(DRLCMACDL, LOGL_INFO,
527 "MS (IMSI %s): High error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100528 "reducing CS level to %s\n",
529 imsi(), error_rate, m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200530 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200531 }
532 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200533 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200534 if (now - m_last_cs_not_low > 1000) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100535 m_current_cs_dl.inc(mode());
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200536
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200537 LOGP(DRLCMACDL, LOGL_INFO,
538 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100539 "increasing DL CS level to %s\n",
540 imsi(), error_rate,
541 m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200542 m_last_cs_not_low = now;
543 } else {
544 LOGP(DRLCMACDL, LOGL_DEBUG,
545 "MS (IMSI %s): Low error rate %d%%, "
546 "ignored (within blocking period)\n",
547 imsi(), error_rate);
548 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200549 }
550 } else {
551 LOGP(DRLCMACDL, LOGL_DEBUG,
552 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
553 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200554 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200555 }
556}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200557
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100558GprsCodingScheme GprsMs::max_cs_ul() const
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200559{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200560 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200561
562 OSMO_ASSERT(m_bts != NULL);
563 bts_data = m_bts->bts_data();
564
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100565 if (m_current_cs_ul.isGprs()) {
566 if (!bts_data->max_cs_ul)
567 return GprsCodingScheme(GprsCodingScheme::CS4);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200568
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100569 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200570 }
571
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100572 if (!m_current_cs_ul.isEgprs())
573 return GprsCodingScheme(); /* UNKNOWN */
574
575 if (bts_data->max_mcs_ul)
576 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
577 else if (bts_data->max_cs_ul)
578 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
579
580 return GprsCodingScheme(GprsCodingScheme::MCS4);
581}
582
Aravind Sirsikare8ccafc2016-07-13 11:37:47 +0530583void GprsMs::set_current_cs_dl(GprsCodingScheme::Scheme scheme)
584{
585 m_current_cs_dl = scheme;
586}
587
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100588GprsCodingScheme GprsMs::max_cs_dl() const
589{
590 struct gprs_rlcmac_bts *bts_data;
591
592 OSMO_ASSERT(m_bts != NULL);
593 bts_data = m_bts->bts_data();
594
595 if (m_current_cs_dl.isGprs()) {
596 if (!bts_data->max_cs_dl)
597 return GprsCodingScheme(GprsCodingScheme::CS4);
598
599 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
600 }
601
602 if (!m_current_cs_dl.isEgprs())
603 return GprsCodingScheme(); /* UNKNOWN */
604
605 if (bts_data->max_mcs_dl)
606 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
607 else if (bts_data->max_cs_dl)
608 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
609
610 return GprsCodingScheme(GprsCodingScheme::MCS4);
611}
612
613void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
614{
615 struct gprs_rlcmac_bts *bts_data;
616 GprsCodingScheme max_cs_ul = this->max_cs_ul();
617
618 int old_link_qual;
619 int low;
620 int high;
621 GprsCodingScheme new_cs_ul = m_current_cs_ul;
622 unsigned current_cs_num = m_current_cs_ul.to_num();
623
624 bts_data = m_bts->bts_data();
625
626 if (!max_cs_ul) {
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400627 LOGP(DRLCMACMEAS, LOGL_ERROR,
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100628 "max_cs_ul cannot be derived (current UL CS: %s)\n",
629 m_current_cs_ul.name());
630 return;
631 }
632
633 if (!m_current_cs_ul)
634 return;
635
636 if (!meas->have_link_qual)
637 return;
638
639 old_link_qual = meas->link_qual;
640
641 if (m_current_cs_ul.isGprs()) {
642 low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
643 high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
644 } else if (m_current_cs_ul.isEgprs()) {
Aravind Sirsikar99ab0a82016-06-15 17:46:41 +0530645 if (current_cs_num > MAX_GPRS_CS)
646 current_cs_num = MAX_GPRS_CS;
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400647 low = bts_data->mcs_lqual_ranges[current_cs_num-1].low;
648 high = bts_data->mcs_lqual_ranges[current_cs_num-1].high;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100649 } else {
650 return;
651 }
652
653 if (m_l1_meas.have_link_qual)
654 old_link_qual = m_l1_meas.link_qual;
655
656 if (meas->link_qual < low && old_link_qual < low)
657 new_cs_ul.dec(mode());
658 else if (meas->link_qual > high && old_link_qual > high &&
659 m_current_cs_ul < max_cs_ul)
660 new_cs_ul.inc(mode());
661
662 if (m_current_cs_ul != new_cs_ul) {
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400663 LOGP(DRLCMACMEAS, LOGL_INFO,
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100664 "MS (IMSI %s): "
665 "Link quality %ddB (%ddB) left window [%d, %d], "
666 "modifying uplink CS level: %s -> %s\n",
667 imsi(), meas->link_qual, old_link_qual,
668 low, high,
669 m_current_cs_ul.name(), new_cs_ul.name());
670
671 m_current_cs_ul = new_cs_ul;
672 }
673}
674
675void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
676{
677 unsigned i;
678
679 update_cs_ul(meas);
680
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200681 if (meas->have_rssi)
682 m_l1_meas.set_rssi(meas->rssi);
683 if (meas->have_bto)
684 m_l1_meas.set_bto(meas->bto);
685 if (meas->have_ber)
686 m_l1_meas.set_ber(meas->ber);
687 if (meas->have_link_qual)
688 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200689
690 if (meas->have_ms_rx_qual)
691 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
692 if (meas->have_ms_c_value)
693 m_l1_meas.set_ms_c_value(meas->ms_c_value);
694 if (meas->have_ms_sign_var)
695 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
696
697 if (meas->have_ms_i_level) {
698 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
699 if (meas->ts[i].have_ms_i_level)
700 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
701 else
702 m_l1_meas.ts[i].have_ms_i_level = 0;
703 }
704 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200705}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200706
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100707GprsCodingScheme GprsMs::current_cs_dl() const
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200708{
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100709 GprsCodingScheme cs = m_current_cs_dl;
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200710 size_t unencoded_octets;
711
712 if (!m_bts)
713 return cs;
714
715 unencoded_octets = m_llc_queue.octets();
716
717 /* If the DL TBF is active, add number of unencoded chunk octets */
718 if (m_dl_tbf)
Jacob Erlbeck14e26cb2016-02-03 15:26:29 +0100719 unencoded_octets += m_dl_tbf->m_llc.chunk_size();
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200720
721 /* There are many unencoded octets, don't reduce */
722 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
723 return cs;
724
725 /* RF conditions are good, don't reduce */
726 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
727 return cs;
728
729 /* The throughput would probably be better if the CS level was reduced */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100730 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200731
732 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100733 if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
734 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200735
736 return cs;
737}
738
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200739int GprsMs::first_common_ts() const
740{
741 if (m_dl_tbf)
742 return m_dl_tbf->first_common_ts;
743
744 if (m_ul_tbf)
745 return m_ul_tbf->first_common_ts;
746
747 return -1;
748}
749
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200750uint8_t GprsMs::dl_slots() const
751{
752 uint8_t slots = 0;
753
754 if (m_dl_tbf)
755 slots |= m_dl_tbf->dl_slots();
756
757 if (m_ul_tbf)
758 slots |= m_ul_tbf->dl_slots();
759
760 return slots;
761}
762
763uint8_t GprsMs::ul_slots() const
764{
765 uint8_t slots = 0;
766
767 if (m_dl_tbf)
768 slots |= m_dl_tbf->ul_slots();
769
770 if (m_ul_tbf)
771 slots |= m_ul_tbf->ul_slots();
772
773 return slots;
774}
775
Jacob Erlbeck7c72aca2016-01-22 17:06:14 +0100776uint8_t GprsMs::current_pacch_slots() const
777{
778 uint8_t slots = 0;
779
780 bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
781 bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
782
783 if (!is_dl_active && !is_ul_active)
784 return 0;
785
786 /* see TS 44.060, 8.1.1.2.2 */
787 if (is_dl_active && !is_ul_active)
788 slots = m_dl_tbf->dl_slots();
789 else if (!is_dl_active && is_ul_active)
790 slots = m_ul_tbf->ul_slots();
791 else
792 slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
793
794 /* Assume a multislot class 1 device */
795 /* TODO: For class 2 devices, this could be removed */
796 slots = pcu_lsb(slots);
797
798 return slots;
799}
800
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200801void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
802 uint8_t ul_slots, uint8_t dl_slots)
803{
804 if (m_current_trx) {
805 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
806 m_reserved_dl_slots);
807 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
808 m_reserved_ul_slots);
809 m_reserved_dl_slots = 0;
810 m_reserved_ul_slots = 0;
811 }
812 m_current_trx = trx;
813 if (trx) {
814 m_reserved_dl_slots = dl_slots;
815 m_reserved_ul_slots = ul_slots;
816 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
817 m_reserved_dl_slots);
818 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
819 m_reserved_ul_slots);
820 }
821}
822
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200823gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
824{
825 switch (dir) {
826 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
827 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
828 }
829
830 return NULL;
831}