blob: 189e4bd29727bed764d34337ae4d5c665ad45a3a [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),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +020092 m_list(this),
93 m_delay(0)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020094{
95 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +020096
97 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020098 memset(&m_timer, 0, sizeof(m_timer));
99 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200100 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200101 if (m_bts) {
102 m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
103 if (m_current_cs_ul < 1)
104 m_current_cs_ul = 1;
105
106 m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
107 if (m_current_cs_dl < 1)
108 m_current_cs_dl = 1;
109 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200110 m_last_cs_not_low = now_msec();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200111}
112
113GprsMs::~GprsMs()
114{
115 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200116
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200117 if (osmo_timer_pending(&m_timer))
118 osmo_timer_del(&m_timer);
119
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200120 if (m_ul_tbf) {
121 m_ul_tbf->set_ms(NULL);
122 m_ul_tbf = NULL;
123 }
124
125 if (m_dl_tbf) {
126 m_dl_tbf->set_ms(NULL);
127 m_dl_tbf = NULL;
128 }
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200129 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200130}
131
132void* GprsMs::operator new(size_t size)
133{
134 static void *tall_ms_ctx = NULL;
135 if (!tall_ms_ctx)
136 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
137
138 return talloc_size(tall_ms_ctx, size);
139}
140
141void GprsMs::operator delete(void* p)
142{
143 talloc_free(p);
144}
145
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200146GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200147{
148 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200149 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200150}
151
152void GprsMs::unref()
153{
154 OSMO_ASSERT(m_ref >= 0);
155 m_ref -= 1;
156 if (m_ref == 0)
157 update_status();
158}
159
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200160void GprsMs::start_timer()
161{
162 if (m_delay == 0)
163 return;
164
165 if (!m_timer.data)
166 m_timer.data = ref();
167
168 osmo_timer_schedule(&m_timer, m_delay, 0);
169}
170
171void GprsMs::stop_timer()
172{
173 if (!m_timer.data)
174 return;
175
176 osmo_timer_del(&m_timer);
177 m_timer.data = NULL;
178 unref();
179}
180
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200181void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
182{
183 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
184 attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
185 else
186 attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
187}
188
189void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
190{
191 if (m_ul_tbf == tbf)
192 return;
193
194 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
195 tlli(), tbf->name());
196
197 Guard guard(this);
198
199 if (m_ul_tbf)
200 detach_tbf(m_ul_tbf);
201
202 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200203
204 if (tbf)
205 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200206}
207
208void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
209{
210 if (m_dl_tbf == tbf)
211 return;
212
213 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
214 tlli(), tbf->name());
215
216 Guard guard(this);
217
218 if (m_dl_tbf)
219 detach_tbf(m_dl_tbf);
220
221 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200222
223 if (tbf)
224 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200225}
226
227void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
228{
229 if (m_ul_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf))
230 m_ul_tbf = NULL;
231 else if (m_dl_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf))
232 m_dl_tbf = NULL;
233 else
234 return;
235
236 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
237 tlli(), tbf->name());
238
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200239 if (tbf->ms() == this)
240 tbf->set_ms(NULL);
241
Jacob Erlbeck0808f682015-06-11 13:02:29 +0200242 if (!m_dl_tbf && !m_ul_tbf)
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200243 start_timer();
244
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200245 update_status();
246}
247
248void GprsMs::update_status()
249{
250 if (m_ref > 0)
251 return;
252
253 if (is_idle() && !m_is_idle) {
254 m_is_idle = true;
255 m_cb->ms_idle(this);
256 /* this can be deleted by now, do not access it */
257 return;
258 }
259
260 if (!is_idle() && m_is_idle) {
261 m_is_idle = false;
262 m_cb->ms_active(this);
263 }
264}
265
266void GprsMs::set_tlli(uint32_t tlli)
267{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200268 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200269 return;
270
Jacob Erlbeck93990462015-05-15 15:50:43 +0200271 if (tlli != m_new_dl_tlli) {
272 LOGP(DRLCMAC, LOGL_INFO,
273 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
274 "not yet confirmed\n",
275 this->tlli(), tlli);
276 m_new_ul_tlli = tlli;
277 return;
278 }
279
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200280 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200281 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
282 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200283 m_tlli, tlli);
284
285 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200286 m_new_dl_tlli = 0;
287 m_new_ul_tlli = 0;
288}
289
290bool GprsMs::confirm_tlli(uint32_t tlli)
291{
292 if (tlli == m_tlli || tlli == m_new_dl_tlli)
293 return false;
294
295 if (tlli != m_new_ul_tlli) {
296 /* The MS has not sent a message with the new TLLI, which may
297 * happen according to the spec [TODO: add reference]. */
298
299 LOGP(DRLCMAC, LOGL_INFO,
300 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
301 "partly confirmed\n", tlli);
302 /* Use the network's idea of TLLI as candidate, this does not
303 * change the result value of tlli() */
304 m_new_dl_tlli = tlli;
305 return false;
306 }
307
308 LOGP(DRLCMAC, LOGL_INFO,
309 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
310
311 m_tlli = tlli;
312 m_new_dl_tlli = 0;
313 m_new_ul_tlli = 0;
314
315 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200316}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200317
318void GprsMs::set_imsi(const char *imsi)
319{
320 if (!imsi) {
321 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
322 return;
323 }
324
325 if (imsi[0] && strlen(imsi) < 3) {
326 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
327 imsi);
328 return;
329 }
330
331 if (strcmp(imsi, m_imsi) == 0)
332 return;
333
334 LOGP(DRLCMAC, LOGL_INFO,
335 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
336 tlli(), m_imsi, imsi);
337
338 strncpy(m_imsi, imsi, sizeof(m_imsi));
339 m_imsi[sizeof(m_imsi) - 1] = '\0';
340}
341
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200342void GprsMs::set_ta(uint8_t ta_)
343{
344 if (ta_ == m_ta)
345 return;
346
347 LOGP(DRLCMAC, LOGL_INFO,
348 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
349 tlli(), m_ta, ta_);
350
351 m_ta = ta_;
352}
353
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200354void GprsMs::set_ms_class(uint8_t ms_class_)
355{
356 if (ms_class_ == m_ms_class)
357 return;
358
359 LOGP(DRLCMAC, LOGL_INFO,
360 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
361 tlli(), m_ms_class, ms_class_);
362
363 m_ms_class = ms_class_;
364}
365
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200366void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
367{
368 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200369 int64_t now;
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200370 uint8_t max_cs_ul = 4, max_cs_dl = 4;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200371
372 OSMO_ASSERT(m_bts != NULL);
373 bts_data = m_bts->bts_data();
374
375 if (error_rate < 0)
376 return;
377
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200378 now = now_msec();
379
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200380 if (bts_data->max_cs_ul)
381 max_cs_ul = bts_data->max_cs_ul;
382
383 if (bts_data->max_cs_dl)
384 max_cs_dl = bts_data->max_cs_dl;
385
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200386 /* TODO: Support different CS values for UL and DL */
387
388 if (error_rate > bts_data->cs_adj_upper_limit) {
389 if (m_current_cs_dl > 1) {
390 m_current_cs_dl -= 1;
391 m_current_cs_ul = m_current_cs_dl;
392 LOGP(DRLCMACDL, LOGL_INFO,
393 "MS (IMSI %s): High error rate %d%%, "
394 "reducing CS level to %d\n",
395 imsi(), error_rate, m_current_cs_dl);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200396 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200397 }
398 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200399 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200400 if (now - m_last_cs_not_low > 1000) {
401 m_current_cs_dl += 1;
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200402 if (m_current_cs_dl <= max_cs_ul)
403 m_current_cs_ul = m_current_cs_dl;
404
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200405 LOGP(DRLCMACDL, LOGL_INFO,
406 "MS (IMSI %s): Low error rate %d%%, "
407 "increasing CS level to %d\n",
408 imsi(), error_rate, m_current_cs_dl);
409 m_last_cs_not_low = now;
410 } else {
411 LOGP(DRLCMACDL, LOGL_DEBUG,
412 "MS (IMSI %s): Low error rate %d%%, "
413 "ignored (within blocking period)\n",
414 imsi(), error_rate);
415 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200416 }
417 } else {
418 LOGP(DRLCMACDL, LOGL_DEBUG,
419 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
420 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200421 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200422 }
423}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200424
425void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
426{
427 if (meas->have_rssi)
428 m_l1_meas.set_rssi(meas->rssi);
429 if (meas->have_bto)
430 m_l1_meas.set_bto(meas->bto);
431 if (meas->have_ber)
432 m_l1_meas.set_ber(meas->ber);
433 if (meas->have_link_qual)
434 m_l1_meas.set_link_qual(meas->link_qual);
435}