blob: dc8783c4ab84b7091d9ff6509d94f4d8367ca3d6 [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
28extern "C" {
29 #include <osmocom/core/talloc.h>
30 #include <osmocom/core/utils.h>
31}
32
33extern void *tall_pcu_ctx;
34
35struct GprsMsDefaultCallback: public GprsMs::Callback {
36 virtual void ms_idle(class GprsMs *ms) {
37 delete ms;
38 }
39 virtual void ms_active(class GprsMs *) {}
40};
41
42static GprsMsDefaultCallback gprs_default_cb;
43
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020044GprsMs::Guard::Guard(GprsMs *ms) :
45 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020046{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020047}
48
49GprsMs::Guard::~Guard()
50{
51 if (m_ms)
52 m_ms->unref();
53}
54
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020055void GprsMs::timeout(void *priv_)
56{
57 GprsMs *ms = static_cast<GprsMs *>(priv_);
58
59 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
60 ms->tlli());
61
62 if (ms->m_timer.data) {
63 ms->m_timer.data = NULL;
64 ms->unref();
65 }
66}
67
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020068GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
69 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020070 m_cb(&gprs_default_cb),
71 m_ul_tbf(NULL),
72 m_dl_tbf(NULL),
73 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +020074 m_new_ul_tlli(0),
75 m_new_dl_tlli(0),
Jacob Erlbeck9200ce62015-05-22 17:48:04 +020076 m_ta(0),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +020077 m_ms_class(0),
Jacob Erlbecka700dd92015-06-02 16:00:41 +020078 m_current_cs_ul(1),
79 m_current_cs_dl(1),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020080 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +020081 m_ref(0),
82 m_list(this)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020083{
84 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +020085
86 m_imsi[0] = 0;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020087 memset(&m_timer, 0, sizeof(m_timer));
88 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +020089 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +020090 if (m_bts) {
91 m_current_cs_ul = m_bts->bts_data()->initial_cs_ul;
92 if (m_current_cs_ul < 1)
93 m_current_cs_ul = 1;
94
95 m_current_cs_dl = m_bts->bts_data()->initial_cs_dl;
96 if (m_current_cs_dl < 1)
97 m_current_cs_dl = 1;
98 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020099}
100
101GprsMs::~GprsMs()
102{
103 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200104
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200105 if (osmo_timer_pending(&m_timer))
106 osmo_timer_del(&m_timer);
107
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200108 if (m_ul_tbf) {
109 m_ul_tbf->set_ms(NULL);
110 m_ul_tbf = NULL;
111 }
112
113 if (m_dl_tbf) {
114 m_dl_tbf->set_ms(NULL);
115 m_dl_tbf = NULL;
116 }
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200117 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200118}
119
120void* GprsMs::operator new(size_t size)
121{
122 static void *tall_ms_ctx = NULL;
123 if (!tall_ms_ctx)
124 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
125
126 return talloc_size(tall_ms_ctx, size);
127}
128
129void GprsMs::operator delete(void* p)
130{
131 talloc_free(p);
132}
133
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200134GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200135{
136 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200137 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200138}
139
140void GprsMs::unref()
141{
142 OSMO_ASSERT(m_ref >= 0);
143 m_ref -= 1;
144 if (m_ref == 0)
145 update_status();
146}
147
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200148void GprsMs::start_timer()
149{
150 if (m_delay == 0)
151 return;
152
153 if (!m_timer.data)
154 m_timer.data = ref();
155
156 osmo_timer_schedule(&m_timer, m_delay, 0);
157}
158
159void GprsMs::stop_timer()
160{
161 if (!m_timer.data)
162 return;
163
164 osmo_timer_del(&m_timer);
165 m_timer.data = NULL;
166 unref();
167}
168
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200169void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
170{
171 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
172 attach_dl_tbf(static_cast<gprs_rlcmac_dl_tbf *>(tbf));
173 else
174 attach_ul_tbf(static_cast<gprs_rlcmac_ul_tbf *>(tbf));
175}
176
177void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
178{
179 if (m_ul_tbf == tbf)
180 return;
181
182 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
183 tlli(), tbf->name());
184
185 Guard guard(this);
186
187 if (m_ul_tbf)
188 detach_tbf(m_ul_tbf);
189
190 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200191
192 if (tbf)
193 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200194}
195
196void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
197{
198 if (m_dl_tbf == tbf)
199 return;
200
201 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
202 tlli(), tbf->name());
203
204 Guard guard(this);
205
206 if (m_dl_tbf)
207 detach_tbf(m_dl_tbf);
208
209 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200210
211 if (tbf)
212 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200213}
214
215void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
216{
217 if (m_ul_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf))
218 m_ul_tbf = NULL;
219 else if (m_dl_tbf && tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf))
220 m_dl_tbf = NULL;
221 else
222 return;
223
224 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
225 tlli(), tbf->name());
226
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200227 if (tbf->ms() == this)
228 tbf->set_ms(NULL);
229
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200230 if (!m_dl_tbf && !m_dl_tbf)
231 start_timer();
232
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200233 update_status();
234}
235
236void GprsMs::update_status()
237{
238 if (m_ref > 0)
239 return;
240
241 if (is_idle() && !m_is_idle) {
242 m_is_idle = true;
243 m_cb->ms_idle(this);
244 /* this can be deleted by now, do not access it */
245 return;
246 }
247
248 if (!is_idle() && m_is_idle) {
249 m_is_idle = false;
250 m_cb->ms_active(this);
251 }
252}
253
254void GprsMs::set_tlli(uint32_t tlli)
255{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200256 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200257 return;
258
Jacob Erlbeck93990462015-05-15 15:50:43 +0200259 if (tlli != m_new_dl_tlli) {
260 LOGP(DRLCMAC, LOGL_INFO,
261 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
262 "not yet confirmed\n",
263 this->tlli(), tlli);
264 m_new_ul_tlli = tlli;
265 return;
266 }
267
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200268 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200269 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
270 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200271 m_tlli, tlli);
272
273 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200274 m_new_dl_tlli = 0;
275 m_new_ul_tlli = 0;
276}
277
278bool GprsMs::confirm_tlli(uint32_t tlli)
279{
280 if (tlli == m_tlli || tlli == m_new_dl_tlli)
281 return false;
282
283 if (tlli != m_new_ul_tlli) {
284 /* The MS has not sent a message with the new TLLI, which may
285 * happen according to the spec [TODO: add reference]. */
286
287 LOGP(DRLCMAC, LOGL_INFO,
288 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
289 "partly confirmed\n", tlli);
290 /* Use the network's idea of TLLI as candidate, this does not
291 * change the result value of tlli() */
292 m_new_dl_tlli = tlli;
293 return false;
294 }
295
296 LOGP(DRLCMAC, LOGL_INFO,
297 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
298
299 m_tlli = tlli;
300 m_new_dl_tlli = 0;
301 m_new_ul_tlli = 0;
302
303 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200304}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200305
306void GprsMs::set_imsi(const char *imsi)
307{
308 if (!imsi) {
309 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
310 return;
311 }
312
313 if (imsi[0] && strlen(imsi) < 3) {
314 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
315 imsi);
316 return;
317 }
318
319 if (strcmp(imsi, m_imsi) == 0)
320 return;
321
322 LOGP(DRLCMAC, LOGL_INFO,
323 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
324 tlli(), m_imsi, imsi);
325
326 strncpy(m_imsi, imsi, sizeof(m_imsi));
327 m_imsi[sizeof(m_imsi) - 1] = '\0';
328}
329
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200330void GprsMs::set_ta(uint8_t ta_)
331{
332 if (ta_ == m_ta)
333 return;
334
335 LOGP(DRLCMAC, LOGL_INFO,
336 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
337 tlli(), m_ta, ta_);
338
339 m_ta = ta_;
340}
341
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200342void GprsMs::set_ms_class(uint8_t ms_class_)
343{
344 if (ms_class_ == m_ms_class)
345 return;
346
347 LOGP(DRLCMAC, LOGL_INFO,
348 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
349 tlli(), m_ms_class, ms_class_);
350
351 m_ms_class = ms_class_;
352}
353