diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index c9cc170a0..7095cb599 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -35,6 +35,7 @@ trxcon_SOURCES += \ sched_lchan_desc.c \ sched_lchan_xcch.c \ sched_lchan_tchf.c \ + sched_lchan_tchh.c \ sched_lchan_rach.c \ sched_lchan_sch.c \ sched_mframe.c \ diff --git a/src/host/trxcon/sched_lchan_tchh.c b/src/host/trxcon/sched_lchan_tchh.c new file mode 100644 index 000000000..316f995b6 --- /dev/null +++ b/src/host/trxcon/sched_lchan_tchh.c @@ -0,0 +1,183 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2018 by Vadim Yanitskiy + * + * All Rights Reserved + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include "scheduler.h" +#include "sched_trx.h" + +static const uint8_t tch_h0_traffic_block_map[3][4] = { + /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */ + { 0, 2, 4, 6 }, + { 4, 6, 8, 10 }, + { 8, 10, 0, 2 }, +}; + +static const uint8_t tch_h1_traffic_block_map[3][4] = { + /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */ + { 1, 3, 5, 7 }, + { 5, 7, 9, 11 }, + { 9, 11, 1, 3 }, +}; + +static const uint8_t tch_h0_dl_facch_block_map[3][6] = { + /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */ + { 4, 6, 8, 10, 13, 15 }, + { 13, 15, 17, 19, 21, 23 }, + { 21, 23, 0, 2, 4, 6 }, +}; + +static const uint8_t tch_h0_ul_facch_block_map[3][6] = { + /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */ + { 0, 2, 4, 6, 8, 10 }, + { 8, 10, 13, 15, 17, 19 }, + { 17, 19, 21, 23, 0, 2 }, +}; + +static const uint8_t tch_h1_dl_facch_block_map[3][6] = { + /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */ + { 5, 7, 9, 11, 14, 16 }, + { 14, 16, 18, 20, 22, 24 }, + { 22, 24, 1, 3, 5, 7 }, +}; + +const uint8_t tch_h1_ul_facch_block_map[3][6] = { + /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */ + { 1, 3, 5, 7, 9, 11 }, + { 9, 11, 14, 16, 18, 20 }, + { 18, 20, 22, 24, 1, 3 }, +}; + +/** + * Can a TCH/H block transmission be initiated / finished + * on a given frame number and a given channel type? + * + * See GSM 05.02, clause 7, table 1 + * + * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1) + * @param fn the current frame number + * @param ul Uplink or Downlink? + * @param facch FACCH/H or traffic? + * @param start init or end of transmission? + * @return true (yes) or false (no) + */ +bool sched_tchh_block_map_fn(enum trx_lchan_type chan, + uint32_t fn, bool ul, bool facch, bool start) +{ + uint8_t fn_mf; + int i = 0; + + /* Just to be sure */ + OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1); + + /* Calculate a modulo */ + fn_mf = facch ? (fn % 26) : (fn % 13); + +#define MAP_GET_POS(map) \ + (start ? 0 : ARRAY_SIZE(map[i]) - 1) + +#define BLOCK_MAP_FN(map) \ + do { \ + if (map[i][MAP_GET_POS(map)] == fn_mf) \ + return true; \ + } while (++i < ARRAY_SIZE(map)) + + /* Choose a proper block map */ + if (facch) { + if (ul) { + if (chan == TRXC_TCHH_0) + BLOCK_MAP_FN(tch_h0_ul_facch_block_map); + else + BLOCK_MAP_FN(tch_h1_ul_facch_block_map); + } else { + if (chan == TRXC_TCHH_0) + BLOCK_MAP_FN(tch_h0_dl_facch_block_map); + else + BLOCK_MAP_FN(tch_h1_dl_facch_block_map); + } + } else { + if (chan == TRXC_TCHH_0) + BLOCK_MAP_FN(tch_h0_traffic_block_map); + else + BLOCK_MAP_FN(tch_h1_traffic_block_map); + } + + return false; +} + +/** + * Calculates a frame number of the first burst + * using given frame number of the last burst. + * + * See GSM 05.02, clause 7, table 1 + * + * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1) + * @param last_fn frame number of the last burst + * @param facch FACCH/H or traffic? + * @return either frame number of the first burst, + * or fn=last_fn if calculation failed + */ +uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan, + uint32_t last_fn, bool facch) +{ + uint8_t fn_mf, fn_diff; + int i = 0; + + /* Just to be sure */ + OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1); + + /* Calculate a modulo */ + fn_mf = facch ? (last_fn % 26) : (last_fn % 13); + +#define BLOCK_FIRST_FN(map) \ + do { \ + if (map[i][ARRAY_SIZE(map[i]) - 1] == fn_mf) { \ + fn_diff = TDMA_FN_DIFF(fn_mf, map[i][0]); \ + return TDMA_FN_SUB(last_fn, fn_diff); \ + } \ + } while (++i < ARRAY_SIZE(map)) + + /* Choose a proper block map */ + if (facch) { + if (chan == TRXC_TCHH_0) + BLOCK_FIRST_FN(tch_h0_dl_facch_block_map); + else + BLOCK_FIRST_FN(tch_h1_dl_facch_block_map); + } else { + if (chan == TRXC_TCHH_0) + BLOCK_FIRST_FN(tch_h0_traffic_block_map); + else + BLOCK_FIRST_FN(tch_h1_traffic_block_map); + } + + LOGP(DSCHD, LOGL_ERROR, "Failed to calculate TDMA " + "frame number of the first burst of %s block, " + "using the current fn=%u\n", facch ? + "FACCH/H" : "TCH/H", last_fn); + + /* Couldn't calculate the first fn, return the last */ + return last_fn; +} diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index e663bc3f3..6b160d1ca 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -248,23 +248,10 @@ static struct trx_ts_prim *prim_dequeue_tch_h(struct llist_head *queue, { struct trx_ts_prim *facch; struct trx_ts_prim *tch; - bool facch_now = false; - uint32_t fn_mf; + bool facch_now; - /* Traffic multiframe period */ - fn_mf = fn % 26; - - /* FACCH/H0 frame alignment */ - if (lchan_type == TRXC_TCHH_0) - if (fn_mf == 0 || fn_mf == 8 || fn_mf == 17) - facch_now = true; - - /* FACCH/H1 frame alignment */ - if (lchan_type == TRXC_TCHH_1) - if (fn_mf == 1 || fn_mf == 9 || fn_mf == 18) - facch_now = true; - - /* If FACCH/H is not allowed for a given frame number */ + /* May we initiate an UL FACCH/H frame transmission now? */ + facch_now = sched_tchh_facch_start(lchan_type, fn, true); if (!facch_now) /* Just dequeue a TCH/H prim */ goto no_facch; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 730923b4c..2c8b4d8f2 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -323,3 +323,19 @@ int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts, int bit_error_count, bool dec_failed, bool traffic); int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts, struct trx_lchan_state *lchan, uint32_t fn, bool traffic); + +/* Interleaved TCH/H block TDMA frame mapping */ +uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan, + uint32_t last_fn, bool facch); +bool sched_tchh_block_map_fn(enum trx_lchan_type chan, + uint32_t fn, bool ul, bool facch, bool start); + +#define sched_tchh_traffic_start(chan, fn, ul) \ + sched_tchh_block_map_fn(chan, fn, ul, 0, 1) +#define sched_tchh_traffic_end(chan, fn, ul) \ + sched_tchh_block_map_fn(chan, fn, ul, 0, 0) + +#define sched_tchh_facch_start(chan, fn, ul) \ + sched_tchh_block_map_fn(chan, fn, ul, 1, 1) +#define sched_tchh_facch_end(chan, fn, ul) \ + sched_tchh_block_map_fn(chan, fn, ul, 1, 0) diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h index fccf7d23a..7ab17ab55 100644 --- a/src/host/trxcon/scheduler.h +++ b/src/host/trxcon/scheduler.h @@ -17,6 +17,10 @@ ((a + GSM_HYPERFRAME - b) % GSM_HYPERFRAME) #define TDMA_FN_INC(fn) \ TDMA_FN_SUM(fn, 1) +#define TDMA_FN_MIN(a, b) \ + (a < b ? a : b) +#define TDMA_FN_DIFF(a, b) \ + TDMA_FN_MIN(TDMA_FN_SUB(a, b), TDMA_FN_SUB(b, a)) enum tdma_sched_clck_state { SCH_CLCK_STATE_WAIT,