blob: 76fe47c2c539c83852ca2ffe6d6a42f35a13332c [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 Erlbecka700dd92015-06-02 16:00:41 +0200100 m_current_cs_ul(1),
101 m_current_cs_dl(1),
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),
110 m_codel_state(NULL)
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 if (m_bts) {
121 m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
122 if (m_current_cs_ul < 1)
123 m_current_cs_ul = 1;
124
125 m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
126 if (m_current_cs_dl < 1)
127 m_current_cs_dl = 1;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200128
129 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
130 }
131
132 if (codel_interval) {
133 if (codel_interval == LLC_CODEL_USE_DEFAULT)
134 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
135 m_codel_state = talloc(this, struct gprs_codel);
136 gprs_codel_init(m_codel_state);
137 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200138 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200139 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200140}
141
142GprsMs::~GprsMs()
143{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200144 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
145
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200146 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200147
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200148 set_reserved_slots(NULL, 0, 0);
149
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200150 if (osmo_timer_pending(&m_timer))
151 osmo_timer_del(&m_timer);
152
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200153 if (m_ul_tbf) {
154 m_ul_tbf->set_ms(NULL);
155 m_ul_tbf = NULL;
156 }
157
158 if (m_dl_tbf) {
159 m_dl_tbf->set_ms(NULL);
160 m_dl_tbf = NULL;
161 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200162
163 llist_for_each_safe(pos, tmp, &m_old_tbfs)
164 pos->entry()->set_ms(NULL);
165
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200166 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200167}
168
169void* GprsMs::operator new(size_t size)
170{
171 static void *tall_ms_ctx = NULL;
172 if (!tall_ms_ctx)
173 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
174
175 return talloc_size(tall_ms_ctx, size);
176}
177
178void GprsMs::operator delete(void* p)
179{
180 talloc_free(p);
181}
182
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200183GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200184{
185 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200186 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200187}
188
189void GprsMs::unref()
190{
191 OSMO_ASSERT(m_ref >= 0);
192 m_ref -= 1;
193 if (m_ref == 0)
194 update_status();
195}
196
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200197void GprsMs::start_timer()
198{
199 if (m_delay == 0)
200 return;
201
202 if (!m_timer.data)
203 m_timer.data = ref();
204
205 osmo_timer_schedule(&m_timer, m_delay, 0);
206}
207
208void GprsMs::stop_timer()
209{
210 if (!m_timer.data)
211 return;
212
213 osmo_timer_del(&m_timer);
214 m_timer.data = NULL;
215 unref();
216}
217
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200218void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
219{
220 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
221 attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
222 else
223 attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
224}
225
226void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
227{
228 if (m_ul_tbf == tbf)
229 return;
230
231 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
232 tlli(), tbf->name());
233
234 Guard guard(this);
235
236 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200237 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200238
239 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200240
241 if (tbf)
242 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200243}
244
245void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
246{
247 if (m_dl_tbf == tbf)
248 return;
249
250 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
251 tlli(), tbf->name());
252
253 Guard guard(this);
254
255 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200256 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200257
258 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200259
260 if (tbf)
261 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200262}
263
264void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
265{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200266 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200267 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200268 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200269 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200270 } else {
271 bool found = false;
272
273 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
274 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
275 if (pos->entry() == tbf) {
276 llist_del(pos);
277 found = true;
278 break;
279 }
280 }
281
282 /* Protect against recursive calls via set_ms() */
283 if (!found)
284 return;
285 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200286
287 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
288 tlli(), tbf->name());
289
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200290 if (tbf->ms() == this)
291 tbf->set_ms(NULL);
292
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200293 if (!m_dl_tbf && !m_ul_tbf) {
294 set_reserved_slots(NULL, 0, 0);
295
296 if (tlli() != 0)
297 start_timer();
298 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200299
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200300 update_status();
301}
302
303void GprsMs::update_status()
304{
305 if (m_ref > 0)
306 return;
307
308 if (is_idle() && !m_is_idle) {
309 m_is_idle = true;
310 m_cb->ms_idle(this);
311 /* this can be deleted by now, do not access it */
312 return;
313 }
314
315 if (!is_idle() && m_is_idle) {
316 m_is_idle = false;
317 m_cb->ms_active(this);
318 }
319}
320
Jacob Erlbeckac289052015-08-14 12:50:54 +0200321void GprsMs::reset()
322{
323 LOGP(DRLCMAC, LOGL_INFO,
324 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
325 tlli(), imsi());
326
327 stop_timer();
328
329 m_tlli = 0;
330 m_new_dl_tlli = 0;
331 m_new_ul_tlli = 0;
332 m_imsi[0] = '\0';
333}
334
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200335void GprsMs::merge_old_ms(GprsMs *old_ms)
336{
337 if (old_ms == this)
338 return;
339
340 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
341 set_imsi(old_ms->imsi());
342
343 if (!ms_class() && old_ms->ms_class())
344 set_ms_class(old_ms->ms_class());
345
Jacob Erlbecke0b21f42015-08-21 18:30:05 +0200346 m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
347
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200348 old_ms->reset();
349}
350
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200351void GprsMs::set_tlli(uint32_t tlli)
352{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200353 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200354 return;
355
Jacob Erlbeck93990462015-05-15 15:50:43 +0200356 if (tlli != m_new_dl_tlli) {
357 LOGP(DRLCMAC, LOGL_INFO,
358 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
359 "not yet confirmed\n",
360 this->tlli(), tlli);
361 m_new_ul_tlli = tlli;
362 return;
363 }
364
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200365 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200366 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
367 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200368 m_tlli, tlli);
369
370 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200371 m_new_dl_tlli = 0;
372 m_new_ul_tlli = 0;
373}
374
375bool GprsMs::confirm_tlli(uint32_t tlli)
376{
377 if (tlli == m_tlli || tlli == m_new_dl_tlli)
378 return false;
379
380 if (tlli != m_new_ul_tlli) {
381 /* The MS has not sent a message with the new TLLI, which may
382 * happen according to the spec [TODO: add reference]. */
383
384 LOGP(DRLCMAC, LOGL_INFO,
385 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
386 "partly confirmed\n", tlli);
387 /* Use the network's idea of TLLI as candidate, this does not
388 * change the result value of tlli() */
389 m_new_dl_tlli = tlli;
390 return false;
391 }
392
393 LOGP(DRLCMAC, LOGL_INFO,
394 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
395
396 m_tlli = tlli;
397 m_new_dl_tlli = 0;
398 m_new_ul_tlli = 0;
399
400 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200401}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200402
403void GprsMs::set_imsi(const char *imsi)
404{
405 if (!imsi) {
406 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
407 return;
408 }
409
410 if (imsi[0] && strlen(imsi) < 3) {
411 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
412 imsi);
413 return;
414 }
415
416 if (strcmp(imsi, m_imsi) == 0)
417 return;
418
419 LOGP(DRLCMAC, LOGL_INFO,
420 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
421 tlli(), m_imsi, imsi);
422
423 strncpy(m_imsi, imsi, sizeof(m_imsi));
424 m_imsi[sizeof(m_imsi) - 1] = '\0';
425}
426
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200427void GprsMs::set_ta(uint8_t ta_)
428{
429 if (ta_ == m_ta)
430 return;
431
432 LOGP(DRLCMAC, LOGL_INFO,
433 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
434 tlli(), m_ta, ta_);
435
436 m_ta = ta_;
437}
438
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200439void GprsMs::set_ms_class(uint8_t ms_class_)
440{
441 if (ms_class_ == m_ms_class)
442 return;
443
444 LOGP(DRLCMAC, LOGL_INFO,
445 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
446 tlli(), m_ms_class, ms_class_);
447
448 m_ms_class = ms_class_;
449}
450
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200451void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
452{
453 if (ms_class_ == m_egprs_ms_class)
454 return;
455
456 LOGP(DRLCMAC, LOGL_INFO,
457 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
458 tlli(), m_egprs_ms_class, ms_class_);
459
460 m_egprs_ms_class = ms_class_;
461}
462
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200463void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
464{
465 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200466 int64_t now;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200467 uint8_t max_cs_dl = 4;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200468
469 OSMO_ASSERT(m_bts != NULL);
470 bts_data = m_bts->bts_data();
471
472 if (error_rate < 0)
473 return;
474
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200475 now = now_msec();
476
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200477 if (bts_data->max_cs_dl)
478 max_cs_dl = bts_data->max_cs_dl;
479
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200480 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200481 /* TODO: Support different CS values for UL and DL */
482
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200483 m_nack_rate_dl = error_rate;
484
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200485 if (error_rate > bts_data->cs_adj_upper_limit) {
486 if (m_current_cs_dl > 1) {
487 m_current_cs_dl -= 1;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200488 LOGP(DRLCMACDL, LOGL_INFO,
489 "MS (IMSI %s): High error rate %d%%, "
490 "reducing CS level to %d\n",
491 imsi(), error_rate, m_current_cs_dl);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200492 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200493 }
494 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200495 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200496 if (now - m_last_cs_not_low > 1000) {
497 m_current_cs_dl += 1;
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200498
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200499 LOGP(DRLCMACDL, LOGL_INFO,
500 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200501 "increasing DL CS level to %d\n",
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200502 imsi(), error_rate, m_current_cs_dl);
503 m_last_cs_not_low = now;
504 } else {
505 LOGP(DRLCMACDL, LOGL_DEBUG,
506 "MS (IMSI %s): Low error rate %d%%, "
507 "ignored (within blocking period)\n",
508 imsi(), error_rate);
509 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200510 }
511 } else {
512 LOGP(DRLCMACDL, LOGL_DEBUG,
513 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
514 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200515 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200516 }
517}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200518
519void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
520{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200521 struct gprs_rlcmac_bts *bts_data;
522 uint8_t max_cs_ul = 4;
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200523 unsigned i;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200524
525 OSMO_ASSERT(m_bts != NULL);
526 bts_data = m_bts->bts_data();
527
528 if (bts_data->max_cs_ul)
529 max_cs_ul = bts_data->max_cs_ul;
530
531 if (meas->have_link_qual) {
532 int old_link_qual = meas->link_qual;
533 int low = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
534 int high = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
535 uint8_t new_cs_ul = m_current_cs_ul;
536
537 if (m_l1_meas.have_link_qual)
538 old_link_qual = m_l1_meas.link_qual;
539
540 if (meas->link_qual < low && old_link_qual < low)
541 new_cs_ul = m_current_cs_ul - 1;
542 else if (meas->link_qual > high && old_link_qual > high &&
543 m_current_cs_ul < max_cs_ul)
544 new_cs_ul = m_current_cs_ul + 1;
545
546 if (m_current_cs_ul != new_cs_ul) {
547 LOGP(DRLCMACDL, LOGL_INFO,
548 "MS (IMSI %s): "
549 "Link quality %ddB (%ddB) left window [%d, %d], "
550 "modifying uplink CS level: %d -> %d\n",
551 imsi(), meas->link_qual, old_link_qual,
552 low, high,
553 m_current_cs_ul, new_cs_ul);
554
555 m_current_cs_ul = new_cs_ul;
556 }
557 }
558
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200559 if (meas->have_rssi)
560 m_l1_meas.set_rssi(meas->rssi);
561 if (meas->have_bto)
562 m_l1_meas.set_bto(meas->bto);
563 if (meas->have_ber)
564 m_l1_meas.set_ber(meas->ber);
565 if (meas->have_link_qual)
566 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200567
568 if (meas->have_ms_rx_qual)
569 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
570 if (meas->have_ms_c_value)
571 m_l1_meas.set_ms_c_value(meas->ms_c_value);
572 if (meas->have_ms_sign_var)
573 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
574
575 if (meas->have_ms_i_level) {
576 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
577 if (meas->ts[i].have_ms_i_level)
578 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
579 else
580 m_l1_meas.ts[i].have_ms_i_level = 0;
581 }
582 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200583}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200584
585uint8_t GprsMs::current_cs_dl() const
586{
587 uint8_t cs = m_current_cs_dl;
588 size_t unencoded_octets;
589
590 if (!m_bts)
591 return cs;
592
593 unencoded_octets = m_llc_queue.octets();
594
595 /* If the DL TBF is active, add number of unencoded chunk octets */
596 if (m_dl_tbf)
597 unencoded_octets = m_dl_tbf->m_llc.chunk_size();
598
599 /* There are many unencoded octets, don't reduce */
600 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
601 return cs;
602
603 /* RF conditions are good, don't reduce */
604 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
605 return cs;
606
607 /* The throughput would probably be better if the CS level was reduced */
608 cs -= 1;
609
610 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
611 if (cs == 2)
612 cs -= 1;
613
614 return cs;
615}
616
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200617int GprsMs::first_common_ts() const
618{
619 if (m_dl_tbf)
620 return m_dl_tbf->first_common_ts;
621
622 if (m_ul_tbf)
623 return m_ul_tbf->first_common_ts;
624
625 return -1;
626}
627
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200628uint8_t GprsMs::dl_slots() const
629{
630 uint8_t slots = 0;
631
632 if (m_dl_tbf)
633 slots |= m_dl_tbf->dl_slots();
634
635 if (m_ul_tbf)
636 slots |= m_ul_tbf->dl_slots();
637
638 return slots;
639}
640
641uint8_t GprsMs::ul_slots() const
642{
643 uint8_t slots = 0;
644
645 if (m_dl_tbf)
646 slots |= m_dl_tbf->ul_slots();
647
648 if (m_ul_tbf)
649 slots |= m_ul_tbf->ul_slots();
650
651 return slots;
652}
653
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200654void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
655 uint8_t ul_slots, uint8_t dl_slots)
656{
657 if (m_current_trx) {
658 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
659 m_reserved_dl_slots);
660 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
661 m_reserved_ul_slots);
662 m_reserved_dl_slots = 0;
663 m_reserved_ul_slots = 0;
664 }
665 m_current_trx = trx;
666 if (trx) {
667 m_reserved_dl_slots = dl_slots;
668 m_reserved_ul_slots = ul_slots;
669 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
670 m_reserved_dl_slots);
671 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
672 m_reserved_ul_slots);
673 }
674}
675
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200676gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
677{
678 switch (dir) {
679 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
680 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
681 }
682
683 return NULL;
684}