2013-10-19 16:50:22 +00:00
|
|
|
/* encoding.cpp
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Ivan Klyuchnikov
|
|
|
|
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
|
|
|
* Copyright (C) 2013 by Holger Hans Peter Freyther
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2013-10-19 17:04:03 +00:00
|
|
|
#include <encoding.h>
|
2013-10-19 16:50:22 +00:00
|
|
|
#include <gprs_rlcmac.h>
|
2013-10-19 19:10:38 +00:00
|
|
|
#include <bts.h>
|
2013-10-19 16:50:22 +00:00
|
|
|
#include <tbf.h>
|
2019-09-25 15:47:02 +00:00
|
|
|
#include <tbf_ul.h>
|
2021-01-14 15:48:38 +00:00
|
|
|
#include <tbf_dl.h>
|
2013-10-19 16:50:22 +00:00
|
|
|
#include <gprs_debug.h>
|
2017-01-16 10:11:21 +00:00
|
|
|
#include <egprs_rlc_compression.h>
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2016-07-12 00:05:19 +00:00
|
|
|
extern "C" {
|
|
|
|
#include <osmocom/gprs/protocol/gsm_04_60.h>
|
2016-07-14 13:41:17 +00:00
|
|
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
paging: pass struct osmo_mobile_identity, not encoded IE bytes
In get_paging_mi(), before this, an encoded buffer of Mobile Identity bytes is
returned. Code paths following this repeatedly decode the Mobile Identity
bytes, e.g. for logging. Also, in get_paging_mi(), since the TMSI is read in
from a different encoding than a typical Mobile Identity IE, the TMSI was
manually encoded into a typical Mobile Identity IE. This is essentially a code
dup of osmo_mobile_identity_encode(). Stop this madness.
Instead, in get_paging_mi(), return a decoded struct osmo_mobile_identity. Code
paths after this use the struct osmo_mobile_identity directly without repeated
decoding.
At the point of finally needing an encoded Mobile Identity IE (in
Encoding::write_paging_request()), do a proper osmo_mobile_identity_encode().
Since this may return errors, add an rc check for the caller of
write_paging_request(), gprs_rlcmac_paging_request().
A side effect is stricter validation of the Mobile Identity passing through the
Paging code path. Before, invalid MI might have passed through unnoticed.
Change-Id: Iad845acb0096b75dc453105c9c16b2252879b4ca
2020-08-21 14:21:23 +00:00
|
|
|
#include <osmocom/gsm/gsm48.h>
|
2019-02-19 17:08:27 +00:00
|
|
|
}
|
2016-07-14 13:41:17 +00:00
|
|
|
|
|
|
|
#include <stdbool.h>
|
2016-01-08 09:07:53 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2019-02-19 16:49:27 +00:00
|
|
|
#define CHECK(rc) { if (rc < 0) return rc; }
|
|
|
|
#define SET_X(bv, x) { if (bitvec_set_bit(bv, x) < 0) return -EOWNERDEAD; }
|
|
|
|
#define SET_0(bv) SET_X(bv, ZERO)
|
|
|
|
#define SET_1(bv) SET_X(bv, ONE)
|
|
|
|
#define SET_L(bv) SET_X(bv, L)
|
|
|
|
#define SET_H(bv) SET_X(bv, H)
|
|
|
|
|
|
|
|
/* 3GPP TS 44.018 § 10.5.2.16:
|
|
|
|
{ 0 | 1 < ALPHA : bit (4) > }
|
|
|
|
< GAMMA : bit (5) >
|
|
|
|
*/
|
|
|
|
static int write_alpha_gamma(bitvec *dest, uint8_t alpha, uint8_t gamma)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (alpha) {
|
|
|
|
SET_1(dest);
|
|
|
|
rc = bitvec_set_u64(dest, alpha, 4, false);
|
|
|
|
CHECK(rc);
|
|
|
|
} else
|
|
|
|
SET_0(dest);
|
|
|
|
|
|
|
|
rc = bitvec_set_u64(dest, gamma, 5, false);
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TBF_STARTING_TIME -- same as 3GPP TS 44.018 §10.5.2.38 Starting Time without tag: */
|
|
|
|
static int write_tbf_start_time(bitvec *dest, uint32_t fn)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
/* Set values according to 3GPP TS 44.018 Table 10.5.2.38.1 */
|
|
|
|
|
|
|
|
/* T1' */
|
|
|
|
rc = bitvec_set_u64(dest, (fn / (26 * 51)) % 32, 5, false);
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
/* T3 */
|
|
|
|
rc = bitvec_set_u64(dest, fn % 51, 6, false);
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
/* T2 */
|
|
|
|
rc = bitvec_set_u64(dest, fn % 26, 5, false);
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-02-19 17:25:13 +00:00
|
|
|
/* 3GPP TS 44.018 §10.5.2.16:
|
|
|
|
< TFI_ASSIGNMENT : bit (5) >
|
|
|
|
< POLLING : bit >
|
|
|
|
0 -- The value '1' was allocated in an earlier version of the protocol and shall not be used.
|
|
|
|
< USF: bit (3) >
|
|
|
|
< USF_GRANULARITY : bit >
|
|
|
|
{ 0 | 1 < P0 : bit (4) > < PR_MODE : bit (1) > }
|
|
|
|
*/
|
|
|
|
static int write_tfi_usf(bitvec *dest, const gprs_rlcmac_ul_tbf *tbf, uint8_t usf)
|
|
|
|
{
|
2019-10-04 01:40:26 +00:00
|
|
|
int rc = bitvec_set_u64(dest, tbf->tfi(), 5, false); /* TFI_ASSIGNMENT */
|
|
|
|
CHECK(rc);
|
2019-02-19 17:25:13 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
SET_0(dest); /* POLLING -- no action is required from MS */
|
2019-02-19 17:25:13 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
SET_0(dest);
|
2019-02-19 17:25:13 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
rc = bitvec_set_u64(dest, usf, 3, false); /* USF */
|
|
|
|
CHECK(rc);
|
2019-02-19 17:25:13 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
SET_0(dest); /* USF_GRANULARITY -- the mobile station shall transmit one RLC/MAC block */
|
2019-02-19 17:25:13 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
SET_0(dest); /* No P0 nor PR_MODE */
|
2019-02-19 17:25:13 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
return 0;
|
2019-02-19 17:25:13 +00:00
|
|
|
}
|
|
|
|
|
2016-07-28 10:03:08 +00:00
|
|
|
/* { 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) > } */
|
2019-02-19 16:49:27 +00:00
|
|
|
static int write_ta_index(bitvec *dest, int8_t tai)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2019-03-13 14:55:54 +00:00
|
|
|
if (tai < 0) { /* No TIMING_ADVANCE_INDEX: */
|
2019-02-19 16:49:27 +00:00
|
|
|
SET_0(dest);
|
2019-03-13 14:55:54 +00:00
|
|
|
} else { /* TIMING_ADVANCE_INDEX: */
|
|
|
|
SET_1(dest);
|
|
|
|
rc = bitvec_set_u64(dest, tai, 4, false);
|
|
|
|
CHECK(rc);
|
|
|
|
}
|
2019-02-19 16:49:27 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-28 10:03:08 +00:00
|
|
|
static inline bool write_tai(bitvec *dest, unsigned& wp, int8_t tai)
|
|
|
|
{
|
|
|
|
if (tai < 0) { /* No TIMING_ADVANCE_INDEX: */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1);
|
2016-07-28 10:03:08 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* TIMING_ADVANCE_INDEX: */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 1, 1);
|
|
|
|
bitvec_write_field(dest, &wp, tai, 4);
|
2016-07-28 10:03:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* { 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > } */
|
2020-07-07 15:20:59 +00:00
|
|
|
static inline void write_ta(bitvec *dest, unsigned& wp, uint8_t ta)
|
2016-07-28 10:03:08 +00:00
|
|
|
{
|
2020-08-20 10:26:31 +00:00
|
|
|
if (ta > 63) /* No TIMING_ADVANCE_VALUE: */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1);
|
2016-07-28 10:03:08 +00:00
|
|
|
else { /* TIMING_ADVANCE_VALUE: */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 1, 1);
|
|
|
|
bitvec_write_field(dest, &wp, ta, 6);
|
2016-07-28 10:03:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-15 10:21:57 +00:00
|
|
|
/* 3GPP TS 44.060 Table 12.5.2.1 */
|
|
|
|
static inline uint16_t enc_ws(uint16_t ws)
|
|
|
|
{
|
|
|
|
return (ws - 64) / 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void write_ws(bitvec *dest, unsigned int *write_index, uint16_t ws)
|
|
|
|
{
|
|
|
|
dest->cur_bit = *write_index;
|
|
|
|
|
2020-02-08 20:59:52 +00:00
|
|
|
int rc = bitvec_set_u64(dest, enc_ws(ws), 5, false);
|
|
|
|
OSMO_ASSERT(rc == 0);
|
2017-12-15 10:21:57 +00:00
|
|
|
|
|
|
|
*write_index += 5;
|
|
|
|
}
|
|
|
|
|
2016-07-28 10:03:08 +00:00
|
|
|
/* 3GPP TS 44.060 § 12.12:
|
|
|
|
{ 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > }
|
|
|
|
{ 0 | 1 < TIMING_ADVANCE_INDEX : bit (4) >
|
|
|
|
< TIMING_ADVANCE_TIMESLOT_NUMBER : bit (3) > }
|
|
|
|
*/
|
|
|
|
static inline void write_ta_ie(bitvec *dest, unsigned& wp,
|
2020-07-07 15:20:59 +00:00
|
|
|
uint8_t ta, int8_t tai, uint8_t ts)
|
2016-07-28 10:03:08 +00:00
|
|
|
{
|
|
|
|
write_ta(dest, wp, ta);
|
|
|
|
if (write_tai(dest, wp, tai)) /* TIMING_ADVANCE_TIMESLOT_NUMBER: */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, ts, 3);
|
2016-07-28 10:03:08 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 14:37:03 +00:00
|
|
|
static int write_ia_rest_downlink(const gprs_rlcmac_dl_tbf *tbf, bitvec * dest, bool polling, bool ta_valid,
|
2019-02-19 16:49:27 +00:00
|
|
|
uint32_t fn, uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
2016-02-01 14:23:35 +00:00
|
|
|
{
|
2019-02-19 14:37:03 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
2019-02-19 16:49:27 +00:00
|
|
|
SET_H(dest); SET_H(dest);
|
|
|
|
SET_0(dest); SET_1(dest); /* 00 Packet Downlink Assignment */
|
|
|
|
|
|
|
|
rc = bitvec_set_u64(dest, tbf->tlli(), 32, false); /* TLLI */
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
SET_1(dest);
|
|
|
|
rc = bitvec_set_u64(dest, tbf->tfi(), 5, false); /* TFI_ASSIGNMENT */
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
/* RLC acknowledged mode */
|
2020-05-23 12:23:56 +00:00
|
|
|
rc = bitvec_set_bit(dest, (bit_value) RLC_MODE_ACKNOWLEDGED);
|
|
|
|
CHECK(rc);
|
2019-02-19 16:49:27 +00:00
|
|
|
|
|
|
|
rc = write_alpha_gamma(dest, alpha, gamma);
|
|
|
|
CHECK(rc);
|
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
rc = bitvec_set_bit(dest, (bit_value) polling); /* POLLING */
|
2019-02-19 16:49:27 +00:00
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
/* N. B: NOT related to TAI! */
|
2019-10-04 01:40:26 +00:00
|
|
|
rc = bitvec_set_bit(dest, (bit_value) ta_valid); /* TA_VALID */
|
2019-02-19 16:49:27 +00:00
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
rc = write_ta_index(dest, ta_idx);
|
|
|
|
CHECK(rc);
|
|
|
|
|
2016-02-01 14:23:35 +00:00
|
|
|
if (polling) {
|
2019-02-19 16:49:27 +00:00
|
|
|
SET_1(dest);
|
|
|
|
rc = write_tbf_start_time(dest, fn);
|
|
|
|
CHECK(rc);
|
2019-10-04 01:40:26 +00:00
|
|
|
} else
|
2019-02-19 16:49:27 +00:00
|
|
|
SET_0(dest);
|
|
|
|
|
|
|
|
SET_0(dest); /* No P0 nor PR_MODE */
|
|
|
|
|
2016-02-01 15:39:52 +00:00
|
|
|
if (tbf->is_egprs_enabled()) {
|
2019-02-19 16:49:27 +00:00
|
|
|
SET_H(dest);
|
|
|
|
rc = bitvec_set_u64(dest, enc_ws(tbf->window_size()), 5, false); /* EGPRS Window Size */
|
|
|
|
CHECK(rc);
|
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
/* The mobile station shall not report measurements: (see 3GPP TS 44.060 Table 11.2.7.1) */
|
2019-02-19 16:49:27 +00:00
|
|
|
SET_0(dest); SET_0(dest); /* LINK_QUALITY_MEASUREMENT_MODE */
|
|
|
|
SET_1(dest); /* No BEP_PERIOD2 */
|
|
|
|
} else
|
|
|
|
SET_L(dest); /* No Additions for Rel-6 */
|
2016-02-01 15:13:38 +00:00
|
|
|
|
2019-02-19 14:37:03 +00:00
|
|
|
return rc;
|
2016-02-01 14:23:35 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 17:41:22 +00:00
|
|
|
/* 3GPP TS 44.018 Table 10.5.2.16.1 < Packet Uplink Assignment > -- Single Block Allocation */
|
|
|
|
static int write_ia_rest_uplink_sba(bitvec *dest, uint32_t fn, uint8_t alpha, uint8_t gamma)
|
2016-02-01 14:23:35 +00:00
|
|
|
{
|
2019-02-19 14:37:03 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
2019-02-19 17:41:22 +00:00
|
|
|
SET_0(dest); /* Single Block Allocation */
|
|
|
|
rc = write_alpha_gamma(dest, alpha, gamma);
|
|
|
|
CHECK(rc);
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-02-19 17:41:22 +00:00
|
|
|
/* A 'Timing Advance index' shall not be allocated at a Single Block allocation.
|
|
|
|
A 'TBF Starting Time' shall be allocated at a Single Block allocation. */
|
|
|
|
SET_0(dest);
|
|
|
|
SET_1(dest);
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-02-19 17:41:22 +00:00
|
|
|
rc = write_tbf_start_time(dest, fn);
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
/* No P0 nor PR_MODE */
|
|
|
|
SET_L(dest);
|
|
|
|
|
|
|
|
/* No Additions for R99 */
|
|
|
|
SET_L(dest);
|
|
|
|
|
|
|
|
/* No Additions for Rel-6 */
|
|
|
|
SET_L(dest);
|
2019-02-19 14:37:03 +00:00
|
|
|
|
|
|
|
return rc;
|
2016-02-01 14:23:35 +00:00
|
|
|
}
|
|
|
|
|
2019-02-19 14:37:03 +00:00
|
|
|
static int write_ia_rest_uplink_mba(const gprs_rlcmac_ul_tbf *tbf, bitvec *dest, uint8_t usf,
|
2019-03-12 17:42:09 +00:00
|
|
|
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
2016-02-01 15:39:52 +00:00
|
|
|
{
|
2019-02-19 14:37:03 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
2019-03-12 17:31:54 +00:00
|
|
|
SET_1(dest); /* Multi Block Allocation */
|
|
|
|
|
|
|
|
rc = write_tfi_usf(dest, tbf, usf);
|
|
|
|
CHECK(rc);
|
2016-09-02 11:20:43 +00:00
|
|
|
|
2019-03-12 17:31:54 +00:00
|
|
|
/* 3GPP TS 44.060 Table 11.2.28.2 Channel Coding Indicator */
|
|
|
|
rc = bitvec_set_u64(dest, mcs_chan_code(tbf->current_cs()), 2, false); /* CHANNEL_CODING_COMMAND */
|
|
|
|
CHECK(rc);
|
2016-09-02 11:20:43 +00:00
|
|
|
|
2019-03-12 17:31:54 +00:00
|
|
|
/* TLLI_BLOCK_CHANNEL_CODING */
|
|
|
|
SET_1(dest); /* use coding scheme as specified by the corresponding CHANNEL CODING COMMAND */
|
|
|
|
|
|
|
|
rc = write_alpha_gamma(dest, alpha, gamma);
|
|
|
|
CHECK(rc);
|
|
|
|
|
2019-03-12 17:42:09 +00:00
|
|
|
rc = write_ta_index(dest, ta_idx);
|
|
|
|
CHECK(rc);
|
2019-03-12 17:31:54 +00:00
|
|
|
|
|
|
|
/* No TBF_STARTING_TIME */
|
|
|
|
SET_0(dest);
|
2016-09-02 11:20:43 +00:00
|
|
|
|
2019-02-19 14:37:03 +00:00
|
|
|
return rc;
|
|
|
|
}
|
2018-01-09 12:15:05 +00:00
|
|
|
|
2019-02-19 17:10:12 +00:00
|
|
|
static int write_ia_rest_egprs_uplink_mba(bitvec * dest, uint32_t fn, uint8_t alpha, uint8_t gamma)
|
2019-02-19 14:37:03 +00:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2018-01-09 12:15:05 +00:00
|
|
|
|
2019-02-19 17:10:12 +00:00
|
|
|
SET_0(dest); /* Multi Block Allocation */
|
2018-01-09 12:15:05 +00:00
|
|
|
|
2019-02-19 17:10:12 +00:00
|
|
|
rc = write_alpha_gamma(dest, alpha, gamma);
|
|
|
|
CHECK(rc);
|
2018-01-09 12:15:05 +00:00
|
|
|
|
2019-02-19 17:10:12 +00:00
|
|
|
rc = write_tbf_start_time(dest, fn);
|
|
|
|
CHECK(rc);
|
2016-09-02 11:20:43 +00:00
|
|
|
|
2019-02-19 17:10:12 +00:00
|
|
|
SET_0(dest); /* NUMBER OF RADIO BLOCKS ALLOCATED: */
|
|
|
|
SET_0(dest); /* 1 radio block reserved for uplink transmission */
|
|
|
|
SET_0(dest); /* No P0 */
|
2019-02-19 14:37:03 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int write_ia_rest_egprs_uplink_sba(const gprs_rlcmac_ul_tbf *tbf, bitvec * dest, uint8_t usf,
|
2019-03-12 17:42:09 +00:00
|
|
|
uint8_t alpha, uint8_t gamma, int8_t ta_idx)
|
2019-02-19 14:37:03 +00:00
|
|
|
{
|
|
|
|
int rc = 0;
|
2018-01-09 12:15:05 +00:00
|
|
|
|
2019-02-19 17:25:13 +00:00
|
|
|
SET_1(dest); /* Single Block Allocation */
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-02-19 17:25:13 +00:00
|
|
|
rc = write_tfi_usf(dest, tbf, usf);
|
|
|
|
CHECK(rc);
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-02-19 17:25:13 +00:00
|
|
|
/* 3GPP TS 44.060 §12.10d EGPRS Modulation and coding Scheme description: */
|
2019-03-06 17:17:32 +00:00
|
|
|
rc = bitvec_set_u64(dest, mcs_chan_code(tbf->current_cs()), 4, false); /* EGPRS CHANNEL_CODING_COMMAND */
|
2019-02-19 17:25:13 +00:00
|
|
|
CHECK(rc);
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-02-19 17:25:13 +00:00
|
|
|
/* TLLI_BLOCK_CHANNEL_CODING */
|
|
|
|
rc = bitvec_set_bit(dest, (bit_value)tbf->tlli());
|
2020-05-23 19:01:28 +00:00
|
|
|
CHECK(rc);
|
2019-02-19 17:25:13 +00:00
|
|
|
|
|
|
|
/* No BEP_PERIOD2 */
|
|
|
|
SET_0(dest);
|
|
|
|
|
|
|
|
/* Retransmitted RLC data blocks shall not be re-segmented: (see 3GPP TS 44.060 §12.10e) */
|
|
|
|
SET_0(dest); /* RESEGMENT */
|
|
|
|
|
|
|
|
rc = bitvec_set_u64(dest, enc_ws(tbf->window_size()), 5, false); /* EGPRS Window Size */
|
|
|
|
CHECK(rc);
|
2016-09-02 11:20:43 +00:00
|
|
|
|
2019-02-19 17:25:13 +00:00
|
|
|
rc = write_alpha_gamma(dest, alpha, gamma);
|
|
|
|
CHECK(rc);
|
|
|
|
|
2019-03-12 17:42:09 +00:00
|
|
|
rc = write_ta_index(dest, ta_idx);
|
|
|
|
CHECK(rc);
|
2019-02-19 17:25:13 +00:00
|
|
|
|
|
|
|
/* No TBF_STARTING_TIME */
|
|
|
|
SET_0(dest);
|
|
|
|
|
|
|
|
/* No Additions for Rel-7 */
|
|
|
|
SET_0(dest);
|
2019-02-19 14:37:03 +00:00
|
|
|
|
|
|
|
return rc;
|
2016-02-01 15:39:52 +00:00
|
|
|
}
|
|
|
|
|
2016-11-09 10:57:00 +00:00
|
|
|
/*
|
|
|
|
* Immediate assignment reject, sent on the CCCH/AGCH
|
|
|
|
* see GSM 44.018, 9.1.20 + 10.5.2.30
|
|
|
|
*/
|
2021-04-26 11:36:16 +00:00
|
|
|
int Encoding::write_immediate_assignment_reject(bitvec *dest, uint16_t ra,
|
|
|
|
uint32_t ref_fn, enum ph_burst_type burst_type, uint8_t t3142)
|
2016-11-09 10:57:00 +00:00
|
|
|
{
|
|
|
|
unsigned wp = 0;
|
|
|
|
int plen;
|
|
|
|
int i;
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 4); // Skip Indicator
|
|
|
|
bitvec_write_field(dest, &wp, 0x6, 4); // Protocol Discriminator
|
|
|
|
bitvec_write_field(dest, &wp, 0x3A, 8); // Immediate Assign Message Type
|
2016-11-09 10:57:00 +00:00
|
|
|
|
|
|
|
// feature indicator
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1); // spare
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1); // spare
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1); // no cs
|
|
|
|
bitvec_write_field(dest, &wp, 0x1, 1); // implicit detach for PS
|
2016-11-09 10:57:00 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 4); // Page Mode
|
2016-11-09 10:57:00 +00:00
|
|
|
/*
|
|
|
|
* 9.1.20.2 of 44.018 version 11.7.0 Release 11
|
|
|
|
* Filling of the message
|
|
|
|
* If necessary the request reference information element and the
|
|
|
|
* wait indication information element should be duplicated to
|
|
|
|
* fill the message.
|
|
|
|
* TODO: group rejection for multiple MS
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
//10.5.2.30 Request Reference
|
|
|
|
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
|
|
|
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
|
|
|
//9.1.20.2a of 44.018 version 11.7.0 Release 11
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x7f, 8); /* RACH value */
|
2016-11-09 10:57:00 +00:00
|
|
|
} else {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, ra, 8); /* RACH value */
|
2016-11-09 10:57:00 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,
|
2016-11-09 10:57:00 +00:00
|
|
|
(ref_fn / (26 * 51)) % 32, 5); // T1'
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, ref_fn % 51, 6); // T3
|
|
|
|
bitvec_write_field(dest, &wp, ref_fn % 26, 5); // T2
|
2016-11-09 10:57:00 +00:00
|
|
|
|
2021-04-26 11:36:16 +00:00
|
|
|
/* 10.5.2.43 Wait Indication */
|
|
|
|
bitvec_write_field(dest, &wp, t3142, 8);
|
2016-11-09 10:57:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
plen = wp / 8;
|
|
|
|
|
|
|
|
if ((wp % 8)) {
|
|
|
|
LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS.Rej without"
|
|
|
|
"rest octets is not multiple of 8 bits, PLEASE FIX!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extended RA
|
|
|
|
else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
|
|
|
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
|
|
|
//9.1.20.2a of 44.018 version 11.7.0 Release 11
|
|
|
|
uint8_t extended_ra = 0;
|
|
|
|
|
|
|
|
extended_ra = (ra & 0x1F);
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x1, 1);
|
|
|
|
bitvec_write_field(dest, &wp, extended_ra, 5); /* Extended RA */
|
2016-11-09 10:57:00 +00:00
|
|
|
} else {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1);
|
2016-11-09 10:57:00 +00:00
|
|
|
}
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1);
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1);
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1);
|
2016-11-09 10:57:00 +00:00
|
|
|
|
|
|
|
return plen;
|
|
|
|
}
|
|
|
|
|
2016-02-01 14:23:35 +00:00
|
|
|
/*
|
|
|
|
* Immediate assignment, sent on the CCCH/AGCH
|
|
|
|
* see GSM 04.08, 9.1.18 and GSM 44.018, 9.1.18 + 10.5.2.16
|
|
|
|
*/
|
2013-10-19 17:04:03 +00:00
|
|
|
int Encoding::write_immediate_assignment(
|
2020-07-18 16:34:17 +00:00
|
|
|
const struct gprs_rlcmac_pdch *pdch,
|
2016-02-01 15:13:38 +00:00
|
|
|
struct gprs_rlcmac_tbf *tbf,
|
2019-02-18 17:52:38 +00:00
|
|
|
bitvec * dest, bool downlink, uint16_t ra,
|
2020-07-18 16:34:17 +00:00
|
|
|
uint32_t ref_fn, uint8_t ta,
|
2019-02-18 17:52:38 +00:00
|
|
|
uint8_t usf, bool polling, uint32_t fn, uint8_t alpha,
|
|
|
|
uint8_t gamma, int8_t ta_idx, enum ph_burst_type burst_type)
|
2013-10-19 16:50:22 +00:00
|
|
|
{
|
|
|
|
unsigned wp = 0;
|
2016-02-01 15:13:38 +00:00
|
|
|
int plen;
|
|
|
|
int rc;
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,4); // Skip Indicator
|
|
|
|
bitvec_write_field(dest, &wp,0x6,4); // Protocol Discriminator
|
|
|
|
bitvec_write_field(dest, &wp,0x3F,8); // Immediate Assignment Message Type
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
// 10.5.2.25b Dedicated mode or TBF
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // spare
|
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // TMA : Two-message assignment: No meaning
|
|
|
|
bitvec_write_field(dest, &wp,downlink,1); // Downlink : Downlink assignment to mobile in packet idle mode
|
|
|
|
bitvec_write_field(dest, &wp,0x1,1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF).
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,4); // Page Mode
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
// GSM 04.08 10.5.2.25a Packet Channel Description
|
2020-07-18 16:34:17 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x01, 5); // Channel type
|
|
|
|
bitvec_write_field(dest, &wp, pdch->ts_no, 3); // TN
|
|
|
|
bitvec_write_field(dest, &wp, pdch->tsc, 3); // TSC
|
|
|
|
|
2020-07-18 17:47:20 +00:00
|
|
|
/* RF channel configuraion: hopping or non-hopping */
|
|
|
|
if (pdch->fh.enabled) {
|
|
|
|
bitvec_write_field(dest, &wp, 0x01, 1); /* direct encoding */
|
|
|
|
bitvec_write_field(dest, &wp, pdch->fh.maio, 6);
|
|
|
|
bitvec_write_field(dest, &wp, pdch->fh.hsn, 6);
|
|
|
|
} else {
|
|
|
|
bitvec_write_field(dest, &wp, 0x00, 3);
|
|
|
|
bitvec_write_field(dest, &wp, pdch->trx->arfcn, 10);
|
|
|
|
}
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
//10.5.2.30 Request Reference
|
2016-09-02 11:20:43 +00:00
|
|
|
if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) ||
|
|
|
|
(burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x7f, 8); /* RACH value */
|
2016-09-02 11:20:43 +00:00
|
|
|
} else {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, ra, 8); /* RACH value */
|
2016-09-02 11:20:43 +00:00
|
|
|
}
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,(ref_fn / (26 * 51)) % 32,5); // T1'
|
|
|
|
bitvec_write_field(dest, &wp,ref_fn % 51,6); // T3
|
|
|
|
bitvec_write_field(dest, &wp,ref_fn % 26,5); // T2
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
// 10.5.2.40 Timing Advance
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,2); // spare
|
|
|
|
bitvec_write_field(dest, &wp,ta,6); // Timing Advance value
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2020-07-18 17:47:20 +00:00
|
|
|
/* 10.5.2.21 Mobile Allocation */
|
|
|
|
if (pdch->fh.enabled) {
|
|
|
|
bitvec_write_field(dest, &wp, pdch->fh.ma_oct_len, 8);
|
|
|
|
for (int i = 0; i < pdch->fh.ma_oct_len; i++)
|
|
|
|
bitvec_write_field(dest, &wp, pdch->fh.ma[i], 8);
|
|
|
|
} else {
|
|
|
|
// No mobile allocation in non-hopping systems.
|
|
|
|
// A zero-length LV. Just write L=0.
|
|
|
|
bitvec_write_field(dest, &wp, 0x00, 8);
|
|
|
|
}
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2020-07-18 13:47:47 +00:00
|
|
|
OSMO_ASSERT(wp % 8 == 0);
|
2017-03-08 17:53:30 +00:00
|
|
|
|
2013-10-19 16:50:22 +00:00
|
|
|
plen = wp / 8;
|
|
|
|
|
2019-02-19 14:37:03 +00:00
|
|
|
/* 3GPP TS 44.018 §10.5.2.16 IA Rest Octets */
|
2019-03-13 18:08:02 +00:00
|
|
|
dest->cur_bit = wp;
|
2019-02-19 14:37:03 +00:00
|
|
|
if (downlink) {
|
2020-07-18 13:54:22 +00:00
|
|
|
OSMO_ASSERT(as_dl_tbf(tbf) != NULL);
|
2019-02-19 16:49:27 +00:00
|
|
|
|
2019-02-19 14:37:03 +00:00
|
|
|
rc = write_ia_rest_downlink(as_dl_tbf(tbf), dest, polling, gsm48_ta_is_valid(ta), fn, alpha, gamma,
|
2019-02-19 16:49:27 +00:00
|
|
|
ta_idx);
|
2019-02-19 14:37:03 +00:00
|
|
|
} else if (((burst_type == GSM_L1_BURST_TYPE_ACCESS_1) || (burst_type == GSM_L1_BURST_TYPE_ACCESS_2))) {
|
2019-03-13 18:08:02 +00:00
|
|
|
SET_L(dest); SET_H(dest); // "LH"
|
|
|
|
SET_0(dest); SET_0(dest); // "00" < EGPRS Packet Uplink Assignment >
|
|
|
|
rc = bitvec_set_u64(dest, ra & 0x1F, 5, false); // < Extended RA >
|
|
|
|
CHECK(rc);
|
|
|
|
|
|
|
|
SET_0(dest); // No < Access Technologies Request struct >
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-03-13 18:08:02 +00:00
|
|
|
if (as_ul_tbf(tbf) != NULL)
|
2019-03-12 17:42:09 +00:00
|
|
|
rc = write_ia_rest_egprs_uplink_sba(as_ul_tbf(tbf), dest, usf, alpha, gamma, ta_idx);
|
2019-03-13 18:08:02 +00:00
|
|
|
else
|
2019-02-19 17:10:12 +00:00
|
|
|
rc = write_ia_rest_egprs_uplink_mba(dest, fn, alpha, gamma);
|
2019-02-19 14:37:03 +00:00
|
|
|
} else {
|
|
|
|
OSMO_ASSERT(!tbf || !tbf->is_egprs_enabled());
|
|
|
|
|
2019-03-13 18:08:02 +00:00
|
|
|
SET_H(dest); SET_H(dest); // "HH"
|
|
|
|
SET_0(dest); SET_0(dest); // "00" < Packet Uplink Assignment >
|
2019-02-19 14:37:03 +00:00
|
|
|
|
2019-03-13 18:08:02 +00:00
|
|
|
if (as_ul_tbf(tbf) != NULL)
|
2019-03-12 17:42:09 +00:00
|
|
|
rc = write_ia_rest_uplink_mba(as_ul_tbf(tbf), dest, usf, alpha, gamma, ta_idx);
|
2019-03-13 18:08:02 +00:00
|
|
|
else
|
2019-02-19 17:41:22 +00:00
|
|
|
rc = write_ia_rest_uplink_sba(dest, fn, alpha, gamma);
|
2019-02-19 14:37:03 +00:00
|
|
|
}
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2016-02-01 15:13:38 +00:00
|
|
|
if (rc < 0) {
|
|
|
|
LOGP(DRLCMAC, LOGL_ERROR,
|
2018-10-06 08:42:58 +00:00
|
|
|
"Failed to create IMMEDIATE ASSIGNMENT (%s) for %s\n",
|
2016-02-01 15:13:38 +00:00
|
|
|
downlink ? "downlink" : "uplink",
|
|
|
|
tbf ? tbf->name() : "single block allocation");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-10-19 16:50:22 +00:00
|
|
|
return plen;
|
|
|
|
}
|
|
|
|
|
2020-07-18 17:47:20 +00:00
|
|
|
/* Prepare to be encoded Frequency Parameters IE (see Table 12.8.1) */
|
|
|
|
static void gen_freq_params(Frequency_Parameters_t *freq_params,
|
|
|
|
const struct gprs_rlcmac_tbf *tbf)
|
|
|
|
{
|
|
|
|
const struct gprs_rlcmac_pdch *pdch;
|
|
|
|
Direct_encoding_1_t fh_params;
|
|
|
|
|
|
|
|
/* Check one PDCH, if it's hopping then all other should too */
|
|
|
|
pdch = tbf->pdch[tbf->first_ts];
|
|
|
|
OSMO_ASSERT(pdch != NULL);
|
|
|
|
|
|
|
|
/* Training Sequence Code */
|
|
|
|
freq_params->TSC = pdch->tsc;
|
|
|
|
|
|
|
|
/* If frequency hopping is not in use, encode a single ARFCN */
|
2020-09-10 11:20:32 +00:00
|
|
|
if (!pdch->fh.enabled) {
|
2020-07-18 17:47:20 +00:00
|
|
|
freq_params->UnionType = 0x00;
|
|
|
|
freq_params->u.ARFCN = tbf->trx->arfcn;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Direct encoding 1 (see Table 12.8.1) */
|
|
|
|
freq_params->UnionType = 0x2;
|
|
|
|
|
|
|
|
/* HSN / MAIO */
|
|
|
|
fh_params.MAIO = pdch->fh.maio;
|
|
|
|
fh_params.GPRS_Mobile_Allocation.HSN = pdch->fh.hsn;
|
|
|
|
fh_params.GPRS_Mobile_Allocation.ElementsOf_RFL_NUMBER = 0;
|
|
|
|
|
|
|
|
/* Mobile Allocation bitmap */
|
|
|
|
fh_params.GPRS_Mobile_Allocation.UnionType = 0; /* MA bitmap */
|
|
|
|
fh_params.GPRS_Mobile_Allocation.u.MA.MA_LENGTH = pdch->fh.ma_oct_len; /* in bytes */
|
|
|
|
fh_params.GPRS_Mobile_Allocation.u.MA.MA_BitLength = pdch->fh.ma_bit_len; /* in bits */
|
|
|
|
memcpy(fh_params.GPRS_Mobile_Allocation.u.MA.MA_BITMAP, pdch->fh.ma, pdch->fh.ma_oct_len);
|
|
|
|
|
|
|
|
freq_params->u.Direct_encoding_1 = fh_params;
|
|
|
|
}
|
|
|
|
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
/* Generate Packet Uplink Assignment as per 3GPP TS 44.060, section 11.2.29.
|
|
|
|
* NOTE: 'block' is expected to be zero-initialized by the caller. */
|
2021-07-27 10:27:08 +00:00
|
|
|
void write_packet_uplink_assignment(RlcMacDownlink_t *block, uint8_t old_tfi,
|
2013-10-19 16:50:22 +00:00
|
|
|
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
|
2020-08-20 09:49:34 +00:00
|
|
|
const struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t rrbp, uint8_t alpha,
|
2020-08-21 17:12:30 +00:00
|
|
|
uint8_t gamma, int8_t ta_idx, bool use_egprs)
|
2013-10-19 16:50:22 +00:00
|
|
|
{
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
Packet_Uplink_Assignment_t *pua;
|
|
|
|
Packet_Timing_Advance_t *pta;
|
|
|
|
Frequency_Parameters_t *fp;
|
|
|
|
Dynamic_Allocation_t *da;
|
|
|
|
|
|
|
|
/* RLC/MAC control block without the optional RLC/MAC control header */
|
|
|
|
block->PAYLOAD_TYPE = 0x01; // Payload Type
|
|
|
|
block->RRBP = rrbp; // RRBP (e.g. N+13)
|
|
|
|
block->SP = poll; // RRBP field is valid
|
|
|
|
block->USF = 0x00; // Uplink state flag
|
|
|
|
|
|
|
|
/* See 3GPP TS 44.060, section 11.2.29 */
|
|
|
|
pua = &block->u.Packet_Uplink_Assignment;
|
|
|
|
pua->MESSAGE_TYPE = 0x0a; // Packet Uplink Assignment
|
|
|
|
pua->PAGE_MODE = 0x00; // Normal Paging
|
|
|
|
|
|
|
|
/* TLLI or Global (UL/DL) TFI */
|
|
|
|
if (use_tlli) {
|
|
|
|
pua->ID.UnionType = 0x01;
|
|
|
|
pua->ID.u.TLLI = tlli;
|
|
|
|
} else {
|
|
|
|
pua->ID.UnionType = 0x00;
|
|
|
|
pua->ID.u.Global_TFI.UnionType = old_downlink;
|
|
|
|
pua->ID.u.Global_TFI.u.UPLINK_TFI = old_tfi;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GPRS/EGPRS specific parameters */
|
|
|
|
pua->UnionType = use_egprs ? 0x01 : 0x00;
|
|
|
|
if (!use_egprs) {
|
|
|
|
PUA_GPRS_t *gprs = &pua->u.PUA_GPRS_Struct;
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2021-01-25 13:39:12 +00:00
|
|
|
/* Use the commanded CS/MCS value during the content resolution */
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
gprs->CHANNEL_CODING_COMMAND = mcs_chan_code(tbf->current_cs());
|
|
|
|
gprs->TLLI_BLOCK_CHANNEL_CODING = 0x01; // ^^^
|
2013-10-19 16:50:22 +00:00
|
|
|
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
/* Dynamic allocation */
|
|
|
|
gprs->UnionType = 0x01;
|
|
|
|
/* Frequency Parameters IE is present */
|
|
|
|
gprs->Exist_Frequency_Parameters = 0x01;
|
2013-10-19 16:50:22 +00:00
|
|
|
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
/* Common parameters to be set below */
|
|
|
|
pta = &gprs->Packet_Timing_Advance;
|
|
|
|
fp = &gprs->Frequency_Parameters;
|
|
|
|
da = &gprs->u.Dynamic_Allocation;
|
2013-10-19 16:50:22 +00:00
|
|
|
} else {
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
PUA_EGPRS_00_t *egprs = &pua->u.PUA_EGPRS_Struct.u.PUA_EGPRS_00;
|
|
|
|
pua->u.PUA_EGPRS_Struct.UnionType = 0x00; // 'Normal' EGPRS, not EGPRS2
|
|
|
|
|
2021-01-25 13:39:12 +00:00
|
|
|
/* Use the commanded CS/MCS value during the content resolution */
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
egprs->EGPRS_CHANNEL_CODING_COMMAND = mcs_chan_code(tbf->current_cs());
|
|
|
|
egprs->TLLI_BLOCK_CHANNEL_CODING = 0x01; // ^^^
|
|
|
|
egprs->RESEGMENT = 0x01; // Enable segmentation
|
|
|
|
egprs->EGPRS_WindowSize = tbf->window_size();
|
|
|
|
|
|
|
|
/* Dynamic allocation */
|
|
|
|
egprs->UnionType = 0x01;
|
|
|
|
/* Frequency Parameters IE is present */
|
|
|
|
egprs->Exist_Frequency_Parameters = 0x01;
|
|
|
|
|
|
|
|
/* Common parameters to be set below */
|
|
|
|
pta = &egprs->Packet_Timing_Advance;
|
|
|
|
fp = &egprs->Frequency_Parameters;
|
|
|
|
da = &egprs->u.Dynamic_Allocation;
|
2013-10-19 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
/* Packet Timing Advance (if known) */
|
2021-05-11 11:01:53 +00:00
|
|
|
if (gsm48_ta_is_valid(tbf->ta())) { /* { 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > } */
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
pta->Exist_TIMING_ADVANCE_VALUE = 0x01; // Present
|
|
|
|
pta->TIMING_ADVANCE_VALUE = tbf->ta();
|
2013-10-19 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
/* Continuous Timing Advance Control */
|
|
|
|
if (ta_idx >= 0 && ta_idx < 16) {
|
|
|
|
pta->Exist_IndexAndtimeSlot = 0x01; // Present
|
|
|
|
pta->TIMING_ADVANCE_TIMESLOT_NUMBER = 0; // FIXME!
|
|
|
|
pta->TIMING_ADVANCE_INDEX = ta_idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Frequency Parameters IE */
|
2020-07-18 17:47:20 +00:00
|
|
|
gen_freq_params(fp, tbf);
|
encoding: use CSN.1 codec to generate Packet Uplink Assignment
It's quite odd to see that in write_packet_downlink_assignment()
we initialize an 'RlcMacDownlink_t', so then the caller can use
the power of CSN.1 codec to generate the final sequence of bytes
to be transmitted, while in write_packet_uplink_assignment() we
already compose the final RLC/MAC message straight away using
the low-level bitvec API (like bitvec_write_field()).
I guess the reason is that at the time of writing this code, the
CSN.1 codec was not stable enough, so it was safer to generate
the message 'by hand'. This would also explain why we *decode*
the final RLC/MAC message in create_ul_ass() right after encoding.
Rewrite write_packet_uplink_assignment(), so now it initializes
a caller-provided 'RlcMacDownlink_t' structure. Given that it's
allocated on heap using talloc_zero(), do not initialize presence
indicators of fields that are not present in the message.
This would facilitate handling of frequency hopping parameters
in the upcoming changes, in particular we can now introduce a
function that would compose Frequency Parameters IE for both
write_packet_{downlink,uplink}_assignment().
Tested manually by running a GPRS-enabled network, as well as by
running test cases from ttcn3-pcu-test => no regressions observed.
Change-Id: I2850b91e0043cdca8ae7498a5fc727eeedd029b6
Related: SYS#4868, OS#4547
2020-08-21 19:05:28 +00:00
|
|
|
|
|
|
|
/* Dynamic allocation parameters */
|
|
|
|
da->USF_GRANULARITY = 0x00;
|
|
|
|
|
|
|
|
/* Assign an Uplink TFI */
|
|
|
|
da->Exist_UPLINK_TFI_ASSIGNMENT = 0x01;
|
|
|
|
da->UPLINK_TFI_ASSIGNMENT = tbf->tfi();
|
|
|
|
|
|
|
|
/* Timeslot Allocation with or without Power Control */
|
|
|
|
da->UnionType = (alpha || gamma) ? 0x01 : 0x00;
|
|
|
|
if (da->UnionType == 0x01)
|
|
|
|
da->u.Timeslot_Allocation_Power_Ctrl_Param.ALPHA = alpha;
|
|
|
|
|
|
|
|
for (unsigned int tn = 0; tn < 8; tn++) {
|
|
|
|
if (tbf->pdch[tn] == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (da->UnionType == 0x01) {
|
|
|
|
Timeslot_Allocation_Power_Ctrl_Param_t *params = \
|
|
|
|
&da->u.Timeslot_Allocation_Power_Ctrl_Param;
|
|
|
|
params->Slot[tn].Exist = 0x01; // Enable this timeslot
|
|
|
|
params->Slot[tn].USF_TN = tbf->m_usf[tn]; // USF_TN(i)
|
|
|
|
params->Slot[tn].GAMMA_TN = gamma;
|
|
|
|
} else {
|
|
|
|
Timeslot_Allocation_t *slot = &da->u.Timeslot_Allocation[tn];
|
|
|
|
slot->Exist = 0x01; // Enable this timeslot
|
|
|
|
slot->USF_TN = tbf->m_usf[tn]; // USF_TN(i)
|
|
|
|
}
|
2013-10-19 16:50:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-07 19:46:05 +00:00
|
|
|
/* Generate Packet Downlink Assignment as per 3GPP TS 44.060, section 11.2.7.
|
|
|
|
* NOTE: 'block' is expected to be zero-initialized by the caller. */
|
2021-07-27 15:33:07 +00:00
|
|
|
void write_packet_downlink_assignment(RlcMacDownlink_t * block,
|
2016-01-29 15:39:21 +00:00
|
|
|
bool old_tfi_is_valid, uint8_t old_tfi, uint8_t old_downlink,
|
2020-08-20 09:49:34 +00:00
|
|
|
const struct gprs_rlcmac_dl_tbf *tbf, uint8_t poll, uint8_t rrbp,
|
2016-01-29 15:39:21 +00:00
|
|
|
uint8_t alpha, uint8_t gamma, int8_t ta_idx,
|
2021-07-27 15:33:07 +00:00
|
|
|
uint8_t ta_ts, bool use_egprs, uint8_t control_ack)
|
2013-10-19 16:50:22 +00:00
|
|
|
{
|
2016-01-07 15:04:29 +00:00
|
|
|
PDA_AdditionsR99_t *pda_r99;
|
|
|
|
|
2013-10-19 16:50:22 +00:00
|
|
|
uint8_t tn;
|
|
|
|
|
|
|
|
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
|
2016-01-26 20:46:26 +00:00
|
|
|
block->RRBP = rrbp; // 0: N+13
|
2013-10-19 16:50:22 +00:00
|
|
|
block->SP = poll; // RRBP field is valid
|
|
|
|
block->USF = 0x0; // Uplink state flag
|
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2; // Packet Downlink Assignment
|
|
|
|
block->u.Packet_Downlink_Assignment.PAGE_MODE = 0x0; // Normal Paging
|
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL = 0x0; // PERSISTENCE_LEVEL: off
|
|
|
|
|
2016-01-29 15:39:21 +00:00
|
|
|
if (old_tfi_is_valid) {
|
|
|
|
block->u.Packet_Downlink_Assignment.ID.UnionType = 0x0; // TFI = on
|
|
|
|
block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType = old_downlink; // 0=UPLINK TFI, 1=DL TFI
|
|
|
|
block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = old_tfi; // TFI
|
|
|
|
} else {
|
|
|
|
block->u.Packet_Downlink_Assignment.ID.UnionType = 0x1; // TLLI
|
|
|
|
block->u.Packet_Downlink_Assignment.ID.u.TLLI = tbf->tlli();
|
|
|
|
}
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.MAC_MODE = 0x0; // Dynamic Allocation
|
2020-05-23 12:23:56 +00:00
|
|
|
block->u.Packet_Downlink_Assignment.RLC_MODE = RLC_MODE_ACKNOWLEDGED;
|
2021-07-27 15:33:07 +00:00
|
|
|
block->u.Packet_Downlink_Assignment.CONTROL_ACK = control_ack; // NW establishes no new DL TBF for the MS with running timer T3192
|
2013-10-19 16:50:22 +00:00
|
|
|
block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0; // timeslot(s)
|
|
|
|
for (tn = 0; tn < 8; tn++) {
|
|
|
|
if (tbf->pdch[tn])
|
|
|
|
block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn; // timeslot(s)
|
|
|
|
}
|
|
|
|
|
2020-08-20 10:26:31 +00:00
|
|
|
if (tbf->ta() > 63) { /* { 0 | 1 < TIMING_ADVANCE_VALUE : bit (6) > } */
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x0; // TIMING_ADVANCE_VALUE = off
|
|
|
|
} else {
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = tbf->ta(); // TIMING_ADVANCE_VALUE
|
|
|
|
}
|
|
|
|
|
2013-10-19 16:50:22 +00:00
|
|
|
if (ta_idx < 0) {
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x0; // TIMING_ADVANCE_INDEX = off
|
|
|
|
} else {
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x1; // TIMING_ADVANCE_INDEX = on
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_INDEX = ta_idx; // TIMING_ADVANCE_INDEX
|
|
|
|
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_TIMESLOT_NUMBER = ta_ts; // TIMING_ADVANCE_TS
|
|
|
|
}
|
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0; // POWER CONTROL = off
|
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters = 0x1; // Frequency Parameters = on
|
2020-07-18 17:47:20 +00:00
|
|
|
gen_freq_params(&block->u.Packet_Downlink_Assignment.Frequency_Parameters, tbf);
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT = 0x1; // DOWNLINK TFI ASSIGNMENT = on
|
2013-10-27 19:31:47 +00:00
|
|
|
block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = tbf->tfi(); // TFI
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1; // Power Control Parameters = on
|
|
|
|
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = alpha; // ALPHA
|
|
|
|
|
|
|
|
for (tn = 0; tn < 8; tn++)
|
|
|
|
{
|
|
|
|
if (tbf->pdch[tn])
|
|
|
|
{
|
|
|
|
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x1; // Slot[i] = on
|
|
|
|
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = gamma; // GAMMA_TN
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x0; // Slot[i] = off
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time = 0x0; // TBF Starting TIME = off
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off
|
2016-01-07 15:04:29 +00:00
|
|
|
if (!use_egprs) {
|
|
|
|
block->u.Packet_Downlink_Assignment.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off
|
|
|
|
return;
|
|
|
|
}
|
2016-01-19 14:53:30 +00:00
|
|
|
|
2016-01-07 15:04:29 +00:00
|
|
|
block->u.Packet_Downlink_Assignment.Exist_AdditionsR99 = 0x1; // AdditionsR99 = on
|
|
|
|
pda_r99 = &block->u.Packet_Downlink_Assignment.AdditionsR99;
|
|
|
|
pda_r99->Exist_EGPRS_Params = 1;
|
2017-12-15 10:21:57 +00:00
|
|
|
pda_r99->EGPRS_WindowSize = enc_ws(tbf->window_size()); /* see TS 44.060, table 12.5.2.1 */
|
2016-01-07 15:04:29 +00:00
|
|
|
pda_r99->LINK_QUALITY_MEASUREMENT_MODE = 0x0; /* no meas, see TS 44.060, table 11.2.7.2 */
|
|
|
|
pda_r99->Exist_BEP_PERIOD2 = 0; /* No extra EGPRS BEP PERIOD */
|
|
|
|
pda_r99->Exist_Packet_Extended_Timing_Advance = 0;
|
|
|
|
pda_r99->Exist_COMPACT_ReducedMA = 0;
|
2013-10-19 16:50:22 +00:00
|
|
|
}
|
|
|
|
|
2018-05-02 12:53:16 +00:00
|
|
|
/* Generate paging request. See 44.018, sections 10 and 9.1.22 */
|
paging: pass struct osmo_mobile_identity, not encoded IE bytes
In get_paging_mi(), before this, an encoded buffer of Mobile Identity bytes is
returned. Code paths following this repeatedly decode the Mobile Identity
bytes, e.g. for logging. Also, in get_paging_mi(), since the TMSI is read in
from a different encoding than a typical Mobile Identity IE, the TMSI was
manually encoded into a typical Mobile Identity IE. This is essentially a code
dup of osmo_mobile_identity_encode(). Stop this madness.
Instead, in get_paging_mi(), return a decoded struct osmo_mobile_identity. Code
paths after this use the struct osmo_mobile_identity directly without repeated
decoding.
At the point of finally needing an encoded Mobile Identity IE (in
Encoding::write_paging_request()), do a proper osmo_mobile_identity_encode().
Since this may return errors, add an rc check for the caller of
write_paging_request(), gprs_rlcmac_paging_request().
A side effect is stricter validation of the Mobile Identity passing through the
Paging code path. Before, invalid MI might have passed through unnoticed.
Change-Id: Iad845acb0096b75dc453105c9c16b2252879b4ca
2020-08-21 14:21:23 +00:00
|
|
|
int Encoding::write_paging_request(bitvec * dest, const struct osmo_mobile_identity *mi)
|
2013-10-19 16:50:22 +00:00
|
|
|
{
|
paging: pass struct osmo_mobile_identity, not encoded IE bytes
In get_paging_mi(), before this, an encoded buffer of Mobile Identity bytes is
returned. Code paths following this repeatedly decode the Mobile Identity
bytes, e.g. for logging. Also, in get_paging_mi(), since the TMSI is read in
from a different encoding than a typical Mobile Identity IE, the TMSI was
manually encoded into a typical Mobile Identity IE. This is essentially a code
dup of osmo_mobile_identity_encode(). Stop this madness.
Instead, in get_paging_mi(), return a decoded struct osmo_mobile_identity. Code
paths after this use the struct osmo_mobile_identity directly without repeated
decoding.
At the point of finally needing an encoded Mobile Identity IE (in
Encoding::write_paging_request()), do a proper osmo_mobile_identity_encode().
Since this may return errors, add an rc check for the caller of
write_paging_request(), gprs_rlcmac_paging_request().
A side effect is stricter validation of the Mobile Identity passing through the
Paging code path. Before, invalid MI might have passed through unnoticed.
Change-Id: Iad845acb0096b75dc453105c9c16b2252879b4ca
2020-08-21 14:21:23 +00:00
|
|
|
uint8_t mi_buf[GSM48_MID_MAX_SIZE];
|
|
|
|
int mi_len;
|
2013-10-19 16:50:22 +00:00
|
|
|
unsigned wp = 0;
|
|
|
|
int plen;
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,4); // Skip Indicator
|
|
|
|
bitvec_write_field(dest, &wp,0x6,4); // Protocol Discriminator
|
2018-05-02 12:53:16 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x21,8); // Paging Request Message Type 1
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,4); // Page Mode
|
|
|
|
bitvec_write_field(dest, &wp,0x0,4); // Channel Needed
|
2013-10-19 16:50:22 +00:00
|
|
|
|
paging: pass struct osmo_mobile_identity, not encoded IE bytes
In get_paging_mi(), before this, an encoded buffer of Mobile Identity bytes is
returned. Code paths following this repeatedly decode the Mobile Identity
bytes, e.g. for logging. Also, in get_paging_mi(), since the TMSI is read in
from a different encoding than a typical Mobile Identity IE, the TMSI was
manually encoded into a typical Mobile Identity IE. This is essentially a code
dup of osmo_mobile_identity_encode(). Stop this madness.
Instead, in get_paging_mi(), return a decoded struct osmo_mobile_identity. Code
paths after this use the struct osmo_mobile_identity directly without repeated
decoding.
At the point of finally needing an encoded Mobile Identity IE (in
Encoding::write_paging_request()), do a proper osmo_mobile_identity_encode().
Since this may return errors, add an rc check for the caller of
write_paging_request(), gprs_rlcmac_paging_request().
A side effect is stricter validation of the Mobile Identity passing through the
Paging code path. Before, invalid MI might have passed through unnoticed.
Change-Id: Iad845acb0096b75dc453105c9c16b2252879b4ca
2020-08-21 14:21:23 +00:00
|
|
|
mi_len = osmo_mobile_identity_encode_buf(mi_buf, sizeof(mi_buf), mi, true);
|
|
|
|
if (mi_len <= 0)
|
|
|
|
return mi_len;
|
2019-12-10 18:26:27 +00:00
|
|
|
bitvec_write_field(dest, &wp, mi_len, 8); // Mobile Identity length
|
paging: pass struct osmo_mobile_identity, not encoded IE bytes
In get_paging_mi(), before this, an encoded buffer of Mobile Identity bytes is
returned. Code paths following this repeatedly decode the Mobile Identity
bytes, e.g. for logging. Also, in get_paging_mi(), since the TMSI is read in
from a different encoding than a typical Mobile Identity IE, the TMSI was
manually encoded into a typical Mobile Identity IE. This is essentially a code
dup of osmo_mobile_identity_encode(). Stop this madness.
Instead, in get_paging_mi(), return a decoded struct osmo_mobile_identity. Code
paths after this use the struct osmo_mobile_identity directly without repeated
decoding.
At the point of finally needing an encoded Mobile Identity IE (in
Encoding::write_paging_request()), do a proper osmo_mobile_identity_encode().
Since this may return errors, add an rc check for the caller of
write_paging_request(), gprs_rlcmac_paging_request().
A side effect is stricter validation of the Mobile Identity passing through the
Paging code path. Before, invalid MI might have passed through unnoticed.
Change-Id: Iad845acb0096b75dc453105c9c16b2252879b4ca
2020-08-21 14:21:23 +00:00
|
|
|
bitvec_set_bytes(dest, mi_buf, mi_len); // Mobile Identity
|
2019-12-10 18:26:27 +00:00
|
|
|
wp += mi_len * 8;
|
2017-03-08 17:53:30 +00:00
|
|
|
|
2020-07-18 13:47:47 +00:00
|
|
|
OSMO_ASSERT(wp % 8 == 0);
|
2017-03-08 17:53:30 +00:00
|
|
|
|
2013-10-19 16:50:22 +00:00
|
|
|
plen = wp / 8;
|
2018-05-02 12:53:16 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // "L" Notification List Number; NLN(PCH) = off
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // "L" Priority1 = off
|
|
|
|
bitvec_write_field(dest, &wp,0x1,1); // "L" Priority2 = off
|
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // "L" Group Call information = off
|
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // "H" Packet Page Indication 1 = packet paging procedure
|
|
|
|
bitvec_write_field(dest, &wp,0x1,1); // "H" Packet Page Indication 2 = packet paging procedure
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
return plen;
|
|
|
|
}
|
|
|
|
|
2013-12-11 13:35:29 +00:00
|
|
|
/**
|
|
|
|
* The index of the array show_rbb is the bit position inside the rbb
|
|
|
|
* (show_rbb[63] relates to BSN ssn-1)
|
|
|
|
*/
|
2021-07-28 19:48:26 +00:00
|
|
|
void Encoding::encode_rbb(const char *show_rbb, bitvec *bv)
|
2013-12-11 13:35:29 +00:00
|
|
|
{
|
|
|
|
// RECEIVE_BLOCK_BITMAP
|
|
|
|
for (int i = 0; i < 64; i++) {
|
2021-02-01 12:06:45 +00:00
|
|
|
/* Set bit at the appropriate position (see 3GPP TS 44.060 9.1.8.1) */
|
2021-07-28 19:48:26 +00:00
|
|
|
bitvec_set_bit(bv, show_rbb[i] == 'R' ? ONE : ZERO);
|
2013-12-11 13:35:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-22 13:58:34 +00:00
|
|
|
static void write_packet_ack_nack_desc_gprs(
|
2019-06-16 13:25:30 +00:00
|
|
|
bitvec * dest, unsigned& wp,
|
2015-12-22 13:58:34 +00:00
|
|
|
gprs_rlc_ul_window *window, bool is_final)
|
|
|
|
{
|
|
|
|
char rbb[65];
|
|
|
|
|
|
|
|
window->update_rbb(rbb);
|
|
|
|
|
|
|
|
rbb[64] = 0;
|
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG, "- V(N): \"%s\" R=Received "
|
|
|
|
"I=Invalid\n", rbb);
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, is_final, 1); // FINAL_ACK_INDICATION
|
|
|
|
bitvec_write_field(dest, &wp, window->ssn(), 7); // STARTING_SEQUENCE_NUMBER
|
2015-12-22 13:58:34 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 64; i++) {
|
2021-02-01 12:06:45 +00:00
|
|
|
/* Set bit at the appropriate position (see 3GPP TS 44.060 9.1.8.1) */
|
2015-12-22 13:58:34 +00:00
|
|
|
bool is_ack = (rbb[i] == 'R');
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, is_ack, 1);
|
2015-12-22 13:58:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_packet_uplink_ack_gprs(
|
2019-06-16 13:25:30 +00:00
|
|
|
bitvec * dest, unsigned& wp,
|
2015-12-22 13:58:34 +00:00
|
|
|
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
|
|
|
|
{
|
2020-10-23 19:00:23 +00:00
|
|
|
gprs_rlc_ul_window *window = static_cast<gprs_rlc_ul_window *>(tbf->window());
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2019-03-06 17:17:32 +00:00
|
|
|
bitvec_write_field(dest, &wp, mcs_chan_code(tbf->current_cs()), 2); // CHANNEL_CODING_COMMAND
|
2020-10-23 19:00:23 +00:00
|
|
|
write_packet_ack_nack_desc_gprs(dest, wp, window, is_final);
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2021-05-11 10:52:41 +00:00
|
|
|
if (tbf->is_tlli_valid()) {
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have CONTENTION_RESOLUTION_TLLI
|
|
|
|
bitvec_write_field(dest, &wp, tbf->tlli(), 32); // CONTENTION_RESOLUTION_TLLI
|
|
|
|
} else {
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have CONTENTION_RESOLUTION_TLLI
|
|
|
|
}
|
2017-02-04 02:10:08 +00:00
|
|
|
|
2021-05-11 11:17:31 +00:00
|
|
|
if (gsm48_ta_is_valid(tbf->ta())) {
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have Packet Timing Advance IE (TS 44.060 12.12)
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have TIMING_ADVANCE_VALUE
|
|
|
|
bitvec_write_field(dest, &wp, tbf->ta(), 6); // TIMING_ADVANCE_VALUE
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have TIMING_ADVANCE_INDEX
|
|
|
|
} else {
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Packet Timing Advance
|
|
|
|
}
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Power Control Parameters
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Extension Bits
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // fixed 0
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have Additions R99
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Packet Extended Timing Advance
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // TBF_EST (enabled)
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have REL 5
|
2015-12-22 13:58:34 +00:00
|
|
|
};
|
|
|
|
|
2019-06-16 15:05:40 +00:00
|
|
|
/* Encode the Ack/Nack for EGPRS. 44.060
|
|
|
|
* The PCU encodes to receive block bitmap to the following rules:
|
|
|
|
* - always encode the lenght field
|
|
|
|
* - use compressed receive block bitmap if it's smaller than uncompressed
|
|
|
|
* receive block bitmap
|
|
|
|
* - use the remaining bits for an uncompressed receive block bitmap if needed
|
|
|
|
*
|
|
|
|
* Note: The spec also defines an Ack/Nack without length field, but the PCU's
|
|
|
|
* doesn't support this in UL. It would require a lot more code complexity
|
|
|
|
* and only saves 7 bit in lossy sitations.
|
|
|
|
*/
|
2015-12-22 13:58:34 +00:00
|
|
|
static void write_packet_ack_nack_desc_egprs(
|
2019-06-16 13:25:30 +00:00
|
|
|
bitvec * dest, unsigned& wp,
|
2019-06-16 19:53:03 +00:00
|
|
|
gprs_rlc_ul_window *window, bool is_final, unsigned rest_bits)
|
2015-12-22 13:58:34 +00:00
|
|
|
{
|
2017-01-16 10:11:21 +00:00
|
|
|
unsigned int urbb_len = 0;
|
|
|
|
uint8_t crbb_len = 0;
|
|
|
|
uint8_t len;
|
2015-12-22 13:58:34 +00:00
|
|
|
bool bow = true;
|
|
|
|
bool eow = true;
|
2019-06-16 14:20:10 +00:00
|
|
|
uint16_t ssn = window->mod_sns(window->v_q() + 1);
|
2017-01-16 10:11:21 +00:00
|
|
|
unsigned int num_blocks = window->mod_sns(window->v_r() - window->v_q());
|
2019-06-16 14:20:10 +00:00
|
|
|
uint16_t esn_crbb = window->mod_sns(ssn - 1);
|
2017-01-16 10:11:21 +00:00
|
|
|
static uint8_t rbb[RLC_EGPRS_MAX_WS] = {'\0'};
|
|
|
|
uint8_t iter = 0;
|
2016-10-24 06:51:21 +00:00
|
|
|
int is_compressed = 0;
|
2017-01-16 10:11:21 +00:00
|
|
|
bool try_compression = false;
|
2019-06-16 14:20:10 +00:00
|
|
|
uint16_t ucmp_bmplen;
|
2017-01-16 10:11:21 +00:00
|
|
|
uint8_t crbb_bitmap[23] = {'\0'};
|
|
|
|
bitvec ucmp_vec;
|
|
|
|
bitvec crbb_vec;
|
|
|
|
uint8_t uclen_crbb = 0;
|
|
|
|
uint8_t crbb_start_clr_code;
|
2016-10-24 06:51:21 +00:00
|
|
|
uint8_t i;
|
2019-06-16 13:16:30 +00:00
|
|
|
|
|
|
|
/* static size of 16 bits
|
2019-06-16 15:05:40 +00:00
|
|
|
..1. .... = ACKNACK: (Union)
|
|
|
|
0 0000 000 Length
|
2019-10-04 01:40:26 +00:00
|
|
|
Desc
|
2019-12-10 16:09:51 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
...0 .... = FINAL_ACK_INDICATION: False
|
2019-12-10 16:09:51 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
.... 1... = BEGINNING_OF_WINDOW: 1
|
2019-12-10 16:09:51 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
.... .1.. = END_OF_WINDOW: 1
|
2019-12-10 16:09:51 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
.... ..10 0101 0001 1... .... = STARTING_SEQUENCE_NUMBER: 1187
|
2019-12-10 16:09:51 +00:00
|
|
|
|
2019-06-16 15:05:40 +00:00
|
|
|
.0.. .... = CRBB Exist: 0
|
|
|
|
minimal size is 24 rest_bits */
|
|
|
|
rest_bits -= 24;
|
2015-12-22 13:58:34 +00:00
|
|
|
|
|
|
|
if (num_blocks > 0)
|
|
|
|
/* V(Q) is NACK and omitted -> SSN = V(Q) + 1 */
|
|
|
|
num_blocks -= 1;
|
|
|
|
|
|
|
|
if (num_blocks > window->ws())
|
|
|
|
num_blocks = window->ws();
|
2017-01-16 10:11:21 +00:00
|
|
|
/* Try Compression as number of blocks does not fit */
|
|
|
|
if (num_blocks > rest_bits) {
|
|
|
|
try_compression = true;
|
|
|
|
}
|
|
|
|
if (try_compression == true) {
|
|
|
|
ucmp_bmplen = window->update_egprs_rbb(rbb);
|
|
|
|
ucmp_vec.data = rbb;
|
|
|
|
ucmp_vec.cur_bit = ucmp_bmplen;
|
|
|
|
ucmp_vec.data_len = 127;
|
|
|
|
crbb_vec.data = crbb_bitmap;
|
|
|
|
crbb_vec.cur_bit = 0;
|
|
|
|
crbb_vec.data_len = 127;
|
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG,
|
|
|
|
"rest_bits=%d uncompressed len %d and uncompressed bitmap = %s\n",
|
|
|
|
rest_bits, ucmp_bmplen,
|
|
|
|
osmo_hexdump(ucmp_vec.data, (ucmp_bmplen+7)/8));
|
|
|
|
|
|
|
|
is_compressed = egprs_compress::compress_rbb(&ucmp_vec, /* Uncompressed bitmap*/
|
|
|
|
&crbb_vec, /*Compressed bitmap vector */
|
|
|
|
&uclen_crbb,
|
|
|
|
(rest_bits - 16));/* CRBBlength:7 colourcode:1 dissector length:8*/
|
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG,
|
|
|
|
"the ucmp len=%d uclen_crbb=%d num_blocks=%d crbb length %d, "
|
|
|
|
"and the CRBB bitmap = %s\n",
|
|
|
|
ucmp_bmplen, uclen_crbb, num_blocks, crbb_vec.cur_bit,
|
|
|
|
osmo_hexdump(crbb_bitmap, (crbb_vec.cur_bit+7)/8));
|
|
|
|
crbb_len = crbb_vec.cur_bit;
|
|
|
|
}
|
2016-10-24 06:51:21 +00:00
|
|
|
|
2017-01-16 10:11:21 +00:00
|
|
|
|
2019-06-16 15:05:40 +00:00
|
|
|
if (is_compressed) {
|
|
|
|
/* 8 = 7 (CRBBlength) + 1 (CRBB starting color code) */
|
|
|
|
rest_bits -= 8;
|
2016-10-24 06:51:21 +00:00
|
|
|
} else {
|
2019-06-16 15:05:40 +00:00
|
|
|
uclen_crbb = 0;
|
|
|
|
crbb_len = 0;
|
2016-10-24 06:51:21 +00:00
|
|
|
}
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2019-06-16 15:05:40 +00:00
|
|
|
if (num_blocks > uclen_crbb + rest_bits) {
|
|
|
|
eow = false;
|
|
|
|
urbb_len = rest_bits - crbb_len;
|
|
|
|
} else
|
|
|
|
urbb_len = num_blocks - uclen_crbb;
|
|
|
|
|
|
|
|
if (is_compressed)
|
|
|
|
len = urbb_len + crbb_len + 23;
|
|
|
|
else
|
|
|
|
len = urbb_len + 15;
|
|
|
|
|
|
|
|
|
|
|
|
/* EGPRS Ack/Nack Description IE
|
|
|
|
* do not support Ack/Nack without length */
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have length
|
|
|
|
bitvec_write_field(dest, &wp, len, 8); // length
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, is_final, 1); // FINAL_ACK_INDICATION
|
|
|
|
bitvec_write_field(dest, &wp, bow, 1); // BEGINNING_OF_WINDOW
|
|
|
|
bitvec_write_field(dest, &wp, eow, 1); // END_OF_WINDOW
|
|
|
|
bitvec_write_field(dest, &wp, ssn, 11); // STARTING_SEQUENCE_NUMBER
|
2016-10-24 06:51:21 +00:00
|
|
|
if (is_compressed) {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // CRBB_Exist
|
|
|
|
bitvec_write_field(dest, &wp, crbb_len, 7); // CRBB_LENGTH
|
2017-01-16 10:11:21 +00:00
|
|
|
crbb_start_clr_code = (0x80 & ucmp_vec.data[0])>>7;
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, crbb_start_clr_code, 1); // CRBB_clr_code
|
2017-01-16 10:11:21 +00:00
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG,
|
|
|
|
"EGPRS CRBB, crbb_len = %d, crbb_start_clr_code = %d\n",
|
|
|
|
crbb_len, crbb_start_clr_code);
|
|
|
|
while (crbb_len != 0) {
|
|
|
|
if (crbb_len > 8) {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, crbb_bitmap[iter], 8);
|
2017-01-16 10:11:21 +00:00
|
|
|
crbb_len = crbb_len - 8;
|
|
|
|
iter++;
|
|
|
|
} else {
|
2019-05-31 15:19:53 +00:00
|
|
|
bitvec_write_field(dest, &wp, crbb_bitmap[iter] >> (8 - crbb_len), crbb_len);
|
2017-01-16 10:11:21 +00:00
|
|
|
crbb_len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
esn_crbb = window->mod_sns(esn_crbb + uclen_crbb);
|
|
|
|
} else {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // CRBB_Exist
|
2017-01-16 10:11:21 +00:00
|
|
|
}
|
2015-12-22 13:58:34 +00:00
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG,
|
2019-06-16 14:20:10 +00:00
|
|
|
"EGPRS URBB, urbb len = %d, SSN = %u, ESN_CRBB = %u, "
|
2019-06-16 15:05:40 +00:00
|
|
|
"desc len = %d, "
|
2015-12-22 13:58:34 +00:00
|
|
|
"SNS = %d, WS = %d, V(Q) = %d, V(R) = %d%s%s\n",
|
2019-06-16 15:05:40 +00:00
|
|
|
urbb_len, ssn, esn_crbb, len,
|
2015-12-22 13:58:34 +00:00
|
|
|
window->sns(), window->ws(), window->v_q(), window->v_r(),
|
|
|
|
bow ? ", BOW" : "", eow ? ", EOW" : "");
|
2016-10-24 06:51:21 +00:00
|
|
|
|
|
|
|
for (i = urbb_len; i > 0; i--) {
|
2021-02-01 12:06:45 +00:00
|
|
|
/* Set bit at the appropriate position (see 3GPP TS 44.060 12.3.1) */
|
2015-12-22 13:58:34 +00:00
|
|
|
bool is_ack = window->m_v_n.is_received(esn_crbb + i);
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, is_ack, 1);
|
2015-12-22 13:58:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void write_packet_uplink_ack_egprs(
|
2019-06-16 13:25:30 +00:00
|
|
|
bitvec * dest, unsigned& wp,
|
2015-12-22 13:58:34 +00:00
|
|
|
struct gprs_rlcmac_ul_tbf *tbf, bool is_final)
|
|
|
|
{
|
2020-10-23 19:00:23 +00:00
|
|
|
gprs_rlc_ul_window *window = static_cast<gprs_rlc_ul_window *>(tbf->window());
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 2); // fixed 00
|
2016-06-15 12:16:41 +00:00
|
|
|
/* CHANNEL_CODING_COMMAND */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,
|
2019-03-06 17:17:32 +00:00
|
|
|
mcs_chan_code(tbf->current_cs()), 4);
|
2016-07-26 12:56:21 +00:00
|
|
|
/* 0: no RESEGMENT, 1: Segmentation*/
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 1, 1);
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // PRE_EMPTIVE_TRANSMISSION, TODO: This resembles GPRS, change it?
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: no PRR_RETRANSMISSION_REQUEST, TODO: clarify
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: no ARAC_RETRANSMISSION_REQUEST, TODO: clarify
|
2021-05-11 10:52:41 +00:00
|
|
|
|
|
|
|
if (tbf->is_tlli_valid()) {
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have CONTENTION_RESOLUTION_TLLI
|
|
|
|
bitvec_write_field(dest, &wp, tbf->tlli(), 32); // CONTENTION_RESOLUTION_TLLI
|
|
|
|
} else {
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have CONTENTION_RESOLUTION_TLLI
|
|
|
|
}
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // TBF_EST (enabled)
|
2021-05-11 11:17:31 +00:00
|
|
|
|
|
|
|
if (gsm48_ta_is_valid(tbf->ta())) {
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have Packet Timing Advance IE (TS 44.060 12.12)
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: have TIMING_ADVANCE_VALUE
|
|
|
|
bitvec_write_field(dest, &wp, tbf->ta(), 6); // TIMING_ADVANCE_VALUE
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have TIMING_ADVANCE_INDEX
|
|
|
|
} else {
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Packet Timing Advance
|
|
|
|
}
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Packet Extended Timing Advance
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Power Control Parameters
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have Extension Bits
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2017-01-16 10:11:21 +00:00
|
|
|
/* -2 for last bit 0 mandatory and REL5 not supported */
|
|
|
|
unsigned bits_ack_nack = dest->data_len * 8 - wp - 2;
|
2020-10-23 19:00:23 +00:00
|
|
|
write_packet_ack_nack_desc_egprs(dest, wp, window, is_final, bits_ack_nack);
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // fixed 0
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: don't have REL 5
|
2015-12-22 13:58:34 +00:00
|
|
|
};
|
|
|
|
|
2021-07-29 16:39:16 +00:00
|
|
|
void write_packet_uplink_ack(struct bitvec *dest, struct gprs_rlcmac_ul_tbf *tbf,
|
|
|
|
bool is_final, uint8_t rrbp)
|
2015-12-22 13:58:34 +00:00
|
|
|
{
|
|
|
|
unsigned wp = 0;
|
|
|
|
|
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG, "Encoding Ack/Nack for %s "
|
|
|
|
"(final=%d)\n", tbf_name(tbf), is_final);
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x1, 2); // Payload Type
|
|
|
|
bitvec_write_field(dest, &wp, rrbp, 2); // Uplink block with TDMA framenumber
|
|
|
|
bitvec_write_field(dest, &wp, is_final, 1); // Suppl/Polling Bit
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 3); // Uplink state flag
|
|
|
|
bitvec_write_field(dest, &wp, 0x9, 6); // MESSAGE TYPE Uplink Ack/Nack
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 2); // Page Mode
|
2015-12-22 13:58:34 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 2); // fixed 00
|
|
|
|
bitvec_write_field(dest, &wp, tbf->tfi(), 5); // Uplink TFI
|
2015-12-22 13:58:34 +00:00
|
|
|
|
|
|
|
if (tbf->is_egprs_enabled()) {
|
|
|
|
/* PU_AckNack_EGPRS = on */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // 1: EGPRS
|
2019-06-16 13:25:30 +00:00
|
|
|
write_packet_uplink_ack_egprs(dest, wp, tbf, is_final);
|
2015-12-22 13:58:34 +00:00
|
|
|
} else {
|
|
|
|
/* PU_AckNack_GPRS = on */
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // 0: GPRS
|
2019-06-16 13:25:30 +00:00
|
|
|
write_packet_uplink_ack_gprs(dest, wp, tbf, is_final);
|
2015-12-22 13:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOGP(DRLCMACUL, LOGL_DEBUG,
|
|
|
|
"Uplink Ack/Nack bit count %d, max %d, message = %s\n",
|
|
|
|
wp, dest->data_len * 8,
|
|
|
|
osmo_hexdump(dest->data, dest->data_len));
|
|
|
|
}
|
|
|
|
|
2013-10-19 17:04:03 +00:00
|
|
|
unsigned Encoding::write_packet_paging_request(bitvec * dest)
|
2013-10-19 16:50:22 +00:00
|
|
|
{
|
|
|
|
unsigned wp = 0;
|
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x1, 2); // Payload Type
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 3); // No polling
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 3); // Uplink state flag
|
|
|
|
bitvec_write_field(dest, &wp, 0x22, 6); // MESSAGE TYPE
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 2); // Page Mode
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2019-10-04 01:40:26 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1); // No PERSISTENCE_LEVEL
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1); // No NLN
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
return wp;
|
|
|
|
}
|
|
|
|
|
2016-07-26 19:16:08 +00:00
|
|
|
/* 3GPP TS 44.060 § 11.2.10:
|
|
|
|
< Repeated Page info struct > ::=
|
|
|
|
{ 0 -- Page request for TBF establishment
|
|
|
|
{ 0 < PTMSI : bit (32) >
|
|
|
|
| 1 < Length of Mobile Identity contents : bit (4) >
|
|
|
|
< Mobile Identity : octet (val (Length of Mobile Identity contents)) > }
|
|
|
|
| 1 -- Page request for RR conn. establishment
|
|
|
|
{ 0 < TMSI : bit (32) >
|
|
|
|
| 1 < Length of Mobile Identity contents : bit (4) >
|
|
|
|
< Mobile Identity : octet (val (Length of Mobile Identity contents)) > }
|
|
|
|
< CHANNEL_NEEDED : bit (2) >
|
|
|
|
{ 0 | 1 < eMLPP_PRIORITY : bit (3) > }
|
|
|
|
}
|
|
|
|
*/
|
2013-10-19 17:04:03 +00:00
|
|
|
unsigned Encoding::write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
|
2013-10-19 16:50:22 +00:00
|
|
|
uint8_t *identity, uint8_t chan_needed)
|
|
|
|
{
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x1,1); // Repeated Page info exists
|
2013-10-19 16:50:22 +00:00
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x1,1); // RR connection paging
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
if ((identity[0] & 0x07) == 4) {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // TMSI
|
2013-10-19 16:50:22 +00:00
|
|
|
identity++;
|
|
|
|
len--;
|
|
|
|
} else {
|
2016-07-26 19:16:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,0x1,1); // MI
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,len,4); // MI len
|
2013-10-19 16:50:22 +00:00
|
|
|
}
|
|
|
|
while (len) {
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,*identity++,8); // MI data
|
2013-10-19 16:50:22 +00:00
|
|
|
len--;
|
|
|
|
}
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp,chan_needed,2); // CHANNEL_NEEDED
|
|
|
|
bitvec_write_field(dest, &wp,0x0,1); // No eMLPP_PRIORITY
|
2013-10-19 16:50:22 +00:00
|
|
|
|
|
|
|
return wp;
|
|
|
|
}
|
|
|
|
|
2016-01-08 09:07:53 +00:00
|
|
|
int Encoding::rlc_write_dl_data_header(const struct gprs_rlc_data_info *rlc,
|
|
|
|
uint8_t *data)
|
|
|
|
{
|
2016-02-02 10:48:37 +00:00
|
|
|
struct gprs_rlc_dl_header_egprs_1 *egprs1;
|
|
|
|
struct gprs_rlc_dl_header_egprs_2 *egprs2;
|
2016-01-08 09:07:53 +00:00
|
|
|
struct gprs_rlc_dl_header_egprs_3 *egprs3;
|
|
|
|
struct rlc_dl_header *gprs;
|
|
|
|
unsigned int e_fbi_header;
|
2020-05-18 09:35:35 +00:00
|
|
|
enum CodingScheme cs = rlc->cs;
|
2016-02-02 10:48:37 +00:00
|
|
|
unsigned int offs;
|
|
|
|
unsigned int bsn_delta;
|
2016-01-08 09:07:53 +00:00
|
|
|
|
2020-05-18 09:35:35 +00:00
|
|
|
switch(mcs_header_type(cs)) {
|
2019-03-13 16:14:13 +00:00
|
|
|
case HEADER_GPRS_DATA:
|
2016-01-08 09:07:53 +00:00
|
|
|
gprs = static_cast<struct rlc_dl_header *>
|
|
|
|
((void *)data);
|
|
|
|
|
|
|
|
gprs->usf = rlc->usf;
|
|
|
|
gprs->s_p = rlc->es_p != 0 ? 1 : 0;
|
|
|
|
gprs->rrbp = rlc->rrbp;
|
|
|
|
gprs->pt = 0;
|
|
|
|
gprs->tfi = rlc->tfi;
|
|
|
|
gprs->pr = rlc->pr;
|
|
|
|
|
|
|
|
gprs->fbi = rlc->block_info[0].cv == 0;
|
|
|
|
gprs->e = rlc->block_info[0].e;
|
|
|
|
gprs->bsn = rlc->block_info[0].bsn;
|
|
|
|
break;
|
|
|
|
|
2019-03-13 16:14:13 +00:00
|
|
|
case HEADER_EGPRS_DATA_TYPE_1:
|
2016-02-02 10:48:37 +00:00
|
|
|
egprs1 = static_cast<struct gprs_rlc_dl_header_egprs_1 *>
|
|
|
|
((void *)data);
|
|
|
|
|
|
|
|
egprs1->usf = rlc->usf;
|
|
|
|
egprs1->es_p = rlc->es_p;
|
|
|
|
egprs1->rrbp = rlc->rrbp;
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs1->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
|
|
|
egprs1->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
2016-02-02 10:48:37 +00:00
|
|
|
egprs1->pr = rlc->pr;
|
|
|
|
egprs1->cps = rlc->cps;
|
|
|
|
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs1->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
|
|
|
egprs1->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
|
|
|
egprs1->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
2016-02-02 10:48:37 +00:00
|
|
|
|
|
|
|
bsn_delta = (rlc->block_info[1].bsn - rlc->block_info[0].bsn) &
|
|
|
|
(RLC_EGPRS_SNS - 1);
|
|
|
|
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs1->bsn2_hi = bsn_delta >> 0; /* 7 bits LSB */
|
|
|
|
egprs1->bsn2_lo = bsn_delta >> 7; /* 3 bits */
|
2016-02-02 10:48:37 +00:00
|
|
|
|
|
|
|
/* first FBI/E header */
|
|
|
|
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
|
|
|
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
|
2016-02-03 17:35:27 +00:00
|
|
|
offs = rlc->data_offs_bits[0] / 8;
|
|
|
|
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 2);
|
2016-02-02 10:48:37 +00:00
|
|
|
e_fbi_header <<= 0;
|
2016-02-03 17:35:27 +00:00
|
|
|
data[offs] = (data[offs] & 0b11111100) | e_fbi_header;
|
2016-02-02 10:48:37 +00:00
|
|
|
|
|
|
|
/* second FBI/E header */
|
|
|
|
e_fbi_header = rlc->block_info[1].e ? 0x01 : 0;
|
|
|
|
e_fbi_header |= rlc->block_info[1].cv == 0 ? 0x02 : 0; /* FBI */
|
|
|
|
offs = rlc->data_offs_bits[1] / 8;
|
|
|
|
OSMO_ASSERT(rlc->data_offs_bits[1] % 8 == 4);
|
|
|
|
e_fbi_header <<= 2;
|
|
|
|
data[offs] = (data[offs] & 0b11110011) | e_fbi_header;
|
|
|
|
break;
|
|
|
|
|
2019-03-13 16:14:13 +00:00
|
|
|
case HEADER_EGPRS_DATA_TYPE_2:
|
2016-02-02 10:48:37 +00:00
|
|
|
egprs2 = static_cast<struct gprs_rlc_dl_header_egprs_2 *>
|
|
|
|
((void *)data);
|
|
|
|
|
|
|
|
egprs2->usf = rlc->usf;
|
|
|
|
egprs2->es_p = rlc->es_p;
|
|
|
|
egprs2->rrbp = rlc->rrbp;
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs2->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
|
|
|
egprs2->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
2016-02-02 10:48:37 +00:00
|
|
|
egprs2->pr = rlc->pr;
|
|
|
|
egprs2->cps = rlc->cps;
|
|
|
|
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs2->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
|
|
|
egprs2->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
|
|
|
egprs2->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
2016-02-02 10:48:37 +00:00
|
|
|
|
|
|
|
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
|
|
|
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
|
2016-02-03 17:35:27 +00:00
|
|
|
offs = rlc->data_offs_bits[0] / 8;
|
|
|
|
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 6);
|
2016-02-02 10:48:37 +00:00
|
|
|
e_fbi_header <<= 4;
|
2016-02-03 17:35:27 +00:00
|
|
|
data[offs] = (data[offs] & 0b11001111) | e_fbi_header;
|
2016-02-02 10:48:37 +00:00
|
|
|
break;
|
|
|
|
|
2019-03-13 16:14:13 +00:00
|
|
|
case HEADER_EGPRS_DATA_TYPE_3:
|
2016-01-08 09:07:53 +00:00
|
|
|
egprs3 = static_cast<struct gprs_rlc_dl_header_egprs_3 *>
|
|
|
|
((void *)data);
|
|
|
|
|
|
|
|
egprs3->usf = rlc->usf;
|
|
|
|
egprs3->es_p = rlc->es_p;
|
|
|
|
egprs3->rrbp = rlc->rrbp;
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs3->tfi_hi = rlc->tfi >> 0; /* 1 bit LSB */
|
|
|
|
egprs3->tfi_lo = rlc->tfi >> 1; /* 4 bits */
|
2016-01-08 09:07:53 +00:00
|
|
|
egprs3->pr = rlc->pr;
|
|
|
|
egprs3->cps = rlc->cps;
|
|
|
|
|
2016-07-12 00:05:19 +00:00
|
|
|
egprs3->bsn1_hi = rlc->block_info[0].bsn >> 0; /* 2 bits LSB */
|
|
|
|
egprs3->bsn1_mid = rlc->block_info[0].bsn >> 2; /* 8 bits */
|
|
|
|
egprs3->bsn1_lo = rlc->block_info[0].bsn >> 10; /* 1 bit */
|
2016-01-08 09:07:53 +00:00
|
|
|
|
|
|
|
egprs3->spb = rlc->block_info[0].spb;
|
|
|
|
|
|
|
|
e_fbi_header = rlc->block_info[0].e ? 0x01 : 0;
|
|
|
|
e_fbi_header |= rlc->block_info[0].cv == 0 ? 0x02 : 0; /* FBI */
|
2016-02-03 17:35:27 +00:00
|
|
|
offs = rlc->data_offs_bits[0] / 8;
|
|
|
|
OSMO_ASSERT(rlc->data_offs_bits[0] % 8 == 1);
|
2016-01-08 09:07:53 +00:00
|
|
|
e_fbi_header <<= 7;
|
2016-02-03 17:35:27 +00:00
|
|
|
data[offs-1] = (data[offs-1] & 0b01111111) | (e_fbi_header >> 0);
|
|
|
|
data[offs] = (data[offs] & 0b11111110) | (e_fbi_header >> 8);
|
2016-01-08 09:07:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOGP(DRLCMACDL, LOGL_ERROR,
|
|
|
|
"Encoding of uplink %s data blocks not yet supported.\n",
|
2019-03-05 13:59:03 +00:00
|
|
|
mcs_name(cs));
|
2016-01-08 09:07:53 +00:00
|
|
|
return -ENOTSUP;
|
|
|
|
};
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Copy LSB bitstream RLC data block from byte aligned buffer.
|
|
|
|
*
|
|
|
|
* Note that the bitstream is encoded in LSB first order, so the two octets
|
|
|
|
* 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3
|
|
|
|
* (LSB has bit position 1). This is a different order than the one used by
|
|
|
|
* CSN.1.
|
|
|
|
*
|
|
|
|
* \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise
|
|
|
|
* \param src A pointer to the start of the RLC block (incl. the header)
|
|
|
|
* \param buffer A data area of a least the size of the RLC block
|
|
|
|
* \returns the number of bytes copied
|
|
|
|
*/
|
|
|
|
unsigned int Encoding::rlc_copy_from_aligned_buffer(
|
|
|
|
const struct gprs_rlc_data_info *rlc,
|
|
|
|
unsigned int data_block_idx,
|
|
|
|
uint8_t *dst, const uint8_t *buffer)
|
|
|
|
{
|
|
|
|
unsigned int hdr_bytes;
|
|
|
|
unsigned int extra_bits;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
uint8_t c, last_c;
|
|
|
|
const uint8_t *src;
|
|
|
|
const struct gprs_rlc_data_block_info *rdbi;
|
|
|
|
|
|
|
|
OSMO_ASSERT(data_block_idx < rlc->num_data_blocks);
|
|
|
|
rdbi = &rlc->block_info[data_block_idx];
|
|
|
|
|
|
|
|
hdr_bytes = rlc->data_offs_bits[data_block_idx] / 8;
|
|
|
|
extra_bits = (rlc->data_offs_bits[data_block_idx] % 8);
|
|
|
|
|
|
|
|
if (extra_bits == 0) {
|
|
|
|
/* It is aligned already */
|
|
|
|
memmove(dst + hdr_bytes, buffer, rdbi->data_len);
|
|
|
|
return rdbi->data_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = buffer;
|
|
|
|
dst = dst + hdr_bytes;
|
|
|
|
last_c = *dst << (8 - extra_bits);
|
|
|
|
|
|
|
|
for (i = 0; i < rdbi->data_len; i++) {
|
|
|
|
c = src[i];
|
|
|
|
*(dst++) = (last_c >> (8 - extra_bits)) | (c << extra_bits);
|
|
|
|
last_c = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* overwrite the lower extra_bits */
|
|
|
|
*dst = (*dst & (0xff << extra_bits)) | (last_c >> (8 - extra_bits));
|
|
|
|
|
|
|
|
return rdbi->data_len;
|
|
|
|
}
|
2016-01-12 10:58:13 +00:00
|
|
|
|
2016-05-30 17:10:48 +00:00
|
|
|
/*!
|
|
|
|
* \brief (GPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
|
|
|
|
* \param rdbi rlc/mac block info
|
|
|
|
* \param llc llc pdu
|
|
|
|
* \param offset given offset within the rlc/mac block
|
|
|
|
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
|
|
|
* \param data_block buffer holds rlc/mac data
|
|
|
|
* \param is_final if this is the last rlc/mac within a TBF
|
2016-05-30 17:30:21 +00:00
|
|
|
* \param count_payload if not NULL save the written size of payload in bytes into it
|
2016-05-30 17:10:48 +00:00
|
|
|
* \return the state of the rlc/mac like if there is more space for another chunk
|
|
|
|
*/
|
2016-01-13 09:51:25 +00:00
|
|
|
static Encoding::AppendResult rlc_data_to_dl_append_gprs(
|
2016-01-12 10:58:13 +00:00
|
|
|
struct gprs_rlc_data_block_info *rdbi,
|
|
|
|
gprs_llc *llc, int *offset, int *num_chunks,
|
2016-05-30 17:30:21 +00:00
|
|
|
uint8_t *data_block, bool is_final, int *count_payload)
|
2016-01-12 10:58:13 +00:00
|
|
|
{
|
|
|
|
int chunk;
|
|
|
|
int space;
|
|
|
|
struct rlc_li_field *li;
|
|
|
|
uint8_t *delimiter, *data, *e_pointer;
|
|
|
|
|
|
|
|
data = data_block + *offset;
|
|
|
|
delimiter = data_block + *num_chunks;
|
|
|
|
e_pointer = (*num_chunks ? delimiter - 1 : NULL);
|
|
|
|
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
chunk = llc_chunk_size(llc);
|
2016-01-12 10:58:13 +00:00
|
|
|
space = rdbi->data_len - *offset;
|
|
|
|
|
|
|
|
/* if chunk will exceed block limit */
|
|
|
|
if (chunk > space) {
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
|
|
|
"larger than space (%d) left in block: copy "
|
|
|
|
"only remaining space, and we are done\n",
|
|
|
|
chunk, space);
|
2020-11-27 13:51:27 +00:00
|
|
|
if (e_pointer) {
|
|
|
|
/* LLC frame not finished, so there is no extension octet */
|
|
|
|
*e_pointer |= 0x02; /* set previous M bit = 1 */
|
|
|
|
}
|
2016-01-12 10:58:13 +00:00
|
|
|
/* fill only space */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, space);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = space;
|
2016-01-12 10:58:13 +00:00
|
|
|
/* return data block as message */
|
|
|
|
*offset = rdbi->data_len;
|
|
|
|
(*num_chunks)++;
|
2016-01-13 09:51:25 +00:00
|
|
|
return Encoding::AR_NEED_MORE_BLOCKS;
|
2016-01-12 10:58:13 +00:00
|
|
|
}
|
|
|
|
/* if FINAL chunk would fit precisely in space left */
|
|
|
|
if (chunk == space && is_final)
|
|
|
|
{
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
|
|
|
"would exactly fit into space (%d): because "
|
|
|
|
"this is a final block, we don't add length "
|
|
|
|
"header, and we are done\n", chunk, space);
|
|
|
|
/* block is filled, so there is no extension */
|
|
|
|
if (e_pointer)
|
|
|
|
*e_pointer |= 0x01;
|
|
|
|
/* fill space */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, space);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = space;
|
2016-01-12 10:58:13 +00:00
|
|
|
*offset = rdbi->data_len;
|
|
|
|
(*num_chunks)++;
|
|
|
|
rdbi->cv = 0;
|
2016-01-13 09:51:25 +00:00
|
|
|
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
2016-01-12 10:58:13 +00:00
|
|
|
}
|
|
|
|
/* if chunk would fit exactly in space left */
|
|
|
|
if (chunk == space) {
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
|
|
|
"would exactly fit into space (%d): add length "
|
|
|
|
"header with LI=0, to make frame extend to "
|
|
|
|
"next block, and we are done\n", chunk, space);
|
|
|
|
/* make space for delimiter */
|
|
|
|
if (delimiter != data)
|
|
|
|
memmove(delimiter + 1, delimiter,
|
|
|
|
data - delimiter);
|
2020-11-27 13:51:27 +00:00
|
|
|
if (e_pointer) {
|
|
|
|
*e_pointer &= 0xfe; /* set previous E bit = 0 */
|
|
|
|
*e_pointer |= 0x02; /* set previous M bit = 1 */
|
|
|
|
}
|
2016-01-12 10:58:13 +00:00
|
|
|
data++;
|
|
|
|
(*offset)++;
|
|
|
|
space--;
|
|
|
|
/* add LI with 0 length */
|
|
|
|
li = (struct rlc_li_field *)delimiter;
|
|
|
|
li->e = 1; /* not more extension */
|
|
|
|
li->m = 0; /* shall be set to 0, in case of li = 0 */
|
|
|
|
li->li = 0; /* chunk fills the complete space */
|
|
|
|
rdbi->e = 0; /* 0: extensions present */
|
|
|
|
// no need to set e_pointer nor increase delimiter
|
|
|
|
/* fill only space, which is 1 octet less than chunk */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, space);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = space;
|
2016-01-12 10:58:13 +00:00
|
|
|
/* return data block as message */
|
|
|
|
*offset = rdbi->data_len;
|
|
|
|
(*num_chunks)++;
|
2016-01-13 09:51:25 +00:00
|
|
|
return Encoding::AR_NEED_MORE_BLOCKS;
|
2016-01-12 10:58:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
|
|
|
|
"than remaining space (%d): add length header to "
|
2020-11-13 12:46:27 +00:00
|
|
|
"delimit LLC frame\n", chunk, space);
|
2016-01-12 10:58:13 +00:00
|
|
|
/* the LLC frame chunk ends in this block */
|
|
|
|
/* make space for delimiter */
|
|
|
|
if (delimiter != data)
|
|
|
|
memmove(delimiter + 1, delimiter, data - delimiter);
|
2020-11-27 13:51:27 +00:00
|
|
|
if (e_pointer) {
|
|
|
|
*e_pointer &= 0xfe; /* set previous E bit = 0 */
|
|
|
|
*e_pointer |= 0x02; /* set previous M bit = 1 */
|
|
|
|
}
|
2016-01-12 10:58:13 +00:00
|
|
|
data++;
|
|
|
|
(*offset)++;
|
|
|
|
space--;
|
|
|
|
/* add LI to delimit frame */
|
|
|
|
li = (struct rlc_li_field *)delimiter;
|
2020-11-27 13:51:27 +00:00
|
|
|
li->e = 1; /* not more extension, maybe set later */
|
2016-01-12 10:58:13 +00:00
|
|
|
li->m = 0; /* will be set later, if there is more LLC data */
|
|
|
|
li->li = chunk; /* length of chunk */
|
|
|
|
rdbi->e = 0; /* 0: extensions present */
|
|
|
|
(*num_chunks)++;
|
|
|
|
/* copy (rest of) LLC frame to space and reset later */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, chunk);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = chunk;
|
2016-01-12 10:58:13 +00:00
|
|
|
data += chunk;
|
|
|
|
space -= chunk;
|
|
|
|
(*offset) += chunk;
|
|
|
|
/* if we have more data and we have space left */
|
2020-11-27 13:51:27 +00:00
|
|
|
if (space > 0 && !is_final)
|
2016-01-13 09:51:25 +00:00
|
|
|
return Encoding::AR_COMPLETED_SPACE_LEFT;
|
2020-11-27 13:51:27 +00:00
|
|
|
|
2016-01-12 10:58:13 +00:00
|
|
|
/* if we don't have more LLC frames */
|
|
|
|
if (is_final) {
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
|
|
|
|
"done.\n");
|
|
|
|
rdbi->cv = 0;
|
2016-01-13 09:51:25 +00:00
|
|
|
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
2016-01-12 10:58:13 +00:00
|
|
|
}
|
|
|
|
/* we have no space left */
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
|
|
|
|
"done.\n");
|
2016-01-13 09:51:25 +00:00
|
|
|
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
|
|
|
}
|
|
|
|
|
2016-05-30 17:10:48 +00:00
|
|
|
/*!
|
|
|
|
* \brief (EGPRS) put llc pdu into an rlc/mac block. fragment the llc pdu if needed
|
|
|
|
* \param rdbi rlc/mac block info
|
|
|
|
* \param llc llc pdu
|
|
|
|
* \param offset given offset within the rlc/mac block
|
|
|
|
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
|
|
|
* \param data_block buffer holds rlc/mac data
|
|
|
|
* \param is_final if this is the last rlc/mac within a TBF
|
2016-05-30 17:30:21 +00:00
|
|
|
* \param count_payload if not NULL save the written size of payload in bytes into it
|
2016-05-30 17:10:48 +00:00
|
|
|
* \return the state of the rlc/mac like if there is more space for another chunk
|
|
|
|
*/
|
2016-01-13 10:28:10 +00:00
|
|
|
static Encoding::AppendResult rlc_data_to_dl_append_egprs(
|
|
|
|
struct gprs_rlc_data_block_info *rdbi,
|
|
|
|
gprs_llc *llc, int *offset, int *num_chunks,
|
|
|
|
uint8_t *data_block,
|
2016-05-30 17:30:21 +00:00
|
|
|
bool is_final, int *count_payload)
|
2016-01-13 10:28:10 +00:00
|
|
|
{
|
|
|
|
int chunk;
|
|
|
|
int space;
|
|
|
|
struct rlc_li_field_egprs *li;
|
|
|
|
struct rlc_li_field_egprs *prev_li;
|
|
|
|
uint8_t *delimiter, *data;
|
|
|
|
|
|
|
|
data = data_block + *offset;
|
|
|
|
delimiter = data_block + *num_chunks;
|
|
|
|
prev_li = (struct rlc_li_field_egprs *)
|
|
|
|
(*num_chunks ? delimiter - 1 : NULL);
|
|
|
|
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
chunk = llc_chunk_size(llc);
|
2016-01-13 10:28:10 +00:00
|
|
|
space = rdbi->data_len - *offset;
|
|
|
|
|
|
|
|
/* if chunk will exceed block limit */
|
|
|
|
if (chunk > space) {
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
|
|
|
"larger than space (%d) left in block: copy "
|
|
|
|
"only remaining space, and we are done\n",
|
|
|
|
chunk, space);
|
|
|
|
/* fill only space */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, space);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = space;
|
2016-01-13 10:28:10 +00:00
|
|
|
/* return data block as message */
|
|
|
|
*offset = rdbi->data_len;
|
|
|
|
(*num_chunks)++;
|
|
|
|
return Encoding::AR_NEED_MORE_BLOCKS;
|
|
|
|
}
|
|
|
|
/* if FINAL chunk would fit precisely in space left */
|
|
|
|
if (chunk == space && is_final)
|
|
|
|
{
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
|
|
|
"would exactly fit into space (%d): because "
|
|
|
|
"this is a final block, we don't add length "
|
|
|
|
"header, and we are done\n", chunk, space);
|
|
|
|
/* fill space */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, space);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = space;
|
2016-01-13 10:28:10 +00:00
|
|
|
*offset = rdbi->data_len;
|
|
|
|
(*num_chunks)++;
|
|
|
|
rdbi->cv = 0;
|
|
|
|
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
|
|
|
}
|
|
|
|
/* if chunk would fit exactly in space left */
|
|
|
|
if (chunk == space) {
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
|
|
|
|
"would exactly fit into space (%d): just copy "
|
|
|
|
"it, and we are done. The next block will have "
|
|
|
|
"to start with an empty chunk\n",
|
|
|
|
chunk, space);
|
|
|
|
/* fill space */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, space);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = space;
|
2016-01-13 10:28:10 +00:00
|
|
|
*offset = rdbi->data_len;
|
|
|
|
(*num_chunks)++;
|
|
|
|
return Encoding::AR_NEED_MORE_BLOCKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
|
|
|
|
"than remaining space (%d): add length header to "
|
|
|
|
"to delimit LLC frame\n", chunk, space);
|
|
|
|
/* the LLC frame chunk ends in this block */
|
|
|
|
/* make space for delimiter */
|
|
|
|
|
|
|
|
if (delimiter != data)
|
|
|
|
memmove(delimiter + 1, delimiter, data - delimiter);
|
|
|
|
|
|
|
|
data += 1;
|
|
|
|
(*offset) += 1;
|
|
|
|
space -= 1;
|
|
|
|
/* add LI to delimit frame */
|
|
|
|
li = (struct rlc_li_field_egprs *)delimiter;
|
2020-11-27 13:51:27 +00:00
|
|
|
li->e = 1; /* not more extension, maybe set later */
|
2016-01-13 10:28:10 +00:00
|
|
|
li->li = chunk; /* length of chunk */
|
|
|
|
/* tell previous extension header about the new one */
|
|
|
|
if (prev_li)
|
|
|
|
prev_li->e = 0;
|
|
|
|
rdbi->e = 0; /* 0: extensions present */
|
|
|
|
delimiter++;
|
|
|
|
prev_li = li;
|
|
|
|
(*num_chunks)++;
|
|
|
|
/* copy (rest of) LLC frame to space and reset later */
|
Convert GprsMS and helpers classes to C
As we integrate osmo-pcu more and more with libosmocore features, it
becomes really hard to use them since libosmocore relies heavily on C
specific compilation features, which are not available in old C++
compilers (such as designated initializers for complex types in FSMs).
GprsMs is right now a quite simple object since initial design of
osmo-pcu made it optional and most of the logic was placed and stored
duplicated in TBF objects. However, that's changing as we introduce more
features, with the GprsMS class getting more weight. Hence, let's move
it now to be a C struct in order to be able to easily use libosmocore
features there, such as FSMs.
Some helper classes which GprsMs uses are also mostly move to C since
they are mostly structs with methods, so there's no point in having
duplicated APIs for C++ and C for such simple cases.
For some more complex classes, like (ul_,dl_)tbf, C API bindings are
added where needed so that GprsMs can use functionalitites from that
class. Most of those APIs can be kept afterwards and drop the C++ ones
since they provide no benefit in general.
Change-Id: I0b50e3367aaad9dcada76da97b438e452c8b230c
2020-12-16 14:59:45 +00:00
|
|
|
llc_consume_data(llc, data, chunk);
|
2016-05-30 17:30:21 +00:00
|
|
|
if (count_payload)
|
|
|
|
*count_payload = chunk;
|
2016-01-13 10:28:10 +00:00
|
|
|
data += chunk;
|
|
|
|
space -= chunk;
|
|
|
|
(*offset) += chunk;
|
|
|
|
/* if we have more data and we have space left */
|
2020-11-27 13:51:27 +00:00
|
|
|
if (!is_final) {
|
|
|
|
if (space > 0) {
|
2016-01-13 10:28:10 +00:00
|
|
|
return Encoding::AR_COMPLETED_SPACE_LEFT;
|
2020-11-27 13:51:27 +00:00
|
|
|
} else {
|
|
|
|
/* we have no space left */
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
|
|
|
|
"done.\n");
|
|
|
|
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
|
|
|
}
|
|
|
|
} else {
|
2016-01-13 10:28:10 +00:00
|
|
|
/* we don't have more LLC frames */
|
2020-11-27 13:51:27 +00:00
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we are done.\n");
|
2016-01-13 10:28:10 +00:00
|
|
|
rdbi->cv = 0;
|
2020-11-27 13:51:27 +00:00
|
|
|
if (space > 0)
|
|
|
|
Encoding::rlc_data_to_dl_append_egprs_li_padding(rdbi,
|
|
|
|
offset,
|
|
|
|
num_chunks,
|
|
|
|
data_block);
|
2016-01-13 10:28:10 +00:00
|
|
|
return Encoding::AR_COMPLETED_BLOCK_FILLED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-30 17:10:48 +00:00
|
|
|
/*!
|
|
|
|
* \brief Encoding::rlc_data_to_dl_append
|
|
|
|
* \param rdbi rlc/mac block info
|
|
|
|
* \param cs the coding scheme to use
|
|
|
|
* \param llc llc pdu
|
|
|
|
* \param offset given offset within the rlc/mac block
|
|
|
|
* \param num_chunks count the chunks (llc pdu data) within rlc/mac
|
|
|
|
* \param data_block buffer holds rlc/mac data
|
|
|
|
* \param is_final if this is the last rlc/mac within a TBF
|
2016-05-30 17:30:21 +00:00
|
|
|
* \param count_payload if not NULL save the written size of payload in bytes into it
|
2016-05-30 17:10:48 +00:00
|
|
|
* \return the state of the rlc/mac like if there is more space for another chunk
|
|
|
|
*/
|
2016-01-13 09:51:25 +00:00
|
|
|
Encoding::AppendResult Encoding::rlc_data_to_dl_append(
|
2020-05-18 09:35:35 +00:00
|
|
|
struct gprs_rlc_data_block_info *rdbi, enum CodingScheme cs,
|
2016-01-13 09:51:25 +00:00
|
|
|
gprs_llc *llc, int *offset, int *num_chunks,
|
2016-05-30 17:30:21 +00:00
|
|
|
uint8_t *data_block, bool is_final, int *count_payload)
|
2016-01-13 09:51:25 +00:00
|
|
|
{
|
2019-03-25 15:32:50 +00:00
|
|
|
if (mcs_is_gprs(cs))
|
2016-01-13 09:51:25 +00:00
|
|
|
return rlc_data_to_dl_append_gprs(rdbi,
|
2016-05-30 17:30:21 +00:00
|
|
|
llc, offset, num_chunks, data_block, is_final,
|
|
|
|
count_payload);
|
2016-01-13 09:51:25 +00:00
|
|
|
|
2019-03-25 15:32:50 +00:00
|
|
|
if (mcs_is_edge(cs))
|
2016-01-13 10:28:10 +00:00
|
|
|
return rlc_data_to_dl_append_egprs(rdbi,
|
2016-05-30 17:30:21 +00:00
|
|
|
llc, offset, num_chunks, data_block, is_final,
|
|
|
|
count_payload);
|
2016-01-13 10:28:10 +00:00
|
|
|
|
2016-01-13 09:51:25 +00:00
|
|
|
LOGP(DRLCMACDL, LOGL_ERROR, "%s data block encoding not implemented\n",
|
2019-03-05 13:59:03 +00:00
|
|
|
mcs_name(cs));
|
2020-11-04 16:53:07 +00:00
|
|
|
OSMO_ASSERT(mcs_is_valid(cs));
|
2016-01-13 09:51:25 +00:00
|
|
|
|
|
|
|
return AR_NEED_MORE_BLOCKS;
|
2016-01-12 10:58:13 +00:00
|
|
|
}
|
2020-11-27 13:51:27 +00:00
|
|
|
|
|
|
|
void Encoding::rlc_data_to_dl_append_egprs_li_padding(
|
|
|
|
const struct gprs_rlc_data_block_info *rdbi,
|
|
|
|
int *offset, int *num_chunks, uint8_t *data_block)
|
|
|
|
{
|
|
|
|
struct rlc_li_field_egprs *li;
|
|
|
|
struct rlc_li_field_egprs *prev_li;
|
|
|
|
uint8_t *delimiter, *data;
|
|
|
|
|
|
|
|
LOGP(DRLCMACDL, LOGL_DEBUG, "Adding LI=127 to signal padding\n");
|
|
|
|
|
|
|
|
data = data_block + *offset;
|
|
|
|
delimiter = data_block + *num_chunks;
|
|
|
|
prev_li = (struct rlc_li_field_egprs *)(*num_chunks ? delimiter - 1 : NULL);
|
|
|
|
|
|
|
|
/* we don't have more LLC frames */
|
|
|
|
/* We will have to add another chunk with filling octets */
|
|
|
|
|
|
|
|
if (delimiter != data)
|
|
|
|
memmove(delimiter + 1, delimiter, data - delimiter);
|
|
|
|
|
|
|
|
/* set filling bytes extension */
|
|
|
|
li = (struct rlc_li_field_egprs *)delimiter;
|
|
|
|
li->e = 1;
|
|
|
|
li->li = 127;
|
|
|
|
|
|
|
|
/* tell previous extension header about the new one */
|
|
|
|
if (prev_li)
|
|
|
|
prev_li->e = 0;
|
|
|
|
|
|
|
|
(*num_chunks)++;
|
|
|
|
*offset = rdbi->data_len;
|
|
|
|
}
|
2016-11-11 11:45:10 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Refer 44.060 version 7.27.0 Release 7
|
|
|
|
* section 7.1.3.2.1 On receipt of a PACKET RESOURCE REQUEST message
|
|
|
|
* 8.1.2.5 Establishment of uplink TBF
|
|
|
|
*/
|
2021-07-27 10:27:08 +00:00
|
|
|
void write_packet_access_reject(struct bitvec *dest, uint32_t tlli, unsigned long t3172_ms)
|
2016-11-11 11:45:10 +00:00
|
|
|
{
|
|
|
|
unsigned wp = 0;
|
|
|
|
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0x1, 2); // Payload Type
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 2); // Uplink block with TDMA FN
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1); // No Polling Bit
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 3); // Uplink state flag
|
|
|
|
bitvec_write_field(dest, &wp,
|
2016-11-11 11:45:10 +00:00
|
|
|
MT_PACKET_ACCESS_REJECT, 6); // MESSAGE TYPE
|
2017-02-04 02:10:08 +00:00
|
|
|
bitvec_write_field(dest, &wp, 0, 2); // fixed 00
|
|
|
|
bitvec_write_field(dest, &wp, 0x0, 1); // TLLI / G-RNTI : bit (32)
|
|
|
|
bitvec_write_field(dest, &wp, tlli, 32); // CONTENTION_RESOLUTION_TLLI
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1); // WAIT_INDICATION size in seconds
|
2021-04-26 15:49:05 +00:00
|
|
|
|
|
|
|
/* WAIT_INDICATION, WAIT_INDICATION_SIZE */
|
|
|
|
if (t3172_ms / 20 <= 255) { /* In units of 20 milliseconds */
|
|
|
|
bitvec_write_field(dest, &wp, t3172_ms/20, 8);
|
|
|
|
bitvec_write_field(dest, &wp, 1, 1);
|
|
|
|
} else { /* value too big to fit in ms, do it in seconds */
|
|
|
|
bitvec_write_field(dest, &wp, t3172_ms/1000, 8);
|
|
|
|
bitvec_write_field(dest, &wp, 0, 1);
|
|
|
|
}
|
2016-11-11 11:45:10 +00:00
|
|
|
}
|
Introduce NACC support
A new nacc_fsm is introduced per MS object, with its partner priv
structure struct nacc_fsm_ctx, which exists and is available in the MS
object only during the duration of the NACC procedure.
The NACC context is created on an MS whenever a Pkt Cell Change
Notification is received on Uplink RLCMAC, which asks for neighbor
information of a given ARFCN+BSIC.
First, the target ARFCN+BSIC needs to be translated into a CGI-PS
(RAC+CI) address. That's done by asking the BSC through the Neighbour
Resolution Service available in osmo-bsc using the CTRL interface.
Once the CGI-PS of the target cell is known, PCU starts a RIM RAN-INFO
request against the SGSN (which will route the request as needed), and
wait for a response containing the SI bits from the target cell.
After the SI are received, the scheduler is instructed to eventually
poll a TBF for the MS originating the CCN, so that we can send the SI
encapsulated into multiple Packet Neighbor Cell Data messages on the
downlink.
One all the SI bits are sent, the scheduler is instructed to send a
Packet Cell Change Continue message.
Once the message above has been sent, the FSM autodestroys itself.
Caches are also introduced in this patch which allows for re-using
recently known translations ARFCN+BSIC -> CGI-PS and CGI-PS -> SI_INFO
respectively.
Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca
2021-01-21 17:46:13 +00:00
|
|
|
|
|
|
|
void write_packet_neighbour_cell_data(RlcMacDownlink_t *block,
|
|
|
|
bool tfi_is_dl, uint8_t tfi, uint8_t container_id,
|
|
|
|
uint8_t container_idx, PNCDContainer_t *container)
|
|
|
|
{
|
|
|
|
|
|
|
|
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
|
|
|
|
block->RRBP = 0; // 0: N+13
|
2021-02-01 15:26:29 +00:00
|
|
|
block->SP = 0; // RRBP field is not valid
|
Introduce NACC support
A new nacc_fsm is introduced per MS object, with its partner priv
structure struct nacc_fsm_ctx, which exists and is available in the MS
object only during the duration of the NACC procedure.
The NACC context is created on an MS whenever a Pkt Cell Change
Notification is received on Uplink RLCMAC, which asks for neighbor
information of a given ARFCN+BSIC.
First, the target ARFCN+BSIC needs to be translated into a CGI-PS
(RAC+CI) address. That's done by asking the BSC through the Neighbour
Resolution Service available in osmo-bsc using the CTRL interface.
Once the CGI-PS of the target cell is known, PCU starts a RIM RAN-INFO
request against the SGSN (which will route the request as needed), and
wait for a response containing the SI bits from the target cell.
After the SI are received, the scheduler is instructed to eventually
poll a TBF for the MS originating the CCN, so that we can send the SI
encapsulated into multiple Packet Neighbor Cell Data messages on the
downlink.
One all the SI bits are sent, the scheduler is instructed to send a
Packet Cell Change Continue message.
Once the message above has been sent, the FSM autodestroys itself.
Caches are also introduced in this patch which allows for re-using
recently known translations ARFCN+BSIC -> CGI-PS and CGI-PS -> SI_INFO
respectively.
Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca
2021-01-21 17:46:13 +00:00
|
|
|
block->USF = 0x0; // Uplink state flag
|
|
|
|
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.MESSAGE_TYPE = MT_PACKET_NEIGHBOUR_CELL_DATA;
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.PAGE_MODE = 0x0; // Normal Paging
|
|
|
|
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.Global_TFI.UnionType = tfi_is_dl; // 0=UPLINK TFI, 1=DL TFI
|
|
|
|
if (tfi_is_dl) {
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.Global_TFI.u.DOWNLINK_TFI = tfi;
|
|
|
|
} else {
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.Global_TFI.u.UPLINK_TFI = tfi;
|
|
|
|
}
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.CONTAINER_ID = container_id;
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.spare = 0;
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.CONTAINER_INDEX = container_idx;
|
|
|
|
block->u.Packet_Neighbour_Cell_Data.Container = *container;
|
|
|
|
}
|
|
|
|
|
2021-02-01 13:52:48 +00:00
|
|
|
void write_packet_cell_change_continue(RlcMacDownlink_t *block, uint8_t poll, uint8_t rrbp,
|
|
|
|
bool tfi_is_dl, uint8_t tfi, bool exist_id,
|
|
|
|
uint16_t arfcn, uint8_t bsic, uint8_t container_id)
|
Introduce NACC support
A new nacc_fsm is introduced per MS object, with its partner priv
structure struct nacc_fsm_ctx, which exists and is available in the MS
object only during the duration of the NACC procedure.
The NACC context is created on an MS whenever a Pkt Cell Change
Notification is received on Uplink RLCMAC, which asks for neighbor
information of a given ARFCN+BSIC.
First, the target ARFCN+BSIC needs to be translated into a CGI-PS
(RAC+CI) address. That's done by asking the BSC through the Neighbour
Resolution Service available in osmo-bsc using the CTRL interface.
Once the CGI-PS of the target cell is known, PCU starts a RIM RAN-INFO
request against the SGSN (which will route the request as needed), and
wait for a response containing the SI bits from the target cell.
After the SI are received, the scheduler is instructed to eventually
poll a TBF for the MS originating the CCN, so that we can send the SI
encapsulated into multiple Packet Neighbor Cell Data messages on the
downlink.
One all the SI bits are sent, the scheduler is instructed to send a
Packet Cell Change Continue message.
Once the message above has been sent, the FSM autodestroys itself.
Caches are also introduced in this patch which allows for re-using
recently known translations ARFCN+BSIC -> CGI-PS and CGI-PS -> SI_INFO
respectively.
Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca
2021-01-21 17:46:13 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
|
2021-02-01 13:52:48 +00:00
|
|
|
block->RRBP = rrbp; // RRBP (e.g. N+13)
|
|
|
|
block->SP = poll; // RRBP field is valid?
|
Introduce NACC support
A new nacc_fsm is introduced per MS object, with its partner priv
structure struct nacc_fsm_ctx, which exists and is available in the MS
object only during the duration of the NACC procedure.
The NACC context is created on an MS whenever a Pkt Cell Change
Notification is received on Uplink RLCMAC, which asks for neighbor
information of a given ARFCN+BSIC.
First, the target ARFCN+BSIC needs to be translated into a CGI-PS
(RAC+CI) address. That's done by asking the BSC through the Neighbour
Resolution Service available in osmo-bsc using the CTRL interface.
Once the CGI-PS of the target cell is known, PCU starts a RIM RAN-INFO
request against the SGSN (which will route the request as needed), and
wait for a response containing the SI bits from the target cell.
After the SI are received, the scheduler is instructed to eventually
poll a TBF for the MS originating the CCN, so that we can send the SI
encapsulated into multiple Packet Neighbor Cell Data messages on the
downlink.
One all the SI bits are sent, the scheduler is instructed to send a
Packet Cell Change Continue message.
Once the message above has been sent, the FSM autodestroys itself.
Caches are also introduced in this patch which allows for re-using
recently known translations ARFCN+BSIC -> CGI-PS and CGI-PS -> SI_INFO
respectively.
Change-Id: Id35f40d05f3e081f32fddbf1fa34cb338db452ca
2021-01-21 17:46:13 +00:00
|
|
|
block->USF = 0x0; // Uplink state flag
|
|
|
|
|
|
|
|
block->u.Packet_Cell_Change_Continue.MESSAGE_TYPE = MT_PACKET_CELL_CHANGE_CONTINUE;
|
|
|
|
block->u.Packet_Cell_Change_Continue.PAGE_MODE = 0x0; // Normal Paging
|
|
|
|
|
|
|
|
block->u.Packet_Cell_Change_Continue.Global_TFI.UnionType = tfi_is_dl; // 0=UPLINK TFI, 1=DL TFI
|
|
|
|
if (tfi_is_dl) {
|
|
|
|
block->u.Packet_Cell_Change_Continue.Global_TFI.u.DOWNLINK_TFI = tfi;
|
|
|
|
} else {
|
|
|
|
block->u.Packet_Cell_Change_Continue.Global_TFI.u.UPLINK_TFI = tfi;
|
|
|
|
}
|
|
|
|
|
|
|
|
block->u.Packet_Cell_Change_Continue.Exist_ID = exist_id;
|
|
|
|
if (exist_id) {
|
|
|
|
block->u.Packet_Cell_Change_Continue.ARFCN = arfcn;
|
|
|
|
block->u.Packet_Cell_Change_Continue.BSIC = bsic;
|
|
|
|
}
|
|
|
|
block->u.Packet_Cell_Change_Continue.CONTAINER_ID = container_id;
|
|
|
|
}
|