blob: 4296fd33817bc4f1252d5441765549ca58be916c [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>
35}
36
Jacob Erlbeck1c3b8992015-08-14 16:01:36 +020037#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020038
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020039extern void *tall_pcu_ctx;
40
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020041static int64_t now_msec()
42{
43 struct timespec ts;
44 clock_gettime(CLOCK_MONOTONIC, &ts);
45
46 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
47}
48
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020049struct GprsMsDefaultCallback: public GprsMs::Callback {
50 virtual void ms_idle(class GprsMs *ms) {
51 delete ms;
52 }
53 virtual void ms_active(class GprsMs *) {}
54};
55
56static GprsMsDefaultCallback gprs_default_cb;
57
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020058GprsMs::Guard::Guard(GprsMs *ms) :
59 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020060{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020061}
62
63GprsMs::Guard::~Guard()
64{
65 if (m_ms)
66 m_ms->unref();
67}
68
Jacob Erlbeckb2439bb2015-07-13 14:23:32 +020069bool GprsMs::Guard::is_idle() const
70{
71 if (!m_ms)
72 return true;
73
74 return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
75}
76
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020077void GprsMs::timeout(void *priv_)
78{
79 GprsMs *ms = static_cast<GprsMs *>(priv_);
80
81 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
82 ms->tlli());
83
84 if (ms->m_timer.data) {
85 ms->m_timer.data = NULL;
86 ms->unref();
87 }
88}
89
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020090GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
91 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020092 m_cb(&gprs_default_cb),
93 m_ul_tbf(NULL),
94 m_dl_tbf(NULL),
95 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020096 m_new_ul_tlli(0),
97 m_new_dl_tlli(0),
Jacob Erlbeck9200ce62015-05-22 17:48:04 +020098 m_ta(0),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +020099 m_ms_class(0),
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200100 m_egprs_ms_class(0),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200101 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +0200102 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +0200103 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200104 m_delay(0),
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200105 m_nack_rate_dl(0),
106 m_reserved_dl_slots(0),
107 m_reserved_ul_slots(0),
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200108 m_current_trx(NULL),
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100109 m_codel_state(NULL),
110 m_mode(GprsCodingScheme::GPRS)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200111{
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200112 int codel_interval = LLC_CODEL_USE_DEFAULT;
113
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200114 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200115
116 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200117 memset(&m_timer, 0, sizeof(m_timer));
118 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200119 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200120
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100121 set_mode(m_mode);
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200122
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100123 if (m_bts)
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200124 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200125
126 if (codel_interval) {
127 if (codel_interval == LLC_CODEL_USE_DEFAULT)
128 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
129 m_codel_state = talloc(this, struct gprs_codel);
130 gprs_codel_init(m_codel_state);
131 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200132 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200133 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200134}
135
136GprsMs::~GprsMs()
137{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200138 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
139
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200140 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200141
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200142 set_reserved_slots(NULL, 0, 0);
143
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200144 if (osmo_timer_pending(&m_timer))
145 osmo_timer_del(&m_timer);
146
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200147 if (m_ul_tbf) {
148 m_ul_tbf->set_ms(NULL);
149 m_ul_tbf = NULL;
150 }
151
152 if (m_dl_tbf) {
153 m_dl_tbf->set_ms(NULL);
154 m_dl_tbf = NULL;
155 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200156
157 llist_for_each_safe(pos, tmp, &m_old_tbfs)
158 pos->entry()->set_ms(NULL);
159
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200160 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200161}
162
163void* GprsMs::operator new(size_t size)
164{
165 static void *tall_ms_ctx = NULL;
166 if (!tall_ms_ctx)
167 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
168
169 return talloc_size(tall_ms_ctx, size);
170}
171
172void GprsMs::operator delete(void* p)
173{
174 talloc_free(p);
175}
176
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200177GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200178{
179 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200180 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200181}
182
183void GprsMs::unref()
184{
185 OSMO_ASSERT(m_ref >= 0);
186 m_ref -= 1;
187 if (m_ref == 0)
188 update_status();
189}
190
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200191void GprsMs::start_timer()
192{
193 if (m_delay == 0)
194 return;
195
196 if (!m_timer.data)
197 m_timer.data = ref();
198
199 osmo_timer_schedule(&m_timer, m_delay, 0);
200}
201
202void GprsMs::stop_timer()
203{
204 if (!m_timer.data)
205 return;
206
207 osmo_timer_del(&m_timer);
208 m_timer.data = NULL;
209 unref();
210}
211
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100212void GprsMs::set_mode(GprsCodingScheme::Mode mode)
213{
214 m_mode = mode;
215
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100216 if (!m_bts)
217 return;
218
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100219 switch (m_mode) {
220 case GprsCodingScheme::GPRS:
221 if (!m_current_cs_ul.isGprs()) {
222 m_current_cs_ul = GprsCodingScheme::getGprsByNum(
223 m_bts->bts_data()->initial_cs_ul);
224 if (!m_current_cs_ul.isValid())
225 m_current_cs_ul = GprsCodingScheme::CS1;
226 }
227 if (!m_current_cs_dl.isGprs()) {
228 m_current_cs_dl = GprsCodingScheme::getGprsByNum(
229 m_bts->bts_data()->initial_cs_dl);
230 if (!m_current_cs_dl.isValid())
231 m_current_cs_dl = GprsCodingScheme::CS1;
232 }
233 break;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100234
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100235 case GprsCodingScheme::EGPRS_GMSK:
236 case GprsCodingScheme::EGPRS:
237 if (!m_current_cs_ul.isEgprs()) {
238 m_current_cs_ul = GprsCodingScheme::getEgprsByNum(
239 m_bts->bts_data()->initial_mcs_ul);
Holger Hans Peter Freytherfd263b02016-03-04 18:24:50 +0100240 if (!m_current_cs_ul.isValid())
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100241 m_current_cs_ul = GprsCodingScheme::MCS1;
242 }
243 if (!m_current_cs_dl.isEgprs()) {
244 m_current_cs_dl = GprsCodingScheme::getEgprsByNum(
245 m_bts->bts_data()->initial_mcs_dl);
246 if (!m_current_cs_dl.isValid())
247 m_current_cs_dl = GprsCodingScheme::MCS1;
248 }
249 break;
250 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100251}
252
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200253void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
254{
255 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100256 attach_dl_tbf(as_dl_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200257 else
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100258 attach_ul_tbf(as_ul_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200259}
260
261void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
262{
263 if (m_ul_tbf == tbf)
264 return;
265
266 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
267 tlli(), tbf->name());
268
269 Guard guard(this);
270
271 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200272 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200273
274 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200275
276 if (tbf)
277 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200278}
279
280void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
281{
282 if (m_dl_tbf == tbf)
283 return;
284
285 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
286 tlli(), tbf->name());
287
288 Guard guard(this);
289
290 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200291 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200292
293 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200294
295 if (tbf)
296 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200297}
298
299void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
300{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200301 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200302 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200303 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200304 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200305 } else {
306 bool found = false;
307
308 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
309 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
310 if (pos->entry() == tbf) {
311 llist_del(pos);
312 found = true;
313 break;
314 }
315 }
316
317 /* Protect against recursive calls via set_ms() */
318 if (!found)
319 return;
320 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200321
322 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
323 tlli(), tbf->name());
324
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200325 if (tbf->ms() == this)
326 tbf->set_ms(NULL);
327
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200328 if (!m_dl_tbf && !m_ul_tbf) {
329 set_reserved_slots(NULL, 0, 0);
330
331 if (tlli() != 0)
332 start_timer();
333 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200334
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200335 update_status();
336}
337
338void GprsMs::update_status()
339{
340 if (m_ref > 0)
341 return;
342
343 if (is_idle() && !m_is_idle) {
344 m_is_idle = true;
345 m_cb->ms_idle(this);
346 /* this can be deleted by now, do not access it */
347 return;
348 }
349
350 if (!is_idle() && m_is_idle) {
351 m_is_idle = false;
352 m_cb->ms_active(this);
353 }
354}
355
Jacob Erlbeckac289052015-08-14 12:50:54 +0200356void GprsMs::reset()
357{
358 LOGP(DRLCMAC, LOGL_INFO,
359 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
360 tlli(), imsi());
361
362 stop_timer();
363
364 m_tlli = 0;
365 m_new_dl_tlli = 0;
366 m_new_ul_tlli = 0;
367 m_imsi[0] = '\0';
368}
369
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200370void GprsMs::merge_old_ms(GprsMs *old_ms)
371{
372 if (old_ms == this)
373 return;
374
375 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
376 set_imsi(old_ms->imsi());
377
378 if (!ms_class() && old_ms->ms_class())
379 set_ms_class(old_ms->ms_class());
380
Jacob Erlbecke0b21f42015-08-21 18:30:05 +0200381 m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
382
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200383 old_ms->reset();
384}
385
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200386void GprsMs::set_tlli(uint32_t tlli)
387{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200388 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200389 return;
390
Jacob Erlbeck93990462015-05-15 15:50:43 +0200391 if (tlli != m_new_dl_tlli) {
392 LOGP(DRLCMAC, LOGL_INFO,
393 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
394 "not yet confirmed\n",
395 this->tlli(), tlli);
396 m_new_ul_tlli = tlli;
397 return;
398 }
399
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200400 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200401 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
402 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200403 m_tlli, tlli);
404
405 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200406 m_new_dl_tlli = 0;
407 m_new_ul_tlli = 0;
408}
409
410bool GprsMs::confirm_tlli(uint32_t tlli)
411{
412 if (tlli == m_tlli || tlli == m_new_dl_tlli)
413 return false;
414
415 if (tlli != m_new_ul_tlli) {
416 /* The MS has not sent a message with the new TLLI, which may
417 * happen according to the spec [TODO: add reference]. */
418
419 LOGP(DRLCMAC, LOGL_INFO,
420 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
421 "partly confirmed\n", tlli);
422 /* Use the network's idea of TLLI as candidate, this does not
423 * change the result value of tlli() */
424 m_new_dl_tlli = tlli;
425 return false;
426 }
427
428 LOGP(DRLCMAC, LOGL_INFO,
429 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
430
431 m_tlli = tlli;
432 m_new_dl_tlli = 0;
433 m_new_ul_tlli = 0;
434
435 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200436}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200437
438void GprsMs::set_imsi(const char *imsi)
439{
440 if (!imsi) {
441 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
442 return;
443 }
444
445 if (imsi[0] && strlen(imsi) < 3) {
446 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
447 imsi);
448 return;
449 }
450
451 if (strcmp(imsi, m_imsi) == 0)
452 return;
453
454 LOGP(DRLCMAC, LOGL_INFO,
455 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
456 tlli(), m_imsi, imsi);
457
458 strncpy(m_imsi, imsi, sizeof(m_imsi));
459 m_imsi[sizeof(m_imsi) - 1] = '\0';
460}
461
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200462void GprsMs::set_ta(uint8_t ta_)
463{
464 if (ta_ == m_ta)
465 return;
466
467 LOGP(DRLCMAC, LOGL_INFO,
468 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
469 tlli(), m_ta, ta_);
470
471 m_ta = ta_;
472}
473
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200474void GprsMs::set_ms_class(uint8_t ms_class_)
475{
476 if (ms_class_ == m_ms_class)
477 return;
478
479 LOGP(DRLCMAC, LOGL_INFO,
480 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
481 tlli(), m_ms_class, ms_class_);
482
483 m_ms_class = ms_class_;
484}
485
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200486void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
487{
488 if (ms_class_ == m_egprs_ms_class)
489 return;
490
491 LOGP(DRLCMAC, LOGL_INFO,
492 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
493 tlli(), m_egprs_ms_class, ms_class_);
494
495 m_egprs_ms_class = ms_class_;
496}
497
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200498void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
499{
500 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200501 int64_t now;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100502 GprsCodingScheme max_cs_dl = this->max_cs_dl();
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200503
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100504 OSMO_ASSERT(max_cs_dl);
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200505 bts_data = m_bts->bts_data();
506
507 if (error_rate < 0)
508 return;
509
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200510 now = now_msec();
511
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200512 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200513 /* TODO: Support different CS values for UL and DL */
514
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200515 m_nack_rate_dl = error_rate;
516
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200517 if (error_rate > bts_data->cs_adj_upper_limit) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100518 if (m_current_cs_dl.to_num() > 1) {
519 m_current_cs_dl.dec(mode());
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200520 LOGP(DRLCMACDL, LOGL_INFO,
521 "MS (IMSI %s): High error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100522 "reducing CS level to %s\n",
523 imsi(), error_rate, m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200524 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200525 }
526 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200527 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200528 if (now - m_last_cs_not_low > 1000) {
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100529 m_current_cs_dl.inc(mode());
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200530
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200531 LOGP(DRLCMACDL, LOGL_INFO,
532 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100533 "increasing DL CS level to %s\n",
534 imsi(), error_rate,
535 m_current_cs_dl.name());
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200536 m_last_cs_not_low = now;
537 } else {
538 LOGP(DRLCMACDL, LOGL_DEBUG,
539 "MS (IMSI %s): Low error rate %d%%, "
540 "ignored (within blocking period)\n",
541 imsi(), error_rate);
542 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200543 }
544 } else {
545 LOGP(DRLCMACDL, LOGL_DEBUG,
546 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
547 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200548 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200549 }
550}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200551
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100552GprsCodingScheme GprsMs::max_cs_ul() const
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200553{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200554 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200555
556 OSMO_ASSERT(m_bts != NULL);
557 bts_data = m_bts->bts_data();
558
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100559 if (m_current_cs_ul.isGprs()) {
560 if (!bts_data->max_cs_ul)
561 return GprsCodingScheme(GprsCodingScheme::CS4);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200562
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100563 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_ul);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200564 }
565
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100566 if (!m_current_cs_ul.isEgprs())
567 return GprsCodingScheme(); /* UNKNOWN */
568
569 if (bts_data->max_mcs_ul)
570 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_ul);
571 else if (bts_data->max_cs_ul)
572 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_ul);
573
574 return GprsCodingScheme(GprsCodingScheme::MCS4);
575}
576
577GprsCodingScheme GprsMs::max_cs_dl() const
578{
579 struct gprs_rlcmac_bts *bts_data;
580
581 OSMO_ASSERT(m_bts != NULL);
582 bts_data = m_bts->bts_data();
583
584 if (m_current_cs_dl.isGprs()) {
585 if (!bts_data->max_cs_dl)
586 return GprsCodingScheme(GprsCodingScheme::CS4);
587
588 return GprsCodingScheme::getGprsByNum(bts_data->max_cs_dl);
589 }
590
591 if (!m_current_cs_dl.isEgprs())
592 return GprsCodingScheme(); /* UNKNOWN */
593
594 if (bts_data->max_mcs_dl)
595 return GprsCodingScheme::getEgprsByNum(bts_data->max_mcs_dl);
596 else if (bts_data->max_cs_dl)
597 return GprsCodingScheme::getEgprsByNum(bts_data->max_cs_dl);
598
599 return GprsCodingScheme(GprsCodingScheme::MCS4);
600}
601
602void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
603{
604 struct gprs_rlcmac_bts *bts_data;
605 GprsCodingScheme max_cs_ul = this->max_cs_ul();
606
607 int old_link_qual;
608 int low;
609 int high;
610 GprsCodingScheme new_cs_ul = m_current_cs_ul;
611 unsigned current_cs_num = m_current_cs_ul.to_num();
612
613 bts_data = m_bts->bts_data();
614
615 if (!max_cs_ul) {
616 LOGP(DRLCMACDL, LOGL_ERROR,
617 "max_cs_ul cannot be derived (current UL CS: %s)\n",
618 m_current_cs_ul.name());
619 return;
620 }
621
622 if (!m_current_cs_ul)
623 return;
624
625 if (!meas->have_link_qual)
626 return;
627
628 old_link_qual = meas->link_qual;
629
630 if (m_current_cs_ul.isGprs()) {
631 low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
632 high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
633 } else if (m_current_cs_ul.isEgprs()) {
634 /* TODO, use separate table */
635 if (current_cs_num > 4)
636 current_cs_num = 4;
637 low = bts_data->cs_lqual_ranges[current_cs_num-1].low;
638 high = bts_data->cs_lqual_ranges[current_cs_num-1].high;
639 } else {
640 return;
641 }
642
643 if (m_l1_meas.have_link_qual)
644 old_link_qual = m_l1_meas.link_qual;
645
646 if (meas->link_qual < low && old_link_qual < low)
647 new_cs_ul.dec(mode());
648 else if (meas->link_qual > high && old_link_qual > high &&
649 m_current_cs_ul < max_cs_ul)
650 new_cs_ul.inc(mode());
651
652 if (m_current_cs_ul != new_cs_ul) {
653 LOGP(DRLCMACDL, LOGL_INFO,
654 "MS (IMSI %s): "
655 "Link quality %ddB (%ddB) left window [%d, %d], "
656 "modifying uplink CS level: %s -> %s\n",
657 imsi(), meas->link_qual, old_link_qual,
658 low, high,
659 m_current_cs_ul.name(), new_cs_ul.name());
660
661 m_current_cs_ul = new_cs_ul;
662 }
663}
664
665void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
666{
667 unsigned i;
668
669 update_cs_ul(meas);
670
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200671 if (meas->have_rssi)
672 m_l1_meas.set_rssi(meas->rssi);
673 if (meas->have_bto)
674 m_l1_meas.set_bto(meas->bto);
675 if (meas->have_ber)
676 m_l1_meas.set_ber(meas->ber);
677 if (meas->have_link_qual)
678 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200679
680 if (meas->have_ms_rx_qual)
681 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
682 if (meas->have_ms_c_value)
683 m_l1_meas.set_ms_c_value(meas->ms_c_value);
684 if (meas->have_ms_sign_var)
685 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
686
687 if (meas->have_ms_i_level) {
688 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
689 if (meas->ts[i].have_ms_i_level)
690 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
691 else
692 m_l1_meas.ts[i].have_ms_i_level = 0;
693 }
694 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200695}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200696
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100697GprsCodingScheme GprsMs::current_cs_dl() const
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200698{
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100699 GprsCodingScheme cs = m_current_cs_dl;
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200700 size_t unencoded_octets;
701
702 if (!m_bts)
703 return cs;
704
705 unencoded_octets = m_llc_queue.octets();
706
707 /* If the DL TBF is active, add number of unencoded chunk octets */
708 if (m_dl_tbf)
Jacob Erlbeck14e26cb2016-02-03 15:26:29 +0100709 unencoded_octets += m_dl_tbf->m_llc.chunk_size();
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200710
711 /* There are many unencoded octets, don't reduce */
712 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
713 return cs;
714
715 /* RF conditions are good, don't reduce */
716 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
717 return cs;
718
719 /* The throughput would probably be better if the CS level was reduced */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100720 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200721
722 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100723 if (cs == GprsCodingScheme(GprsCodingScheme::CS2))
724 cs.dec(mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200725
726 return cs;
727}
728
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200729int GprsMs::first_common_ts() const
730{
731 if (m_dl_tbf)
732 return m_dl_tbf->first_common_ts;
733
734 if (m_ul_tbf)
735 return m_ul_tbf->first_common_ts;
736
737 return -1;
738}
739
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200740uint8_t GprsMs::dl_slots() const
741{
742 uint8_t slots = 0;
743
744 if (m_dl_tbf)
745 slots |= m_dl_tbf->dl_slots();
746
747 if (m_ul_tbf)
748 slots |= m_ul_tbf->dl_slots();
749
750 return slots;
751}
752
753uint8_t GprsMs::ul_slots() const
754{
755 uint8_t slots = 0;
756
757 if (m_dl_tbf)
758 slots |= m_dl_tbf->ul_slots();
759
760 if (m_ul_tbf)
761 slots |= m_ul_tbf->ul_slots();
762
763 return slots;
764}
765
Jacob Erlbeck7c72aca2016-01-22 17:06:14 +0100766uint8_t GprsMs::current_pacch_slots() const
767{
768 uint8_t slots = 0;
769
770 bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
771 bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
772
773 if (!is_dl_active && !is_ul_active)
774 return 0;
775
776 /* see TS 44.060, 8.1.1.2.2 */
777 if (is_dl_active && !is_ul_active)
778 slots = m_dl_tbf->dl_slots();
779 else if (!is_dl_active && is_ul_active)
780 slots = m_ul_tbf->ul_slots();
781 else
782 slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
783
784 /* Assume a multislot class 1 device */
785 /* TODO: For class 2 devices, this could be removed */
786 slots = pcu_lsb(slots);
787
788 return slots;
789}
790
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200791void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
792 uint8_t ul_slots, uint8_t dl_slots)
793{
794 if (m_current_trx) {
795 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
796 m_reserved_dl_slots);
797 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
798 m_reserved_ul_slots);
799 m_reserved_dl_slots = 0;
800 m_reserved_ul_slots = 0;
801 }
802 m_current_trx = trx;
803 if (trx) {
804 m_reserved_dl_slots = dl_slots;
805 m_reserved_ul_slots = ul_slots;
806 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
807 m_reserved_dl_slots);
808 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
809 m_reserved_ul_slots);
810 }
811}
812
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200813gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
814{
815 switch (dir) {
816 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
817 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
818 }
819
820 return NULL;
821}