blob: 9126543e5770cb4ccab37b868426793211273fb7 [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 Erlbecke04e0b02015-05-06 18:30:48 +020028
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020029#include <time.h>
30
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020031extern "C" {
32 #include <osmocom/core/talloc.h>
33 #include <osmocom/core/utils.h>
34}
35
Jacob Erlbeck1c3b8992015-08-14 16:01:36 +020036#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020037
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020038extern void *tall_pcu_ctx;
39
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020040static int64_t now_msec()
41{
42 struct timespec ts;
43 clock_gettime(CLOCK_MONOTONIC, &ts);
44
45 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
46}
47
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020048struct GprsMsDefaultCallback: public GprsMs::Callback {
49 virtual void ms_idle(class GprsMs *ms) {
50 delete ms;
51 }
52 virtual void ms_active(class GprsMs *) {}
53};
54
55static GprsMsDefaultCallback gprs_default_cb;
56
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020057GprsMs::Guard::Guard(GprsMs *ms) :
58 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020059{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020060}
61
62GprsMs::Guard::~Guard()
63{
64 if (m_ms)
65 m_ms->unref();
66}
67
Jacob Erlbeckb2439bb2015-07-13 14:23:32 +020068bool GprsMs::Guard::is_idle() const
69{
70 if (!m_ms)
71 return true;
72
73 return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
74}
75
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020076void GprsMs::timeout(void *priv_)
77{
78 GprsMs *ms = static_cast<GprsMs *>(priv_);
79
80 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
81 ms->tlli());
82
83 if (ms->m_timer.data) {
84 ms->m_timer.data = NULL;
85 ms->unref();
86 }
87}
88
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020089GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
90 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020091 m_cb(&gprs_default_cb),
92 m_ul_tbf(NULL),
93 m_dl_tbf(NULL),
94 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020095 m_new_ul_tlli(0),
96 m_new_dl_tlli(0),
Jacob Erlbeck9200ce62015-05-22 17:48:04 +020097 m_ta(0),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +020098 m_ms_class(0),
Jacob Erlbeckc3c58042015-09-28 17:55:32 +020099 m_egprs_ms_class(0),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200100 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +0200101 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +0200102 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200103 m_delay(0),
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200104 m_nack_rate_dl(0),
105 m_reserved_dl_slots(0),
106 m_reserved_ul_slots(0),
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200107 m_current_trx(NULL),
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100108 m_codel_state(NULL),
109 m_mode(GprsCodingScheme::GPRS)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200110{
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200111 int codel_interval = LLC_CODEL_USE_DEFAULT;
112
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200113 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200114
115 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200116 memset(&m_timer, 0, sizeof(m_timer));
117 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200118 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200119
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100120 set_mode(m_mode);
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200121
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100122 if (m_bts)
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200123 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200124
125 if (codel_interval) {
126 if (codel_interval == LLC_CODEL_USE_DEFAULT)
127 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
128 m_codel_state = talloc(this, struct gprs_codel);
129 gprs_codel_init(m_codel_state);
130 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200131 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200132 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200133}
134
135GprsMs::~GprsMs()
136{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200137 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
138
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200139 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200140
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200141 set_reserved_slots(NULL, 0, 0);
142
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200143 if (osmo_timer_pending(&m_timer))
144 osmo_timer_del(&m_timer);
145
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200146 if (m_ul_tbf) {
147 m_ul_tbf->set_ms(NULL);
148 m_ul_tbf = NULL;
149 }
150
151 if (m_dl_tbf) {
152 m_dl_tbf->set_ms(NULL);
153 m_dl_tbf = NULL;
154 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200155
156 llist_for_each_safe(pos, tmp, &m_old_tbfs)
157 pos->entry()->set_ms(NULL);
158
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200159 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200160}
161
162void* GprsMs::operator new(size_t size)
163{
164 static void *tall_ms_ctx = NULL;
165 if (!tall_ms_ctx)
166 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
167
168 return talloc_size(tall_ms_ctx, size);
169}
170
171void GprsMs::operator delete(void* p)
172{
173 talloc_free(p);
174}
175
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200176GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200177{
178 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200179 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200180}
181
182void GprsMs::unref()
183{
184 OSMO_ASSERT(m_ref >= 0);
185 m_ref -= 1;
186 if (m_ref == 0)
187 update_status();
188}
189
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200190void GprsMs::start_timer()
191{
192 if (m_delay == 0)
193 return;
194
195 if (!m_timer.data)
196 m_timer.data = ref();
197
198 osmo_timer_schedule(&m_timer, m_delay, 0);
199}
200
201void GprsMs::stop_timer()
202{
203 if (!m_timer.data)
204 return;
205
206 osmo_timer_del(&m_timer);
207 m_timer.data = NULL;
208 unref();
209}
210
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100211void GprsMs::set_mode(GprsCodingScheme::Mode mode)
212{
213 m_mode = mode;
214
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100215 if (!m_bts)
216 return;
217
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100218 switch (m_mode) {
219 case GprsCodingScheme::GPRS:
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100220 if (!m_current_cs_ul.isGprs()) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100221 m_current_cs_ul = GprsCodingScheme::getGprsByNum(
222 m_bts->bts_data()->initial_cs_ul);
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100223 if (!m_current_cs_ul.isValid())
224 m_current_cs_ul = GprsCodingScheme::CS1;
225 }
226 if (!m_current_cs_dl.isGprs()) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100227 m_current_cs_dl = GprsCodingScheme::getGprsByNum(
228 m_bts->bts_data()->initial_cs_dl);
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100229 if (!m_current_cs_dl.isValid())
230 m_current_cs_dl = GprsCodingScheme::CS1;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100231 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100232 break;
233
234 case GprsCodingScheme::EGPRS_GMSK:
235 case GprsCodingScheme::EGPRS:
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100236 if (!m_current_cs_ul.isEgprs()) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100237 m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
238 m_bts->bts_data()->initial_mcs_ul);
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100239 if (!m_current_cs_dl.isValid())
240 m_current_cs_ul = GprsCodingScheme::MCS1;
241 }
242 if (!m_current_cs_dl.isEgprs()) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100243 m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
244 m_bts->bts_data()->initial_mcs_dl);
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100245 if (!m_current_cs_dl.isValid())
246 m_current_cs_dl = GprsCodingScheme::MCS1;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100247 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100248 break;
249 }
250}
251
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200252void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
253{
254 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100255 attach_dl_tbf(as_dl_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200256 else
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100257 attach_ul_tbf(as_ul_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200258}
259
260void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
261{
262 if (m_ul_tbf == tbf)
263 return;
264
265 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
266 tlli(), tbf->name());
267
268 Guard guard(this);
269
270 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200271 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200272
273 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200274
275 if (tbf)
276 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200277}
278
279void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
280{
281 if (m_dl_tbf == tbf)
282 return;
283
284 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
285 tlli(), tbf->name());
286
287 Guard guard(this);
288
289 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200290 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200291
292 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200293
294 if (tbf)
295 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200296}
297
298void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
299{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200300 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200301 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200302 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200303 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200304 } else {
305 bool found = false;
306
307 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
308 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
309 if (pos->entry() == tbf) {
310 llist_del(pos);
311 found = true;
312 break;
313 }
314 }
315
316 /* Protect against recursive calls via set_ms() */
317 if (!found)
318 return;
319 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200320
321 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
322 tlli(), tbf->name());
323
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200324 if (tbf->ms() == this)
325 tbf->set_ms(NULL);
326
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200327 if (!m_dl_tbf && !m_ul_tbf) {
328 set_reserved_slots(NULL, 0, 0);
329
330 if (tlli() != 0)
331 start_timer();
332 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200333
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200334 update_status();
335}
336
337void GprsMs::update_status()
338{
339 if (m_ref > 0)
340 return;
341
342 if (is_idle() && !m_is_idle) {
343 m_is_idle = true;
344 m_cb->ms_idle(this);
345 /* this can be deleted by now, do not access it */
346 return;
347 }
348
349 if (!is_idle() && m_is_idle) {
350 m_is_idle = false;
351 m_cb->ms_active(this);
352 }
353}
354
Jacob Erlbeckac289052015-08-14 12:50:54 +0200355void GprsMs::reset()
356{
357 LOGP(DRLCMAC, LOGL_INFO,
358 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
359 tlli(), imsi());
360
361 stop_timer();
362
363 m_tlli = 0;
364 m_new_dl_tlli = 0;
365 m_new_ul_tlli = 0;
366 m_imsi[0] = '\0';
367}
368
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200369void GprsMs::merge_old_ms(GprsMs *old_ms)
370{
371 if (old_ms == this)
372 return;
373
374 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
375 set_imsi(old_ms->imsi());
376
377 if (!ms_class() && old_ms->ms_class())
378 set_ms_class(old_ms->ms_class());
379
Jacob Erlbecke0b21f42015-08-21 18:30:05 +0200380 m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
381
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200382 old_ms->reset();
383}
384
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200385void GprsMs::set_tlli(uint32_t tlli)
386{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200387 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200388 return;
389
Jacob Erlbeck93990462015-05-15 15:50:43 +0200390 if (tlli != m_new_dl_tlli) {
391 LOGP(DRLCMAC, LOGL_INFO,
392 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
393 "not yet confirmed\n",
394 this->tlli(), tlli);
395 m_new_ul_tlli = tlli;
396 return;
397 }
398
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200399 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200400 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
401 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200402 m_tlli, tlli);
403
404 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200405 m_new_dl_tlli = 0;
406 m_new_ul_tlli = 0;
407}
408
409bool GprsMs::confirm_tlli(uint32_t tlli)
410{
411 if (tlli == m_tlli || tlli == m_new_dl_tlli)
412 return false;
413
414 if (tlli != m_new_ul_tlli) {
415 /* The MS has not sent a message with the new TLLI, which may
416 * happen according to the spec [TODO: add reference]. */
417
418 LOGP(DRLCMAC, LOGL_INFO,
419 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
420 "partly confirmed\n", tlli);
421 /* Use the network's idea of TLLI as candidate, this does not
422 * change the result value of tlli() */
423 m_new_dl_tlli = tlli;
424 return false;
425 }
426
427 LOGP(DRLCMAC, LOGL_INFO,
428 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
429
430 m_tlli = tlli;
431 m_new_dl_tlli = 0;
432 m_new_ul_tlli = 0;
433
434 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200435}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200436
437void GprsMs::set_imsi(const char *imsi)
438{
439 if (!imsi) {
440 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
441 return;
442 }
443
444 if (imsi[0] && strlen(imsi) < 3) {
445 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
446 imsi);
447 return;
448 }
449
450 if (strcmp(imsi, m_imsi) == 0)
451 return;
452
453 LOGP(DRLCMAC, LOGL_INFO,
454 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
455 tlli(), m_imsi, imsi);
456
457 strncpy(m_imsi, imsi, sizeof(m_imsi));
458 m_imsi[sizeof(m_imsi) - 1] = '\0';
459}
460
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200461void GprsMs::set_ta(uint8_t ta_)
462{
463 if (ta_ == m_ta)
464 return;
465
466 LOGP(DRLCMAC, LOGL_INFO,
467 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
468 tlli(), m_ta, ta_);
469
470 m_ta = ta_;
471}
472
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200473void GprsMs::set_ms_class(uint8_t ms_class_)
474{
475 if (ms_class_ == m_ms_class)
476 return;
477
478 LOGP(DRLCMAC, LOGL_INFO,
479 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
480 tlli(), m_ms_class, ms_class_);
481
482 m_ms_class = ms_class_;
483}
484
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200485void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
486{
487 if (ms_class_ == m_egprs_ms_class)
488 return;
489
490 LOGP(DRLCMAC, LOGL_INFO,
491 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
492 tlli(), m_egprs_ms_class, ms_class_);
493
494 m_egprs_ms_class = ms_class_;
495}
496
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200497void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
498{
499 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200500 int64_t now;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100501 GprsCodingScheme max_cs_dl = this->max_cs_dl();
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200502
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100503 OSMO_ASSERT(max_cs_dl);
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200504 bts_data = m_bts->bts_data();
505
506 if (error_rate < 0)
507 return;
508
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200509 now = now_msec();
510
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200511 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200512 /* TODO: Support different CS values for UL and DL */
513
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200514 m_nack_rate_dl = error_rate;
515
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200516 if (error_rate > bts_data->cs_adj_upper_limit) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100517 if (m_current_cs_dl.to_num() > 1) {
518 m_current_cs_dl.dec(mode());
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200519 LOGP(DRLCMACDL, LOGL_INFO,
520 "MS (IMSI %s): High error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100521 "reducing CS level to %s\n",
522 imsi(), error_rate, m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200523 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200524 }
525 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200526 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200527 if (now - m_last_cs_not_low > 1000) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100528 m_current_cs_dl.inc(mode());
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200529
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200530 LOGP(DRLCMACDL, LOGL_INFO,
531 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100532 "increasing DL CS level to %s\n",
533 imsi(), error_rate,
534 m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200535 m_last_cs_not_low = now;
536 } else {
537 LOGP(DRLCMACDL, LOGL_DEBUG,
538 "MS (IMSI %s): Low error rate %d%%, "
539 "ignored (within blocking period)\n",
540 imsi(), error_rate);
541 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200542 }
543 } else {
544 LOGP(DRLCMACDL, LOGL_DEBUG,
545 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
546 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200547 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200548 }
549}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200550
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100551GprsCodingScheme GprsMs::max_cs_ul() const
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200552{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200553 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200554
555 OSMO_ASSERT(m_bts != NULL);
556 bts_data = m_bts->bts_data();
557
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100558 if (m_current_cs_ul.isGprs()) {
559 if (!bts_data->max_cs_ul)
560 return GprsCodingScheme(GprsCodingScheme::CS4);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200561
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100562 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200563 }
564
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100565 if (!m_current_cs_ul.isEgprs())
566 return GprsCodingScheme(); /* UNKNOWN */
567
568 if (bts_data->max_mcs_ul)
569 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
570 else if (bts_data->max_cs_ul)
571 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
572
573 return GprsCodingScheme(GprsCodingScheme::MCS4);
574}
575
576GprsCodingScheme GprsMs::max_cs_dl() const
577{
578 struct gprs_rlcmac_bts *bts_data;
579
580 OSMO_ASSERT(m_bts != NULL);
581 bts_data = m_bts->bts_data();
582
583 if (m_current_cs_dl.isGprs()) {
584 if (!bts_data->max_cs_dl)
585 return GprsCodingScheme(GprsCodingScheme::CS4);
586
587 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
588 }
589
590 if (!m_current_cs_dl.isEgprs())
591 return GprsCodingScheme(); /* UNKNOWN */
592
593 if (bts_data->max_mcs_dl)
594 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
595 else if (bts_data->max_cs_dl)
596 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
597
598 return GprsCodingScheme(GprsCodingScheme::MCS4);
599}
600
601void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
602{
603 struct gprs_rlcmac_bts *bts_data;
604 GprsCodingScheme max_cs_ul = this->max_cs_ul();
605
606 int old_link_qual;
607 int low;
608 int high;
609 GprsCodingScheme new_cs_ul = m_current_cs_ul;
610 unsigned current_cs_num = m_current_cs_ul.to_num();
611
612 bts_data = m_bts->bts_data();
613
614 if (!max_cs_ul) {
615 LOGP(DRLCMACDL, LOGL_ERROR,
616 "max_cs_ul cannot be derived (current UL CS: %s)\n",
617 m_current_cs_ul.name());
618 return;
619 }
620
621 if (!m_current_cs_ul)
622 return;
623
624 if (!meas->have_link_qual)
625 return;
626
627 old_link_qual = meas->link_qual;
628
629 if (m_current_cs_ul.isGprs()) {
630 low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
631 high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
632 } else if (m_current_cs_ul.isEgprs()) {
633 /* TODO, use separate table */
634 if (current_cs_num > 4)
635 current_cs_num = 4;
636 low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
637 high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
638 } else {
639 return;
640 }
641
642 if (m_l1_meas.have_link_qual)
643 old_link_qual = m_l1_meas.link_qual;
644
645 if (meas->link_qual < low && old_link_qual < low)
646 new_cs_ul.dec(mode());
647 else if (meas->link_qual > high && old_link_qual > high &&
648 m_current_cs_ul < max_cs_ul)
649 new_cs_ul.inc(mode());
650
651 if (m_current_cs_ul != new_cs_ul) {
652 LOGP(DRLCMACDL, LOGL_INFO,
653 "MS (IMSI %s): "
654 "Link quality %ddB (%ddB) left window [%d, %d], "
655 "modifying uplink CS level: %s -> %s\n",
656 imsi(), meas->link_qual, old_link_qual,
657 low, high,
658 m_current_cs_ul.name(), new_cs_ul.name());
659
660 m_current_cs_ul = new_cs_ul;
661 }
662}
663
664void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
665{
666 unsigned i;
667
668 update_cs_ul(meas);
669
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200670 if (meas->have_rssi)
671 m_l1_meas.set_rssi(meas->rssi);
672 if (meas->have_bto)
673 m_l1_meas.set_bto(meas->bto);
674 if (meas->have_ber)
675 m_l1_meas.set_ber(meas->ber);
676 if (meas->have_link_qual)
677 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200678
679 if (meas->have_ms_rx_qual)
680 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
681 if (meas->have_ms_c_value)
682 m_l1_meas.set_ms_c_value(meas->ms_c_value);
683 if (meas->have_ms_sign_var)
684 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
685
686 if (meas->have_ms_i_level) {
687 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
688 if (meas->ts[i].have_ms_i_level)
689 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
690 else
691 m_l1_meas.ts[i].have_ms_i_level = 0;
692 }
693 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200694}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200695
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100696GprsCodingScheme GprsMs::current_cs_dl() const
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200697{
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100698 GprsCodingScheme cs = m_current_cs_dl;
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200699 size_t unencoded_octets;
700
701 if (!m_bts)
702 return cs;
703
704 unencoded_octets = m_llc_queue.octets();
705
706 /* If the DL TBF is active, add number of unencoded chunk octets */
707 if (m_dl_tbf)
708 unencoded_octets = m_dl_tbf->m_llc.chunk_size();
709
710 /* There are many unencoded octets, don't reduce */
711 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
712 return cs;
713
714 /* RF conditions are good, don't reduce */
715 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
716 return cs;
717
718 /* The throughput would probably be better if the CS level was reduced */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100719 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200720
721 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100722 if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
723 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200724
725 return cs;
726}
727
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200728int GprsMs::first_common_ts() const
729{
730 if (m_dl_tbf)
731 return m_dl_tbf->first_common_ts;
732
733 if (m_ul_tbf)
734 return m_ul_tbf->first_common_ts;
735
736 return -1;
737}
738
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200739uint8_t GprsMs::dl_slots() const
740{
741 uint8_t slots = 0;
742
743 if (m_dl_tbf)
744 slots |= m_dl_tbf->dl_slots();
745
746 if (m_ul_tbf)
747 slots |= m_ul_tbf->dl_slots();
748
749 return slots;
750}
751
752uint8_t GprsMs::ul_slots() const
753{
754 uint8_t slots = 0;
755
756 if (m_dl_tbf)
757 slots |= m_dl_tbf->ul_slots();
758
759 if (m_ul_tbf)
760 slots |= m_ul_tbf->ul_slots();
761
762 return slots;
763}
764
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200765void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
766 uint8_t ul_slots, uint8_t dl_slots)
767{
768 if (m_current_trx) {
769 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
770 m_reserved_dl_slots);
771 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
772 m_reserved_ul_slots);
773 m_reserved_dl_slots = 0;
774 m_reserved_ul_slots = 0;
775 }
776 m_current_trx = trx;
777 if (trx) {
778 m_reserved_dl_slots = dl_slots;
779 m_reserved_ul_slots = ul_slots;
780 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
781 m_reserved_dl_slots);
782 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
783 m_reserved_ul_slots);
784 }
785}
786
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200787gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
788{
789 switch (dir) {
790 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
791 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
792 }
793
794 return NULL;
795}