blob: f82026a5ff7f89fbe6bc5e2547dad9acf2aa3764 [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"
Jacob Erlbecka700dd92015-06-02 16:00:41 +020023#include "bts.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020024#include "tbf.h"
Pau Espin Pedrol9d1cdb12019-09-25 17:47:02 +020025#include "tbf_ul.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020026#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>
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010035 #include <osmocom/core/timer.h>
Max9bbe1602016-07-18 12:50:18 +020036 #include <osmocom/gsm/protocol/gsm_04_08.h>
Max1187a772018-01-26 13:31:42 +010037 #include <osmocom/core/logging.h>
Max136ebcc2019-03-05 14:59:03 +010038 #include "coding_scheme.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020039}
40
Jacob Erlbeck1c3b8992015-08-14 16:01:36 +020041#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020042
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020043extern void *tall_pcu_ctx;
44
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020045static int64_t now_msec()
46{
47 struct timespec ts;
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010048 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020049
50 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
51}
52
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020053struct GprsMsDefaultCallback: public GprsMs::Callback {
54 virtual void ms_idle(class GprsMs *ms) {
55 delete ms;
56 }
57 virtual void ms_active(class GprsMs *) {}
58};
59
60static GprsMsDefaultCallback gprs_default_cb;
61
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020062GprsMs::Guard::Guard(GprsMs *ms) :
63 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020064{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020065}
66
67GprsMs::Guard::~Guard()
68{
69 if (m_ms)
70 m_ms->unref();
71}
72
Jacob Erlbeckb2439bb2015-07-13 14:23:32 +020073bool GprsMs::Guard::is_idle() const
74{
75 if (!m_ms)
76 return true;
77
78 return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
79}
80
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020081void GprsMs::timeout(void *priv_)
82{
83 GprsMs *ms = static_cast<GprsMs *>(priv_);
84
85 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
86 ms->tlli());
87
88 if (ms->m_timer.data) {
89 ms->m_timer.data = NULL;
90 ms->unref();
91 }
92}
93
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020094GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
95 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020096 m_cb(&gprs_default_cb),
97 m_ul_tbf(NULL),
98 m_dl_tbf(NULL),
99 m_tlli(tlli),
Jacob Erlbeck93990462015-05-15 15:50:43 +0200100 m_new_ul_tlli(0),
101 m_new_dl_tlli(0),
Max9bbe1602016-07-18 12:50:18 +0200102 m_ta(GSM48_TA_INVALID),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200103 m_ms_class(0),
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200104 m_egprs_ms_class(0),
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200105 m_current_cs_ul(UNKNOWN),
106 m_current_cs_dl(UNKNOWN),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200107 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +0200108 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +0200109 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200110 m_delay(0),
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200111 m_nack_rate_dl(0),
112 m_reserved_dl_slots(0),
113 m_reserved_ul_slots(0),
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200114 m_current_trx(NULL),
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100115 m_codel_state(NULL),
Maxa4de02d2019-03-13 16:35:09 +0100116 m_mode(GPRS),
sivasankarida7250a2016-12-16 12:57:18 +0530117 m_dl_ctrl_msg(0)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200118{
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200119 int codel_interval = LLC_CODEL_USE_DEFAULT;
120
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200121 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200122
Pau Espin Pedrol43f0bce2020-06-26 13:09:44 +0200123 m_imsi[0] = '\0';
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200124 memset(&m_timer, 0, sizeof(m_timer));
125 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200126 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200127
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100128 set_mode(m_mode);
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200129
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100130 if (m_bts)
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200131 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200132
133 if (codel_interval) {
134 if (codel_interval == LLC_CODEL_USE_DEFAULT)
135 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
136 m_codel_state = talloc(this, struct gprs_codel);
137 gprs_codel_init(m_codel_state);
138 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200139 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200140 m_last_cs_not_low = now_msec();
Oliver Smithcfb63212019-09-05 17:13:33 +0200141 app_info_pending = false;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200142}
143
144GprsMs::~GprsMs()
145{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200146 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
147
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200148 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200149
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200150 set_reserved_slots(NULL, 0, 0);
151
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200152 if (osmo_timer_pending(&m_timer))
153 osmo_timer_del(&m_timer);
154
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200155 if (m_ul_tbf) {
156 m_ul_tbf->set_ms(NULL);
157 m_ul_tbf = NULL;
158 }
159
160 if (m_dl_tbf) {
161 m_dl_tbf->set_ms(NULL);
162 m_dl_tbf = NULL;
163 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200164
165 llist_for_each_safe(pos, tmp, &m_old_tbfs)
166 pos->entry()->set_ms(NULL);
167
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200168 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200169}
170
171void* GprsMs::operator new(size_t size)
172{
173 static void *tall_ms_ctx = NULL;
174 if (!tall_ms_ctx)
175 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
176
177 return talloc_size(tall_ms_ctx, size);
178}
179
180void GprsMs::operator delete(void* p)
181{
182 talloc_free(p);
183}
184
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200185GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200186{
187 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200188 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200189}
190
191void GprsMs::unref()
192{
193 OSMO_ASSERT(m_ref >= 0);
194 m_ref -= 1;
195 if (m_ref == 0)
196 update_status();
197}
198
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200199void GprsMs::start_timer()
200{
201 if (m_delay == 0)
202 return;
203
204 if (!m_timer.data)
205 m_timer.data = ref();
206
207 osmo_timer_schedule(&m_timer, m_delay, 0);
208}
209
210void GprsMs::stop_timer()
211{
212 if (!m_timer.data)
213 return;
214
215 osmo_timer_del(&m_timer);
216 m_timer.data = NULL;
217 unref();
218}
219
Maxa4de02d2019-03-13 16:35:09 +0100220void GprsMs::set_mode(enum mcs_kind mode)
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100221{
222 m_mode = mode;
223
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100224 if (!m_bts)
225 return;
226
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100227 switch (m_mode) {
Maxa4de02d2019-03-13 16:35:09 +0100228 case GPRS:
Max8a8e0fb2019-03-25 16:32:50 +0100229 if (!mcs_is_gprs(m_current_cs_ul)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200230 m_current_cs_ul = mcs_get_gprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100231 m_bts->bts_data()->initial_cs_ul);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200232 if (!mcs_is_valid(m_current_cs_ul))
Maxbea2edb2019-03-06 17:04:59 +0100233 m_current_cs_ul = CS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100234 }
Max8a8e0fb2019-03-25 16:32:50 +0100235 if (!mcs_is_gprs(m_current_cs_dl)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200236 m_current_cs_dl = mcs_get_gprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100237 m_bts->bts_data()->initial_cs_dl);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200238 if (!mcs_is_valid(m_current_cs_dl))
Maxbea2edb2019-03-06 17:04:59 +0100239 m_current_cs_dl = CS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100240 }
241 break;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100242
Maxa4de02d2019-03-13 16:35:09 +0100243 case EGPRS_GMSK:
244 case EGPRS:
Max8a8e0fb2019-03-25 16:32:50 +0100245 if (!mcs_is_edge(m_current_cs_ul)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200246 m_current_cs_ul = mcs_get_egprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100247 m_bts->bts_data()->initial_mcs_ul);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200248 if (!mcs_is_valid(m_current_cs_ul))
Maxbea2edb2019-03-06 17:04:59 +0100249 m_current_cs_ul = MCS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100250 }
Max8a8e0fb2019-03-25 16:32:50 +0100251 if (!mcs_is_edge(m_current_cs_dl)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200252 m_current_cs_dl = mcs_get_egprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100253 m_bts->bts_data()->initial_mcs_dl);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200254 if (!mcs_is_valid(m_current_cs_dl))
Maxbea2edb2019-03-06 17:04:59 +0100255 m_current_cs_dl = MCS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100256 }
257 break;
258 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100259}
260
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200261void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
262{
263 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100264 attach_dl_tbf(as_dl_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200265 else
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100266 attach_ul_tbf(as_ul_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200267}
268
269void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
270{
271 if (m_ul_tbf == tbf)
272 return;
273
274 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
275 tlli(), tbf->name());
276
277 Guard guard(this);
278
279 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200280 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200281
282 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200283
284 if (tbf)
285 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200286}
287
288void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
289{
290 if (m_dl_tbf == tbf)
291 return;
292
293 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
294 tlli(), tbf->name());
295
296 Guard guard(this);
297
298 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200299 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200300
301 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200302
303 if (tbf)
304 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200305}
306
307void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
308{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200309 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200310 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200311 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200312 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200313 } else {
314 bool found = false;
315
316 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
317 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
318 if (pos->entry() == tbf) {
319 llist_del(pos);
320 found = true;
321 break;
322 }
323 }
324
325 /* Protect against recursive calls via set_ms() */
326 if (!found)
327 return;
328 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200329
330 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
331 tlli(), tbf->name());
332
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200333 if (tbf->ms() == this)
334 tbf->set_ms(NULL);
335
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200336 if (!m_dl_tbf && !m_ul_tbf) {
337 set_reserved_slots(NULL, 0, 0);
338
339 if (tlli() != 0)
340 start_timer();
341 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200342
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200343 update_status();
344}
345
346void GprsMs::update_status()
347{
348 if (m_ref > 0)
349 return;
350
351 if (is_idle() && !m_is_idle) {
352 m_is_idle = true;
353 m_cb->ms_idle(this);
354 /* this can be deleted by now, do not access it */
355 return;
356 }
357
358 if (!is_idle() && m_is_idle) {
359 m_is_idle = false;
360 m_cb->ms_active(this);
361 }
362}
363
Jacob Erlbeckac289052015-08-14 12:50:54 +0200364void GprsMs::reset()
365{
366 LOGP(DRLCMAC, LOGL_INFO,
367 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
368 tlli(), imsi());
369
370 stop_timer();
371
372 m_tlli = 0;
373 m_new_dl_tlli = 0;
374 m_new_ul_tlli = 0;
375 m_imsi[0] = '\0';
376}
377
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200378void GprsMs::merge_old_ms(GprsMs *old_ms)
379{
380 if (old_ms == this)
381 return;
382
383 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
384 set_imsi(old_ms->imsi());
385
386 if (!ms_class() && old_ms->ms_class())
387 set_ms_class(old_ms->ms_class());
388
Pau Espin Pedrolcbf05f52020-06-26 14:16:47 +0200389 if (!egprs_ms_class() && old_ms->egprs_ms_class())
390 set_egprs_ms_class(old_ms->egprs_ms_class());
391
Jacob Erlbecke0b21f42015-08-21 18:30:05 +0200392 m_llc_queue.move_and_merge(&old_ms->m_llc_queue);
393
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200394 old_ms->reset();
395}
396
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200397void GprsMs::set_tlli(uint32_t tlli)
398{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200399 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200400 return;
401
Jacob Erlbeck93990462015-05-15 15:50:43 +0200402 if (tlli != m_new_dl_tlli) {
403 LOGP(DRLCMAC, LOGL_INFO,
404 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
405 "not yet confirmed\n",
406 this->tlli(), tlli);
407 m_new_ul_tlli = tlli;
408 return;
409 }
410
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200411 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200412 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
413 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200414 m_tlli, tlli);
415
416 m_tlli = tlli;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200417 m_new_dl_tlli = 0;
418 m_new_ul_tlli = 0;
419}
420
421bool GprsMs::confirm_tlli(uint32_t tlli)
422{
423 if (tlli == m_tlli || tlli == m_new_dl_tlli)
424 return false;
425
426 if (tlli != m_new_ul_tlli) {
427 /* The MS has not sent a message with the new TLLI, which may
428 * happen according to the spec [TODO: add reference]. */
429
430 LOGP(DRLCMAC, LOGL_INFO,
431 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
432 "partly confirmed\n", tlli);
433 /* Use the network's idea of TLLI as candidate, this does not
434 * change the result value of tlli() */
435 m_new_dl_tlli = tlli;
436 return false;
437 }
438
439 LOGP(DRLCMAC, LOGL_INFO,
440 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
441
442 m_tlli = tlli;
443 m_new_dl_tlli = 0;
444 m_new_ul_tlli = 0;
445
446 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200447}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200448
449void GprsMs::set_imsi(const char *imsi)
450{
451 if (!imsi) {
452 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
453 return;
454 }
455
456 if (imsi[0] && strlen(imsi) < 3) {
457 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
458 imsi);
459 return;
460 }
461
462 if (strcmp(imsi, m_imsi) == 0)
463 return;
464
465 LOGP(DRLCMAC, LOGL_INFO,
466 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
467 tlli(), m_imsi, imsi);
468
Pau Espin Pedrol43f0bce2020-06-26 13:09:44 +0200469 osmo_strlcpy(m_imsi, imsi, sizeof(m_imsi));
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200470}
471
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200472void GprsMs::set_ta(uint8_t ta_)
473{
474 if (ta_ == m_ta)
475 return;
476
Max9bbe1602016-07-18 12:50:18 +0200477 if (gsm48_ta_is_valid(ta_)) {
478 LOGP(DRLCMAC, LOGL_INFO,
479 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
480 tlli(), m_ta, ta_);
481 m_ta = ta_;
482 } else
483 LOGP(DRLCMAC, LOGL_NOTICE,
484 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
485 "value %d kept)\n", tlli(), ta_, m_ta);
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200486}
487
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200488void GprsMs::set_ms_class(uint8_t ms_class_)
489{
490 if (ms_class_ == m_ms_class)
491 return;
492
493 LOGP(DRLCMAC, LOGL_INFO,
494 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
495 tlli(), m_ms_class, ms_class_);
496
497 m_ms_class = ms_class_;
498}
499
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200500void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
501{
502 if (ms_class_ == m_egprs_ms_class)
503 return;
504
505 LOGP(DRLCMAC, LOGL_INFO,
506 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
507 tlli(), m_egprs_ms_class, ms_class_);
508
509 m_egprs_ms_class = ms_class_;
510}
511
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200512void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
513{
514 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200515 int64_t now;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200516 enum CodingScheme max_cs_dl = this->max_cs_dl();
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200517
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100518 OSMO_ASSERT(max_cs_dl);
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200519 bts_data = m_bts->bts_data();
520
521 if (error_rate < 0)
522 return;
523
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200524 now = now_msec();
525
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200526 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200527 /* TODO: Support different CS values for UL and DL */
528
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200529 m_nack_rate_dl = error_rate;
530
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200531 if (error_rate > bts_data->cs_adj_upper_limit) {
Max898dddb2019-03-12 15:50:57 +0100532 if (mcs_chan_code(m_current_cs_dl) > 0) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200533 mcs_dec_kind(&m_current_cs_dl, mode());
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200534 LOGP(DRLCMACDL, LOGL_INFO,
535 "MS (IMSI %s): High error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100536 "reducing CS level to %s\n",
Max136ebcc2019-03-05 14:59:03 +0100537 imsi(), error_rate, mcs_name(m_current_cs_dl));
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200538 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200539 }
540 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200541 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200542 if (now - m_last_cs_not_low > 1000) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200543 mcs_inc_kind(&m_current_cs_dl, mode());
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200544
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200545 LOGP(DRLCMACDL, LOGL_INFO,
546 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100547 "increasing DL CS level to %s\n",
548 imsi(), error_rate,
Max136ebcc2019-03-05 14:59:03 +0100549 mcs_name(m_current_cs_dl));
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200550 m_last_cs_not_low = now;
551 } else {
552 LOGP(DRLCMACDL, LOGL_DEBUG,
553 "MS (IMSI %s): Low error rate %d%%, "
554 "ignored (within blocking period)\n",
555 imsi(), error_rate);
556 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200557 }
558 } else {
559 LOGP(DRLCMACDL, LOGL_DEBUG,
560 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
561 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200562 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200563 }
564}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200565
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200566enum CodingScheme GprsMs::max_cs_ul() const
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200567{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200568 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200569
570 OSMO_ASSERT(m_bts != NULL);
571 bts_data = m_bts->bts_data();
572
Max8a8e0fb2019-03-25 16:32:50 +0100573 if (mcs_is_gprs(m_current_cs_ul)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200574 if (!bts_data->max_cs_ul) {
575 return CS4;
576 }
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200577
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200578 return mcs_get_gprs_by_num(bts_data->max_cs_ul);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200579 }
580
Max8a8e0fb2019-03-25 16:32:50 +0100581 if (!mcs_is_edge(m_current_cs_ul))
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200582 return UNKNOWN;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100583
584 if (bts_data->max_mcs_ul)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200585 return mcs_get_egprs_by_num(bts_data->max_mcs_ul);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100586 else if (bts_data->max_cs_ul)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200587 return mcs_get_egprs_by_num(bts_data->max_cs_ul);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100588
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200589 return MCS4;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100590}
591
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200592void GprsMs::set_current_cs_dl(enum CodingScheme scheme)
Aravind Sirsikare8ccafc2016-07-13 11:37:47 +0530593{
594 m_current_cs_dl = scheme;
595}
596
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200597enum CodingScheme GprsMs::max_cs_dl() const
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100598{
599 struct gprs_rlcmac_bts *bts_data;
600
601 OSMO_ASSERT(m_bts != NULL);
602 bts_data = m_bts->bts_data();
603
Max8a8e0fb2019-03-25 16:32:50 +0100604 if (mcs_is_gprs(m_current_cs_dl)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200605 if (!bts_data->max_cs_dl) {
606 return CS4;
607 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100608
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200609 return mcs_get_gprs_by_num(bts_data->max_cs_dl);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100610 }
611
Max8a8e0fb2019-03-25 16:32:50 +0100612 if (!mcs_is_edge(m_current_cs_dl))
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200613 return UNKNOWN;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100614
615 if (bts_data->max_mcs_dl)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200616 return mcs_get_egprs_by_num(bts_data->max_mcs_dl);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100617 else if (bts_data->max_cs_dl)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200618 return mcs_get_egprs_by_num(bts_data->max_cs_dl);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100619
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200620 return MCS4;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100621}
622
623void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
624{
625 struct gprs_rlcmac_bts *bts_data;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200626 enum CodingScheme max_cs_ul = this->max_cs_ul();
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100627
628 int old_link_qual;
629 int low;
630 int high;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200631 enum CodingScheme new_cs_ul = m_current_cs_ul;
Max898dddb2019-03-12 15:50:57 +0100632 uint8_t current_cs = mcs_chan_code(m_current_cs_ul);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100633
634 bts_data = m_bts->bts_data();
635
636 if (!max_cs_ul) {
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400637 LOGP(DRLCMACMEAS, LOGL_ERROR,
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100638 "max_cs_ul cannot be derived (current UL CS: %s)\n",
Max136ebcc2019-03-05 14:59:03 +0100639 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100640 return;
641 }
642
Maxfa3085b2019-03-07 11:46:43 +0100643 if (!m_current_cs_ul) {
644 LOGP(DRLCMACMEAS, LOGL_ERROR,
645 "Unable to update UL (M)CS because it's not set: %s\n",
Max136ebcc2019-03-05 14:59:03 +0100646 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100647 return;
Maxfa3085b2019-03-07 11:46:43 +0100648 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100649
Maxfa3085b2019-03-07 11:46:43 +0100650 if (!meas->have_link_qual) {
651 LOGP(DRLCMACMEAS, LOGL_ERROR,
652 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
Max136ebcc2019-03-05 14:59:03 +0100653 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100654 return;
Maxfa3085b2019-03-07 11:46:43 +0100655 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100656
Max8a8e0fb2019-03-25 16:32:50 +0100657 if (mcs_is_gprs(m_current_cs_ul)) {
Max898dddb2019-03-12 15:50:57 +0100658 if (current_cs >= MAX_GPRS_CS)
659 current_cs = MAX_GPRS_CS - 1;
660 low = bts_data->cs_lqual_ranges[current_cs].low;
661 high = bts_data->cs_lqual_ranges[current_cs].high;
Max8a8e0fb2019-03-25 16:32:50 +0100662 } else if (mcs_is_edge(m_current_cs_ul)) {
Max898dddb2019-03-12 15:50:57 +0100663 if (current_cs >= MAX_EDGE_MCS)
664 current_cs = MAX_EDGE_MCS - 1;
665 low = bts_data->mcs_lqual_ranges[current_cs].low;
666 high = bts_data->mcs_lqual_ranges[current_cs].high;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100667 } else {
Maxfa3085b2019-03-07 11:46:43 +0100668 LOGP(DRLCMACMEAS, LOGL_ERROR,
669 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
Max136ebcc2019-03-05 14:59:03 +0100670 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100671 return;
672 }
673
Vadim Yanitskiyfd734de2019-11-08 05:37:18 +0700674 /* To avoid rapid changes of the coding scheme, we also take
675 * the old link quality value into account (if present). */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100676 if (m_l1_meas.have_link_qual)
677 old_link_qual = m_l1_meas.link_qual;
Vadim Yanitskiyfd734de2019-11-08 05:37:18 +0700678 else
679 old_link_qual = meas->link_qual;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100680
681 if (meas->link_qual < low && old_link_qual < low)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200682 mcs_dec_kind(&new_cs_ul, mode());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100683 else if (meas->link_qual > high && old_link_qual > high &&
684 m_current_cs_ul < max_cs_ul)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200685 mcs_inc_kind(&new_cs_ul, mode());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100686
687 if (m_current_cs_ul != new_cs_ul) {
Pau Espin Pedrolf2dad592020-08-18 20:26:25 +0200688 LOGPMS(this, DRLCMACMEAS, LOGL_INFO,
689 "Link quality %ddB (old %ddB) left window [%d, %d], "
690 "modifying uplink CS level: %s -> %s\n",
691 meas->link_qual, old_link_qual,
692 low, high,
693 mcs_name(m_current_cs_ul), mcs_name(new_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100694
695 m_current_cs_ul = new_cs_ul;
696 }
697}
698
699void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
700{
701 unsigned i;
702
703 update_cs_ul(meas);
704
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200705 if (meas->have_rssi)
706 m_l1_meas.set_rssi(meas->rssi);
707 if (meas->have_bto)
708 m_l1_meas.set_bto(meas->bto);
709 if (meas->have_ber)
710 m_l1_meas.set_ber(meas->ber);
711 if (meas->have_link_qual)
712 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200713
714 if (meas->have_ms_rx_qual)
715 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
716 if (meas->have_ms_c_value)
717 m_l1_meas.set_ms_c_value(meas->ms_c_value);
718 if (meas->have_ms_sign_var)
719 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
720
721 if (meas->have_ms_i_level) {
722 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
723 if (meas->ts[i].have_ms_i_level)
724 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
725 else
726 m_l1_meas.ts[i].have_ms_i_level = 0;
727 }
728 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200729}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200730
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200731enum CodingScheme GprsMs::current_cs_dl() const
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200732{
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200733 enum CodingScheme cs = m_current_cs_dl;
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200734 size_t unencoded_octets;
735
736 if (!m_bts)
737 return cs;
738
739 unencoded_octets = m_llc_queue.octets();
740
741 /* If the DL TBF is active, add number of unencoded chunk octets */
742 if (m_dl_tbf)
Jacob Erlbeck14e26cb2016-02-03 15:26:29 +0100743 unencoded_octets += m_dl_tbf->m_llc.chunk_size();
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200744
745 /* There are many unencoded octets, don't reduce */
746 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
747 return cs;
748
749 /* RF conditions are good, don't reduce */
750 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
751 return cs;
752
753 /* The throughput would probably be better if the CS level was reduced */
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200754 mcs_dec_kind(&cs, mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200755
756 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200757 if (cs == CS2)
758 mcs_dec_kind(&cs, mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200759
760 return cs;
761}
762
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200763int GprsMs::first_common_ts() const
764{
765 if (m_dl_tbf)
766 return m_dl_tbf->first_common_ts;
767
768 if (m_ul_tbf)
769 return m_ul_tbf->first_common_ts;
770
771 return -1;
772}
773
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200774uint8_t GprsMs::dl_slots() const
775{
776 uint8_t slots = 0;
777
778 if (m_dl_tbf)
779 slots |= m_dl_tbf->dl_slots();
780
781 if (m_ul_tbf)
782 slots |= m_ul_tbf->dl_slots();
783
784 return slots;
785}
786
787uint8_t GprsMs::ul_slots() const
788{
789 uint8_t slots = 0;
790
791 if (m_dl_tbf)
792 slots |= m_dl_tbf->ul_slots();
793
794 if (m_ul_tbf)
795 slots |= m_ul_tbf->ul_slots();
796
797 return slots;
798}
799
Jacob Erlbeck7c72aca2016-01-22 17:06:14 +0100800uint8_t GprsMs::current_pacch_slots() const
801{
802 uint8_t slots = 0;
803
804 bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
805 bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
806
807 if (!is_dl_active && !is_ul_active)
808 return 0;
809
810 /* see TS 44.060, 8.1.1.2.2 */
811 if (is_dl_active && !is_ul_active)
812 slots = m_dl_tbf->dl_slots();
813 else if (!is_dl_active && is_ul_active)
814 slots = m_ul_tbf->ul_slots();
815 else
816 slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
817
818 /* Assume a multislot class 1 device */
819 /* TODO: For class 2 devices, this could be removed */
820 slots = pcu_lsb(slots);
821
822 return slots;
823}
824
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200825void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
826 uint8_t ul_slots, uint8_t dl_slots)
827{
828 if (m_current_trx) {
829 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
830 m_reserved_dl_slots);
831 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
832 m_reserved_ul_slots);
833 m_reserved_dl_slots = 0;
834 m_reserved_ul_slots = 0;
835 }
836 m_current_trx = trx;
837 if (trx) {
838 m_reserved_dl_slots = dl_slots;
839 m_reserved_ul_slots = ul_slots;
840 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
841 m_reserved_dl_slots);
842 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
843 m_reserved_ul_slots);
844 }
845}
846
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200847gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
848{
849 switch (dir) {
850 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
851 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
852 }
853
854 return NULL;
855}