blob: adb6ff52b754c8be0c4983e1effc75eb59f4b2ab [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"
27
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020028#include <time.h>
29
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020030extern "C" {
31 #include <osmocom/core/talloc.h>
32 #include <osmocom/core/utils.h>
33}
34
35extern void *tall_pcu_ctx;
36
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020037static int64_t now_msec()
38{
39 struct timespec ts;
40 clock_gettime(CLOCK_MONOTONIC, &ts);
41
42 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
43}
44
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020045struct GprsMsDefaultCallback: public GprsMs::Callback {
46 virtual void ms_idle(class GprsMs *ms) {
47 delete ms;
48 }
49 virtual void ms_active(class GprsMs *) {}
50};
51
52static GprsMsDefaultCallback gprs_default_cb;
53
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020054GprsMs::Guard::Guard(GprsMs *ms) :
55 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020056{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020057}
58
59GprsMs::Guard::~Guard()
60{
61 if (m_ms)
62 m_ms->unref();
63}
64
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020065void GprsMs::timeout(void *priv_)
66{
67 GprsMs *ms = static_cast<GprsMs *>(priv_);
68
69 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
70 ms->tlli());
71
72 if (ms->m_timer.data) {
73 ms->m_timer.data = NULL;
74 ms->unref();
75 }
76}
77
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020078GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
79 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020080 m_cb(&gprs_default_cb),
81 m_ul_tbf(NULL),
82 m_dl_tbf(NULL),
83 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020084 m_new_ul_tlli(0),
85 m_new_dl_tlli(0),
Jacob Erlbeck9200ce62015-05-22 17:48:04 +020086 m_ta(0),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +020087 m_ms_class(0),
Jacob Erlbecka700dd92015-06-02 16:00:41 +020088 m_current_cs_ul(1),
89 m_current_cs_dl(1),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020090 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +020091 m_ref(0),
92 m_list(this)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020093{
94 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +020095
96 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020097 memset(&m_timer, 0, sizeof(m_timer));
98 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +020099 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200100 if (m_bts) {
101 m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
102 if (m_current_cs_ul < 1)
103 m_current_cs_ul = 1;
104
105 m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
106 if (m_current_cs_dl < 1)
107 m_current_cs_dl = 1;
108 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200109 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200110}
111
112GprsMs::~GprsMs()
113{
114 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200115
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200116 if (osmo_timer_pending(&m_timer))
117 osmo_timer_del(&m_timer);
118
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200119 if (m_ul_tbf) {
120 m_ul_tbf->set_ms(NULL);
121 m_ul_tbf = NULL;
122 }
123
124 if (m_dl_tbf) {
125 m_dl_tbf->set_ms(NULL);
126 m_dl_tbf = NULL;
127 }
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200128 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200129}
130
131void* GprsMs::operator new(size_t size)
132{
133 static void *tall_ms_ctx = NULL;
134 if (!tall_ms_ctx)
135 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
136
137 return talloc_size(tall_ms_ctx, size);
138}
139
140void GprsMs::operator delete(void* p)
141{
142 talloc_free(p);
143}
144
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200145GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200146{
147 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200148 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200149}
150
151void GprsMs::unref()
152{
153 OSMO_ASSERT(m_ref >= 0);
154 m_ref -= 1;
155 if (m_ref == 0)
156 update_status();
157}
158
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200159void GprsMs::start_timer()
160{
161 if (m_delay == 0)
162 return;
163
164 if (!m_timer.data)
165 m_timer.data = ref();
166
167 osmo_timer_schedule(&m_timer, m_delay, 0);
168}
169
170void GprsMs::stop_timer()
171{
172 if (!m_timer.data)
173 return;
174
175 osmo_timer_del(&m_timer);
176 m_timer.data = NULL;
177 unref();
178}
179
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200180void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
181{
182 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
183 attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
184 else
185 attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
186}
187
188void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
189{
190 if (m_ul_tbf == tbf)
191 return;
192
193 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
194 tlli(), tbf->name());
195
196 Guard guard(this);
197
198 if (m_ul_tbf)
199 detach_tbf(m_ul_tbf);
200
201 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200202
203 if (tbf)
204 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200205}
206
207void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
208{
209 if (m_dl_tbf == tbf)
210 return;
211
212 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
213 tlli(), tbf->name());
214
215 Guard guard(this);
216
217 if (m_dl_tbf)
218 detach_tbf(m_dl_tbf);
219
220 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200221
222 if (tbf)
223 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200224}
225
226void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
227{
228 if (m_ul_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf))
229 m_ul_tbf = NULL;
230 else if (m_dl_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf))
231 m_dl_tbf = NULL;
232 else
233 return;
234
235 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
236 tlli(), tbf->name());
237
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200238 if (tbf->ms() == this)
239 tbf->set_ms(NULL);
240
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200241 if (!m_dl_tbf && !m_dl_tbf)
242 start_timer();
243
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200244 update_status();
245}
246
247void GprsMs::update_status()
248{
249 if (m_ref > 0)
250 return;
251
252 if (is_idle() && !m_is_idle) {
253 m_is_idle = true;
254 m_cb->ms_idle(this);
255 /* this can be deleted by now, do not access it */
256 return;
257 }
258
259 if (!is_idle() && m_is_idle) {
260 m_is_idle = false;
261 m_cb->ms_active(this);
262 }
263}
264
265void GprsMs::set_tlli(uint32_t tlli)
266{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200267 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200268 return;
269
Jacob Erlbeck93990462015-05-15 15:50:43 +0200270 if (tlli != m_new_dl_tlli) {
271 LOGP(DRLCMAC, LOGL_INFO,
272 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
273 "not yet confirmed\n",
274 this->tlli(), tlli);
275 m_new_ul_tlli = tlli;
276 return;
277 }
278
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200279 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200280 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
281 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200282 m_tlli, tlli);
283
284 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200285 m_new_dl_tlli = 0;
286 m_new_ul_tlli = 0;
287}
288
289bool GprsMs::confirm_tlli(uint32_t tlli)
290{
291 if (tlli == m_tlli || tlli == m_new_dl_tlli)
292 return false;
293
294 if (tlli != m_new_ul_tlli) {
295 /* The MS has not sent a message with the new TLLI, which may
296 * happen according to the spec [TODO: add reference]. */
297
298 LOGP(DRLCMAC, LOGL_INFO,
299 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
300 "partly confirmed\n", tlli);
301 /* Use the network's idea of TLLI as candidate, this does not
302 * change the result value of tlli() */
303 m_new_dl_tlli = tlli;
304 return false;
305 }
306
307 LOGP(DRLCMAC, LOGL_INFO,
308 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
309
310 m_tlli = tlli;
311 m_new_dl_tlli = 0;
312 m_new_ul_tlli = 0;
313
314 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200315}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200316
317void GprsMs::set_imsi(const char *imsi)
318{
319 if (!imsi) {
320 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
321 return;
322 }
323
324 if (imsi[0] && strlen(imsi) < 3) {
325 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
326 imsi);
327 return;
328 }
329
330 if (strcmp(imsi, m_imsi) == 0)
331 return;
332
333 LOGP(DRLCMAC, LOGL_INFO,
334 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
335 tlli(), m_imsi, imsi);
336
337 strncpy(m_imsi, imsi, sizeof(m_imsi));
338 m_imsi[sizeof(m_imsi) - 1] = '\0';
339}
340
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200341void GprsMs::set_ta(uint8_t ta_)
342{
343 if (ta_ == m_ta)
344 return;
345
346 LOGP(DRLCMAC, LOGL_INFO,
347 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
348 tlli(), m_ta, ta_);
349
350 m_ta = ta_;
351}
352
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200353void GprsMs::set_ms_class(uint8_t ms_class_)
354{
355 if (ms_class_ == m_ms_class)
356 return;
357
358 LOGP(DRLCMAC, LOGL_INFO,
359 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
360 tlli(), m_ms_class, ms_class_);
361
362 m_ms_class = ms_class_;
363}
364
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200365void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
366{
367 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200368 int64_t now;
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200369 uint8_t max_cs_ul = 4, max_cs_dl = 4;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200370
371 OSMO_ASSERT(m_bts != NULL);
372 bts_data = m_bts->bts_data();
373
374 if (error_rate < 0)
375 return;
376
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200377 now = now_msec();
378
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200379 if (bts_data->max_cs_ul)
380 max_cs_ul = bts_data->max_cs_ul;
381
382 if (bts_data->max_cs_dl)
383 max_cs_dl = bts_data->max_cs_dl;
384
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200385 /* TODO: Support different CS values for UL and DL */
386
387 if (error_rate > bts_data->cs_adj_upper_limit) {
388 if (m_current_cs_dl > 1) {
389 m_current_cs_dl -= 1;
390 m_current_cs_ul = m_current_cs_dl;
391 LOGP(DRLCMACDL, LOGL_INFO,
392 "MS (IMSI %s): High error rate %d%%, "
393 "reducing CS level to %d\n",
394 imsi(), error_rate, m_current_cs_dl);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200395 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200396 }
397 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200398 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200399 if (now - m_last_cs_not_low > 1000) {
400 m_current_cs_dl += 1;
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200401 if (m_current_cs_dl <= max_cs_ul)
402 m_current_cs_ul = m_current_cs_dl;
403
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200404 LOGP(DRLCMACDL, LOGL_INFO,
405 "MS (IMSI %s): Low error rate %d%%, "
406 "increasing CS level to %d\n",
407 imsi(), error_rate, m_current_cs_dl);
408 m_last_cs_not_low = now;
409 } else {
410 LOGP(DRLCMACDL, LOGL_DEBUG,
411 "MS (IMSI %s): Low error rate %d%%, "
412 "ignored (within blocking period)\n",
413 imsi(), error_rate);
414 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200415 }
416 } else {
417 LOGP(DRLCMACDL, LOGL_DEBUG,
418 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
419 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200420 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200421 }
422}