treewide: remove FSF address
[osmocom-bb.git] / src / host / trxcon / sched_lchan_tchh.c
blobae67de483ae23e38e3f3d5487037c799ddab74dc
1 /*
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
9 * All Rights Reserved
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.
23 #include <errno.h>
24 #include <string.h>
25 #include <stdint.h>
26 #include <stdbool.h>
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"
41 #include "logging.h"
42 #include "trx_if.h"
43 #include "l1ctl.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) */
47 { 0, 2, 4, 6 },
48 { 4, 6, 8, 10 },
49 { 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) */
54 { 1, 3, 5, 7 },
55 { 5, 7, 9, 11 },
56 { 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 },
87 /**
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)
103 uint8_t fn_mf;
104 int i = 0;
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) \
116 do { \
117 if (map[i][MAP_GET_POS(map)] == fn_mf) \
118 return true; \
119 } while (++i < ARRAY_SIZE(map))
121 /* Choose a proper block map */
122 if (facch) {
123 if (ul) {
124 if (chan == TRXC_TCHH_0)
125 BLOCK_MAP_FN(tch_h0_ul_facch_block_map);
126 else
127 BLOCK_MAP_FN(tch_h1_ul_facch_block_map);
128 } else {
129 if (chan == TRXC_TCHH_0)
130 BLOCK_MAP_FN(tch_h0_dl_facch_block_map);
131 else
132 BLOCK_MAP_FN(tch_h1_dl_facch_block_map);
134 } else {
135 if (chan == TRXC_TCHH_0)
136 BLOCK_MAP_FN(tch_h0_traffic_block_map);
137 else
138 BLOCK_MAP_FN(tch_h1_traffic_block_map);
141 return false;
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;
160 int i = 0;
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) \
169 do { \
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 */
177 if (facch) {
178 if (chan == TRXC_TCHH_0)
179 BLOCK_FIRST_FN(tch_h0_dl_facch_block_map);
180 else
181 BLOCK_FIRST_FN(tch_h1_dl_facch_block_map);
182 } else {
183 if (chan == TRXC_TCHH_0)
184 BLOCK_FIRST_FN(tch_h0_traffic_block_map);
185 else
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 */
195 return last_fn;
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;
206 size_t l2_len;
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);
216 if (*mask == 0x00) {
217 /* Align to the first burst */
218 if (bid > 0)
219 return 0;
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))
224 return 0;
225 } else { /* or TCH/H traffic frame */
226 if (!sched_tchh_traffic_start(lchan->type, fn, 0))
227 return 0;
231 /* Update mask */
232 *mask |= (1 << bid);
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 */
243 if (bid != 1)
244 return 0;
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)
250 goto bfi_shift;
251 } else {
252 /* Traffic is interleaved over 4 bursts */
253 if ((*mask & 0x0f) != 0x0f)
254 goto bfi_shift;
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);
269 break;
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");
276 return -ENOTSUP;
277 default:
278 LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode);
279 return -EINVAL;
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 */
287 *mask = *mask << 2;
289 /* Check decoding result */
290 if (rc < 4) {
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);
297 /* Send BFI */
298 goto bfi;
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 */
312 goto bfi;
313 } else {
314 /* A good TCH frame received */
315 l2_len = rc;
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);
325 bfi_shift:
326 /* Shift buffer */
327 memcpy(buffer, buffer + 232, 232);
328 memcpy(buffer + 232, buffer + 464, 232);
330 /* Shift burst mask */
331 *mask = *mask << 2;
333 bfi:
334 /* Didn't try to decode, fake measurements */
335 if (n_errors < 0) {
336 lchan->meas_avg = (struct trx_meas_set) {
337 .fn = sched_tchh_block_dl_first_fn(lchan->type, fn, false),
338 .toa256 = 0,
339 .rssi = -110,
342 /* No bursts => no errors */
343 n_errors = 0;
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;
365 const uint8_t *tsc;
366 uint8_t *mask;
367 size_t l2_len;
368 int rc;
370 /* Set up pointers */
371 lchan_desc = &trx_lchan_desc[lchan->type];
372 mask = &lchan->tx_burst_mask;
373 buffer = lchan->tx_bursts;
375 if (br->bid > 0) {
376 /* Align to the first burst */
377 if (*mask == 0x00)
378 return 0;
379 goto send_burst;
382 if (*mask == 0x00) {
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))
386 return 0;
389 /* Shift buffer by 2 bursts back for interleaving */
390 memcpy(buffer, buffer + 232, 232);
392 /* Also shift TX burst mask */
393 *mask = *mask << 2;
395 /* If FACCH/H blocks are still pending */
396 if (lchan->ul_facch_blocks > 2) {
397 memcpy(buffer + 232, buffer + 464, 232);
398 goto send_burst;
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;
406 break;
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);
417 return -ENOTSUP;
418 default:
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);
424 return -EINVAL;
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);
437 return -EINVAL;
440 /* Encode the payload */
441 rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len);
442 if (rc) {
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);
449 return -EINVAL;
452 /* A FACCH/H frame occupies 6 bursts */
453 if (PRIM_IS_FACCH(lchan->prim))
454 lchan->ul_facch_blocks = 6;
456 send_burst:
457 /* Determine which burst should be sent */
458 offset = buffer + br->bid * 116;
460 /* Update mask */
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);
494 return 0;