blob: e4a761da3cadbd61227ae28f1256b0cac270c24c [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file gsm0502.c
2 * Paging helper code */
3/*
4 * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
Sylvain Munaut63bee452020-05-14 03:02:26 +07005 * (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
6 *
Harald Welteea19c972011-06-26 14:47:16 +02007 * All Rights Reserved
8 *
Harald Weltee08da972017-11-13 01:00:26 +09009 * SPDX-License-Identifier: GPL-2.0+
10 *
Harald Welteea19c972011-06-26 14:47:16 +020011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#include <stdint.h>
27
28#include <osmocom/gsm/protocol/gsm_04_08.h>
29#include <osmocom/gsm/gsm0502.h>
30#include <osmocom/gsm/gsm48.h>
31#include <osmocom/gsm/rsl.h>
Philipp Maier69e00cc2019-10-09 13:38:38 +020032#include <osmocom/gsm/gsm_utils.h>
33#include <osmocom/core/logging.h>
34#include <inttypes.h>
Harald Welteea19c972011-06-26 14:47:16 +020035
36unsigned int
Pau Espin Pedrol6ca0a432022-11-24 17:09:03 +010037gsm0502_calc_paging_group(const struct gsm48_control_channel_descr *chan_desc, uint64_t imsi)
Harald Welteea19c972011-06-26 14:47:16 +020038{
39 int ccch_conf;
40 int bs_cc_chans;
41 int blocks;
42 unsigned int group;
43
44 ccch_conf = chan_desc->ccch_conf;
45 bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf);
46 /* code word + 2, as 2 channels equals 0x0 */
47 blocks = gsm48_number_of_paging_subchannels(chan_desc);
48 group = gsm0502_get_paging_group(imsi, bs_cc_chans, blocks);
49
50 return group;
51}
Philipp Maier69e00cc2019-10-09 13:38:38 +020052
53/* Clause 7 Table 1 of 5 Mapping of logical channels onto physical channels */
54#define TCH_REPEAT_LENGTH 13
55#define FACCH_F_REPEAT_LENGTH 13
56#define FACCH_H_REPEAT_LENGTH 26
57
58static const uint8_t gsm0502_tch_f_traffic_block_map[3][8] = {
59 {0, 1, 2, 3, 4, 5, 6, 7},
60 {4, 5, 6, 7, 8, 9, 10, 11},
61 {8, 9, 10, 11, 0, 1, 2, 3}
62};
63
64static const uint8_t gsm0502_tch_h0_traffic_block_map[3][4] = {
65 {0, 2, 4, 6},
66 {4, 6, 8, 10},
67 {8, 10, 0, 2}
68};
69
70static const uint8_t gsm0502_tch_h1_traffic_block_map[3][4] = {
71 {1, 3, 5, 7},
72 {5, 7, 9, 11},
73 {9, 11, 1, 3}
74};
75
76static const uint8_t gsm0502_tch_f_facch_block_map[3][8] = {
77 {0, 1, 2, 3, 4, 5, 6, 7},
78 {4, 5, 6, 7, 8, 9, 10, 11},
79 {8, 9, 10, 11, 0, 1, 2, 3}
80};
81
82static const uint8_t gsm0502_tch_h0_facch_block_map[3][6] = {
83 {0, 2, 4, 6, 8, 10},
84 {8, 10, 13, 15, 17, 19},
85 {17, 19, 21, 23, 0, 2}
86};
87
88static const uint8_t gsm0502_tch_h1_facch_block_map[3][6] = {
89 {1, 3, 5, 7, 9, 11},
90 {9, 11, 14, 16, 18, 20},
91 {18, 20, 22, 24, 1, 3}
92};
93
94/* Struct to describe a remapping function for block frame nbumbers. The member
95 * blockend describes the ending of a block for which we want to determine the
96 * beginning frame number. The member distance describes the value we need to
97 * subtract from the blockend frame number in order to get the beginning of the
98 * the block. The member cycle describes the Repeat length in TDMA frames we
99 * are dealing with. For traffic channels this is always 13, for control
100 * channels it is different. The member len simply defines amount of
101 * blockendings and distances we store in the remap table */
102struct fn_remap_table {
103 unsigned int cycle;
104 unsigned int len;
105 uint8_t blockend[8];
106 uint8_t distance[8];
107};
108
109/* Memory to hold the remap tables we will automatically generate on startup */
110static struct fn_remap_table tch_f_remap_table;
111static struct fn_remap_table tch_h0_remap_table;
112static struct fn_remap_table tch_h1_remap_table;
113static struct fn_remap_table facch_f_remap_table;
114static struct fn_remap_table facch_h0_remap_table;
115static struct fn_remap_table facch_h1_remap_table;
116static struct fn_remap_table *fn_remap_table_ptr[FN_REMAP_MAX];
117
118/* Generate a remap table from a given block map. A block map lists the block
119 * layout as defined in GSM 05.02, Clause 7 Table 1 of 5, one block per row.
120 * Parameters:
121 * table: name of the remap table to output
122 * map: traffic block map input
123 * rows: length of the traffic block map
124 * cols: witdh of the traffic block map
125 * repeat: repeat length in TDMA frames (cycle) */
126#define fn_remap_table_from_traffic_block_map(table, map, rows, cols, repeat) \
127 for(i=0;i<rows;i++) { \
128 table.blockend[i] = map[i][cols-1]; \
129 if(map[i][0] <= map[i][cols-1]) \
130 table.distance[i] = map[i][cols-1] - map[i][0]; \
131 else \
132 table.distance[i] = repeat - map[i][0] + map[i][cols-1]; \
133 } \
134 table.cycle = repeat; \
135 table.len = rows;
136
137/* Automatically generate fn remap tables on startupmake */
138static __attribute__ ((constructor))
139void fn_remap_tables_build(void)
140{
141 /* Required by macro */
142 unsigned int i;
143
144 /* Generate tables */
145 fn_remap_table_from_traffic_block_map(tch_f_remap_table,
146 gsm0502_tch_f_traffic_block_map, 3, 8,
147 TCH_REPEAT_LENGTH);
148 fn_remap_table_from_traffic_block_map(tch_h0_remap_table,
149 gsm0502_tch_h0_traffic_block_map, 3, 4,
150 TCH_REPEAT_LENGTH);
151 fn_remap_table_from_traffic_block_map(tch_h1_remap_table,
152 gsm0502_tch_h1_traffic_block_map, 3, 4,
153 TCH_REPEAT_LENGTH);
154 fn_remap_table_from_traffic_block_map(facch_f_remap_table,
155 gsm0502_tch_f_facch_block_map, 3, 8,
156 FACCH_F_REPEAT_LENGTH);
157 fn_remap_table_from_traffic_block_map(facch_h0_remap_table,
158 gsm0502_tch_h0_facch_block_map, 3, 6,
159 FACCH_H_REPEAT_LENGTH);
160 fn_remap_table_from_traffic_block_map(facch_h1_remap_table,
161 gsm0502_tch_h1_facch_block_map, 3, 6,
162 FACCH_H_REPEAT_LENGTH);
163
164 fn_remap_table_ptr[FN_REMAP_TCH_F] = &tch_f_remap_table;
165 fn_remap_table_ptr[FN_REMAP_TCH_H0] = &tch_h0_remap_table;
166 fn_remap_table_ptr[FN_REMAP_TCH_H1] = &tch_h1_remap_table;
167 fn_remap_table_ptr[FN_REMAP_FACCH_F] = &facch_f_remap_table;
168 fn_remap_table_ptr[FN_REMAP_FACCH_H0] = &facch_h0_remap_table;
169 fn_remap_table_ptr[FN_REMAP_FACCH_H1] = &facch_h1_remap_table;
170}
171
172/*! Calculate the frame number of the beginning of a block.
173 * \param[in] fn frame number of the block ending.
174 * \param[in] channel channel type (see also enum fn_remap_channel).
175 * \returns frame number of the beginning of the block or input frame number if
176 * remapping was not possible. */
177uint32_t gsm0502_fn_remap(uint32_t fn, enum gsm0502_fn_remap_channel channel)
178{
179 uint8_t fn_cycle;
180 uint8_t i;
181 int sub = -1;
Philipp Maier69e00cc2019-10-09 13:38:38 +0200182 struct fn_remap_table *table;
183
184 OSMO_ASSERT(channel < ARRAY_SIZE(fn_remap_table_ptr));
185 table = fn_remap_table_ptr[(uint8_t)channel];
186
187 fn_cycle = fn % table->cycle;
188
189 for (i = 0; i < table->len; i++) {
190 if (table->blockend[i] == fn_cycle) {
191 sub = table->distance[i];
192 break;
193 }
194 }
195
196 if (sub == -1) {
Vadim Yanitskiyddb75862020-05-14 02:01:07 +0700197 LOGP(DLGLOBAL, LOGL_ERROR, "could not remap frame number!, fn=%" PRIu32 "\n", fn);
Philipp Maier69e00cc2019-10-09 13:38:38 +0200198 return fn;
199 }
200
Vadim Yanitskiy19bd12e2022-10-26 18:13:13 +0700201 return GSM_TDMA_FN_SUB(fn, sub);
Philipp Maier69e00cc2019-10-09 13:38:38 +0200202}
Sylvain Munaut63bee452020-05-14 03:02:26 +0700203
204/* Magic numbers (RNTABLE) for pseudo-random hopping sequence generation. */
205static const uint8_t rn_table[114] = {
206 48, 98, 63, 1, 36, 95, 78, 102, 94, 73,
207 0, 64, 25, 81, 76, 59, 124, 23, 104, 100,
208 101, 47, 118, 85, 18, 56, 96, 86, 54, 2,
209 80, 34, 127, 13, 6, 89, 57, 103, 12, 74,
210 55, 111, 75, 38, 109, 71, 112, 29, 11, 88,
211 87, 19, 3, 68, 110, 26, 33, 31, 8, 45,
212 82, 58, 40, 107, 32, 5, 106, 92, 62, 67,
213 77, 108, 122, 37, 60, 66, 121, 42, 51, 126,
214 117, 114, 4, 90, 43, 52, 53, 113, 120, 72,
215 16, 49, 7, 79, 119, 61, 22, 84, 9, 97,
216 91, 15, 21, 24, 46, 39, 93, 105, 65, 70,
217 125, 99, 17, 123,
218};
219
Harald Welte830b8db2020-05-14 03:21:59 +0700220/*! Hopping sequence generation as per 3GPP TS 45.002, section 6.2.3.
221 * \param[in] t GSM time (TDMA frame number, T1/T2/T3).
222 * \param[in] hsn Hopping Sequence Number.
223 * \param[in] maio Mobile Allocation Index Offset.
224 * \param[in] n number of entries in mobile allocation (arfcn table).
225 * \param[in] ma array of ARFCNs (sorted in ascending order)
226 * representing the Mobile Allocation.
227 * \returns ARFCN to use for given input parameters at time 't'
228 * or Mobile Allocation Index if ma == NULL.
229 */
Sylvain Munaut63bee452020-05-14 03:02:26 +0700230uint16_t gsm0502_hop_seq_gen(const struct gsm_time *t,
231 uint8_t hsn, uint8_t maio,
232 size_t n, const uint16_t *ma)
233{
234 unsigned int mai;
235
236 if (hsn == 0) {
237 /* cyclic hopping */
238 mai = (t->fn + maio) % n;
239 } else {
240 /* pseudo random hopping */
241 int m, mp, tp, s, pnm;
242
243 pnm = (n >> 0) | (n >> 1)
244 | (n >> 2) | (n >> 3)
245 | (n >> 4) | (n >> 5)
246 | (n >> 6);
247
248 m = t->t2 + rn_table[(hsn ^ (t->t1 & 63)) + t->t3];
249 mp = m & pnm;
250
251 if (mp < n)
252 s = mp;
253 else {
254 tp = t->t3 & pnm;
255 s = (mp + tp) % n;
256 }
257
258 mai = (s + maio) % n;
259 }
260
261 return ma ? ma[mai] : mai;
262}
Vadim Yanitskiy418d76c2023-06-22 23:00:05 +0700263
264#define CB_FCCH -1
265#define CB_SCH -2
266#define CB_BCCH -3
267#define CB_IDLE -4
268
269/* Clause 7 Table 3 and Figure 8a */
270static const int ccch_block_table[51] = {
271 CB_FCCH, CB_SCH, /* 0..1 */
272 CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */
273 0, 0, 0, 0, /* 6..9: B0 */
274 CB_FCCH, CB_SCH, /* 10..11 */
275 1, 1, 1, 1, /* 12..15: B1 */
276 2, 2, 2, 2, /* 16..19: B2 */
277 CB_FCCH, CB_SCH, /* 20..21 */
278 3, 3, 3, 3, /* 22..25: B3 */
279 4, 4, 4, 4, /* 26..29: B4 */
280 CB_FCCH, CB_SCH, /* 30..31 */
281 5, 5, 5, 5, /* 32..35: B5 */
282 6, 6, 6, 6, /* 36..39: B6 */
283 CB_FCCH, CB_SCH, /* 40..41 */
284 7, 7, 7, 7, /* 42..45: B7 */
285 8, 8, 8, 8, /* 46..49: B8 */
286 CB_IDLE /* 50: Idle */
287};
288
289/*! Calculate CCCH block number from the given TDMA frame number.
290 * \param[in] fn TDMA frame number (of first or last burst).
291 * \returns CCCH block number 0..8 or a negative value,
292 * if the given frame number cannot carry CCCH.
293 */
294int gsm0502_fn2ccch_block(uint32_t fn)
295{
Vadim Yanitskiyf0a1f412023-06-25 15:47:13 +0700296 return ccch_block_table[fn % ARRAY_SIZE(ccch_block_table)];
Vadim Yanitskiy418d76c2023-06-22 23:00:05 +0700297}