blob: c891cdfdf58e2b516f875df26986c084d291fd6d [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>
Vadim Yanitskiycb988942020-11-08 13:27:35 +070037 #include <osmocom/gsm/gsm48.h>
Max1187a772018-01-26 13:31:42 +010038 #include <osmocom/core/logging.h>
Max136ebcc2019-03-05 14:59:03 +010039 #include "coding_scheme.h"
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020040}
41
Jacob Erlbeck1c3b8992015-08-14 16:01:36 +020042#define GPRS_CODEL_SLOW_INTERVAL_MS 4000
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +020043
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020044extern void *tall_pcu_ctx;
45
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020046static int64_t now_msec()
47{
48 struct timespec ts;
Pau Espin Pedrol1de68732020-03-11 14:04:52 +010049 osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +020050
51 return int64_t(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
52}
53
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020054struct GprsMsDefaultCallback: public GprsMs::Callback {
55 virtual void ms_idle(class GprsMs *ms) {
56 delete ms;
57 }
58 virtual void ms_active(class GprsMs *) {}
59};
60
61static GprsMsDefaultCallback gprs_default_cb;
62
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020063GprsMs::Guard::Guard(GprsMs *ms) :
64 m_ms(ms ? ms->ref() : NULL)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020065{
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020066}
67
68GprsMs::Guard::~Guard()
69{
70 if (m_ms)
71 m_ms->unref();
72}
73
Jacob Erlbeckb2439bb2015-07-13 14:23:32 +020074bool GprsMs::Guard::is_idle() const
75{
76 if (!m_ms)
77 return true;
78
79 return !m_ms->m_ul_tbf && !m_ms->m_dl_tbf && m_ms->m_ref == 1;
80}
81
Jacob Erlbeckd9e10242015-05-28 15:43:53 +020082void GprsMs::timeout(void *priv_)
83{
84 GprsMs *ms = static_cast<GprsMs *>(priv_);
85
86 LOGP(DRLCMAC, LOGL_INFO, "Timeout for MS object, TLLI = 0x%08x\n",
87 ms->tlli());
88
89 if (ms->m_timer.data) {
90 ms->m_timer.data = NULL;
91 ms->unref();
92 }
93}
94
Jacob Erlbeck17214bb2015-06-02 14:06:12 +020095GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
96 m_bts(bts),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +020097 m_cb(&gprs_default_cb),
98 m_ul_tbf(NULL),
99 m_dl_tbf(NULL),
100 m_tlli(tlli),
Vadim Yanitskiycb988942020-11-08 13:27:35 +0700101 m_new_ul_tlli(GSM_RESERVED_TMSI),
102 m_new_dl_tlli(GSM_RESERVED_TMSI),
Max9bbe1602016-07-18 12:50:18 +0200103 m_ta(GSM48_TA_INVALID),
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200104 m_ms_class(0),
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200105 m_egprs_ms_class(0),
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200106 m_current_cs_ul(UNKNOWN),
107 m_current_cs_dl(UNKNOWN),
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200108 m_is_idle(true),
Jacob Erlbeck53670862015-05-12 17:54:33 +0200109 m_ref(0),
Jacob Erlbeck25db7c62015-06-11 12:59:01 +0200110 m_list(this),
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200111 m_delay(0),
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200112 m_nack_rate_dl(0),
113 m_reserved_dl_slots(0),
114 m_reserved_ul_slots(0),
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200115 m_current_trx(NULL),
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100116 m_codel_state(NULL),
Maxa4de02d2019-03-13 16:35:09 +0100117 m_mode(GPRS),
sivasankarida7250a2016-12-16 12:57:18 +0530118 m_dl_ctrl_msg(0)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200119{
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200120 int codel_interval = LLC_CODEL_USE_DEFAULT;
121
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200122 LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200123
Pau Espin Pedrol43f0bce2020-06-26 13:09:44 +0200124 m_imsi[0] = '\0';
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200125 memset(&m_timer, 0, sizeof(m_timer));
126 m_timer.cb = GprsMs::timeout;
Jacob Erlbeck489a2b32015-05-28 19:07:01 +0200127 m_llc_queue.init();
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200128
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100129 set_mode(m_mode);
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200130
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100131 if (m_bts)
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200132 codel_interval = m_bts->bts_data()->llc_codel_interval_msec;
Jacob Erlbeckd4ad7312015-07-17 16:39:09 +0200133
134 if (codel_interval) {
135 if (codel_interval == LLC_CODEL_USE_DEFAULT)
136 codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS;
137 m_codel_state = talloc(this, struct gprs_codel);
138 gprs_codel_init(m_codel_state);
139 gprs_codel_set_interval(m_codel_state, codel_interval);
Jacob Erlbecka700dd92015-06-02 16:00:41 +0200140 }
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200141 m_last_cs_not_low = now_msec();
Oliver Smithcfb63212019-09-05 17:13:33 +0200142 app_info_pending = false;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200143}
144
145GprsMs::~GprsMs()
146{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200147 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
148
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200149 LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200150
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200151 set_reserved_slots(NULL, 0, 0);
152
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200153 if (osmo_timer_pending(&m_timer))
154 osmo_timer_del(&m_timer);
155
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200156 if (m_ul_tbf) {
157 m_ul_tbf->set_ms(NULL);
158 m_ul_tbf = NULL;
159 }
160
161 if (m_dl_tbf) {
162 m_dl_tbf->set_ms(NULL);
163 m_dl_tbf = NULL;
164 }
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200165
166 llist_for_each_safe(pos, tmp, &m_old_tbfs)
167 pos->entry()->set_ms(NULL);
168
Jacob Erlbeck17214bb2015-06-02 14:06:12 +0200169 m_llc_queue.clear(m_bts);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200170}
171
172void* GprsMs::operator new(size_t size)
173{
174 static void *tall_ms_ctx = NULL;
175 if (!tall_ms_ctx)
176 tall_ms_ctx = talloc_named_const(tall_pcu_ctx, 0, __PRETTY_FUNCTION__);
177
178 return talloc_size(tall_ms_ctx, size);
179}
180
181void GprsMs::operator delete(void* p)
182{
183 talloc_free(p);
184}
185
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200186GprsMs *GprsMs::ref()
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200187{
188 m_ref += 1;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200189 return this;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200190}
191
192void GprsMs::unref()
193{
194 OSMO_ASSERT(m_ref >= 0);
195 m_ref -= 1;
196 if (m_ref == 0)
197 update_status();
198}
199
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200200void GprsMs::start_timer()
201{
202 if (m_delay == 0)
203 return;
204
205 if (!m_timer.data)
206 m_timer.data = ref();
207
208 osmo_timer_schedule(&m_timer, m_delay, 0);
209}
210
211void GprsMs::stop_timer()
212{
213 if (!m_timer.data)
214 return;
215
216 osmo_timer_del(&m_timer);
217 m_timer.data = NULL;
218 unref();
219}
220
Maxa4de02d2019-03-13 16:35:09 +0100221void GprsMs::set_mode(enum mcs_kind mode)
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100222{
223 m_mode = mode;
224
Jacob Erlbeck53bc1862016-01-18 17:23:09 +0100225 if (!m_bts)
226 return;
227
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100228 switch (m_mode) {
Maxa4de02d2019-03-13 16:35:09 +0100229 case GPRS:
Max8a8e0fb2019-03-25 16:32:50 +0100230 if (!mcs_is_gprs(m_current_cs_ul)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200231 m_current_cs_ul = mcs_get_gprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100232 m_bts->bts_data()->initial_cs_ul);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200233 if (!mcs_is_valid(m_current_cs_ul))
Maxbea2edb2019-03-06 17:04:59 +0100234 m_current_cs_ul = CS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100235 }
Max8a8e0fb2019-03-25 16:32:50 +0100236 if (!mcs_is_gprs(m_current_cs_dl)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200237 m_current_cs_dl = mcs_get_gprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100238 m_bts->bts_data()->initial_cs_dl);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200239 if (!mcs_is_valid(m_current_cs_dl))
Maxbea2edb2019-03-06 17:04:59 +0100240 m_current_cs_dl = CS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100241 }
242 break;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100243
Maxa4de02d2019-03-13 16:35:09 +0100244 case EGPRS_GMSK:
245 case EGPRS:
Max8a8e0fb2019-03-25 16:32:50 +0100246 if (!mcs_is_edge(m_current_cs_ul)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200247 m_current_cs_ul = mcs_get_egprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100248 m_bts->bts_data()->initial_mcs_ul);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200249 if (!mcs_is_valid(m_current_cs_ul))
Maxbea2edb2019-03-06 17:04:59 +0100250 m_current_cs_ul = MCS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100251 }
Max8a8e0fb2019-03-25 16:32:50 +0100252 if (!mcs_is_edge(m_current_cs_dl)) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200253 m_current_cs_dl = mcs_get_egprs_by_num(
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100254 m_bts->bts_data()->initial_mcs_dl);
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200255 if (!mcs_is_valid(m_current_cs_dl))
Maxbea2edb2019-03-06 17:04:59 +0100256 m_current_cs_dl = MCS1;
Holger Hans Peter Freyther99db40a2016-03-04 18:19:02 +0100257 }
258 break;
259 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100260}
261
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200262void GprsMs::attach_tbf(struct gprs_rlcmac_tbf *tbf)
263{
264 if (tbf->direction == GPRS_RLCMAC_DL_TBF)
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100265 attach_dl_tbf(as_dl_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200266 else
Jacob Erlbeckaa9daa12015-12-28 18:49:12 +0100267 attach_ul_tbf(as_ul_tbf(tbf));
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200268}
269
270void GprsMs::attach_ul_tbf(struct gprs_rlcmac_ul_tbf *tbf)
271{
272 if (m_ul_tbf == tbf)
273 return;
274
275 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
276 tlli(), tbf->name());
277
278 Guard guard(this);
279
280 if (m_ul_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200281 llist_add_tail(&m_ul_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200282
283 m_ul_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200284
285 if (tbf)
286 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200287}
288
289void GprsMs::attach_dl_tbf(struct gprs_rlcmac_dl_tbf *tbf)
290{
291 if (m_dl_tbf == tbf)
292 return;
293
294 LOGP(DRLCMAC, LOGL_INFO, "Attaching TBF to MS object, TLLI = 0x%08x, TBF = %s\n",
295 tlli(), tbf->name());
296
297 Guard guard(this);
298
299 if (m_dl_tbf)
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200300 llist_add_tail(&m_dl_tbf->ms_list(), &m_old_tbfs);
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200301
302 m_dl_tbf = tbf;
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200303
304 if (tbf)
305 stop_timer();
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200306}
307
308void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
309{
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200310 if (tbf == static_cast<gprs_rlcmac_tbf *>(m_ul_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200311 m_ul_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200312 } else if (tbf == static_cast<gprs_rlcmac_tbf *>(m_dl_tbf)) {
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200313 m_dl_tbf = NULL;
Jacob Erlbeck6835cea2015-08-21 15:24:02 +0200314 } else {
315 bool found = false;
316
317 LListHead<gprs_rlcmac_tbf> *pos, *tmp;
318 llist_for_each_safe(pos, tmp, &m_old_tbfs) {
319 if (pos->entry() == tbf) {
320 llist_del(pos);
321 found = true;
322 break;
323 }
324 }
325
326 /* Protect against recursive calls via set_ms() */
327 if (!found)
328 return;
329 }
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200330
331 LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
332 tlli(), tbf->name());
333
Jacob Erlbeckfecece02015-05-08 12:13:08 +0200334 if (tbf->ms() == this)
335 tbf->set_ms(NULL);
336
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200337 if (!m_dl_tbf && !m_ul_tbf) {
338 set_reserved_slots(NULL, 0, 0);
339
340 if (tlli() != 0)
341 start_timer();
342 }
Jacob Erlbeckd9e10242015-05-28 15:43:53 +0200343
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200344 update_status();
345}
346
347void GprsMs::update_status()
348{
349 if (m_ref > 0)
350 return;
351
352 if (is_idle() && !m_is_idle) {
353 m_is_idle = true;
354 m_cb->ms_idle(this);
355 /* this can be deleted by now, do not access it */
356 return;
357 }
358
359 if (!is_idle() && m_is_idle) {
360 m_is_idle = false;
361 m_cb->ms_active(this);
362 }
363}
364
Jacob Erlbeckac289052015-08-14 12:50:54 +0200365void GprsMs::reset()
366{
367 LOGP(DRLCMAC, LOGL_INFO,
368 "Clearing MS object, TLLI: 0x%08x, IMSI: '%s'\n",
369 tlli(), imsi());
370
371 stop_timer();
372
Vadim Yanitskiycb988942020-11-08 13:27:35 +0700373 m_tlli = GSM_RESERVED_TMSI;
374 m_new_dl_tlli = m_tlli;
375 m_new_ul_tlli = m_tlli;
Jacob Erlbeckac289052015-08-14 12:50:54 +0200376 m_imsi[0] = '\0';
377}
378
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200379void GprsMs::merge_old_ms(GprsMs *old_ms)
380{
Pau Espin Pedrol528820d2020-10-26 12:40:11 +0100381 OSMO_ASSERT(old_ms != this);
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200382
383 if (strlen(imsi()) == 0 && strlen(old_ms->imsi()) != 0)
Pau Espin Pedrol528820d2020-10-26 12:40:11 +0100384 osmo_strlcpy(m_imsi, old_ms->imsi(), sizeof(m_imsi));
Jacob Erlbeck2b349b52015-08-18 11:55:03 +0200385
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
Pau Espin Pedrol528820d2020-10-26 12:40:11 +0100397void GprsMs::merge_and_clear_ms(GprsMs *old_ms)
398{
399 OSMO_ASSERT(old_ms != this);
400
401 GprsMs::Guard guard_old(old_ms);
402
403 /* Clean up the old MS object */
404 /* TODO: Use timer? */
405 if (old_ms->ul_tbf() && !old_ms->ul_tbf()->timers_pending(T_MAX))
406 tbf_free(old_ms->ul_tbf());
407 if (old_ms->dl_tbf() && !old_ms->dl_tbf()->timers_pending(T_MAX))
408 tbf_free(old_ms->dl_tbf());
409
410 merge_old_ms(old_ms);
411}
412
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200413void GprsMs::set_tlli(uint32_t tlli)
414{
Jacob Erlbeck93990462015-05-15 15:50:43 +0200415 if (tlli == m_tlli || tlli == m_new_ul_tlli)
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200416 return;
417
Jacob Erlbeck93990462015-05-15 15:50:43 +0200418 if (tlli != m_new_dl_tlli) {
419 LOGP(DRLCMAC, LOGL_INFO,
420 "Modifying MS object, UL TLLI: 0x%08x -> 0x%08x, "
421 "not yet confirmed\n",
422 this->tlli(), tlli);
423 m_new_ul_tlli = tlli;
424 return;
425 }
426
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200427 LOGP(DRLCMAC, LOGL_INFO,
Jacob Erlbeck93990462015-05-15 15:50:43 +0200428 "Modifying MS object, TLLI: 0x%08x -> 0x%08x, "
429 "already confirmed partly\n",
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200430 m_tlli, tlli);
431
432 m_tlli = tlli;
Vadim Yanitskiycb988942020-11-08 13:27:35 +0700433 m_new_dl_tlli = GSM_RESERVED_TMSI;
434 m_new_ul_tlli = GSM_RESERVED_TMSI;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200435}
436
437bool GprsMs::confirm_tlli(uint32_t tlli)
438{
439 if (tlli == m_tlli || tlli == m_new_dl_tlli)
440 return false;
441
442 if (tlli != m_new_ul_tlli) {
443 /* The MS has not sent a message with the new TLLI, which may
444 * happen according to the spec [TODO: add reference]. */
445
446 LOGP(DRLCMAC, LOGL_INFO,
447 "The MS object cannot fully confirm an unexpected TLLI: 0x%08x, "
448 "partly confirmed\n", tlli);
449 /* Use the network's idea of TLLI as candidate, this does not
450 * change the result value of tlli() */
451 m_new_dl_tlli = tlli;
452 return false;
453 }
454
455 LOGP(DRLCMAC, LOGL_INFO,
456 "Modifying MS object, TLLI: 0x%08x confirmed\n", tlli);
457
458 m_tlli = tlli;
Vadim Yanitskiycb988942020-11-08 13:27:35 +0700459 m_new_dl_tlli = GSM_RESERVED_TMSI;
460 m_new_ul_tlli = GSM_RESERVED_TMSI;
Jacob Erlbeck93990462015-05-15 15:50:43 +0200461
462 return true;
Jacob Erlbecke04e0b02015-05-06 18:30:48 +0200463}
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200464
465void GprsMs::set_imsi(const char *imsi)
466{
467 if (!imsi) {
468 LOGP(DRLCMAC, LOGL_ERROR, "Expected IMSI!\n");
469 return;
470 }
471
472 if (imsi[0] && strlen(imsi) < 3) {
473 LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI '%s'!\n",
474 imsi);
475 return;
476 }
477
478 if (strcmp(imsi, m_imsi) == 0)
479 return;
480
481 LOGP(DRLCMAC, LOGL_INFO,
482 "Modifying MS object, TLLI = 0x%08x, IMSI '%s' -> '%s'\n",
483 tlli(), m_imsi, imsi);
484
Pau Espin Pedrol528820d2020-10-26 12:40:11 +0100485 GprsMs *old_ms = m_bts->ms_store().get_ms(0, 0, imsi);
486 /* Check if we are going to store a different MS object with already
487 existing IMSI. This is probably a bug in code calling this function,
488 since it should take care of this explicitly */
489 if (old_ms) {
490 /* We cannot find m_ms by IMSI since we know that it has a
491 * different IMSI */
492 OSMO_ASSERT(old_ms != this);
493
494 LOGPMS(this, DRLCMAC, LOGL_NOTICE,
495 "IMSI '%s' was already assigned to another "
496 "MS object: TLLI = 0x%08x, that IMSI will be removed\n",
497 imsi, old_ms->tlli());
498
499 merge_and_clear_ms(old_ms);
500 }
501
502
Pau Espin Pedrol43f0bce2020-06-26 13:09:44 +0200503 osmo_strlcpy(m_imsi, imsi, sizeof(m_imsi));
Jacob Erlbeckb0e5eaf2015-05-21 11:07:16 +0200504}
505
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200506void GprsMs::set_ta(uint8_t ta_)
507{
508 if (ta_ == m_ta)
509 return;
510
Max9bbe1602016-07-18 12:50:18 +0200511 if (gsm48_ta_is_valid(ta_)) {
512 LOGP(DRLCMAC, LOGL_INFO,
513 "Modifying MS object, TLLI = 0x%08x, TA %d -> %d\n",
514 tlli(), m_ta, ta_);
515 m_ta = ta_;
516 } else
517 LOGP(DRLCMAC, LOGL_NOTICE,
518 "MS object, TLLI = 0x%08x, invalid TA %d rejected (old "
519 "value %d kept)\n", tlli(), ta_, m_ta);
Jacob Erlbeck9200ce62015-05-22 17:48:04 +0200520}
521
Jacob Erlbeckbefc7602015-06-02 12:33:30 +0200522void GprsMs::set_ms_class(uint8_t ms_class_)
523{
524 if (ms_class_ == m_ms_class)
525 return;
526
527 LOGP(DRLCMAC, LOGL_INFO,
528 "Modifying MS object, TLLI = 0x%08x, MS class %d -> %d\n",
529 tlli(), m_ms_class, ms_class_);
530
531 m_ms_class = ms_class_;
532}
533
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200534void GprsMs::set_egprs_ms_class(uint8_t ms_class_)
535{
536 if (ms_class_ == m_egprs_ms_class)
537 return;
538
539 LOGP(DRLCMAC, LOGL_INFO,
540 "Modifying MS object, TLLI = 0x%08x, EGPRS MS class %d -> %d\n",
541 tlli(), m_egprs_ms_class, ms_class_);
542
543 m_egprs_ms_class = ms_class_;
Pau Espin Pedrol8072e352020-10-30 18:18:06 +0100544
Pau Espin Pedrol9897b262020-11-04 18:01:56 +0100545 if (!m_bts->max_mcs_ul() || !m_bts->max_mcs_dl()) {
546 LOGPMS(this, DRLCMAC, LOGL_DEBUG,
547 "Avoid enabling EGPRS because use of MCS is disabled: ul=%u dl=%u\n",
548 m_bts->max_mcs_ul(), m_bts->max_mcs_dl());
549 return;
550 }
551
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100552 if (mcs_is_edge_gmsk(mcs_get_egprs_by_num(m_bts->max_mcs_ul())) &&
553 mcs_is_edge_gmsk(mcs_get_egprs_by_num(m_bts->max_mcs_dl())) &&
Pau Espin Pedrol8072e352020-10-30 18:18:06 +0100554 mode() != EGPRS)
555 {
556 set_mode(EGPRS_GMSK);
557 } else {
558 set_mode(EGPRS);
559 }
560 LOGPMS(this, DRLCMAC, LOGL_INFO, "Enabled EGPRS, mode %s\n", mode_name(mode()));
Jacob Erlbeckc3c58042015-09-28 17:55:32 +0200561}
562
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200563void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
564{
565 struct gprs_rlcmac_bts *bts_data;
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200566 int64_t now;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200567 enum CodingScheme max_cs_dl = this->max_cs_dl();
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200568
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100569 OSMO_ASSERT(max_cs_dl);
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200570 bts_data = m_bts->bts_data();
571
572 if (error_rate < 0)
573 return;
574
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200575 now = now_msec();
576
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200577 /* TODO: Check for TBF direction */
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200578 /* TODO: Support different CS values for UL and DL */
579
Jacob Erlbeck04a10862015-06-12 16:01:56 +0200580 m_nack_rate_dl = error_rate;
581
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200582 if (error_rate > bts_data->cs_adj_upper_limit) {
Max898dddb2019-03-12 15:50:57 +0100583 if (mcs_chan_code(m_current_cs_dl) > 0) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200584 mcs_dec_kind(&m_current_cs_dl, mode());
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200585 LOGP(DRLCMACDL, LOGL_INFO,
586 "MS (IMSI %s): High error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100587 "reducing CS level to %s\n",
Max136ebcc2019-03-05 14:59:03 +0100588 imsi(), error_rate, mcs_name(m_current_cs_dl));
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200589 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200590 }
591 } else if (error_rate < bts_data->cs_adj_lower_limit) {
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200592 if (m_current_cs_dl < max_cs_dl) {
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200593 if (now - m_last_cs_not_low > 1000) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200594 mcs_inc_kind(&m_current_cs_dl, mode());
Jacob Erlbeckb33e6752015-06-04 19:04:30 +0200595
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200596 LOGP(DRLCMACDL, LOGL_INFO,
597 "MS (IMSI %s): Low error rate %d%%, "
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100598 "increasing DL CS level to %s\n",
599 imsi(), error_rate,
Max136ebcc2019-03-05 14:59:03 +0100600 mcs_name(m_current_cs_dl));
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200601 m_last_cs_not_low = now;
602 } else {
603 LOGP(DRLCMACDL, LOGL_DEBUG,
604 "MS (IMSI %s): Low error rate %d%%, "
605 "ignored (within blocking period)\n",
606 imsi(), error_rate);
607 }
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200608 }
609 } else {
610 LOGP(DRLCMACDL, LOGL_DEBUG,
611 "MS (IMSI %s): Medium error rate %d%%, ignored\n",
612 imsi(), error_rate);
Jacob Erlbeck8158ea72015-06-04 17:46:33 +0200613 m_last_cs_not_low = now;
Jacob Erlbeck1751c622015-06-04 12:12:32 +0200614 }
615}
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200616
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200617enum CodingScheme GprsMs::max_cs_ul() const
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200618{
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200619 OSMO_ASSERT(m_bts != NULL);
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200620
Max8a8e0fb2019-03-25 16:32:50 +0100621 if (mcs_is_gprs(m_current_cs_ul)) {
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100622 if (!m_bts->max_cs_ul()) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200623 return CS4;
624 }
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200625
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100626 return mcs_get_gprs_by_num(m_bts->max_cs_ul());
Jacob Erlbeck94cde132015-06-09 09:44:36 +0200627 }
628
Max8a8e0fb2019-03-25 16:32:50 +0100629 if (!mcs_is_edge(m_current_cs_ul))
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200630 return UNKNOWN;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100631
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100632 if (m_bts->max_mcs_ul())
633 return mcs_get_egprs_by_num(m_bts->max_mcs_ul());
634 else if (m_bts->max_cs_ul())
635 return mcs_get_gprs_by_num(m_bts->max_cs_ul());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100636
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200637 return MCS4;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100638}
639
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200640void GprsMs::set_current_cs_dl(enum CodingScheme scheme)
Aravind Sirsikare8ccafc2016-07-13 11:37:47 +0530641{
642 m_current_cs_dl = scheme;
643}
644
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200645enum CodingScheme GprsMs::max_cs_dl() const
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100646{
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100647 OSMO_ASSERT(m_bts != NULL);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100648
Max8a8e0fb2019-03-25 16:32:50 +0100649 if (mcs_is_gprs(m_current_cs_dl)) {
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100650 if (!m_bts->max_cs_dl()) {
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200651 return CS4;
652 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100653
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100654 return mcs_get_gprs_by_num(m_bts->max_cs_dl());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100655 }
656
Max8a8e0fb2019-03-25 16:32:50 +0100657 if (!mcs_is_edge(m_current_cs_dl))
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200658 return UNKNOWN;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100659
Pau Espin Pedrol343ec9b2020-10-30 18:35:54 +0100660 if (m_bts->max_mcs_dl())
661 return mcs_get_egprs_by_num(m_bts->max_mcs_dl());
662 else if (m_bts->max_cs_dl())
663 return mcs_get_gprs_by_num(m_bts->max_cs_dl());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100664
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200665 return MCS4;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100666}
667
668void GprsMs::update_cs_ul(const pcu_l1_meas *meas)
669{
670 struct gprs_rlcmac_bts *bts_data;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200671 enum CodingScheme max_cs_ul = this->max_cs_ul();
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100672
673 int old_link_qual;
674 int low;
675 int high;
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200676 enum CodingScheme new_cs_ul = m_current_cs_ul;
Max898dddb2019-03-12 15:50:57 +0100677 uint8_t current_cs = mcs_chan_code(m_current_cs_ul);
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100678
679 bts_data = m_bts->bts_data();
680
681 if (!max_cs_ul) {
Minh-Quang Nguyen1f189092017-08-16 09:50:06 -0400682 LOGP(DRLCMACMEAS, LOGL_ERROR,
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100683 "max_cs_ul cannot be derived (current UL CS: %s)\n",
Max136ebcc2019-03-05 14:59:03 +0100684 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100685 return;
686 }
687
Maxfa3085b2019-03-07 11:46:43 +0100688 if (!m_current_cs_ul) {
689 LOGP(DRLCMACMEAS, LOGL_ERROR,
690 "Unable to update UL (M)CS because it's not set: %s\n",
Max136ebcc2019-03-05 14:59:03 +0100691 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100692 return;
Maxfa3085b2019-03-07 11:46:43 +0100693 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100694
Maxfa3085b2019-03-07 11:46:43 +0100695 if (!meas->have_link_qual) {
696 LOGP(DRLCMACMEAS, LOGL_ERROR,
697 "Unable to update UL (M)CS %s because we don't have link quality measurements.\n",
Max136ebcc2019-03-05 14:59:03 +0100698 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100699 return;
Maxfa3085b2019-03-07 11:46:43 +0100700 }
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100701
Max8a8e0fb2019-03-25 16:32:50 +0100702 if (mcs_is_gprs(m_current_cs_ul)) {
Max898dddb2019-03-12 15:50:57 +0100703 if (current_cs >= MAX_GPRS_CS)
704 current_cs = MAX_GPRS_CS - 1;
705 low = bts_data->cs_lqual_ranges[current_cs].low;
706 high = bts_data->cs_lqual_ranges[current_cs].high;
Max8a8e0fb2019-03-25 16:32:50 +0100707 } else if (mcs_is_edge(m_current_cs_ul)) {
Max898dddb2019-03-12 15:50:57 +0100708 if (current_cs >= MAX_EDGE_MCS)
709 current_cs = MAX_EDGE_MCS - 1;
710 low = bts_data->mcs_lqual_ranges[current_cs].low;
711 high = bts_data->mcs_lqual_ranges[current_cs].high;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100712 } else {
Maxfa3085b2019-03-07 11:46:43 +0100713 LOGP(DRLCMACMEAS, LOGL_ERROR,
714 "Unable to update UL (M)CS because it's neither GPRS nor EDGE: %s\n",
Max136ebcc2019-03-05 14:59:03 +0100715 mcs_name(m_current_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100716 return;
717 }
718
Vadim Yanitskiyfd734de2019-11-08 05:37:18 +0700719 /* To avoid rapid changes of the coding scheme, we also take
720 * the old link quality value into account (if present). */
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100721 if (m_l1_meas.have_link_qual)
722 old_link_qual = m_l1_meas.link_qual;
Vadim Yanitskiyfd734de2019-11-08 05:37:18 +0700723 else
724 old_link_qual = meas->link_qual;
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100725
726 if (meas->link_qual < low && old_link_qual < low)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200727 mcs_dec_kind(&new_cs_ul, mode());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100728 else if (meas->link_qual > high && old_link_qual > high &&
729 m_current_cs_ul < max_cs_ul)
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200730 mcs_inc_kind(&new_cs_ul, mode());
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100731
732 if (m_current_cs_ul != new_cs_ul) {
Pau Espin Pedrolf2dad592020-08-18 20:26:25 +0200733 LOGPMS(this, DRLCMACMEAS, LOGL_INFO,
734 "Link quality %ddB (old %ddB) left window [%d, %d], "
735 "modifying uplink CS level: %s -> %s\n",
736 meas->link_qual, old_link_qual,
737 low, high,
738 mcs_name(m_current_cs_ul), mcs_name(new_cs_ul));
Jacob Erlbeckcb728902016-01-05 15:33:03 +0100739
740 m_current_cs_ul = new_cs_ul;
741 }
742}
743
744void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
745{
746 unsigned i;
747
748 update_cs_ul(meas);
749
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200750 if (meas->have_rssi)
751 m_l1_meas.set_rssi(meas->rssi);
752 if (meas->have_bto)
753 m_l1_meas.set_bto(meas->bto);
754 if (meas->have_ber)
755 m_l1_meas.set_ber(meas->ber);
756 if (meas->have_link_qual)
757 m_l1_meas.set_link_qual(meas->link_qual);
Jacob Erlbeck51b11512015-06-11 16:54:50 +0200758
759 if (meas->have_ms_rx_qual)
760 m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
761 if (meas->have_ms_c_value)
762 m_l1_meas.set_ms_c_value(meas->ms_c_value);
763 if (meas->have_ms_sign_var)
764 m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
765
766 if (meas->have_ms_i_level) {
767 for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
768 if (meas->ts[i].have_ms_i_level)
769 m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
770 else
771 m_l1_meas.ts[i].have_ms_i_level = 0;
772 }
773 }
Jacob Erlbecke4bcb622015-06-08 11:26:38 +0200774}
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200775
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200776enum CodingScheme GprsMs::current_cs_dl() const
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200777{
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200778 enum CodingScheme cs = m_current_cs_dl;
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200779 size_t unencoded_octets;
780
781 if (!m_bts)
782 return cs;
783
784 unencoded_octets = m_llc_queue.octets();
785
786 /* If the DL TBF is active, add number of unencoded chunk octets */
787 if (m_dl_tbf)
Jacob Erlbeck14e26cb2016-02-03 15:26:29 +0100788 unencoded_octets += m_dl_tbf->m_llc.chunk_size();
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200789
790 /* There are many unencoded octets, don't reduce */
791 if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
792 return cs;
793
794 /* RF conditions are good, don't reduce */
795 if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
796 return cs;
797
798 /* The throughput would probably be better if the CS level was reduced */
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200799 mcs_dec_kind(&cs, mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200800
801 /* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
Pau Espin Pedrol2ae83372020-05-18 11:35:35 +0200802 if (cs == CS2)
803 mcs_dec_kind(&cs, mode());
Jacob Erlbeck70b96aa2015-06-12 10:52:34 +0200804
805 return cs;
806}
807
Jacob Erlbeck699b8dc2015-06-29 14:05:55 +0200808int GprsMs::first_common_ts() const
809{
810 if (m_dl_tbf)
811 return m_dl_tbf->first_common_ts;
812
813 if (m_ul_tbf)
814 return m_ul_tbf->first_common_ts;
815
816 return -1;
817}
818
Jacob Erlbeck617c7122015-06-30 09:18:30 +0200819uint8_t GprsMs::dl_slots() const
820{
821 uint8_t slots = 0;
822
823 if (m_dl_tbf)
824 slots |= m_dl_tbf->dl_slots();
825
826 if (m_ul_tbf)
827 slots |= m_ul_tbf->dl_slots();
828
829 return slots;
830}
831
832uint8_t GprsMs::ul_slots() const
833{
834 uint8_t slots = 0;
835
836 if (m_dl_tbf)
837 slots |= m_dl_tbf->ul_slots();
838
839 if (m_ul_tbf)
840 slots |= m_ul_tbf->ul_slots();
841
842 return slots;
843}
844
Jacob Erlbeck7c72aca2016-01-22 17:06:14 +0100845uint8_t GprsMs::current_pacch_slots() const
846{
847 uint8_t slots = 0;
848
849 bool is_dl_active = m_dl_tbf && m_dl_tbf->is_tfi_assigned();
850 bool is_ul_active = m_ul_tbf && m_ul_tbf->is_tfi_assigned();
851
852 if (!is_dl_active && !is_ul_active)
853 return 0;
854
855 /* see TS 44.060, 8.1.1.2.2 */
856 if (is_dl_active && !is_ul_active)
857 slots = m_dl_tbf->dl_slots();
858 else if (!is_dl_active && is_ul_active)
859 slots = m_ul_tbf->ul_slots();
860 else
861 slots = m_ul_tbf->ul_slots() & m_dl_tbf->dl_slots();
862
863 /* Assume a multislot class 1 device */
864 /* TODO: For class 2 devices, this could be removed */
865 slots = pcu_lsb(slots);
866
867 return slots;
868}
869
Jacob Erlbeck23f93a12015-06-30 08:52:54 +0200870void GprsMs::set_reserved_slots(gprs_rlcmac_trx *trx,
871 uint8_t ul_slots, uint8_t dl_slots)
872{
873 if (m_current_trx) {
874 m_current_trx->unreserve_slots(GPRS_RLCMAC_DL_TBF,
875 m_reserved_dl_slots);
876 m_current_trx->unreserve_slots(GPRS_RLCMAC_UL_TBF,
877 m_reserved_ul_slots);
878 m_reserved_dl_slots = 0;
879 m_reserved_ul_slots = 0;
880 }
881 m_current_trx = trx;
882 if (trx) {
883 m_reserved_dl_slots = dl_slots;
884 m_reserved_ul_slots = ul_slots;
885 m_current_trx->reserve_slots(GPRS_RLCMAC_DL_TBF,
886 m_reserved_dl_slots);
887 m_current_trx->reserve_slots(GPRS_RLCMAC_UL_TBF,
888 m_reserved_ul_slots);
889 }
890}
891
Jacob Erlbeckac89a552015-06-29 14:18:46 +0200892gprs_rlcmac_tbf *GprsMs::tbf(enum gprs_rlcmac_tbf_direction dir) const
893{
894 switch (dir) {
895 case GPRS_RLCMAC_DL_TBF: return m_dl_tbf;
896 case GPRS_RLCMAC_UL_TBF: return m_ul_tbf;
897 }
898
899 return NULL;
900}