2 * OsmocomBB <-> SDR connection bridge
3 * TDMA scheduler: handlers for DL / UL bursts on logical channels
5 * (C) 2018-2021 by Vadim Yanitskiy <axilirator@gmail.com>
6 * (C) 2018 by Harald Welte <laforge@gnumonks.org>
7 * Contributions by sysmocom - s.f.m.c. GmbH
11 * 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.
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.
28 #include <osmocom/core/logging.h>
29 #include <osmocom/core/utils.h>
30 #include <osmocom/core/bits.h>
32 #include <osmocom/gsm/protocol/gsm_04_08.h>
33 #include <osmocom/gsm/gsm_utils.h>
35 #include <osmocom/coding/gsm0503_coding.h>
36 #include <osmocom/codec/codec.h>
38 #include "l1ctl_proto.h"
39 #include "scheduler.h"
40 #include "sched_trx.h"
45 static const uint8_t tch_h0_traffic_block_map
[3][4] = {
46 /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */
52 static const uint8_t tch_h1_traffic_block_map
[3][4] = {
53 /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */
59 static const uint8_t tch_h0_dl_facch_block_map
[3][6] = {
60 /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */
61 { 4, 6, 8, 10, 13, 15 },
62 { 13, 15, 17, 19, 21, 23 },
63 { 21, 23, 0, 2, 4, 6 },
66 static const uint8_t tch_h0_ul_facch_block_map
[3][6] = {
67 /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */
68 { 0, 2, 4, 6, 8, 10 },
69 { 8, 10, 13, 15, 17, 19 },
70 { 17, 19, 21, 23, 0, 2 },
73 static const uint8_t tch_h1_dl_facch_block_map
[3][6] = {
74 /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */
75 { 5, 7, 9, 11, 14, 16 },
76 { 14, 16, 18, 20, 22, 24 },
77 { 22, 24, 1, 3, 5, 7 },
80 const uint8_t tch_h1_ul_facch_block_map
[3][6] = {
81 /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */
82 { 1, 3, 5, 7, 9, 11 },
83 { 9, 11, 14, 16, 18, 20 },
84 { 18, 20, 22, 24, 1, 3 },
88 * Can a TCH/H block transmission be initiated / finished
89 * on a given frame number and a given channel type?
91 * See GSM 05.02, clause 7, table 1
93 * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1)
94 * @param fn the current frame number
95 * @param ul Uplink or Downlink?
96 * @param facch FACCH/H or traffic?
97 * @param start init or end of transmission?
98 * @return true (yes) or false (no)
100 bool sched_tchh_block_map_fn(enum trx_lchan_type chan
,
101 uint32_t fn
, bool ul
, bool facch
, bool start
)
106 /* Just to be sure */
107 OSMO_ASSERT(chan
== TRXC_TCHH_0
|| chan
== TRXC_TCHH_1
);
109 /* Calculate a modulo */
110 fn_mf
= facch
? (fn
% 26) : (fn
% 13);
112 #define MAP_GET_POS(map) \
113 (start ? 0 : ARRAY_SIZE(map[i]) - 1)
115 #define BLOCK_MAP_FN(map) \
117 if (map[i][MAP_GET_POS(map)] == fn_mf) \
119 } while (++i < ARRAY_SIZE(map))
121 /* Choose a proper block map */
124 if (chan
== TRXC_TCHH_0
)
125 BLOCK_MAP_FN(tch_h0_ul_facch_block_map
);
127 BLOCK_MAP_FN(tch_h1_ul_facch_block_map
);
129 if (chan
== TRXC_TCHH_0
)
130 BLOCK_MAP_FN(tch_h0_dl_facch_block_map
);
132 BLOCK_MAP_FN(tch_h1_dl_facch_block_map
);
135 if (chan
== TRXC_TCHH_0
)
136 BLOCK_MAP_FN(tch_h0_traffic_block_map
);
138 BLOCK_MAP_FN(tch_h1_traffic_block_map
);
145 * Calculates a frame number of the first burst
146 * using given frame number of the last burst.
148 * See GSM 05.02, clause 7, table 1
150 * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1)
151 * @param last_fn frame number of the last burst
152 * @param facch FACCH/H or traffic?
153 * @return either frame number of the first burst,
154 * or fn=last_fn if calculation failed
156 uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan
,
157 uint32_t last_fn
, bool facch
)
159 uint8_t fn_mf
, fn_diff
;
162 /* Just to be sure */
163 OSMO_ASSERT(chan
== TRXC_TCHH_0
|| chan
== TRXC_TCHH_1
);
165 /* Calculate a modulo */
166 fn_mf
= facch
? (last_fn
% 26) : (last_fn
% 13);
168 #define BLOCK_FIRST_FN(map) \
170 if (map[i][ARRAY_SIZE(map[i]) - 1] == fn_mf) { \
171 fn_diff = GSM_TDMA_FN_DIFF(fn_mf, map[i][0]); \
172 return GSM_TDMA_FN_SUB(last_fn, fn_diff); \
174 } while (++i < ARRAY_SIZE(map))
176 /* Choose a proper block map */
178 if (chan
== TRXC_TCHH_0
)
179 BLOCK_FIRST_FN(tch_h0_dl_facch_block_map
);
181 BLOCK_FIRST_FN(tch_h1_dl_facch_block_map
);
183 if (chan
== TRXC_TCHH_0
)
184 BLOCK_FIRST_FN(tch_h0_traffic_block_map
);
186 BLOCK_FIRST_FN(tch_h1_traffic_block_map
);
189 LOGP(DSCHD
, LOGL_ERROR
, "Failed to calculate TDMA "
190 "frame number of the first burst of %s block, "
191 "using the current fn=%u\n", facch
?
192 "FACCH/H" : "TCH/H", last_fn
);
194 /* Couldn't calculate the first fn, return the last */
198 int rx_tchh_fn(struct trx_instance
*trx
, struct trx_ts
*ts
,
199 struct trx_lchan_state
*lchan
, uint32_t fn
, uint8_t bid
,
200 const sbit_t
*bits
, const struct trx_meas_set
*meas
)
202 const struct trx_lchan_desc
*lchan_desc
;
203 int n_errors
= -1, n_bits_total
, rc
;
204 sbit_t
*buffer
, *offset
;
205 uint8_t l2
[128], *mask
;
208 /* Set up pointers */
209 lchan_desc
= &trx_lchan_desc
[lchan
->type
];
210 mask
= &lchan
->rx_burst_mask
;
211 buffer
= lchan
->rx_bursts
;
213 LOGP(DSCHD
, LOGL_DEBUG
, "Traffic received on %s: fn=%u ts=%u bid=%u\n",
214 lchan_desc
->name
, fn
, ts
->index
, bid
);
217 /* Align to the first burst */
221 /* Align reception of the first FACCH/H frame */
222 if (lchan
->tch_mode
== GSM48_CMODE_SIGN
) {
223 if (!sched_tchh_facch_start(lchan
->type
, fn
, 0))
225 } else { /* or TCH/H traffic frame */
226 if (!sched_tchh_traffic_start(lchan
->type
, fn
, 0))
234 /* Store the measurements */
235 sched_trx_meas_push(lchan
, meas
);
237 /* Copy burst to the end of buffer of 6 bursts */
238 offset
= buffer
+ bid
* 116 + 464;
239 memcpy(offset
, bits
+ 3, 58);
240 memcpy(offset
+ 58, bits
+ 87, 58);
242 /* Wait until the second burst */
246 /* Wait for complete set of bursts */
247 if (lchan
->tch_mode
== GSM48_CMODE_SIGN
) {
248 /* FACCH/H is interleaved over 6 bursts */
249 if ((*mask
& 0x3f) != 0x3f)
252 /* Traffic is interleaved over 4 bursts */
253 if ((*mask
& 0x0f) != 0x0f)
257 /* Skip decoding attempt in case of FACCH/H */
258 if (lchan
->dl_ongoing_facch
) {
259 lchan
->dl_ongoing_facch
= false;
260 goto bfi_shift
; /* 2/2 BFI */
263 switch (lchan
->tch_mode
) {
264 case GSM48_CMODE_SIGN
:
265 case GSM48_CMODE_SPEECH_V1
: /* HR */
266 rc
= gsm0503_tch_hr_decode(l2
, buffer
,
267 !sched_tchh_facch_end(lchan
->type
, fn
, 0),
268 &n_errors
, &n_bits_total
);
270 case GSM48_CMODE_SPEECH_AMR
: /* AMR */
272 * TODO: AMR requires a dedicated loop,
273 * which will be implemented later...
275 LOGP(DSCHD
, LOGL_ERROR
, "AMR isn't supported yet\n");
278 LOGP(DSCHD
, LOGL_ERROR
, "Invalid TCH mode: %u\n", lchan
->tch_mode
);
282 /* Shift buffer by 4 bursts for interleaving */
283 memcpy(buffer
, buffer
+ 232, 232);
284 memcpy(buffer
+ 232, buffer
+ 464, 232);
286 /* Shift burst mask */
289 /* Check decoding result */
291 /* Calculate AVG of the measurements (assuming 4 bursts) */
292 sched_trx_meas_avg(lchan
, 4);
294 LOGP(DSCHD
, LOGL_ERROR
, "Received bad %s frame (rc=%d, ber=%d/%d) at fn=%u\n",
295 lchan_desc
->name
, rc
, n_errors
, n_bits_total
, lchan
->meas_avg
.fn
);
299 } else if (rc
== GSM_MACBLOCK_LEN
) {
300 /* Skip decoding of the next 2 stolen bursts */
301 lchan
->dl_ongoing_facch
= true;
303 /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */
304 sched_trx_meas_avg(lchan
, 6);
306 /* FACCH/H received, forward to the higher layers */
307 sched_send_dt_ind(trx
, ts
, lchan
, l2
, GSM_MACBLOCK_LEN
,
308 n_errors
, false, false);
310 /* Send BFI substituting 1/2 stolen TCH frames */
311 n_errors
= -1; /* ensure fake measurements */
314 /* A good TCH frame received */
317 /* Calculate AVG of the measurements (traffic takes 4 bursts) */
318 sched_trx_meas_avg(lchan
, 4);
321 /* Send a traffic frame to the higher layers */
322 return sched_send_dt_ind(trx
, ts
, lchan
, l2
, l2_len
,
323 n_errors
, false, true);
327 memcpy(buffer
, buffer
+ 232, 232);
328 memcpy(buffer
+ 232, buffer
+ 464, 232);
330 /* Shift burst mask */
334 /* Didn't try to decode, fake measurements */
336 lchan
->meas_avg
= (struct trx_meas_set
) {
337 .fn
= sched_tchh_block_dl_first_fn(lchan
->type
, fn
, false),
342 /* No bursts => no errors */
346 /* BFI is not applicable in signalling mode */
347 if (lchan
->tch_mode
== GSM48_CMODE_SIGN
)
348 return sched_send_dt_ind(trx
, ts
, lchan
, NULL
, 0,
349 n_errors
, true, false);
351 /* Bad frame indication */
352 l2_len
= sched_bad_frame_ind(l2
, lchan
);
354 /* Send a BFI frame to the higher layers */
355 return sched_send_dt_ind(trx
, ts
, lchan
, l2
, l2_len
,
356 n_errors
, true, true);
359 int tx_tchh_fn(struct trx_instance
*trx
, struct trx_ts
*ts
,
360 struct trx_lchan_state
*lchan
,
361 struct sched_burst_req
*br
)
363 const struct trx_lchan_desc
*lchan_desc
;
364 ubit_t
*buffer
, *offset
;
370 /* Set up pointers */
371 lchan_desc
= &trx_lchan_desc
[lchan
->type
];
372 mask
= &lchan
->tx_burst_mask
;
373 buffer
= lchan
->tx_bursts
;
376 /* Align to the first burst */
383 /* Align transmission of the first FACCH/H frame */
384 if (lchan
->tch_mode
== GSM48_CMODE_SIGN
)
385 if (!sched_tchh_facch_start(lchan
->type
, br
->fn
, 1))
389 /* Shift buffer by 2 bursts back for interleaving */
390 memcpy(buffer
, buffer
+ 232, 232);
392 /* Also shift TX burst mask */
395 /* If FACCH/H blocks are still pending */
396 if (lchan
->ul_facch_blocks
> 2) {
397 memcpy(buffer
+ 232, buffer
+ 464, 232);
401 /* Check the current TCH mode */
402 switch (lchan
->tch_mode
) {
403 case GSM48_CMODE_SIGN
:
404 case GSM48_CMODE_SPEECH_V1
: /* HR */
405 l2_len
= GSM_HR_BYTES
+ 1;
407 case GSM48_CMODE_SPEECH_AMR
: /* AMR */
409 * TODO: AMR requires a dedicated loop,
410 * which will be implemented later...
412 LOGP(DSCHD
, LOGL_ERROR
, "AMR isn't supported yet, "
413 "dropping frame...\n");
415 /* Forget this primitive */
416 sched_prim_drop(lchan
);
419 LOGP(DSCHD
, LOGL_ERROR
, "Invalid TCH mode: %u, "
420 "dropping frame...\n", lchan
->tch_mode
);
422 /* Forget this primitive */
423 sched_prim_drop(lchan
);
427 /* Determine payload length */
428 if (PRIM_IS_FACCH(lchan
->prim
)) {
429 l2_len
= GSM_MACBLOCK_LEN
; /* FACCH */
430 } else if (lchan
->prim
->payload_len
!= l2_len
) {
431 LOGP(DSCHD
, LOGL_ERROR
, "Primitive has odd length %zu "
432 "(expected %zu for TCH or %u for FACCH), so dropping...\n",
433 lchan
->prim
->payload_len
, l2_len
, GSM_MACBLOCK_LEN
);
435 /* Forget this primitive */
436 sched_prim_drop(lchan
);
440 /* Encode the payload */
441 rc
= gsm0503_tch_hr_encode(buffer
, lchan
->prim
->payload
, l2_len
);
443 LOGP(DSCHD
, LOGL_ERROR
, "Failed to encode L2 payload (len=%zu): %s\n",
444 lchan
->prim
->payload_len
, osmo_hexdump(lchan
->prim
->payload
,
445 lchan
->prim
->payload_len
));
447 /* Forget this primitive */
448 sched_prim_drop(lchan
);
452 /* A FACCH/H frame occupies 6 bursts */
453 if (PRIM_IS_FACCH(lchan
->prim
))
454 lchan
->ul_facch_blocks
= 6;
457 /* Determine which burst should be sent */
458 offset
= buffer
+ br
->bid
* 116;
461 *mask
|= (1 << br
->bid
);
463 /* Choose proper TSC */
464 tsc
= sched_nb_training_bits
[trx
->tsc
];
466 /* Compose a new burst */
467 memset(br
->burst
, 0, 3); /* TB */
468 memcpy(br
->burst
+ 3, offset
, 58); /* Payload 1/2 */
469 memcpy(br
->burst
+ 61, tsc
, 26); /* TSC */
470 memcpy(br
->burst
+ 87, offset
+ 58, 58); /* Payload 2/2 */
471 memset(br
->burst
+ 145, 0, 3); /* TB */
472 br
->burst_len
= GSM_BURST_LEN
;
474 LOGP(DSCHD
, LOGL_DEBUG
, "Scheduled %s fn=%u ts=%u burst=%u\n",
475 lchan_desc
->name
, br
->fn
, ts
->index
, br
->bid
);
477 /* In case of a FACCH/H frame, one block less */
478 if (lchan
->ul_facch_blocks
)
479 lchan
->ul_facch_blocks
--;
481 if ((*mask
& 0x0f) == 0x0f) {
483 * If no more FACCH/H blocks pending,
484 * confirm data / traffic sending
486 if (!lchan
->ul_facch_blocks
)
487 sched_send_dt_conf(trx
, ts
, lchan
, br
->fn
,
488 PRIM_IS_TCH(lchan
->prim
));
490 /* Forget processed primitive */
491 sched_prim_drop(lchan
);