From 2340b88ede52d8da5ff203b97464653c6f4528d5 Mon Sep 17 00:00:00 2001 From: Ivan Kluchnikov Date: Wed, 7 May 2014 18:09:06 +0400 Subject: fix: make sysmobts tests only when sysmobts is enabled --- tests/Makefile.am | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 633b6a4a..955d8f37 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,8 @@ -SUBDIRS = paging cipher sysmobts agch misc +SUBDIRS = paging cipher agch misc + +if ENABLE_SYSMOBTS +SUBDIRS += sysmobts +endif # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac -- cgit v1.2.3 From 75f105bbb5011a04e599cbc8f053afb723582364 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 8 Jul 2013 18:41:05 +0200 Subject: Fix: Retrieve ARFCN (from OML) for TRX other than C0 --- src/common/oml.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/common/oml.c b/src/common/oml.c index 5f9c0a2b..6d778a53 100644 --- a/src/common/oml.c +++ b/src/common/oml.c @@ -590,6 +590,25 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg) trx->arfcn_num = length; } else trx->arfcn_num = 0; +#else + if (trx != trx->bts->c0 && TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) { + const uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST); + uint16_t _value; + uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST); + uint16_t arfcn; + if (length != 2) { + LOGP(DOML, LOGL_ERROR, "Expecting only one ARFCN, " + "because hopping not supported\n"); + /* FIXME: send NACK */ + return -ENOTSUP; + } + memcpy(&_value, value, 2); + arfcn = ntohs(_value); + value += 2; + if (arfcn > 1024) + return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL); + trx->arfcn = arfcn; + } #endif /* call into BTS driver to apply new attributes to hardware */ return bts_model_apply_oml(trx->bts, msg, tp_merged, NM_OC_RADIO_CARRIER, trx); -- cgit v1.2.3 From 5fa388c366b19f753462a19776a59845b7dc5540 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 8 Jul 2013 18:34:14 +0200 Subject: Fix: Process all TRX on GSM Time indication, not only C0 --- src/common/l1sap.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/common/l1sap.c b/src/common/l1sap.c index 1cb752e3..b568cf01 100644 --- a/src/common/l1sap.c +++ b/src/common/l1sap.c @@ -278,11 +278,11 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) } /* time information received from bts model */ -static int l1sap_info_time_ind(struct gsm_bts_trx *trx, +static int l1sap_info_time_ind(struct gsm_bts *bts, struct osmo_phsap_prim *l1sap, struct info_time_ind_param *info_time_ind) { - struct gsm_bts *bts = trx->bts; + struct gsm_bts_trx *trx; struct gsm_bts_role_bts *btsb = bts->role; int frames_expired = info_time_ind->fn - btsb->gsm_time.fn; @@ -297,7 +297,8 @@ static int l1sap_info_time_ind(struct gsm_bts_trx *trx, /* check if the measurement period of some lchan has ended * and pre-compute the respective measurement */ - trx_meas_check_compute(trx, info_time_ind->fn - 1); + llist_for_each_entry(trx, &bts->trx_list, list) + trx_meas_check_compute(trx, info_time_ind->fn - 1); /* increment number of RACH slots that have passed by since the * last time indication */ @@ -352,7 +353,7 @@ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx, switch (info->type) { case PRIM_INFO_TIME: - rc = l1sap_info_time_ind(trx, l1sap, &info->u.time_ind); + rc = l1sap_info_time_ind(trx->bts, l1sap, &info->u.time_ind); break; case PRIM_INFO_MEAS: rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind); -- cgit v1.2.3 From 79bc80102c4cfa2a309e1ba9e90df7df9f34a307 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 13 Jan 2014 14:46:01 +0100 Subject: Fix: Call e1inp_vty_init() before reading config file --- src/common/abis.c | 1 - src/osmo-bts-sysmo/main.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/abis.c b/src/common/abis.c index 9eb49a08..3b9bcdd2 100644 --- a/src/common/abis.c +++ b/src/common/abis.c @@ -203,7 +203,6 @@ void abis_init(struct gsm_bts *bts) g_bts = bts; oml_init(); - e1inp_vty_init(); libosmo_abis_init(NULL); osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts); diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c index b9f9fe98..72c8cfd7 100644 --- a/src/osmo-bts-sysmo/main.c +++ b/src/osmo-bts-sysmo/main.c @@ -319,6 +319,7 @@ int main(int argc, char **argv) bts = gsm_bts_alloc(tall_bts_ctx); vty_init(&bts_vty_info); + e1inp_vty_init(); bts_vty_init(bts, &bts_log_info); handle_options(argc, argv); -- cgit v1.2.3 From c64fa4f88818a63bada0e34d179fd04319de47e1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 26 Mar 2013 07:52:41 +0100 Subject: Change to new structure of multirate at gsm_data_shared.h --- src/common/amr.c | 23 +++++++++++++---------- src/common/rsl.c | 12 ++++++------ src/osmo-bts-sysmo/oml.c | 19 +++++++++++-------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/common/amr.c b/src/common/amr.c index 4a79b69f..b0ff1b9c 100644 --- a/src/common/amr.c +++ b/src/common/amr.c @@ -16,8 +16,9 @@ void amr_log_mr_conf(int ss, int logl, const char *pfx, for (i = 0; i < amr_mrc->num_modes; i++) LOGPC(ss, logl, ", mode[%u] = %u/%u/%u", - i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold, - amr_mrc->mode[i].hysteresis); + i, amr_mrc->mode[i].mode, + amr_mrc->mode[i].threshold_bts, + amr_mrc->mode[i].hysteresis_bts); LOGPC(ss, logl, "\n"); } @@ -68,18 +69,18 @@ int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc, } if (num_codecs >= 2) { - amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F; - amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4; + amr_mrc->mode[0].threshold_bts = mr_conf[1] & 0x3F; + amr_mrc->mode[0].hysteresis_bts = mr_conf[2] >> 4; } if (num_codecs >= 3) { - amr_mrc->mode[1].threshold = + amr_mrc->mode[1].threshold_bts = ((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6); - amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7; + amr_mrc->mode[1].hysteresis_bts = (mr_conf[3] >> 2) & 0xF; } if (num_codecs >= 4) { - amr_mrc->mode[3].threshold = + amr_mrc->mode[2].threshold_bts = ((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4); - amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF; + amr_mrc->mode[2].hysteresis_bts = mr_conf[4] & 0xF; } return num_codecs; @@ -94,10 +95,12 @@ ret_einval: unsigned int amr_get_initial_mode(struct gsm_lchan *lchan) { struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie; - if (lchan->mr_conf.icmi) { + if (mr_conf->icmi) { /* initial mode given, coding in TS 05.09 3.4.1 */ - return lchan->mr_conf.smod; + return mr_conf->smod; } else { /* implicit rule according to TS 05.09 Chapter 3.4.3 */ switch (amr_mrc->num_modes) { diff --git a/src/common/rsl.c b/src/common/rsl.c index 0908f1cc..a225155a 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -837,12 +837,12 @@ static int rsl_rx_chan_activ(struct msgb *msg) } /* 9.3.52 MultiRate Configuration */ if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) { - if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) { + if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) { LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n"); return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); } - memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), - TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1, + TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1); amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), @@ -1094,12 +1094,12 @@ static int rsl_rx_mode_modif(struct msgb *msg) /* 9.3.52 MultiRate Configuration */ if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) { - if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) { + if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) { LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n"); return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT); } - memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), - TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); + memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1, + TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1); amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c index 4c9ac497..adc0241c 100644 --- a/src/osmo-bts-sysmo/oml.c +++ b/src/osmo-bts-sysmo/oml.c @@ -818,6 +818,9 @@ static void set_payload_format(GsmL1_LogChParam_t *lch_par) static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan) { + struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr; + struct gsm48_multi_rate_conf *mr_conf = + (struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie; int j; LOGP(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n", @@ -857,41 +860,41 @@ static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan) lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset; j = 0; - if (lchan->mr_conf.m4_75) + if (mr_conf->m4_75) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_4_75; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m5_15) + if (mr_conf->m5_15) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_15; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m5_90) + if (mr_conf->m5_90) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_9; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m6_70) + if (mr_conf->m6_70) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_6_7; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m7_40) + if (mr_conf->m7_40) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_4; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m7_95) + if (mr_conf->m7_95) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_95; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m10_2) + if (mr_conf->m10_2) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_10_2; if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet)) break; - if (lchan->mr_conf.m12_2) + if (mr_conf->m12_2) lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2; break; case GSM48_CMODE_DATA_14k5: -- cgit v1.2.3 From acc71ffb4b61b3354bbb2fa14981e4e6a46946e6 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 11:45:28 +0100 Subject: TRX: Introduce osmobts-trx, a layer 1 implementation for OpenBTS tranceivers The code is quite complete, TCH and PDCH channels are not yet tested. --- .gitignore | 1 + configure.ac | 9 + include/osmo-bts/gsm_data.h | 7 + include/osmo-bts/logging.h | 1 + include/osmo-bts/rsl.h | 2 + src/Makefile.am | 3 + src/common/logging.c | 6 + src/osmo-bts-trx/Makefile.am | 11 + src/osmo-bts-trx/l1_if.c | 571 ++++++++++++ src/osmo-bts-trx/l1_if.h | 121 +++ src/osmo-bts-trx/main.c | 402 +++++++++ src/osmo-bts-trx/pxxch.c | 473 ++++++++++ src/osmo-bts-trx/pxxch.h | 7 + src/osmo-bts-trx/rach.c | 118 +++ src/osmo-bts-trx/rach.h | 7 + src/osmo-bts-trx/sch.c | 89 ++ src/osmo-bts-trx/sch.h | 7 + src/osmo-bts-trx/scheduler.c | 1987 ++++++++++++++++++++++++++++++++++++++++++ src/osmo-bts-trx/scheduler.h | 29 + src/osmo-bts-trx/tch_fr.c | 310 +++++++ src/osmo-bts-trx/tch_fr.h | 7 + src/osmo-bts-trx/trx_if.c | 495 +++++++++++ src/osmo-bts-trx/trx_if.h | 29 + src/osmo-bts-trx/trx_vty.c | 281 ++++++ src/osmo-bts-trx/xcch.c | 193 ++++ src/osmo-bts-trx/xcch.h | 10 + 26 files changed, 5176 insertions(+) create mode 100644 src/osmo-bts-trx/Makefile.am create mode 100644 src/osmo-bts-trx/l1_if.c create mode 100644 src/osmo-bts-trx/l1_if.h create mode 100644 src/osmo-bts-trx/main.c create mode 100644 src/osmo-bts-trx/pxxch.c create mode 100644 src/osmo-bts-trx/pxxch.h create mode 100644 src/osmo-bts-trx/rach.c create mode 100644 src/osmo-bts-trx/rach.h create mode 100644 src/osmo-bts-trx/sch.c create mode 100644 src/osmo-bts-trx/sch.h create mode 100644 src/osmo-bts-trx/scheduler.c create mode 100644 src/osmo-bts-trx/scheduler.h create mode 100644 src/osmo-bts-trx/tch_fr.c create mode 100644 src/osmo-bts-trx/tch_fr.h create mode 100644 src/osmo-bts-trx/trx_if.c create mode 100644 src/osmo-bts-trx/trx_if.h create mode 100644 src/osmo-bts-trx/trx_vty.c create mode 100644 src/osmo-bts-trx/xcch.c create mode 100644 src/osmo-bts-trx/xcch.h diff --git a/.gitignore b/.gitignore index 83513493..eda0126c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ src/osmo-bts-sysmo/sysmobts src/osmo-bts-sysmo/sysmobts-remote src/osmo-bts-sysmo/sysmobts-mgr +src/osmo-bts-trx/osmobts-trx tests/atconfig tests/package.m4 diff --git a/configure.ac b/configure.ac index 641a6c9a..752e85b3 100644 --- a/configure.ac +++ b/configure.ac @@ -37,6 +37,14 @@ AC_ARG_ENABLE(sysmocom-bts, AC_MSG_RESULT([$enable_sysmocom_bts]) AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes") +AC_MSG_CHECKING([whether to enable trx hardware support]) +AC_ARG_ENABLE(trx, + AC_HELP_STRING([--enable-trx], + [enable code for trx hardware [default=no]]), + [enable_trx="yes"],[enable_trx="no"]) +AC_MSG_RESULT([$enable_trx]) +AM_CONDITIONAL(ENABLE_TRX, test "x$enable_trx" = "xyes") + # We share gsm_data.h with OpenBSC and need to be pointed to the source # directory of OpenBSC for now. AC_ARG_WITH([openbsc], @@ -70,6 +78,7 @@ AC_OUTPUT( src/Makefile src/common/Makefile src/osmo-bts-sysmo/Makefile + src/osmo-bts-trx/Makefile include/Makefile include/osmo-bts/Makefile tests/Makefile diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h index bfa5285b..06115b33 100644 --- a/include/osmo-bts/gsm_data.h +++ b/include/osmo-bts/gsm_data.h @@ -115,6 +115,13 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx) return trx->role_bts.l1h; } +struct trx_l1h; + +static inline struct trx_l1h *trx_l1h_hdl(struct gsm_bts_trx *trx) +{ + return trx->role_bts.l1h; +} + void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state); diff --git a/include/osmo-bts/logging.h b/include/osmo-bts/logging.h index 226f77b7..b7c8a270 100644 --- a/include/osmo-bts/logging.h +++ b/include/osmo-bts/logging.h @@ -16,6 +16,7 @@ enum { DDSP, DPCU, DHO, + DTRX, DABIS, DRTP, DSUM, diff --git a/include/osmo-bts/rsl.h b/include/osmo-bts/rsl.h index 42ea6ef7..6cb40dd2 100644 --- a/include/osmo-bts/rsl.h +++ b/include/osmo-bts/rsl.h @@ -11,6 +11,8 @@ enum { LCHAN_REL_ACT_OML, }; +int msgb_queue_flush(struct llist_head *list); + int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg); int rsl_tx_rf_res(struct gsm_bts_trx *trx); int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime, diff --git a/src/Makefile.am b/src/Makefile.am index e2eab0da..b8fe655c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,3 +3,6 @@ SUBDIRS = common if ENABLE_SYSMOBTS SUBDIRS += osmo-bts-sysmo endif +if ENABLE_TRX +SUBDIRS += osmo-bts-trx +endif diff --git a/src/common/logging.c b/src/common/logging.c index 1e071dbb..b117ee04 100644 --- a/src/common/logging.c +++ b/src/common/logging.c @@ -107,6 +107,12 @@ static struct log_info_cat bts_log_info_cat[] = { .color = "\033[0;37m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DTRX] = { + .name = "DTRX", + .description = "TRX interface", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, #if 0 [DNS] = { .name = "DNS", diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am new file mode 100644 index 00000000..94a050ce --- /dev/null +++ b/src/osmo-bts-trx/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) +AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) +LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp + +EXTRA_DIST = trx_if.h l1_if.h scheduler.h xcch.h rach.h sch.h pxxch.h tch_fr.h + +bin_PROGRAMS = osmobts-trx + +osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c xcch.c rach.c sch.c pxxch.c tch_fr.c +osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) + diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c new file mode 100644 index 00000000..66922b54 --- /dev/null +++ b/src/osmo-bts-trx/l1_if.c @@ -0,0 +1,571 @@ +/* + * layer 1 primitive handling and interface + * + * Copyright (C) 2013 Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "l1_if.h" +#include "trx_if.h" +#include "scheduler.h" + + +static const uint8_t tranceiver_chan_types[_GSM_PCHAN_MAX] = { + [GSM_PCHAN_NONE] = 8, + [GSM_PCHAN_CCCH] = 6, + [GSM_PCHAN_CCCH_SDCCH4] = 5, + [GSM_PCHAN_TCH_F] = 1, + [GSM_PCHAN_TCH_H] = 2, + [GSM_PCHAN_SDCCH8_SACCH8C] = 7, + [GSM_PCHAN_PDCH] = 13, + //[GSM_PCHAN_TCH_F_PDCH] = FIXME, + [GSM_PCHAN_UNKNOWN] = 0, +}; + + +/* + * create destroy trx l1 instance + */ + +struct trx_l1h *l1if_open(struct gsm_bts_trx *trx) +{ + struct trx_l1h *l1h; + int rc; + + l1h = talloc_zero(tall_bts_ctx, struct trx_l1h); + if (!l1h) + return NULL; + l1h->trx = trx; + trx->role_bts.l1h = l1h; + + trx_sched_init(l1h); + + rc = trx_if_open(l1h); + if (rc < 0) { + LOGP(DL1C, LOGL_FATAL, "Cannot initialize scheduler\n"); + goto err; + } + + return l1h; + +err: + l1if_close(l1h); + return NULL; +} + +void l1if_close(struct trx_l1h *l1h) +{ + trx_if_close(l1h); + trx_sched_exit(l1h); + talloc_free(l1h); +} + +void l1if_reset(struct trx_l1h *l1h) +{ +} + +void check_tranceiver_availability(struct trx_l1h *l1h) +{ + struct gsm_bts_trx *trx = l1h->trx; + uint8_t tn; + + /* HACK, we should change state when we receive first clock from + * tranceiver */ + if (1) { + /* signal availability */ + oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK); + oml_mo_tx_sw_act_rep(&trx->mo); + oml_mo_state_chg(&trx->bb_transc.mo, -1, NM_AVSTATE_OK); + oml_mo_tx_sw_act_rep(&trx->bb_transc.mo); + + for (tn = 0; tn < 8; tn++) + oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED, + (l1h->config.slotmask & (1 << tn)) ? + NM_AVSTATE_DEPENDENCY : + NM_AVSTATE_NOT_INSTALLED); + } else { + oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, + NM_AVSTATE_OFF_LINE); + oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, + NM_AVSTATE_OFF_LINE); + } +} + + +/* + * tranceiver provisioning + */ +int l1if_provision_tranceiver_trx(struct trx_l1h *l1h) +{ + uint8_t tn; + + if (l1h->config.poweron + && l1h->config.tsc_valid + && l1h->config.bsic_valid + && l1h->config.arfcn_valid) { + /* before power on */ + if (l1h->config.arfcn_valid && !l1h->config.arfcn_sent) { + trx_if_cmd_rxtune(l1h, l1h->config.arfcn); + trx_if_cmd_txtune(l1h, l1h->config.arfcn); + l1h->config.arfcn_sent = 1; + } + if (l1h->config.tsc_valid && !l1h->config.tsc_sent) { + trx_if_cmd_settsc(l1h, l1h->config.tsc); + l1h->config.tsc_sent = 1; + } + if (l1h->config.bsic_valid && !l1h->config.bsic_sent) { + trx_if_cmd_setbsic(l1h, l1h->config.bsic); + l1h->config.bsic_sent = 1; + } + + if (!l1h->config.poweron_sent) { + trx_if_cmd_poweron(l1h); + l1h->config.poweron_sent = 1; + } + + /* after power on */ + if (l1h->config.rxgain_valid && !l1h->config.rxgain_sent) { + trx_if_cmd_setrxgain(l1h, l1h->config.rxgain); + l1h->config.rxgain_sent = 1; + } + if (l1h->config.power_valid && !l1h->config.power_sent) { + trx_if_cmd_setpower(l1h, l1h->config.power); + l1h->config.power_sent = 1; + } + if (l1h->config.maxdly_valid && !l1h->config.maxdly_sent) { + trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly); + l1h->config.maxdly_sent = 1; + } + for (tn = 0; tn < 8; tn++) { + if (l1h->config.slottype_valid[tn] + && !l1h->config.slottype_sent[tn]) { + trx_if_cmd_setslot(l1h, tn, + l1h->config.slottype[tn]); + l1h->config.slottype_sent[tn] = 1; + } + } + return 0; + } + + if (!l1h->config.poweron && !l1h->config.poweron_sent) { + trx_if_cmd_poweroff(l1h); + l1h->config.poweron_sent = 1; + l1h->config.rxgain_sent = 0; + l1h->config.power_sent = 0; + l1h->config.maxdly_sent = 0; + for (tn = 0; tn < 8; tn++) + l1h->config.slottype_sent[tn] = 0; + } + + return 0; +} + +int l1if_provision_tranceiver(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct trx_l1h *l1h; + + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = trx_l1h_hdl(trx); + l1h->config.arfcn_sent = 0; + l1h->config.tsc_sent = 0; + l1h->config.bsic_sent = 0; + l1h->config.poweron_sent = 0; + l1h->config.rxgain_sent = 0; + l1h->config.power_sent = 0; + l1h->config.maxdly_sent = 0; + l1if_provision_tranceiver_trx(l1h); + } + return 0; +} + +/* + * activation/configuration/deactivation of tranceiver's TRX + */ + +/* initialize the layer1 */ +static int trx_init(struct gsm_bts_trx *trx) +{ + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + /* power on tranceiver, if not already */ + if (!l1h->config.poweron) { + l1h->config.poweron = 1; + l1h->config.poweron_sent = 0; + l1if_provision_tranceiver_trx(l1h); + } + + if (trx == trx->bts->c0) + lchan_init_lapdm(&trx->ts[0].lchan[4]); + + /* Set to Operational State: Enabled */ + oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK); + + /* Send OPSTART ack */ + return oml_mo_opstart_ack(&trx->mo); +} + +/* deactivate tranceiver */ +static int trx_close(struct gsm_bts_trx *trx) +{ + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + if (l1h->config.poweron) { + l1h->config.poweron = 0; + l1h->config.poweron_sent = 0; + l1if_provision_tranceiver_trx(l1h); + } + + return 0; +} + +/* set bts attributes */ +static uint8_t trx_set_bts(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct trx_l1h *l1h; + uint8_t bsic = bts->bsic; + + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = trx_l1h_hdl(trx); + if (l1h->config.bsic != bsic || !l1h->config.bsic_valid) { + l1h->config.bsic = bsic; + l1h->config.bsic_valid = 1; + l1h->config.bsic_sent = 0; + } + check_tranceiver_availability(l1h); + } + + + return 0; +} + +/* set trx attributes */ +static uint8_t trx_set_trx(struct gsm_bts_trx *trx) +{ + struct trx_l1h *l1h = trx_l1h_hdl(trx); + uint16_t arfcn = trx->arfcn; + + if (l1h->config.arfcn != arfcn || !l1h->config.arfcn_valid) { + l1h->config.arfcn = arfcn; + l1h->config.arfcn_valid = 1; + l1h->config.arfcn_sent = 0; + } + + return 0; +} + +/* set ts attributes */ +static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts) +{ + struct trx_l1h *l1h = trx_l1h_hdl(ts->trx); + uint8_t tn = ts->nr; + uint16_t tsc = ts->tsc; + enum gsm_phys_chan_config pchan = ts->pchan; + uint8_t slottype; + int rc; + + /* all TSC of all timeslots must be equal, because tranceiver only + * supports one TSC per TRX */ + + if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) { + l1h->config.tsc = tsc; + l1h->config.tsc_valid = 1; + l1h->config.tsc_sent = 0; + } + + /* set physical channel */ + rc = trx_sched_set_pchan(l1h, tn, pchan); + if (rc) + return NM_NACK_RES_NOTAVAIL; + + slottype = tranceiver_chan_types[pchan]; + + if (l1h->config.slottype[tn] != slottype + || !l1h->config.slottype_valid[tn]) { + l1h->config.slottype[tn] = slottype; + l1h->config.slottype_valid[tn] = 1; + l1h->config.slottype_sent[tn] = 0; + l1if_provision_tranceiver_trx(l1h); + } + + return 0; +} + + +/* + * primitive handling + */ + +/* enable ciphering */ +static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan, + int downlink) +{ + // FIXME + return 0; +} + +/* channel mode, encryption and/or multirate have changed */ +static int l1if_rsl_mode_modify(struct trx_l1h *l1h, struct gsm_lchan *lchan, + int downlink) +{ + + // FIXME + return 0; +} + +static int mph_info_chan_confirm(struct trx_l1h *l1h, uint8_t chan_nr, + enum osmo_mph_info_type type, uint8_t cause) +{ + struct osmo_phsap_prim l1sap; + + memset(&l1sap, 0, sizeof(l1sap)); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM, + NULL); + l1sap.u.info.type = type; + l1sap.u.info.u.act_cnf.chan_nr = chan_nr; + l1sap.u.info.u.act_cnf.cause = cause; + + return l1sap_up(l1h->trx, &l1sap); +} + +int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn) +{ + struct osmo_phsap_prim l1sap; + + memset(&l1sap, 0, sizeof(l1sap)); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, + PRIM_OP_INDICATION, NULL); + l1sap.u.info.type = PRIM_INFO_TIME; + l1sap.u.info.u.time_ind.fn = fn; + + if (!bts->c0) + return -EINVAL; + + return l1sap_up(bts->c0, &l1sap); +} + + +/* primitive from common part */ +int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) +{ + struct trx_l1h *l1h = trx_l1h_hdl(trx); + struct msgb *msg = l1sap->oph.msg; + uint8_t chan_nr; + uint8_t tn, ss; + int rc = 0; + struct gsm_lchan *lchan; + + switch (OSMO_PRIM_HDR(&l1sap->oph)) { + case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST): + if (!msg) + break; + /* put data into scheduler's queue */ + return trx_sched_ph_data_req(l1h, l1sap); + case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST): + if (!msg) + break; + /* put data into scheduler's queue */ + return trx_sched_tch_req(l1h, l1sap); + case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST): + switch (l1sap->u.info.type) { + case PRIM_INFO_ACT_CIPH: + chan_nr = l1sap->u.info.u.ciph_req.chan_nr; + tn = L1SAP_CHAN2TS(chan_nr); + ss = l1sap_chan2ss(chan_nr); + lchan = &trx->ts[tn].lchan[ss]; + if (l1sap->u.info.u.ciph_req.downlink) { + l1if_set_ciphering(l1h, lchan, 1); + lchan->ciph_state = LCHAN_CIPH_RX_REQ; + } + if (l1sap->u.info.u.ciph_req.uplink) { + l1if_set_ciphering(l1h, lchan, 0); + lchan->ciph_state = LCHAN_CIPH_TXRX_REQ; + } + break; + case PRIM_INFO_ACTIVATE: + case PRIM_INFO_DEACTIVATE: + case PRIM_INFO_MODIFY: + chan_nr = l1sap->u.info.u.act_req.chan_nr; + tn = L1SAP_CHAN2TS(chan_nr); + ss = l1sap_chan2ss(chan_nr); + lchan = &trx->ts[tn].lchan[ss]; + if (l1sap->u.info.type == PRIM_INFO_ACTIVATE) { + if ((chan_nr & 0x80)) { + LOGP(DL1C, LOGL_ERROR, "Cannot activate" + " chan_nr 0x%02x\n", chan_nr); + break; + } + /* activate dedicated channel */ + trx_sched_set_lchan(l1h, chan_nr, 0x00, 0, 1); + trx_sched_set_lchan(l1h, chan_nr, 0x00, 1, 1); + trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 1); + trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 1); + /* init lapdm */ + lchan_init_lapdm(lchan); + /* confirm */ + mph_info_chan_confirm(l1h, chan_nr, + PRIM_INFO_ACTIVATE, 0); + break; + } + if (l1sap->u.info.type == PRIM_INFO_MODIFY) { + l1if_rsl_mode_modify(l1h, lchan, 0); + l1if_rsl_mode_modify(l1h, lchan, 1); + break; + } + if ((chan_nr & 0x80)) { + LOGP(DL1C, LOGL_ERROR, "Cannot deactivate " + "chan_nr 0x%02x\n", chan_nr); + break; + } + /* deactivate dedicated channel */ + if (!l1sap->u.info.u.act_req.sacch_only) { + trx_sched_set_lchan(l1h, chan_nr, 0x00, 0, 0); + trx_sched_set_lchan(l1h, chan_nr, 0x00, 1, 0); + } + trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 0); + trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 0); + /* confirm */ + mph_info_chan_confirm(l1h, chan_nr, + PRIM_INFO_DEACTIVATE, 0); + break; + default: + LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n", + l1sap->u.info.type); + rc = -EINVAL; + goto done; + } + break; + default: + LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n", + l1sap->oph.primitive, l1sap->oph.operation); + rc = -EINVAL; + goto done; + } + +done: + if (msg) + msgb_free(msg); + return rc; +} + + +/* + * oml handling + */ + +/* callback from OML */ +int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type, + struct tlv_parsed *old_attr, struct tlv_parsed *new_attr, + void *obj) +{ + /* FIXME: check if the attributes are valid */ + return 0; +} + +/* callback from OML */ +int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg, + struct tlv_parsed *new_attr, int kind, void *obj) +{ + struct abis_om_fom_hdr *foh = msgb_l3(msg); + int cause = 0; + + switch (foh->msg_type) { + case NM_MT_SET_BTS_ATTR: + cause = trx_set_bts(obj); + break; + case NM_MT_SET_RADIO_ATTR: + cause = trx_set_trx(obj); + break; + case NM_MT_SET_CHAN_ATTR: + cause = trx_set_ts(obj); + break; + } + + return oml_fom_ack_nack(msg, cause); +} + +/* callback from OML */ +int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, + void *obj) +{ + int rc; + + switch (mo->obj_class) { + case NM_OC_RADIO_CARRIER: + /* activate tranceiver */ + rc = trx_init(obj); + break; + case NM_OC_CHANNEL: + /* configure timeslot */ + rc = 0; //ts_connect(obj); + + /* Set to Operational State: Enabled */ + oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK); + + /* Send OPSTART ack */ + rc = oml_mo_opstart_ack(mo); + + break; + case NM_OC_BTS: + case NM_OC_SITE_MANAGER: + case NM_OC_BASEB_TRANSC: + case NM_OC_GPRS_NSE: + case NM_OC_GPRS_CELL: + case NM_OC_GPRS_NSVC: + oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1); + rc = oml_mo_opstart_ack(mo); + break; + default: + rc = oml_mo_opstart_nack(mo, NM_NACK_OBJCLASS_NOTSUPP); + } + return rc; +} + +int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo, + void *obj, uint8_t adm_state) +{ + /* blindly accept all state changes */ + mo->nm_state.administrative = adm_state; + return oml_mo_statechg_ack(mo); +} + +int bts_model_trx_deact_rf(struct gsm_bts_trx *trx) +{ + return trx_close(trx); +} + +int bts_model_oml_estab(struct gsm_bts *bts) +{ + return 0; +} + diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h new file mode 100644 index 00000000..7e0f3688 --- /dev/null +++ b/src/osmo-bts-trx/l1_if.h @@ -0,0 +1,121 @@ +#ifndef L1_IF_H_TRX +#define L1_IF_H_TRX + +/* These types define the different channels on a multiframe. + * Each channel has queues and can be activated individually. + */ +enum trx_chan_type { + TRXC_IDLE = 0, + TRXC_FCCH, + TRXC_SCH, + TRXC_BCCH, + TRXC_RACH, + TRXC_CCCH, + TRXC_TCHF, + TRXC_TCHH_0, + TRXC_TCHH_1, + TRXC_SDCCH4_0, + TRXC_SDCCH4_1, + TRXC_SDCCH4_2, + TRXC_SDCCH4_3, + TRXC_SDCCH8_0, + TRXC_SDCCH8_1, + TRXC_SDCCH8_2, + TRXC_SDCCH8_3, + TRXC_SDCCH8_4, + TRXC_SDCCH8_5, + TRXC_SDCCH8_6, + TRXC_SDCCH8_7, + TRXC_SACCHTF, + TRXC_SACCHTH_0, + TRXC_SACCHTH_1, + TRXC_SACCH4_0, + TRXC_SACCH4_1, + TRXC_SACCH4_2, + TRXC_SACCH4_3, + TRXC_SACCH8_0, + TRXC_SACCH8_1, + TRXC_SACCH8_2, + TRXC_SACCH8_3, + TRXC_SACCH8_4, + TRXC_SACCH8_5, + TRXC_SACCH8_6, + TRXC_SACCH8_7, + TRXC_PDTCH, + TRXC_PTCCH, + _TRX_CHAN_MAX +}; + +/* States each channel on a multiframe */ +struct trx_chan_state { + uint8_t dl_active; /* Channel is active for TX */ + uint8_t ul_active; /* Channel is active for RX */ + ubit_t *dl_bursts; /* burst buffer for TX */ + sbit_t *ul_bursts; /* burst buffer for RX */ + uint32_t ul_first_fn; /* fn of first burst */ + uint8_t ul_mask; /* mask of received bursts */ + uint8_t sacch_lost; /* SACCH loss detection */ +}; + +struct trx_config { + uint8_t poweron; /* poweron(1) or poweroff(0) */ + int poweron_sent; + + int arfcn_valid; + uint16_t arfcn; + int arfcn_sent; + + int tsc_valid; + uint8_t tsc; + int tsc_sent; + + int bsic_valid; + uint8_t bsic; + int bsic_sent; + + int rxgain_valid; + int rxgain; + int rxgain_sent; + + int power_valid; + int power; + int power_sent; + + int maxdly_valid; + int maxdly; + int maxdly_sent; + + uint8_t slotmask; + + int slottype_valid[8]; + uint8_t slottype[8]; + int slottype_sent[8]; +}; + +struct trx_l1h { + struct llist_head trx_ctrl_list; + + struct gsm_bts_trx *trx; + + struct osmo_fd trx_ofd_ctrl; + struct osmo_timer_list trx_ctrl_timer; + struct osmo_fd trx_ofd_data; + + /* tranceiver config */ + struct trx_config config; + + uint8_t mf_index[8]; /* selected multiframe index */ + + /* Channel states for all channels on all timeslots */ + struct trx_chan_state chan_states[8][_TRX_CHAN_MAX]; + struct llist_head dl_prims[8]; /* Queue primitves for TX */ +}; + +struct trx_l1h *l1if_open(struct gsm_bts_trx *trx); +void l1if_close(struct trx_l1h *l1h); +void l1if_reset(struct trx_l1h *l1h); +int l1if_provision_tranceiver_trx(struct trx_l1h *l1h); +int l1if_provision_tranceiver(struct gsm_bts *bts); +int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); + +#endif /* L1_IF_H_TRX */ diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c new file mode 100644 index 00000000..1b3359aa --- /dev/null +++ b/src/osmo-bts-trx/main.c @@ -0,0 +1,402 @@ +/* Main program for OsmoBTS-TRX */ + +/* (C) 2011 by Harald Welte + * (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "l1_if.h" +#include "trx_if.h" +#include "scheduler.h" + +const int pcu_direct = 0; + +int quit = 0; +static const char *config_file = "osmo-bts.cfg"; +static int daemonize = 0; +static char *gsmtap_ip = 0; +static int high_prio = 0; +static int trx_num = 1; +char *software_version = "0.0"; +uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 }; +char *bsc_host = "localhost"; +char *bts_id = "1801/0"; + +// FIXME this is a hack +static void get_mac(void) +{ + struct if_nameindex *ifn = if_nameindex(); + struct ifreq ifr; + int sock; + int ret; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + return; + + memset(&ifr, 0, sizeof(ifr)); + if (!ifn) + return; + while (ifn->if_name) { + strncpy(ifr.ifr_name, ifn->if_name, sizeof(ifr.ifr_name)-1); + ret = ioctl(sock, SIOCGIFHWADDR, &ifr); + if (ret == 0 && !!memcmp(ifr.ifr_hwaddr.sa_data, + "\0\0\0\0\0\0", 6)) { + memcpy(abis_mac, ifr.ifr_hwaddr.sa_data, 6); + printf("Using MAC address of %s: " + "'%02x:%02x:%02x:%02x:%02x:%02x'\n", + ifn->if_name, + abis_mac[0], abis_mac[1], abis_mac[2], + abis_mac[3], abis_mac[4], abis_mac[5]); + break; + } + ifn++; + } +// if_freenameindex(ifn); +} + +int bts_model_init(struct gsm_bts *bts) +{ + void *l1h; + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = l1if_open(trx); + if (!l1h) { + LOGP(DL1C, LOGL_FATAL, "Cannot open L1 Interface\n"); + goto error; + } + + trx->role_bts.l1h = l1h; + trx->nominal_power = 23; + + l1if_reset(l1h); + } + + bts_model_vty_init(bts); + + return 0; + +error: + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = trx->role_bts.l1h; + if (l1h) + l1if_close(l1h); + } + + return -EIO; +} + +/* dummy, since no direct dsp support */ +uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx) +{ + return 0; +} + +static void print_help() +{ + printf( "Some useful options:\n" + " -h --help this text\n" + " -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n" + " -D --daemonize For the process into a background daemon\n" + " -c --config-file Specify the filename of the config file\n" + " -s --disable-color Don't use colors in stderr log output\n" + " -T --timestamp Prefix every log line with a timestamp\n" + " -V --version Print version information and exit\n" + " -e --log-level Set a global log-level\n" + " -t --trx-num Set number of TRX (default=%d)\n" + " -i --gsmtap-ip The destination IP used for GSMTAP.\n" + " -H --high-prio Set realtime scheduler with maximum prio\n" + " -I --tranceiver-ip Set IP of tranceiver (default=%s)\n" + ,trx_num, tranceiver_ip); +} + +/* FIXME: finally get some option parsing code into libosmocore */ +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_idx = 0, c; + static const struct option long_options[] = { + /* FIXME: all those are generic Osmocom app options */ + { "help", 0, 0, 'h' }, + { "debug", 1, 0, 'd' }, + { "daemonize", 0, 0, 'D' }, + { "config-file", 1, 0, 'c' }, + { "disable-color", 0, 0, 's' }, + { "timestamp", 0, 0, 'T' }, + { "version", 0, 0, 'V' }, + { "log-level", 1, 0, 'e' }, + { "trx-num", 1, 0, 't' }, + { "gsmtap-ip", 1, 0, 'i' }, + { "high-prio", 0, 0, 'H' }, + { "tranceiver-ip", 1, 0, 'I' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "hc:d:Dc:sTVe:t:i:HI:", + long_options, &option_idx); + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + break; + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'D': + daemonize = 1; + break; + case 'c': + config_file = strdup(optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; + case 'V': + print_version(1); + exit(0); + break; + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + case 't': + trx_num = atoi(optarg); + if (trx_num < 1) + trx_num = 1; + break; + case 'i': + gsmtap_ip = optarg; + break; + case 'H': + high_prio = 1; + break; + case 'I': + tranceiver_ip = strdup(optarg); + break; + default: + break; + } + } +} + +static struct gsm_bts *bts; + +static void signal_handler(int signal) +{ + fprintf(stderr, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + //osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL); + if (!quit) + bts_shutdown(bts, "SIGINT"); + quit++; + break; + case SIGABRT: + case SIGUSR1: + case SIGUSR2: + talloc_report_full(tall_bts_ctx, stderr); + break; + default: + break; + } +} + +static int write_pid_file(char *procname) +{ + FILE *outf; + char tmp[PATH_MAX+1]; + + snprintf(tmp, sizeof(tmp)-1, "/var/run/%s.pid", procname); + tmp[PATH_MAX-1] = '\0'; + + outf = fopen(tmp, "w"); + if (!outf) + return -1; + + fprintf(outf, "%d\n", getpid()); + + fclose(outf); + + return 0; +} + +int main(int argc, char **argv) +{ + struct gsm_bts_role_bts *btsb; + struct gsm_bts_trx *trx; + struct e1inp_line *line; + void *tall_msgb_ctx; + int rc, i; + + printf("((*))\n |\n / \\ OsmoBTS\n"); + + get_mac(); + + tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context"); + tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb"); + msgb_set_talloc_ctx(tall_msgb_ctx); + + bts_log_init(NULL); + + handle_options(argc, argv); + + bts = gsm_bts_alloc(tall_bts_ctx); + if (!bts) { + fprintf(stderr, "Failed to create BTS structure\n"); + exit(1); + } + for (i = 1; i < trx_num; i++) { + trx = gsm_bts_trx_alloc(bts); + if (!trx) { + fprintf(stderr, "Failed to TRX structure\n"); + exit(1); + } + } + + vty_init(&bts_vty_info); + e1inp_vty_init(); + bts_vty_init(bts, &bts_log_info); + + if (bts_init(bts) < 0) { + fprintf(stderr, "unable to to open bts\n"); + exit(1); + } + btsb = bts_role_bts(bts); + btsb->support.ciphers = 0; // CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3); + + if (gsmtap_ip) { + gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1); + if (!gsmtap) { + fprintf(stderr, "Failed during gsmtap_init()\n"); + exit(1); + } + gsmtap_source_add_sink(gsmtap); + } + + rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", + config_file); + exit(1); + } + + write_pid_file("osmo-bts"); + + rc = telnet_init(tall_bts_ctx, NULL, 4241); + if (rc < 0) { + fprintf(stderr, "Error initializing telnet\n"); + exit(1); + } + + if (pcu_sock_init()) { + fprintf(stderr, "PCU L1 socket failed\n"); + exit(-1); + } + + signal(SIGINT, &signal_handler); + //signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + + if (!btsb->bsc_oml_host) { + fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n"); + exit(1); + } + + line = abis_open(bts, btsb->bsc_oml_host, "sysmoBTS"); + if (!line) { + fprintf(stderr, "unable to connect to BSC\n"); + exit(1); + } + + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } + + if (high_prio) { + struct sched_param schedp; + + /* high priority scheduling required for handling bursts */ + rc = sched_get_priority_max(SCHED_RR); + memset(&schedp, 0, sizeof(schedp)); + schedp.sched_priority = rc; + rc = sched_setscheduler(0, SCHED_RR, &schedp); + if (rc) { + fprintf(stderr, "Error setting scheduler\n"); + } + } + + while (quit < 2) { + log_reset_context(); + osmo_select_main(0); + } + +#if 0 + telnet_exit(); + + talloc_report_full(tall_bts_ctx, stderr); +#endif + + return 0; +} + diff --git a/src/osmo-bts-trx/pxxch.c b/src/osmo-bts-trx/pxxch.c new file mode 100644 index 00000000..17a7c947 --- /dev/null +++ b/src/osmo-bts-trx/pxxch.c @@ -0,0 +1,473 @@ +/* + * pxxch.c + * + * Copyright (c) 2013 Andreas Eversberg + */ + +#include +#include +#include + +#include +#include +#include + +#include "pxxch.h" + + +/* + * GSM PDTCH parity (FIRE code) + * + * g(x) = (x^23 + 1)(x^17 + x^3 + 1) + * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 + */ + +const struct osmo_crc64gen_code pxxch_crc40 = { + .bits = 40, + .poly = 0x0004820009ULL, + .init = 0x0000000000ULL, + .remainder = 0xffffffffffULL, +}; + + +/* + * GSM PDTCH CS-2, CS-3 parity + * + * g(x) = x^16 + x^12 + x^5 + 1 + */ + +const struct osmo_crc16gen_code pdtch_crc16 = { + .bits = 16, + .poly = 0x1021, + .init = 0x0000, + .remainder = 0xffff, +}; + + +/* + * GSM PDTCH convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_cs1_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_cs1_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_cs1 = { + .N = 2, + .K = 5, + .len = 224, + .next_output = conv_cs1_next_output, + .next_state = conv_cs1_next_state, +}; + +static const uint8_t conv_cs2_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_cs2_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_cs2 = { + .N = 2, + .K = 5, + .len = 290, + .next_output = conv_cs2_next_output, + .next_state = conv_cs2_next_state, +}; + +static const uint8_t conv_cs3_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_cs3_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_cs3 = { + .N = 2, + .K = 5, + .len = 334, + .next_output = conv_cs3_next_output, + .next_state = conv_cs3_next_state, +}; + + +/* + * GSM PxxCH interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 4 blocks of 114 bits: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 4) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +static void +pxxch_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + cB[k] = iB[B * 114 + j]; + } +} + +static void +pxxch_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + iB[B * 114 + j] = cB[k]; + } +} + +static void +pxxch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn) +{ + memcpy(iB, eB, 57); + memcpy(iB+57, eB+59, 57); + + if (hl) + *hl = eB[57]; + + if (hn) + *hn = eB[58]; +} + +static void +pxxch_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *hl, ubit_t *hn) +{ + memcpy(eB, iB, 57); + memcpy(eB+59, iB+57, 57); + + if (hl) + eB[57] = *hl; + if (hn) + eB[58] = *hn; +} + +static ubit_t pdtch_hl_hn[4][8] = { + { 1,1, 1,1, 1,1, 1,1 }, + { 1,1, 0,0, 1,0, 0,0 }, + { 0,0, 1,0, 0,0, 0,1 }, + { 0,0, 0,1, 0,1, 1,0 }, +}; + +static ubit_t usf2six[8][6] = { + { 0,0,0, 0,0,0 }, + { 0,0,1, 0,1,1 }, + { 0,1,0, 1,1,0 }, + { 0,1,1, 1,0,1 }, + { 1,0,0, 1,0,1 }, + { 1,0,1, 1,1,0 }, + { 1,1,0, 0,1,1 }, + { 1,1,1, 0,0,0 }, +}; + +static ubit_t usf2twelve[8][12] = { + { 0,0,0, 0,0,0, 0,0,0, 0,0,0 }, + { 0,0,0, 0,1,1, 0,1,1, 1,0,1 }, + { 0,0,1, 1,0,1, 1,1,0, 1,1,0 }, + { 0,0,1, 1,1,0, 1,0,1, 0,1,1 }, + { 1,1,0, 1,0,0, 0,0,1, 0,1,1 }, + { 1,1,0, 1,1,1, 0,1,0, 1,1,0 }, + { 1,1,1, 0,0,1, 1,1,1, 1,0,1 }, + { 1,1,1, 0,1,0, 1,0,0, 0,0,0 }, +}; + +static uint8_t puncture_cs2[588] = { + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1 +}; + +static uint8_t puncture_cs3[676] = { + 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0 +}; + +int +pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) +{ + sbit_t iB[456], cB[676], hl_hn[8]; + ubit_t conv[456]; + int i, j, k, rv, best, cs, usf; + + for (i=0; i<4; i++) + pxxch_burst_unmap(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, + hl_hn + i*2 + 1); + + for (i=0, best=0, cs=1; i<4; j++) { + for (j=0, k=0; j<4; j++) { + if (pdtch_hl_hn[i][j] == hl_hn[j]) + k++; + } + if (k > best) + cs = i+1; + } + + pxxch_deinterleave(cB, iB); + + switch (cs) { + case 1: + osmo_conv_decode(&conv_cs1, cB, conv); + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv, 184, + conv+184); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); + + return 23; + case 2: + for (i=587, j=455; i>=0; i++) + if (!puncture_cs2[i]) + cB[i] = cB[j--]; + else + cB[i] = 0; + + osmo_conv_decode(&conv_cs2, cB, conv); + + for (i=0, best=0, usf=0; i<8; j++) { + for (j=0, k=0; j<6; j++) { + if (usf2six[i][j] == conv[j]) + k++; + } + if (k > best) + usf = i; + } + + conv[3] = (usf >> 2) & 1; + conv[4] = (usf >> 1) & 1; + conv[5] = usf & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+3, 271, + conv+3+271); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 271, 1); + + return 34; + case 3: + for (i=675, j=455; i>=0; i++) + if (!puncture_cs3[i]) + cB[i] = cB[j--]; + else + cB[i] = 0; + + osmo_conv_decode(&conv_cs3, cB, conv); + + for (i=0, best=0, usf=0; i<8; j++) { + for (j=0, k=0; j<6; j++) { + if (usf2six[i][j] == conv[j]) + k++; + } + if (k > best) + usf = i; + } + + conv[3] = (usf >> 2) & 1; + conv[4] = (usf >> 1) & 1; + conv[5] = usf & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+3, 315, + conv+3+315); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 315, 1); + + return 40; + case 4: + for (i=0; i<456;i++) + conv[i] = (cB[i] < 0) ? 1:0; + + for (i=0, best=0, usf=0; i<8; j++) { + for (j=0, k=0; j<12; j++) { + if (usf2twelve[i][j] == conv[j]) + k++; + } + if (k > best) + usf = i; + } + + conv[9] = (usf >> 2) & 1; + conv[10] = (usf >> 1) & 1; + conv[11] = usf & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+9, 431, + conv+9+431); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 431, 1); + + return 54; + } + + return -1; +} + +int +pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) +{ + ubit_t iB[456], cB[676], *hl_hn; + ubit_t conv[334]; + int i, j, usf; + + switch (l2_len) { + case 23: + osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); + + osmo_crc64gen_set_bits(&pxxch_crc40, conv, 184, conv+184); + + osmo_conv_encode(&conv_cs1, conv, cB); + + hl_hn = pdtch_hl_hn[0]; + + break; + case 34: + osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 271, 1); + usf = (conv[3] << 2) | (conv[4] << 1) | conv[5]; + + osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 271, conv+3+271); + + memcpy(conv, usf2six[usf], 6); + + osmo_conv_encode(&conv_cs2, conv, cB); + + for (i=0, j=0; i<588; i++) + if (!puncture_cs2[i]) + cB[j++] = cB[i]; + + hl_hn = pdtch_hl_hn[1]; + + break; + case 40: + osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 315, 1); + usf = (conv[3] << 2) | (conv[4] << 1) | conv[5]; + + osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 315, conv+3+315); + + memcpy(conv, usf2six[usf], 6); + + osmo_conv_encode(&conv_cs3, conv, cB); + + for (i=0, j=0; i<676; i++) + if (!puncture_cs3[i]) + cB[j++] = cB[i]; + + hl_hn = pdtch_hl_hn[2]; + + break; + case 54: + osmo_pbit2ubit_ext(cB, 9, l2_data, 0, 431, 1); + usf = (cB[9] << 2) | (cB[10] << 1) | conv[11]; + + osmo_crc16gen_set_bits(&pdtch_crc16, cB+9, 431, cB+9+431); + + memcpy(cB, usf2twelve[usf], 12); + + hl_hn = pdtch_hl_hn[3]; + + break; + default: + return -1; + } + + pxxch_interleave(cB, iB); + + for (i=0; i<4; i++) + pxxch_burst_map(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, + hl_hn + i*2 + 1); + + return 0; +} diff --git a/src/osmo-bts-trx/pxxch.h b/src/osmo-bts-trx/pxxch.h new file mode 100644 index 00000000..313fbf59 --- /dev/null +++ b/src/osmo-bts-trx/pxxch.h @@ -0,0 +1,7 @@ +#ifndef _PXXCH_H +#define _PXXCH_H + +int pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); +int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); + +#endif /* _PXXCH_H */ diff --git a/src/osmo-bts-trx/rach.c b/src/osmo-bts-trx/rach.c new file mode 100644 index 00000000..670a4d1d --- /dev/null +++ b/src/osmo-bts-trx/rach.c @@ -0,0 +1,118 @@ +/* + * conv_rach.c + * + * Convolutional code tables for RACH channels + * + * Copyright (C) 2011 Sylvain Munaut + */ + + +#include +#include +#include + +#include +#include +#include + +#include "rach.h" + + +/* + * GSM RACH parity + * + * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 + */ + +static const struct osmo_crc8gen_code rach_crc6 = { + .bits = 6, + .poly = 0x2f, + .init = 0x00, + .remainder = 0x3f, +}; + + +/* + * GSM RACH convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_rach_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_rach_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +const struct osmo_conv_code conv_rach = { + .N = 2, + .K = 5, + .len = 14, + .next_output = conv_rach_next_output, + .next_state = conv_rach_next_state, +}; + + +/* + * GSM RACH apply BSIC to parity + * + * p(j) = p(j) xor b(j) j = 0, ..., 5 + * b(0) = MSB of PLMN colour code + * b(5) = LSB of BS colour code + */ + +static int +rach_apply_bsic(ubit_t *d, uint8_t bsic) +{ + int i; + + /* Apply it */ + for (i=0; i<6; i++) + d[8+i] ^= ((bsic >> (5-i)) & 1); + + return 0; +} + +int +rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic) +{ + ubit_t conv[14]; + int rv; + + osmo_conv_decode(&conv_rach, burst, conv); + + rach_apply_bsic(conv, bsic); + + rv = osmo_crc8gen_check_bits(&rach_crc6, conv, 8, conv+8); + if (rv) + return -1; + + osmo_ubit2pbit_ext(ra, 0, conv, 0, 8, 1); + + return 0; +} + +int +rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic) +{ + ubit_t conv[14]; + + osmo_pbit2ubit_ext(conv, 0, ra, 0, 8, 1); + + osmo_crc8gen_set_bits(&rach_crc6, conv, 8, conv+8); + + rach_apply_bsic(conv, bsic); + + osmo_conv_encode(&conv_rach, conv, burst); + + return 0; +} diff --git a/src/osmo-bts-trx/rach.h b/src/osmo-bts-trx/rach.h new file mode 100644 index 00000000..15555593 --- /dev/null +++ b/src/osmo-bts-trx/rach.h @@ -0,0 +1,7 @@ +#ifndef _RACH_H +#define _RACH_H + +int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); +int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); + +#endif /* _RACH_H */ diff --git a/src/osmo-bts-trx/sch.c b/src/osmo-bts-trx/sch.c new file mode 100644 index 00000000..e0d6e8db --- /dev/null +++ b/src/osmo-bts-trx/sch.c @@ -0,0 +1,89 @@ +/* + */ + + +#include +#include +#include + +#include +#include +#include + +#include "sch.h" + + +/* + * GSM SCH parity + * + * g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1 + */ + +const struct osmo_crc16gen_code sch_crc10 = { + .bits = 10, + .poly = 0x175, + .init = 0x000, + .remainder = 0x3ff, +}; + + +/* + * GSM SCH convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_rach_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_rach_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_sch = { + .N = 2, + .K = 5, + .len = 35, + .next_output = conv_rach_next_output, + .next_state = conv_rach_next_state, +}; + + +int +sch_decode(uint8_t *sb_info, sbit_t *burst) +{ + ubit_t conv[35]; + int rv; + + osmo_conv_decode(&conv_sch, burst, conv); + + rv = osmo_crc16gen_check_bits(&sch_crc10, conv, 25, conv+25); + if (rv) + return -1; + + osmo_ubit2pbit_ext(sb_info, 0, conv, 0, 25, 1); + + return 0; +} + +int +sch_encode(ubit_t *burst, uint8_t *sb_info) +{ + ubit_t conv[35]; + + osmo_pbit2ubit_ext(conv, 0, sb_info, 0, 25, 1); + + osmo_crc16gen_set_bits(&sch_crc10, conv, 25, conv+25); + + osmo_conv_encode(&conv_sch, conv, burst); + + return 0; +} diff --git a/src/osmo-bts-trx/sch.h b/src/osmo-bts-trx/sch.h new file mode 100644 index 00000000..8e60191b --- /dev/null +++ b/src/osmo-bts-trx/sch.h @@ -0,0 +1,7 @@ +#ifndef _SCH_H +#define _SCH_H + +int sch_decode(uint8_t *sb_info, sbit_t *burst); +int sch_encode(ubit_t *burst, uint8_t *sb_info); + +#endif /* _SCH_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c new file mode 100644 index 00000000..a0caeeca --- /dev/null +++ b/src/osmo-bts-trx/scheduler.c @@ -0,0 +1,1987 @@ +/* Scheduler for OsmoBTS-TRX */ + +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "l1_if.h" +#include "scheduler.h" +#include "xcch.h" +#include "tch_fr.h" +#include "rach.h" +#include "sch.h" +#include "pxxch.h" +#include "trx_if.h" + +void *tall_bts_ctx; + +static struct gsm_bts *bts; + +/* clock states */ +static uint32_t tranceiver_lost; +uint32_t tranceiver_last_fn; +static struct timeval tranceiver_clock_tv; +static struct osmo_timer_list tranceiver_clock_timer; + +/* clock advance for the tranceiver */ +uint32_t trx_clock_advance = 10; + +/* advance RTS to give some time for data processing. (especially PCU) */ +uint32_t trx_rts_advance = 5; /* about 20ms */ + +typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan); +typedef const ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn, + uint32_t fn, enum trx_chan_type chan, uint8_t bid); +typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + +static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan); +static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan); +static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid); +static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); +static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); +static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); +static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); +static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + +static const ubit_t dummy_burst[148] = { + 0,0,0, + 1,1,1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,1,0, + 0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0, + 0,1,0,1,1,1,0,0,0,1,0,1,1,1,0,0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,1,0, + 0,0,1,1,0,0,1,1,0,0,1,1,1,0,0,1,1,1,1,0,1,0,0,1,1,1,1,1,0,0,0,1, + 0,0,1,0,1,1,1,1,1,0,1,0,1,0, + 0,0,0, +}; + +static const ubit_t fcch_burst[148] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +static const ubit_t tsc[8][26] = { + { 0,0,1,0,0,1,0,1,1,1,0,0,0,0,1,0,0,0,1,0,0,1,0,1,1,1, }, + { 0,0,1,0,1,1,0,1,1,1,0,1,1,1,1,0,0,0,1,0,1,1,0,1,1,1, }, + { 0,1,0,0,0,0,1,1,1,0,1,1,1,0,1,0,0,1,0,0,0,0,1,1,1,0, }, + { 0,1,0,0,0,1,1,1,1,0,1,1,0,1,0,0,0,1,0,0,0,1,1,1,1,0, }, + { 0,0,0,1,1,0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,1,0,1,0,1,1, }, + { 0,1,0,0,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,1,0,1,0, }, + { 1,0,1,0,0,1,1,1,1,1,0,1,1,0,0,0,1,0,1,0,0,1,1,1,1,1, }, + { 1,1,1,0,1,1,1,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,1,1,0,0, }, +}; + +static const ubit_t sch_train[64] = { + 1,0,1,1,1,0,0,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,1, + 0,0,1,0,1,1,0,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1,1,0,0,0,0,1,1,0,1,1, +}; + +/* + * subchannel description structure + */ + +struct trx_chan_desc { + enum trx_chan_type chan; + uint8_t chan_nr; + uint8_t link_id; + const char *name; + trx_sched_rts_func *rts_fn; + trx_sched_dl_func *dl_fn; + trx_sched_ul_func *ul_fn; + int auto_active; +}; +struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { + { TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 }, + { TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 }, + { TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 }, + { TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 }, + { TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 }, + { TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 }, + { TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, + { TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_SACCH8_7, 0x68, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, + { TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, +}; + + +/* + * init / exit + */ + +int trx_sched_init(struct trx_l1h *l1h) +{ + uint8_t tn; + int i; + struct trx_chan_state *chan_state; + + LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1h->trx->nr); + + /* hack to get bts */ + bts = l1h->trx->bts; + + for (tn = 0; tn < 8; tn++) { + l1h->mf_index[tn] = 0; + for (i = 0; i < _TRX_CHAN_MAX; i++) { + INIT_LLIST_HEAD(&l1h->dl_prims[tn]); + chan_state = &l1h->chan_states[tn][i]; + chan_state->ul_mask = 0x0; + } + } + + return 0; +} + +void trx_sched_exit(struct trx_l1h *l1h) +{ + uint8_t tn; + int i; + struct trx_chan_state *chan_state; + + LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1h->trx->nr); + + for (tn = 0; tn < 8; tn++) { + for (i = 0; i < _TRX_CHAN_MAX; i++) { + msgb_queue_flush(&l1h->dl_prims[tn]); + chan_state = &l1h->chan_states[tn][i]; + if (chan_state->dl_bursts) { + talloc_free(chan_state->dl_bursts); + chan_state->dl_bursts = NULL; + } + if (chan_state->ul_bursts) { + talloc_free(chan_state->ul_bursts); + chan_state->ul_bursts = NULL; + } + } + } +} + + +/* + * data request (from upper layer) + */ + +int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap) +{ + uint8_t tn = l1sap->u.data.chan_nr & 7; + + LOGP(DL1C, LOGL_INFO, "PH-DATA.req: chan_nr=0x%02x link_id=0x%02x " + "fn=%u ts=%u trx=%u\n", l1sap->u.data.chan_nr, + l1sap->u.data.link_id, l1sap->u.data.fn, tn, l1h->trx->nr); + + if (!l1sap->oph.msg) + abort(); + + msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg); + + return 0; +} + +int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap) +{ + uint8_t tn = l1sap->u.tch.chan_nr & 7; + + LOGP(DL1C, LOGL_INFO, "TCH.req: chan_nr=0x%02x " + "fn=%u ts=%u trx=%u\n", l1sap->u.tch.chan_nr, + l1sap->u.tch.fn, tn, l1h->trx->nr); + + if (!l1sap->oph.msg) + abort(); + + msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg); + + return 0; +} + + +/* + * ready-to-send indication (to upper layer) + */ + +/* RTS for data frame */ +static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) +{ + uint8_t chan_nr, link_id; + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* get data for RTS indication */ + chan_nr = trx_chan_desc[chan].chan_nr | tn; + link_id = trx_chan_desc[chan].link_id; + + if (!chan_nr) { + LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing " + "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr); + return -ENODEV; + } + + LOGP(DL1C, LOGL_INFO, "PH-RTS.ind: chan=%s chan_nr=0x%02x " + "link_id=0x%02x fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, + chan_nr, link_id, fn, tn, l1h->trx->nr); + + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) + return -ENOMEM; + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, + PRIM_OP_INDICATION, msg); + l1sap->u.data.chan_nr = chan_nr; + l1sap->u.data.link_id = link_id; + l1sap->u.data.fn = fn; + + return l1sap_up(l1h->trx, l1sap); +} + +/* RTS for traffic frame */ +static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) +{ + uint8_t chan_nr, link_id; + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* get data for RTS indication */ + chan_nr = trx_chan_desc[chan].chan_nr | tn; + link_id = trx_chan_desc[chan].link_id; + + if (!chan_nr) { + LOGP(DL1C, LOGL_FATAL, "RTS func for %s with non-existing " + "chan_nr %d\n", trx_chan_desc[chan].name, chan_nr); + return -ENODEV; + } + + LOGP(DL1C, LOGL_INFO, "TCH.ind: chan=%s chan_nr=0x%02x " + "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, + chan_nr, fn, tn, l1h->trx->nr); + + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) + return -ENOMEM; + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, + PRIM_OP_INDICATION, msg); + l1sap->u.tch.chan_nr = chan_nr | tn; + l1sap->u.tch.fn = fn; + + l1sap_up(l1h->trx, l1sap); + + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) + return -ENOMEM; + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, + PRIM_OP_INDICATION, msg); + l1sap->u.data.chan_nr = chan_nr; + l1sap->u.data.chan_nr = link_id; + l1sap->u.data.fn = fn; + + return l1sap_up(l1h->trx, l1sap); +} + + +/* + * TX on donlink + */ + +/* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ +static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr); + + return NULL; +} + +static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr); + + return fcch_burst; +} + +static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + static ubit_t bits[148], burst[78]; + uint8_t sb_info[4]; + struct gsm_time t; + uint8_t t3p, bsic; + + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr); + + /* create SB info from GSM time and BSIC */ + gsm_fn2gsmtime(&t, fn); + t3p = t.t3 / 10; + bsic = l1h->trx->bts->bsic; + sb_info[0] = + ((bsic & 0x3f) << 2) | + ((t.t1 & 0x600) >> 9); + sb_info[1] = + ((t.t1 & 0x1fe) >> 1); + sb_info[2] = + ((t.t1 & 0x001) << 7) | + ((t.t2 & 0x1f) << 2) | + ((t3p & 0x6) >> 1); + sb_info[2] = + (t3p & 0x1); + + /* encode bursts */ + sch_encode(burst, sb_info); + + /* compose burst */ + memset(bits, 0, 3); + memcpy(bits + 3, burst, 39); + memcpy(bits + 42, sch_train, 64); + memcpy(bits + 106, burst + 39, 39); + memset(bits + 145, 0, 3); + + return bits; +} + +static struct msgb *dequeue_prim(struct trx_l1h *l1h, int8_t tn,uint32_t fn, + enum trx_chan_type chan) +{ + struct msgb *found = NULL, *msg, *msg2; /* make GCC happy */ + struct osmo_phsap_prim *l1sap = NULL; /* make GCC happy */ + uint32_t check_fn; + + /* get burst from queue */ + llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) { + l1sap = msgb_l1sap_prim(msg); + check_fn = ((l1sap->u.data.fn + 2715648 - fn) % 2715648); + if (check_fn > 20) { + LOGP(DL1C, LOGL_ERROR, "Prim for trx=%u ts=%u at fn=%u " + "is out of range. (current fn=%u)\n", + l1h->trx->nr, tn, l1sap->u.data.fn, fn); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + continue; + } + if (check_fn > 0) + continue; + + /* found second message, check if we have TCH+FACCH */ + if (found) { + /* we found a message earlier */ + l1sap = msgb_l1sap_prim(found); + /* if we have TCH+something */ + if (l1sap->oph.primitive == PRIM_TCH) { + /* unlink and free message */ + llist_del(&found->list); + msgb_free(found); + found = msg; + } else { + l1sap = msgb_l1sap_prim(msg); + /* if we have TCH+something */ + if (l1sap->oph.primitive == PRIM_TCH) { + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + } + } + break; + } + found = msg; + } + + if (!found) { + LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + return NULL; + } + + l1sap = msgb_l1sap_prim(found); + + if ((l1sap->oph.primitive != PRIM_PH_DATA + && l1sap->oph.primitive != PRIM_TCH) + || l1sap->oph.operation != PRIM_OP_REQUEST) { + LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has wrong " + "type.\n", tn, fn); +free_msg: + /* unlink and free message */ + llist_del(&found->list); + msgb_free(found); + return NULL; + } + if ((l1sap->u.data.chan_nr ^ (trx_chan_desc[chan].chan_nr | tn)) + || ((l1sap->u.data.link_id ^ trx_chan_desc[chan].link_id) & 0x40)) { + LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has wrong " + "chan_nr=%02x link_id=%02x, expecting chan_nr=%02x " + "link_id=%02x.\n", tn, fn, l1sap->u.data.chan_nr, + l1sap->u.data.link_id, trx_chan_desc[chan].chan_nr | tn, + trx_chan_desc[chan].link_id); + goto free_msg; + } + + return found; +} + +static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len); + +static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct msgb *msg = NULL; /* make GCC happy */ + ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; + static ubit_t bits[148]; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + /* get burst from queue */ + msg = dequeue_prim(l1h, tn, fn, chan); + if (msg) + goto got_msg; + +no_msg: + /* free burst memory */ + if (*bursts_p) { + talloc_free(*bursts_p); + *bursts_p = NULL; + } + return NULL; + +got_msg: + /* check validity of message */ + if (msgb_l2len(msg) != 23) { + LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg)); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + + /* handle loss detection of sacch */ + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { + /* count and send BFI */ + if (++(l1h->chan_states[tn][chan].sacch_lost) > 1) + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0); + } + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return NULL; + } + + /* encode bursts */ + xcch_encode(*bursts_p, msg->l2h); + + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, tsc[l1h->config.tsc], 26); + memcpy(bits + 87, burst + 58, 58); + memset(bits + 145, 0, 3); + + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + return bits; +} + +static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct msgb *msg = NULL; /* make GCC happy */ + ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; + static ubit_t bits[148]; + int rc; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + /* get burst from queue */ + msg = dequeue_prim(l1h, tn, fn, chan); + if (msg) + goto got_msg; + +no_msg: + /* free burst memory */ + if (*bursts_p) { + talloc_free(*bursts_p); + *bursts_p = NULL; + } + return NULL; + +got_msg: + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return NULL; + } + + /* encode bursts */ + rc = pdtch_encode(*bursts_p, msg->l2h, msg->tail - msg->l2h); + + /* check validity of message */ + if (rc) { + LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! " + "(len=%d)\n", rc); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, tsc[l1h->config.tsc], 26); + memcpy(bits + 87, burst + 58, 58); + memset(bits + 145, 0, 3); + + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + return bits; +} + +static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct msgb *msg = NULL; /* make GCC happy */ + ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; + static ubit_t bits[148]; + struct osmo_phsap_prim *l1sap; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + /* get burst from queue */ + msg = dequeue_prim(l1h, tn, fn, chan); + if (msg) + goto got_msg; + +no_msg: + /* free burst memory */ + if (*bursts_p) { + talloc_free(*bursts_p); + *bursts_p = NULL; + } + return NULL; + +got_msg: + l1sap = msgb_l1sap_prim(msg); + if (l1sap->oph.primitive == PRIM_TCH) { + /* check validity of message */ + if (msgb_l2len(msg) != 33) { + LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg)); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + } else { + /* check validity of message */ + if (msgb_l2len(msg) != 23) { + LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg)); + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + goto no_msg; + } + } + + /* alloc burst memory, if not already, + * otherwise shift buffer by 4 bursts for interleaving */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 928); + if (!*bursts_p) + return NULL; + } else + memcpy(*bursts_p, *bursts_p + 464, 464); + + /* encode bursts */ + tch_fr_encode(*bursts_p, msg->l2h, msgb_l2len(msg)); + + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, tsc[l1h->config.tsc], 26); + memcpy(bits + 87, burst + 58, 58); + memset(bits + 145, 0, 3); + + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + return bits; +} + +static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + // FIXME + return NULL; +} + + + +/* + * RX on uplink (indication to upper layer) + */ + +static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) +{ + struct osmo_phsap_prim l1sap; + uint8_t ra; + int rc; + + LOGP(DL1C, LOGL_DEBUG, "Received %s fn=%u\n", + trx_chan_desc[chan].name, fn); + + /* decode */ + rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic); + if (rc) { + LOGP(DL1C, LOGL_NOTICE, "Received bad rach frame at fn=%u " + "ra=%u\n", fn, ra); + return 0; + } + + /* compose primitive */ + /* generate prim */ + memset(&l1sap, 0, sizeof(l1sap)); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, + NULL); + l1sap.u.rach_ind.ra = ra; + l1sap.u.rach_ind.acc_delay = 0; //FIXME: TOA + l1sap.u.rach_ind.fn = fn; + + /* forward primitive */ + l1sap_up(l1h->trx, &l1sap); + + return 0; +} + +static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len) +{ + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* compose primitive */ + msg = l1sap_msgb_alloc(l2_len); + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, + PRIM_OP_INDICATION, msg); + l1sap->u.data.chan_nr = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.data.link_id = trx_chan_desc[chan].link_id; + l1sap->u.data.fn = fn; + msg->l2h = msgb_put(msg, l2_len); + if (l2_len) + memcpy(msg->l2h, l2, l2_len); + + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) + l1h->chan_states[tn][chan].sacch_lost = 0; + + /* forward primitive */ + l1sap_up(l1h->trx, l1sap); + + return 0; +} + +static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) +{ + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint32_t *first_fn = &chan_state->ul_first_fn; + uint8_t *mask = &chan_state->ul_mask; + uint8_t l2[23], l2_len; + int rc; + + LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return -ENOMEM; + } + + /* update mask */ + *mask |= (1 << bid); + + /* store frame number of first burst */ + if (bid == 0) + *first_fn = fn; + + /* copy burst to buffer of 4 bursts */ + burst = *bursts_p + bid * 116; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + // FIXME: decrypt burst + + /* wait until complete set of bursts */ + if ((*mask & 0xf) != 0xf) + return 0; + *mask = 0x0; + + /* decode */ + rc = xcch_decode(l2, *bursts_p); + if (rc) { + LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u for " + "%s\n", *first_fn, trx_chan_desc[chan].name); + l2_len = 0; + } else + l2_len = 23; + + return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len); +} + +static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) +{ + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint32_t *first_fn = &chan_state->ul_first_fn; + uint8_t *mask = &chan_state->ul_mask; + uint8_t l2[54+1]; + int rc; + + LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 464); + if (!*bursts_p) + return -ENOMEM; + } + + /* update mask */ + *mask |= (1 << bid); + + /* store frame number of first burst */ + if (bid == 0) + *first_fn = fn; + + /* copy burst to buffer of 4 bursts */ + burst = *bursts_p + bid * 116; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + // FIXME: decrypt burst + + /* wait until complete set of bursts */ + if ((*mask & 0xf) != 0xf) + return 0; + *mask = 0x0; + + /* decode */ + rc = pdch_decode(l2 + 1, *bursts_p, NULL); + if (rc <= 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH frame at fn=%u for " + "%s\n", *first_fn, trx_chan_desc[chan].name); + l2[0] = 0; /* bad frame */ + rc = 0; + } else + l2[0] = 7; /* valid frame */ + + return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, rc + 1); +} + +static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len) +{ + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* compose primitive */ + msg = l1sap_msgb_alloc(tch_len); + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, + PRIM_OP_INDICATION, msg); + l1sap->u.tch.chan_nr = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.tch.fn = fn; + msg->l2h = msgb_put(msg, tch_len); + if (tch_len) + memcpy(msg->l2h, tch, tch_len); + + /* forward primitive */ + l1sap_up(l1h->trx, l1sap); + + return 0; +} + +static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) +{ + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint32_t *first_fn = &chan_state->ul_first_fn; + uint8_t *mask = &chan_state->ul_mask; + uint8_t tch_data[33]; + int rc; + + LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 928); + if (!*bursts_p) + return -ENOMEM; + } + + /* update mask */ + *mask |= (1 << bid); + + /* store frame number of first burst */ + if (bid == 0) + *first_fn = fn; + + /* copy burst to end of buffer of 8 bursts */ + burst = *bursts_p + bid * 116 + 464; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + // FIXME: decrypt burst + + /* wait until complete set of bursts */ + if (*mask != 0xff) + return 0; + *mask = 0xf0; + + /* decode + * also shift buffer by 4 bursts for interleaving */ + rc = tch_fr_decode(tch_data, *bursts_p); + memcpy(*bursts_p, *bursts_p + 464, 464); + if (rc < 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad tch frame at fn=%u " + "for %s\n", *first_fn, trx_chan_desc[chan].name); + rc = 0; + } + + /* FACCH */ + if (rc == 23) + return compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, + 23); + + /* TCH or BFI */ + return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); +} + +static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) +{ + LOGP(DL1C, LOGL_DEBUG, "TCH/H Received %s fn=%u ts=%u trx=%u bid=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + // FIXME + return 0; +} + + +/* + * multiframe structure + */ + +/* frame structures */ +struct trx_sched_frame { + enum trx_chan_type dl_chan; + uint8_t dl_bid; + enum trx_chan_type ul_chan; + uint8_t ul_bid; +}; + +static struct trx_sched_frame frame_bcch[51] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_BCCH, 0, TRXC_RACH, 0 }, + { TRXC_BCCH, 1, TRXC_RACH, 0 }, + { TRXC_BCCH, 2, TRXC_RACH, 0 }, + { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_IDLE, 0, TRXC_RACH, 0 }, +}; + +static struct trx_sched_frame frame_bcch_sdcch4[102] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 }, + { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 }, + { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 }, + { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 }, + { TRXC_BCCH, 2, TRXC_RACH, 0 }, + { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_SACCH4_2, 0 }, + { TRXC_CCCH, 1, TRXC_SACCH4_2, 1 }, + { TRXC_CCCH, 2, TRXC_SACCH4_2, 2 }, + { TRXC_CCCH, 3, TRXC_SACCH4_2, 3 }, + { TRXC_FCCH, 0, TRXC_SACCH4_3, 0 }, + { TRXC_SCH, 0, TRXC_SACCH4_3, 1 }, + { TRXC_CCCH, 0, TRXC_SACCH4_3, 2 }, + { TRXC_CCCH, 1, TRXC_SACCH4_3, 3 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 }, + { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 }, + { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 }, + { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 }, + { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 }, + { TRXC_SACCH4_0, 0, TRXC_SDCCH4_1, 1 }, + { TRXC_SACCH4_0, 1, TRXC_SDCCH4_1, 2 }, + { TRXC_SACCH4_0, 2, TRXC_SDCCH4_1, 3 }, + { TRXC_SACCH4_0, 3, TRXC_RACH, 0 }, + { TRXC_SACCH4_1, 0, TRXC_RACH, 0 }, + { TRXC_SACCH4_1, 1, TRXC_SDCCH4_2, 0 }, + { TRXC_SACCH4_1, 2, TRXC_SDCCH4_2, 1 }, + { TRXC_SACCH4_1, 3, TRXC_SDCCH4_2, 2 }, + { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 }, + { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 }, + { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 }, + { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 }, + { TRXC_BCCH, 1, TRXC_SDCCH4_3, 3 }, + { TRXC_BCCH, 2, TRXC_RACH, 0 }, + { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_SACCH4_0, 0 }, + { TRXC_CCCH, 1, TRXC_SACCH4_0, 1 }, + { TRXC_CCCH, 2, TRXC_SACCH4_0, 2 }, + { TRXC_CCCH, 3, TRXC_SACCH4_0, 3 }, + { TRXC_FCCH, 0, TRXC_SACCH4_1, 0 }, + { TRXC_SCH, 0, TRXC_SACCH4_1, 1 }, + { TRXC_CCCH, 0, TRXC_SACCH4_1, 2 }, + { TRXC_CCCH, 1, TRXC_SACCH4_1, 3 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, + { TRXC_CCCH, 1, TRXC_RACH, 0 }, + { TRXC_CCCH, 2, TRXC_RACH, 0 }, + { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_0, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_1, 3, TRXC_RACH, 0 }, + { TRXC_FCCH, 0, TRXC_RACH, 0 }, + { TRXC_SCH, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 1, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 2, TRXC_RACH, 0 }, + { TRXC_SDCCH4_2, 3, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 0, TRXC_RACH, 0 }, + { TRXC_SDCCH4_3, 1, TRXC_SDCCH4_0, 0 }, + { TRXC_SDCCH4_3, 2, TRXC_SDCCH4_0, 1 }, + { TRXC_SDCCH4_3, 3, TRXC_SDCCH4_0, 2 }, + { TRXC_FCCH, 0, TRXC_SDCCH4_0, 3 }, + { TRXC_SCH, 0, TRXC_SDCCH4_1, 0 }, + { TRXC_SACCH4_2, 0, TRXC_SDCCH4_1, 1 }, + { TRXC_SACCH4_2, 1, TRXC_SDCCH4_1, 2 }, + { TRXC_SACCH4_2, 2, TRXC_SDCCH4_1, 3 }, + { TRXC_SACCH4_2, 3, TRXC_RACH, 0 }, + { TRXC_SACCH4_3, 0, TRXC_RACH, 0 }, + { TRXC_SACCH4_3, 1, TRXC_SDCCH4_2, 0 }, + { TRXC_SACCH4_3, 2, TRXC_SDCCH4_2, 1 }, + { TRXC_SACCH4_3, 3, TRXC_SDCCH4_2, 2 }, + { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 }, +}; + +static struct trx_sched_frame frame_sdcch8[102] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_SDCCH8_0, 0, TRXC_SACCH8_5, 0 }, + { TRXC_SDCCH8_0, 1, TRXC_SACCH8_5, 1 }, + { TRXC_SDCCH8_0, 2, TRXC_SACCH8_5, 2 }, + { TRXC_SDCCH8_0, 3, TRXC_SACCH8_5, 3 }, + { TRXC_SDCCH8_1, 0, TRXC_SACCH8_6, 0 }, + { TRXC_SDCCH8_1, 1, TRXC_SACCH8_6, 1 }, + { TRXC_SDCCH8_1, 2, TRXC_SACCH8_6, 2 }, + { TRXC_SDCCH8_1, 3, TRXC_SACCH8_6, 3 }, + { TRXC_SDCCH8_2, 0, TRXC_SACCH8_7, 0 }, + { TRXC_SDCCH8_2, 1, TRXC_SACCH8_7, 1 }, + { TRXC_SDCCH8_2, 2, TRXC_SACCH8_7, 2 }, + { TRXC_SDCCH8_2, 3, TRXC_SACCH8_7, 3 }, + { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 }, + { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 }, + { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 }, + { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 }, + { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 }, + { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 }, + { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 }, + { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 }, + { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 }, + { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 }, + { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 }, + { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 }, + { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 }, + { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 }, + { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 }, + { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 }, + { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 }, + { TRXC_SACCH8_0, 0, TRXC_SDCCH8_4, 1 }, + { TRXC_SACCH8_0, 1, TRXC_SDCCH8_4, 2 }, + { TRXC_SACCH8_0, 2, TRXC_SDCCH8_4, 3 }, + { TRXC_SACCH8_0, 3, TRXC_SDCCH8_5, 0 }, + { TRXC_SACCH8_1, 0, TRXC_SDCCH8_5, 1 }, + { TRXC_SACCH8_1, 1, TRXC_SDCCH8_5, 2 }, + { TRXC_SACCH8_1, 2, TRXC_SDCCH8_5, 3 }, + { TRXC_SACCH8_1, 3, TRXC_SDCCH8_6, 0 }, + { TRXC_SACCH8_2, 0, TRXC_SDCCH8_6, 1 }, + { TRXC_SACCH8_2, 1, TRXC_SDCCH8_6, 2 }, + { TRXC_SACCH8_2, 2, TRXC_SDCCH8_6, 3 }, + { TRXC_SACCH8_2, 3, TRXC_SDCCH8_7, 0 }, + { TRXC_SACCH8_3, 0, TRXC_SDCCH8_7, 1 }, + { TRXC_SACCH8_3, 1, TRXC_SDCCH8_7, 2 }, + { TRXC_SACCH8_3, 2, TRXC_SDCCH8_7, 3 }, + { TRXC_SACCH8_3, 3, TRXC_SACCH8_0, 0 }, + { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 }, + { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 }, + { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 }, + { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 }, + { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 }, + { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 }, + { TRXC_SDCCH8_0, 3, TRXC_SACCH8_1, 3 }, + { TRXC_SDCCH8_1, 0, TRXC_SACCH8_2, 0 }, + { TRXC_SDCCH8_1, 1, TRXC_SACCH8_2, 1 }, + { TRXC_SDCCH8_1, 2, TRXC_SACCH8_2, 2 }, + { TRXC_SDCCH8_1, 3, TRXC_SACCH8_2, 3 }, + { TRXC_SDCCH8_2, 0, TRXC_SACCH8_3, 0 }, + { TRXC_SDCCH8_2, 1, TRXC_SACCH8_3, 1 }, + { TRXC_SDCCH8_2, 2, TRXC_SACCH8_3, 2 }, + { TRXC_SDCCH8_2, 3, TRXC_SACCH8_3, 3 }, + { TRXC_SDCCH8_3, 0, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 1, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 2, TRXC_IDLE, 0 }, + { TRXC_SDCCH8_3, 3, TRXC_SDCCH8_0, 0 }, + { TRXC_SDCCH8_4, 0, TRXC_SDCCH8_0, 1 }, + { TRXC_SDCCH8_4, 1, TRXC_SDCCH8_0, 2 }, + { TRXC_SDCCH8_4, 2, TRXC_SDCCH8_0, 3 }, + { TRXC_SDCCH8_4, 3, TRXC_SDCCH8_1, 0 }, + { TRXC_SDCCH8_5, 0, TRXC_SDCCH8_1, 1 }, + { TRXC_SDCCH8_5, 1, TRXC_SDCCH8_1, 2 }, + { TRXC_SDCCH8_5, 2, TRXC_SDCCH8_1, 3 }, + { TRXC_SDCCH8_5, 3, TRXC_SDCCH8_2, 0 }, + { TRXC_SDCCH8_6, 0, TRXC_SDCCH8_2, 1 }, + { TRXC_SDCCH8_6, 1, TRXC_SDCCH8_2, 2 }, + { TRXC_SDCCH8_6, 2, TRXC_SDCCH8_2, 3 }, + { TRXC_SDCCH8_6, 3, TRXC_SDCCH8_3, 0 }, + { TRXC_SDCCH8_7, 0, TRXC_SDCCH8_3, 1 }, + { TRXC_SDCCH8_7, 1, TRXC_SDCCH8_3, 2 }, + { TRXC_SDCCH8_7, 2, TRXC_SDCCH8_3, 3 }, + { TRXC_SDCCH8_7, 3, TRXC_SDCCH8_4, 0 }, + { TRXC_SACCH8_4, 0, TRXC_SDCCH8_4, 1 }, + { TRXC_SACCH8_4, 1, TRXC_SDCCH8_4, 2 }, + { TRXC_SACCH8_4, 2, TRXC_SDCCH8_4, 3 }, + { TRXC_SACCH8_4, 3, TRXC_SDCCH8_5, 0 }, + { TRXC_SACCH8_5, 0, TRXC_SDCCH8_5, 1 }, + { TRXC_SACCH8_5, 1, TRXC_SDCCH8_5, 2 }, + { TRXC_SACCH8_5, 2, TRXC_SDCCH8_5, 3 }, + { TRXC_SACCH8_5, 3, TRXC_SDCCH8_6, 0 }, + { TRXC_SACCH8_6, 0, TRXC_SDCCH8_6, 1 }, + { TRXC_SACCH8_6, 1, TRXC_SDCCH8_6, 2 }, + { TRXC_SACCH8_6, 2, TRXC_SDCCH8_6, 3 }, + { TRXC_SACCH8_6, 3, TRXC_SDCCH8_7, 0 }, + { TRXC_SACCH8_7, 0, TRXC_SDCCH8_7, 1 }, + { TRXC_SACCH8_7, 1, TRXC_SDCCH8_7, 2 }, + { TRXC_SACCH8_7, 2, TRXC_SDCCH8_7, 3 }, + { TRXC_SACCH8_7, 3, TRXC_SACCH8_4, 0 }, + { TRXC_IDLE, 0, TRXC_SACCH8_4, 1 }, + { TRXC_IDLE, 0, TRXC_SACCH8_4, 2 }, + { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 }, +}; + +static struct trx_sched_frame frame_tchf[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, + { TRXC_TCHF, 1, TRXC_TCHF, 1 }, + { TRXC_TCHF, 2, TRXC_TCHF, 2 }, + { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static struct trx_sched_frame frame_tchh[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, + { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, + { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, + { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, +}; + +static struct trx_sched_frame frame_pdch[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 0, TRXC_PTCCH, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 1, TRXC_PTCCH, 1 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 2, TRXC_PTCCH, 2 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PTCCH, 3, TRXC_PTCCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, + { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, + { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, + { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +/* multiframe structure */ +struct trx_sched_multiframe { + enum gsm_phys_chan_config pchan; + uint8_t period; + struct trx_sched_frame *frames; + const char *name; +}; + +static struct trx_sched_multiframe trx_sched_multiframes[] = { + { GSM_PCHAN_NONE, 0, NULL, "NONE"}, + { GSM_PCHAN_CCCH, 51, frame_bcch, "BCCH+CCCH" }, + { GSM_PCHAN_CCCH_SDCCH4, 102, frame_bcch_sdcch4, "BCCH+CCCH+SDCCH/4+SACCH/4" }, + { GSM_PCHAN_SDCCH8_SACCH8C, 102, frame_sdcch8, "SDCCH/8+SACCH/8" }, + { GSM_PCHAN_TCH_F, 104, frame_tchf, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_H, 104, frame_tchh, "TCH/H+SACCH" }, + { GSM_PCHAN_PDCH, 104, frame_pdch, "PDCH" }, +}; + + +/* + * scheduler functions + */ + +/* set multiframe scheduler to given pchan */ +int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, + enum gsm_phys_chan_config pchan) +{ + int i; + + /* ignore disabled slots */ + if (!(l1h->config.slotmask & (1 << tn))) + return -ENOTSUP; + + for (i = 0; ARRAY_SIZE(trx_sched_multiframes); i++) { + if (trx_sched_multiframes[i].pchan == pchan) { + l1h->mf_index[tn] = i; + LOGP(DL1C, LOGL_NOTICE, "Configuring multiframe with " + "%s trx=%d ts=%d\n", + trx_sched_multiframes[i].name, + l1h->trx->nr, tn); + return 0; + } + } + + return -EINVAL; +} + +/* setting all logical channels given attributes to active/inactive */ +int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, + int downlink, int active) +{ + uint8_t tn = L1SAP_CHAN2TS(chan_nr); + int i; + int rc = -EINVAL; + + /* look for all matching chan_nr/link_id */ + for (i = 0; i < _TRX_CHAN_MAX; i++) { + if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) + && trx_chan_desc[i].link_id == link_id) { + LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", + (active) ? "Activating" : "Deactivating", + (downlink) ? "downlink" : "uplink", + trx_chan_desc[i].name, l1h->trx->nr, tn); + if (downlink) { + l1h->chan_states[tn][i].dl_active = active; + l1h->chan_states[tn][i].dl_active = active; + } else { + l1h->chan_states[tn][i].ul_active = active; + l1h->chan_states[tn][i].ul_active = active; + } + l1h->chan_states[tn][i].sacch_lost = 0; + rc = 0; + } + } + + return rc; +} + +/* process ready-to-send */ +static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) +{ + struct trx_sched_frame *frame; + uint8_t offset, period, bid; + trx_sched_rts_func *func; + enum trx_chan_type chan; + + /* no multiframe set */ + if (!l1h->mf_index[tn]) + return 0; + + /* get frame from multiframe */ + period = trx_sched_multiframes[l1h->mf_index[tn]].period; + offset = fn % period; + frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + + chan = frame->dl_chan; + bid = frame->dl_bid; + func = trx_chan_desc[frame->dl_chan].rts_fn; + + /* only on bid == 0 */ + if (bid != 0) + return 0; + + /* no RTS function */ + if (!func) + return 0; + + /* check if channel is active */ + if (!trx_chan_desc[chan].auto_active + && !l1h->chan_states[tn][chan].dl_active) + return -EINVAL; + + return func(l1h, tn, fn, frame->dl_chan); +} + +/* process downlink burst */ +static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, + uint32_t fn) +{ + struct trx_sched_frame *frame; + uint8_t offset, period, bid; + trx_sched_dl_func *func; + enum trx_chan_type chan; + const ubit_t *bits = NULL; + + if (!l1h->mf_index[tn]) + goto no_data; + + /* get frame from multiframe */ + period = trx_sched_multiframes[l1h->mf_index[tn]].period; + offset = fn % period; + frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + + chan = frame->dl_chan; + bid = frame->dl_bid; + func = trx_chan_desc[chan].dl_fn; + + /* check if channel is active */ + if (!trx_chan_desc[chan].auto_active + && !l1h->chan_states[tn][chan].dl_active) + goto no_data; + + /* get burst from function */ + bits = func(l1h, tn, fn, chan, bid); + +no_data: + /* in case of C0, we need a dummy burst to maintain RF power */ + if (bits == NULL && l1h->trx == l1h->trx->bts->c0) { +if (0) if (chan != TRXC_IDLE) // hack + LOGP(DL1C, LOGL_DEBUG, "No burst data for %s fn=%u ts=%u " + "burst=%d on C0, so filling with dummy burst\n", + trx_chan_desc[chan].name, fn, tn, bid); + bits = dummy_burst; + } + + return bits; +} + +/* process uplink burst */ +int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + sbit_t *bits, int8_t rssi, int16_t toa) +{ + struct trx_sched_frame *frame; + uint8_t offset, period, bid; + trx_sched_ul_func *func; + enum trx_chan_type chan; + int rc; + + if (!l1h->mf_index) + return -EINVAL; + + /* get frame from multiframe */ + period = trx_sched_multiframes[l1h->mf_index[tn]].period; + offset = fn % period; + frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + + chan = frame->ul_chan; + bid = frame->ul_bid; + func = trx_chan_desc[chan].ul_fn; + + /* check if channel is active */ + if (!trx_chan_desc[chan].auto_active + && !l1h->chan_states[tn][chan].ul_active) + return -EINVAL; + + /* put burst to function */ + rc = func(l1h, tn, fn, chan, bid, bits, toa); + + return rc; +} + +/* schedule all frames of all TRX for given FN */ +static int trx_sched_fn(uint32_t fn) +{ + struct gsm_bts_trx *trx; + struct trx_l1h *l1h; + uint8_t tn; + const ubit_t *bits; + uint8_t gain; + + /* send time indication */ + l1if_mph_time_ind(bts, fn); + + /* advance frame number, so the tranceiver has more time until + * it must be transmitted. */ + fn = (fn + trx_clock_advance) % 2715648; + + /* process every TRX */ + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = trx_l1h_hdl(trx); + + /* we don't schedule, if power is off */ + if (!l1h->config.poweron) + continue; + + /* process every TS of TRX */ + for (tn = 0; tn < 8; tn++) { + /* ignore disabled slots */ + if (!(l1h->config.slotmask & (1 << tn))) + continue; + /* ready-to-send */ + trx_sched_rts(l1h, tn, + (fn + trx_rts_advance) % 2715648); + /* get burst for FN */ + bits = trx_sched_dl_burst(l1h, tn, fn); + if (!bits) { + /* if no bits, send dummy burst with no gain */ + bits = dummy_burst; + gain = 128; + } else + gain = 0; + trx_if_data(l1h, tn, fn, gain, bits); + } + } + + return 0; +} + + +/* + * frame clock + */ + +#define FRAME_DURATION_uS 4615 +#define MAX_FN_SKEW 50 +#define TRX_LOSS_FRAMES 400 + +extern int quit; +/* this timer fires for every FN to be processed */ +static void trx_ctrl_timer_cb(void *data) +{ + struct timeval tv_now, *tv_clock = &tranceiver_clock_tv; + int32_t elapsed; + + /* check if tranceiver is still alive */ + if (tranceiver_lost++ == TRX_LOSS_FRAMES) { + struct gsm_bts_trx *trx; + + LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); + + tranceiver_available = 0; + + /* flush pending messages of transceiver */ + llist_for_each_entry(trx, &bts->trx_list, list) + trx_if_flush(trx_l1h_hdl(trx)); + + /* start over provisioning tranceiver */ + l1if_provision_tranceiver(bts); + + return; + } + + gettimeofday(&tv_now, NULL); + + elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 + + (tv_now.tv_usec - tv_clock->tv_usec); + + /* if someone played with clock, or if the process stalled */ + if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) { + LOGP(DL1C, LOGL_NOTICE, "PC clock skew: elapsed uS %d\n", + elapsed); + tranceiver_available = 0; + return; + } + + while (elapsed > FRAME_DURATION_uS / 2) { + tv_clock->tv_usec += FRAME_DURATION_uS; + if (tv_clock->tv_usec >= 1000000) { + tv_clock->tv_sec++; + tv_clock->tv_usec -= 1000000; + } + tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; + trx_sched_fn(tranceiver_last_fn); + elapsed -= FRAME_DURATION_uS; + } + osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS - elapsed); +} + + +/* receive clock from tranceiver */ +int trx_sched_clock(uint32_t fn) +{ + struct timeval tv_now, *tv_clock = &tranceiver_clock_tv; + int32_t elapsed; + int32_t elapsed_fn; + + /* reset lost counter */ + tranceiver_lost = 0; + + gettimeofday(&tv_now, NULL); + + /* clock becomes valid */ + if (!tranceiver_available) { + LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n", + fn); +new_clock: + tranceiver_last_fn = fn; + trx_sched_fn(tranceiver_last_fn); + + /* schedule first FN to be transmitted */ + memcpy(tv_clock, &tv_now, sizeof(struct timeval)); + tranceiver_available = 1; + tranceiver_clock_timer.cb = trx_ctrl_timer_cb; + tranceiver_clock_timer.data = bts; + osmo_timer_schedule(&tranceiver_clock_timer, 0, + FRAME_DURATION_uS); + + return 0; + } + + osmo_timer_del(&tranceiver_clock_timer); + + /* calculate elapsed time since last_fn */ + elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 + + (tv_now.tv_usec - tv_clock->tv_usec); + + /* how much frames have been elapsed since last fn processed */ + elapsed_fn = (fn + 2715648 - tranceiver_last_fn) % 2715648; + if (elapsed_fn >= 135774) + elapsed_fn -= 2715648; + + /* check for max clock skew */ + if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) { + LOGP(DL1C, LOGL_NOTICE, "GSM clock skew: old fn=%u, " + "new fn=%u\n", tranceiver_last_fn, fn); + goto new_clock; + } + + LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n", elapsed_fn * FRAME_DURATION_uS - elapsed); + + /* too many frames have been processed already */ + if (elapsed_fn < 0) { + /* set clock to the time or last FN should have been + * transmitted. */ + tv_clock->tv_sec = tv_now.tv_sec; + tv_clock->tv_usec = tv_now.tv_usec + + (0 - elapsed_fn) * FRAME_DURATION_uS; + if (tv_clock->tv_usec >= 1000000) { + tv_clock->tv_sec++; + tv_clock->tv_usec -= 1000000; + } + /* set time to the time our next FN hast to be transmitted */ + osmo_timer_schedule(&tranceiver_clock_timer, 0, + FRAME_DURATION_uS * (1 - elapsed_fn)); + + return 0; + } + + /* transmit what we still need to transmit */ + while (fn != tranceiver_last_fn) { + tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; + trx_sched_fn(tranceiver_last_fn); + } + + /* schedule next FN to be transmitted */ + memcpy(tv_clock, &tv_now, sizeof(struct timeval)); + osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); + + return 0; +} + diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h new file mode 100644 index 00000000..eb82327a --- /dev/null +++ b/src/osmo-bts-trx/scheduler.h @@ -0,0 +1,29 @@ +#ifndef TRX_SCHEDULER_H +#define TRX_SCHEDULER_H + +extern uint32_t trx_clock_advance; +extern uint32_t tranceiver_last_fn; + + +int trx_sched_init(struct trx_l1h *l1h); + +void trx_sched_exit(struct trx_l1h *l1h); + +int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap); + +int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap); + +int trx_sched_clock(uint32_t fn); + +int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + sbit_t *bits, int8_t rssi, int16_t toa); + +/* set multiframe scheduler to given pchan */ +int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, + enum gsm_phys_chan_config pchan); + +/* setting all logical channels given attributes to active/inactive */ +int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, + int downlink, int active); + +#endif /* TRX_SCHEDULER_H */ diff --git a/src/osmo-bts-trx/tch_fr.c b/src/osmo-bts-trx/tch_fr.c new file mode 100644 index 00000000..75f54752 --- /dev/null +++ b/src/osmo-bts-trx/tch_fr.c @@ -0,0 +1,310 @@ +/* + * tch_fr.c + * + * Copyright (c) 2013 Andreas Eversberg + */ + +#include +#include +#include + +#include +#include +#include + +#include "xcch.h" +#include "tch_fr.h" + + +/* + * GSM TCH FR/EFR parity + * + * g(x) = x^3 + x + 1 + */ + +const struct osmo_crc8gen_code tch_fr_crc3 = { + .bits = 3, + .poly = 0x2, + .init = 0x0, + .remainder = 0x7, +}; + + +/* + * GSM TCH FR/EFR convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_tch_fr_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_fr_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_tch_fr = { + .N = 2, + .K = 5, + .len = 185, + .next_output = conv_tch_fr_next_output, + .next_state = conv_tch_fr_next_state, +}; + + +/* + * GSM TCH FR/EFR interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 8 blocks of 114 bits, + * where event bits of the first 4 block and off bits of the last 4 block + * are used: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 8) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +static void +tch_fr_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 7; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + cB[k] = iB[B * 114 + j]; + } +} + +static void +tch_fr_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 7; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + iB[B * 114 + j] = cB[k]; + } +} + +static void +tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) +{ + int i; + + /* brainfuck: only copy even or odd bits */ + for (i=odd; i<57; i+=2) + iB[i] = eB[i]; + for (i=58-odd; i<114; i+=2) + iB[i] = eB[i+2]; + + if (h && !odd) + *h = eB[57]; + + if (h && odd) + *h = eB[58]; +} + +static void +tch_fr_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *h, int odd) +{ + int i; + + /* brainfuck: only copy even or odd bits */ + for (i=odd; i<57; i+=2) + eB[i] = iB[i]; + for (i=58-odd; i<114; i+=2) + eB[i+2] = iB[i]; + + if (h && !odd) + eB[57] = *h; + if (h && odd) + eB[58] = *h; +} + +/* this corresponds to the bit-lengths of the individual codec + * parameters as indicated in Table 1.1 of TS 06.10 */ +static const uint8_t gsm_fr_map[] = { + 6, 6, 5, 5, 4, 4, 3, 3, + 7, 2, 2, 6, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 7, 2, 2, 6, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 7, 2, 2, 6, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 7, 2, 2, 6, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3 +}; + +static void +tch_fr_reassemble(uint8_t *tch_data, ubit_t *d_bits) +{ + int i, j, k, l, o; + + tch_data[0] = 0xd << 4; + /* reassemble d-bits */ + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + k = gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset input bits */ + while (i < 260) { + tch_data[j>>3] |= (d_bits[k+o] << (7-(j&7))); + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } +} + +static void +tch_fr_disassemble(ubit_t *d_bits, uint8_t *tch_data) +{ + int i, j, k, l, o; + + i = 0; /* counts bits */ + j = 4; /* counts input bits */ + k = gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset output bits */ + while (i < 260) { + d_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } +} + +static void +tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) +{ + int i; + + for (i=0; i<91; i++) { + d[i<<1] = u[i]; + d[(i<<1)+1] = u[184-i]; + } + for (i=0; i<3; i++) + p[i] = u[91+i]; +} + +static void +tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) +{ + int i; + + for (i=0; i<91; i++) { + u[i] = d[i<<1]; + u[184-i] = d[(i<<1)+1]; + } + for (i=0; i<3; i++) + u[91+i] = p[i]; +} + +int +tch_fr_decode(uint8_t *tch_data, sbit_t *bursts) +{ + sbit_t iB[912], cB[456], h; + ubit_t conv[185], d[260], p[3]; + int i, rv, len, steal = 0; + + for (i=0; i<8; i++) { + tch_fr_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i>>2); + if (h < 0) + steal++; + } + + tch_fr_deinterleave(cB, iB); + + if (steal < 4) { + osmo_conv_decode(&conv_tch_fr, cB, conv); + + tch_fr_unreorder(d, p, conv); + + for (i=0; i<78; i++) + d[i+182] = (cB[i+378] < 0) ? 1:0; + + rv = osmo_crc8gen_check_bits(&tch_fr_crc3, d, 50, p); + if (rv) + return -1; + + tch_fr_reassemble(tch_data, d); + + len = 33; + } else { + rv = xcch_decode_cB(tch_data, cB); + if (rv) + return -1; + + len = 23; + } + + return len; +} + +int +tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len) +{ + ubit_t iB[912], cB[456], h; + ubit_t conv[185], d[260], p[3]; + int i; + + switch (len) { + case 33: /* TCH FR */ + tch_fr_disassemble(d, tch_data); + + osmo_crc8gen_set_bits(&tch_fr_crc3, d, 50, p); + + tch_fr_reorder(conv, d, p); + + memcpy(cB+378, d+182, 78); + + osmo_conv_encode(&conv_tch_fr, conv, cB); + + h = 0; + + break; + case 23: /* FACCH */ + xcch_encode_cB(cB, tch_data); + + h = 1; + + break; + default: + return -1; + } + + tch_fr_interleave(cB, iB); + + for (i=0; i<8; i++) + tch_fr_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>2); + + return 0; +} diff --git a/src/osmo-bts-trx/tch_fr.h b/src/osmo-bts-trx/tch_fr.h new file mode 100644 index 00000000..68a93185 --- /dev/null +++ b/src/osmo-bts-trx/tch_fr.h @@ -0,0 +1,7 @@ +#ifndef _TCH_FR_H +#define _TCH_FR_H + +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts); +int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len); + +#endif /* _TCH_FR_H */ diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c new file mode 100644 index 00000000..abf992e0 --- /dev/null +++ b/src/osmo-bts-trx/trx_if.c @@ -0,0 +1,495 @@ +/* + * OpenBTS TRX interface handling + * + * Copyright (C) 2013 Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "l1_if.h" +#include "trx_if.h" +#include "scheduler.h" + +int tranceiver_available = 0; +const char *tranceiver_ip = "127.0.0.1"; + +/* + * socket + */ + +static uint16_t base_port_local = 5800; + +/* open socket */ +static int trx_udp_open(void *priv, struct osmo_fd *ofd, uint16_t port, + int (*cb)(struct osmo_fd *fd, unsigned int what)) +{ + struct sockaddr_storage sas; + struct sockaddr *sa = (struct sockaddr *)&sas; + socklen_t sa_len; + + int rc; + + /* Init */ + ofd->fd = -1; + ofd->cb = cb; + ofd->data = priv; + + /* Listen / Binds */ + rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, tranceiver_ip, + port, OSMO_SOCK_F_BIND); + if (rc < 0) + return rc; + + /* Connect */ + sa_len = sizeof(sas); + rc = getsockname(ofd->fd, sa, &sa_len); + if (rc) + return rc; + + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + sin->sin_port = htons(ntohs(sin->sin_port) - 100); + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + sin6->sin6_port = htons(ntohs(sin6->sin6_port) - 100); + } else { + return -EINVAL; + } + + rc = connect(ofd->fd, sa, sa_len); + if (rc) + return rc; + + + return 0; +} + +/* close socket */ +static void trx_udp_close(struct osmo_fd *ofd) +{ + if (ofd->fd > 0) { + osmo_fd_unregister(ofd); + close(ofd->fd); + ofd->fd = -1; + } +} + + +/* + * clock + */ + +static struct osmo_fd trx_ofd_clk; + + +/* get clock from clock socket */ +static int trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what) +{ + char buf[1500]; + int len; + + len = recv(ofd->fd, buf, sizeof(buf) - 1, 0); + if (len <= 0) + return len; + buf[len] = '\0'; + + if (!strncmp(buf, "IND CLOCK ", 10)) { + uint32_t fn; + + sscanf(buf, "IND CLOCK %u", &fn); + LOGP(DTRX, LOGL_INFO, "Clock indication: fn=%u\n", fn); + trx_sched_clock(fn); + } else + LOGP(DTRX, LOGL_NOTICE, "Unknown message on clock port: %s\n", + buf); + + return 0; +} + + +/* + * ctrl + */ + +static void trx_ctrl_timer_cb(void *data); + +/* send first ctrl message and start timer */ +static void trx_ctrl_send(struct trx_l1h *l1h) +{ + struct trx_ctrl_msg *tcm; + + /* get first command */ + if (llist_empty(&l1h->trx_ctrl_list)) + return; + tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg, list); + + LOGP(DTRX, LOGL_DEBUG, "Sending control '%s' to trx=%u\n", tcm->cmd, + l1h->trx->nr); + /* send command */ + send(l1h->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd)+1, 0); + + /* start timer */ + l1h->trx_ctrl_timer.cb = trx_ctrl_timer_cb; + l1h->trx_ctrl_timer.data = l1h; + osmo_timer_schedule(&l1h->trx_ctrl_timer, 2, 0); +} + +/* send first ctrl message and start timer */ +static void trx_ctrl_timer_cb(void *data) +{ + struct trx_l1h *l1h = data; + + LOGP(DTRX, LOGL_NOTICE, "No response from tranceiver\n"); + + trx_ctrl_send(l1h); +} + +/* add a new ctrl command */ +static int trx_ctrl_cmd(struct trx_l1h *l1h, const char *cmd, const char *fmt, + ...) +{ + struct trx_ctrl_msg *tcm; + va_list ap; + int l; + + /* create message */ + tcm = talloc_zero(tall_bts_ctx, struct trx_ctrl_msg); + if (!tcm) + return -ENOMEM; + if (fmt && fmt[0]) { + l = snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s ", cmd); + va_start(ap, fmt); + vsnprintf(tcm->cmd + l, sizeof(tcm->cmd) - l - 1, fmt, ap); + va_end(ap); + } else + snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s", cmd); + tcm->cmd_len = strlen(cmd); + llist_add_tail(&tcm->list, &l1h->trx_ctrl_list); + LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd); + + /* send message, if no pending message */ + if (!osmo_timer_pending(&l1h->trx_ctrl_timer)) + trx_ctrl_send(l1h); + + return 0; +} + +int trx_if_cmd_poweroff(struct trx_l1h *l1h) +{ + return trx_ctrl_cmd(l1h, "POWEROFF", ""); +} + +int trx_if_cmd_poweron(struct trx_l1h *l1h) +{ + return trx_ctrl_cmd(l1h, "POWERON", ""); +} + +int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc) +{ + return trx_ctrl_cmd(l1h, "SETTSC", "%d", tsc); +} + +int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic) +{ + return trx_ctrl_cmd(l1h, "SETBSIC", "%d", bsic); +} + +int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db) +{ + return trx_ctrl_cmd(l1h, "SETRXGAIN", "%d", db); +} + +int trx_if_cmd_setpower(struct trx_l1h *l1h, int db) +{ + return trx_ctrl_cmd(l1h, "SETPOWER", "%d", db); +} + +int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly) +{ + return trx_ctrl_cmd(l1h, "SETMAXDLY", "%d", dly); +} + +int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type) +{ + return trx_ctrl_cmd(l1h, "SETSLOT", "%d %d", tn, type); +} + +int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn) +{ + uint16_t freq10; + + freq10 = gsm_arfcn2freq10(arfcn, 1); /* RX = uplink */ + if (freq10 == 0xffff) { + LOGP(DTRX, LOGL_ERROR, "Arfcn %d not defined.\n", arfcn); + return -ENOTSUP; + } + + return trx_ctrl_cmd(l1h, "RXTUNE", "%d", freq10 * 100); +} + +int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn) +{ + uint16_t freq10; + + freq10 = gsm_arfcn2freq10(arfcn, 0); /* TX = downlink */ + if (freq10 == 0xffff) { + LOGP(DTRX, LOGL_ERROR, "Arfcn %d not defined.\n", arfcn); + return -ENOTSUP; + } + + return trx_ctrl_cmd(l1h, "TXTUNE", "%d", freq10 * 100); +} + +/* get response from ctrl socket */ +static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct trx_l1h *l1h = ofd->data; + char buf[1500]; + int len, resp; + + len = recv(ofd->fd, buf, sizeof(buf) - 1, 0); + if (len <= 0) + return len; + buf[len] = '\0'; + + if (!strncmp(buf, "RSP ", 4)) { + struct trx_ctrl_msg *tcm; + char *p; + int rsp_len = 0; + + /* calculate the length of response item */ + p = strchr(buf + 4, ' '); + if (p) + rsp_len = p - buf - 4; + else + rsp_len = strlen(buf) - 4; + + LOGP(DTRX, LOGL_DEBUG, "Response message: '%s'\n", buf); + + /* get command for response message */ + if (llist_empty(&l1h->trx_ctrl_list)) { + LOGP(DTRX, LOGL_NOTICE, "Response message without " + "command\n"); + return -EINVAL; + } + tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg, + list); + + /* check if respose matches command */ + if (rsp_len != tcm->cmd_len) { + notmatch: + LOGP(DTRX, LOGL_NOTICE, "Response message '%s' does " + "not match command message '%s'\n", buf, + tcm->cmd); + return -EINVAL; + } + if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) + goto notmatch; + + /* check for response code */ + if (*p) { + sscanf(p + 1, "%d", &resp); + if (resp) { + LOGP(DTRX, LOGL_ERROR, "Tranceiver rejected " + "TRX command with response: '%s'\n", + buf); + } + } + + /* remove command from list */ + llist_del(&tcm->list); + talloc_free(tcm); + + /* abort timer and send next message, if any */ + if (osmo_timer_pending(&l1h->trx_ctrl_timer)) + osmo_timer_del(&l1h->trx_ctrl_timer); + trx_ctrl_send(l1h); + } else + LOGP(DTRX, LOGL_NOTICE, "Unknown message on ctrl port: %s\n", + buf); + + return 0; +} + + +/* + * data + */ + +static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) +{ + struct trx_l1h *l1h = ofd->data; + uint8_t buf[256]; + int len; + uint8_t tn; + int8_t rssi; + int16_t toa; + uint32_t fn; + sbit_t bits[148]; + int i; + + len = recv(ofd->fd, buf, sizeof(buf), 0); + if (len <= 0) + return len; + if (len != 158) { + LOGP(DTRX, LOGL_NOTICE, "Got data message with invalid lenght " + "'%d'\n", len); + return -EINVAL; + } + tn = buf[0]; + fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; + rssi = (int8_t)buf[5]; + toa = (int16_t)(buf[6] << 8) | buf[7]; + + /* copy and convert bits {254..0} to sbits {-127..127} */ + for (i = 0; i < 148; i++) { + if (buf[8 + i] == 255) + bits[i] = -127; + else + bits[i] = 127 - buf[8 + i]; + } + + if (tn >= 8) { + LOGP(DTRX, LOGL_ERROR, "Illegal TS %d\n", tn); + return -EINVAL; + } + if (fn >= 2715648) { + LOGP(DTRX, LOGL_ERROR, "Illegal FN %u\n", fn); + return -EINVAL; + } + + LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d ", + tn, fn, rssi, toa); + + trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa); + + return 0; +} + +int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr, + const ubit_t *bits) +{ + uint8_t buf[256]; + + LOGP(DTRX, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr); + + buf[0] = tn; + buf[1] = (fn >> 24) & 0xff; + buf[2] = (fn >> 16) & 0xff; + buf[3] = (fn >> 8) & 0xff; + buf[4] = (fn >> 0) & 0xff; + buf[5] = pwr; + + /* copy ubits {0,1} */ + memcpy(buf + 6, bits, 148); + + /* we must be sure that we have clock, and we have sent all control + * data */ + if (tranceiver_available && llist_empty(&l1h->trx_ctrl_list)) { + send(l1h->trx_ofd_data.fd, buf, 154, 0); + } else + LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, tranceiver " + "offline.\n"); + + return 0; +} + + +/* + * open/close + */ + +int trx_if_open(struct trx_l1h *l1h) +{ + int rc; + + LOGP(DTRX, LOGL_NOTICE, "Open tranceiver for trx=%u\n", l1h->trx->nr); + + /* open sockets */ + if (l1h->trx->nr == 0) { + rc = trx_udp_open(NULL, &trx_ofd_clk, base_port_local, + trx_clk_read_cb); + if (rc < 0) + return rc; + LOGP(DTRX, LOGL_NOTICE, "Waiting for tranceiver send clock\n"); + } + rc = trx_udp_open(l1h, &l1h->trx_ofd_ctrl, + base_port_local + (l1h->trx->nr << 1) + 1, trx_ctrl_read_cb); + if (rc < 0) + goto err; + rc = trx_udp_open(l1h, &l1h->trx_ofd_data, + base_port_local + (l1h->trx->nr << 1) + 2, trx_data_read_cb); + if (rc < 0) + goto err; + + /* initialize ctrl queue */ + INIT_LLIST_HEAD(&l1h->trx_ctrl_list); + + /* enable all slots */ + l1h->config.slotmask = 0xff; + + return 0; + +err: + trx_if_close(l1h); + return rc; +} + +/* flush pending control messages */ +void trx_if_flush(struct trx_l1h *l1h) +{ + struct trx_ctrl_msg *tcm; + + /* free ctrl message list */ + while (!llist_empty(&l1h->trx_ctrl_list)) { + tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg, + list); + llist_del(&tcm->list); + talloc_free(tcm); + } +} + +void trx_if_close(struct trx_l1h *l1h) +{ + LOGP(DTRX, LOGL_NOTICE, "Close tranceiver for trx=%u\n", l1h->trx->nr); + + trx_if_flush(l1h); + + /* close sockets */ + if (l1h->trx->nr == 0) + trx_udp_close(&trx_ofd_clk); + trx_udp_close(&l1h->trx_ofd_ctrl); + trx_udp_close(&l1h->trx_ofd_data); +} + diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h new file mode 100644 index 00000000..656bd6a7 --- /dev/null +++ b/src/osmo-bts-trx/trx_if.h @@ -0,0 +1,29 @@ +#ifndef TRX_IF_H +#define TRX_IF_H + +extern int tranceiver_available; +extern const char *tranceiver_ip; + +struct trx_ctrl_msg { + struct llist_head list; + char cmd[128]; + int cmd_len; +}; + +int trx_if_cmd_poweroff(struct trx_l1h *l1h); +int trx_if_cmd_poweron(struct trx_l1h *l1h); +int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc); +int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic); +int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db); +int trx_if_cmd_setpower(struct trx_l1h *l1h, int db); +int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly); +int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type); +int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn); +int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn); +int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr, + const ubit_t *bits); +int trx_if_open(struct trx_l1h *l1h); +void trx_if_flush(struct trx_l1h *l1h); +void trx_if_close(struct trx_l1h *l1h); + +#endif /* TRX_IF_H */ diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c new file mode 100644 index 00000000..720f3538 --- /dev/null +++ b/src/osmo-bts-trx/trx_vty.c @@ -0,0 +1,281 @@ +/* VTY interface for sysmoBTS */ + +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "l1_if.h" +#include "scheduler.h" +#include "trx_if.h" + +static struct gsm_bts *vty_bts; + +DEFUN(show_tranceiver, show_tranceiver_cmd, "show tranceiver", + SHOW_STR "Display information about tranceivers\n") +{ + struct gsm_bts *bts = vty_bts; + struct gsm_bts_trx *trx; + struct trx_l1h *l1h; + uint8_t tn; + + if (!tranceiver_available) { + vty_out(vty, "Tranceiver is not connected%s", VTY_NEWLINE); + } else { + vty_out(vty, "Tranceiver is connected, current fn=%u%s", + tranceiver_last_fn, VTY_NEWLINE); + } + + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = trx_l1h_hdl(trx); + vty_out(vty, "TRX %d%s", trx->nr, VTY_NEWLINE); + vty_out(vty, " %s%s", + (l1h->config.poweron) ? "poweron":"poweroff", + VTY_NEWLINE); + if (l1h->config.arfcn_valid) + vty_out(vty, " arfcn : %d%s%s", + (l1h->config.arfcn & ~ARFCN_PCS), + (l1h->config.arfcn & ARFCN_PCS) ? " (PCS)" : "", + VTY_NEWLINE); + else + vty_out(vty, " arfcn : undefined%s", VTY_NEWLINE); + if (l1h->config.tsc_valid) + vty_out(vty, " tsc : %d%s", l1h->config.tsc, + VTY_NEWLINE); + else + vty_out(vty, " tsc : undefined%s", VTY_NEWLINE); + if (l1h->config.bsic_valid) + vty_out(vty, " bsic : %d%s", l1h->config.bsic, + VTY_NEWLINE); + else + vty_out(vty, " bisc : undefined%s", VTY_NEWLINE); + if (l1h->config.rxgain_valid) + vty_out(vty, " rxgain : %d%s", l1h->config.rxgain, + VTY_NEWLINE); + else + vty_out(vty, " rxgain : undefined%s", VTY_NEWLINE); + if (l1h->config.power_valid) + vty_out(vty, " power : %d%s", l1h->config.power, + VTY_NEWLINE); + else + vty_out(vty, " power : undefined%s", VTY_NEWLINE); + if (l1h->config.maxdly_valid) + vty_out(vty, " maxdly : %d%s", l1h->config.maxdly, + VTY_NEWLINE); + else + vty_out(vty, " maxdly : undefined%s", VTY_NEWLINE); + for (tn = 0; tn < 8; tn++) { + if (!((1 << tn) & l1h->config.slotmask)) + vty_out(vty, " slot #%d: unsupported%s", tn, + VTY_NEWLINE); + else if (l1h->config.slottype_valid[tn]) + vty_out(vty, " slot #%d: type %d%s", tn, + l1h->config.slottype[tn], + VTY_NEWLINE); + else + vty_out(vty, " slot #%d: undefined%s", tn, + VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd, + "fn-advance <0-30>", + "Set the number of frames to be transmitted in advance of current FN\n" + "Advance in frames\n") +{ + trx_clock_advance = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_rxgain, cfg_trx_rxgain_cmd, + "rxgain <0-50>", + "Set the receiver gain in dB\n" + "Gain in dB\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.rxgain = atoi(argv[0]); + l1h->config.rxgain_valid = 1; + l1h->config.rxgain_sent = 0; + l1if_provision_tranceiver_trx(l1h); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_power, cfg_trx_power_cmd, + "power <0-50>", + "Set the transmitter power dampening\n" + "Power dampening in dB\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.power = atoi(argv[0]); + l1h->config.power_valid = 1; + l1h->config.power_sent = 0; + l1if_provision_tranceiver_trx(l1h); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_maxdly, cfg_trx_maxdly_cmd, + "maxdly <0-31>", + "Set the maximum delay of GSM symbols\n" + "GSM symbols (approx. 1.1km per symbol)\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.maxdly = atoi(argv[0]); + l1h->config.maxdly_valid = 1; + l1h->config.maxdly_sent = 0; + l1if_provision_tranceiver_trx(l1h); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_slotmask, cfg_trx_slotmask_cmd, + "slotmask (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0)", + "Set the supported slots\n" + "TS0 supported\nTS0 unsupported\nTS1 supported\nTS1 unsupported\n" + "TS2 supported\nTS2 unsupported\nTS3 supported\nTS3 unsupported\n" + "TS4 supported\nTS4 unsupported\nTS5 supported\nTS5 unsupported\n" + "TS6 supported\nTS6 unsupported\nTS7 supported\nTS7 unsupported\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + uint8_t tn; + + l1h->config.slotmask = 0; + for (tn = 0; tn < 8; tn++) + if (argv[tn][0] == '1') + l1h->config.slotmask |= (1 << tn); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_no_rxgain, cfg_trx_no_rxgain_cmd, + "no rxgain <0-50>", + NO_STR "Unset the receiver gain in dB\n" + "Gain in dB\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.rxgain_valid = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_no_power, cfg_trx_no_power_cmd, + "no power <0-50>", + NO_STR "Unset the transmitter power dampening\n" + "Power dampening in dB\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.power_valid = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_no_maxdly, cfg_trx_no_maxdly_cmd, + "no maxdly <0-31>", + NO_STR "Unset the maximum delay of GSM symbols\n" + "GSM symbols (approx. 1.1km per symbol)\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.maxdly_valid = 0; + + return CMD_SUCCESS; +} + +void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts) +{ + vty_out(vty, " fn-advance %d%s", trx_clock_advance, VTY_NEWLINE); +} + +void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx) +{ + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + if (l1h->config.rxgain_valid) + vty_out(vty, " rxgain %d%s", l1h->config.rxgain, VTY_NEWLINE); + if (l1h->config.power_valid) + vty_out(vty, " power %d%s", l1h->config.power, VTY_NEWLINE); + if (l1h->config.maxdly_valid) + vty_out(vty, " maxdly %d%s", l1h->config.maxdly, VTY_NEWLINE); + if (l1h->config.slotmask != 0xff) + vty_out(vty, " slotmask %d %d %d %d %d %d %d %d%s", + l1h->config.slotmask & 1, + (l1h->config.slotmask >> 1) & 1, + (l1h->config.slotmask >> 2) & 1, + (l1h->config.slotmask >> 3) & 1, + (l1h->config.slotmask >> 4) & 1, + (l1h->config.slotmask >> 5) & 1, + (l1h->config.slotmask >> 6) & 1, + l1h->config.slotmask >> 7, + VTY_NEWLINE); +} + +int bts_model_vty_init(struct gsm_bts *bts) +{ + vty_bts = bts; + + install_element_ve(&show_tranceiver_cmd); + + install_element(BTS_NODE, &cfg_bts_fn_advance_cmd); + + install_element(TRX_NODE, &cfg_trx_rxgain_cmd); + install_element(TRX_NODE, &cfg_trx_power_cmd); + install_element(TRX_NODE, &cfg_trx_maxdly_cmd); + install_element(TRX_NODE, &cfg_trx_slotmask_cmd); + install_element(TRX_NODE, &cfg_trx_no_rxgain_cmd); + install_element(TRX_NODE, &cfg_trx_no_power_cmd); + install_element(TRX_NODE, &cfg_trx_no_maxdly_cmd); + + return 0; +} diff --git a/src/osmo-bts-trx/xcch.c b/src/osmo-bts-trx/xcch.c new file mode 100644 index 00000000..024bf071 --- /dev/null +++ b/src/osmo-bts-trx/xcch.c @@ -0,0 +1,193 @@ +/* + * xcch.c + * + * Copyright (c) 2011 Sylvain Munaut + */ + +#include +#include +#include + +#include +#include +#include + +#include "xcch.h" + + +/* + * GSM xCCH parity (FIRE code) + * + * g(x) = (x^23 + 1)(x^17 + x^3 + 1) + * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 + */ + +const struct osmo_crc64gen_code xcch_crc40 = { + .bits = 40, + .poly = 0x0004820009ULL, + .init = 0x0000000000ULL, + .remainder = 0xffffffffffULL, +}; + + +/* + * GSM xCCH convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_xcch_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_xcch_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const struct osmo_conv_code conv_xcch = { + .N = 2, + .K = 5, + .len = 224, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + +/* + * GSM xCCH interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 4 blocks of 114 bits: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 4) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +static void +xcch_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + cB[k] = iB[B * 114 + j]; + } +} + +static void +xcch_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + iB[B * 114 + j] = cB[k]; + } +} + +static void +xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn) +{ + memcpy(iB, eB, 57); + memcpy(iB+57, eB+59, 57); + + if (hl) + *hl = eB[57]; + + if (hn) + *hn = eB[58]; +} + +static void +xcch_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *hl, ubit_t *hn) +{ + memcpy(eB, iB, 57); + memcpy(eB+59, iB+57, 57); + + if (hl) + eB[57] = *hl; + if (hn) + eB[58] = *hn; +} + +int +xcch_decode_cB(uint8_t *l2_data, sbit_t *cB) +{ + ubit_t conv[224]; + int rv; + + osmo_conv_decode(&conv_xcch, cB, conv); + + rv = osmo_crc64gen_check_bits(&xcch_crc40, conv, 184, conv+184); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); + + return 0; +} + +int +xcch_decode(uint8_t *l2_data, sbit_t *bursts) +{ + sbit_t iB[456], cB[456]; + int i; + + for (i=0; i<4; i++) + xcch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, NULL); + + xcch_deinterleave(cB, iB); + + return xcch_decode_cB(l2_data, cB); +} + +int +xcch_encode_cB(ubit_t *cB, uint8_t *l2_data) +{ + ubit_t conv[224]; + + osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); + + osmo_crc64gen_set_bits(&xcch_crc40, conv, 184, conv+184); + + osmo_conv_encode(&conv_xcch, conv, cB); + + return 0; +} + +int +xcch_encode(ubit_t *bursts, uint8_t *l2_data) +{ + ubit_t iB[456], cB[456], hl = 1, hn = 1; + int i; + + xcch_encode_cB(cB, l2_data); + + xcch_interleave(cB, iB); + + for (i=0; i<4; i++) + xcch_burst_map(&iB[i * 114], &bursts[i * 116], &hl, &hn); + + return 0; +} diff --git a/src/osmo-bts-trx/xcch.h b/src/osmo-bts-trx/xcch.h new file mode 100644 index 00000000..8e7d3929 --- /dev/null +++ b/src/osmo-bts-trx/xcch.h @@ -0,0 +1,10 @@ +#ifndef _XCCH_H +#define _XCCH_H + +int xcch_decode_cB(uint8_t *l2_data, sbit_t *cB); +int xcch_decode(uint8_t *l2_data, sbit_t *bursts); +int xcch_encode_cB(ubit_t *cB, uint8_t *l2_data); +int xcch_encode(ubit_t *bursts, uint8_t *l2_data); + +#endif /* _XCCH_H */ + -- cgit v1.2.3 From 1de7085d3181b8d29929e80f29448e2c7d6eda3b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 11:52:38 +0100 Subject: Add test code for testing GSM burst transcoding --- .gitignore | 1 + configure.ac | 1 + tests/Makefile.am | 2 +- tests/bursts/Makefile.am | 8 ++ tests/bursts/bursts_test.c | 210 ++++++++++++++++++++++++++++++++++++++++++++ tests/bursts/bursts_test.ok | 1 + tests/testsuite.at | 6 ++ 7 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 tests/bursts/Makefile.am create mode 100644 tests/bursts/bursts_test.c create mode 100644 tests/bursts/bursts_test.ok diff --git a/.gitignore b/.gitignore index eda0126c..caa6cd77 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ tests/paging/paging_test tests/cipher/cipher_test tests/sysmobts/sysmobts_test tests/misc/misc_test +tests/bursts/bursts_test tests/testsuite tests/testsuite.log diff --git a/configure.ac b/configure.ac index 752e85b3..d3fcff28 100644 --- a/configure.ac +++ b/configure.ac @@ -87,4 +87,5 @@ AC_OUTPUT( tests/cipher/Makefile tests/sysmobts/Makefile tests/misc/Makefile + tests/bursts/Makefile Makefile) diff --git a/tests/Makefile.am b/tests/Makefile.am index 955d8f37..70055909 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = paging cipher agch misc +SUBDIRS = paging cipher agch misc bursts if ENABLE_SYSMOBTS SUBDIRS += sysmobts diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am new file mode 100644 index 00000000..9f88f9b9 --- /dev/null +++ b/tests/bursts/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) +LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) +noinst_PROGRAMS = bursts_test +EXTRA_DIST = bursts_test.ok + +bursts_test_SOURCES = bursts_test.c $(top_builddir)/src/osmo-bts-trx/xcch.c $(top_builddir)/src/osmo-bts-trx/rach.c $(top_builddir)/src/osmo-bts-trx/sch.c +bursts_test_LDADD = $(LDADD) diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c new file mode 100644 index 00000000..07769133 --- /dev/null +++ b/tests/bursts/bursts_test.c @@ -0,0 +1,210 @@ +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "../../src/osmo-bts-trx/xcch.h" +#include "../../src/osmo-bts-trx/rach.h" +#include "../../src/osmo-bts-trx/sch.h" + + +#define ASSERT_TRUE(rc) \ + if (!(rc)) { \ + printf("Assert failed in %s:%d.\n", \ + __FILE__, __LINE__); \ + abort(); \ + } + +/* set condition to 1, to show debugging */ +#define printd if (0) printf + +static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if ((*ubits++) & 1) + *sbits++ = -127; + else + *sbits++ = 127; + } + + return count; +} + +static void test_xcch(uint8_t *l2) +{ + uint8_t result[23]; + ubit_t bursts_u[116 * 4]; + sbit_t bursts_s[116 * 4]; + + printd("Encoding: %s\n", osmo_hexdump(l2, 23)); + + /* encode */ + xcch_encode(bursts_u, l2); + + printd("U-Bits:\n"); + printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u, 57), + bursts_u[57], bursts_u[58], + osmo_hexdump(bursts_u + 59, 57)); + printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u + 116, 57), + bursts_u[57 + 116], bursts_u[58 + 116], + osmo_hexdump(bursts_u + 59 + 116, 57)); + printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u + 232, 57), + bursts_u[57 + 232], bursts_u[58 + 232], + osmo_hexdump(bursts_u + 59 + 232, 57)); + printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u + 348, 57), + bursts_u[57 + 348], bursts_u[58 + 348], + osmo_hexdump(bursts_u + 59 + 348, 57)); + ubits2sbits(bursts_u, bursts_s, 116 * 4); + printd("S-Bits:\n"); + printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s, 57), + (uint8_t)bursts_s[57], (uint8_t)bursts_s[58], + osmo_hexdump((uint8_t *)bursts_s + 59, 57)); + printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s + 116, 57), + (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116], + osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); + printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s + 232, 57), + (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232], + osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); + printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s + 348, 57), + (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348], + osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); + + /* destroy */ + memset(bursts_s, 0, 30); + memset(bursts_s + 116, 0, 30); + + /* decode */ + xcch_decode(result, bursts_s); + + printd("Decoded: %s\n", osmo_hexdump(result, 23)); + + ASSERT_TRUE(!memcmp(l2, result, 23)); + + printd("\n"); +} + +static void test_rach(uint8_t bsic, uint8_t ra) +{ + uint8_t result; + ubit_t bursts_u[36]; + sbit_t bursts_s[36]; + + printd("Encoding: %02x\n", ra); + + /* encode */ + rach_encode(bursts_u, &ra, bsic); + + printd("U-Bits:\n"); + printd("%s\n", osmo_hexdump(bursts_u, 36)); + ubits2sbits(bursts_u, bursts_s, 36); + printd("S-Bits:\n"); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s, 36)); + + /* destroy */ + memset(bursts_s + 6, 0, 8); + + /* decode */ + rach_decode(&result, bursts_s, bsic); + + printd("Decoded: %02x\n", result); + + ASSERT_TRUE(ra == result); + + printd("\n"); +} + +static void test_sch(uint8_t *info) +{ + uint8_t result[4]; + ubit_t bursts_u[78]; + sbit_t bursts_s[78]; + + /* zero bits 25 and above */ + info[3] &= 1; + result[3] = 0; + + printd("Encoding: %s\n", osmo_hexdump(info, 4)); + + /* encode */ + sch_encode(bursts_u, info); + + printd("U-Bits:\n"); + printd("%s\n", osmo_hexdump(bursts_u, 78)); + ubits2sbits(bursts_u, bursts_s, 78); + printd("S-Bits:\n"); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s, 78)); + + /* destroy */ + memset(bursts_s + 6, 0, 10); + + /* decode */ + sch_decode(result, bursts_s); + + printd("Decoded: %s\n", osmo_hexdump(result, 4)); + + ASSERT_TRUE(!memcmp(info, result, 4)); + + printd("\n"); +} + +uint8_t test_l2[][23] = { + /* dummy frame */ + { 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* random frame */ + { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, + 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92, + 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8 }, + /* jolly frame */ + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, +}; + +int main(int argc, char **argv) +{ + int i; + + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) + test_xcch(test_l2[i]); + + for (i = 0; i < 256; i++) { + test_rach(0x3f, i); + test_rach(0x00, i); + test_rach(0x1a, i); + } + + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) + test_sch(test_l2[i]); + + printf("Success\n"); + + return 0; +} + diff --git a/tests/bursts/bursts_test.ok b/tests/bursts/bursts_test.ok new file mode 100644 index 00000000..35821117 --- /dev/null +++ b/tests/bursts/bursts_test.ok @@ -0,0 +1 @@ +Success diff --git a/tests/testsuite.at b/tests/testsuite.at index b528335f..e8f5d168 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -24,3 +24,9 @@ AT_KEYWORDS([misc]) cat $abs_srcdir/misc/misc_test.ok > expout AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/misc/misc_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([bursts]) +AT_KEYWORDS([bursts]) +cat $abs_srcdir/cipher/bursts_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/bursts/bursts_test], [], [expout], [ignore]) +AT_CLEANUP -- cgit v1.2.3 From cd0581d815f76dd159406a2e797ad554df82c761 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 13:46:03 +0100 Subject: TRX: On negative response of critical commands, shutdown BTS --- src/osmo-bts-trx/trx_if.c | 54 +++++++++++++++++++++++++++-------------------- src/osmo-bts-trx/trx_if.h | 1 + 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index abf992e0..e35b7cd4 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -176,12 +176,15 @@ static void trx_ctrl_timer_cb(void *data) } /* add a new ctrl command */ -static int trx_ctrl_cmd(struct trx_l1h *l1h, const char *cmd, const char *fmt, - ...) +static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd, + const char *fmt, ...) { struct trx_ctrl_msg *tcm; va_list ap; - int l; + int l, pending = 0; + + if (!llist_empty(&l1h->trx_ctrl_list)) + pending = 1; /* create message */ tcm = talloc_zero(tall_bts_ctx, struct trx_ctrl_msg); @@ -195,11 +198,12 @@ static int trx_ctrl_cmd(struct trx_l1h *l1h, const char *cmd, const char *fmt, } else snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s", cmd); tcm->cmd_len = strlen(cmd); + tcm->critical = critical; llist_add_tail(&tcm->list, &l1h->trx_ctrl_list); LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd); /* send message, if no pending message */ - if (!osmo_timer_pending(&l1h->trx_ctrl_timer)) + if (!pending) trx_ctrl_send(l1h); return 0; @@ -207,42 +211,42 @@ static int trx_ctrl_cmd(struct trx_l1h *l1h, const char *cmd, const char *fmt, int trx_if_cmd_poweroff(struct trx_l1h *l1h) { - return trx_ctrl_cmd(l1h, "POWEROFF", ""); + return trx_ctrl_cmd(l1h, 1, "POWEROFF", ""); } int trx_if_cmd_poweron(struct trx_l1h *l1h) { - return trx_ctrl_cmd(l1h, "POWERON", ""); + return trx_ctrl_cmd(l1h, 1, "POWERON", ""); } int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc) { - return trx_ctrl_cmd(l1h, "SETTSC", "%d", tsc); + return trx_ctrl_cmd(l1h, 0, "SETTSC", "%d", tsc); } int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic) { - return trx_ctrl_cmd(l1h, "SETBSIC", "%d", bsic); + return trx_ctrl_cmd(l1h, 0, "SETBSIC", "%d", bsic); } int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db) { - return trx_ctrl_cmd(l1h, "SETRXGAIN", "%d", db); + return trx_ctrl_cmd(l1h, 0, "SETRXGAIN", "%d", db); } int trx_if_cmd_setpower(struct trx_l1h *l1h, int db) { - return trx_ctrl_cmd(l1h, "SETPOWER", "%d", db); + return trx_ctrl_cmd(l1h, 0, "SETPOWER", "%d", db); } int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly) { - return trx_ctrl_cmd(l1h, "SETMAXDLY", "%d", dly); + return trx_ctrl_cmd(l1h, 0, "SETMAXDLY", "%d", dly); } int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type) { - return trx_ctrl_cmd(l1h, "SETSLOT", "%d %d", tn, type); + return trx_ctrl_cmd(l1h, 1, "SETSLOT", "%d %d", tn, type); } int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn) @@ -255,7 +259,7 @@ int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn) return -ENOTSUP; } - return trx_ctrl_cmd(l1h, "RXTUNE", "%d", freq10 * 100); + return trx_ctrl_cmd(l1h, 1, "RXTUNE", "%d", freq10 * 100); } int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn) @@ -268,7 +272,7 @@ int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn) return -ENOTSUP; } - return trx_ctrl_cmd(l1h, "TXTUNE", "%d", freq10 * 100); + return trx_ctrl_cmd(l1h, 1, "TXTUNE", "%d", freq10 * 100); } /* get response from ctrl socket */ @@ -297,6 +301,10 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) LOGP(DTRX, LOGL_DEBUG, "Response message: '%s'\n", buf); + /* abort timer and send next message, if any */ + if (osmo_timer_pending(&l1h->trx_ctrl_timer)) + osmo_timer_del(&l1h->trx_ctrl_timer); + /* get command for response message */ if (llist_empty(&l1h->trx_ctrl_list)) { LOGP(DTRX, LOGL_NOTICE, "Response message without " @@ -318,12 +326,15 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) goto notmatch; /* check for response code */ - if (*p) { - sscanf(p + 1, "%d", &resp); - if (resp) { - LOGP(DTRX, LOGL_ERROR, "Tranceiver rejected " - "TRX command with response: '%s'\n", - buf); + sscanf(p + 1, "%d", &resp); + if (resp) { + LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE, + "Tranceiver rejected TRX command with " + "response: '%s'\n", buf); + if (tcm->critical) { + bts_shutdown(l1h->trx->bts, "SIGINT"); + /* keep tcm list, so process is stopped */ + return -EIO; } } @@ -331,9 +342,6 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) llist_del(&tcm->list); talloc_free(tcm); - /* abort timer and send next message, if any */ - if (osmo_timer_pending(&l1h->trx_ctrl_timer)) - osmo_timer_del(&l1h->trx_ctrl_timer); trx_ctrl_send(l1h); } else LOGP(DTRX, LOGL_NOTICE, "Unknown message on ctrl port: %s\n", diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h index 656bd6a7..8ec071fe 100644 --- a/src/osmo-bts-trx/trx_if.h +++ b/src/osmo-bts-trx/trx_if.h @@ -8,6 +8,7 @@ struct trx_ctrl_msg { struct llist_head list; char cmd[128]; int cmd_len; + int critical; }; int trx_if_cmd_poweroff(struct trx_l1h *l1h); -- cgit v1.2.3 From 7a0d11dd68528d96afff61083c3cf90a50af11bb Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 16:47:57 +0100 Subject: ABIS: Introduce bts_model_abis_close to indicate ABIS link failure. sysmocom-bts model shuts down on link loss, but other models may not want this, so shutdown is moved tor bts_model_abis_close of osmo-bts-sysmo. --- include/osmo-bts/bts_model.h | 2 ++ src/common/abis.c | 3 ++- src/common/bts.c | 2 ++ src/osmo-bts-sysmo/main.c | 6 ++++++ src/osmo-bts-trx/main.c | 3 +++ tests/stubs.c | 3 +++ tests/sysmobts/sysmobts_test.c | 2 ++ 7 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/osmo-bts/bts_model.h b/include/osmo-bts/bts_model.h index de846365..ecbfc3d3 100644 --- a/include/osmo-bts/bts_model.h +++ b/include/osmo-bts/bts_model.h @@ -40,4 +40,6 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan); int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap); +void bts_model_abis_close(struct gsm_bts *bts); + #endif diff --git a/src/common/abis.c b/src/common/abis.c index 3b9bcdd2..3b8a7af1 100644 --- a/src/common/abis.c +++ b/src/common/abis.c @@ -47,6 +47,7 @@ #include #include #include +#include static struct gsm_bts *g_bts; @@ -114,7 +115,7 @@ static void sign_link_down(struct e1inp_line *line) e1inp_sign_link_destroy(g_bts->oml_link); g_bts->oml_link = NULL; - bts_shutdown(g_bts, "Abis close"); + bts_model_abis_close(g_bts); } diff --git a/src/common/bts.c b/src/common/bts.c index 77302e2f..3fa2bddc 100644 --- a/src/common/bts.c +++ b/src/common/bts.c @@ -242,6 +242,8 @@ int trx_link_estab(struct gsm_bts_trx *trx) if (link) rsl_tx_rf_res(trx); + else + bts_model_trx_deact_rf(trx); return 0; } diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c index 72c8cfd7..bee5bda2 100644 --- a/src/osmo-bts-sysmo/main.c +++ b/src/osmo-bts-sysmo/main.c @@ -420,3 +420,9 @@ int main(int argc, char **argv) osmo_select_main(0); } } + +void bts_model_abis_close(struct gsm_bts *bts) +{ + /* for now, we simply terminate the program and re-spawn */ + bts_shutdown(bts, "Abis close"); +} diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 1b3359aa..4e942499 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -400,3 +400,6 @@ int main(int argc, char **argv) return 0; } +void bts_model_abis_close(struct gsm_bts *bts) +{ +} diff --git a/tests/stubs.c b/tests/stubs.c index 89ab8027..f1ba5cc7 100644 --- a/tests/stubs.c +++ b/tests/stubs.c @@ -59,3 +59,6 @@ int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power) int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) { return 0; } + +void bts_model_abis_close(struct gsm_bts *bts) +{ } diff --git a/tests/sysmobts/sysmobts_test.c b/tests/sysmobts/sysmobts_test.c index 99fd4c7d..def7a819 100644 --- a/tests/sysmobts/sysmobts_test.c +++ b/tests/sysmobts/sysmobts_test.c @@ -265,3 +265,5 @@ int bts_model_init(struct gsm_bts *bts) { return 0; } int bts_model_oml_estab(struct gsm_bts *bts) { return 0; } +void bts_model_abis_close(struct gsm_bts *bts) +{ } -- cgit v1.2.3 From 414faaca19812016d5fb756838988f341c23ba51 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 5 Feb 2013 16:53:04 +0100 Subject: TRX: Power down tranceiver and reset scheduler, if abis link is lost If BTS is gone, TRX is powered down, due to loss of abis link. If link is esablished again, tranceiver and scheduler are provisioned again by BTS. --- src/osmo-bts-trx/l1_if.c | 7 +++++++ src/osmo-bts-trx/scheduler.c | 15 ++++++++++++--- src/osmo-bts-trx/scheduler.h | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 66922b54..25aa0f98 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -239,12 +239,19 @@ static int trx_close(struct gsm_bts_trx *trx) { struct trx_l1h *l1h = trx_l1h_hdl(trx); + /* close all logical channels and reset timeslots */ + trx_sched_reset(l1h); + + /* power off tranceiver, if not already */ if (l1h->config.poweron) { l1h->config.poweron = 0; l1h->config.poweron_sent = 0; l1if_provision_tranceiver_trx(l1h); } + /* Set to Operational State: Disabled */ + oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE); + return 0; } diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index a0caeeca..d51c1eb4 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -201,10 +201,12 @@ int trx_sched_init(struct trx_l1h *l1h) for (tn = 0; tn < 8; tn++) { l1h->mf_index[tn] = 0; + INIT_LLIST_HEAD(&l1h->dl_prims[tn]); for (i = 0; i < _TRX_CHAN_MAX; i++) { - INIT_LLIST_HEAD(&l1h->dl_prims[tn]); chan_state = &l1h->chan_states[tn][i]; - chan_state->ul_mask = 0x0; + chan_state->dl_active = 0; + chan_state->ul_active = 0; + chan_state->ul_mask = 0x00; } } @@ -220,8 +222,8 @@ void trx_sched_exit(struct trx_l1h *l1h) LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1h->trx->nr); for (tn = 0; tn < 8; tn++) { + msgb_queue_flush(&l1h->dl_prims[tn]); for (i = 0; i < _TRX_CHAN_MAX; i++) { - msgb_queue_flush(&l1h->dl_prims[tn]); chan_state = &l1h->chan_states[tn][i]; if (chan_state->dl_bursts) { talloc_free(chan_state->dl_bursts); @@ -235,6 +237,13 @@ void trx_sched_exit(struct trx_l1h *l1h) } } +/* close all logical channels and reset timeslots */ +void trx_sched_reset(struct trx_l1h *l1h) +{ + trx_sched_exit(l1h); + trx_sched_init(l1h); +} + /* * data request (from upper layer) diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index eb82327a..97803bd4 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -26,4 +26,7 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, int downlink, int active); +/* close all logical channels and reset timeslots */ +void trx_sched_reset(struct trx_l1h *l1h); + #endif /* TRX_SCHEDULER_H */ -- cgit v1.2.3 From d0603d96e9f6a69df8d2e37380b19eceb2f89d50 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 6 Feb 2013 11:35:06 +0100 Subject: TRX: Completed transcoding of TCH with reordering Table 2 of TS 05.03 --- src/osmo-bts-trx/tch_fr.c | 65 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/src/osmo-bts-trx/tch_fr.c b/src/osmo-bts-trx/tch_fr.c index 75f54752..6b3be24c 100644 --- a/src/osmo-bts-trx/tch_fr.c +++ b/src/osmo-bts-trx/tch_fr.c @@ -159,11 +159,12 @@ static const uint8_t gsm_fr_map[] = { }; static void -tch_fr_reassemble(uint8_t *tch_data, ubit_t *d_bits) +tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits) { int i, j, k, l, o; tch_data[0] = 0xd << 4; + memset(tch_data + 1, 0, 32); /* reassemble d-bits */ i = 0; /* counts bits */ j = 4; /* counts output bits */ @@ -171,7 +172,7 @@ tch_fr_reassemble(uint8_t *tch_data, ubit_t *d_bits) l = 0; /* counts element bits */ o = 0; /* offset input bits */ while (i < 260) { - tch_data[j>>3] |= (d_bits[k+o] << (7-(j&7))); + tch_data[j>>3] |= (b_bits[k+o] << (7-(j&7))); if (--k < 0) { o += gsm_fr_map[l]; k = gsm_fr_map[++l]-1; @@ -179,10 +180,12 @@ tch_fr_reassemble(uint8_t *tch_data, ubit_t *d_bits) i++; j++; } + + /* rearrange according to Table 2 of TS 05.03 */ } static void -tch_fr_disassemble(ubit_t *d_bits, uint8_t *tch_data) +tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data) { int i, j, k, l, o; @@ -192,7 +195,7 @@ tch_fr_disassemble(ubit_t *d_bits, uint8_t *tch_data) l = 0; /* counts element bits */ o = 0; /* offset output bits */ while (i < 260) { - d_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; + b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; if (--k < 0) { o += gsm_fr_map[l]; k = gsm_fr_map[++l]-1; @@ -200,6 +203,48 @@ tch_fr_disassemble(ubit_t *d_bits, uint8_t *tch_data) i++; j++; } + +} + + +/* b(0..259) from d(0..259) according to (corrected) Table 2 of T 05.03 */ +static uint16_t d_to_b_index[260] = { + 5, 52,108,164,220, 4, 11, 16, 3, 10, 15, 21, 42, 98,154,210, + 51,107,163,219, 9, 25, 29, 41, 97,153,209, 40, 96,152,208, 39, + 95,151,207, 38, 94,150,206, 50,106,162,218, 2, 20, 32, 37, 93, + 149,205, 24, 28, 44,100,156,212, 36, 92,148,204, 46,102,158,214, + 1, 8, 14, 35, 19, 23, 31, 43, 99,155,211, 49,105,161,217, 55, + 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91,111,114,117,120, + 123,126,129,132,135,138,141,144,147,167,170,173,176,179,182,185, + 188,191,194,197,200,203,223,226,229,232,235,238,241,244,247,250, + 253,256,259, 45,101,157,213, 48,104,160,216, 54, 57, 60, 63, 66, + 69, 72, 75, 78, 81, 84, 87, 90,110,113,116,119,122,125,128,131, + 134,137,140,143,146,166,169,172,175,178,181,184,187,190,193,196, + 199,202,222,225,228,231,234,237,240,243,246,249,252,255,258, 0, + 7, 13, 27, 30, 34, 33, 12, 18, 17, 22, 47,103,159,215, 53, 56, + 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89,109,112,115,118,121, + 124,127,130,133,136,139,142,145,165,168,171,174,177,180,183,186, + 189,192,195,198,201,221,224,227,230,233,236,239,242,245,248,251, + 254,257, 6, 26, +}; + + +static void +tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) +{ + int i; + + for (i = 0; i < 260; i++) + b_bits[d_to_b_index[i]] = d_bits[i]; +} + +static void +tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) +{ + int i; + + for (i = 0; i < 260; i++) + d_bits[i] = b_bits[d_to_b_index[i]]; } static void @@ -232,7 +277,7 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts) { sbit_t iB[912], cB[456], h; - ubit_t conv[185], d[260], p[3]; + ubit_t conv[185], b[260], d[260], p[3]; int i, rv, len, steal = 0; for (i=0; i<8; i++) { @@ -255,7 +300,9 @@ tch_fr_decode(uint8_t *tch_data, sbit_t *bursts) if (rv) return -1; - tch_fr_reassemble(tch_data, d); + tch_fr_d_to_b(b, d); + + tch_fr_reassemble(tch_data, b); len = 33; } else { @@ -273,12 +320,14 @@ int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len) { ubit_t iB[912], cB[456], h; - ubit_t conv[185], d[260], p[3]; + ubit_t conv[185], b[260], d[260], p[3]; int i; switch (len) { case 33: /* TCH FR */ - tch_fr_disassemble(d, tch_data); + tch_fr_disassemble(b, tch_data); + + tch_fr_b_to_d(d, b); osmo_crc8gen_set_bits(&tch_fr_crc3, d, 50, p); -- cgit v1.2.3 From 74d63b72126bfe7a2a6ac641be4ac789a39eea6d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 6 Feb 2013 11:37:59 +0100 Subject: Add test routing to test transcoding of TCH FR / FACCH frames --- tests/bursts/Makefile.am | 2 +- tests/bursts/bursts_test.c | 142 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 119 insertions(+), 25 deletions(-) diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am index 9f88f9b9..fd6c41f7 100644 --- a/tests/bursts/Makefile.am +++ b/tests/bursts/Makefile.am @@ -4,5 +4,5 @@ LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) noinst_PROGRAMS = bursts_test EXTRA_DIST = bursts_test.ok -bursts_test_SOURCES = bursts_test.c $(top_builddir)/src/osmo-bts-trx/xcch.c $(top_builddir)/src/osmo-bts-trx/rach.c $(top_builddir)/src/osmo-bts-trx/sch.c +bursts_test_SOURCES = bursts_test.c $(top_builddir)/src/osmo-bts-trx/xcch.c $(top_builddir)/src/osmo-bts-trx/rach.c $(top_builddir)/src/osmo-bts-trx/sch.c $(top_builddir)/src/osmo-bts-trx/tch_fr.c bursts_test_LDADD = $(LDADD) diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index 07769133..9e64e3f1 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -29,6 +29,7 @@ #include "../../src/osmo-bts-trx/xcch.h" #include "../../src/osmo-bts-trx/rach.h" #include "../../src/osmo-bts-trx/sch.h" +#include "../../src/osmo-bts-trx/tch_fr.h" #define ASSERT_TRUE(rc) \ @@ -46,6 +47,11 @@ static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count) int i; for (i = 0; i < count; i++) { + if (*ubits == 0x23) { + ubits++; + sbits++; + continue; + } if ((*ubits++) & 1) *sbits++ = -127; else @@ -67,32 +73,32 @@ static void test_xcch(uint8_t *l2) xcch_encode(bursts_u, l2); printd("U-Bits:\n"); - printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u, 57), - bursts_u[57], bursts_u[58], - osmo_hexdump(bursts_u + 59, 57)); - printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u + 116, 57), - bursts_u[57 + 116], bursts_u[58 + 116], - osmo_hexdump(bursts_u + 59 + 116, 57)); - printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u + 232, 57), - bursts_u[57 + 232], bursts_u[58 + 232], - osmo_hexdump(bursts_u + 59 + 232, 57)); - printd("%s %02x %02x %s\n", osmo_hexdump(bursts_u + 348, 57), - bursts_u[57 + 348], bursts_u[58 + 348], - osmo_hexdump(bursts_u + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57), + bursts_u[57], bursts_u[58]); + printd("%s\n", osmo_hexdump(bursts_u + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57), + bursts_u[57 + 116], bursts_u[58 + 116]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57), + bursts_u[57 + 232], bursts_u[58 + 232]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57), + bursts_u[57 + 348], bursts_u[58 + 348]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57)); ubits2sbits(bursts_u, bursts_s, 116 * 4); printd("S-Bits:\n"); - printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s, 57), - (uint8_t)bursts_s[57], (uint8_t)bursts_s[58], - osmo_hexdump((uint8_t *)bursts_s + 59, 57)); - printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s + 116, 57), - (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116], - osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); - printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s + 232, 57), - (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232], - osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); - printd("%s %02x %02x %s\n", osmo_hexdump((uint8_t *)bursts_s + 348, 57), - (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348], - osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57), + (uint8_t)bursts_s[57], (uint8_t)bursts_s[58]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57), + (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57), + (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57), + (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); /* destroy */ memset(bursts_s, 0, 30); @@ -172,6 +178,85 @@ static void test_sch(uint8_t *info) printd("\n"); } +static void test_fr(uint8_t *speech, int len) +{ + uint8_t result[33]; + ubit_t bursts_u[116 * 8]; + sbit_t bursts_s[116 * 8]; + int rc; + + memset(bursts_u, 0x23, sizeof(bursts_u)); + memset(bursts_s, 0, sizeof(bursts_s)); + + printd("Encoding: %s\n", osmo_hexdump(speech, len)); + + /* encode */ + tch_fr_encode(bursts_u, speech, len); + + printd("U-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57), + bursts_u[57], bursts_u[58]); + printd("%s\n", osmo_hexdump(bursts_u + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57), + bursts_u[57 + 116], bursts_u[58 + 116]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57), + bursts_u[57 + 232], bursts_u[58 + 232]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57), + bursts_u[57 + 348], bursts_u[58 + 348]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57), + bursts_u[57 + 464], bursts_u[58 + 464]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57), + bursts_u[57 + 580], bursts_u[58 + 580]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 696, 57), + bursts_u[57 + 696], bursts_u[58 + 696]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 696, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 812, 57), + bursts_u[57 + 812], bursts_u[58 + 812]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 812, 57)); + ubits2sbits(bursts_u, bursts_s, 116 * 8); + printd("S-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57), + (uint8_t)bursts_s[57], (uint8_t)bursts_s[58]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57), + (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57), + (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57), + (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57), + (uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57), + (uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 696, 57), + (uint8_t)bursts_s[57 + 696], (uint8_t)bursts_s[58 + 696]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 696, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 812, 57), + (uint8_t)bursts_s[57 + 812], (uint8_t)bursts_s[58 + 812]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 812, 57)); + + /* decode */ + rc = tch_fr_decode(result, bursts_s); + + ASSERT_TRUE(rc == len); + + printd("Decoded: %s\n", osmo_hexdump(result, len)); + + ASSERT_TRUE(!memcmp(speech, result, len)); + + printd("\n"); +} + uint8_t test_l2[][23] = { /* dummy frame */ { 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -187,6 +272,8 @@ uint8_t test_l2[][23] = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, }; +uint8_t test_speech[33]; + int main(int argc, char **argv) { int i; @@ -203,6 +290,13 @@ int main(int argc, char **argv) for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_sch(test_l2[i]); + for (i = 0; i < sizeof(test_speech); i++) + test_speech[i] = i; + test_speech[0] = 0xd0; + test_fr(test_speech, sizeof(test_speech)); + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) + test_fr(test_l2[i], sizeof(test_l2[0])); + printf("Success\n"); return 0; -- cgit v1.2.3 From 2c8787224ff4698e8bf04620441919262887c7ee Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 7 Feb 2013 13:09:34 +0100 Subject: Fix: Check right result on bursts_test --- tests/testsuite.at | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testsuite.at b/tests/testsuite.at index e8f5d168..7c449bb8 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -27,6 +27,6 @@ AT_CLEANUP AT_SETUP([bursts]) AT_KEYWORDS([bursts]) -cat $abs_srcdir/cipher/bursts_test.ok > expout +cat $abs_srcdir/bursts/bursts_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bursts/bursts_test], [], [expout], [ignore]) AT_CLEANUP -- cgit v1.2.3 From 2ea68e2b7be7af78fb87c42e39133a812b709d48 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 7 Feb 2013 13:16:28 +0100 Subject: TRX: Fixes and improvements of scheduler --- src/osmo-bts-trx/l1_if.h | 2 + src/osmo-bts-trx/scheduler.c | 122 +++++++++++++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 7e0f3688..bb8407fb 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -105,6 +105,8 @@ struct trx_l1h { struct trx_config config; uint8_t mf_index[8]; /* selected multiframe index */ + uint8_t mf_period[8]; /* period of multiframe */ + struct trx_sched_frame *mf_frames[8]; /* pointer to frame layout */ /* Channel states for all channels on all timeslots */ struct trx_chan_state chan_states[8][_TRX_CHAN_MAX]; diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index d51c1eb4..3224296f 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -260,6 +260,12 @@ int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap) if (!l1sap->oph.msg) abort(); + /* ignore empty frame */ + if (!msgb_l2len(l1sap->oph.msg)) { + msgb_free(l1sap->oph.msg); + return 0; + } + msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg); return 0; @@ -276,6 +282,12 @@ int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap) if (!l1sap->oph.msg) abort(); + /* ignore empty frame */ + if (!msgb_l2len(l1sap->oph.msg)) { + msgb_free(l1sap->oph.msg); + return 0; + } + msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg); return 0; @@ -825,7 +837,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t l2[23], l2_len; int rc; - LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", + LOGP(DL1C, LOGL_NOTICE, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); /* alloc burst memory, if not already */ @@ -835,12 +847,15 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* update mask */ - *mask |= (1 << bid); - /* store frame number of first burst */ - if (bid == 0) + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; *first_fn = fn; + } + + /* update mask */ + *mask |= (1 << bid); /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -850,15 +865,30 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, // FIXME: decrypt burst /* wait until complete set of bursts */ - if ((*mask & 0xf) != 0xf) + if (bid != 3) return 0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete data frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + trx_chan_desc[chan].name); + /* we require first burst to have correct FN */ + if (!(*mask & 0x1)) { + *mask = 0x0; + return 0; + } + } *mask = 0x0; /* decode */ rc = xcch_decode(l2, *bursts_p); if (rc) { - LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u for " - "%s\n", *first_fn, trx_chan_desc[chan].name); + LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u " + "(%u/%u) for %s\n", *first_fn, + (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + trx_chan_desc[chan].name); l2_len = 0; } else l2_len = 23; @@ -886,12 +916,15 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* update mask */ - *mask |= (1 << bid); - /* store frame number of first burst */ - if (bid == 0) + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; *first_fn = fn; + } + + /* update mask */ + *mask |= (1 << bid); /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -901,14 +934,27 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, // FIXME: decrypt burst /* wait until complete set of bursts */ - if ((*mask & 0xf) != 0xf) + if (bid != 3) return 0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + trx_chan_desc[chan].name); + /* we require first burst to have correct FN */ + if (!(*mask & 0x1)) { + *mask = 0x0; + return 0; + } + } *mask = 0x0; /* decode */ rc = pdch_decode(l2 + 1, *bursts_p, NULL); if (rc <= 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH frame at fn=%u for " + LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block at fn=%u for " "%s\n", *first_fn, trx_chan_desc[chan].name); l2[0] = 0; /* bad frame */ rc = 0; @@ -961,12 +1007,15 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* update mask */ - *mask |= (1 << bid); - /* store frame number of first burst */ - if (bid == 0) + if (bid == 0) { + memset(*bursts_p, 0, 464); + *mask = 0x0; *first_fn = fn; + } + + /* update mask */ + *mask |= (1 << bid); /* copy burst to end of buffer of 8 bursts */ burst = *bursts_p + bid * 116 + 464; @@ -976,16 +1025,29 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, // FIXME: decrypt burst /* wait until complete set of bursts */ - if (*mask != 0xff) + if (bid != 3) return 0; - *mask = 0xf0; + + /* check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame at " + "fn=%u (%u/%u) for %s\n", *first_fn, + (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + trx_chan_desc[chan].name); + /* we require first burst to have correct FN */ + if (!(*mask & 0x1)) { + *mask = 0x0; + return 0; + } + } + *mask = 0x0; /* decode * also shift buffer by 4 bursts for interleaving */ rc = tch_fr_decode(tch_data, *bursts_p); memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad tch frame at fn=%u " + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " "for %s\n", *first_fn, trx_chan_desc[chan].name); rc = 0; } @@ -1649,6 +1711,8 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, for (i = 0; ARRAY_SIZE(trx_sched_multiframes); i++) { if (trx_sched_multiframes[i].pchan == pchan) { l1h->mf_index[tn] = i; + l1h->mf_period[tn] = trx_sched_multiframes[i].period; + l1h->mf_frames[tn] = trx_sched_multiframes[i].frames; LOGP(DL1C, LOGL_NOTICE, "Configuring multiframe with " "%s trx=%d ts=%d\n", trx_sched_multiframes[i].name, @@ -1704,9 +1768,9 @@ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) return 0; /* get frame from multiframe */ - period = trx_sched_multiframes[l1h->mf_index[tn]].period; + period = l1h->mf_period[tn]; offset = fn % period; - frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + frame = l1h->mf_frames[tn] + offset; chan = frame->dl_chan; bid = frame->dl_bid; @@ -1742,9 +1806,9 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, goto no_data; /* get frame from multiframe */ - period = trx_sched_multiframes[l1h->mf_index[tn]].period; + period = l1h->mf_period[tn]; offset = fn % period; - frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + frame = l1h->mf_frames[tn] + offset; chan = frame->dl_chan; bid = frame->dl_bid; @@ -1781,13 +1845,13 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan; int rc; - if (!l1h->mf_index) + if (!l1h->mf_index[tn]) return -EINVAL; /* get frame from multiframe */ - period = trx_sched_multiframes[l1h->mf_index[tn]].period; + period = l1h->mf_period[tn]; offset = fn % period; - frame = trx_sched_multiframes[l1h->mf_index[tn]].frames + offset; + frame = l1h->mf_frames[tn] + offset; chan = frame->ul_chan; bid = frame->ul_bid; @@ -1935,6 +1999,8 @@ new_clock: /* schedule first FN to be transmitted */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); tranceiver_available = 1; + memset(&tranceiver_clock_timer, 0, + sizeof(tranceiver_clock_timer)); tranceiver_clock_timer.cb = trx_ctrl_timer_cb; tranceiver_clock_timer.data = bts; osmo_timer_schedule(&tranceiver_clock_timer, 0, -- cgit v1.2.3 From e0959e7929cf9c8659919f2849c54360eda95032 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 7 Feb 2013 14:09:06 +0100 Subject: TRX: Use received TRX clocks to determine availablility of tranceiver Only if transceiver becomes available, control commands are sent. If tranceiver is gone, reset scheduler. The current availability state is sent to BSC via OML state change commands. --- src/osmo-bts-trx/l1_if.c | 30 ++++++++++++++++++++++++++---- src/osmo-bts-trx/l1_if.h | 1 + src/osmo-bts-trx/scheduler.c | 22 ++++++++++++++++------ src/osmo-bts-trx/trx_if.c | 6 ++++++ 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 25aa0f98..8040f4e0 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -93,14 +93,14 @@ void l1if_reset(struct trx_l1h *l1h) { } -void check_tranceiver_availability(struct trx_l1h *l1h) +static void check_tranceiver_availability_trx(struct trx_l1h *l1h, int avail) { struct gsm_bts_trx *trx = l1h->trx; uint8_t tn; /* HACK, we should change state when we receive first clock from * tranceiver */ - if (1) { + if (avail) { /* signal availability */ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK); oml_mo_tx_sw_act_rep(&trx->mo); @@ -117,7 +117,23 @@ void check_tranceiver_availability(struct trx_l1h *l1h) NM_AVSTATE_OFF_LINE); oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE); + + for (tn = 0; tn < 8; tn++) + oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED, + NM_AVSTATE_OFF_LINE); + } +} + +int check_tranceiver_availability(struct gsm_bts *bts, int avail) +{ + struct gsm_bts_trx *trx; + struct trx_l1h *l1h; + + llist_for_each_entry(trx, &bts->trx_list, list) { + l1h = trx_l1h_hdl(trx); + check_tranceiver_availability_trx(l1h, avail); } + return 0; } @@ -128,6 +144,9 @@ int l1if_provision_tranceiver_trx(struct trx_l1h *l1h) { uint8_t tn; + if (!tranceiver_available) + return -EIO; + if (l1h->config.poweron && l1h->config.tsc_valid && l1h->config.bsic_valid @@ -250,7 +269,7 @@ static int trx_close(struct gsm_bts_trx *trx) } /* Set to Operational State: Disabled */ - oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE); + check_tranceiver_availability_trx(l1h, 0); return 0; } @@ -268,9 +287,10 @@ static uint8_t trx_set_bts(struct gsm_bts *bts) l1h->config.bsic = bsic; l1h->config.bsic_valid = 1; l1h->config.bsic_sent = 0; + l1if_provision_tranceiver_trx(l1h); } - check_tranceiver_availability(l1h); } + check_tranceiver_availability(bts, tranceiver_available); return 0; @@ -286,6 +306,7 @@ static uint8_t trx_set_trx(struct gsm_bts_trx *trx) l1h->config.arfcn = arfcn; l1h->config.arfcn_valid = 1; l1h->config.arfcn_sent = 0; + l1if_provision_tranceiver_trx(l1h); } return 0; @@ -308,6 +329,7 @@ static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts) l1h->config.tsc = tsc; l1h->config.tsc_valid = 1; l1h->config.tsc_sent = 0; + l1if_provision_tranceiver_trx(l1h); } /* set physical channel */ diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index bb8407fb..7a6afb28 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -116,6 +116,7 @@ struct trx_l1h { struct trx_l1h *l1if_open(struct gsm_bts_trx *trx); void l1if_close(struct trx_l1h *l1h); void l1if_reset(struct trx_l1h *l1h); +int check_tranceiver_availability(struct gsm_bts *bts, int avail); int l1if_provision_tranceiver_trx(struct trx_l1h *l1h); int l1if_provision_tranceiver(struct gsm_bts *bts); int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 3224296f..917cbdaf 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1937,14 +1937,18 @@ static void trx_ctrl_timer_cb(void *data) LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); +no_clock: tranceiver_available = 0; /* flush pending messages of transceiver */ - llist_for_each_entry(trx, &bts->trx_list, list) + /* close all logical channels and reset timeslots */ + llist_for_each_entry(trx, &bts->trx_list, list) { trx_if_flush(trx_l1h_hdl(trx)); + trx_sched_reset(trx_l1h_hdl(trx)); + } - /* start over provisioning tranceiver */ - l1if_provision_tranceiver(bts); + /* tell BSC */ + check_tranceiver_availability(bts, 0); return; } @@ -1958,10 +1962,10 @@ static void trx_ctrl_timer_cb(void *data) if (elapsed > FRAME_DURATION_uS * MAX_FN_SKEW || elapsed < 0) { LOGP(DL1C, LOGL_NOTICE, "PC clock skew: elapsed uS %d\n", elapsed); - tranceiver_available = 0; - return; + goto no_clock; } + /* schedule next FN clock */ while (elapsed > FRAME_DURATION_uS / 2) { tv_clock->tv_usec += FRAME_DURATION_uS; if (tv_clock->tv_usec >= 1000000) { @@ -1996,7 +2000,7 @@ new_clock: tranceiver_last_fn = fn; trx_sched_fn(tranceiver_last_fn); - /* schedule first FN to be transmitted */ + /* schedule first FN clock */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); tranceiver_available = 1; memset(&tranceiver_clock_timer, 0, @@ -2006,6 +2010,12 @@ new_clock: osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); + /* start provisioning tranceiver */ + l1if_provision_tranceiver(bts); + + /* tell BSC */ + check_tranceiver_availability(bts, 1); + return 0; } diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index e35b7cd4..7d1be857 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -183,6 +183,12 @@ static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd, va_list ap; int l, pending = 0; + if (!tranceiver_available) { + LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from " + "tranceiver, please fix!\n"); + return -EIO; + } + if (!llist_empty(&l1h->trx_ctrl_list)) pending = 1; -- cgit v1.2.3 From 7d684d6866e711f96a9bac37225dbe537b514401 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 7 Feb 2013 16:58:00 +0100 Subject: TRX: Fix, never send confirm for DEACT SACCH request (TS 05.08 4.6) Sending it would cause BSC to change to a state, where it does not release rf channel. --- src/osmo-bts-trx/l1_if.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 8040f4e0..23f87b21 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -457,6 +457,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) /* activate dedicated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x00, 0, 1); trx_sched_set_lchan(l1h, chan_nr, 0x00, 1, 1); + /* activate assoicated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 1); trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 1); /* init lapdm */ @@ -476,16 +477,17 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) "chan_nr 0x%02x\n", chan_nr); break; } + /* deactivate assoicated channel */ + trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 0); + trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 0); /* deactivate dedicated channel */ if (!l1sap->u.info.u.act_req.sacch_only) { trx_sched_set_lchan(l1h, chan_nr, 0x00, 0, 0); trx_sched_set_lchan(l1h, chan_nr, 0x00, 1, 0); + /* confirm only on dedicated channel */ + mph_info_chan_confirm(l1h, chan_nr, + PRIM_INFO_DEACTIVATE, 0); } - trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 0); - trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 0); - /* confirm */ - mph_info_chan_confirm(l1h, chan_nr, - PRIM_INFO_DEACTIVATE, 0); break; default: LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n", -- cgit v1.2.3 From cd463dd72acbe2286dc33fa266ec081d75b57751 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 11 Feb 2013 12:52:56 +0100 Subject: TRX: Minor fixes, especially handle TOA of RACH correctly --- src/osmo-bts-trx/scheduler.c | 54 ++++++++++++++++++++++++++++---------------- src/osmo-bts-trx/scheduler.h | 2 +- src/osmo-bts-trx/trx_if.c | 6 ++--- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 917cbdaf..9f369909 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -42,6 +42,11 @@ #include "pxxch.h" #include "trx_if.h" +/* Enable this to multiply TOA of RACH by 10. + * This usefull to check tenth of timing advances with RSSI test tool. + * Note that regular phones will not work when using this test! */ +//#define TA_TEST + void *tall_bts_ctx; static struct gsm_bts *bts; @@ -63,7 +68,7 @@ typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, typedef const ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); @@ -84,15 +89,15 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); static const ubit_t dummy_burst[148] = { 0,0,0, @@ -768,20 +773,20 @@ static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, */ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct osmo_phsap_prim l1sap; uint8_t ra; int rc; - LOGP(DL1C, LOGL_DEBUG, "Received %s fn=%u\n", - trx_chan_desc[chan].name, fn); + LOGP(DL1C, LOGL_NOTICE, "Received %s fn=%u toa=%.2f\n", + trx_chan_desc[chan].name, fn, toa); /* decode */ rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic); if (rc) { LOGP(DL1C, LOGL_NOTICE, "Received bad rach frame at fn=%u " - "ra=%u\n", fn, ra); + "(%u/51)\n", fn, fn % 51); return 0; } @@ -791,7 +796,11 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL); l1sap.u.rach_ind.ra = ra; - l1sap.u.rach_ind.acc_delay = 0; //FIXME: TOA +#ifdef TA_TEST +#warning TIMING ADVANCE TEST-HACK IS ENABLED!!! + toa *= 10; +#endif + l1sap.u.rach_ind.acc_delay = (toa >= 0) ? toa : 0; l1sap.u.rach_ind.fn = fn; /* forward primitive */ @@ -828,7 +837,7 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -837,7 +846,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t l2[23], l2_len; int rc; - LOGP(DL1C, LOGL_NOTICE, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", + LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); /* alloc burst memory, if not already */ @@ -897,7 +906,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -988,7 +997,7 @@ static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -1062,7 +1071,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int16_t toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { LOGP(DL1C, LOGL_DEBUG, "TCH/H Received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1192,6 +1201,7 @@ static struct trx_sched_frame frame_bcch_sdcch4[102] = { { TRXC_SACCH4_1, 2, TRXC_SDCCH4_2, 1 }, { TRXC_SACCH4_1, 3, TRXC_SDCCH4_2, 2 }, { TRXC_IDLE, 0, TRXC_SDCCH4_2, 3 }, + { TRXC_FCCH, 0, TRXC_SDCCH4_3, 0 }, { TRXC_SCH, 0, TRXC_SDCCH4_3, 1 }, { TRXC_BCCH, 0, TRXC_SDCCH4_3, 2 }, @@ -1837,7 +1847,7 @@ if (0) if (chan != TRXC_IDLE) // hack /* process uplink burst */ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - sbit_t *bits, int8_t rssi, int16_t toa) + sbit_t *bits, int8_t rssi, float toa) { struct trx_sched_frame *frame; uint8_t offset, period, bid; @@ -1862,6 +1872,10 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, && !l1h->chan_states[tn][chan].ul_active) return -EINVAL; + /* omit bursts which have no handler, like IDLE bursts */ + if (!func) + return 0; + /* put burst to function */ rc = func(l1h, tn, fn, chan, bid, bits, toa); @@ -1976,7 +1990,8 @@ no_clock: trx_sched_fn(tranceiver_last_fn); elapsed -= FRAME_DURATION_uS; } - osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS - elapsed); + osmo_timer_schedule(&tranceiver_clock_timer, 0, + FRAME_DURATION_uS - elapsed); } @@ -2037,7 +2052,8 @@ new_clock: goto new_clock; } - LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n", elapsed_fn * FRAME_DURATION_uS - elapsed); + LOGP(DL1C, LOGL_INFO, "GSM clock jitter: %d\n", + elapsed_fn * FRAME_DURATION_uS - elapsed); /* too many frames have been processed already */ if (elapsed_fn < 0) { diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index 97803bd4..4a493fa6 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -16,7 +16,7 @@ int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap); int trx_sched_clock(uint32_t fn); int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - sbit_t *bits, int8_t rssi, int16_t toa); + sbit_t *bits, int8_t rssi, float toa); /* set multiframe scheduler to given pchan */ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 7d1be857..b8535e38 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -368,7 +368,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) int len; uint8_t tn; int8_t rssi; - int16_t toa; + float toa; uint32_t fn; sbit_t bits[148]; int i; @@ -384,7 +384,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) tn = buf[0]; fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; rssi = (int8_t)buf[5]; - toa = (int16_t)(buf[6] << 8) | buf[7]; + toa = ((int16_t)(buf[6] << 8) | buf[7]) / 256.0F; /* copy and convert bits {254..0} to sbits {-127..127} */ for (i = 0; i < 148; i++) { @@ -403,7 +403,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) return -EINVAL; } - LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d ", + LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f ", tn, fn, rssi, toa); trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa); -- cgit v1.2.3 From b104aed5ec01fe27c0c2e0ddd988ba11608cb0bd Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 11 Feb 2013 21:42:35 +0100 Subject: TRX: Fixed swapped stealing bits Thanx to Sylvain for pointing to this bug. --- src/osmo-bts-trx/tch_fr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/osmo-bts-trx/tch_fr.c b/src/osmo-bts-trx/tch_fr.c index 6b3be24c..cfaf18ba 100644 --- a/src/osmo-bts-trx/tch_fr.c +++ b/src/osmo-bts-trx/tch_fr.c @@ -120,10 +120,10 @@ tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) iB[i] = eB[i+2]; if (h && !odd) - *h = eB[57]; + *h = eB[58]; if (h && odd) - *h = eB[58]; + *h = eB[57]; } static void @@ -138,9 +138,9 @@ tch_fr_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *h, int odd) eB[i+2] = iB[i]; if (h && !odd) - eB[57] = *h; - if (h && odd) eB[58] = *h; + if (h && odd) + eB[57] = *h; } /* this corresponds to the bit-lengths of the individual codec -- cgit v1.2.3 From d10eaee4cce04aee4907b87e5bd73b9d866061b7 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Feb 2013 11:21:13 +0100 Subject: TRX: Completed TCH/F full rate support Full rate is now tested and working. --- src/osmo-bts-trx/l1_if.c | 17 +- src/osmo-bts-trx/l1_if.h | 1 + src/osmo-bts-trx/scheduler.c | 1087 ++++++++++++++++++++++++------------------ src/osmo-bts-trx/scheduler.h | 4 + src/osmo-bts-trx/tch_fr.c | 2 +- 5 files changed, 647 insertions(+), 464 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 23f87b21..51b1f439 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -363,15 +363,6 @@ static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan, return 0; } -/* channel mode, encryption and/or multirate have changed */ -static int l1if_rsl_mode_modify(struct trx_l1h *l1h, struct gsm_lchan *lchan, - int downlink) -{ - - // FIXME - return 0; -} - static int mph_info_chan_confirm(struct trx_l1h *l1h, uint8_t chan_nr, enum osmo_mph_info_type type, uint8_t cause) { @@ -460,6 +451,9 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) /* activate assoicated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 1); trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 1); + /* set mode */ + trx_sched_set_mode(l1h, chan_nr, + lchan->rsl_cmode, lchan->tch_mode); /* init lapdm */ lchan_init_lapdm(lchan); /* confirm */ @@ -468,8 +462,9 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) break; } if (l1sap->u.info.type == PRIM_INFO_MODIFY) { - l1if_rsl_mode_modify(l1h, lchan, 0); - l1if_rsl_mode_modify(l1h, lchan, 1); + /* change mode */ + trx_sched_set_mode(l1h, chan_nr, + lchan->rsl_cmode, lchan->tch_mode); break; } if ((chan_nr & 0x80)) { diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 7a6afb28..a2a5e9c4 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -55,6 +55,7 @@ struct trx_chan_state { uint32_t ul_first_fn; /* fn of first burst */ uint8_t ul_mask; /* mask of received bursts */ uint8_t sacch_lost; /* SACCH loss detection */ + uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */ }; struct trx_config { diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9f369909..d5c7bb54 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -357,7 +357,7 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENODEV; } - LOGP(DL1C, LOGL_INFO, "TCH.ind: chan=%s chan_nr=0x%02x " + LOGP(DL1C, LOGL_INFO, "TCH RTS.ind: chan=%s chan_nr=0x%02x " "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, chan_nr, fn, tn, l1h->trx->nr); @@ -366,11 +366,15 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (!msg) return -ENOMEM; l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, PRIM_OP_INDICATION, msg); - l1sap->u.tch.chan_nr = chan_nr | tn; - l1sap->u.tch.fn = fn; + l1sap->u.data.chan_nr = chan_nr; + l1sap->u.data.link_id = link_id; + l1sap->u.data.fn = fn; + /* stop here, if TCH is in signalling only mode */ + if (l1h->chan_states[tn][chan].rsl_cmode == RSL_CMOD_SPD_SIGN) + return l1sap_up(l1h->trx, l1sap); l1sap_up(l1h->trx, l1sap); /* generate prim */ @@ -378,11 +382,10 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (!msg) return -ENOMEM; l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = chan_nr; - l1sap->u.data.chan_nr = link_id; - l1sap->u.data.fn = fn; + l1sap->u.tch.chan_nr = chan_nr; + l1sap->u.tch.fn = fn; return l1sap_up(l1h->trx, l1sap); } @@ -454,81 +457,69 @@ static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static struct msgb *dequeue_prim(struct trx_l1h *l1h, int8_t tn,uint32_t fn, enum trx_chan_type chan) { - struct msgb *found = NULL, *msg, *msg2; /* make GCC happy */ - struct osmo_phsap_prim *l1sap = NULL; /* make GCC happy */ - uint32_t check_fn; + struct msgb *msg, *msg2; + struct osmo_phsap_prim *l1sap; + uint32_t prim_fn; + uint8_t chan_nr, link_id; - /* get burst from queue */ + /* get prim of current fn from queue */ llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) { l1sap = msgb_l1sap_prim(msg); - check_fn = ((l1sap->u.data.fn + 2715648 - fn) % 2715648); - if (check_fn > 20) { + if (l1sap->oph.operation != PRIM_OP_REQUEST) { +wrong_type: + LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has " + "wrong type.\n", tn, fn); +free_msg: + /* unlink and free message */ + llist_del(&msg->list); + msgb_free(msg); + return NULL; + } + switch (l1sap->oph.primitive) { + case PRIM_PH_DATA: + chan_nr = l1sap->u.data.chan_nr; + link_id = l1sap->u.data.link_id; + prim_fn = ((l1sap->u.data.fn + 2715648 - fn) % 2715648); + break; + case PRIM_TCH: + chan_nr = l1sap->u.tch.chan_nr; + link_id = 0; + prim_fn = ((l1sap->u.tch.fn + 2715648 - fn) % 2715648); + break; + default: + goto wrong_type; + } + if (prim_fn > 20) { LOGP(DL1C, LOGL_ERROR, "Prim for trx=%u ts=%u at fn=%u " "is out of range. (current fn=%u)\n", - l1h->trx->nr, tn, l1sap->u.data.fn, fn); + l1h->trx->nr, tn, prim_fn, fn); /* unlink and free message */ llist_del(&msg->list); msgb_free(msg); continue; } - if (check_fn > 0) + if (prim_fn > 0) continue; - /* found second message, check if we have TCH+FACCH */ - if (found) { - /* we found a message earlier */ - l1sap = msgb_l1sap_prim(found); - /* if we have TCH+something */ - if (l1sap->oph.primitive == PRIM_TCH) { - /* unlink and free message */ - llist_del(&found->list); - msgb_free(found); - found = msg; - } else { - l1sap = msgb_l1sap_prim(msg); - /* if we have TCH+something */ - if (l1sap->oph.primitive == PRIM_TCH) { - /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); - } - } - break; - } - found = msg; - } - - if (!found) { - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " - "trx=%u ts=%u at fn=%u to transmit.\n", - trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); - return NULL; + goto found_msg; } - l1sap = msgb_l1sap_prim(found); + return NULL; - if ((l1sap->oph.primitive != PRIM_PH_DATA - && l1sap->oph.primitive != PRIM_TCH) - || l1sap->oph.operation != PRIM_OP_REQUEST) { - LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has wrong " - "type.\n", tn, fn); -free_msg: - /* unlink and free message */ - llist_del(&found->list); - msgb_free(found); - return NULL; - } - if ((l1sap->u.data.chan_nr ^ (trx_chan_desc[chan].chan_nr | tn)) - || ((l1sap->u.data.link_id ^ trx_chan_desc[chan].link_id) & 0x40)) { +found_msg: + if ((chan_nr ^ (trx_chan_desc[chan].chan_nr | tn)) + || ((link_id & 0xc0) ^ trx_chan_desc[chan].link_id)) { LOGP(DL1C, LOGL_ERROR, "Prim for ts=%u at fn=%u has wrong " "chan_nr=%02x link_id=%02x, expecting chan_nr=%02x " - "link_id=%02x.\n", tn, fn, l1sap->u.data.chan_nr, - l1sap->u.data.link_id, trx_chan_desc[chan].chan_nr | tn, + "link_id=%02x.\n", tn, fn, chan_nr, link_id, + trx_chan_desc[chan].chan_nr | tn, trx_chan_desc[chan].link_id); goto free_msg; } - return found; + /* unlink and return message */ + llist_del(&msg->list); + return msg; } static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -553,6 +544,10 @@ static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; + LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + no_msg: /* free burst memory */ if (*bursts_p) { @@ -566,8 +561,7 @@ got_msg: if (msgb_l2len(msg) != 23) { LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " "(len=%d)\n", msgb_l2len(msg)); - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); goto no_msg; } @@ -589,8 +583,7 @@ got_msg: /* encode bursts */ xcch_encode(*bursts_p, msg->l2h); - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); send_burst: @@ -628,6 +621,10 @@ static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; + LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + no_msg: /* free burst memory */ if (*bursts_p) { @@ -651,14 +648,12 @@ got_msg: if (rc) { LOGP(DL1C, LOGL_FATAL, "Prim invalid length, please FIX! " "(len=%d)\n", rc); - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); goto no_msg; } - /* unlink and free message */ - llist_del(&msg->list); + /* free message */ msgb_free(msg); send_burst: @@ -679,7 +674,7 @@ send_burst: static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { - struct msgb *msg = NULL; /* make GCC happy */ + struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; static ubit_t bits[148]; struct osmo_phsap_prim *l1sap; @@ -691,41 +686,56 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - /* get burst from queue */ - msg = dequeue_prim(l1h, tn, fn, chan); - if (msg) - goto got_msg; - -no_msg: - /* free burst memory */ - if (*bursts_p) { - talloc_free(*bursts_p); - *bursts_p = NULL; + /* get frame and unlink from queue */ + msg1 = dequeue_prim(l1h, tn, fn, chan); + msg2 = dequeue_prim(l1h, tn, fn, chan); + if (msg1) { + l1sap = msgb_l1sap_prim(msg1); + if (l1sap->oph.primitive == PRIM_TCH) { + msg_tch = msg1; + if (msg2) { + l1sap = msgb_l1sap_prim(msg2); + if (l1sap->oph.primitive == PRIM_TCH) { + LOGP(DL1C, LOGL_FATAL, "TCH twice, " + "please FIX! "); + msgb_free(msg2); + } else + msg_facch = msg2; + } + } else { + msg_facch = msg1; + if (msg2) { + l1sap = msgb_l1sap_prim(msg2); + if (l1sap->oph.primitive != PRIM_TCH) { + LOGP(DL1C, LOGL_FATAL, "FACCH twice, " + "please FIX! "); + msgb_free(msg2); + } else + msg_tch = msg2; + } + } + } else if (msg2) { + l1sap = msgb_l1sap_prim(msg2); + if (l1sap->oph.primitive == PRIM_TCH) + msg_tch = msg2; + else + msg_facch = msg2; } - return NULL; -got_msg: - l1sap = msgb_l1sap_prim(msg); - if (l1sap->oph.primitive == PRIM_TCH) { - /* check validity of message */ - if (msgb_l2len(msg) != 33) { - LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " - "(len=%d)\n", msgb_l2len(msg)); - /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); - goto no_msg; - } - } else { - /* check validity of message */ - if (msgb_l2len(msg) != 23) { - LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " - "(len=%d)\n", msgb_l2len(msg)); - /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); - goto no_msg; - } + /* check validity of message */ + if (msg_tch && msgb_l2len(msg_tch) != 33) { + LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg_tch)); + /* free message */ + msgb_free(msg_tch); + msg_tch = NULL; + } + if (msg_facch && msgb_l2len(msg_facch) != 23) { + LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " + "(len=%d)\n", msgb_l2len(msg_facch)); + /* free message */ + msgb_free(msg_facch); + msg_facch = NULL; } /* alloc burst memory, if not already, @@ -734,15 +744,30 @@ got_msg: *bursts_p = talloc_zero_size(tall_bts_ctx, 928); if (!*bursts_p) return NULL; - } else + } else { memcpy(*bursts_p, *bursts_p + 464, 464); + memset(*bursts_p + 464, 0, 464); + } - /* encode bursts */ - tch_fr_encode(*bursts_p, msg->l2h, msgb_l2len(msg)); + /* mo message at all */ + if (!msg_tch && !msg_facch) { + LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + goto send_burst; + } + + /* encode bursts (priorize FACCH) */ + if (msg_facch) + tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); + else + tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); /* unlink and free message */ - llist_del(&msg->list); - msgb_free(msg); + if (msg_tch) + msgb_free(msg_tch); + if (msg_facch) + msgb_free(msg_facch); send_burst: /* compose burst */ @@ -1018,7 +1043,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* store frame number of first burst */ if (bid == 0) { - memset(*bursts_p, 0, 464); + memset(*bursts_p + 464, 0, 464); *mask = 0x0; *first_fn = fn; } @@ -1046,7 +1071,8 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* we require first burst to have correct FN */ if (!(*mask & 0x1)) { *mask = 0x0; - return 0; + rc = 0; + goto bfi; } } *mask = 0x0; @@ -1058,7 +1084,9 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " "for %s\n", *first_fn, trx_chan_desc[chan].name); - rc = 0; + memset(tch_data, 0, sizeof(tch_data)); + // FIXME length depends on codec + rc = 33; } /* FACCH */ @@ -1066,6 +1094,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, 23); +bfi: /* TCH or BFI */ return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); } @@ -1097,54 +1126,24 @@ static struct trx_sched_frame frame_bcch[51] = { /* dl_chan dl_bid ul_chan ul_bid */ { TRXC_FCCH, 0, TRXC_RACH, 0 }, { TRXC_SCH, 0, TRXC_RACH, 0 }, - { TRXC_BCCH, 0, TRXC_RACH, 0 }, - { TRXC_BCCH, 1, TRXC_RACH, 0 }, - { TRXC_BCCH, 2, TRXC_RACH, 0 }, - { TRXC_BCCH, 3, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_BCCH, 0, TRXC_RACH, 0 }, { TRXC_BCCH, 1, TRXC_RACH, 0 }, { TRXC_BCCH, 2, TRXC_RACH, 0 }, { TRXC_BCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, { TRXC_FCCH, 0, TRXC_RACH, 0 }, { TRXC_SCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, { TRXC_FCCH, 0, TRXC_RACH, 0 }, { TRXC_SCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, { TRXC_FCCH, 0, TRXC_RACH, 0 }, { TRXC_SCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, { TRXC_FCCH, 0, TRXC_RACH, 0 }, { TRXC_SCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, - { TRXC_CCCH, 0, TRXC_RACH, 0 }, - { TRXC_CCCH, 1, TRXC_RACH, 0 }, - { TRXC_CCCH, 2, TRXC_RACH, 0 }, - { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, + { TRXC_CCCH, 0, TRXC_RACH, 0 }, { TRXC_CCCH, 1, TRXC_RACH, 0 }, { TRXC_CCCH, 2, TRXC_RACH, 0 }, { TRXC_CCCH, 3, TRXC_RACH, 0 }, { TRXC_IDLE, 0, TRXC_RACH, 0 }, }; @@ -1308,6 +1307,7 @@ static struct trx_sched_frame frame_sdcch8[102] = { { TRXC_IDLE, 0, TRXC_SACCH8_0, 1 }, { TRXC_IDLE, 0, TRXC_SACCH8_0, 2 }, { TRXC_IDLE, 0, TRXC_SACCH8_0, 3 }, + { TRXC_SDCCH8_0, 0, TRXC_SACCH8_1, 0 }, { TRXC_SDCCH8_0, 1, TRXC_SACCH8_1, 1 }, { TRXC_SDCCH8_0, 2, TRXC_SACCH8_1, 2 }, @@ -1361,346 +1361,501 @@ static struct trx_sched_frame frame_sdcch8[102] = { { TRXC_IDLE, 0, TRXC_SACCH8_4, 3 }, }; -static struct trx_sched_frame frame_tchf[104] = { +static struct trx_sched_frame frame_tchf_ts0[104] = { /* dl_chan dl_bid ul_chan ul_bid */ - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, - { TRXC_TCHF, 0, TRXC_TCHF, 0 }, - { TRXC_TCHF, 1, TRXC_TCHF, 1 }, - { TRXC_TCHF, 2, TRXC_TCHF, 2 }, - { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, }; -static struct trx_sched_frame frame_tchh[104] = { +static struct trx_sched_frame frame_tchf_ts1[104] = { /* dl_chan dl_bid ul_chan ul_bid */ - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, +}; + +static struct trx_sched_frame frame_tchf_ts2[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static struct trx_sched_frame frame_tchf_ts3[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, +}; + +static struct trx_sched_frame frame_tchf_ts4[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static struct trx_sched_frame frame_tchf_ts5[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, +}; + +static struct trx_sched_frame frame_tchf_ts6[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, +}; + +static struct trx_sched_frame frame_tchf_ts7[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 1, TRXC_SACCHTF, 1 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 2, TRXC_SACCHTF, 2 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 3, TRXC_SACCHTF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_IDLE, 0, TRXC_IDLE, 0 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_TCHF, 0, TRXC_TCHF, 0 }, { TRXC_TCHF, 1, TRXC_TCHF, 1 }, { TRXC_TCHF, 2, TRXC_TCHF, 2 }, { TRXC_TCHF, 3, TRXC_TCHF, 3 }, + { TRXC_SACCHTF, 0, TRXC_SACCHTF, 0 }, +}; + +static struct trx_sched_frame frame_tchh_ts01[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, +}; + +static struct trx_sched_frame frame_tchh_ts23[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, +}; + +static struct trx_sched_frame frame_tchh_ts45[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, +}; + +static struct trx_sched_frame frame_tchh_ts67[104] = { +/* dl_chan dl_bid ul_chan ul_bid */ + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_0, 1, TRXC_SACCHTH_0, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_1, 1, TRXC_SACCHTH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_0, 2, TRXC_SACCHTH_0, 2 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_1, 2, TRXC_SACCHTH_1, 2 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_0, 3, TRXC_SACCHTH_0, 3 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, - { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, - { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, - { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, - { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, { TRXC_SACCHTH_1, 3, TRXC_SACCHTH_1, 3 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_0, 0, TRXC_SACCHTH_0, 0 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_TCHH_0, 0, TRXC_TCHH_0, 0 }, { TRXC_TCHH_1, 0, TRXC_TCHH_1, 0 }, { TRXC_TCHH_0, 1, TRXC_TCHH_0, 1 }, { TRXC_TCHH_1, 1, TRXC_TCHH_1, 1 }, + { TRXC_SACCHTH_1, 0, TRXC_SACCHTH_1, 0 }, }; static struct trx_sched_frame frame_pdch[104] = { /* dl_chan dl_bid ul_chan ul_bid */ - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_PTCCH, 0, TRXC_PTCCH, 0 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_PTCCH, 1, TRXC_PTCCH, 1 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_PTCCH, 2, TRXC_PTCCH, 2 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_PTCCH, 3, TRXC_PTCCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, - { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, - { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, - { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, - { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, + { TRXC_PDTCH, 0, TRXC_PDTCH, 0 }, { TRXC_PDTCH, 1, TRXC_PDTCH, 1 }, { TRXC_PDTCH, 2, TRXC_PDTCH, 2 }, { TRXC_PDTCH, 3, TRXC_PDTCH, 3 }, { TRXC_IDLE, 0, TRXC_IDLE, 0 }, }; /* multiframe structure */ struct trx_sched_multiframe { enum gsm_phys_chan_config pchan; + uint8_t slotmask; uint8_t period; struct trx_sched_frame *frames; const char *name; }; static struct trx_sched_multiframe trx_sched_multiframes[] = { - { GSM_PCHAN_NONE, 0, NULL, "NONE"}, - { GSM_PCHAN_CCCH, 51, frame_bcch, "BCCH+CCCH" }, - { GSM_PCHAN_CCCH_SDCCH4, 102, frame_bcch_sdcch4, "BCCH+CCCH+SDCCH/4+SACCH/4" }, - { GSM_PCHAN_SDCCH8_SACCH8C, 102, frame_sdcch8, "SDCCH/8+SACCH/8" }, - { GSM_PCHAN_TCH_F, 104, frame_tchf, "TCH/F+SACCH" }, - { GSM_PCHAN_TCH_H, 104, frame_tchh, "TCH/H+SACCH" }, - { GSM_PCHAN_PDCH, 104, frame_pdch, "PDCH" }, + { GSM_PCHAN_NONE, 0xff, 0, NULL, "NONE"}, + { GSM_PCHAN_CCCH, 0xff, 51, frame_bcch, "BCCH+CCCH" }, + { GSM_PCHAN_CCCH_SDCCH4, 0xff, 102, frame_bcch_sdcch4, "BCCH+CCCH+SDCCH/4+SACCH/4" }, + { GSM_PCHAN_SDCCH8_SACCH8C, 0xff, 102, frame_sdcch8, "SDCCH/8+SACCH/8" }, + { GSM_PCHAN_TCH_F, 0x01, 104, frame_tchf_ts0, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x02, 104, frame_tchf_ts1, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x04, 104, frame_tchf_ts2, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x08, 104, frame_tchf_ts3, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x10, 104, frame_tchf_ts4, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x20, 104, frame_tchf_ts5, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x40, 104, frame_tchf_ts6, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_F, 0x80, 104, frame_tchf_ts7, "TCH/F+SACCH" }, + { GSM_PCHAN_TCH_H, 0x03, 104, frame_tchh_ts01, "TCH/H+SACCH" }, + { GSM_PCHAN_TCH_H, 0x0c, 104, frame_tchh_ts23, "TCH/H+SACCH" }, + { GSM_PCHAN_TCH_H, 0x30, 104, frame_tchh_ts45, "TCH/H+SACCH" }, + { GSM_PCHAN_TCH_H, 0xc0, 104, frame_tchh_ts67, "TCH/H+SACCH" }, + { GSM_PCHAN_PDCH, 0xff, 104, frame_pdch, "PDCH" }, }; @@ -1718,8 +1873,9 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, if (!(l1h->config.slotmask & (1 << tn))) return -ENOTSUP; - for (i = 0; ARRAY_SIZE(trx_sched_multiframes); i++) { - if (trx_sched_multiframes[i].pchan == pchan) { + for (i = 0; i < ARRAY_SIZE(trx_sched_multiframes); i++) { + if (trx_sched_multiframes[i].pchan == pchan + && (trx_sched_multiframes[i].slotmask & (1 << tn))) { l1h->mf_index[tn] = i; l1h->mf_period[tn] = trx_sched_multiframes[i].period; l1h->mf_frames[tn] = trx_sched_multiframes[i].frames; @@ -1731,7 +1887,10 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, } } - return -EINVAL; + LOGP(DL1C, LOGL_NOTICE, "Failed to configuring multiframe " + "trx=%d ts=%d\n", l1h->trx->nr, tn); + + return -ENOTSUP; } /* setting all logical channels given attributes to active/inactive */ @@ -1765,6 +1924,30 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, return rc; } +/* setting all logical channels given attributes to active/inactive */ +int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, + uint8_t tch_mode) +{ + uint8_t tn = L1SAP_CHAN2TS(chan_nr); + int i; + int rc = -EINVAL; + + /* look for all matching chan_nr/link_id */ + for (i = 0; i < _TRX_CHAN_MAX; i++) { + if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) + && trx_chan_desc[i].link_id == 0x00) { + LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u on " + "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, + trx_chan_desc[i].name, l1h->trx->nr, tn); + l1h->chan_states[tn][i].rsl_cmode = rsl_cmode; + l1h->chan_states[tn][i].tch_mode = tch_mode; + rc = 0; + } + } + + return rc; +} + /* process ready-to-send */ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) { diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index 4a493fa6..28c72b53 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -26,6 +26,10 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, int downlink, int active); +/* setting all logical channels given attributes to active/inactive */ +int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, + uint8_t tch_mode); + /* close all logical channels and reset timeslots */ void trx_sched_reset(struct trx_l1h *l1h); diff --git a/src/osmo-bts-trx/tch_fr.c b/src/osmo-bts-trx/tch_fr.c index cfaf18ba..16fc8541 100644 --- a/src/osmo-bts-trx/tch_fr.c +++ b/src/osmo-bts-trx/tch_fr.c @@ -24,7 +24,7 @@ const struct osmo_crc8gen_code tch_fr_crc3 = { .bits = 3, - .poly = 0x2, + .poly = 0x3, .init = 0x0, .remainder = 0x7, }; -- cgit v1.2.3 From b9880bc81289fa13b12801abcfc77fe6e83b8a94 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Feb 2013 12:22:42 +0100 Subject: TRX: Allow transcoding of TCH FR with MSB first (RTP) or LSB first (E1) --- src/osmo-bts-trx/scheduler.c | 7 ++++--- src/osmo-bts-trx/tch_fr.c | 18 ++++++++++++------ src/osmo-bts-trx/tch_fr.h | 5 +++-- tests/bursts/bursts_test.c | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index d5c7bb54..f65a7025 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -759,9 +759,10 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* encode bursts (priorize FACCH) */ if (msg_facch) - tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); + tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), + 1); else - tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); + tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); /* unlink and free message */ if (msg_tch) @@ -1079,7 +1080,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* decode * also shift buffer by 4 bursts for interleaving */ - rc = tch_fr_decode(tch_data, *bursts_p); + rc = tch_fr_decode(tch_data, *bursts_p, 1); memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " diff --git a/src/osmo-bts-trx/tch_fr.c b/src/osmo-bts-trx/tch_fr.c index 16fc8541..23130e94 100644 --- a/src/osmo-bts-trx/tch_fr.c +++ b/src/osmo-bts-trx/tch_fr.c @@ -274,7 +274,7 @@ tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) } int -tch_fr_decode(uint8_t *tch_data, sbit_t *bursts) +tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int network_order) { sbit_t iB[912], cB[456], h; ubit_t conv[185], b[260], d[260], p[3]; @@ -300,9 +300,12 @@ tch_fr_decode(uint8_t *tch_data, sbit_t *bursts) if (rv) return -1; - tch_fr_d_to_b(b, d); + if (network_order) { + tch_fr_d_to_b(b, d); - tch_fr_reassemble(tch_data, b); + tch_fr_reassemble(tch_data, b); + } else + tch_fr_d_to_b(tch_data, d); len = 33; } else { @@ -317,7 +320,7 @@ tch_fr_decode(uint8_t *tch_data, sbit_t *bursts) } int -tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len) +tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int network_order) { ubit_t iB[912], cB[456], h; ubit_t conv[185], b[260], d[260], p[3]; @@ -325,9 +328,12 @@ tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len) switch (len) { case 33: /* TCH FR */ - tch_fr_disassemble(b, tch_data); + if (network_order) { + tch_fr_disassemble(b, tch_data); - tch_fr_b_to_d(d, b); + tch_fr_b_to_d(d, b); + } else + tch_fr_b_to_d(d, tch_data); osmo_crc8gen_set_bits(&tch_fr_crc3, d, 50, p); diff --git a/src/osmo-bts-trx/tch_fr.h b/src/osmo-bts-trx/tch_fr.h index 68a93185..01414572 100644 --- a/src/osmo-bts-trx/tch_fr.h +++ b/src/osmo-bts-trx/tch_fr.h @@ -1,7 +1,8 @@ #ifndef _TCH_FR_H #define _TCH_FR_H -int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts); -int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len); +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int network_order); +int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, + int network_order); #endif /* _TCH_FR_H */ diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index 9e64e3f1..9fc7df04 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -191,7 +191,7 @@ static void test_fr(uint8_t *speech, int len) printd("Encoding: %s\n", osmo_hexdump(speech, len)); /* encode */ - tch_fr_encode(bursts_u, speech, len); + tch_fr_encode(bursts_u, speech, len, 1); printd("U-Bits:\n"); printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57), @@ -246,7 +246,7 @@ static void test_fr(uint8_t *speech, int len) printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 812, 57)); /* decode */ - rc = tch_fr_decode(result, bursts_s); + rc = tch_fr_decode(result, bursts_s, 1); ASSERT_TRUE(rc == len); -- cgit v1.2.3 From 9de67ca9621deea3283b9f9c2bab99287a110e45 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 07:51:01 +0100 Subject: TRX: Lost TCH frame detection of omitted bursts from tranceiver --- src/osmo-bts-trx/l1_if.h | 2 +- src/osmo-bts-trx/scheduler.c | 143 +++++++++++++++++++++++++------------------ 2 files changed, 84 insertions(+), 61 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index a2a5e9c4..0ed0991c 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -54,7 +54,7 @@ struct trx_chan_state { sbit_t *ul_bursts; /* burst buffer for RX */ uint32_t ul_first_fn; /* fn of first burst */ uint8_t ul_mask; /* mask of received bursts */ - uint8_t sacch_lost; /* SACCH loss detection */ + uint8_t lost; /* (SACCH) loss detection */ uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */ }; diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f65a7025..629f8788 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -523,7 +523,57 @@ found_msg: } static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len); + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len) +{ + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* compose primitive */ + msg = l1sap_msgb_alloc(l2_len); + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, + PRIM_OP_INDICATION, msg); + l1sap->u.data.chan_nr = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.data.link_id = trx_chan_desc[chan].link_id; + l1sap->u.data.fn = fn; + msg->l2h = msgb_put(msg, l2_len); + if (l2_len) + memcpy(msg->l2h, l2, l2_len); + + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) + l1h->chan_states[tn][chan].lost = 0; + + /* forward primitive */ + l1sap_up(l1h->trx, l1sap); + + return 0; +} + +static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len) +{ + struct msgb *msg; + struct osmo_phsap_prim *l1sap; + + /* compose primitive */ + msg = l1sap_msgb_alloc(tch_len); + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, + PRIM_OP_INDICATION, msg); + l1sap->u.tch.chan_nr = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.tch.fn = fn; + msg->l2h = msgb_put(msg, tch_len); + if (tch_len) + memcpy(msg->l2h, tch, tch_len); + + if (l1h->chan_states[tn][chan].lost) + l1h->chan_states[tn][chan].lost--; + + /* forward primitive */ + l1sap_up(l1h->trx, l1sap); + + return 0; +} static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) @@ -569,7 +619,7 @@ got_msg: /* handle loss detection of sacch */ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { /* count and send BFI */ - if (++(l1h->chan_states[tn][chan].sacch_lost) > 1) + if (++(l1h->chan_states[tn][chan].lost) > 1) compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0); } @@ -686,6 +736,19 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } + /* handle loss detection of received TCH frames */ + if (++(l1h->chan_states[tn][chan].lost) > 5) { + uint8_t tch_data[33]; + + LOGP(DL1C, LOGL_NOTICE, "Missing TCH bursts detected, sending " + "BFI for %s\n", trx_chan_desc[chan].name); + + /* indicate bad frame */ + memset(tch_data, 0, sizeof(tch_data)); + // FIXME length depends on codec + compose_tch_ind(l1h, tn, 0, chan, tch_data, 33); + } + /* get frame and unlink from queue */ msg1 = dequeue_prim(l1h, tn, fn, chan); msg2 = dequeue_prim(l1h, tn, fn, chan); @@ -757,6 +820,14 @@ static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } + /* bad frame */ + if (msg_tch && !msg_facch && (msg_tch->l2h[0] >> 4) != 0xd) { + LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad frame' trx=%u " + "ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + goto send_burst; + } + /* encode bursts (priorize FACCH) */ if (msg_facch) tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), @@ -835,33 +906,6 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return 0; } -static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len) -{ - struct msgb *msg; - struct osmo_phsap_prim *l1sap; - - /* compose primitive */ - msg = l1sap_msgb_alloc(l2_len); - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, - PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = trx_chan_desc[chan].chan_nr | tn; - l1sap->u.data.link_id = trx_chan_desc[chan].link_id; - l1sap->u.data.fn = fn; - msg->l2h = msgb_put(msg, l2_len); - if (l2_len) - memcpy(msg->l2h, l2, l2_len); - - if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) - l1h->chan_states[tn][chan].sacch_lost = 0; - - /* forward primitive */ - l1sap_up(l1h->trx, l1sap); - - return 0; -} - static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { @@ -999,29 +1043,6 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, rc + 1); } -static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len) -{ - struct msgb *msg; - struct osmo_phsap_prim *l1sap; - - /* compose primitive */ - msg = l1sap_msgb_alloc(tch_len); - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, - PRIM_OP_INDICATION, msg); - l1sap->u.tch.chan_nr = trx_chan_desc[chan].chan_nr | tn; - l1sap->u.tch.fn = fn; - msg->l2h = msgb_put(msg, tch_len); - if (tch_len) - memcpy(msg->l2h, tch, tch_len); - - /* forward primitive */ - l1sap_up(l1h->trx, l1sap); - - return 0; -} - static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) { @@ -1085,17 +1106,19 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " "for %s\n", *first_fn, trx_chan_desc[chan].name); - memset(tch_data, 0, sizeof(tch_data)); - // FIXME length depends on codec - rc = 33; + goto bfi; } /* FACCH */ - if (rc == 23) - return compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, - 23); - + if (rc == 23) { + compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, 23); bfi: + // FIXME length depends on codec + rc = 33; + /* indicate bad tch frame */ + memset(tch_data, 0, sizeof(tch_data)); + } + /* TCH or BFI */ return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); } @@ -1917,7 +1940,7 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, l1h->chan_states[tn][i].ul_active = active; l1h->chan_states[tn][i].ul_active = active; } - l1h->chan_states[tn][i].sacch_lost = 0; + l1h->chan_states[tn][i].lost = 0; rc = 0; } } -- cgit v1.2.3 From 78b2080027d398c7242ae6e180b462e32f57a786 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 13:29:29 +0100 Subject: TRX: PDTCH (GPRS) works now Detection and transcoding of all four coding schemes are supported. --- src/osmo-bts-trx/pxxch.c | 140 ++++++++++++++++++++++++------------------- src/osmo-bts-trx/pxxch.h | 2 +- src/osmo-bts-trx/scheduler.c | 117 ++++++++++++++++++------------------ 3 files changed, 140 insertions(+), 119 deletions(-) diff --git a/src/osmo-bts-trx/pxxch.c b/src/osmo-bts-trx/pxxch.c index 17a7c947..07d011a9 100644 --- a/src/osmo-bts-trx/pxxch.c +++ b/src/osmo-bts-trx/pxxch.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -31,7 +32,7 @@ const struct osmo_crc64gen_code pxxch_crc40 = { /* - * GSM PDTCH CS-2, CS-3 parity + * GSM PDTCH CS-2, CS-3, CS-4 parity * * g(x) = x^16 + x^12 + x^5 + 1 */ @@ -189,35 +190,53 @@ pxxch_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *hl, ubit_t *hn) eB[58] = *hn; } -static ubit_t pdtch_hl_hn[4][8] = { +static ubit_t pdtch_hl_hn_ubit[4][8] = { { 1,1, 1,1, 1,1, 1,1 }, { 1,1, 0,0, 1,0, 0,0 }, { 0,0, 1,0, 0,0, 0,1 }, { 0,0, 0,1, 0,1, 1,0 }, }; +static sbit_t pdtch_hl_hn_sbit[4][8] = { + { -127,-127, -127,-127, -127,-127, -127,-127 }, + { -127,-127, 127, 127, -127, 127, 127, 127 }, + { 127, 127, -127, 127, 127, 127, 127,-127 }, + { 127, 127, 127,-127, 127,-127, -127, 127 }, +}; + static ubit_t usf2six[8][6] = { { 0,0,0, 0,0,0 }, - { 0,0,1, 0,1,1 }, - { 0,1,0, 1,1,0 }, - { 0,1,1, 1,0,1 }, { 1,0,0, 1,0,1 }, - { 1,0,1, 1,1,0 }, + { 0,1,0, 1,1,0 }, { 1,1,0, 0,1,1 }, + { 0,0,1, 0,1,1 }, + { 1,0,1, 1,1,0 }, + { 0,1,1, 1,0,1 }, { 1,1,1, 0,0,0 }, }; -static ubit_t usf2twelve[8][12] = { +static ubit_t usf2twelve_ubit[8][12] = { { 0,0,0, 0,0,0, 0,0,0, 0,0,0 }, - { 0,0,0, 0,1,1, 0,1,1, 1,0,1 }, - { 0,0,1, 1,0,1, 1,1,0, 1,1,0 }, - { 0,0,1, 1,1,0, 1,0,1, 0,1,1 }, { 1,1,0, 1,0,0, 0,0,1, 0,1,1 }, - { 1,1,0, 1,1,1, 0,1,0, 1,1,0 }, + { 0,0,1, 1,0,1, 1,1,0, 1,1,0 }, { 1,1,1, 0,0,1, 1,1,1, 1,0,1 }, + { 0,0,0, 0,1,1, 0,1,1, 1,0,1 }, + { 1,1,0, 1,1,1, 0,1,0, 1,1,0 }, + { 0,0,1, 1,1,0, 1,0,1, 0,1,1 }, { 1,1,1, 0,1,0, 1,0,0, 0,0,0 }, }; +static sbit_t usf2twelve_sbit[8][12] = { + { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }, + { -127,-127, 127, -127, 127, 127, 127, 127,-127, 127,-127,-127 }, + { 127, 127,-127, -127, 127,-127, -127,-127, 127, -127,-127, 127 }, + { -127,-127,-127, 127, 127,-127, -127,-127,-127, -127, 127,-127 }, + { 127, 127, 127, 127,-127,-127, 127,-127,-127, -127, 127,-127 }, + { -127,-127, 127, -127,-127,-127, 127,-127, 127, -127,-127, 127 }, + { 127, 127,-127, -127,-127, 127, -127, 127,-127, 127,-127,-127 }, + { -127,-127,-127, 127,-127, 127, -127, 127, 127, 127, 127, 127 }, +}; + static uint8_t puncture_cs2[588] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, @@ -267,23 +286,23 @@ static uint8_t puncture_cs3[676] = { }; int -pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) +pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) { sbit_t iB[456], cB[676], hl_hn[8]; ubit_t conv[456]; - int i, j, k, rv, best, cs, usf; + int i, j, k, rv, best = 0, cs = 0, usf = 0; /* make GCC happy */ for (i=0; i<4; i++) pxxch_burst_unmap(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, hl_hn + i*2 + 1); - for (i=0, best=0, cs=1; i<4; j++) { - for (j=0, k=0; j<4; j++) { - if (pdtch_hl_hn[i][j] == hl_hn[j]) - k++; - } - if (k > best) + for (i=0; i<4; i++) { + for (j=0, k=0; j<8; j++) + k += abs(((int)pdtch_hl_hn_sbit[i][j]) - ((int)hl_hn[j])); + if (i == 0 || k < best) { + best = k; cs = i+1; + } } pxxch_deinterleave(cB, iB); @@ -301,7 +320,7 @@ pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) return 23; case 2: - for (i=587, j=455; i>=0; i++) + for (i=587, j=455; i>=0; i--) if (!puncture_cs2[i]) cB[i] = cB[j--]; else @@ -309,31 +328,31 @@ pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) osmo_conv_decode(&conv_cs2, cB, conv); - for (i=0, best=0, usf=0; i<8; j++) { - for (j=0, k=0; j<6; j++) { - if (usf2six[i][j] == conv[j]) - k++; - } - if (k > best) + for (i=0; i<8; i++) { + for (j=0, k=0; j<6; j++) + k += abs(((int)usf2six[i][j]) - ((int)conv[j])); + if (i == 0 || k < best) { + best = k; usf = i; + } } - conv[3] = (usf >> 2) & 1; + conv[3] = usf & 1; conv[4] = (usf >> 1) & 1; - conv[5] = usf & 1; + conv[5] = (usf >> 2) & 1; if (usf_p) *usf_p = usf; - rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+3, 271, + rv = osmo_crc16gen_check_bits(&pdtch_crc16, conv+3, 271, conv+3+271); if (rv) return -1; - osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 271, 1); + osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 271, 1); return 34; case 3: - for (i=675, j=455; i>=0; i++) + for (i=675, j=455; i>=0; i--) if (!puncture_cs3[i]) cB[i] = cB[j--]; else @@ -341,54 +360,55 @@ pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) osmo_conv_decode(&conv_cs3, cB, conv); - for (i=0, best=0, usf=0; i<8; j++) { - for (j=0, k=0; j<6; j++) { - if (usf2six[i][j] == conv[j]) - k++; - } - if (k > best) + for (i=0; i<8; i++) { + for (j=0, k=0; j<6; j++) + k += abs(((int)usf2six[i][j]) - ((int)conv[j])); + if (i == 0 || k < best) { + best = k; usf = i; + } } - conv[3] = (usf >> 2) & 1; + conv[3] = usf & 1; conv[4] = (usf >> 1) & 1; - conv[5] = usf & 1; + conv[5] = (usf >> 2) & 1; if (usf_p) *usf_p = usf; - rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+3, 315, + rv = osmo_crc16gen_check_bits(&pdtch_crc16, conv+3, 315, conv+3+315); if (rv) return -1; - osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 315, 1); + osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 315, 1); return 40; case 4: - for (i=0; i<456;i++) + for (i=12; i<456;i++) conv[i] = (cB[i] < 0) ? 1:0; - for (i=0, best=0, usf=0; i<8; j++) { - for (j=0, k=0; j<12; j++) { - if (usf2twelve[i][j] == conv[j]) - k++; - } - if (k > best) + for (i=0; i<8; i++) { + for (j=0, k=0; j<12; j++) + k += abs(((int)usf2twelve_sbit[i][j]) - + ((int)cB[j])); + if (i == 0 || k < best) { + best = k; usf = i; + } } - conv[9] = (usf >> 2) & 1; + conv[9] = usf & 1; conv[10] = (usf >> 1) & 1; - conv[11] = usf & 1; + conv[11] = (usf >> 2) & 1; if (usf_p) *usf_p = usf; - rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv+9, 431, + rv = osmo_crc16gen_check_bits(&pdtch_crc16, conv+9, 431, conv+9+431); if (rv) return -1; - osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 431, 1); + osmo_ubit2pbit_ext(l2_data, 0, conv, 9, 431, 1); return 54; } @@ -411,12 +431,12 @@ pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) osmo_conv_encode(&conv_cs1, conv, cB); - hl_hn = pdtch_hl_hn[0]; + hl_hn = pdtch_hl_hn_ubit[0]; break; case 34: osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 271, 1); - usf = (conv[3] << 2) | (conv[4] << 1) | conv[5]; + usf = l2_data[0] & 0x7; osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 271, conv+3+271); @@ -428,12 +448,12 @@ pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) if (!puncture_cs2[i]) cB[j++] = cB[i]; - hl_hn = pdtch_hl_hn[1]; + hl_hn = pdtch_hl_hn_ubit[1]; break; case 40: osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 315, 1); - usf = (conv[3] << 2) | (conv[4] << 1) | conv[5]; + usf = l2_data[0] & 0x7; osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 315, conv+3+315); @@ -445,18 +465,18 @@ pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) if (!puncture_cs3[i]) cB[j++] = cB[i]; - hl_hn = pdtch_hl_hn[2]; + hl_hn = pdtch_hl_hn_ubit[2]; break; case 54: osmo_pbit2ubit_ext(cB, 9, l2_data, 0, 431, 1); - usf = (cB[9] << 2) | (cB[10] << 1) | conv[11]; + usf = l2_data[0] & 0x7; osmo_crc16gen_set_bits(&pdtch_crc16, cB+9, 431, cB+9+431); - memcpy(cB, usf2twelve[usf], 12); + memcpy(cB, usf2twelve_ubit[usf], 12); - hl_hn = pdtch_hl_hn[3]; + hl_hn = pdtch_hl_hn_ubit[3]; break; default: diff --git a/src/osmo-bts-trx/pxxch.h b/src/osmo-bts-trx/pxxch.h index 313fbf59..990bd36d 100644 --- a/src/osmo-bts-trx/pxxch.h +++ b/src/osmo-bts-trx/pxxch.h @@ -1,7 +1,7 @@ #ifndef _PXXCH_H #define _PXXCH_H -int pdch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); +int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); #endif /* _PXXCH_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 629f8788..f44df519 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -138,6 +138,7 @@ static const ubit_t sch_train[64] = { */ struct trx_chan_desc { + int pdch; enum trx_chan_type chan; uint8_t chan_nr; uint8_t link_id; @@ -148,44 +149,44 @@ struct trx_chan_desc { int auto_active; }; struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { - { TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 }, - { TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 }, - { TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 }, - { TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 }, - { TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 }, - { TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 }, - { TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, - { TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, - { TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, - { TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_SACCH8_7, 0x68, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, - { TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, + { 0, TRXC_IDLE, 0, 0, "IDLE", NULL, tx_idle_fn, NULL, 1 }, + { 0, TRXC_FCCH, 0, 0, "FCCH", NULL, tx_fcch_fn, NULL, 1 }, + { 0, TRXC_SCH, 0, 0, "SCH", NULL, tx_sch_fn, NULL, 1 }, + { 0, TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 }, + { 0, TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 }, + { 0, TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 }, + { 0, TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, + { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { 0, TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH4_3, 0x38, 0x00, "SDCCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_0, 0x40, 0x00, "SDCCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_1, 0x48, 0x00, "SDCCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_2, 0x50, 0x00, "SDCCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_3, 0x58, 0x00, "SDCCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_4, 0x60, 0x00, "SDCCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_5, 0x68, 0x00, "SDCCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_6, 0x70, 0x00, "SDCCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SDCCH8_7, 0x78, 0x00, "SDCCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCHTF, 0x08, 0x40, "SACCH/TF", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCHTH_0, 0x10, 0x40, "SACCH/TH(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCHTH_1, 0x18, 0x40, "SACCH/TH(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH4_0, 0x20, 0x40, "SACCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH4_1, 0x28, 0x40, "SACCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH4_2, 0x30, 0x40, "SACCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH4_3, 0x38, 0x40, "SACCH/4(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_0, 0x40, 0x40, "SACCH/8(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_1, 0x48, 0x40, "SACCH/8(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_2, 0x50, 0x40, "SACCH/8(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_3, 0x58, 0x40, "SACCH/8(3)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_7, 0x68, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 1, TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, + { 1, TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, }; @@ -926,7 +927,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* store frame number of first burst */ + /* clear burst & store frame number of first burst */ if (bid == 0) { memset(*bursts_p, 0, 464); *mask = 0x0; @@ -980,7 +981,6 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; - uint32_t *first_fn = &chan_state->ul_first_fn; uint8_t *mask = &chan_state->ul_mask; uint8_t l2[54+1]; int rc; @@ -995,11 +995,10 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* store frame number of first burst */ + /* clear burst */ if (bid == 0) { memset(*bursts_p, 0, 464); *mask = 0x0; - *first_fn = fn; } /* update mask */ @@ -1018,29 +1017,26 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block at " - "fn=%u (%u/%u) for %s\n", *first_fn, - (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block " + "ending at fn=%u (%u/%u) for %s\n", fn, + fn % l1h->mf_period[tn], l1h->mf_period[tn], trx_chan_desc[chan].name); - /* we require first burst to have correct FN */ - if (!(*mask & 0x1)) { - *mask = 0x0; - return 0; - } } *mask = 0x0; /* decode */ - rc = pdch_decode(l2 + 1, *bursts_p, NULL); + rc = pdtch_decode(l2 + 1, *bursts_p, NULL); if (rc <= 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block at fn=%u for " - "%s\n", *first_fn, trx_chan_desc[chan].name); - l2[0] = 0; /* bad frame */ - rc = 0; - } else - l2[0] = 7; /* valid frame */ + LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block ending at " + "fn=%u (%u/%u) for %s\n", fn, fn % l1h->mf_period[tn], + l1h->mf_period[tn], trx_chan_desc[chan].name); + return 0; + } - return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, rc + 1); + l2[0] = 7; /* valid frame */ + + return compose_ph_data_ind(l1h, tn, (fn + 2715648 -3) % 2715648, chan, + l2, rc + 1); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1927,6 +1923,11 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* look for all matching chan_nr/link_id */ for (i = 0; i < _TRX_CHAN_MAX; i++) { + /* skip if pchan type does not match pdch flag */ + if ((trx_sched_multiframes[l1h->mf_index[tn]].pchan + == GSM_PCHAN_PDCH) + != trx_chan_desc[i].pdch) + continue; if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) && trx_chan_desc[i].link_id == link_id) { LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", -- cgit v1.2.3 From 450d32919ada7b037b41d89ea8dd181924ed48e9 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 13:31:10 +0100 Subject: TRX: Add test code for PDTCH transcoding --- tests/bursts/Makefile.am | 7 +++- tests/bursts/bursts_test.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am index fd6c41f7..09275188 100644 --- a/tests/bursts/Makefile.am +++ b/tests/bursts/Makefile.am @@ -4,5 +4,10 @@ LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) noinst_PROGRAMS = bursts_test EXTRA_DIST = bursts_test.ok -bursts_test_SOURCES = bursts_test.c $(top_builddir)/src/osmo-bts-trx/xcch.c $(top_builddir)/src/osmo-bts-trx/rach.c $(top_builddir)/src/osmo-bts-trx/sch.c $(top_builddir)/src/osmo-bts-trx/tch_fr.c +bursts_test_SOURCES = bursts_test.c \ + $(top_builddir)/src/osmo-bts-trx/xcch.c \ + $(top_builddir)/src/osmo-bts-trx/rach.c \ + $(top_builddir)/src/osmo-bts-trx/sch.c \ + $(top_builddir)/src/osmo-bts-trx/tch_fr.c \ + $(top_builddir)/src/osmo-bts-trx/pxxch.c bursts_test_LDADD = $(LDADD) diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index 9fc7df04..ad1f6859 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -30,6 +30,7 @@ #include "../../src/osmo-bts-trx/rach.h" #include "../../src/osmo-bts-trx/sch.h" #include "../../src/osmo-bts-trx/tch_fr.h" +#include "../../src/osmo-bts-trx/pxxch.h" #define ASSERT_TRUE(rc) \ @@ -257,6 +258,71 @@ static void test_fr(uint8_t *speech, int len) printd("\n"); } +static void test_pdtch(uint8_t *l2, int len) +{ + uint8_t result[len]; + ubit_t bursts_u[116 * 4]; + sbit_t bursts_s[116 * 4]; + int rc; + + /* zero the not coded tail bits */ + switch (len) { + case 34: + case 54: + l2[len - 1] &= 0x7f; + result[len - 1] &= 0x7f; + break; + case 40: + l2[len - 1] &= 0x07; + result[len - 1] &= 0x07; + break; + } + + printd("Encoding: %s\n", osmo_hexdump(l2, len)); + + /* encode */ + pdtch_encode(bursts_u, l2, len); + + printd("U-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57), + bursts_u[57], bursts_u[58]); + printd("%s\n", osmo_hexdump(bursts_u + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57), + bursts_u[57 + 116], bursts_u[58 + 116]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57), + bursts_u[57 + 232], bursts_u[58 + 232]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57), + bursts_u[57 + 348], bursts_u[58 + 348]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57)); + ubits2sbits(bursts_u, bursts_s, 116 * 4); + printd("S-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57), + (uint8_t)bursts_s[57], (uint8_t)bursts_s[58]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57), + (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57), + (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57), + (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); + + /* decode */ + rc = pdtch_decode(result, bursts_s, NULL); + + ASSERT_TRUE(rc == len); + + printd("Decoded: %s\n", osmo_hexdump(result, len)); + + ASSERT_TRUE(!memcmp(l2, result, len)); + + printd("\n"); +} + uint8_t test_l2[][23] = { /* dummy frame */ { 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -272,6 +338,21 @@ uint8_t test_l2[][23] = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, }; +uint8_t test_macblock[][54] = { + /* random frame */ + { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, + 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92, + 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8, 0x42, + 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, + 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, + 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92, + 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0xa8 }, + /* jolly frame */ + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, +}; + uint8_t test_speech[33]; int main(int argc, char **argv) @@ -297,6 +378,13 @@ int main(int argc, char **argv) for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_fr(test_l2[i], sizeof(test_l2[0])); + for (i = 0; i < sizeof(test_macblock) / sizeof(test_macblock[0]); i++) { + test_pdtch(test_macblock[i], 23); + test_pdtch(test_macblock[i], 34); + test_pdtch(test_macblock[i], 40); + test_pdtch(test_macblock[i], 54); + } + printf("Success\n"); return 0; -- cgit v1.2.3 From 7451ce29a79a8da6da0ac1dbf155ed4af016dcdc Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 16:17:43 +0100 Subject: TRX: Detect missing received bursts and fill them with zero-sbits --- src/osmo-bts-trx/l1_if.h | 1 + src/osmo-bts-trx/scheduler.c | 95 +++++++++++++++++++++++++++----------------- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 0ed0991c..8a0a90e6 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -106,6 +106,7 @@ struct trx_l1h { struct trx_config config; uint8_t mf_index[8]; /* selected multiframe index */ + uint32_t mf_last_fn[8]; /* last received frame */ uint8_t mf_period[8]; /* period of multiframe */ struct trx_sched_frame *mf_frames[8]; /* pointer to frame layout */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f44df519..31a0fcd4 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -207,6 +207,7 @@ int trx_sched_init(struct trx_l1h *l1h) for (tn = 0; tn < 8; tn++) { l1h->mf_index[tn] = 0; + l1h->mf_last_fn[tn] = 0; INIT_LLIST_HEAD(&l1h->dl_prims[tn]); for (i = 0; i < _TRX_CHAN_MAX; i++) { chan_state = &l1h->chan_states[tn][i]; @@ -1035,7 +1036,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ - return compose_ph_data_ind(l1h, tn, (fn + 2715648 -3) % 2715648, chan, + return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, l2, rc + 1); } @@ -1044,7 +1045,6 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; - uint32_t *first_fn = &chan_state->ul_first_fn; uint8_t *mask = &chan_state->ul_mask; uint8_t tch_data[33]; int rc; @@ -1059,11 +1059,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -ENOMEM; } - /* store frame number of first burst */ + /* clear burst */ if (bid == 0) { memset(*bursts_p + 464, 0, 464); *mask = 0x0; - *first_fn = fn; } /* update mask */ @@ -1082,16 +1081,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* check for complete set of bursts */ if ((*mask & 0xf) != 0xf) { - LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame at " - "fn=%u (%u/%u) for %s\n", *first_fn, - (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " + "at fn=%u (%u/%u) for %s\n", fn, + fn % l1h->mf_period[tn], l1h->mf_period[tn], trx_chan_desc[chan].name); - /* we require first burst to have correct FN */ - if (!(*mask & 0x1)) { - *mask = 0x0; - rc = 0; - goto bfi; - } } *mask = 0x0; @@ -1100,14 +1093,15 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, rc = tch_fr_decode(tch_data, *bursts_p, 1); memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { - LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame at fn=%u " - "for %s\n", *first_fn, trx_chan_desc[chan].name); + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, trx_chan_desc[chan].name); goto bfi; } /* FACCH */ if (rc == 23) { - compose_ph_data_ind(l1h, tn, *first_fn, chan, tch_data, 23); + compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, + tch_data, 23); bfi: // FIXME length depends on codec rc = 33; @@ -1116,7 +1110,8 @@ bfi: } /* TCH or BFI */ - return compose_tch_ind(l1h, tn, *first_fn, chan, tch_data, rc); + return compose_tch_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, + tch_data, rc); } static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -2054,40 +2049,68 @@ if (0) if (chan != TRXC_IDLE) // hack } /* process uplink burst */ -int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, sbit_t *bits, int8_t rssi, float toa) { struct trx_sched_frame *frame; uint8_t offset, period, bid; trx_sched_ul_func *func; enum trx_chan_type chan; - int rc; + uint32_t fn, elapsed; if (!l1h->mf_index[tn]) return -EINVAL; - /* get frame from multiframe */ - period = l1h->mf_period[tn]; - offset = fn % period; - frame = l1h->mf_frames[tn] + offset; + /* calculate how many frames have been elapsed */ + elapsed = (current_fn + 2715648 - l1h->mf_last_fn[tn]) % 2715648; - chan = frame->ul_chan; - bid = frame->ul_bid; - func = trx_chan_desc[chan].ul_fn; + /* start counting from last fn + 1, but only if not too many fn have + * been elapsed */ + if (elapsed < 10) + fn = (l1h->mf_last_fn[tn] + 1) % 2715648; + else + fn = current_fn; + + while (42) { + /* get frame from multiframe */ + period = l1h->mf_period[tn]; + offset = fn % period; + frame = l1h->mf_frames[tn] + offset; + + chan = frame->ul_chan; + bid = frame->ul_bid; + func = trx_chan_desc[chan].ul_fn; + + /* check if channel is active */ + if (!trx_chan_desc[chan].auto_active + && !l1h->chan_states[tn][chan].ul_active) + goto next_frame; + + /* omit bursts which have no handler, like IDLE bursts */ + if (!func) + goto next_frame; + + /* put burst to function */ + if (fn == current_fn) + func(l1h, tn, fn, chan, bid, bits, toa); + else { + sbit_t spare[148]; + + memset(spare, 0, 148); + func(l1h, tn, fn, chan, bid, spare, toa); + } - /* check if channel is active */ - if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].ul_active) - return -EINVAL; +next_frame: + /* reached current fn */ + if (fn == current_fn) + break; - /* omit bursts which have no handler, like IDLE bursts */ - if (!func) - return 0; + fn = (fn + 1) % 2715648; + } - /* put burst to function */ - rc = func(l1h, tn, fn, chan, bid, bits, toa); + l1h->mf_last_fn[tn] = fn; - return rc; + return 0; } /* schedule all frames of all TRX for given FN */ -- cgit v1.2.3 From 801c182c02a38c9e3fb89c431caabc4c5abddf6f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 15 Feb 2013 16:20:19 +0100 Subject: TRX: By default, send 20 frames in advance to tranceiver --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 31a0fcd4..afe4aba3 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -58,7 +58,7 @@ static struct timeval tranceiver_clock_tv; static struct osmo_timer_list tranceiver_clock_timer; /* clock advance for the tranceiver */ -uint32_t trx_clock_advance = 10; +uint32_t trx_clock_advance = 20; /* advance RTS to give some time for data processing. (especially PCU) */ uint32_t trx_rts_advance = 5; /* about 20ms */ -- cgit v1.2.3 From 89e36c0e646a9e73728ee65cc6ac32f01d388885 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 24 Feb 2013 10:12:09 +0100 Subject: TRX: Cleanup of channel transcoding --- src/osmo-bts-trx/Makefile.am | 4 +- src/osmo-bts-trx/gsm0503_coding.c | 571 ++++++++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_coding.h | 15 + src/osmo-bts-trx/gsm0503_conv.c | 83 +++++ src/osmo-bts-trx/gsm0503_conv.h | 11 + src/osmo-bts-trx/gsm0503_interleaving.c | 97 ++++++ src/osmo-bts-trx/gsm0503_interleaving.h | 9 + src/osmo-bts-trx/gsm0503_mapping.c | 65 ++++ src/osmo-bts-trx/gsm0503_mapping.h | 10 + src/osmo-bts-trx/gsm0503_parity.c | 77 +++++ src/osmo-bts-trx/gsm0503_parity.h | 10 + src/osmo-bts-trx/gsm0503_tables.c | 139 ++++++++ src/osmo-bts-trx/gsm0503_tables.h | 14 + src/osmo-bts-trx/pxxch.c | 493 --------------------------- src/osmo-bts-trx/pxxch.h | 7 - src/osmo-bts-trx/rach.c | 118 ------- src/osmo-bts-trx/rach.h | 7 - src/osmo-bts-trx/sch.c | 89 ----- src/osmo-bts-trx/sch.h | 7 - src/osmo-bts-trx/scheduler.c | 6 +- src/osmo-bts-trx/tch_fr.c | 365 -------------------- src/osmo-bts-trx/tch_fr.h | 8 - src/osmo-bts-trx/xcch.c | 193 ----------- src/osmo-bts-trx/xcch.h | 10 - tests/bursts/Makefile.am | 11 +- tests/bursts/bursts_test.c | 6 +- 26 files changed, 1111 insertions(+), 1314 deletions(-) create mode 100644 src/osmo-bts-trx/gsm0503_coding.c create mode 100644 src/osmo-bts-trx/gsm0503_coding.h create mode 100644 src/osmo-bts-trx/gsm0503_conv.c create mode 100644 src/osmo-bts-trx/gsm0503_conv.h create mode 100644 src/osmo-bts-trx/gsm0503_interleaving.c create mode 100644 src/osmo-bts-trx/gsm0503_interleaving.h create mode 100644 src/osmo-bts-trx/gsm0503_mapping.c create mode 100644 src/osmo-bts-trx/gsm0503_mapping.h create mode 100644 src/osmo-bts-trx/gsm0503_parity.c create mode 100644 src/osmo-bts-trx/gsm0503_parity.h create mode 100644 src/osmo-bts-trx/gsm0503_tables.c create mode 100644 src/osmo-bts-trx/gsm0503_tables.h delete mode 100644 src/osmo-bts-trx/pxxch.c delete mode 100644 src/osmo-bts-trx/pxxch.h delete mode 100644 src/osmo-bts-trx/rach.c delete mode 100644 src/osmo-bts-trx/rach.h delete mode 100644 src/osmo-bts-trx/sch.c delete mode 100644 src/osmo-bts-trx/sch.h delete mode 100644 src/osmo-bts-trx/tch_fr.c delete mode 100644 src/osmo-bts-trx/tch_fr.h delete mode 100644 src/osmo-bts-trx/xcch.c delete mode 100644 src/osmo-bts-trx/xcch.h diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index 94a050ce..d5021c92 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -2,10 +2,10 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp -EXTRA_DIST = trx_if.h l1_if.h scheduler.h xcch.h rach.h sch.h pxxch.h tch_fr.h +EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h bin_PROGRAMS = osmobts-trx -osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c xcch.c rach.c sch.c pxxch.c tch_fr.c +osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c new file mode 100644 index 00000000..2d3f7705 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -0,0 +1,571 @@ + +#include +#include +#include +#include + +#include +#include +#include + +#include "gsm0503_conv.h" +#include "gsm0503_parity.h" +#include "gsm0503_mapping.h" +#include "gsm0503_interleaving.h" +#include "gsm0503_tables.h" +#include "gsm0503_coding.h" + +static int _xcch_decode_cB(uint8_t *l2_data, sbit_t *cB) +{ + ubit_t conv[224]; + int rv; + + osmo_conv_decode(&gsm0503_conv_xcch, cB, conv); + + rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40, conv, 184, conv+184); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); + + return 0; +} + +static int _xcch_encode_cB(ubit_t *cB, uint8_t *l2_data) +{ + ubit_t conv[224]; + + osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); + + osmo_crc64gen_set_bits(&gsm0503_fire_crc40, conv, 184, conv+184); + + osmo_conv_encode(&gsm0503_conv_xcch, conv, cB); + + return 0; +} + + +/* + * GSM xCCH block transcoding + */ + +int xcch_decode(uint8_t *l2_data, sbit_t *bursts) +{ + sbit_t iB[456], cB[456]; + int i; + + for (i=0; i<4; i++) + gsm0503_xcch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, + NULL); + + gsm0503_xcch_deinterleave(cB, iB); + + return _xcch_decode_cB(l2_data, cB); +} + +int xcch_encode(ubit_t *bursts, uint8_t *l2_data) +{ + ubit_t iB[456], cB[456], hl = 1, hn = 1; + int i; + + _xcch_encode_cB(cB, l2_data); + + gsm0503_xcch_interleave(cB, iB); + + for (i=0; i<4; i++) + gsm0503_xcch_burst_map(&iB[i * 114], &bursts[i * 116], &hl, + &hn); + + return 0; +} + + +/* + * GSM PDTCH block transcoding + */ + +int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) +{ + sbit_t iB[456], cB[676], hl_hn[8]; + ubit_t conv[456]; + int i, j, k, rv, best = 0, cs = 0, usf = 0; /* make GCC happy */ + + for (i=0; i<4; i++) + gsm0503_xcch_burst_unmap(&iB[i * 114], &bursts[i * 116], + hl_hn + i*2, hl_hn + i*2 + 1); + + for (i=0; i<4; i++) { + for (j=0, k=0; j<8; j++) + k += abs(((int)gsm0503_pdtch_hl_hn_sbit[i][j]) - + ((int)hl_hn[j])); + if (i == 0 || k < best) { + best = k; + cs = i+1; + } + } + + gsm0503_xcch_deinterleave(cB, iB); + + switch (cs) { + case 1: + osmo_conv_decode(&gsm0503_conv_xcch, cB, conv); + + rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40, conv, 184, + conv+184); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); + + return 23; + case 2: + for (i=587, j=455; i>=0; i--) + if (!gsm0503_puncture_cs2[i]) + cB[i] = cB[j--]; + else + cB[i] = 0; + + osmo_conv_decode(&gsm0503_conv_cs2, cB, conv); + + for (i=0; i<8; i++) { + for (j=0, k=0; j<6; j++) + k += abs(((int)gsm0503_usf2six[i][j]) - + ((int)conv[j])); + if (i == 0 || k < best) { + best = k; + usf = i; + } + } + + conv[3] = usf & 1; + conv[4] = (usf >> 1) & 1; + conv[5] = (usf >> 2) & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16, conv+3, 271, + conv+3+271); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 271, 1); + + return 34; + case 3: + for (i=675, j=455; i>=0; i--) + if (!gsm0503_puncture_cs3[i]) + cB[i] = cB[j--]; + else + cB[i] = 0; + + osmo_conv_decode(&gsm0503_conv_cs3, cB, conv); + + for (i=0; i<8; i++) { + for (j=0, k=0; j<6; j++) + k += abs(((int)gsm0503_usf2six[i][j]) - + ((int)conv[j])); + if (i == 0 || k < best) { + best = k; + usf = i; + } + } + + conv[3] = usf & 1; + conv[4] = (usf >> 1) & 1; + conv[5] = (usf >> 2) & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16, conv+3, 315, + conv+3+315); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 315, 1); + + return 40; + case 4: + for (i=12; i<456;i++) + conv[i] = (cB[i] < 0) ? 1:0; + + for (i=0; i<8; i++) { + for (j=0, k=0; j<12; j++) + k += abs(((int)gsm0503_usf2twelve_sbit[i][j]) - + ((int)cB[j])); + if (i == 0 || k < best) { + best = k; + usf = i; + } + } + + conv[9] = usf & 1; + conv[10] = (usf >> 1) & 1; + conv[11] = (usf >> 2) & 1; + if (usf_p) + *usf_p = usf; + + rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16, conv+9, 431, + conv+9+431); + if (rv) + return -1; + + osmo_ubit2pbit_ext(l2_data, 0, conv, 9, 431, 1); + + return 54; + } + + return -1; +} + +int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) +{ + ubit_t iB[456], cB[676]; + const ubit_t *hl_hn; + ubit_t conv[334]; + int i, j, usf; + + switch (l2_len) { + case 23: + osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); + + osmo_crc64gen_set_bits(&gsm0503_fire_crc40, conv, 184, + conv+184); + + osmo_conv_encode(&gsm0503_conv_xcch, conv, cB); + + hl_hn = gsm0503_pdtch_hl_hn_ubit[0]; + + break; + case 34: + osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 271, 1); + usf = l2_data[0] & 0x7; + + osmo_crc16gen_set_bits(&gsm0503_cs234_crc16, conv+3, 271, + conv+3+271); + + memcpy(conv, gsm0503_usf2six[usf], 6); + + osmo_conv_encode(&gsm0503_conv_cs2, conv, cB); + + for (i=0, j=0; i<588; i++) + if (!gsm0503_puncture_cs2[i]) + cB[j++] = cB[i]; + + hl_hn = gsm0503_pdtch_hl_hn_ubit[1]; + + break; + case 40: + osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 315, 1); + usf = l2_data[0] & 0x7; + + osmo_crc16gen_set_bits(&gsm0503_cs234_crc16, conv+3, 315, + conv+3+315); + + memcpy(conv, gsm0503_usf2six[usf], 6); + + osmo_conv_encode(&gsm0503_conv_cs3, conv, cB); + + for (i=0, j=0; i<676; i++) + if (!gsm0503_puncture_cs3[i]) + cB[j++] = cB[i]; + + hl_hn = gsm0503_pdtch_hl_hn_ubit[2]; + + break; + case 54: + osmo_pbit2ubit_ext(cB, 9, l2_data, 0, 431, 1); + usf = l2_data[0] & 0x7; + + osmo_crc16gen_set_bits(&gsm0503_cs234_crc16, cB+9, 431, + cB+9+431); + + memcpy(cB, gsm0503_usf2twelve_ubit[usf], 12); + + hl_hn = gsm0503_pdtch_hl_hn_ubit[3]; + + break; + default: + return -1; + } + + gsm0503_xcch_interleave(cB, iB); + + for (i=0; i<4; i++) + gsm0503_xcch_burst_map(&iB[i * 114], &bursts[i * 116], + hl_hn + i*2, hl_hn + i*2 + 1); + + return 0; +} + + +/* + * GSM TCH/F FR transcoding + */ + +static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +{ + int i, j, k, l, o; + + tch_data[0] = 0xd << 4; + memset(tch_data + 1, 0, 32); + /* reassemble d-bits */ + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset input bits */ + while (i < 260) { + tch_data[j>>3] |= (b_bits[k+o] << (7-(j&7))); + if (--k < 0) { + o += gsm0503_gsm_fr_map[l]; + k = gsm0503_gsm_fr_map[++l]-1; + } + i++; + j++; + } + + /* rearrange according to Table 2 of TS 05.03 */ +} + +static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +{ + int i, j, k, l, o; + + i = 0; /* counts bits */ + j = 4; /* counts input bits */ + k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset output bits */ + while (i < 260) { + b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; + if (--k < 0) { + o += gsm0503_gsm_fr_map[l]; + k = gsm0503_gsm_fr_map[++l]-1; + } + i++; + j++; + } + +} + +static void tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) +{ + int i; + + for (i = 0; i < 260; i++) + b_bits[gsm0503_d_to_b_index[i]] = d_bits[i]; +} + +static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) +{ + int i; + + for (i = 0; i < 260; i++) + d_bits[i] = b_bits[gsm0503_d_to_b_index[i]]; +} + +static void tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) +{ + int i; + + for (i=0; i<91; i++) { + d[i<<1] = u[i]; + d[(i<<1)+1] = u[184-i]; + } + for (i=0; i<3; i++) + p[i] = u[91+i]; +} + +static void tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) +{ + int i; + + for (i=0; i<91; i++) { + u[i] = d[i<<1]; + u[184-i] = d[(i<<1)+1]; + } + for (i=0; i<3; i++) + u[91+i] = p[i]; +} + +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) +{ + sbit_t iB[912], cB[456], h; + ubit_t conv[185], b[260], d[260], p[3]; + int i, rv, len, steal = 0; + + for (i=0; i<8; i++) { + gsm0503_tch_fr_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, + i>>2); + steal -= h; + } + + gsm0503_tch_fr_deinterleave(cB, iB); + + if (steal <= 0) { + osmo_conv_decode(&gsm0503_conv_tch_fr, cB, conv); + + tch_fr_unreorder(d, p, conv); + + for (i=0; i<78; i++) + d[i+182] = (cB[i+378] < 0) ? 1:0; + + rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d, 50, p); + if (rv) + return -1; + + if (net_order) { + tch_fr_d_to_b(b, d); + + tch_fr_reassemble(tch_data, b); + } else + tch_fr_d_to_b(tch_data, d); + + len = 33; + } else { + rv = _xcch_decode_cB(tch_data, cB); + if (rv) + return -1; + + len = 23; + } + + return len; +} + +int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order) +{ + ubit_t iB[912], cB[456], h; + ubit_t conv[185], b[260], d[260], p[3]; + int i; + + switch (len) { + case 33: /* TCH FR */ + if (net_order) { + tch_fr_disassemble(b, tch_data); + + tch_fr_b_to_d(d, b); + } else + tch_fr_b_to_d(d, tch_data); + + osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p); + + tch_fr_reorder(conv, d, p); + + memcpy(cB+378, d+182, 78); + + osmo_conv_encode(&gsm0503_conv_tch_fr, conv, cB); + + h = 0; + + break; + case 23: /* FACCH */ + _xcch_encode_cB(cB, tch_data); + + h = 1; + + break; + default: + return -1; + } + + gsm0503_tch_fr_interleave(cB, iB); + + for (i=0; i<8; i++) + gsm0503_tch_fr_burst_map(&iB[i * 114], &bursts[i * 116], &h, + i>>2); + + return 0; +} + + +/* + * GSM RACH transcoding + */ + +/* + * GSM RACH apply BSIC to parity + * + * p(j) = p(j) xor b(j) j = 0, ..., 5 + * b(0) = MSB of PLMN colour code + * b(5) = LSB of BS colour code + */ + +static int rach_apply_bsic(ubit_t *d, uint8_t bsic) +{ + int i; + + /* Apply it */ + for (i=0; i<6; i++) + d[8+i] ^= ((bsic >> (5-i)) & 1); + + return 0; +} + +int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic) +{ + ubit_t conv[14]; + int rv; + + osmo_conv_decode(&gsm0503_conv_rach, burst, conv); + + rach_apply_bsic(conv, bsic); + + rv = osmo_crc8gen_check_bits(&gsm0503_rach_crc6, conv, 8, conv+8); + if (rv) + return -1; + + osmo_ubit2pbit_ext(ra, 0, conv, 0, 8, 1); + + return 0; +} + +int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic) +{ + ubit_t conv[14]; + + osmo_pbit2ubit_ext(conv, 0, ra, 0, 8, 1); + + osmo_crc8gen_set_bits(&gsm0503_rach_crc6, conv, 8, conv+8); + + rach_apply_bsic(conv, bsic); + + osmo_conv_encode(&gsm0503_conv_rach, conv, burst); + + return 0; +} + + +/* + * GSM SCH transcoding + */ + +int sch_decode(uint8_t *sb_info, sbit_t *burst) +{ + ubit_t conv[35]; + int rv; + + osmo_conv_decode(&gsm0503_conv_sch, burst, conv); + + rv = osmo_crc16gen_check_bits(&gsm0503_sch_crc10, conv, 25, conv+25); + if (rv) + return -1; + + osmo_ubit2pbit_ext(sb_info, 0, conv, 0, 25, 1); + + return 0; +} + +int sch_encode(ubit_t *burst, uint8_t *sb_info) +{ + ubit_t conv[35]; + + osmo_pbit2ubit_ext(conv, 0, sb_info, 0, 25, 1); + + osmo_crc16gen_set_bits(&gsm0503_sch_crc10, conv, 25, conv+25); + + osmo_conv_encode(&gsm0503_conv_sch, conv, burst); + + return 0; +} + diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h new file mode 100644 index 00000000..240b2c07 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -0,0 +1,15 @@ +#ifndef _0503_CODING_H +#define _0503_CODING_H + +int xcch_decode(uint8_t *l2_data, sbit_t *bursts); +int xcch_encode(ubit_t *bursts, uint8_t *l2_data); +int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); +int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order); +int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order); +int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); +int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); +int sch_decode(uint8_t *sb_info, sbit_t *burst); +int sch_encode(ubit_t *burst, uint8_t *sb_info); + +#endif /* _0503_CODING_H */ diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c new file mode 100644 index 00000000..79667715 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -0,0 +1,83 @@ + +#include + +#include + +#include "gsm0503_conv.h" + +/* + * GSM convolutional coding + * + * G_0 = 1 + x^3 + x^4 + * G_1 = 1 + x + x^3 + x^4 + */ + +static const uint8_t conv_xcch_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_xcch_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + + +const struct osmo_conv_code gsm0503_conv_xcch = { + .N = 2, + .K = 5, + .len = 224, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + +const struct osmo_conv_code gsm0503_conv_cs2 = { + .N = 2, + .K = 5, + .len = 290, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + +const struct osmo_conv_code gsm0503_conv_cs3 = { + .N = 2, + .K = 5, + .len = 334, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + +const struct osmo_conv_code gsm0503_conv_rach = { + .N = 2, + .K = 5, + .len = 14, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + +const struct osmo_conv_code gsm0503_conv_sch = { + .N = 2, + .K = 5, + .len = 35, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + +const struct osmo_conv_code gsm0503_conv_tch_fr = { + .N = 2, + .K = 5, + .len = 185, + .next_output = conv_xcch_next_output, + .next_state = conv_xcch_next_state, +}; + + diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h new file mode 100644 index 00000000..a73d24ba --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_conv.h @@ -0,0 +1,11 @@ +#ifndef _0503_CONV_H +#define _0503_CONV_H + +extern const struct osmo_conv_code gsm0503_conv_xcch; +extern const struct osmo_conv_code gsm0503_conv_cs2; +extern const struct osmo_conv_code gsm0503_conv_cs3; +extern const struct osmo_conv_code gsm0503_conv_rach; +extern const struct osmo_conv_code gsm0503_conv_sch; +extern const struct osmo_conv_code gsm0503_conv_tch_fr; + +#endif /* _0503_CONV_H */ diff --git a/src/osmo-bts-trx/gsm0503_interleaving.c b/src/osmo-bts-trx/gsm0503_interleaving.c new file mode 100644 index 00000000..f8a63625 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_interleaving.c @@ -0,0 +1,97 @@ + +#include + +#include + +#include "gsm0503_interleaving.h" + +/* + * GSM xCCH interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 4 blocks of 114 bits: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 4) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + cB[k] = iB[B * 114 + j]; + } +} + +void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 3; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + iB[B * 114 + j] = cB[k]; + } +} + +/* + * GSM TCH FR/EFR interleaving and burst mapping + * + * Interleaving: + * + * Given 456 coded input bits, form 8 blocks of 114 bits, + * where event bits of the first 4 block and off bits of the last 4 block + * are used: + * + * i(B, j) = c(n, k) k = 0, ..., 455 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 4n + (k mod 8) + * j = 2(49k mod 57) + ((k mod 8) div 4) + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 7; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + cB[k] = iB[B * 114 + j]; + } +} + +void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<456; k++) { + B = k & 7; + j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); + iB[B * 114 + j] = cB[k]; + } +} + diff --git a/src/osmo-bts-trx/gsm0503_interleaving.h b/src/osmo-bts-trx/gsm0503_interleaving.h new file mode 100644 index 00000000..d4c2fdbb --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_interleaving.h @@ -0,0 +1,9 @@ +#ifndef _0503_INTERLEAVING_H +#define _0503_INTERLEAVING_H + +void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB); +void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB); +void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB); +void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB); + +#endif /* _0503_INTERLEAVING_H */ diff --git a/src/osmo-bts-trx/gsm0503_mapping.c b/src/osmo-bts-trx/gsm0503_mapping.c new file mode 100644 index 00000000..e5ee9206 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_mapping.c @@ -0,0 +1,65 @@ + +#include +#include + +#include + +#include "gsm0503_mapping.h" + +void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn) +{ + memcpy(iB, eB, 57); + memcpy(iB+57, eB+59, 57); + + if (hl) + *hl = eB[57]; + + if (hn) + *hn = eB[58]; +} + +void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl, + const ubit_t *hn) +{ + memcpy(eB, iB, 57); + memcpy(eB+59, iB+57, 57); + + if (hl) + eB[57] = *hl; + if (hn) + eB[58] = *hn; +} + +void gsm0503_tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) +{ + int i; + + /* brainfuck: only copy even or odd bits */ + for (i=odd; i<57; i+=2) + iB[i] = eB[i]; + for (i=58-odd; i<114; i+=2) + iB[i] = eB[i+2]; + + if (h && !odd) + *h = eB[58]; + + if (h && odd) + *h = eB[57]; +} + +void gsm0503_tch_fr_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd) +{ + int i; + + /* brainfuck: only copy even or odd bits */ + for (i=odd; i<57; i+=2) + eB[i] = iB[i]; + for (i=58-odd; i<114; i+=2) + eB[i+2] = iB[i]; + + if (h && !odd) + eB[58] = *h; + if (h && odd) + eB[57] = *h; +} + diff --git a/src/osmo-bts-trx/gsm0503_mapping.h b/src/osmo-bts-trx/gsm0503_mapping.h new file mode 100644 index 00000000..6123266e --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_mapping.h @@ -0,0 +1,10 @@ +#ifndef _0503_MAPPING_H +#define _0503_MAPPING_H + +void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn); +void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl, + const ubit_t *hn); +void gsm0503_tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd); +void gsm0503_tch_fr_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd); + +#endif /* _0503_INTERLEAVING_H */ diff --git a/src/osmo-bts-trx/gsm0503_parity.c b/src/osmo-bts-trx/gsm0503_parity.c new file mode 100644 index 00000000..1ecadcc7 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_parity.c @@ -0,0 +1,77 @@ + +#include + +#include + +#include "gsm0503_parity.h" + +/* + * GSM (SACCH) parity (FIRE code) + * + * g(x) = (x^23 + 1)(x^17 + x^3 + 1) + * = x^40 + x^26 + x^23 + x^17 + x^3 + a1 + */ + +const struct osmo_crc64gen_code gsm0503_fire_crc40 = { + .bits = 40, + .poly = 0x0004820009ULL, + .init = 0x0000000000ULL, + .remainder = 0xffffffffffULL, +}; + + +/* + * GSM PDTCH CS-2, CS-3, CS-4 parity + * + * g(x) = x^16 + x^12 + x^5 + 1 + */ + +const struct osmo_crc16gen_code gsm0503_cs234_crc16 = { + .bits = 16, + .poly = 0x1021, + .init = 0x0000, + .remainder = 0xffff, +}; + + +/* + * GSM RACH parity + * + * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 + */ + +const struct osmo_crc8gen_code gsm0503_rach_crc6 = { + .bits = 6, + .poly = 0x2f, + .init = 0x00, + .remainder = 0x3f, +}; + + +/* + * GSM SCH parity + * + * g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1 + */ + +const struct osmo_crc16gen_code gsm0503_sch_crc10 = { + .bits = 10, + .poly = 0x175, + .init = 0x000, + .remainder = 0x3ff, +}; + + +/* + * GSM TCH FR/EFR parity + * + * g(x) = x^3 + x + 1 + */ + +const struct osmo_crc8gen_code gsm0503_tch_fr_crc3 = { + .bits = 3, + .poly = 0x3, + .init = 0x0, + .remainder = 0x7, +}; + diff --git a/src/osmo-bts-trx/gsm0503_parity.h b/src/osmo-bts-trx/gsm0503_parity.h new file mode 100644 index 00000000..50d14bfc --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_parity.h @@ -0,0 +1,10 @@ +#ifndef _0503_PARITY_H +#define _0503_PARITY_H + +const struct osmo_crc64gen_code gsm0503_fire_crc40; +const struct osmo_crc16gen_code gsm0503_cs234_crc16; +const struct osmo_crc8gen_code gsm0503_rach_crc6; +const struct osmo_crc16gen_code gsm0503_sch_crc10; +const struct osmo_crc8gen_code gsm0503_tch_fr_crc3; + +#endif /* _0503_PARITY_H */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c new file mode 100644 index 00000000..c4a3dff2 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -0,0 +1,139 @@ + +#include + +#include + +#include "gsm0503_tables.h" + +const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8] = { + { 1,1, 1,1, 1,1, 1,1 }, + { 1,1, 0,0, 1,0, 0,0 }, + { 0,0, 1,0, 0,0, 0,1 }, + { 0,0, 0,1, 0,1, 1,0 }, +}; + +const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8] = { + { -127,-127, -127,-127, -127,-127, -127,-127 }, + { -127,-127, 127, 127, -127, 127, 127, 127 }, + { 127, 127, -127, 127, 127, 127, 127,-127 }, + { 127, 127, 127,-127, 127,-127, -127, 127 }, +}; + +const ubit_t gsm0503_usf2six[8][6] = { + { 0,0,0, 0,0,0 }, + { 1,0,0, 1,0,1 }, + { 0,1,0, 1,1,0 }, + { 1,1,0, 0,1,1 }, + { 0,0,1, 0,1,1 }, + { 1,0,1, 1,1,0 }, + { 0,1,1, 1,0,1 }, + { 1,1,1, 0,0,0 }, +}; + +const ubit_t gsm0503_usf2twelve_ubit[8][12] = { + { 0,0,0, 0,0,0, 0,0,0, 0,0,0 }, + { 1,1,0, 1,0,0, 0,0,1, 0,1,1 }, + { 0,0,1, 1,0,1, 1,1,0, 1,1,0 }, + { 1,1,1, 0,0,1, 1,1,1, 1,0,1 }, + { 0,0,0, 0,1,1, 0,1,1, 1,0,1 }, + { 1,1,0, 1,1,1, 0,1,0, 1,1,0 }, + { 0,0,1, 1,1,0, 1,0,1, 0,1,1 }, + { 1,1,1, 0,1,0, 1,0,0, 0,0,0 }, +}; + +const sbit_t gsm0503_usf2twelve_sbit[8][12] = { + { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }, + { -127,-127, 127, -127, 127, 127, 127, 127,-127, 127,-127,-127 }, + { 127, 127,-127, -127, 127,-127, -127,-127, 127, -127,-127, 127 }, + { -127,-127,-127, 127, 127,-127, -127,-127,-127, -127, 127,-127 }, + { 127, 127, 127, 127,-127,-127, 127,-127,-127, -127, 127,-127 }, + { -127,-127, 127, -127,-127,-127, 127,-127, 127, -127,-127, 127 }, + { 127, 127,-127, -127,-127, 127, -127, 127,-127, 127,-127,-127 }, + { -127,-127,-127, 127,-127, 127, -127, 127, 127, 127, 127, 127 }, +}; + +const uint8_t gsm0503_puncture_cs2[588] = { + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, + 0,0,0,1, 0,0,0,1, 0,0,0,1 +}; + +const uint8_t gsm0503_puncture_cs3[676] = { + 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, + 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0 +}; + +/* this corresponds to the bit-lengths of the individual codec + * parameters as indicated in Table 1.1 of TS 06.10 */ +const uint8_t gsm0503_gsm_fr_map[76] = { + 6, 6, 5, 5, 4, 4, 3, 3, + 7, 2, 2, 6, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 7, 2, 2, 6, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 7, 2, 2, 6, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 7, 2, 2, 6, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3 +}; + + +/* b(0..259) from d(0..259) according to (corrected) Table 2 of T 05.03 */ +const uint16_t gsm0503_d_to_b_index[260] = { + 5, 52,108,164,220, 4, 11, 16, 3, 10, 15, 21, 42, 98,154,210, + 51,107,163,219, 9, 25, 29, 41, 97,153,209, 40, 96,152,208, 39, + 95,151,207, 38, 94,150,206, 50,106,162,218, 2, 20, 32, 37, 93, + 149,205, 24, 28, 44,100,156,212, 36, 92,148,204, 46,102,158,214, + 1, 8, 14, 35, 19, 23, 31, 43, 99,155,211, 49,105,161,217, 55, + 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91,111,114,117,120, + 123,126,129,132,135,138,141,144,147,167,170,173,176,179,182,185, + 188,191,194,197,200,203,223,226,229,232,235,238,241,244,247,250, + 253,256,259, 45,101,157,213, 48,104,160,216, 54, 57, 60, 63, 66, + 69, 72, 75, 78, 81, 84, 87, 90,110,113,116,119,122,125,128,131, + 134,137,140,143,146,166,169,172,175,178,181,184,187,190,193,196, + 199,202,222,225,228,231,234,237,240,243,246,249,252,255,258, 0, + 7, 13, 27, 30, 34, 33, 12, 18, 17, 22, 47,103,159,215, 53, 56, + 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89,109,112,115,118,121, + 124,127,130,133,136,139,142,145,165,168,171,174,177,180,183,186, + 189,192,195,198,201,221,224,227,230,233,236,239,242,245,248,251, + 254,257, 6, 26, +}; + diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h new file mode 100644 index 00000000..abeff2d7 --- /dev/null +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -0,0 +1,14 @@ +#ifndef _0503_TABLES_H +#define _0503_TABLES_H + +extern const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8]; +extern const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8]; +extern const ubit_t gsm0503_usf2six[8][6]; +extern const ubit_t gsm0503_usf2twelve_ubit[8][12]; +extern const sbit_t gsm0503_usf2twelve_sbit[8][12]; +extern const uint8_t gsm0503_puncture_cs2[588]; +extern const uint8_t gsm0503_puncture_cs3[676]; +extern const uint8_t gsm0503_gsm_fr_map[76]; +extern const uint16_t gsm0503_d_to_b_index[260]; + +#endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/pxxch.c b/src/osmo-bts-trx/pxxch.c deleted file mode 100644 index 07d011a9..00000000 --- a/src/osmo-bts-trx/pxxch.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * pxxch.c - * - * Copyright (c) 2013 Andreas Eversberg - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "pxxch.h" - - -/* - * GSM PDTCH parity (FIRE code) - * - * g(x) = (x^23 + 1)(x^17 + x^3 + 1) - * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 - */ - -const struct osmo_crc64gen_code pxxch_crc40 = { - .bits = 40, - .poly = 0x0004820009ULL, - .init = 0x0000000000ULL, - .remainder = 0xffffffffffULL, -}; - - -/* - * GSM PDTCH CS-2, CS-3, CS-4 parity - * - * g(x) = x^16 + x^12 + x^5 + 1 - */ - -const struct osmo_crc16gen_code pdtch_crc16 = { - .bits = 16, - .poly = 0x1021, - .init = 0x0000, - .remainder = 0xffff, -}; - - -/* - * GSM PDTCH convolutional coding - * - * G_0 = 1 + x^3 + x^4 - * G_1 = 1 + x + x^3 + x^4 - */ - -static const uint8_t conv_cs1_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_cs1_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -static const struct osmo_conv_code conv_cs1 = { - .N = 2, - .K = 5, - .len = 224, - .next_output = conv_cs1_next_output, - .next_state = conv_cs1_next_state, -}; - -static const uint8_t conv_cs2_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_cs2_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -static const struct osmo_conv_code conv_cs2 = { - .N = 2, - .K = 5, - .len = 290, - .next_output = conv_cs2_next_output, - .next_state = conv_cs2_next_state, -}; - -static const uint8_t conv_cs3_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_cs3_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -static const struct osmo_conv_code conv_cs3 = { - .N = 2, - .K = 5, - .len = 334, - .next_output = conv_cs3_next_output, - .next_state = conv_cs3_next_state, -}; - - -/* - * GSM PxxCH interleaving and burst mapping - * - * Interleaving: - * - * Given 456 coded input bits, form 4 blocks of 114 bits: - * - * i(B, j) = c(n, k) k = 0, ..., 455 - * n = 0, ..., N, N + 1, ... - * B = B_0 + 4n + (k mod 4) - * j = 2(49k mod 57) + ((k mod 8) div 4) - * - * Mapping on Burst: - * - * e(B, j) = i(B, j) - * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 - * e(B, 57) = h_l(B) - * e(B, 58) = h_n(B) - * - * Where hl(B) and hn(B) are bits in burst B indicating flags. - */ - -static void -pxxch_deinterleave(sbit_t *cB, sbit_t *iB) -{ - int j, k, B; - - for (k=0; k<456; k++) { - B = k & 3; - j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); - cB[k] = iB[B * 114 + j]; - } -} - -static void -pxxch_interleave(ubit_t *cB, ubit_t *iB) -{ - int j, k, B; - - for (k=0; k<456; k++) { - B = k & 3; - j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); - iB[B * 114 + j] = cB[k]; - } -} - -static void -pxxch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn) -{ - memcpy(iB, eB, 57); - memcpy(iB+57, eB+59, 57); - - if (hl) - *hl = eB[57]; - - if (hn) - *hn = eB[58]; -} - -static void -pxxch_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *hl, ubit_t *hn) -{ - memcpy(eB, iB, 57); - memcpy(eB+59, iB+57, 57); - - if (hl) - eB[57] = *hl; - if (hn) - eB[58] = *hn; -} - -static ubit_t pdtch_hl_hn_ubit[4][8] = { - { 1,1, 1,1, 1,1, 1,1 }, - { 1,1, 0,0, 1,0, 0,0 }, - { 0,0, 1,0, 0,0, 0,1 }, - { 0,0, 0,1, 0,1, 1,0 }, -}; - -static sbit_t pdtch_hl_hn_sbit[4][8] = { - { -127,-127, -127,-127, -127,-127, -127,-127 }, - { -127,-127, 127, 127, -127, 127, 127, 127 }, - { 127, 127, -127, 127, 127, 127, 127,-127 }, - { 127, 127, 127,-127, 127,-127, -127, 127 }, -}; - -static ubit_t usf2six[8][6] = { - { 0,0,0, 0,0,0 }, - { 1,0,0, 1,0,1 }, - { 0,1,0, 1,1,0 }, - { 1,1,0, 0,1,1 }, - { 0,0,1, 0,1,1 }, - { 1,0,1, 1,1,0 }, - { 0,1,1, 1,0,1 }, - { 1,1,1, 0,0,0 }, -}; - -static ubit_t usf2twelve_ubit[8][12] = { - { 0,0,0, 0,0,0, 0,0,0, 0,0,0 }, - { 1,1,0, 1,0,0, 0,0,1, 0,1,1 }, - { 0,0,1, 1,0,1, 1,1,0, 1,1,0 }, - { 1,1,1, 0,0,1, 1,1,1, 1,0,1 }, - { 0,0,0, 0,1,1, 0,1,1, 1,0,1 }, - { 1,1,0, 1,1,1, 0,1,0, 1,1,0 }, - { 0,0,1, 1,1,0, 1,0,1, 0,1,1 }, - { 1,1,1, 0,1,0, 1,0,0, 0,0,0 }, -}; - -static sbit_t usf2twelve_sbit[8][12] = { - { 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 }, - { -127,-127, 127, -127, 127, 127, 127, 127,-127, 127,-127,-127 }, - { 127, 127,-127, -127, 127,-127, -127,-127, 127, -127,-127, 127 }, - { -127,-127,-127, 127, 127,-127, -127,-127,-127, -127, 127,-127 }, - { 127, 127, 127, 127,-127,-127, 127,-127,-127, -127, 127,-127 }, - { -127,-127, 127, -127,-127,-127, 127,-127, 127, -127,-127, 127 }, - { 127, 127,-127, -127,-127, 127, -127, 127,-127, 127,-127,-127 }, - { -127,-127,-127, 127,-127, 127, -127, 127, 127, 127, 127, 127 }, -}; - -static uint8_t puncture_cs2[588] = { - 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, - 0,0,0,1, 0,0,0,1, 0,0,0,1 -}; - -static uint8_t puncture_cs3[676] = { - 0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, - 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0 -}; - -int -pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) -{ - sbit_t iB[456], cB[676], hl_hn[8]; - ubit_t conv[456]; - int i, j, k, rv, best = 0, cs = 0, usf = 0; /* make GCC happy */ - - for (i=0; i<4; i++) - pxxch_burst_unmap(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, - hl_hn + i*2 + 1); - - for (i=0; i<4; i++) { - for (j=0, k=0; j<8; j++) - k += abs(((int)pdtch_hl_hn_sbit[i][j]) - ((int)hl_hn[j])); - if (i == 0 || k < best) { - best = k; - cs = i+1; - } - } - - pxxch_deinterleave(cB, iB); - - switch (cs) { - case 1: - osmo_conv_decode(&conv_cs1, cB, conv); - - rv = osmo_crc64gen_check_bits(&pxxch_crc40, conv, 184, - conv+184); - if (rv) - return -1; - - osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); - - return 23; - case 2: - for (i=587, j=455; i>=0; i--) - if (!puncture_cs2[i]) - cB[i] = cB[j--]; - else - cB[i] = 0; - - osmo_conv_decode(&conv_cs2, cB, conv); - - for (i=0; i<8; i++) { - for (j=0, k=0; j<6; j++) - k += abs(((int)usf2six[i][j]) - ((int)conv[j])); - if (i == 0 || k < best) { - best = k; - usf = i; - } - } - - conv[3] = usf & 1; - conv[4] = (usf >> 1) & 1; - conv[5] = (usf >> 2) & 1; - if (usf_p) - *usf_p = usf; - - rv = osmo_crc16gen_check_bits(&pdtch_crc16, conv+3, 271, - conv+3+271); - if (rv) - return -1; - - osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 271, 1); - - return 34; - case 3: - for (i=675, j=455; i>=0; i--) - if (!puncture_cs3[i]) - cB[i] = cB[j--]; - else - cB[i] = 0; - - osmo_conv_decode(&conv_cs3, cB, conv); - - for (i=0; i<8; i++) { - for (j=0, k=0; j<6; j++) - k += abs(((int)usf2six[i][j]) - ((int)conv[j])); - if (i == 0 || k < best) { - best = k; - usf = i; - } - } - - conv[3] = usf & 1; - conv[4] = (usf >> 1) & 1; - conv[5] = (usf >> 2) & 1; - if (usf_p) - *usf_p = usf; - - rv = osmo_crc16gen_check_bits(&pdtch_crc16, conv+3, 315, - conv+3+315); - if (rv) - return -1; - - osmo_ubit2pbit_ext(l2_data, 0, conv, 3, 315, 1); - - return 40; - case 4: - for (i=12; i<456;i++) - conv[i] = (cB[i] < 0) ? 1:0; - - for (i=0; i<8; i++) { - for (j=0, k=0; j<12; j++) - k += abs(((int)usf2twelve_sbit[i][j]) - - ((int)cB[j])); - if (i == 0 || k < best) { - best = k; - usf = i; - } - } - - conv[9] = usf & 1; - conv[10] = (usf >> 1) & 1; - conv[11] = (usf >> 2) & 1; - if (usf_p) - *usf_p = usf; - - rv = osmo_crc16gen_check_bits(&pdtch_crc16, conv+9, 431, - conv+9+431); - if (rv) - return -1; - - osmo_ubit2pbit_ext(l2_data, 0, conv, 9, 431, 1); - - return 54; - } - - return -1; -} - -int -pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) -{ - ubit_t iB[456], cB[676], *hl_hn; - ubit_t conv[334]; - int i, j, usf; - - switch (l2_len) { - case 23: - osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); - - osmo_crc64gen_set_bits(&pxxch_crc40, conv, 184, conv+184); - - osmo_conv_encode(&conv_cs1, conv, cB); - - hl_hn = pdtch_hl_hn_ubit[0]; - - break; - case 34: - osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 271, 1); - usf = l2_data[0] & 0x7; - - osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 271, conv+3+271); - - memcpy(conv, usf2six[usf], 6); - - osmo_conv_encode(&conv_cs2, conv, cB); - - for (i=0, j=0; i<588; i++) - if (!puncture_cs2[i]) - cB[j++] = cB[i]; - - hl_hn = pdtch_hl_hn_ubit[1]; - - break; - case 40: - osmo_pbit2ubit_ext(conv, 3, l2_data, 0, 315, 1); - usf = l2_data[0] & 0x7; - - osmo_crc16gen_set_bits(&pdtch_crc16, conv+3, 315, conv+3+315); - - memcpy(conv, usf2six[usf], 6); - - osmo_conv_encode(&conv_cs3, conv, cB); - - for (i=0, j=0; i<676; i++) - if (!puncture_cs3[i]) - cB[j++] = cB[i]; - - hl_hn = pdtch_hl_hn_ubit[2]; - - break; - case 54: - osmo_pbit2ubit_ext(cB, 9, l2_data, 0, 431, 1); - usf = l2_data[0] & 0x7; - - osmo_crc16gen_set_bits(&pdtch_crc16, cB+9, 431, cB+9+431); - - memcpy(cB, usf2twelve_ubit[usf], 12); - - hl_hn = pdtch_hl_hn_ubit[3]; - - break; - default: - return -1; - } - - pxxch_interleave(cB, iB); - - for (i=0; i<4; i++) - pxxch_burst_map(&iB[i * 114], &bursts[i * 116], hl_hn + i*2, - hl_hn + i*2 + 1); - - return 0; -} diff --git a/src/osmo-bts-trx/pxxch.h b/src/osmo-bts-trx/pxxch.h deleted file mode 100644 index 990bd36d..00000000 --- a/src/osmo-bts-trx/pxxch.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _PXXCH_H -#define _PXXCH_H - -int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); -int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); - -#endif /* _PXXCH_H */ diff --git a/src/osmo-bts-trx/rach.c b/src/osmo-bts-trx/rach.c deleted file mode 100644 index 670a4d1d..00000000 --- a/src/osmo-bts-trx/rach.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * conv_rach.c - * - * Convolutional code tables for RACH channels - * - * Copyright (C) 2011 Sylvain Munaut - */ - - -#include -#include -#include - -#include -#include -#include - -#include "rach.h" - - -/* - * GSM RACH parity - * - * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 - */ - -static const struct osmo_crc8gen_code rach_crc6 = { - .bits = 6, - .poly = 0x2f, - .init = 0x00, - .remainder = 0x3f, -}; - - -/* - * GSM RACH convolutional coding - * - * G_0 = 1 + x^3 + x^4 - * G_1 = 1 + x + x^3 + x^4 - */ - -static const uint8_t conv_rach_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_rach_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -const struct osmo_conv_code conv_rach = { - .N = 2, - .K = 5, - .len = 14, - .next_output = conv_rach_next_output, - .next_state = conv_rach_next_state, -}; - - -/* - * GSM RACH apply BSIC to parity - * - * p(j) = p(j) xor b(j) j = 0, ..., 5 - * b(0) = MSB of PLMN colour code - * b(5) = LSB of BS colour code - */ - -static int -rach_apply_bsic(ubit_t *d, uint8_t bsic) -{ - int i; - - /* Apply it */ - for (i=0; i<6; i++) - d[8+i] ^= ((bsic >> (5-i)) & 1); - - return 0; -} - -int -rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic) -{ - ubit_t conv[14]; - int rv; - - osmo_conv_decode(&conv_rach, burst, conv); - - rach_apply_bsic(conv, bsic); - - rv = osmo_crc8gen_check_bits(&rach_crc6, conv, 8, conv+8); - if (rv) - return -1; - - osmo_ubit2pbit_ext(ra, 0, conv, 0, 8, 1); - - return 0; -} - -int -rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic) -{ - ubit_t conv[14]; - - osmo_pbit2ubit_ext(conv, 0, ra, 0, 8, 1); - - osmo_crc8gen_set_bits(&rach_crc6, conv, 8, conv+8); - - rach_apply_bsic(conv, bsic); - - osmo_conv_encode(&conv_rach, conv, burst); - - return 0; -} diff --git a/src/osmo-bts-trx/rach.h b/src/osmo-bts-trx/rach.h deleted file mode 100644 index 15555593..00000000 --- a/src/osmo-bts-trx/rach.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _RACH_H -#define _RACH_H - -int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); -int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); - -#endif /* _RACH_H */ diff --git a/src/osmo-bts-trx/sch.c b/src/osmo-bts-trx/sch.c deleted file mode 100644 index e0d6e8db..00000000 --- a/src/osmo-bts-trx/sch.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - */ - - -#include -#include -#include - -#include -#include -#include - -#include "sch.h" - - -/* - * GSM SCH parity - * - * g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1 - */ - -const struct osmo_crc16gen_code sch_crc10 = { - .bits = 10, - .poly = 0x175, - .init = 0x000, - .remainder = 0x3ff, -}; - - -/* - * GSM SCH convolutional coding - * - * G_0 = 1 + x^3 + x^4 - * G_1 = 1 + x + x^3 + x^4 - */ - -static const uint8_t conv_rach_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_rach_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -static const struct osmo_conv_code conv_sch = { - .N = 2, - .K = 5, - .len = 35, - .next_output = conv_rach_next_output, - .next_state = conv_rach_next_state, -}; - - -int -sch_decode(uint8_t *sb_info, sbit_t *burst) -{ - ubit_t conv[35]; - int rv; - - osmo_conv_decode(&conv_sch, burst, conv); - - rv = osmo_crc16gen_check_bits(&sch_crc10, conv, 25, conv+25); - if (rv) - return -1; - - osmo_ubit2pbit_ext(sb_info, 0, conv, 0, 25, 1); - - return 0; -} - -int -sch_encode(ubit_t *burst, uint8_t *sb_info) -{ - ubit_t conv[35]; - - osmo_pbit2ubit_ext(conv, 0, sb_info, 0, 25, 1); - - osmo_crc16gen_set_bits(&sch_crc10, conv, 25, conv+25); - - osmo_conv_encode(&conv_sch, conv, burst); - - return 0; -} diff --git a/src/osmo-bts-trx/sch.h b/src/osmo-bts-trx/sch.h deleted file mode 100644 index 8e60191b..00000000 --- a/src/osmo-bts-trx/sch.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _SCH_H -#define _SCH_H - -int sch_decode(uint8_t *sb_info, sbit_t *burst); -int sch_encode(ubit_t *burst, uint8_t *sb_info); - -#endif /* _SCH_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index afe4aba3..6f5dddb0 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -35,11 +35,7 @@ #include "l1_if.h" #include "scheduler.h" -#include "xcch.h" -#include "tch_fr.h" -#include "rach.h" -#include "sch.h" -#include "pxxch.h" +#include "gsm0503_coding.h" #include "trx_if.h" /* Enable this to multiply TOA of RACH by 10. diff --git a/src/osmo-bts-trx/tch_fr.c b/src/osmo-bts-trx/tch_fr.c deleted file mode 100644 index 23130e94..00000000 --- a/src/osmo-bts-trx/tch_fr.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * tch_fr.c - * - * Copyright (c) 2013 Andreas Eversberg - */ - -#include -#include -#include - -#include -#include -#include - -#include "xcch.h" -#include "tch_fr.h" - - -/* - * GSM TCH FR/EFR parity - * - * g(x) = x^3 + x + 1 - */ - -const struct osmo_crc8gen_code tch_fr_crc3 = { - .bits = 3, - .poly = 0x3, - .init = 0x0, - .remainder = 0x7, -}; - - -/* - * GSM TCH FR/EFR convolutional coding - * - * G_0 = 1 + x^3 + x^4 - * G_1 = 1 + x + x^3 + x^4 - */ - -static const uint8_t conv_tch_fr_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_tch_fr_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -static const struct osmo_conv_code conv_tch_fr = { - .N = 2, - .K = 5, - .len = 185, - .next_output = conv_tch_fr_next_output, - .next_state = conv_tch_fr_next_state, -}; - - -/* - * GSM TCH FR/EFR interleaving and burst mapping - * - * Interleaving: - * - * Given 456 coded input bits, form 8 blocks of 114 bits, - * where event bits of the first 4 block and off bits of the last 4 block - * are used: - * - * i(B, j) = c(n, k) k = 0, ..., 455 - * n = 0, ..., N, N + 1, ... - * B = B_0 + 4n + (k mod 8) - * j = 2(49k mod 57) + ((k mod 8) div 4) - * - * Mapping on Burst: - * - * e(B, j) = i(B, j) - * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 - * e(B, 57) = h_l(B) - * e(B, 58) = h_n(B) - * - * Where hl(B) and hn(B) are bits in burst B indicating flags. - */ - -static void -tch_fr_deinterleave(sbit_t *cB, sbit_t *iB) -{ - int j, k, B; - - for (k=0; k<456; k++) { - B = k & 7; - j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); - cB[k] = iB[B * 114 + j]; - } -} - -static void -tch_fr_interleave(ubit_t *cB, ubit_t *iB) -{ - int j, k, B; - - for (k=0; k<456; k++) { - B = k & 7; - j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); - iB[B * 114 + j] = cB[k]; - } -} - -static void -tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) -{ - int i; - - /* brainfuck: only copy even or odd bits */ - for (i=odd; i<57; i+=2) - iB[i] = eB[i]; - for (i=58-odd; i<114; i+=2) - iB[i] = eB[i+2]; - - if (h && !odd) - *h = eB[58]; - - if (h && odd) - *h = eB[57]; -} - -static void -tch_fr_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *h, int odd) -{ - int i; - - /* brainfuck: only copy even or odd bits */ - for (i=odd; i<57; i+=2) - eB[i] = iB[i]; - for (i=58-odd; i<114; i+=2) - eB[i+2] = iB[i]; - - if (h && !odd) - eB[58] = *h; - if (h && odd) - eB[57] = *h; -} - -/* this corresponds to the bit-lengths of the individual codec - * parameters as indicated in Table 1.1 of TS 06.10 */ -static const uint8_t gsm_fr_map[] = { - 6, 6, 5, 5, 4, 4, 3, 3, - 7, 2, 2, 6, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 7, 2, 2, 6, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 7, 2, 2, 6, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 7, 2, 2, 6, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3 -}; - -static void -tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits) -{ - int i, j, k, l, o; - - tch_data[0] = 0xd << 4; - memset(tch_data + 1, 0, 32); - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts output bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset input bits */ - while (i < 260) { - tch_data[j>>3] |= (b_bits[k+o] << (7-(j&7))); - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - - /* rearrange according to Table 2 of TS 05.03 */ -} - -static void -tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data) -{ - int i, j, k, l, o; - - i = 0; /* counts bits */ - j = 4; /* counts input bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset output bits */ - while (i < 260) { - b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - -} - - -/* b(0..259) from d(0..259) according to (corrected) Table 2 of T 05.03 */ -static uint16_t d_to_b_index[260] = { - 5, 52,108,164,220, 4, 11, 16, 3, 10, 15, 21, 42, 98,154,210, - 51,107,163,219, 9, 25, 29, 41, 97,153,209, 40, 96,152,208, 39, - 95,151,207, 38, 94,150,206, 50,106,162,218, 2, 20, 32, 37, 93, - 149,205, 24, 28, 44,100,156,212, 36, 92,148,204, 46,102,158,214, - 1, 8, 14, 35, 19, 23, 31, 43, 99,155,211, 49,105,161,217, 55, - 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91,111,114,117,120, - 123,126,129,132,135,138,141,144,147,167,170,173,176,179,182,185, - 188,191,194,197,200,203,223,226,229,232,235,238,241,244,247,250, - 253,256,259, 45,101,157,213, 48,104,160,216, 54, 57, 60, 63, 66, - 69, 72, 75, 78, 81, 84, 87, 90,110,113,116,119,122,125,128,131, - 134,137,140,143,146,166,169,172,175,178,181,184,187,190,193,196, - 199,202,222,225,228,231,234,237,240,243,246,249,252,255,258, 0, - 7, 13, 27, 30, 34, 33, 12, 18, 17, 22, 47,103,159,215, 53, 56, - 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89,109,112,115,118,121, - 124,127,130,133,136,139,142,145,165,168,171,174,177,180,183,186, - 189,192,195,198,201,221,224,227,230,233,236,239,242,245,248,251, - 254,257, 6, 26, -}; - - -static void -tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) -{ - int i; - - for (i = 0; i < 260; i++) - b_bits[d_to_b_index[i]] = d_bits[i]; -} - -static void -tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) -{ - int i; - - for (i = 0; i < 260; i++) - d_bits[i] = b_bits[d_to_b_index[i]]; -} - -static void -tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) -{ - int i; - - for (i=0; i<91; i++) { - d[i<<1] = u[i]; - d[(i<<1)+1] = u[184-i]; - } - for (i=0; i<3; i++) - p[i] = u[91+i]; -} - -static void -tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) -{ - int i; - - for (i=0; i<91; i++) { - u[i] = d[i<<1]; - u[184-i] = d[(i<<1)+1]; - } - for (i=0; i<3; i++) - u[91+i] = p[i]; -} - -int -tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int network_order) -{ - sbit_t iB[912], cB[456], h; - ubit_t conv[185], b[260], d[260], p[3]; - int i, rv, len, steal = 0; - - for (i=0; i<8; i++) { - tch_fr_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i>>2); - if (h < 0) - steal++; - } - - tch_fr_deinterleave(cB, iB); - - if (steal < 4) { - osmo_conv_decode(&conv_tch_fr, cB, conv); - - tch_fr_unreorder(d, p, conv); - - for (i=0; i<78; i++) - d[i+182] = (cB[i+378] < 0) ? 1:0; - - rv = osmo_crc8gen_check_bits(&tch_fr_crc3, d, 50, p); - if (rv) - return -1; - - if (network_order) { - tch_fr_d_to_b(b, d); - - tch_fr_reassemble(tch_data, b); - } else - tch_fr_d_to_b(tch_data, d); - - len = 33; - } else { - rv = xcch_decode_cB(tch_data, cB); - if (rv) - return -1; - - len = 23; - } - - return len; -} - -int -tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int network_order) -{ - ubit_t iB[912], cB[456], h; - ubit_t conv[185], b[260], d[260], p[3]; - int i; - - switch (len) { - case 33: /* TCH FR */ - if (network_order) { - tch_fr_disassemble(b, tch_data); - - tch_fr_b_to_d(d, b); - } else - tch_fr_b_to_d(d, tch_data); - - osmo_crc8gen_set_bits(&tch_fr_crc3, d, 50, p); - - tch_fr_reorder(conv, d, p); - - memcpy(cB+378, d+182, 78); - - osmo_conv_encode(&conv_tch_fr, conv, cB); - - h = 0; - - break; - case 23: /* FACCH */ - xcch_encode_cB(cB, tch_data); - - h = 1; - - break; - default: - return -1; - } - - tch_fr_interleave(cB, iB); - - for (i=0; i<8; i++) - tch_fr_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>2); - - return 0; -} diff --git a/src/osmo-bts-trx/tch_fr.h b/src/osmo-bts-trx/tch_fr.h deleted file mode 100644 index 01414572..00000000 --- a/src/osmo-bts-trx/tch_fr.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _TCH_FR_H -#define _TCH_FR_H - -int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int network_order); -int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, - int network_order); - -#endif /* _TCH_FR_H */ diff --git a/src/osmo-bts-trx/xcch.c b/src/osmo-bts-trx/xcch.c deleted file mode 100644 index 024bf071..00000000 --- a/src/osmo-bts-trx/xcch.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * xcch.c - * - * Copyright (c) 2011 Sylvain Munaut - */ - -#include -#include -#include - -#include -#include -#include - -#include "xcch.h" - - -/* - * GSM xCCH parity (FIRE code) - * - * g(x) = (x^23 + 1)(x^17 + x^3 + 1) - * = x^40 + x^26 + x^23 + x^17 + x^3 + 1 - */ - -const struct osmo_crc64gen_code xcch_crc40 = { - .bits = 40, - .poly = 0x0004820009ULL, - .init = 0x0000000000ULL, - .remainder = 0xffffffffffULL, -}; - - -/* - * GSM xCCH convolutional coding - * - * G_0 = 1 + x^3 + x^4 - * G_1 = 1 + x + x^3 + x^4 - */ - -static const uint8_t conv_xcch_next_output[][2] = { - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 }, - { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, -}; - -static const uint8_t conv_xcch_next_state[][2] = { - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, - { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, - { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, -}; - -static const struct osmo_conv_code conv_xcch = { - .N = 2, - .K = 5, - .len = 224, - .next_output = conv_xcch_next_output, - .next_state = conv_xcch_next_state, -}; - - -/* - * GSM xCCH interleaving and burst mapping - * - * Interleaving: - * - * Given 456 coded input bits, form 4 blocks of 114 bits: - * - * i(B, j) = c(n, k) k = 0, ..., 455 - * n = 0, ..., N, N + 1, ... - * B = B_0 + 4n + (k mod 4) - * j = 2(49k mod 57) + ((k mod 8) div 4) - * - * Mapping on Burst: - * - * e(B, j) = i(B, j) - * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 - * e(B, 57) = h_l(B) - * e(B, 58) = h_n(B) - * - * Where hl(B) and hn(B) are bits in burst B indicating flags. - */ - -static void -xcch_deinterleave(sbit_t *cB, sbit_t *iB) -{ - int j, k, B; - - for (k=0; k<456; k++) { - B = k & 3; - j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); - cB[k] = iB[B * 114 + j]; - } -} - -static void -xcch_interleave(ubit_t *cB, ubit_t *iB) -{ - int j, k, B; - - for (k=0; k<456; k++) { - B = k & 3; - j = 2 * ((49 * k) % 57) + ((k & 7) >> 2); - iB[B * 114 + j] = cB[k]; - } -} - -static void -xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn) -{ - memcpy(iB, eB, 57); - memcpy(iB+57, eB+59, 57); - - if (hl) - *hl = eB[57]; - - if (hn) - *hn = eB[58]; -} - -static void -xcch_burst_map(ubit_t *iB, ubit_t *eB, ubit_t *hl, ubit_t *hn) -{ - memcpy(eB, iB, 57); - memcpy(eB+59, iB+57, 57); - - if (hl) - eB[57] = *hl; - if (hn) - eB[58] = *hn; -} - -int -xcch_decode_cB(uint8_t *l2_data, sbit_t *cB) -{ - ubit_t conv[224]; - int rv; - - osmo_conv_decode(&conv_xcch, cB, conv); - - rv = osmo_crc64gen_check_bits(&xcch_crc40, conv, 184, conv+184); - if (rv) - return -1; - - osmo_ubit2pbit_ext(l2_data, 0, conv, 0, 184, 1); - - return 0; -} - -int -xcch_decode(uint8_t *l2_data, sbit_t *bursts) -{ - sbit_t iB[456], cB[456]; - int i; - - for (i=0; i<4; i++) - xcch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, NULL); - - xcch_deinterleave(cB, iB); - - return xcch_decode_cB(l2_data, cB); -} - -int -xcch_encode_cB(ubit_t *cB, uint8_t *l2_data) -{ - ubit_t conv[224]; - - osmo_pbit2ubit_ext(conv, 0, l2_data, 0, 184, 1); - - osmo_crc64gen_set_bits(&xcch_crc40, conv, 184, conv+184); - - osmo_conv_encode(&conv_xcch, conv, cB); - - return 0; -} - -int -xcch_encode(ubit_t *bursts, uint8_t *l2_data) -{ - ubit_t iB[456], cB[456], hl = 1, hn = 1; - int i; - - xcch_encode_cB(cB, l2_data); - - xcch_interleave(cB, iB); - - for (i=0; i<4; i++) - xcch_burst_map(&iB[i * 114], &bursts[i * 116], &hl, &hn); - - return 0; -} diff --git a/src/osmo-bts-trx/xcch.h b/src/osmo-bts-trx/xcch.h deleted file mode 100644 index 8e7d3929..00000000 --- a/src/osmo-bts-trx/xcch.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _XCCH_H -#define _XCCH_H - -int xcch_decode_cB(uint8_t *l2_data, sbit_t *cB); -int xcch_decode(uint8_t *l2_data, sbit_t *bursts); -int xcch_encode_cB(ubit_t *cB, uint8_t *l2_data); -int xcch_encode(ubit_t *bursts, uint8_t *l2_data); - -#endif /* _XCCH_H */ - diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am index 09275188..c3c04067 100644 --- a/tests/bursts/Makefile.am +++ b/tests/bursts/Makefile.am @@ -5,9 +5,10 @@ noinst_PROGRAMS = bursts_test EXTRA_DIST = bursts_test.ok bursts_test_SOURCES = bursts_test.c \ - $(top_builddir)/src/osmo-bts-trx/xcch.c \ - $(top_builddir)/src/osmo-bts-trx/rach.c \ - $(top_builddir)/src/osmo-bts-trx/sch.c \ - $(top_builddir)/src/osmo-bts-trx/tch_fr.c \ - $(top_builddir)/src/osmo-bts-trx/pxxch.c + $(top_builddir)/src/osmo-bts-trx/gsm0503_coding.c \ + $(top_builddir)/src/osmo-bts-trx/gsm0503_conv.c \ + $(top_builddir)/src/osmo-bts-trx/gsm0503_interleaving.c \ + $(top_builddir)/src/osmo-bts-trx/gsm0503_mapping.c \ + $(top_builddir)/src/osmo-bts-trx/gsm0503_tables.c \ + $(top_builddir)/src/osmo-bts-trx/gsm0503_parity.c bursts_test_LDADD = $(LDADD) diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index ad1f6859..e04899cc 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -26,11 +26,7 @@ #include #include -#include "../../src/osmo-bts-trx/xcch.h" -#include "../../src/osmo-bts-trx/rach.h" -#include "../../src/osmo-bts-trx/sch.h" -#include "../../src/osmo-bts-trx/tch_fr.h" -#include "../../src/osmo-bts-trx/pxxch.h" +#include "../../src/osmo-bts-trx/gsm0503_coding.h" #define ASSERT_TRUE(rc) \ -- cgit v1.2.3 From d692b6e054b25474615401dcaf33209c03923e9d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 16 Feb 2013 14:28:25 +0100 Subject: TRX: Replaced GSM 06.10 ordering table by table in libosmocodec --- configure.ac | 1 + src/osmo-bts-trx/Makefile.am | 4 ++-- src/osmo-bts-trx/gsm0503_coding.c | 46 +++++++++++++++++++++++++++------------ src/osmo-bts-trx/gsm0503_tables.c | 22 ------------------- src/osmo-bts-trx/gsm0503_tables.h | 1 - tests/bursts/Makefile.am | 4 ++-- 6 files changed, 37 insertions(+), 41 deletions(-) diff --git a/configure.ac b/configure.ac index d3fcff28..60421b82 100644 --- a/configure.ac +++ b/configure.ac @@ -28,6 +28,7 @@ PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis) PKG_CHECK_MODULES(LIBGPS, libgps) +PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec) AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support]) AC_ARG_ENABLE(sysmocom-bts, diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index d5021c92..28221d15 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -1,6 +1,6 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) -AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) -LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp +AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) +LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 2d3f7705..c7fe28db 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "gsm0503_conv.h" #include "gsm0503_parity.h" @@ -302,12 +303,24 @@ int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) * GSM TCH/F FR transcoding */ -static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits, int net_order) { int i, j, k, l, o; tch_data[0] = 0xd << 4; memset(tch_data + 1, 0, 32); + + if (net_order) { + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + while (i < 260) { + tch_data[j>>3] |= (b_bits[i] << (7-(j&7))); + i++; + j++; + } + return; + } + /* reassemble d-bits */ i = 0; /* counts bits */ j = 4; /* counts output bits */ @@ -327,10 +340,21 @@ static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits) /* rearrange according to Table 2 of TS 05.03 */ } -static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) { int i, j, k, l, o; + if (net_order) { + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + while (i < 260) { + b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } + return; + } + i = 0; /* counts bits */ j = 4; /* counts input bits */ k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ @@ -353,7 +377,7 @@ static void tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) int i; for (i = 0; i < 260; i++) - b_bits[gsm0503_d_to_b_index[i]] = d_bits[i]; + b_bits[gsm610_bitorder[i]] = d_bits[i]; } static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) @@ -361,7 +385,7 @@ static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) int i; for (i = 0; i < 260; i++) - d_bits[i] = b_bits[gsm0503_d_to_b_index[i]]; + d_bits[i] = b_bits[gsm610_bitorder[i]]; } static void tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) @@ -414,12 +438,9 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) if (rv) return -1; - if (net_order) { - tch_fr_d_to_b(b, d); + tch_fr_d_to_b(b, d); - tch_fr_reassemble(tch_data, b); - } else - tch_fr_d_to_b(tch_data, d); + tch_fr_reassemble(tch_data, b, net_order); len = 33; } else { @@ -441,12 +462,9 @@ int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order) switch (len) { case 33: /* TCH FR */ - if (net_order) { - tch_fr_disassemble(b, tch_data); + tch_fr_disassemble(b, tch_data, net_order); - tch_fr_b_to_d(d, b); - } else - tch_fr_b_to_d(d, tch_data); + tch_fr_b_to_d(d, b); osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p); diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index c4a3dff2..0e3e0d05 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -115,25 +115,3 @@ const uint8_t gsm0503_gsm_fr_map[76] = { 3, 3, 3, 3 }; - -/* b(0..259) from d(0..259) according to (corrected) Table 2 of T 05.03 */ -const uint16_t gsm0503_d_to_b_index[260] = { - 5, 52,108,164,220, 4, 11, 16, 3, 10, 15, 21, 42, 98,154,210, - 51,107,163,219, 9, 25, 29, 41, 97,153,209, 40, 96,152,208, 39, - 95,151,207, 38, 94,150,206, 50,106,162,218, 2, 20, 32, 37, 93, - 149,205, 24, 28, 44,100,156,212, 36, 92,148,204, 46,102,158,214, - 1, 8, 14, 35, 19, 23, 31, 43, 99,155,211, 49,105,161,217, 55, - 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91,111,114,117,120, - 123,126,129,132,135,138,141,144,147,167,170,173,176,179,182,185, - 188,191,194,197,200,203,223,226,229,232,235,238,241,244,247,250, - 253,256,259, 45,101,157,213, 48,104,160,216, 54, 57, 60, 63, 66, - 69, 72, 75, 78, 81, 84, 87, 90,110,113,116,119,122,125,128,131, - 134,137,140,143,146,166,169,172,175,178,181,184,187,190,193,196, - 199,202,222,225,228,231,234,237,240,243,246,249,252,255,258, 0, - 7, 13, 27, 30, 34, 33, 12, 18, 17, 22, 47,103,159,215, 53, 56, - 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89,109,112,115,118,121, - 124,127,130,133,136,139,142,145,165,168,171,174,177,180,183,186, - 189,192,195,198,201,221,224,227,230,233,236,239,242,245,248,251, - 254,257, 6, 26, -}; - diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index abeff2d7..827fded4 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -9,6 +9,5 @@ extern const sbit_t gsm0503_usf2twelve_sbit[8][12]; extern const uint8_t gsm0503_puncture_cs2[588]; extern const uint8_t gsm0503_puncture_cs3[676]; extern const uint8_t gsm0503_gsm_fr_map[76]; -extern const uint16_t gsm0503_d_to_b_index[260]; #endif /* _0503_TABLES_H */ diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am index c3c04067..992bbb14 100644 --- a/tests/bursts/Makefile.am +++ b/tests/bursts/Makefile.am @@ -1,6 +1,6 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) -AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) -LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) +LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) noinst_PROGRAMS = bursts_test EXTRA_DIST = bursts_test.ok -- cgit v1.2.3 From 7bd6e8b89bbb38b292caa0df3c52902c46911c1a Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 21 Feb 2013 09:27:52 +0100 Subject: TRX: Ciphering --- src/osmo-bts-trx/l1_if.c | 36 +++++++++---- src/osmo-bts-trx/l1_if.h | 6 +++ src/osmo-bts-trx/main.c | 2 +- src/osmo-bts-trx/scheduler.c | 118 +++++++++++++++++++++++++++++++++---------- src/osmo-bts-trx/scheduler.h | 4 ++ 5 files changed, 129 insertions(+), 37 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 51b1f439..58c2587b 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -357,9 +357,29 @@ static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts) /* enable ciphering */ static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan, - int downlink) + uint8_t chan_nr, int downlink) { - // FIXME + /* ciphering already enabled in both directions */ + if (lchan->ciph_state == LCHAN_CIPH_TXRX_CONF) + return -EINVAL; + + if (!downlink) { + /* set uplink */ + trx_sched_set_cipher(l1h, chan_nr, 0, lchan->encr.alg_id - 1, + lchan->encr.key, lchan->encr.key_len); + lchan->ciph_state = LCHAN_CIPH_RX_CONF; + } else { + /* set downlink and also set uplink, if not already */ + if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) { + trx_sched_set_cipher(l1h, chan_nr, 0, + lchan->encr.alg_id - 1, lchan->encr.key, + lchan->encr.key_len); + } + trx_sched_set_cipher(l1h, chan_nr, 1, lchan->encr.alg_id - 1, + lchan->encr.key, lchan->encr.key_len); + lchan->ciph_state = LCHAN_CIPH_TXRX_CONF; + } + return 0; } @@ -423,14 +443,10 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) tn = L1SAP_CHAN2TS(chan_nr); ss = l1sap_chan2ss(chan_nr); lchan = &trx->ts[tn].lchan[ss]; - if (l1sap->u.info.u.ciph_req.downlink) { - l1if_set_ciphering(l1h, lchan, 1); - lchan->ciph_state = LCHAN_CIPH_RX_REQ; - } - if (l1sap->u.info.u.ciph_req.uplink) { - l1if_set_ciphering(l1h, lchan, 0); - lchan->ciph_state = LCHAN_CIPH_TXRX_REQ; - } + if (l1sap->u.info.u.ciph_req.uplink) + l1if_set_ciphering(l1h, lchan, chan_nr, 0); + if (l1sap->u.info.u.ciph_req.downlink) + l1if_set_ciphering(l1h, lchan, chan_nr, 1); break; case PRIM_INFO_ACTIVATE: case PRIM_INFO_DEACTIVATE: diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 8a0a90e6..0f4e8113 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -56,6 +56,12 @@ struct trx_chan_state { uint8_t ul_mask; /* mask of received bursts */ uint8_t lost; /* (SACCH) loss detection */ uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */ + int ul_encr_algo; /* A5/x encry algo downlink */ + int dl_encr_algo; /* A5/x encry algo uplink */ + int ul_encr_key_len; + int dl_encr_key_len; + uint8_t ul_encr_key[8]; + uint8_t dl_encr_key[8]; }; struct trx_config { diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 4e942499..7701c2c9 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -317,7 +317,7 @@ int main(int argc, char **argv) exit(1); } btsb = bts_role_bts(bts); - btsb->support.ciphers = 0; // CIPHER_A5(1) | CIPHER_A5(2) | CIPHER_A5(3); + btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2); if (gsmtap_ip) { gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1); diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6f5dddb0..f2e7dad5 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -61,7 +62,7 @@ uint32_t trx_rts_advance = 5; /* about 20ms */ typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); -typedef const ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn, +typedef ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); @@ -70,19 +71,19 @@ static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); -static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); -static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); -static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); -static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); -static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); -static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); -static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); @@ -95,7 +96,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); -static const ubit_t dummy_burst[148] = { +static ubit_t dummy_burst[148] = { 0,0,0, 1,1,1,1,1,0,1,1,0,1,1,1,0,1,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,1,1,0, 0,0,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0, @@ -105,7 +106,7 @@ static const ubit_t dummy_burst[148] = { 0,0,0, }; -static const ubit_t fcch_burst[148] = { +static ubit_t fcch_burst[148] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -394,7 +395,7 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, */ /* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ -static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n", @@ -403,7 +404,7 @@ static const ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return NULL; } -static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n", @@ -412,7 +413,7 @@ static const ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return fcch_burst; } -static const ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { static ubit_t bits[148], burst[78]; @@ -573,7 +574,7 @@ static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return 0; } -static const ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { struct msgb *msg = NULL; /* make GCC happy */ @@ -649,7 +650,7 @@ send_burst: return bits; } -static const ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { struct msgb *msg = NULL; /* make GCC happy */ @@ -719,7 +720,7 @@ send_burst: return bits; } -static const ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; @@ -854,7 +855,7 @@ send_burst: return bits; } -static const ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { // FIXME @@ -939,8 +940,6 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); - // FIXME: decrypt burst - /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -1006,8 +1005,6 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); - // FIXME: decrypt burst - /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -1069,8 +1066,6 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); - // FIXME: decrypt burst - /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -1964,6 +1959,48 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, return rc; } +/* setting cipher on logical channels */ +int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, + int algo, uint8_t *key, int key_len) +{ + uint8_t tn = L1SAP_CHAN2TS(chan_nr); + int i; + int rc = -EINVAL; + struct trx_chan_state *chan_state; + + if (algo < 0 || key_len > 8 || (algo && key_len != 8)) { + LOGP(DL1C, LOGL_ERROR, "Algo A5/%d not supported with given " + "key len=%d\n", algo, key_len); + return -ENOTSUP; + } + + /* look for all matching chan_nr */ + for (i = 0; i < _TRX_CHAN_MAX; i++) { + /* skip if pchan type */ + if (trx_chan_desc[i].pdch) + continue; + if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)) { + chan_state = &l1h->chan_states[tn][i]; + LOGP(DL1C, LOGL_NOTICE, "Set a5/%d %s for %s on trx=%d " + "ts=%d\n", algo, + (downlink) ? "downlink" : "uplink", + trx_chan_desc[i].name, l1h->trx->nr, tn); + if (downlink) { + chan_state->dl_encr_algo = algo; + memcpy(chan_state->dl_encr_key, key, key_len); + chan_state->dl_encr_key_len = key_len; + } else { + chan_state->ul_encr_algo = algo; + memcpy(chan_state->ul_encr_key, key, key_len); + chan_state->ul_encr_key_len = key_len; + } + rc = 0; + } + } + + return rc; +} + /* process ready-to-send */ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) { @@ -2009,7 +2046,7 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, uint8_t offset, period, bid; trx_sched_dl_func *func; enum trx_chan_type chan; - const ubit_t *bits = NULL; + ubit_t *bits = NULL; if (!l1h->mf_index[tn]) goto no_data; @@ -2031,6 +2068,19 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, /* get burst from function */ bits = func(l1h, tn, fn, chan, bid); + /* encrypt */ + if (bits && l1h->chan_states[tn][chan].dl_encr_algo) { + ubit_t ks[114]; + int i; + + osmo_a5(l1h->chan_states[tn][chan].dl_encr_algo, + l1h->chan_states[tn][chan].dl_encr_key, fn, ks, NULL); + for (i = 0; i < 57; i++) { + bits[i + 3] ^= ks[i]; + bits[i + 88] ^= ks[i + 57]; + } + } + no_data: /* in case of C0, we need a dummy burst to maintain RF power */ if (bits == NULL && l1h->trx == l1h->trx->bts->c0) { @@ -2087,9 +2137,25 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, goto next_frame; /* put burst to function */ - if (fn == current_fn) + if (fn == current_fn) { + /* decrypt */ + if (bits && l1h->chan_states[tn][chan].ul_encr_algo) { + ubit_t ks[114]; + int i; + + osmo_a5(l1h->chan_states[tn][chan].ul_encr_algo, + l1h->chan_states[tn][chan].ul_encr_key, + fn, NULL, ks); + for (i = 0; i < 57; i++) { + if (ks[i]) + bits[i + 3] = - bits[i + 3]; + if (ks[i + 57]) + bits[i + 88] = - bits[i + 88]; + } + } + func(l1h, tn, fn, chan, bid, bits, toa); - else { + } else { sbit_t spare[148]; memset(spare, 0, 148); diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index 28c72b53..ab185f3b 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -30,6 +30,10 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, uint8_t tch_mode); +/* setting cipher on logical channels */ +int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, + int algo, uint8_t *key, int key_len); + /* close all logical channels and reset timeslots */ void trx_sched_reset(struct trx_l1h *l1h); -- cgit v1.2.3 From ce0f20b597343f472c6df44c1adf596a388f9728 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 21 Feb 2013 15:39:59 +0100 Subject: TRX: Fix of SCH burst data --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f2e7dad5..b5fe08f2 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -437,7 +437,7 @@ static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, ((t.t1 & 0x001) << 7) | ((t.t2 & 0x1f) << 2) | ((t3p & 0x6) >> 1); - sb_info[2] = + sb_info[3] = (t3p & 0x1); /* encode bursts */ -- cgit v1.2.3 From 23a5183767d96a723190634f4a26d018492880bf Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 21 Feb 2013 15:41:41 +0100 Subject: TRX: Fixes to TRX interface Ignore false response to uncritical commands. --- src/osmo-bts-trx/trx_if.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index b8535e38..c03427a0 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -305,7 +305,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) else rsp_len = strlen(buf) - 4; - LOGP(DTRX, LOGL_DEBUG, "Response message: '%s'\n", buf); + LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf); /* abort timer and send next message, if any */ if (osmo_timer_pending(&l1h->trx_ctrl_timer)) @@ -323,10 +323,10 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) /* check if respose matches command */ if (rsp_len != tcm->cmd_len) { notmatch: - LOGP(DTRX, LOGL_NOTICE, "Response message '%s' does " - "not match command message '%s'\n", buf, - tcm->cmd); - return -EINVAL; + LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE, + "Response message '%s' does not match command " + "message '%s'\n", buf, tcm->cmd); + goto rsp_error; } if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) goto notmatch; @@ -337,6 +337,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE, "Tranceiver rejected TRX command with " "response: '%s'\n", buf); +rsp_error: if (tcm->critical) { bts_shutdown(l1h->trx->bts, "SIGINT"); /* keep tcm list, so process is stopped */ @@ -403,7 +404,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) return -EINVAL; } - LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f ", + LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", tn, fn, rssi, toa); trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa); -- cgit v1.2.3 From 889890da4312916bd617b3c95326a89237078a3b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 22 Feb 2013 07:52:51 +0100 Subject: TRX: Improved handling of clock indications. If no clock is received, a POWEROFF is sent until clock is detected. --- src/osmo-bts-trx/l1_if.c | 3 +++ src/osmo-bts-trx/scheduler.c | 21 ++++++++++++++------- src/osmo-bts-trx/trx_if.c | 5 ++++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 58c2587b..3f297d6a 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -212,6 +212,7 @@ int l1if_provision_tranceiver(struct gsm_bts *bts) { struct gsm_bts_trx *trx; struct trx_l1h *l1h; + uint8_t tn; llist_for_each_entry(trx, &bts->trx_list, list) { l1h = trx_l1h_hdl(trx); @@ -222,6 +223,8 @@ int l1if_provision_tranceiver(struct gsm_bts *bts) l1h->config.rxgain_sent = 0; l1h->config.power_sent = 0; l1h->config.maxdly_sent = 0; + for (tn = 0; tn < 8; tn++) + l1h->config.slottype_sent[tn] = 0; l1if_provision_tranceiver_trx(l1h); } return 0; diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index b5fe08f2..057d5c2e 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2252,6 +2252,8 @@ no_clock: llist_for_each_entry(trx, &bts->trx_list, list) { trx_if_flush(trx_l1h_hdl(trx)); trx_sched_reset(trx_l1h_hdl(trx)); + if (trx->nr == 0) + trx_if_cmd_poweroff(trx_l1h_hdl(trx)); } /* tell BSC */ @@ -2295,6 +2297,9 @@ int trx_sched_clock(uint32_t fn) int32_t elapsed; int32_t elapsed_fn; + if (quit) + return 0; + /* reset lost counter */ tranceiver_lost = 0; @@ -2304,13 +2309,21 @@ int trx_sched_clock(uint32_t fn) if (!tranceiver_available) { LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n", fn); + + tranceiver_available = 1; + + /* start provisioning tranceiver */ + l1if_provision_tranceiver(bts); + + /* tell BSC */ + check_tranceiver_availability(bts, 1); + new_clock: tranceiver_last_fn = fn; trx_sched_fn(tranceiver_last_fn); /* schedule first FN clock */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); - tranceiver_available = 1; memset(&tranceiver_clock_timer, 0, sizeof(tranceiver_clock_timer)); tranceiver_clock_timer.cb = trx_ctrl_timer_cb; @@ -2318,12 +2331,6 @@ new_clock: osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); - /* start provisioning tranceiver */ - l1if_provision_tranceiver(bts); - - /* tell BSC */ - check_tranceiver_availability(bts, 1); - return 0; } diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index c03427a0..6420fdb4 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -183,7 +183,7 @@ static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd, va_list ap; int l, pending = 0; - if (!tranceiver_available) { + if (!tranceiver_available && !!strcmp(cmd, "POWEROFF")) { LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from " "tranceiver, please fix!\n"); return -EIO; @@ -474,6 +474,9 @@ int trx_if_open(struct trx_l1h *l1h) /* enable all slots */ l1h->config.slotmask = 0xff; + if (l1h->trx->nr == 0) + trx_if_cmd_poweroff(l1h); + return 0; err: -- cgit v1.2.3 From 219ece83a3ffe7fa2bd58943cddb47a3eacc2fab Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 24 Feb 2013 11:09:19 +0100 Subject: TRX: Implementation of MS power and timing advance loops --- include/osmo-bts/logging.h | 1 + src/common/logging.c | 6 ++ src/osmo-bts-trx/Makefile.am | 4 +- src/osmo-bts-trx/l1_if.h | 9 ++ src/osmo-bts-trx/loops.c | 251 +++++++++++++++++++++++++++++++++++++++++++ src/osmo-bts-trx/loops.h | 14 +++ src/osmo-bts-trx/scheduler.c | 67 ++++++++---- src/osmo-bts-trx/trx_if.c | 14 ++- src/osmo-bts-trx/trx_vty.c | 50 +++++++++ 9 files changed, 394 insertions(+), 22 deletions(-) create mode 100644 src/osmo-bts-trx/loops.c create mode 100644 src/osmo-bts-trx/loops.h diff --git a/include/osmo-bts/logging.h b/include/osmo-bts/logging.h index b7c8a270..b1b44d54 100644 --- a/include/osmo-bts/logging.h +++ b/include/osmo-bts/logging.h @@ -17,6 +17,7 @@ enum { DPCU, DHO, DTRX, + DLOOP, DABIS, DRTP, DSUM, diff --git a/src/common/logging.c b/src/common/logging.c index b117ee04..5ce9b8b6 100644 --- a/src/common/logging.c +++ b/src/common/logging.c @@ -113,6 +113,12 @@ static struct log_info_cat bts_log_info_cat[] = { .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DLOOP] = { + .name = "DLOOP", + .description = "Control loops", + .color = "\033[0;34m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, #if 0 [DNS] = { .name = "DNS", diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index 28221d15..d9ddca1f 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -2,10 +2,10 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp -EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h +EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h bin_PROGRAMS = osmobts-trx -osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c +osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 0f4e8113..eea49639 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -62,6 +62,15 @@ struct trx_chan_state { int dl_encr_key_len; uint8_t ul_encr_key[8]; uint8_t dl_encr_key[8]; + struct { + uint8_t clock; /* cyclic clock counter */ + int8_t rssi[32]; /* last RSSI values */ + int rssi_count; /* received RSSI values */ + int rssi_valid_count; /* number of stored value */ + int rssi_got_burst; /* any burst received so far */ + float toa_sum; /* sum of TOA values */ + int toa_num; /* number of TOA value */ + } meas; }; struct trx_config { diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c new file mode 100644 index 00000000..c8815286 --- /dev/null +++ b/src/osmo-bts-trx/loops.c @@ -0,0 +1,251 @@ +/* Loop control for OsmoBTS-TRX */ + +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "trx_if.h" +#include "l1_if.h" +#include "loops.h" + +#define MS_PWR_DBM(lvl) ms_pwr_dbm(gsm_arfcn2band(l1h->config.arfcn), lvl) + +/* + * MS Power loop + */ + +int trx_ms_power_loop = 0; +int8_t trx_target_rssi = -10; + +/* how much power levels do we raise/lower as maximum (1 level = 2 dB) */ +#define MS_RAISE_MAX 4 +#define MS_LOWER_MAX 4 + +static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan, + uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t diff) +{ + int8_t new_power; + + new_power = lchan->ms_power - (diff >> 1); + + if (diff == 0) + return 0; + + if (new_power < 0) + new_power = 0; + + // FIXME: to go above 1W, we need to know classmark of MS + if (l1h->config.arfcn >= 512 && l1h->config.arfcn <= 885) { + if (new_power > 15) + new_power = 15; + } else { + if (new_power > 19) + new_power = 19; + } + + /* a higher value means a lower level (and vice versa) */ + if (new_power > lchan->ms_power + MS_LOWER_MAX) + new_power = lchan->ms_power + MS_LOWER_MAX; + else if (new_power < lchan->ms_power - MS_RAISE_MAX) + new_power = lchan->ms_power - MS_RAISE_MAX; + + if (lchan->ms_power == new_power) { + LOGP(DLOOP, LOGL_INFO, "Keeping MS new_power of trx=%u " + "chan_nr=0x%02x at control level %d (%d dBm)\n", + l1h->trx->nr, chan_nr, new_power, + MS_PWR_DBM(new_power)); + + return 0; + } + + LOGP(DLOOP, LOGL_INFO, "%s MS new_power of trx=%u chan_nr=0x%02x from " + "control level %d (%d dBm) to %d (%d dBm)\n", + (diff > 0) ? "Raising" : "Lowering", + l1h->trx->nr, chan_nr, lchan->ms_power, + MS_PWR_DBM(lchan->ms_power), new_power, MS_PWR_DBM(new_power)); + + lchan->ms_power = new_power; + + return 0; +} + +static int ms_power_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, + uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t rssi) +{ + /* ignore inserted dummy frames, treat as lost frames */ + if (rssi < -127) + return 0; + + LOGP(DLOOP, LOGL_DEBUG, "Got RSSI value of %d\n", rssi); + + chan_state->meas.rssi_count++; + + /* check if the current L1 header compares to the current ordered TA */ +// if ((lchan->meas.l1_info[0] >> 3) != lchan->ms_power) +// return 0; + + chan_state->meas.rssi_got_burst = 1; + + /* store and process RSSI */ + if (chan_state->meas.rssi_valid_count + == ARRAY_SIZE(chan_state->meas.rssi)) + return 0; + chan_state->meas.rssi[chan_state->meas.rssi_valid_count++] = rssi; + chan_state->meas.rssi_valid_count++; + + return 0; +} + +static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan, + uint8_t chan_nr, struct trx_chan_state *chan_state) +{ + int rssi; + int i; + + /* skip every second clock, to prevent oscillating due to roundtrip + * delay */ + if (!(chan_state->meas.clock & 1)) + return 0; + + LOGP(DLOOP, LOGL_DEBUG, "Got SACCH master clock at RSSI count %d\n", + chan_state->meas.rssi_count); + + /* wait for initial burst */ + if (!chan_state->meas.rssi_got_burst) + return 0; + + /* if no burst was received from MS at clock */ + if (chan_state->meas.rssi_count == 0) { + LOGP(DLOOP, LOGL_NOTICE, "LOST SACCH frame of trx=%u " + "chan_nr=0x%02x, so we raise MS power\n", + l1h->trx->nr, chan_nr); + return ms_power_diff(l1h, lchan, chan_nr, chan_state, + MS_RAISE_MAX); + } + + /* reset total counter */ + chan_state->meas.rssi_count = 0; + + /* check the minimum level received after MS acknowledged the ordered + * power level */ + if (chan_state->meas.rssi_valid_count == 0) + return 0; + for (rssi = 999, i = 0; i < chan_state->meas.rssi_valid_count; i++) { + if (rssi > chan_state->meas.rssi[i]) + rssi = chan_state->meas.rssi[i]; + } + + /* reset valid counter */ + chan_state->meas.rssi_valid_count = 0; + + /* change RSSI */ + LOGP(DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current " + "MS power: %d (%d dBm) of trx=%u chan_nr=0x%02x\n", rssi, + trx_target_rssi, lchan->ms_power, MS_PWR_DBM(lchan->ms_power), + l1h->trx->nr, chan_nr); + ms_power_diff(l1h, lchan, chan_nr, chan_state, trx_target_rssi - rssi); + + return 0; +} + + +/* + * Timing Advance loop + */ + +int trx_ta_loop = 1; + +int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr, + struct trx_chan_state *chan_state, float toa) +{ + /* check if the current L1 header acks to the current ordered TA */ + if (lchan->meas.l1_info[1] != lchan->rqd_ta) + return 0; + + /* sum measurement */ + chan_state->meas.toa_sum += toa; + if (++(chan_state->meas.toa_num) < 16) + return 0; + + /* complete set */ + toa = chan_state->meas.toa_sum / chan_state->meas.toa_num; + + /* check for change of TOA */ + if (toa < -0.9F && lchan->rqd_ta > 0) { + LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too " + "early (%.2f), now lowering TA from %d to %d\n", + l1h->trx->nr, chan_nr, toa, lchan->rqd_ta, + lchan->rqd_ta - 1); + lchan->rqd_ta--; + } else if (toa > 0.9F && lchan->rqd_ta < 63) { + LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too " + "late (%.2f), now raising TA from %d to %d\n", + l1h->trx->nr, chan_nr, toa, lchan->rqd_ta, + lchan->rqd_ta + 1); + lchan->rqd_ta++; + } else + LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is " + "correct (%.2f), keeping current TA of %d\n", + l1h->trx->nr, chan_nr, toa, lchan->rqd_ta); + + chan_state->meas.toa_num = 0; + chan_state->meas.toa_sum = 0; + + return 0; +} + +int trx_loop_input(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state, int8_t rssi, float toa) +{ + struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)] + .lchan[l1sap_chan2ss(chan_nr)]; + + if (trx_ms_power_loop) + ms_power_val(l1h, lchan, chan_nr, chan_state, rssi); + + if (trx_ta_loop) + ta_val(l1h, lchan, chan_nr, chan_state, toa); + + return 0; +} + +int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state) +{ + struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)] + .lchan[l1sap_chan2ss(chan_nr)]; + + if (trx_ms_power_loop) + ms_power_clock(l1h, lchan, chan_nr, chan_state); + + /* count the number of SACCH clocks */ + chan_state->meas.clock++; + + return 0; +} + diff --git a/src/osmo-bts-trx/loops.h b/src/osmo-bts-trx/loops.h new file mode 100644 index 00000000..5449e41b --- /dev/null +++ b/src/osmo-bts-trx/loops.h @@ -0,0 +1,14 @@ +#ifndef _TRX_LOOPS_H +#define _TRX_LOOPS_H + +extern int trx_ms_power_loop; +extern int8_t trx_target_rssi; +extern int trx_ta_loop; + +int trx_loop_input(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state, int8_t rssi, float toa); + +int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state); + +#endif /* _TRX_LOOPS_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 057d5c2e..a0ffe163 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -38,6 +38,7 @@ #include "scheduler.h" #include "gsm0503_coding.h" #include "trx_if.h" +#include "loops.h" /* Enable this to multiply TOA of RACH by 10. * This usefull to check tenth of timing advances with RSSI test tool. @@ -65,7 +66,8 @@ typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, typedef ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); @@ -86,15 +88,20 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa); + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa); static ubit_t dummy_burst[148] = { 0,0,0, @@ -324,6 +331,10 @@ static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, "link_id=0x%02x fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, chan_nr, link_id, fn, tn, l1h->trx->nr); + /* send clock information to loops process */ + if (L1SAP_IS_LINK_SACCH(link_id)) + trx_loop_sacch_clock(l1h, chan_nr, &l1h->chan_states[tn][chan]); + /* generate prim */ msg = l1sap_msgb_alloc(200); if (!msg) @@ -869,7 +880,8 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, */ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) { struct osmo_phsap_prim l1sap; uint8_t ra; @@ -906,7 +918,8 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -940,6 +953,12 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memcpy(burst, bits + 3, 58); memcpy(burst + 58, bits + 87, 58); + /* send burst information to loops process */ + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { + trx_loop_input(l1h, trx_chan_desc[chan].chan_nr | tn, + chan_state, rssi, toa); + } + /* wait until complete set of bursts */ if (bid != 3) return 0; @@ -950,6 +969,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, "fn=%u (%u/%u) for %s\n", *first_fn, (*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn], trx_chan_desc[chan].name); + /* we require first burst to have correct FN */ if (!(*mask & 0x1)) { *mask = 0x0; @@ -973,7 +993,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -1034,7 +1055,8 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) { struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; @@ -1106,7 +1128,8 @@ bfi: } static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid, sbit_t *bits, float toa) + enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, + float toa) { LOGP(DL1C, LOGL_DEBUG, "TCH/H Received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1906,6 +1929,7 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, uint8_t tn = L1SAP_CHAN2TS(chan_nr); int i; int rc = -EINVAL; + struct trx_chan_state *chan_state; /* look for all matching chan_nr/link_id */ for (i = 0; i < _TRX_CHAN_MAX; i++) { @@ -1916,18 +1940,23 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, continue; if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) && trx_chan_desc[i].link_id == link_id) { + chan_state = &l1h->chan_states[tn][i]; LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", (active) ? "Activating" : "Deactivating", (downlink) ? "downlink" : "uplink", trx_chan_desc[i].name, l1h->trx->nr, tn); if (downlink) { - l1h->chan_states[tn][i].dl_active = active; - l1h->chan_states[tn][i].dl_active = active; + chan_state->dl_active = active; + chan_state->dl_active = active; } else { - l1h->chan_states[tn][i].ul_active = active; - l1h->chan_states[tn][i].ul_active = active; + chan_state->ul_active = active; + chan_state->ul_active = active; + } + chan_state->lost = 0; + if (L1SAP_IS_LINK_SACCH(link_id)) { + memset(&chan_state->meas, 0, + sizeof(chan_state->meas)); } - l1h->chan_states[tn][i].lost = 0; rc = 0; } } @@ -2154,12 +2183,12 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, } } - func(l1h, tn, fn, chan, bid, bits, toa); - } else { + func(l1h, tn, fn, chan, bid, bits, rssi, toa); + } else if (chan != TRXC_RACH) { sbit_t spare[148]; memset(spare, 0, 148); - func(l1h, tn, fn, chan, bid, spare, toa); + func(l1h, tn, fn, chan, bid, spare, -128, 0); } next_frame: diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 6420fdb4..bca9cdca 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -41,6 +41,9 @@ #include "trx_if.h" #include "scheduler.h" +/* enable to print RSSI level graph */ +//#define TOA_RSSI_DEBUG + int tranceiver_available = 0; const char *tranceiver_ip = "127.0.0.1"; @@ -384,7 +387,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) } tn = buf[0]; fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; - rssi = (int8_t)buf[5]; + rssi = -(int8_t)buf[5]; toa = ((int16_t)(buf[6] << 8) | buf[7]) / 256.0F; /* copy and convert bits {254..0} to sbits {-127..127} */ @@ -407,6 +410,15 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n", tn, fn, rssi, toa); +#ifdef TOA_RSSI_DEBUG + char deb[128]; + + sprintf(deb, "| 0 " + " | rssi=%4d toa=%4.2f fn=%u", rssi, toa, fn); + deb[1 + (128 + rssi) / 4] = '*'; + fprintf(stderr, "%s\n", deb); +#endif + trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa); return 0; diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c index 720f3538..fa08df4d 100644 --- a/src/osmo-bts-trx/trx_vty.c +++ b/src/osmo-bts-trx/trx_vty.c @@ -43,6 +43,7 @@ #include "l1_if.h" #include "scheduler.h" #include "trx_if.h" +#include "loops.h" static struct gsm_bts *vty_bts; @@ -126,6 +127,43 @@ DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd, return CMD_SUCCESS; } +DEFUN(cfg_bts_ms_power_loop, cfg_bts_ms_power_loop_cmd, + "ms-power-loop <-127-127>", + "Enable MS power control loop\nTarget RSSI value (tranceiver specific, " + "should be 6dB or more above noise floor)\n") +{ + trx_ms_power_loop = 1; + trx_target_rssi = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_no_ms_power_loop, cfg_bts_no_ms_power_loop_cmd, + "no ms-power-loop", + NO_STR "Disable MS power control loop\n") +{ + trx_ms_power_loop = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_timing_advance_loop, cfg_bts_timing_advance_loop_cmd, + "timing-advance-loop", + "Enable timing advance control loop\n") +{ + trx_ta_loop = 1; + + return CMD_SUCCESS; +} +DEFUN(cfg_bts_no_timing_advance_loop, cfg_bts_no_timing_advance_loop_cmd, + "no timing-advance-loop", + NO_STR "Disable timing advance control loop\n") +{ + trx_ta_loop = 0; + + return CMD_SUCCESS; +} + DEFUN(cfg_trx_rxgain, cfg_trx_rxgain_cmd, "rxgain <0-50>", "Set the receiver gain in dB\n" @@ -236,6 +274,14 @@ DEFUN(cfg_trx_no_maxdly, cfg_trx_no_maxdly_cmd, void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts) { vty_out(vty, " fn-advance %d%s", trx_clock_advance, VTY_NEWLINE); + + if (trx_ms_power_loop) + vty_out(vty, " ms-power-loop %d%s", trx_target_rssi, + VTY_NEWLINE); + else + vty_out(vty, " no ms-power-loop%s", VTY_NEWLINE); + vty_out(vty, " %stiming-advance-loop%s", (trx_ta_loop) ? "":"no ", + VTY_NEWLINE); } void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx) @@ -268,6 +314,10 @@ int bts_model_vty_init(struct gsm_bts *bts) install_element_ve(&show_tranceiver_cmd); install_element(BTS_NODE, &cfg_bts_fn_advance_cmd); + install_element(BTS_NODE, &cfg_bts_ms_power_loop_cmd); + install_element(BTS_NODE, &cfg_bts_no_ms_power_loop_cmd); + install_element(BTS_NODE, &cfg_bts_timing_advance_loop_cmd); + install_element(BTS_NODE, &cfg_bts_no_timing_advance_loop_cmd); install_element(TRX_NODE, &cfg_trx_rxgain_cmd); install_element(TRX_NODE, &cfg_trx_power_cmd); -- cgit v1.2.3 From 9855e8bd4854f6afb740ad940f2999be354b0af1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 1 Mar 2013 10:05:59 +0100 Subject: TRX: Out of range primitives found in downlink queue are not an error --- src/osmo-bts-trx/scheduler.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index a0ffe163..fde14fa8 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -500,9 +500,10 @@ free_msg: goto wrong_type; } if (prim_fn > 20) { - LOGP(DL1C, LOGL_ERROR, "Prim for trx=%u ts=%u at fn=%u " - "is out of range. (current fn=%u)\n", - l1h->trx->nr, tn, prim_fn, fn); + LOGP(DL1C, LOGL_NOTICE, "Prim for trx=%u ts=%u at fn=%u " + "is out of range, or channel already disabled. " + "(current fn=%u)\n", l1h->trx->nr, tn, prim_fn, + fn); /* unlink and free message */ llist_del(&msg->list); msgb_free(msg); -- cgit v1.2.3 From 7ff22823ca8e2dd1e990578a2dbf6942b1911eed Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 10 Mar 2013 11:44:17 +0100 Subject: TRX: Use link timeout value from BSC via OML attribute. --- src/osmo-bts-trx/l1_if.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 3f297d6a..294d77c5 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -278,11 +278,17 @@ static int trx_close(struct gsm_bts_trx *trx) } /* set bts attributes */ -static uint8_t trx_set_bts(struct gsm_bts *bts) +static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr) { struct gsm_bts_trx *trx; struct trx_l1h *l1h; uint8_t bsic = bts->bsic; + struct gsm_bts_role_bts *btsb = bts_role_bts(bts); + + if (TLVP_PRESENT(new_attr, NM_ATT_CONN_FAIL_CRIT)) { + const uint8_t *val = TLVP_VAL(new_attr, NM_ATT_CONN_FAIL_CRIT); + btsb->radio_link_timeout = val[1]; + } llist_for_each_entry(trx, &bts->trx_list, list) { l1h = trx_l1h_hdl(trx); @@ -546,7 +552,7 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg, switch (foh->msg_type) { case NM_MT_SET_BTS_ATTR: - cause = trx_set_bts(obj); + cause = trx_set_bts(obj, new_attr); break; case NM_MT_SET_RADIO_ATTR: cause = trx_set_trx(obj); -- cgit v1.2.3 From 84b9a445351937703334629f3fec02689153f986 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Mar 2013 07:56:05 +0100 Subject: TRX: Code cleanup, prepare for other codecs than GSM full rate --- src/osmo-bts-trx/gsm0503_coding.c | 49 +++--- src/osmo-bts-trx/gsm0503_conv.c | 4 +- src/osmo-bts-trx/gsm0503_interleaving.c | 3 +- src/osmo-bts-trx/gsm0503_mapping.c | 49 +++--- src/osmo-bts-trx/gsm0503_mapping.h | 4 +- src/osmo-bts-trx/gsm0503_tables.c | 12 +- src/osmo-bts-trx/l1_if.c | 12 +- src/osmo-bts-trx/l1_if.h | 12 +- src/osmo-bts-trx/loops.c | 6 +- src/osmo-bts-trx/loops.h | 14 +- src/osmo-bts-trx/scheduler.c | 277 +++++++++++++++++++++----------- src/osmo-bts-trx/scheduler.h | 2 +- 12 files changed, 276 insertions(+), 168 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index c7fe28db..5ceb1d58 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -336,8 +336,6 @@ static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits, int net_order) i++; j++; } - - /* rearrange according to Table 2 of TS 05.03 */ } static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) @@ -419,37 +417,38 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) int i, rv, len, steal = 0; for (i=0; i<8; i++) { - gsm0503_tch_fr_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, i>>2); steal -= h; } gsm0503_tch_fr_deinterleave(cB, iB); - if (steal <= 0) { - osmo_conv_decode(&gsm0503_conv_tch_fr, cB, conv); + if (steal > 0) { + rv = _xcch_decode_cB(tch_data, cB); + if (rv) + return -1; - tch_fr_unreorder(d, p, conv); + return 23; + } - for (i=0; i<78; i++) - d[i+182] = (cB[i+378] < 0) ? 1:0; + osmo_conv_decode(&gsm0503_conv_tch_fr, cB, conv); - rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d, 50, p); - if (rv) - return -1; + tch_fr_unreorder(d, p, conv); - tch_fr_d_to_b(b, d); + for (i=0; i<78; i++) + d[i+182] = (cB[i+378] < 0) ? 1:0; - tch_fr_reassemble(tch_data, b, net_order); + rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d, 50, p); + if (rv) + return -1; - len = 33; - } else { - rv = _xcch_decode_cB(tch_data, cB); - if (rv) - return -1; - len = 23; - } + tch_fr_d_to_b(b, d); + + tch_fr_reassemble(tch_data, b, net_order); + + len = 33; return len; } @@ -457,14 +456,14 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order) { ubit_t iB[912], cB[456], h; - ubit_t conv[185], b[260], d[260], p[3]; + ubit_t conv[185], w[260], d[260], p[8]; int i; switch (len) { case 33: /* TCH FR */ - tch_fr_disassemble(b, tch_data, net_order); + tch_fr_disassemble(w, tch_data, net_order); - tch_fr_b_to_d(d, b); + tch_fr_b_to_d(d, w); osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p); @@ -490,13 +489,11 @@ int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order) gsm0503_tch_fr_interleave(cB, iB); for (i=0; i<8; i++) - gsm0503_tch_fr_burst_map(&iB[i * 114], &bursts[i * 116], &h, - i>>2); + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>2); return 0; } - /* * GSM RACH transcoding */ diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index 79667715..f9fcaa82 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -61,7 +61,7 @@ const struct osmo_conv_code gsm0503_conv_rach = { .next_output = conv_xcch_next_output, .next_state = conv_xcch_next_state, }; - + const struct osmo_conv_code gsm0503_conv_sch = { .N = 2, @@ -70,7 +70,7 @@ const struct osmo_conv_code gsm0503_conv_sch = { .next_output = conv_xcch_next_output, .next_state = conv_xcch_next_state, }; - + const struct osmo_conv_code gsm0503_conv_tch_fr = { .N = 2, diff --git a/src/osmo-bts-trx/gsm0503_interleaving.c b/src/osmo-bts-trx/gsm0503_interleaving.c index f8a63625..7a721b57 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.c +++ b/src/osmo-bts-trx/gsm0503_interleaving.c @@ -3,6 +3,7 @@ #include +#include "gsm0503_tables.h" #include "gsm0503_interleaving.h" /* @@ -55,7 +56,7 @@ void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB) * Interleaving: * * Given 456 coded input bits, form 8 blocks of 114 bits, - * where event bits of the first 4 block and off bits of the last 4 block + * where even bits of the first 4 blocks and odd bits of the last 4 blocks * are used: * * i(B, j) = c(n, k) k = 0, ..., 455 diff --git a/src/osmo-bts-trx/gsm0503_mapping.c b/src/osmo-bts-trx/gsm0503_mapping.c index e5ee9206..bdaddcef 100644 --- a/src/osmo-bts-trx/gsm0503_mapping.c +++ b/src/osmo-bts-trx/gsm0503_mapping.c @@ -30,36 +30,43 @@ void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl, eB[58] = *hn; } -void gsm0503_tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) +void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd) { int i; /* brainfuck: only copy even or odd bits */ - for (i=odd; i<57; i+=2) - iB[i] = eB[i]; - for (i=58-odd; i<114; i+=2) - iB[i] = eB[i+2]; - - if (h && !odd) - *h = eB[58]; - - if (h && odd) - *h = eB[57]; + if (iB) { + for (i=odd; i<57; i+=2) + iB[i] = eB[i]; + for (i=58-odd; i<114; i+=2) + iB[i] = eB[i+2]; + } + + if (h) { + if (!odd) + *h = eB[58]; + else + *h = eB[57]; + } } -void gsm0503_tch_fr_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd) +void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd) { int i; /* brainfuck: only copy even or odd bits */ - for (i=odd; i<57; i+=2) - eB[i] = iB[i]; - for (i=58-odd; i<114; i+=2) - eB[i+2] = iB[i]; - - if (h && !odd) - eB[58] = *h; - if (h && odd) - eB[57] = *h; + if (eB) { + for (i=odd; i<57; i+=2) + eB[i] = iB[i]; + for (i=58-odd; i<114; i+=2) + eB[i+2] = iB[i]; + } + + if (h) { + if (!odd) + eB[58] = *h; + else + eB[57] = *h; + } } diff --git a/src/osmo-bts-trx/gsm0503_mapping.h b/src/osmo-bts-trx/gsm0503_mapping.h index 6123266e..a693b354 100644 --- a/src/osmo-bts-trx/gsm0503_mapping.h +++ b/src/osmo-bts-trx/gsm0503_mapping.h @@ -4,7 +4,7 @@ void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn); void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl, const ubit_t *hn); -void gsm0503_tch_fr_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd); -void gsm0503_tch_fr_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd); +void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd); +void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd); #endif /* _0503_INTERLEAVING_H */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 0e3e0d05..8ab6060b 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -103,15 +103,9 @@ const uint8_t gsm0503_puncture_cs3[676] = { /* this corresponds to the bit-lengths of the individual codec * parameters as indicated in Table 1.1 of TS 06.10 */ const uint8_t gsm0503_gsm_fr_map[76] = { - 6, 6, 5, 5, 4, 4, 3, 3, - 7, 2, 2, 6, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 7, 2, 2, 6, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 7, 2, 2, 6, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 7, 2, 2, 6, 3, - 3, 3, 3, 3, 3, 3, 3, 3, + 6, 6, 5, 5, 4, 4, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 294d77c5..61c02cd4 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -471,11 +471,9 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) break; } /* activate dedicated channel */ - trx_sched_set_lchan(l1h, chan_nr, 0x00, 0, 1); - trx_sched_set_lchan(l1h, chan_nr, 0x00, 1, 1); + trx_sched_set_lchan(l1h, chan_nr, 0x00, 1); /* activate assoicated channel */ - trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 1); - trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 1); + trx_sched_set_lchan(l1h, chan_nr, 0x40, 1); /* set mode */ trx_sched_set_mode(l1h, chan_nr, lchan->rsl_cmode, lchan->tch_mode); @@ -498,12 +496,10 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) break; } /* deactivate assoicated channel */ - trx_sched_set_lchan(l1h, chan_nr, 0x40, 0, 0); - trx_sched_set_lchan(l1h, chan_nr, 0x40, 1, 0); + trx_sched_set_lchan(l1h, chan_nr, 0x40, 0); /* deactivate dedicated channel */ if (!l1sap->u.info.u.act_req.sacch_only) { - trx_sched_set_lchan(l1h, chan_nr, 0x00, 0, 0); - trx_sched_set_lchan(l1h, chan_nr, 0x00, 1, 0); + trx_sched_set_lchan(l1h, chan_nr, 0x00, 0); /* confirm only on dedicated channel */ mph_info_chan_confirm(l1h, chan_nr, PRIM_INFO_DEACTIVATE, 0); diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index eea49639..459496f6 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -48,20 +48,28 @@ enum trx_chan_type { /* States each channel on a multiframe */ struct trx_chan_state { - uint8_t dl_active; /* Channel is active for TX */ - uint8_t ul_active; /* Channel is active for RX */ + /* scheduler */ + uint8_t active; /* Channel is active */ ubit_t *dl_bursts; /* burst buffer for TX */ sbit_t *ul_bursts; /* burst buffer for RX */ uint32_t ul_first_fn; /* fn of first burst */ uint8_t ul_mask; /* mask of received bursts */ + + /* loss detection */ uint8_t lost; /* (SACCH) loss detection */ + + /* mode */ uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */ + + /* encryption */ int ul_encr_algo; /* A5/x encry algo downlink */ int dl_encr_algo; /* A5/x encry algo uplink */ int ul_encr_key_len; int dl_encr_key_len; uint8_t ul_encr_key[8]; uint8_t dl_encr_key[8]; + + /* measurements */ struct { uint8_t clock; /* cyclic clock counter */ int8_t rssi[32]; /* last RSSI values */ diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c index c8815286..ee12e8bd 100644 --- a/src/osmo-bts-trx/loops.c +++ b/src/osmo-bts-trx/loops.c @@ -42,10 +42,6 @@ int trx_ms_power_loop = 0; int8_t trx_target_rssi = -10; -/* how much power levels do we raise/lower as maximum (1 level = 2 dB) */ -#define MS_RAISE_MAX 4 -#define MS_LOWER_MAX 4 - static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t diff) { @@ -219,7 +215,7 @@ int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr, return 0; } -int trx_loop_input(struct trx_l1h *l1h, uint8_t chan_nr, +int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t rssi, float toa) { struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)] diff --git a/src/osmo-bts-trx/loops.h b/src/osmo-bts-trx/loops.h index 5449e41b..8b1d973d 100644 --- a/src/osmo-bts-trx/loops.h +++ b/src/osmo-bts-trx/loops.h @@ -1,11 +1,23 @@ #ifndef _TRX_LOOPS_H #define _TRX_LOOPS_H +/* + * calibration of loops + */ + +/* how much power levels do we raise/lower as maximum (1 level = 2 dB) */ +#define MS_RAISE_MAX 4 +#define MS_LOWER_MAX 1 + +/* + * loops api + */ + extern int trx_ms_power_loop; extern int8_t trx_target_rssi; extern int trx_ta_loop; -int trx_loop_input(struct trx_l1h *l1h, uint8_t chan_nr, +int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t rssi, float toa); int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index fde14fa8..023be372 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -71,7 +71,9 @@ typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); -static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, +static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan); +static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan); static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid); @@ -159,9 +161,9 @@ struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { { 0, TRXC_BCCH, 0x80, 0x00, "BCCH", rts_data_fn, tx_data_fn, NULL, 1 }, { 0, TRXC_RACH, 0x88, 0x00, "RACH", NULL, NULL, rx_rach_fn, 1 }, { 0, TRXC_CCCH, 0x90, 0x00, "CCCH", rts_data_fn, tx_data_fn, NULL, 1 }, - { 0, TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tch_fn, tx_tchf_fn, rx_tchf_fn, 0 }, - { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, - { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tch_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { 0, TRXC_TCHF, 0x08, 0x00, "TCH/F", rts_tchf_fn, tx_tchf_fn, rx_tchf_fn, 0 }, + { 0, TRXC_TCHH_0, 0x10, 0x00, "TCH/H(0)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 }, + { 0, TRXC_TCHH_1, 0x18, 0x00, "TCH/H(1)", rts_tchh_fn, tx_tchh_fn, rx_tchh_fn, 0 }, { 0, TRXC_SDCCH4_0, 0x20, 0x00, "SDCCH/4(0)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, { 0, TRXC_SDCCH4_1, 0x28, 0x00, "SDCCH/4(1)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, { 0, TRXC_SDCCH4_2, 0x30, 0x00, "SDCCH/4(2)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, @@ -215,9 +217,7 @@ int trx_sched_init(struct trx_l1h *l1h) INIT_LLIST_HEAD(&l1h->dl_prims[tn]); for (i = 0; i < _TRX_CHAN_MAX; i++) { chan_state = &l1h->chan_states[tn][i]; - chan_state->dl_active = 0; - chan_state->ul_active = 0; - chan_state->ul_mask = 0x00; + chan_state->active = 0; } } @@ -349,13 +349,13 @@ static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return l1sap_up(l1h->trx, l1sap); } -/* RTS for traffic frame */ -static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan) +static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, int facch) { uint8_t chan_nr, link_id; struct msgb *msg; struct osmo_phsap_prim *l1sap; + int rc = 0; /* get data for RTS indication */ chan_nr = trx_chan_desc[chan].chan_nr | tn; @@ -371,33 +371,55 @@ static int rts_tch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, "fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name, chan_nr, fn, tn, l1h->trx->nr); - /* generate prim */ - msg = l1sap_msgb_alloc(200); - if (!msg) - return -ENOMEM; - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, - PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = chan_nr; - l1sap->u.data.link_id = link_id; - l1sap->u.data.fn = fn; + /* only send, if FACCH is selected */ + if (facch) { + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) + return -ENOMEM; + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_RTS, + PRIM_OP_INDICATION, msg); + l1sap->u.data.chan_nr = chan_nr; + l1sap->u.data.link_id = link_id; + l1sap->u.data.fn = fn; + + rc = l1sap_up(l1h->trx, l1sap); + } + + /* dont send, if TCH is in signalling only mode */ + if (l1h->chan_states[tn][chan].rsl_cmode != RSL_CMOD_SPD_SIGN) { + /* generate prim */ + msg = l1sap_msgb_alloc(200); + if (!msg) + return -ENOMEM; + l1sap = msgb_l1sap_prim(msg); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, + PRIM_OP_INDICATION, msg); + l1sap->u.tch.chan_nr = chan_nr; + l1sap->u.tch.fn = fn; - /* stop here, if TCH is in signalling only mode */ - if (l1h->chan_states[tn][chan].rsl_cmode == RSL_CMOD_SPD_SIGN) return l1sap_up(l1h->trx, l1sap); - l1sap_up(l1h->trx, l1sap); + } - /* generate prim */ - msg = l1sap_msgb_alloc(200); - if (!msg) - return -ENOMEM; - l1sap = msgb_l1sap_prim(msg); - osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH_RTS, - PRIM_OP_INDICATION, msg); - l1sap->u.tch.chan_nr = chan_nr; - l1sap->u.tch.fn = fn; + return rc; +} - return l1sap_up(l1h->trx, l1sap); +/* RTS for full rate traffic frame */ +static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) +{ + /* TCH/F may include FACCH on every 4th burst */ + return rts_tch_common(l1h, tn, fn, chan, 1); +} + + +/* RTS for half rate traffic frame */ +static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan) +{ + // FIXME + return 0; } @@ -732,32 +754,38 @@ send_burst: return bits; } -static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t bid) +static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch, + struct msgb **_msg_facch) { struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; - ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts; - static ubit_t bits[148]; + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + uint8_t rsl_cmode = chan_state->rsl_cmode; + uint8_t tch_mode = chan_state->tch_mode; struct osmo_phsap_prim *l1sap; - /* send burst, if we already got a frame */ - if (bid > 0) { - if (!*bursts_p) - return NULL; - goto send_burst; - } - /* handle loss detection of received TCH frames */ - if (++(l1h->chan_states[tn][chan].lost) > 5) { + if (rsl_cmode == RSL_CMOD_SPD_SPEECH + && ++(l1h->chan_states[tn][chan].lost) > 5) { uint8_t tch_data[33]; + int len; LOGP(DL1C, LOGL_NOTICE, "Missing TCH bursts detected, sending " "BFI for %s\n", trx_chan_desc[chan].name); /* indicate bad frame */ - memset(tch_data, 0, sizeof(tch_data)); - // FIXME length depends on codec - compose_tch_ind(l1h, tn, 0, chan, tch_data, 33); + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + memset(tch_data, 0, 33); + len = 33; + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " + "fix!\n"); + len = 0; + } + if (len) + compose_tch_ind(l1h, tn, 0, chan, tch_data, len); } /* get frame and unlink from queue */ @@ -797,13 +825,6 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } /* check validity of message */ - if (msg_tch && msgb_l2len(msg_tch) != 33) { - LOGP(DL1C, LOGL_FATAL, "Prim not 33 bytes, please FIX! " - "(len=%d)\n", msgb_l2len(msg_tch)); - /* free message */ - msgb_free(msg_tch); - msg_tch = NULL; - } if (msg_facch && msgb_l2len(msg_facch) != 23) { LOGP(DL1C, LOGL_FATAL, "Prim not 23 bytes, please FIX! " "(len=%d)\n", msgb_l2len(msg_facch)); @@ -812,6 +833,69 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, msg_facch = NULL; } + /* check validity of message */ + if (!msg_facch && msg_tch) { + int len; + + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) { + LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, " + "because we are not in speech mode trx=%u " + "ts=%u at fn=%u.\n", trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + len = 33; + if (msgb_l2len(msg_tch) >= 1 + && (msg_tch->l2h[0] >> 4) != 0xd) { + LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad " + "FR frame' trx=%u ts=%u at fn=%u.\n", + trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " + "fix!\n"); + goto free_bad_msg; + } + if (msgb_l2len(msg_tch) != len) { + LOGP(DL1C, LOGL_ERROR, "Cannot send payload with " + "invalid length! (expecing %d, received %d)\n", + len, msgb_l2len(msg_tch)); +free_bad_msg: + /* free message */ + msgb_free(msg_tch); + msg_tch = NULL; + goto send_frame; + } + } + +send_frame: + *_msg_tch = msg_tch; + *_msg_facch = msg_facch; +} + +static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, + enum trx_chan_type chan, uint8_t bid) +{ + struct msgb *msg_tch = NULL, *msg_facch = NULL; + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + ubit_t *burst, **bursts_p = &chan_state->dl_bursts; + static ubit_t bits[148]; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + + tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch); + /* alloc burst memory, if not already, * otherwise shift buffer by 4 bursts for interleaving */ if (!*bursts_p) { @@ -831,14 +915,6 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - /* bad frame */ - if (msg_tch && !msg_facch && (msg_tch->l2h[0] >> 4) != 0xd) { - LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad frame' trx=%u " - "ts=%u at fn=%u to transmit.\n", - trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); - goto send_burst; - } - /* encode bursts (priorize FACCH) */ if (msg_facch) tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), @@ -846,7 +922,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, else tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); - /* unlink and free message */ + /* free message */ if (msg_tch) msgb_free(msg_tch); if (msg_facch) @@ -956,7 +1032,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* send burst information to loops process */ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { - trx_loop_input(l1h, trx_chan_desc[chan].chan_nr | tn, + trx_loop_sacch_input(l1h, trx_chan_desc[chan].chan_nr | tn, chan_state, rssi, toa); } @@ -1062,7 +1138,9 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; uint8_t *mask = &chan_state->ul_mask; - uint8_t tch_data[33]; + uint8_t rsl_cmode = chan_state->rsl_cmode; + uint8_t tch_mode = chan_state->tch_mode; + uint8_t tch_data[128]; /* just to be safe */ int rc; LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", @@ -1104,25 +1182,52 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* decode * also shift buffer by 4 bursts for interleaving */ - rc = tch_fr_decode(tch_data, *bursts_p, 1); + switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 + : tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR */ + rc = tch_fr_decode(tch_data, *bursts_p, 1); + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", + tch_mode); + return -EINVAL; + } memcpy(*bursts_p, *bursts_p + 464, 464); if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " "fn=%u for %s\n", fn, trx_chan_desc[chan].name); goto bfi; } + if (rc < 4) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s with codec mode %d (out of range)\n", + fn, trx_chan_desc[chan].name, rc); + goto bfi; + } /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, tch_data, 23); bfi: - // FIXME length depends on codec - rc = 33; - /* indicate bad tch frame */ - memset(tch_data, 0, sizeof(tch_data)); + if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { + /* indicate bad frame */ + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR */ + memset(tch_data, 0, 33); + rc = 33; + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " + "please fix!\n"); + return -EINVAL; + } + } } + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + /* TCH or BFI */ return compose_tch_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, tch_data, rc); @@ -1925,7 +2030,7 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, - int downlink, int active) + int active) { uint8_t tn = L1SAP_CHAN2TS(chan_nr); int i; @@ -1942,23 +2047,15 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) && trx_chan_desc[i].link_id == link_id) { chan_state = &l1h->chan_states[tn][i]; - LOGP(DL1C, LOGL_NOTICE, "%s %s %s on trx=%d ts=%d\n", + rc = 0; + if (chan_state->active == active) + continue; + LOGP(DL1C, LOGL_NOTICE, "%s %s on trx=%d ts=%d\n", (active) ? "Activating" : "Deactivating", - (downlink) ? "downlink" : "uplink", trx_chan_desc[i].name, l1h->trx->nr, tn); - if (downlink) { - chan_state->dl_active = active; - chan_state->dl_active = active; - } else { - chan_state->ul_active = active; - chan_state->ul_active = active; - } - chan_state->lost = 0; - if (L1SAP_IS_LINK_SACCH(link_id)) { - memset(&chan_state->meas, 0, - sizeof(chan_state->meas)); - } - rc = 0; + if (active) + memset(chan_state, 0, sizeof(*chan_state)); + chan_state->active = active; } } @@ -2062,7 +2159,7 @@ static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn) /* check if channel is active */ if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].dl_active) + && !l1h->chan_states[tn][chan].active) return -EINVAL; return func(l1h, tn, fn, frame->dl_chan); @@ -2092,7 +2189,7 @@ static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn, /* check if channel is active */ if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].dl_active) + && !l1h->chan_states[tn][chan].active) goto no_data; /* get burst from function */ @@ -2159,7 +2256,7 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, /* check if channel is active */ if (!trx_chan_desc[chan].auto_active - && !l1h->chan_states[tn][chan].ul_active) + && !l1h->chan_states[tn][chan].active) goto next_frame; /* omit bursts which have no handler, like IDLE bursts */ diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index ab185f3b..6e7c5293 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -24,7 +24,7 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, - int downlink, int active); + int downlink); /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, -- cgit v1.2.3 From 917cf7018b6a9ec778add719fcec1692f0e35931 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Mar 2013 07:57:07 +0100 Subject: TRX: Add support for EFR transcoding --- src/osmo-bts-trx/gsm0503_coding.c | 131 ++++++++++++++++++++++++++++++++++++-- src/osmo-bts-trx/gsm0503_coding.h | 2 +- src/osmo-bts-trx/gsm0503_parity.c | 13 ++++ src/osmo-bts-trx/gsm0503_parity.h | 1 + src/osmo-bts-trx/gsm0503_tables.c | 12 ++++ src/osmo-bts-trx/gsm0503_tables.h | 1 + src/osmo-bts-trx/scheduler.c | 30 ++++++++- tests/bursts/bursts_test.c | 19 ++++-- 8 files changed, 194 insertions(+), 15 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 5ceb1d58..3f4ae159 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -300,7 +300,7 @@ int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len) /* - * GSM TCH/F FR transcoding + * GSM TCH/F FR/EFR transcoding */ static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits, int net_order) @@ -338,6 +338,35 @@ static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits, int net_order) } } +static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +{ + int i, j; + + tch_data[0] = 0xc << 4; + memset(tch_data + 1, 0, 30); + + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + while (i < 244) { + tch_data[j>>3] |= (b_bits[i] << (7-(j&7))); + i++; + j++; + } +} + +static void tch_efr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +{ + int i, j; + + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + while (i < 244) { + b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } +} + static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) { int i, j, k, l, o; @@ -386,6 +415,30 @@ static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) d_bits[i] = b_bits[gsm610_bitorder[i]]; } +static void tch_efr_d_to_w(ubit_t *b_bits, ubit_t *d_bits) +{ + int i; + + for (i = 0; i < 260; i++) + b_bits[gsm660_bitorder[i]] = d_bits[i]; +} + +static void tch_efr_w_to_d(ubit_t *d_bits, ubit_t *b_bits) +{ + int i; + + for (i = 0; i < 260; i++) + d_bits[i] = b_bits[gsm660_bitorder[i]]; +} + +static void tch_efr_protected(ubit_t *s_bits, ubit_t *b_bits) +{ + int i; + + for (i = 0; i < 65; i++) + b_bits[i] = s_bits[gsm0503_gsm_efr_protected_bits[i]-1]; +} + static void tch_fr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) { int i; @@ -410,10 +463,43 @@ static void tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) u[91+i] = p[i]; } -int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) +static void tch_efr_reorder(ubit_t *w, ubit_t *s, ubit_t *p) +{ + memcpy(w, s, 71); + w[71] = w[72] = s[69]; + memcpy(w+73, s+71, 50); + w[123] = w[124] = s[119]; + memcpy(w+125, s+121, 53); + w[178] = w[179] = s[172]; + memcpy(w+180, s+174, 50); + w[230] = w[231] = s[222]; + memcpy(w+232, s+224, 20); + memcpy(w+252, p, 8); +} + +static void tch_efr_unreorder(ubit_t *s, ubit_t *p, ubit_t *w) +{ + int sum; + + memcpy(s, w, 71); + sum = s[69] + w[71] + w[72]; + s[69] = (sum > 2); + memcpy(s+71, w+73, 50); + sum = s[119] + w[123] + w[124]; + s[119] = (sum > 2); + memcpy(s+121, w+125, 53); + sum = s[172] + w[178] + w[179]; + s[172] = (sum > 2); + memcpy(s+174, w+180, 50); + sum = s[220] + w[230] + w[231]; + s[222] = (sum > 2); + memcpy(s+224, w+232, 20); + memcpy(p, w+252, 8); +} +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr) { sbit_t iB[912], cB[456], h; - ubit_t conv[185], b[260], d[260], p[3]; + ubit_t conv[185], s[244], w[260], b[65], d[260], p[8]; int i, rv, len, steal = 0; for (i=0; i<8; i++) { @@ -444,11 +530,28 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) return -1; - tch_fr_d_to_b(b, d); + if (efr) { + tch_efr_d_to_w(w, d); - tch_fr_reassemble(tch_data, b, net_order); + tch_efr_unreorder(s, p, w); - len = 33; + tch_efr_protected(s, b); + + rv = osmo_crc8gen_check_bits(&gsm0503_tch_efr_crc8, b, + 65, p); + if (rv) + return -1; + + tch_efr_reassemble(tch_data, s); + + len = 31; + } else { + tch_fr_d_to_b(w, d); + + tch_fr_reassemble(tch_data, w, net_order); + + len = 33; + } return len; } @@ -456,15 +559,29 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order) int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order) { ubit_t iB[912], cB[456], h; - ubit_t conv[185], w[260], d[260], p[8]; + ubit_t conv[185], w[260], b[65], s[244], d[260], p[8]; int i; switch (len) { + case 31: /* TCH EFR */ + + tch_efr_disassemble(s, tch_data); + + tch_efr_protected(s, b); + + osmo_crc8gen_set_bits(&gsm0503_tch_efr_crc8, b, 65, p); + + tch_efr_reorder(w, s, p); + + tch_efr_w_to_d(d, w); + + goto coding_efr_fr; case 33: /* TCH FR */ tch_fr_disassemble(w, tch_data, net_order); tch_fr_b_to_d(d, w); +coding_efr_fr: osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d, 50, p); tch_fr_reorder(conv, d, p); diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 240b2c07..96b162f3 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -5,7 +5,7 @@ int xcch_decode(uint8_t *l2_data, sbit_t *bursts); int xcch_encode(ubit_t *bursts, uint8_t *l2_data); int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); -int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order); +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr); int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order); int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); diff --git a/src/osmo-bts-trx/gsm0503_parity.c b/src/osmo-bts-trx/gsm0503_parity.c index 1ecadcc7..cbb2bf0b 100644 --- a/src/osmo-bts-trx/gsm0503_parity.c +++ b/src/osmo-bts-trx/gsm0503_parity.c @@ -75,3 +75,16 @@ const struct osmo_crc8gen_code gsm0503_tch_fr_crc3 = { .remainder = 0x7, }; +/* + * GSM TCH EFR parity + * + * g(x) = x^8 + x^4 + x^3 + x^2 + 1 + */ + +const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = { + .bits = 8, + .poly = 0x1d, + .init = 0x00, + .remainder = 0x00, +}; + diff --git a/src/osmo-bts-trx/gsm0503_parity.h b/src/osmo-bts-trx/gsm0503_parity.h index 50d14bfc..e3678b22 100644 --- a/src/osmo-bts-trx/gsm0503_parity.h +++ b/src/osmo-bts-trx/gsm0503_parity.h @@ -6,5 +6,6 @@ const struct osmo_crc16gen_code gsm0503_cs234_crc16; const struct osmo_crc8gen_code gsm0503_rach_crc6; const struct osmo_crc16gen_code gsm0503_sch_crc10; const struct osmo_crc8gen_code gsm0503_tch_fr_crc3; +const struct osmo_crc8gen_code gsm0503_tch_efr_crc8; #endif /* _0503_PARITY_H */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 8ab6060b..837cc68e 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -109,3 +109,15 @@ const uint8_t gsm0503_gsm_fr_map[76] = { 3, 3, 3, 3 }; +/* this table describes the 65 most importaint bits from EFR coded + * bits as indicated in TS 05.03 (3.1.1.1) */ +const uint8_t gsm0503_gsm_efr_protected_bits[65] = { + 39, 40, 41, 42, 43, 44, 48, 87, 45, 2, + 3, 8, 10, 18, 19, 24, 46, 47,142,143, + 144,145,146,147, 92, 93,195,196, 98,137, + 148, 94,197,149,150, 95,198, 4, 5, 11, + 12, 16, 9, 6, 7, 13, 17, 20, 96,199, + 1, 14, 15, 21, 25, 26, 28,151,201,190, + 240, 88,138,191,241 +}; + diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index 827fded4..66f325bf 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -9,5 +9,6 @@ extern const sbit_t gsm0503_usf2twelve_sbit[8][12]; extern const uint8_t gsm0503_puncture_cs2[588]; extern const uint8_t gsm0503_puncture_cs3[676]; extern const uint8_t gsm0503_gsm_fr_map[76]; +extern const uint8_t gsm0503_gsm_efr_protected_bits[65]; #endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 023be372..f1a0b629 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -779,7 +779,14 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(tch_data, 0, 33); len = 33; break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + if (chan != TRXC_TCHF) + goto inval_mode1; + memset(tch_data, 0, 31); + len = 31; + break; default: +inval_mode1: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " "fix!\n"); len = 0; @@ -857,7 +864,21 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto free_bad_msg; } break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + if (chan != TRXC_TCHF) + goto inval_mode2; + len = 31; + if (msgb_l2len(msg_tch) >= 1 + && (msg_tch->l2h[0] >> 4) != 0xc) { + LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad " + "EFR frame' trx=%u ts=%u at fn=%u.\n", + trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + break; default: +inval_mode2: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " "fix!\n"); goto free_bad_msg; @@ -1185,7 +1206,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 : tch_mode) { case GSM48_CMODE_SPEECH_V1: /* FR */ - rc = tch_fr_decode(tch_data, *bursts_p, 1); + rc = tch_fr_decode(tch_data, *bursts_p, 1, 0); + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + rc = tch_fr_decode(tch_data, *bursts_p, 1, 1); break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", @@ -1217,6 +1241,10 @@ bfi: memset(tch_data, 0, 33); rc = 33; break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + memset(tch_data, 0, 31); + rc = 31; + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " "please fix!\n"); diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index e04899cc..935cbc6d 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -243,7 +243,7 @@ static void test_fr(uint8_t *speech, int len) printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 812, 57)); /* decode */ - rc = tch_fr_decode(result, bursts_s, 1); + rc = tch_fr_decode(result, bursts_s, 1, len == 31); ASSERT_TRUE(rc == len); @@ -349,7 +349,8 @@ uint8_t test_macblock[][54] = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, }; -uint8_t test_speech[33]; +uint8_t test_speech_fr[33]; +uint8_t test_speech_efr[31]; int main(int argc, char **argv) { @@ -367,10 +368,16 @@ int main(int argc, char **argv) for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_sch(test_l2[i]); - for (i = 0; i < sizeof(test_speech); i++) - test_speech[i] = i; - test_speech[0] = 0xd0; - test_fr(test_speech, sizeof(test_speech)); + for (i = 0; i < sizeof(test_speech_fr); i++) + test_speech_fr[i] = i; + test_speech_fr[0] = 0xd0; + test_fr(test_speech_fr, sizeof(test_speech_fr)); + + for (i = 0; i < sizeof(test_speech_efr); i++) + test_speech_efr[i] = i; + test_speech_efr[0] = 0xc0; + test_fr(test_speech_efr, sizeof(test_speech_efr)); + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_fr(test_l2[i], sizeof(test_l2[0])); -- cgit v1.2.3 From 5e2341411f25f4faa66ff9b4506e77d897b97daf Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 16 Mar 2013 16:46:13 +0100 Subject: Get RSSI from received uplink data and send to PCU --- src/common/pcu_sock.c | 1 + src/osmo-bts-sysmo/l1_if.c | 5 ++++- src/osmo-bts-trx/l1_if.h | 4 ++++ src/osmo-bts-trx/scheduler.c | 28 +++++++++++++++++++++------- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c index a4ca25f1..577cfcfa 100644 --- a/src/common/pcu_sock.c +++ b/src/common/pcu_sock.c @@ -330,6 +330,7 @@ int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn, data_ind = &pcu_prim->u.data_ind; data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH; + data_ind->rssi = rssi; data_ind->fn = fn; data_ind->arfcn = arfcn; data_ind->trx_nr = ts->trx->nr; diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c index e2ad500f..12749dd3 100644 --- a/src/osmo-bts-sysmo/l1_if.c +++ b/src/osmo-bts-sysmo/l1_if.c @@ -841,6 +841,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i uint32_t fn; uint8_t *data, len; int rc = 0; + int8_t rssi; chan_nr = chan_nr_by_sapi(trx->ts[data_ind->u8Tn].pchan, data_ind->sapi, data_ind->subCh, data_ind->u8Tn, data_ind->u32Fn); @@ -877,6 +878,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i return rc; } + /* get rssi */ + rssi = (int8_t) (data_ind->measParam.fRssi); /* get data pointer and length */ data = data_ind->msgUnitParam.u8Buffer; len = data_ind->msgUnitParam.u8Size; @@ -893,10 +896,10 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i l1sap = msgb_l1sap_prim(l1p_msg); osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION, l1p_msg); - l1sap->u.data.rssi = data_ind->measParam.fRssi; l1sap->u.data.link_id = link_id; l1sap->u.data.chan_nr = chan_nr; l1sap->u.data.fn = fn; + l1sap->u.data.rssi = rssi; return l1sap_up(trx, l1sap); } diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 459496f6..ff5c3913 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -55,6 +55,10 @@ struct trx_chan_state { uint32_t ul_first_fn; /* fn of first burst */ uint8_t ul_mask; /* mask of received bursts */ + /* RSSI */ + uint8_t rssi_num; /* number of RSSI values */ + float rssi_sum; /* sum of RSSI values */ + /* loss detection */ uint8_t lost; /* (SACCH) loss detection */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f1a0b629..6a40f97d 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -556,7 +556,7 @@ found_msg: } static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len) + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; @@ -569,6 +569,7 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l1sap->u.data.chan_nr = trx_chan_desc[chan].chan_nr | tn; l1sap->u.data.link_id = trx_chan_desc[chan].link_id; l1sap->u.data.fn = fn; + l1sap->u.data.rssi = (int8_t) (rssi); msg->l2h = msgb_put(msg, l2_len); if (l2_len) memcpy(msg->l2h, l2, l2_len); @@ -653,7 +654,7 @@ got_msg: if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { /* count and send BFI */ if (++(l1h->chan_states[tn][chan].lost) > 1) - compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0); + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -128); } /* alloc burst memory, if not already */ @@ -1023,6 +1024,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, sbit_t *burst, **bursts_p = &chan_state->ul_bursts; uint32_t *first_fn = &chan_state->ul_first_fn; uint8_t *mask = &chan_state->ul_mask; + float *rssi_sum = &chan_state->rssi_sum; + uint8_t *rssi_num = &chan_state->rssi_num; uint8_t l2[23], l2_len; int rc; @@ -1041,10 +1044,14 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(*bursts_p, 0, 464); *mask = 0x0; *first_fn = fn; + *rssi_sum = 0; + *rssi_num = 0; } - /* update mask */ + /* update mask + rssi */ *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -1087,7 +1094,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } else l2_len = 23; - return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len); + return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, + *rssi_sum / *rssi_num); } static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1097,6 +1105,8 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; sbit_t *burst, **bursts_p = &chan_state->ul_bursts; uint8_t *mask = &chan_state->ul_mask; + float *rssi_sum = &chan_state->rssi_sum; + uint8_t *rssi_num = &chan_state->rssi_num; uint8_t l2[54+1]; int rc; @@ -1114,10 +1124,14 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (bid == 0) { memset(*bursts_p, 0, 464); *mask = 0x0; + *rssi_sum = 0; + *rssi_num = 0; } - /* update mask */ + /* update mask + rssi */ *mask |= (1 << bid); + *rssi_sum += rssi; + (*rssi_num)++; /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -1149,7 +1163,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, - l2, rc + 1); + l2, rc + 1, *rssi_sum / *rssi_num); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1232,7 +1246,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data, 23); + tch_data, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ -- cgit v1.2.3 From a7f5e077121f857e373b6e68c5fdf803933a12dc Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 26 Mar 2013 09:05:14 +0100 Subject: TRX: Support for AMR full speech --- src/osmo-bts-trx/Makefile.am | 4 +- src/osmo-bts-trx/gsm0503_coding.c | 479 +++++++++++++++++++++++++++++-- src/osmo-bts-trx/gsm0503_coding.h | 5 + src/osmo-bts-trx/gsm0503_conv.c | 484 ++++++++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_conv.h | 8 + src/osmo-bts-trx/gsm0503_interleaving.c | 2 +- src/osmo-bts-trx/gsm0503_parity.c | 13 + src/osmo-bts-trx/gsm0503_parity.h | 1 + src/osmo-bts-trx/gsm0503_tables.c | 15 + src/osmo-bts-trx/gsm0503_tables.h | 2 + src/osmo-bts-trx/l1_if.c | 17 +- src/osmo-bts-trx/l1_if.h | 11 + src/osmo-bts-trx/loops.c | 97 +++++++ src/osmo-bts-trx/loops.h | 5 + src/osmo-bts-trx/scheduler.c | 139 ++++++++- src/osmo-bts-trx/scheduler.h | 5 +- 16 files changed, 1250 insertions(+), 37 deletions(-) diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index d9ddca1f..05056cef 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -2,10 +2,10 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp -EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h +EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h amr.h bin_PROGRAMS = osmobts-trx -osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c +osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c amr.c osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 3f4ae159..0fb07283 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -338,6 +338,37 @@ static void tch_fr_reassemble(uint8_t *tch_data, ubit_t *b_bits, int net_order) } } +static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) +{ + int i, j, k, l, o; + + if (net_order) { + i = 0; /* counts bits */ + j = 4; /* counts output bits */ + while (i < 260) { + b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } + return; + } + + i = 0; /* counts bits */ + j = 4; /* counts input bits */ + k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ + l = 0; /* counts element bits */ + o = 0; /* offset output bits */ + while (i < 260) { + b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; + if (--k < 0) { + o += gsm0503_gsm_fr_map[l]; + k = gsm0503_gsm_fr_map[++l]-1; + } + i++; + j++; + } +} + static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) { int i, j; @@ -367,36 +398,32 @@ static void tch_efr_disassemble(ubit_t *b_bits, uint8_t *tch_data) } } -static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) +static void tch_amr_reassemble(uint8_t *tch_data, ubit_t *d_bits, int len) { - int i, j, k, l, o; + int i, j; - if (net_order) { - i = 0; /* counts bits */ - j = 4; /* counts output bits */ - while (i < 260) { - b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; - i++; - j++; - } - return; - } + memset(tch_data, 0, (len + 7) >> 3); i = 0; /* counts bits */ - j = 4; /* counts input bits */ - k = gsm0503_gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset output bits */ - while (i < 260) { - b_bits[k+o] = (tch_data[j>>3] >> (7-(j&7))) & 1; - if (--k < 0) { - o += gsm0503_gsm_fr_map[l]; - k = gsm0503_gsm_fr_map[++l]-1; - } + j = 0; /* counts output bits */ + while (i < len) { + tch_data[j>>3] |= (d_bits[i] << (7-(j&7))); i++; j++; } +} +static void tch_amr_disassemble(ubit_t *d_bits, uint8_t *tch_data, int len) +{ + int i, j; + + i = 0; /* counts bits */ + j = 0; /* counts output bits */ + while (i < len) { + d_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } } static void tch_fr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) @@ -496,6 +523,21 @@ static void tch_efr_unreorder(ubit_t *s, ubit_t *p, ubit_t *w) memcpy(s+224, w+232, 20); memcpy(p, w+252, 8); } + +static void tch_amr_merge(ubit_t *u, ubit_t *d, ubit_t *p, int len, int prot) +{ + memcpy(u, d, prot); + memcpy(u+prot, p, 6); + memcpy(u+prot+6, d+prot, len-prot); +} + +static void tch_amr_unmerge(ubit_t *d, ubit_t *p, ubit_t *u, int len, int prot) +{ + memcpy(d, u, prot); + memcpy(p, u+prot, 6); + memcpy(d+prot, u+prot+6, len-prot); +} + int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr) { sbit_t iB[912], cB[456], h; @@ -611,6 +653,399 @@ coding_efr_fr: return 0; } +static float amr_calc_ber(sbit_t *orig, ubit_t *test, int len) +{ + int i, err = 0; + + /* count number of wrong bits (sbits with 0-value are omitted) */ + for (i=0; i 0 && (*test)) + err++; + else if ((*orig) < 0 && !(*test)) + err++; + orig++; + test++; + } + + return (float)err / (float)len; +} + +int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, + uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, float *ber) +{ + sbit_t iB[912], cB[456], h; + ubit_t test[456], d[244], p[6], conv[250]; + int i, j, k, best = 0, rv, len, steal = 0, id = 0; + + for (i=0; i<8; i++) { + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, + i>>2); + steal -= h; + } + + gsm0503_tch_fr_deinterleave(cB, iB); + + if (steal > 0) { + rv = _xcch_decode_cB(tch_data, cB); + if (rv) + return -1; + + return 23; + } + + for (i=0; i<4; i++) { + for (j=0, k=0; j<8; j++) + k += abs(((int)gsm0503_afs_ic_sbit[i][j]) - + ((int)cB[j])); + if (i == 0 || k < best) { + best = k; + id = i; + } + } + + /* check if indicated codec fits into range of codecs */ + if (id >= codecs) { + /* codec mode out of range, return id */ + return id; + } + + switch ((codec_mode_req) ? codec[*ft] : codec[id]) { + case 7: /* TCH/AFS12.2 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_12_2, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 244, 81); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 81, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 244); + + len = 31; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_12_2, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 6: /* TCH/AFS10.2 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_10_2, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 204, 65); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 65, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 204); + + len = 26; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_10_2, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 5: /* TCH/AFS7.95 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_7_95, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 159, 75); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 75, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 159); + + len = 20; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_7_95, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 4: /* TCH/AFS7.4 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_7_4, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 148, 61); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 148); + + len = 19; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_7_4, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 3: /* TCH/AFS6.7 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_6_7, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 134, 55); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 134); + + len = 17; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_6_7, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 2: /* TCH/AFS5.9 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_5_9, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 118, 55); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 118); + + len = 15; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_5_9, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 1: /* TCH/AFS5.15 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_5_15, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 103, 49); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 103); + + len = 13; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_5_15, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + case 0: /* TCH/AFS4.75 */ + osmo_conv_decode(&gsm0503_conv_tch_afs_4_75, cB+8, conv); + + tch_amr_unmerge(d, p, conv, 95, 39); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p); + if (rv) + return -1; + + tch_amr_reassemble(tch_data, d, 95); + + len = 12; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_afs_4_75, conv, + test+8); + *ber = amr_calc_ber(cB+8, test+8, 448); + } + + break; + default: + fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); + + return -1; + } + + /* change codec request / indication, if frame is valid */ + if (codec_mode_req) + *cmr = id; + else + *ft = id; + + return len; +} + +int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, + uint8_t cmr) +{ + ubit_t iB[912], cB[456], h; + ubit_t d[244], p[6], conv[250]; + int i; + uint8_t id; + + if (len == 23) { /* FACCH */ + _xcch_encode_cB(cB, tch_data); + + h = 1; + + goto facch; + } + + h = 0; + + if (codec_mode_req) { + if (cmr >= codecs) { + fprintf(stderr, "FIXME: CMR ID %d not in codec list!\n", + cmr); + return -1; + } + id = cmr; + } else { + if (ft >= codecs) { + fprintf(stderr, "FIXME: FT ID %d not in codec list!\n", + ft); + return -1; + } + id = ft; + } + + switch (codec[ft]) { + case 7: /* TCH/AFS12.2 */ + if (len != 31) { +invalid_length: + fprintf(stderr, "FIXME: payload length %d does not " + "comply with codec type %d!\n", len, ft); + return -1; + } + + tch_amr_disassemble(d, tch_data, 244); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 81, p); + + tch_amr_merge(conv, d, p, 244, 81); + + osmo_conv_encode(&gsm0503_conv_tch_afs_12_2, conv, cB+8); + + break; + case 6: /* TCH/AFS10.2 */ + if (len != 26) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 204); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 65, p); + + tch_amr_merge(conv, d, p, 204, 65); + + osmo_conv_encode(&gsm0503_conv_tch_afs_10_2, conv, cB+8); + + break; + case 5: /* TCH/AFS7.95 */ + if (len != 20) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 159); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 75, p); + + tch_amr_merge(conv, d, p, 159, 75); + + osmo_conv_encode(&gsm0503_conv_tch_afs_7_95, conv, cB+8); + + break; + case 4: /* TCH/AFS7.4 */ + if (len != 19) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 148); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p); + + tch_amr_merge(conv, d, p, 148, 61); + + osmo_conv_encode(&gsm0503_conv_tch_afs_7_4, conv, cB+8); + + break; + case 3: /* TCH/AFS6.7 */ + if (len != 17) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 134); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p); + + tch_amr_merge(conv, d, p, 134, 55); + + osmo_conv_encode(&gsm0503_conv_tch_afs_6_7, conv, cB+8); + + break; + case 2: /* TCH/AFS5.9 */ + if (len != 15) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 118); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p); + + tch_amr_merge(conv, d, p, 118, 55); + + osmo_conv_encode(&gsm0503_conv_tch_afs_5_9, conv, cB+8); + + break; + case 1: /* TCH/AFS5.15 */ + if (len != 13) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 103); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p); + + tch_amr_merge(conv, d, p, 103, 49); + + osmo_conv_encode(&gsm0503_conv_tch_afs_5_15, conv, cB+8); + + break; + case 0: /* TCH/AFS4.75 */ + if (len != 12) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 95); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p); + + tch_amr_merge(conv, d, p, 95, 39); + + osmo_conv_encode(&gsm0503_conv_tch_afs_4_75, conv, cB+8); + + break; + default: + fprintf(stderr, "FIXME: FT %d not supported!\n", ft); + + return -1; + } + + memcpy(cB, gsm0503_afs_ic_ubit[id], 8); + +facch: + gsm0503_tch_fr_interleave(cB, iB); + + for (i=0; i<8; i++) + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>2); + + return 0; +} + /* * GSM RACH transcoding */ diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 96b162f3..97da7baf 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -7,6 +7,11 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr); int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order); +int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, + uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, float *ber); +int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, + uint8_t cmr); int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); int sch_decode(uint8_t *sb_info, sbit_t *burst); diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index f9fcaa82..4b95f72c 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -81,3 +81,487 @@ const struct osmo_conv_code gsm0503_conv_tch_fr = { }; +/* TCH/AFS12.2 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_12_2_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_afs_12_2_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 }, + { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_12_2_next_term_output[] = { + 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1, +}; + +static const uint8_t conv_tch_afs_12_2_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_12_2_puncture[] = { + 321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363, + 365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401, + 405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441, + 443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477, + 481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_12_2 = { + .N = 2, + .K = 5, + .len = 250, + .next_output = conv_tch_afs_12_2_next_output, + .next_state = conv_tch_afs_12_2_next_state, + .next_term_output = conv_tch_afs_12_2_next_term_output, + .next_term_state = conv_tch_afs_12_2_next_term_state, + .puncture = conv_tch_afs_12_2_puncture, +}; + + +/* TCH/AFS10.2 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_10_2_next_output[][2] = { + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, +}; + +static const uint8_t conv_tch_afs_10_2_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_10_2_next_term_output[] = { + 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4, +}; + +static const uint8_t conv_tch_afs_10_2_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_10_2_puncture[] = { + 1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43, + 46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91, + 94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139, + 142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187, + 190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235, + 238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283, + 286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328, + 331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364, + 367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400, + 403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436, + 439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472, + 475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508, + 511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544, + 547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580, + 583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613, + 616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637, + 639, 640, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_10_2 = { + .N = 3, + .K = 5, + .len = 210, + .next_output = conv_tch_afs_10_2_next_output, + .next_state = conv_tch_afs_10_2_next_state, + .next_term_output = conv_tch_afs_10_2_next_term_output, + .next_term_state = conv_tch_afs_10_2_next_term_state, + .puncture = conv_tch_afs_10_2_puncture, +}; + + +/* TCH/AFS7.95 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_7_95_next_output[][2] = { + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, +}; + +static const uint8_t conv_tch_afs_7_95_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 }, + { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 }, + { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 }, + { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 }, + { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 }, + { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 }, + { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 }, + { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 }, + { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 }, + { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 }, +}; + +static const uint8_t conv_tch_afs_7_95_next_term_output[] = { + 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0, + 4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4, + 7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7, + 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3, +}; + +static const uint8_t conv_tch_afs_7_95_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_afs_7_95_puncture[] = { + 1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310, + 317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367, + 373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415, + 421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463, + 469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505, + 506, 508, 509, 511, 512, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_7_95 = { + .N = 3, + .K = 7, + .len = 165, + .next_output = conv_tch_afs_7_95_next_output, + .next_state = conv_tch_afs_7_95_next_state, + .next_term_output = conv_tch_afs_7_95_next_term_output, + .next_term_state = conv_tch_afs_7_95_next_term_state, + .puncture = conv_tch_afs_7_95_puncture, +}; + + +/* TCH/AFS7.4 */ +/* ---------- */ + +static const uint8_t conv_tch_afs_7_4_next_output[][2] = { + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, +}; + +static const uint8_t conv_tch_afs_7_4_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_7_4_next_term_output[] = { + 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4, +}; + +static const uint8_t conv_tch_afs_7_4_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_7_4_puncture[] = { + 0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415, + 421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469, + 471, 472, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_7_4 = { + .N = 3, + .K = 5, + .len = 154, + .next_output = conv_tch_afs_7_4_next_output, + .next_state = conv_tch_afs_7_4_next_state, + .next_term_output = conv_tch_afs_7_4_next_term_output, + .next_term_state = conv_tch_afs_7_4_next_term_state, + .puncture = conv_tch_afs_7_4_puncture, +}; + + +/* TCH/AFS6.7 */ +/* ---------- */ + +static const uint8_t conv_tch_afs_6_7_next_output[][2] = { + { 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 }, + { 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 }, + { 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 }, + { 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 }, +}; + +static const uint8_t conv_tch_afs_6_7_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static int conv_tch_afs_6_7_puncture[] = { + 1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107, + 119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267, + 279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327, + 331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371, + 375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403, + 407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435, + 439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467, + 471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499, + 503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531, + 535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559, + 561, 563, 565, 567, 569, 571, 573, 575, + -1, /* end */ +}; + +static const uint8_t conv_tch_afs_6_7_next_term_output[] = { + 0, 11, 7, 12, 11, 0, 12, 7, 15, 4, 8, 3, 4, 15, 3, 8, +}; + +static const uint8_t conv_tch_afs_6_7_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_6_7 = { + .N = 4, + .K = 5, + .len = 140, + .next_output = conv_tch_afs_6_7_next_output, + .next_state = conv_tch_afs_6_7_next_state, + .next_term_output = conv_tch_afs_6_7_next_term_output, + .next_term_state = conv_tch_afs_6_7_next_term_state, + .puncture = conv_tch_afs_6_7_puncture, +}; + + +/* TCH/AFS5.9 */ +/* ---------- */ + +static const uint8_t conv_tch_afs_5_9_next_output[][2] = { + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 }, + { 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 }, + { 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 }, + { 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 }, +}; + +static const uint8_t conv_tch_afs_5_9_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 }, + { 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 }, + { 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 }, + { 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 }, + { 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 }, + { 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, + { 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 }, + { 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 }, + { 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 }, + { 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 }, + { 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 }, + { 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 }, +}; + +static const uint8_t conv_tch_afs_5_9_next_term_output[] = { + 0, 7, 11, 12, 11, 12, 0, 7, 7, 0, 12, 11, 12, 11, 7, 0, + 8, 15, 3, 4, 3, 4, 8, 15, 15, 8, 4, 3, 4, 3, 15, 8, + 15, 8, 4, 3, 4, 3, 15, 8, 8, 15, 3, 4, 3, 4, 8, 15, + 7, 0, 12, 11, 12, 11, 7, 0, 0, 7, 11, 12, 11, 12, 0, 7, +}; + +static const uint8_t conv_tch_afs_5_9_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_afs_5_9_puncture[] = { + 0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95, + 111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287, + 303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375, + 379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439, + 443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491, + 495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_5_9 = { + .N = 4, + .K = 7, + .len = 124, + .next_output = conv_tch_afs_5_9_next_output, + .next_state = conv_tch_afs_5_9_next_state, + .next_term_output = conv_tch_afs_5_9_next_term_output, + .next_term_state = conv_tch_afs_5_9_next_term_state, + .puncture = conv_tch_afs_5_9_puncture, +}; + + +/* TCH/AFS5.15 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_5_15_next_output[][2] = { + { 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 }, + { 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 }, + { 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 }, + { 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 }, +}; + +static const uint8_t conv_tch_afs_5_15_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_afs_5_15_next_term_output[] = { + 0, 27, 7, 28, 27, 0, 28, 7, 31, 4, 24, 3, 4, 31, 3, 24, +}; + +static const uint8_t conv_tch_afs_5_15_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_afs_5_15_puncture[] = { + 0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40, + 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, + 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, + 290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345, + 350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385, + 390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425, + 430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465, + 470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505, + 510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540, + 544, 545, 549, 550, 554, 555, 559, 560, 564, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_5_15 = { + .N = 5, + .K = 5, + .len = 109, + .next_output = conv_tch_afs_5_15_next_output, + .next_state = conv_tch_afs_5_15_next_state, + .next_term_output = conv_tch_afs_5_15_next_term_output, + .next_term_state = conv_tch_afs_5_15_next_term_state, + .puncture = conv_tch_afs_5_15_puncture, +}; + + +/* TCH/AFS4.75 */ +/* ----------- */ + +static const uint8_t conv_tch_afs_4_75_next_output[][2] = { + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 }, + { 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 }, + { 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 }, + { 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 }, +}; + +static const uint8_t conv_tch_afs_4_75_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 }, + { 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 }, + { 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 }, + { 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 }, + { 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 }, + { 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, + { 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 }, + { 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 }, + { 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 }, + { 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 }, + { 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 }, + { 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 }, +}; + +static const uint8_t conv_tch_afs_4_75_next_term_output[] = { + 0, 7, 27, 28, 27, 28, 0, 7, 7, 0, 28, 27, 28, 27, 7, 0, + 24, 31, 3, 4, 3, 4, 24, 31, 31, 24, 4, 3, 4, 3, 31, 24, + 31, 24, 4, 3, 4, 3, 31, 24, 24, 31, 3, 4, 3, 4, 24, 31, + 7, 0, 28, 27, 28, 27, 7, 0, 0, 7, 27, 28, 27, 28, 0, 7, +}; + +static const uint8_t conv_tch_afs_4_75_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_afs_4_75_puncture[] = { + 0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55, + 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175, + 185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295, + 305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405, + 410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460, + 465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509, + 510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530, + 531, 532, 534, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_afs_4_75 = { + .N = 5, + .K = 7, + .len = 101, + .next_output = conv_tch_afs_4_75_next_output, + .next_state = conv_tch_afs_4_75_next_state, + .next_term_output = conv_tch_afs_4_75_next_term_output, + .next_term_state = conv_tch_afs_4_75_next_term_state, + .puncture = conv_tch_afs_4_75_puncture, +}; + + diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h index a73d24ba..7e0121c9 100644 --- a/src/osmo-bts-trx/gsm0503_conv.h +++ b/src/osmo-bts-trx/gsm0503_conv.h @@ -7,5 +7,13 @@ extern const struct osmo_conv_code gsm0503_conv_cs3; extern const struct osmo_conv_code gsm0503_conv_rach; extern const struct osmo_conv_code gsm0503_conv_sch; extern const struct osmo_conv_code gsm0503_conv_tch_fr; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_12_2; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_10_2; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_95; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_4; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_6_7; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_9; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_15; +extern const struct osmo_conv_code gsm0503_conv_tch_afs_4_75; #endif /* _0503_CONV_H */ diff --git a/src/osmo-bts-trx/gsm0503_interleaving.c b/src/osmo-bts-trx/gsm0503_interleaving.c index 7a721b57..014b4c9c 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.c +++ b/src/osmo-bts-trx/gsm0503_interleaving.c @@ -51,7 +51,7 @@ void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB) } /* - * GSM TCH FR/EFR interleaving and burst mapping + * GSM TCH FR/EFR/AFS interleaving and burst mapping * * Interleaving: * diff --git a/src/osmo-bts-trx/gsm0503_parity.c b/src/osmo-bts-trx/gsm0503_parity.c index cbb2bf0b..0c5f77cf 100644 --- a/src/osmo-bts-trx/gsm0503_parity.c +++ b/src/osmo-bts-trx/gsm0503_parity.c @@ -88,3 +88,16 @@ const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = { .remainder = 0x00, }; +/* + * GSM AMR parity + * + * g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1 + */ + +const struct osmo_crc8gen_code gsm0503_amr_crc6 = { + .bits = 6, + .poly = 0x2f, + .init = 0x00, + .remainder = 0x3f, +}; + diff --git a/src/osmo-bts-trx/gsm0503_parity.h b/src/osmo-bts-trx/gsm0503_parity.h index e3678b22..ee52328b 100644 --- a/src/osmo-bts-trx/gsm0503_parity.h +++ b/src/osmo-bts-trx/gsm0503_parity.h @@ -7,5 +7,6 @@ const struct osmo_crc8gen_code gsm0503_rach_crc6; const struct osmo_crc16gen_code gsm0503_sch_crc10; const struct osmo_crc8gen_code gsm0503_tch_fr_crc3; const struct osmo_crc8gen_code gsm0503_tch_efr_crc8; +const struct osmo_crc8gen_code gsm0503_amr_crc6; #endif /* _0503_PARITY_H */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 837cc68e..4c5ab7a1 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -121,3 +121,18 @@ const uint8_t gsm0503_gsm_efr_protected_bits[65] = { 240, 88,138,191,241 }; +/* Encoded in-band data for speech frames */ +const ubit_t gsm0503_afs_ic_ubit[4][8] = { + { 0,0,0,0,0,0,0,0 }, + { 0,1,0,1,1,1,0,1 }, + { 1,0,1,1,1,0,1,0 }, + { 1,1,1,0,0,1,1,1 }, +}; + +const sbit_t gsm0503_afs_ic_sbit[4][8] = { + { 127, 127, 127, 127, 127, 127, 127, 127 }, + { 127,-127, 127,-127,-127,-127, 127,-127 }, + { -127, 127,-127,-127,-127, 127,-127, 127 }, + { -127,-127,-127, 127, 127,-127,-127,-127 }, +}; + diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index 66f325bf..db0e920a 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -10,5 +10,7 @@ extern const uint8_t gsm0503_puncture_cs2[588]; extern const uint8_t gsm0503_puncture_cs3[676]; extern const uint8_t gsm0503_gsm_fr_map[76]; extern const uint8_t gsm0503_gsm_efr_protected_bits[65]; +extern const ubit_t gsm0503_afs_ic_ubit[4][8]; +extern const sbit_t gsm0503_afs_ic_sbit[4][8]; #endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 61c02cd4..9dbec31b 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "l1_if.h" #include "trx_if.h" @@ -476,7 +477,13 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) trx_sched_set_lchan(l1h, chan_nr, 0x40, 1); /* set mode */ trx_sched_set_mode(l1h, chan_nr, - lchan->rsl_cmode, lchan->tch_mode); + lchan->rsl_cmode, lchan->tch_mode, + lchan->tch.amr_mr.num_modes, + lchan->tch.amr_mr.mode[0].mode, + lchan->tch.amr_mr.mode[1].mode, + lchan->tch.amr_mr.mode[2].mode, + lchan->tch.amr_mr.mode[3].mode, + amr_get_initial_mode(lchan)); /* init lapdm */ lchan_init_lapdm(lchan); /* confirm */ @@ -487,7 +494,13 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) if (l1sap->u.info.type == PRIM_INFO_MODIFY) { /* change mode */ trx_sched_set_mode(l1h, chan_nr, - lchan->rsl_cmode, lchan->tch_mode); + lchan->rsl_cmode, lchan->tch_mode, + lchan->tch.amr_mr.num_modes, + lchan->tch.amr_mr.mode[0].mode, + lchan->tch.amr_mr.mode[1].mode, + lchan->tch.amr_mr.mode[2].mode, + lchan->tch.amr_mr.mode[3].mode, + amr_get_initial_mode(lchan)); break; } if ((chan_nr & 0x80)) { diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index ff5c3913..9cc8ee3b 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -65,6 +65,17 @@ struct trx_chan_state { /* mode */ uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */ + /* AMR */ + uint8_t codec[4]; /* 4 possible codecs for amr */ + int codecs; /* number of possible codecs */ + float ber_sum; /* sum of bit error rates */ + int ber_num; /* number of bit error rates */ + uint8_t ul_ft; /* current uplink FT index */ + uint8_t dl_ft; /* current downlink FT index */ + uint8_t ul_cmr; /* current uplink CMR index */ + uint8_t dl_cmr; /* current downlink CMR index */ + uint8_t amr_loop; /* if AMR loop is enabled */ + /* encryption */ int ul_encr_algo; /* A5/x encry algo downlink */ int dl_encr_algo; /* A5/x encry algo uplink */ diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c index ee12e8bd..8b6844be 100644 --- a/src/osmo-bts-trx/loops.c +++ b/src/osmo-bts-trx/loops.c @@ -245,3 +245,100 @@ int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, return 0; } +int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state, float ber) +{ + struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)] + .lchan[l1sap_chan2ss(chan_nr)]; + int c_i; + + /* check if loop is enabled */ + if (!chan_state->amr_loop) + return 0; + + /* wait for MS to use the requested codec */ + if (chan_state->ul_ft != chan_state->dl_cmr) + return 0; + + /* count bit errors */ + if (L1SAP_IS_CHAN_TCHH(chan_nr)) { + chan_state->ber_num += 2; + chan_state->ber_sum += (ber + ber); + } else { + chan_state->ber_num++; + chan_state->ber_sum += ber; + } + + /* count frames */ + if (chan_state->ber_num < 48) + return 0; + + /* calculate average (reuse ber variable) */ + ber = chan_state->ber_sum / chan_state->ber_num; + + /* FIXME: calculate C/I from BER */ + c_i = ber * 100; + + /* reset bit errors */ + chan_state->ber_num = 0; + chan_state->ber_sum = 0; + + LOGP(DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f " + "codec id %d of trx=%u chan_nr=0x%02x\n", ber, + chan_state->ul_ft, l1h->trx->nr, chan_nr); + + /* degrade */ + if (chan_state->dl_cmr > 0) { + /* degrade, if ber is above threshold FIXME: C/I */ + if (ber > + lchan->tch.amr_mr.mode[chan_state->dl_cmr-1].threshold_bts) { + LOGP(DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f " + "from codec id %d to %d of trx=%u " + "chan_nr=0x%02x\n", ber, chan_state->dl_cmr, + chan_state->dl_cmr - 1, l1h->trx->nr, chan_nr); + chan_state->dl_cmr--; + } + + return 0; + } + + /* upgrade */ + if (chan_state->dl_cmr < chan_state->codecs - 1) { + /* degrade, if ber is above threshold FIXME: C/I*/ + if (ber < + lchan->tch.amr_mr.mode[chan_state->dl_cmr].threshold_bts + - lchan->tch.amr_mr.mode[chan_state->dl_cmr].hysteresis_bts) { + LOGP(DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f " + "from codec id %d to %d of trx=%u " + "chan_nr=0x%02x\n", ber, chan_state->dl_cmr, + chan_state->dl_cmr + 1, l1h->trx->nr, chan_nr); + chan_state->dl_cmr++; + } + + return 0; + } + + return 0; +} + +int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop) +{ + if (chan_state->amr_loop && !loop) { + chan_state->amr_loop = 0; + + return 0; + } + + if (!chan_state->amr_loop && loop) { + chan_state->amr_loop = 1; + + /* reset bit errors */ + chan_state->ber_num = 0; + chan_state->ber_sum = 0; + + return 0; + } + + return 0; +} + diff --git a/src/osmo-bts-trx/loops.h b/src/osmo-bts-trx/loops.h index 8b1d973d..27b0ef23 100644 --- a/src/osmo-bts-trx/loops.h +++ b/src/osmo-bts-trx/loops.h @@ -23,4 +23,9 @@ int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr, int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr, struct trx_chan_state *chan_state); +int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr, + struct trx_chan_state *chan_state, float ber); + +int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop); + #endif /* _TRX_LOOPS_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6a40f97d..eb6130eb 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -33,12 +33,15 @@ #include #include #include +#include #include "l1_if.h" #include "scheduler.h" #include "gsm0503_coding.h" #include "trx_if.h" #include "loops.h" +#include "amr.h" +#include "loops.h" /* Enable this to multiply TOA of RACH by 10. * This usefull to check tenth of timing advances with RSSI test tool. @@ -757,7 +760,7 @@ send_burst: static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch, - struct msgb **_msg_facch) + struct msgb **_msg_facch, int codec_mode_request) { struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL; struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; @@ -786,6 +789,15 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(tch_data, 0, 31); len = 31; break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + len = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], 1); + if (len < 2) + break; + memset(tch_data + 2, 0, len - 2); + compose_tch_ind(l1h, tn, 0, chan, tch_data, len); + break; default: inval_mode1: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " @@ -841,9 +853,11 @@ inval_mode1: msg_facch = NULL; } - /* check validity of message */ + /* check validity of message, get AMR ft and cmr */ if (!msg_facch && msg_tch) { int len; + uint8_t bfi, cmr_codec, ft_codec; + int cmr, ft, i; if (rsl_cmode != RSL_CMOD_SPD_SPEECH) { LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, " @@ -878,12 +892,62 @@ inval_mode1: goto free_bad_msg; } break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + len = amr_decompose_payload(msg_tch->l2h, + msgb_l2len(msg_tch), &cmr_codec, &ft_codec, + &bfi); + cmr = -1; + ft = -1; + for (i = 0; i < chan_state->codecs; i++) { + if (chan_state->codec[i] == cmr_codec) + cmr = i; + if (chan_state->codec[i] == ft_codec) + ft = i; + } + if (cmr >= 0) { /* new request */ + chan_state->dl_cmr = cmr; + /* disable AMR loop */ + trx_loop_amr_set(chan_state, 0); + } else { + /* enable AMR loop */ + trx_loop_amr_set(chan_state, 1); + } + if (ft < 0) { + LOGP(DL1C, LOGL_ERROR, "%s Codec (FT = %d) " + " of RTP frame not in list. " + "trx=%u ts=%u\n", + trx_chan_desc[chan].name, ft_codec, + l1h->trx->nr, tn); + goto free_bad_msg; + } + if (codec_mode_request && chan_state->dl_ft != ft) { + LOGP(DL1C, LOGL_NOTICE, "%s Codec (FT = %d) " + " of RTP cannot be changed now, but in " + "next frame. trx=%u ts=%u\n", + trx_chan_desc[chan].name, ft_codec, + l1h->trx->nr, tn); + goto free_bad_msg; + } + chan_state->dl_ft = ft; + if (bfi) { + LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad " + "AMR frame' trx=%u ts=%u at fn=%u.\n", + trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + break; default: inval_mode2: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, please " "fix!\n"); goto free_bad_msg; } + if (len < 0) { + LOGP(DL1C, LOGL_ERROR, "Cannot send invalid AMR " + "payload\n"); + goto free_bad_msg; + } if (msgb_l2len(msg_tch) != len) { LOGP(DL1C, LOGL_ERROR, "Cannot send payload with " "invalid length! (expecing %d, received %d)\n", @@ -906,6 +970,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { struct msgb *msg_tch = NULL, *msg_facch = NULL; struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + uint8_t tch_mode = chan_state->tch_mode; ubit_t *burst, **bursts_p = &chan_state->dl_bursts; static ubit_t bits[148]; @@ -916,7 +981,8 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, goto send_burst; } - tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch); + tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch, + (((fn + 4) % 26) >> 2) & 1); /* alloc burst memory, if not already, * otherwise shift buffer by 4 bursts for interleaving */ @@ -941,6 +1007,15 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg_facch) tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch), 1); + else if (tch_mode == GSM48_CMODE_SPEECH_AMR) + /* the first FN 4,13,21 defines that CMI is included in frame, + * the first FN 0,8,17 defines that CMR is included in frame. + */ + tch_afs_encode(*bursts_p, msg_tch->l2h + 2, + msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, + chan_state->codec, chan_state->codecs, + chan_state->dl_ft, + chan_state->dl_cmr); else tch_fr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch), 1); @@ -1176,7 +1251,8 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t rsl_cmode = chan_state->rsl_cmode; uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ - int rc; + int rc, amr = 0; + float ber; LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1225,6 +1301,27 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, case GSM48_CMODE_SPEECH_EFR: /* EFR */ rc = tch_fr_decode(tch_data, *bursts_p, 1, 1); break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* the first FN 0,8,17 defines that CMI is included in frame, + * the first FN 4,13,21 defines that CMR is included in frame. + * NOTE: A frame ends 7 FN after start. + */ + rc = tch_afs_decode(tch_data + 2, *bursts_p, + (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec, + chan_state->codecs, &chan_state->ul_ft, + &chan_state->ul_cmr, &ber); + if (rc) + trx_loop_amr_input(l1h, + trx_chan_desc[chan].chan_nr | tn, chan_state, + ber); + amr = 2; /* we store tch_data + 2 header bytes */ + /* only good speech frames get rtp header */ + if (rc != 23 && rc >= 4) { + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->ul_cmr], + chan_state->codec[chan_state->ul_ft], 0); + } + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", tch_mode); @@ -1246,7 +1343,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data, 23, rssi); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1259,6 +1356,15 @@ bfi: memset(tch_data, 0, 31); rc = 31; break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], + 1); + if (rc < 2) + break; + memset(tch_data + 2, 0, rc - 2); + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " "please fix!\n"); @@ -2106,21 +2212,38 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, - uint8_t tch_mode) + uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, + uint8_t codec2, uint8_t codec3, uint8_t initial_id) { uint8_t tn = L1SAP_CHAN2TS(chan_nr); int i; int rc = -EINVAL; + struct trx_chan_state *chan_state; /* look for all matching chan_nr/link_id */ for (i = 0; i < _TRX_CHAN_MAX; i++) { if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) && trx_chan_desc[i].link_id == 0x00) { + chan_state = &l1h->chan_states[tn][i]; LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u on " "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, trx_chan_desc[i].name, l1h->trx->nr, tn); - l1h->chan_states[tn][i].rsl_cmode = rsl_cmode; - l1h->chan_states[tn][i].tch_mode = tch_mode; + chan_state->rsl_cmode = rsl_cmode; + chan_state->tch_mode = tch_mode; + if (rsl_cmode == RSL_CMOD_SPD_SPEECH + && tch_mode == GSM48_CMODE_SPEECH_AMR) { + chan_state->codecs = codecs; + chan_state->codec[0] = codec0; + chan_state->codec[1] = codec1; + chan_state->codec[2] = codec2; + chan_state->codec[3] = codec3; + chan_state->ul_ft = initial_id; + chan_state->dl_ft = initial_id; + chan_state->ul_cmr = initial_id; + chan_state->dl_cmr = initial_id; + chan_state->ber_sum = 0; + chan_state->ber_num = 0; + } rc = 0; } } diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index 6e7c5293..2ab7abeb 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -24,11 +24,12 @@ int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, - int downlink); + int active); /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, - uint8_t tch_mode); + uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, + uint8_t codec2, uint8_t codec3, uint8_t initial_codec); /* setting cipher on logical channels */ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, -- cgit v1.2.3 From f62a64e440a1cb8a654e3f49b8f4202022e0348a Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 26 Mar 2013 09:19:53 +0100 Subject: TRX: Add AMR Payload handling --- src/osmo-bts-trx/amr.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/osmo-bts-trx/amr.h | 8 +++++ 2 files changed, 89 insertions(+) create mode 100644 src/osmo-bts-trx/amr.c create mode 100644 src/osmo-bts-trx/amr.h diff --git a/src/osmo-bts-trx/amr.c b/src/osmo-bts-trx/amr.c new file mode 100644 index 00000000..70d94ca9 --- /dev/null +++ b/src/osmo-bts-trx/amr.c @@ -0,0 +1,81 @@ +/* AMR support for OsmoBTS-TRX */ + +/* (C) 2013 by Andreas Eversberg + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +static int amr_len_by_ft[16] = { + 12, 13, 15, 17, 19, 20, 26, 31, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int amr_decompose_payload(uint8_t *payload, int payload_len, uint8_t *_cmr, + uint8_t *_ft, uint8_t *_bfi) +{ + uint8_t cmr, f, ft, q; + + if (payload_len < 2) + return -EINVAL; + + cmr = payload[0] >> 4; + if (_cmr) + *_cmr = cmr; + + f = payload[1] >> 7; + + ft = (payload[1] >> 3) & 0xf; + if (_ft) + *_ft = ft; + + q = (payload[1] >> 2) & 0x1; + if (_bfi) + *_bfi = !q; + + if (f) { + fprintf(stderr, "%s: multiple payloads not supported\n", + __func__); + return -ENOTSUP; + } + + if (payload_len - 2 < amr_len_by_ft[ft]) + return -EINVAL; + + return 2 + amr_len_by_ft[ft]; +} + +int amr_compose_payload(uint8_t *payload, uint8_t cmr, uint8_t ft, uint8_t bfi) +{ + if (cmr >= 16) + return -EINVAL; + + if (ft >= 16) + return -EINVAL; + + payload[0] = cmr << 4; + + payload[1] = (ft << 3) | ((!bfi) << 2); /* F = 0 */ + + return 2 + amr_len_by_ft[ft]; +} + diff --git a/src/osmo-bts-trx/amr.h b/src/osmo-bts-trx/amr.h new file mode 100644 index 00000000..17eb5f04 --- /dev/null +++ b/src/osmo-bts-trx/amr.h @@ -0,0 +1,8 @@ +#ifndef _TRX_AMR_H +#define _TRX_AMR_H + +int amr_decompose_payload(uint8_t *payload, int payload_len, uint8_t *_cmr, + uint8_t *_ft, uint8_t *_bfi); +int amr_compose_payload(uint8_t *payload, uint8_t cmr, uint8_t ft, uint8_t bfi); + +#endif /* _TRX_AMR_H */ -- cgit v1.2.3 From c910a332b261d48fa594f1a3133987c8f650dcb1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 31 Mar 2013 12:17:02 +0200 Subject: TRX: Support for TCH/H and GSM half rate transcoding --- src/osmo-bts-trx/gsm0503_coding.c | 181 ++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_coding.h | 2 + src/osmo-bts-trx/gsm0503_conv.c | 74 +++++++++++ src/osmo-bts-trx/gsm0503_conv.h | 1 + src/osmo-bts-trx/gsm0503_interleaving.c | 46 +++++++ src/osmo-bts-trx/gsm0503_interleaving.h | 2 + src/osmo-bts-trx/gsm0503_parity.c | 2 +- src/osmo-bts-trx/gsm0503_tables.c | 41 ++++++ src/osmo-bts-trx/gsm0503_tables.h | 1 + src/osmo-bts-trx/l1_if.h | 4 + src/osmo-bts-trx/scheduler.c | 224 ++++++++++++++++++++++++++++++-- tests/bursts/bursts_test.c | 76 +++++++++++ 12 files changed, 645 insertions(+), 9 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 0fb07283..490d0f66 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -369,6 +369,35 @@ static void tch_fr_disassemble(ubit_t *b_bits, uint8_t *tch_data, int net_order) } } +static void tch_hr_reassemble(uint8_t *tch_data, ubit_t *b_bits) +{ + int i, j; + + tch_data[0] = 0x00; /* F = 0, FT = 000 */ + memset(tch_data + 1, 0, 14); + + i = 0; /* counts bits */ + j = 8; /* counts output bits */ + while (i < 112) { + tch_data[j>>3] |= (b_bits[i] << (7-(j&7))); + i++; + j++; + } +} + +static void tch_hr_disassemble(ubit_t *b_bits, uint8_t *tch_data) +{ + int i, j; + + i = 0; /* counts bits */ + j = 8; /* counts output bits */ + while (i < 112) { + b_bits[i] = (tch_data[j>>3] >> (7-(j&7))) & 1; + i++; + j++; + } +} + static void tch_efr_reassemble(uint8_t *tch_data, ubit_t *b_bits) { int i, j; @@ -442,6 +471,35 @@ static void tch_fr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) d_bits[i] = b_bits[gsm610_bitorder[i]]; } +static void tch_hr_d_to_b(ubit_t *b_bits, ubit_t *d_bits) +{ + int i; + + const uint16_t *map; + + if (!d_bits[93] && !d_bits[94]) + map = gsm620_unvoiced_bitorder; + else + map = gsm620_voiced_bitorder; + + for (i = 0; i < 112; i++) + b_bits[map[i]] = d_bits[i]; +} + +static void tch_hr_b_to_d(ubit_t *d_bits, ubit_t *b_bits) +{ + int i; + const uint16_t *map; + + if (!b_bits[34] && !b_bits[35]) + map = gsm620_unvoiced_bitorder; + else + map = gsm620_voiced_bitorder; + + for (i = 0; i < 112; i++) + d_bits[i] = b_bits[map[i]]; +} + static void tch_efr_d_to_w(ubit_t *b_bits, ubit_t *d_bits) { int i; @@ -490,6 +548,18 @@ static void tch_fr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) u[91+i] = p[i]; } +static void tch_hr_unreorder(ubit_t *d, ubit_t *p, ubit_t *u) +{ + memcpy(d, u, 95); + memcpy(p, u+95, 3); +} + +static void tch_hr_reorder(ubit_t *u, ubit_t *d, ubit_t *p) +{ + memcpy(u, d, 95); + memcpy(u+95, p, 3); +} + static void tch_efr_reorder(ubit_t *w, ubit_t *s, ubit_t *p) { memcpy(w, s, 71); @@ -653,6 +723,117 @@ coding_efr_fr: return 0; } +int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd) +{ + sbit_t iB[912], cB[456], h; + ubit_t conv[98], b[112], d[112], p[3]; + int i, rv, steal = 0; + + /* only unmap the stealing bits */ + if (!odd) { + for (i=0; i<4; i++) { + gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 0); + steal -= h; + } + for (i=2; i<5; i++) { + gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1); + steal -= h; + } + } + + /* if we found a stole FACCH, but only at correct alignment */ + if (steal > 0) { + for (i=0; i<6; i++) + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], + NULL, i>>2); + for (i=2; i<4; i++) + gsm0503_tch_burst_unmap(&iB[i * 114 + 456], + &bursts[i * 116], NULL, 1); + + gsm0503_tch_fr_deinterleave(cB, iB); + + rv = _xcch_decode_cB(tch_data, cB); + if (rv) + return -1; + + return 23; + } + + for (i=0; i<4; i++) + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, + i>>1); + + gsm0503_tch_hr_deinterleave(cB, iB); + + osmo_conv_decode(&gsm0503_conv_tch_hr, cB, conv); + + tch_hr_unreorder(d, p, conv); + + for (i=0; i<17; i++) + d[i+95] = (cB[i+211] < 0) ? 1:0; + + rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p); + if (rv) + return -1; + + tch_hr_d_to_b(b, d); + + tch_hr_reassemble(tch_data, b); + + return 15; +} + +int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len) +{ + ubit_t iB[912], cB[456], h; + ubit_t conv[98], b[112], d[112], p[3]; + int i; + + switch (len) { + case 15: /* TCH HR */ + tch_hr_disassemble(b, tch_data); + + tch_hr_b_to_d(d, b); + + osmo_crc8gen_set_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p); + + tch_hr_reorder(conv, d, p); + + osmo_conv_encode(&gsm0503_conv_tch_hr, conv, cB); + + memcpy(cB+211, d+95, 17); + + h = 0; + + gsm0503_tch_hr_interleave(cB, iB); + + for (i=0; i<4; i++) + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], + &h, i>>1); + + break; + case 23: /* FACCH */ + _xcch_encode_cB(cB, tch_data); + + h = 1; + + gsm0503_tch_fr_interleave(cB, iB); + + for (i=0; i<6; i++) + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], + &h, i>>2); + for (i=2; i<4; i++) + gsm0503_tch_burst_map(&iB[i * 114 + 456], + &bursts[i * 116], &h, 1); + + break; + default: + return -1; + } + + return 0; +} + static float amr_calc_ber(sbit_t *orig, ubit_t *test, int len) { int i, err = 0; diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 97da7baf..6bc7dd0e 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -7,6 +7,8 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr); int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order); +int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd); +int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len); int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, float *ber); int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index 4b95f72c..71738969 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -81,6 +81,80 @@ const struct osmo_conv_code gsm0503_conv_tch_fr = { }; +/* + * GSM HR convolutional coding + * + * G_0 = 1 + x^2 + x^3 + x^5 + x^6 + * G_1 = 1 + x + x^4 + x^6 + * G_3 = 1 + x + x^2 + x^3 + x^4 + x^6 + */ + +static const uint8_t conv_tch_hr_next_output[][2] = { + { 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 }, + { 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 }, + { 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 }, + { 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 }, + { 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 }, + { 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 }, + { 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 }, + { 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 }, + { 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 }, + { 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 }, +}; + +static const uint8_t conv_tch_hr_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, + { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 }, + { 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 }, +}; + +static const int conv_tch_hr_puncture[] = { + /* Class 1 bits */ + 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, + 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, + 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, + 109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142, + 145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178, + 181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 214, + 217, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250, + 253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283, + + /* Tail bits */ + 295, 298, 301, 304, 307, 310, 313, + + /* End */ + -1, +}; + +const struct osmo_conv_code gsm0503_conv_tch_hr = { + .N = 3, + .K = 7, + .len = 98, + .next_output = conv_tch_hr_next_output, + .next_state = conv_tch_hr_next_state, + .puncture = conv_tch_hr_puncture, +}; + + /* TCH/AFS12.2 */ /* ----------- */ diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h index 7e0121c9..045a0959 100644 --- a/src/osmo-bts-trx/gsm0503_conv.h +++ b/src/osmo-bts-trx/gsm0503_conv.h @@ -7,6 +7,7 @@ extern const struct osmo_conv_code gsm0503_conv_cs3; extern const struct osmo_conv_code gsm0503_conv_rach; extern const struct osmo_conv_code gsm0503_conv_sch; extern const struct osmo_conv_code gsm0503_conv_tch_fr; +extern const struct osmo_conv_code gsm0503_conv_tch_hr; extern const struct osmo_conv_code gsm0503_conv_tch_afs_12_2; extern const struct osmo_conv_code gsm0503_conv_tch_afs_10_2; extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_95; diff --git a/src/osmo-bts-trx/gsm0503_interleaving.c b/src/osmo-bts-trx/gsm0503_interleaving.c index 014b4c9c..ec47a6b3 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.c +++ b/src/osmo-bts-trx/gsm0503_interleaving.c @@ -96,3 +96,49 @@ void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB) } } +/* + * GSM TCH HR/AHS interleaving and burst mapping + * + * Interleaving: + * + * Given 288 coded input bits, form 4 blocks of 114 bits, + * where even bits of the first 2 blocks and odd bits of the last 2 blocks + * are used: + * + * i(B, j) = c(n, k) k = 0, ..., 227 + * n = 0, ..., N, N + 1, ... + * B = B_0 + 2n + b + * j, b = table[k]; + * + * Mapping on Burst: + * + * e(B, j) = i(B, j) + * e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56 + * e(B, 57) = h_l(B) + * e(B, 58) = h_n(B) + * + * Where hl(B) and hn(B) are bits in burst B indicating flags. + */ + +void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB) +{ + int j, k, B; + + for (k=0; k<228; k++) { + B = gsm0503_tch_hr_interleaving[k][1]; + j = gsm0503_tch_hr_interleaving[k][0]; + cB[k] = iB[B * 114 + j]; + } +} + +void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB) +{ + int j, k, B; + + for (k=0; k<228; k++) { + B = gsm0503_tch_hr_interleaving[k][1]; + j = gsm0503_tch_hr_interleaving[k][0]; + iB[B * 114 + j] = cB[k]; + } +} + diff --git a/src/osmo-bts-trx/gsm0503_interleaving.h b/src/osmo-bts-trx/gsm0503_interleaving.h index d4c2fdbb..4d546f6a 100644 --- a/src/osmo-bts-trx/gsm0503_interleaving.h +++ b/src/osmo-bts-trx/gsm0503_interleaving.h @@ -5,5 +5,7 @@ void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB); void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB); void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB); void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB); +void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB); +void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB); #endif /* _0503_INTERLEAVING_H */ diff --git a/src/osmo-bts-trx/gsm0503_parity.c b/src/osmo-bts-trx/gsm0503_parity.c index 0c5f77cf..ba2e144a 100644 --- a/src/osmo-bts-trx/gsm0503_parity.c +++ b/src/osmo-bts-trx/gsm0503_parity.c @@ -63,7 +63,7 @@ const struct osmo_crc16gen_code gsm0503_sch_crc10 = { /* - * GSM TCH FR/EFR parity + * GSM TCH FR/HR/EFR parity * * g(x) = x^3 + x + 1 */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 4c5ab7a1..1c272d8f 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -136,3 +136,44 @@ const sbit_t gsm0503_afs_ic_sbit[4][8] = { { -127,-127,-127, 127, 127,-127,-127,-127 }, }; +const uint8_t gsm0503_tch_hr_interleaving[228][2] = { + { 0 ,0 }, { 1 ,2 }, { 78 ,1 }, { 79 ,3 }, { 48 ,0 }, { 49 ,2 }, + { 54 ,1 }, { 55 ,3 }, { 24 ,0 }, { 25 ,2 }, { 30 ,1 }, { 31 ,3 }, + { 72 ,0 }, { 73 ,2 }, { 6 ,1 }, { 7 ,3 }, { 96 ,0 }, { 97 ,2 }, + { 12 ,0 }, { 13 ,2 }, { 102,1 }, { 103,3 }, { 60 ,0 }, { 61 ,2 }, + { 66 ,1 }, { 67 ,3 }, { 90 ,1 }, { 91 ,3 }, { 36 ,0 }, { 37 ,2 }, + { 42 ,1 }, { 43 ,3 }, { 18 ,1 }, { 19 ,3 }, { 84 ,0 }, { 85 ,2 }, + { 108,0 }, { 109,2 }, { 2 ,0 }, { 3 ,2 }, { 80 ,1 }, { 81 ,3 }, + { 50 ,0 }, { 51 ,2 }, { 56 ,1 }, { 57 ,3 }, { 26 ,0 }, { 27 ,2 }, + { 32 ,1 }, { 33 ,3 }, { 74 ,0 }, { 75 ,2 }, { 8 ,1 }, { 9 ,3 }, + { 98 ,0 }, { 99 ,2 }, { 14 ,0 }, { 15 ,2 }, { 104,1 }, { 105,3 }, + { 62 ,0 }, { 63 ,2 }, { 68 ,1 }, { 69 ,3 }, { 92 ,1 }, { 93 ,3 }, + { 38 ,0 }, { 39 ,2 }, { 44 ,1 }, { 45 ,3 }, { 20 ,1 }, { 21 ,3 }, + { 86 ,0 }, { 87 ,2 }, { 110,0 }, { 111,2 }, { 4 ,0 }, { 5 ,2 }, + { 82 ,1 }, { 83 ,3 }, { 52 ,0 }, { 53 ,2 }, { 58 ,1 }, { 59 ,3 }, + { 28 ,0 }, { 29 ,2 }, { 34 ,1 }, { 35 ,3 }, { 76 ,0 }, { 77 ,2 }, + { 10 ,1 }, { 12 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 }, + { 106,1 }, { 107,3 }, { 64 ,0 }, { 65 ,2 }, { 70 ,1 }, { 71 ,3 }, + { 94 ,1 }, { 95 ,3 }, { 40 ,0 }, { 41 ,2 }, { 46 ,1 }, { 47 ,3 }, + { 22 ,1 }, { 23 ,3 }, { 88 ,0 }, { 89 ,2 }, { 112,0 }, { 113,2 }, + { 6 ,0 }, { 7 ,2 }, { 84 ,1 }, { 85 ,3 }, { 54 ,0 }, { 55 ,2 }, + { 60 ,1 }, { 61 ,3 }, { 30 ,0 }, { 31 ,2 }, { 36 ,1 }, { 37 ,3 }, + { 78 ,0 }, { 79 ,2 }, { 12 ,1 }, { 13 ,3 }, { 102,0 }, { 103,2 }, + { 18 ,0 }, { 19 ,2 }, { 108,1 }, { 109,3 }, { 66 ,0 }, { 67 ,2 }, + { 72 ,1 }, { 73 ,3 }, { 96 ,1 }, { 97 ,3 }, { 42 ,0 }, { 43 ,2 }, + { 48 ,1 }, { 49 ,3 }, { 24 ,1 }, { 25 ,3 }, { 90 ,0 }, { 91 ,2 }, + { 0 ,1 }, { 1 ,3 }, { 8 ,0 }, { 9 ,2 }, { 86 ,1 }, { 87 ,3 }, + { 56 ,0 }, { 57 ,2 }, { 62 ,1 }, { 63 ,3 }, { 32 ,0 }, { 33 ,2 }, + { 38 ,1 }, { 39 ,3 }, { 80 ,0 }, { 81 ,2 }, { 14 ,1 }, { 15 ,3 }, + { 104,0 }, { 105,2 }, { 20 ,0 }, { 21 ,2 }, { 110,1 }, { 111,3 }, + { 68 ,0 }, { 69 ,2 }, { 74 ,1 }, { 75 ,3 }, { 98 ,1 }, { 99 ,3 }, + { 44 ,0 }, { 45 ,2 }, { 50 ,1 }, { 51 ,3 }, { 26 ,1 }, { 27 ,3 }, + { 92 ,0 }, { 93 ,2 }, { 2 ,1 }, { 3 ,3 }, { 10 ,0 }, { 11 ,2 }, + { 88 ,1 }, { 89 ,3 }, { 58 ,0 }, { 59 ,2 }, { 64 ,1 }, { 65 ,3 }, + { 34 ,0 }, { 35 ,2 }, { 40 ,1 }, { 41 ,3 }, { 82 ,0 }, { 83 ,2 }, + { 16 ,1 }, { 17 ,3 }, { 106,0 }, { 107,2 }, { 22 ,0 }, { 23 ,2 }, + { 112,1 }, { 113,3 }, { 70 ,0 }, { 71 ,2 }, { 76 ,1 }, { 77 ,3 }, + { 100,1 }, { 101,3 }, { 46 ,0 }, { 47 ,2 }, { 52 ,1 }, { 53 ,3 }, + { 28 ,1 }, { 29 ,3 }, { 94 ,0 }, { 95 ,2 }, { 4 ,1 }, { 5 ,3 }, +}; + diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index db0e920a..b068fdd6 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -12,5 +12,6 @@ extern const uint8_t gsm0503_gsm_fr_map[76]; extern const uint8_t gsm0503_gsm_efr_protected_bits[65]; extern const ubit_t gsm0503_afs_ic_ubit[4][8]; extern const sbit_t gsm0503_afs_ic_sbit[4][8]; +extern const uint8_t gsm0503_tch_hr_interleaving[228][2]; #endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 9cc8ee3b..0b5d0855 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -76,6 +76,10 @@ struct trx_chan_state { uint8_t dl_cmr; /* current downlink CMR index */ uint8_t amr_loop; /* if AMR loop is enabled */ + /* TCH/H */ + uint8_t dl_ongoing_facch; /* FACCH/H on downlink */ + uint8_t ul_ongoing_facch; /* FACCH/H on uplink */ + /* encryption */ int ul_encr_algo; /* A5/x encry algo downlink */ int dl_encr_algo; /* A5/x encry algo uplink */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index eb6130eb..65da30eb 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -421,8 +421,8 @@ static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan) { - // FIXME - return 0; + /* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */ + return rts_tch_common(l1h, tn, fn, chan, ((fn % 26) >> 2) & 1); } @@ -780,6 +780,12 @@ static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* indicate bad frame */ switch (tch_mode) { case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + if (chan != TRXC_TCHF) { /* HR */ + tch_data[0] = 0x70; /* F = 0, FT = 111 */ + memset(tch_data + 1, 0, 14); + len = 15; + break; + } memset(tch_data, 0, 33); len = 33; break; @@ -869,6 +875,20 @@ inval_mode1: switch (tch_mode) { case GSM48_CMODE_SPEECH_V1: /* FR / HR */ + if (chan != TRXC_TCHF) { /* HR */ + len = 15; + if (msgb_l2len(msg_tch) >= 1 + && (msg_tch->l2h[0] & 0xf0) != 0x00) { + LOGP(DL1C, LOGL_NOTICE, "%s " + "Transmitting 'bad " + "HR frame' trx=%u ts=%u at " + "fn=%u.\n", + trx_chan_desc[chan].name, + l1h->trx->nr, tn, fn); + goto free_bad_msg; + } + break; + } len = 33; if (msgb_l2len(msg_tch) >= 1 && (msg_tch->l2h[0] >> 4) != 0xd) { @@ -1043,10 +1063,84 @@ send_burst: static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid) { - // FIXME - return NULL; -} + struct msgb *msg_tch = NULL, *msg_facch = NULL; + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + ubit_t *burst, **bursts_p = &chan_state->dl_bursts; + static ubit_t bits[148]; + + /* send burst, if we already got a frame */ + if (bid > 0) { + if (!*bursts_p) + return NULL; + goto send_burst; + } + /* get TCH and/or FACCH */ + tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch, + (((fn + 4) % 26) >> 2) & 1); + + /* check for FACCH alignment */ + if (msg_facch && ((((fn + 4) % 26) >> 2) & 1)) { + LOGP(DL1C, LOGL_ERROR, "%s Cannot transmit FACCH starting on " + "even frames, please fix RTS!\n", + trx_chan_desc[chan].name); + msgb_free(msg_facch); + msg_facch = NULL; + } + + /* alloc burst memory, if not already, + * otherwise shift buffer by 2 bursts for interleaving */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 696); + if (!*bursts_p) + return NULL; + } else { + memcpy(*bursts_p, *bursts_p + 232, 232); + if (chan_state->dl_ongoing_facch) { + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + memset(*bursts_p + 464, 0, 232); + } else { + memset(*bursts_p + 232, 0, 232); + } + } + + /* mo message at all */ + if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { + LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + "trx=%u ts=%u at fn=%u to transmit.\n", + trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); + goto send_burst; + } + + /* encode bursts (priorize FACCH) */ + if (msg_facch) { + tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch)); + chan_state->dl_ongoing_facch = 1; /* first of two tch frames */ + } else if (chan_state->dl_ongoing_facch) /* second of two tch frames */ + chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */ + else + tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); + + /* free message */ + if (msg_tch) + msgb_free(msg_tch); + if (msg_facch) + msgb_free(msg_facch); + +send_burst: + /* compose burst */ + burst = *bursts_p + bid * 116; + memset(bits, 0, 3); + memcpy(bits + 3, burst, 58); + memcpy(bits + 61, tsc[l1h->config.tsc], 26); + memcpy(bits + 87, burst + 58, 58); + memset(bits + 145, 0, 3); + + LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n", + trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); + + return bits; +} /* @@ -1385,11 +1479,125 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, float toa) { - LOGP(DL1C, LOGL_DEBUG, "TCH/H Received %s fn=%u ts=%u trx=%u bid=%u\n", + struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + sbit_t *burst, **bursts_p = &chan_state->ul_bursts; + uint8_t *mask = &chan_state->ul_mask; + uint8_t rsl_cmode = chan_state->rsl_cmode; + uint8_t tch_mode = chan_state->tch_mode; + uint8_t tch_data[128]; /* just to be safe */ + int rc, amr = 0; + + LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); - // FIXME - return 0; + /* alloc burst memory, if not already */ + if (!*bursts_p) { + *bursts_p = talloc_zero_size(tall_bts_ctx, 696); + if (!*bursts_p) + return -ENOMEM; + } + + /* clear burst */ + if (bid == 0) { + memset(*bursts_p + 464, 0, 232); + *mask = 0x0; + } + + /* update mask */ + *mask |= (1 << bid); + + /* copy burst to end of buffer of 6 bursts */ + burst = *bursts_p + bid * 116 + 464; + memcpy(burst, bits + 3, 58); + memcpy(burst + 58, bits + 87, 58); + + /* wait until complete set of bursts */ + if (bid != 1) + return 0; + + /* check for complete set of bursts */ + if ((*mask & 0x3) != 0x3) { + LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending " + "at fn=%u (%u/%u) for %s\n", fn, + fn % l1h->mf_period[tn], l1h->mf_period[tn], + trx_chan_desc[chan].name); + } + *mask = 0x0; + + /* skip second of two TCH frames of FACCH was received */ + if (chan_state->ul_ongoing_facch) { + chan_state->ul_ongoing_facch = 0; + memcpy(*bursts_p, *bursts_p + 232, 232); + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + goto bfi; + } + + /* decode + * also shift buffer by 4 bursts for interleaving */ + switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 + : tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* HR or signalling */ + /* Note on FN-10: If we are at FN 10, we decoded an even aligned + * TCH/FACCH frame, because our burst buffer carries 6 bursts. + * Even FN ending at: 10,11,19,20,2,3 + */ + rc = tch_hr_decode(tch_data, *bursts_p, + (((fn + 26 - 10) % 26) >> 2) & 1); + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", + tch_mode); + return -EINVAL; + } + memcpy(*bursts_p, *bursts_p + 232, 232); + memcpy(*bursts_p + 232, *bursts_p + 464, 232); + if (rc < 0) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s\n", fn, trx_chan_desc[chan].name); + goto bfi; + } + if (rc < 4) { + LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " + "fn=%u for %s with codec mode %d (out of range)\n", + fn, trx_chan_desc[chan].name, rc); + goto bfi; + } + + /* FACCH */ + if (rc == 23) { + chan_state->ul_ongoing_facch = 1; + compose_ph_data_ind(l1h, tn, + (fn + 2715648 - 10 - ((fn % 26) >= 19)) % 2715648, chan, + tch_data + amr, 23, rssi); +bfi: + if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { + /* indicate bad frame */ + switch (tch_mode) { + case GSM48_CMODE_SPEECH_V1: /* HR */ + tch_data[0] = 0x70; /* F = 0, FT = 111 */ + memset(tch_data + 1, 0, 14); + rc = 15; + break; + default: + LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " + "please fix!\n"); + return -EINVAL; + } + } + } + + if (rsl_cmode != RSL_CMOD_SPD_SPEECH) + return 0; + + /* TCH or BFI */ + /* Note on FN 19 or 20: If we received the last burst of a frame, + * it actually starts at FN 8 or 9. A burst starting there, overlaps + * with the slot 12, so an extra FN must be substracted to get correct + * start of frame. + */ + return compose_tch_ind(l1h, tn, + (fn + 2715648 - 10 - ((fn%26)==19) - ((fn%26)==20)) % 2715648, + chan, tch_data, rc); } diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index 935cbc6d..608baacb 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -254,6 +254,73 @@ static void test_fr(uint8_t *speech, int len) printd("\n"); } +static void test_hr(uint8_t *speech, int len) +{ + uint8_t result[23]; + ubit_t bursts_u[116 * 6]; + sbit_t bursts_s[116 * 6]; + int rc; + + memset(bursts_u, 0x23, sizeof(bursts_u)); + memset(bursts_s, 0, sizeof(bursts_s)); + + printd("Encoding: %s\n", osmo_hexdump(speech, len)); + + /* encode */ + tch_hr_encode(bursts_u, speech, len); + + printd("U-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57), + bursts_u[57], bursts_u[58]); + printd("%s\n", osmo_hexdump(bursts_u + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57), + bursts_u[57 + 116], bursts_u[58 + 116]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57), + bursts_u[57 + 232], bursts_u[58 + 232]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57), + bursts_u[57 + 348], bursts_u[58 + 348]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57), + bursts_u[57 + 464], bursts_u[58 + 464]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57)); + printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57), + bursts_u[57 + 580], bursts_u[58 + 580]); + printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57)); + ubits2sbits(bursts_u, bursts_s, 116 * 6); + printd("S-Bits:\n"); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57), + (uint8_t)bursts_s[57], (uint8_t)bursts_s[58]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57), + (uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57), + (uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57), + (uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57), + (uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57)); + printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57), + (uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]); + printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57)); + + /* decode */ + rc = tch_hr_decode(result, bursts_s, 0); + + ASSERT_TRUE(rc == len); + + printd("Decoded: %s\n", osmo_hexdump(result, len)); + + ASSERT_TRUE(!memcmp(speech, result, len)); + + printd("\n"); +} + static void test_pdtch(uint8_t *l2, int len) { uint8_t result[len]; @@ -351,6 +418,7 @@ uint8_t test_macblock[][54] = { uint8_t test_speech_fr[33]; uint8_t test_speech_efr[31]; +uint8_t test_speech_hr[15]; int main(int argc, char **argv) { @@ -381,6 +449,14 @@ int main(int argc, char **argv) for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_fr(test_l2[i], sizeof(test_l2[0])); + for (i = 0; i < sizeof(test_speech_hr); i++) + test_speech_hr[i] = i*17; + test_speech_hr[0] = 0x00; + test_hr(test_speech_hr, sizeof(test_speech_hr)); + + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) + test_hr(test_l2[i], sizeof(test_l2[0])); + for (i = 0; i < sizeof(test_macblock) / sizeof(test_macblock[0]); i++) { test_pdtch(test_macblock[i], 23); test_pdtch(test_macblock[i], 34); -- cgit v1.2.3 From c5241c3aa42d7ff89a369f2a816a98bdca932217 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 31 Mar 2013 12:19:26 +0200 Subject: TRX: Support for AMR half speech --- src/osmo-bts-trx/gsm0503_coding.c | 369 ++++++++++++++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_coding.h | 6 + src/osmo-bts-trx/gsm0503_conv.c | 309 +++++++++++++++++++++++++++++++ src/osmo-bts-trx/gsm0503_conv.h | 6 + src/osmo-bts-trx/gsm0503_tables.c | 14 ++ src/osmo-bts-trx/gsm0503_tables.h | 2 + src/osmo-bts-trx/scheduler.c | 42 +++++ 7 files changed, 748 insertions(+) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 490d0f66..c3ce9e57 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -1227,6 +1227,375 @@ facch: return 0; } +int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, + uint8_t *cmr, float *ber) +{ + sbit_t iB[912], cB[456], h; + ubit_t test[456], d[244], p[6], conv[135]; + int i, j, k, best = 0, rv, len, steal = 0, id = 0; + + /* only unmap the stealing bits */ + if (!odd) { + for (i=0; i<4; i++) { + gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 0); + steal -= h; + } + for (i=2; i<5; i++) { + gsm0503_tch_burst_unmap(NULL, &bursts[i * 116], &h, 1); + steal -= h; + } + } + + /* if we found a stole FACCH, but only at correct alignment */ + if (steal > 0) { + for (i=0; i<6; i++) + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], + NULL, i>>2); + for (i=2; i<4; i++) + gsm0503_tch_burst_unmap(&iB[i * 114 + 456], + &bursts[i * 116], NULL, 1); + + gsm0503_tch_fr_deinterleave(cB, iB); + + rv = _xcch_decode_cB(tch_data, cB); + if (rv) + return -1; + + return 23; + } + + for (i=0; i<4; i++) + gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], NULL, + i>>1); + + gsm0503_tch_hr_deinterleave(cB, iB); + + for (i=0; i<4; i++) { + for (j=0, k=0; j<4; j++) + k += abs(((int)gsm0503_ahs_ic_sbit[i][j]) - + ((int)cB[j])); + if (i == 0 || k < best) { + best = k; + id = i; + } + } + + /* check if indicated codec fits into range of codecs */ + if (id >= codecs) { + /* codec mode out of range, return id */ + return id; + } + + switch ((codec_mode_req) ? codec[*ft] : codec[id]) { + case 5: /* TCH/AHS7.95 */ + osmo_conv_decode(&gsm0503_conv_tch_ahs_7_95, cB+4, conv); + + tch_amr_unmerge(d, p, conv, 123, 67); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 67, p); + if (rv) + return -1; + + for (i=0; i<36;i++) + d[i+123] = (cB[i+192] < 0) ? 1:0; + + tch_amr_reassemble(tch_data, d, 159); + + len = 20; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_ahs_7_95, conv, + test+4); + *ber = amr_calc_ber(cB+4, test+4, 188); + } + + break; + case 4: /* TCH/AHS7.4 */ + osmo_conv_decode(&gsm0503_conv_tch_ahs_7_4, cB+4, conv); + + tch_amr_unmerge(d, p, conv, 120, 61); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p); + if (rv) + return -1; + + for (i=0; i<28;i++) + d[i+120] = (cB[i+200] < 0) ? 1:0; + + tch_amr_reassemble(tch_data, d, 148); + + len = 19; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_ahs_7_4, conv, + test+4); + *ber = amr_calc_ber(cB+4, test+4, 196); + } + + break; + case 3: /* TCH/AHS6.7 */ + osmo_conv_decode(&gsm0503_conv_tch_ahs_6_7, cB+4, conv); + + tch_amr_unmerge(d, p, conv, 110, 55); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); + if (rv) + return -1; + + for (i=0; i<24;i++) + d[i+110] = (cB[i+204] < 0) ? 1:0; + + tch_amr_reassemble(tch_data, d, 134); + + len = 17; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_ahs_6_7, conv, + test+4); + *ber = amr_calc_ber(cB+4, test+4, 200); + } + + break; + case 2: /* TCH/AHS5.9 */ + osmo_conv_decode(&gsm0503_conv_tch_ahs_5_9, cB+4, conv); + + tch_amr_unmerge(d, p, conv, 102, 55); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); + if (rv) + return -1; + + for (i=0; i<16;i++) + d[i+102] = (cB[i+212] < 0) ? 1:0; + + tch_amr_reassemble(tch_data, d, 118); + + len = 15; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_ahs_5_9, conv, + test+4); + *ber = amr_calc_ber(cB+4, test+4, 208); + } + + break; + case 1: /* TCH/AHS5.15 */ + osmo_conv_decode(&gsm0503_conv_tch_ahs_5_15, cB+4, conv); + + tch_amr_unmerge(d, p, conv, 91, 49); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p); + if (rv) + return -1; + + for (i=0; i<12;i++) + d[i+91] = (cB[i+216] < 0) ? 1:0; + + tch_amr_reassemble(tch_data, d, 103); + + len = 13; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_ahs_5_15, conv, + test+4); + *ber = amr_calc_ber(cB+4, test+4, 212); + } + + break; + case 0: /* TCH/AHS4.75 */ + osmo_conv_decode(&gsm0503_conv_tch_ahs_4_75, cB+4, conv); + + tch_amr_unmerge(d, p, conv, 83, 39); + + rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p); + if (rv) + return -1; + + for (i=0; i<12;i++) + d[i+83] = (cB[i+216] < 0) ? 1:0; + + tch_amr_reassemble(tch_data, d, 95); + + len = 12; + + if (ber) { + osmo_conv_encode(&gsm0503_conv_tch_ahs_4_75, conv, + test+4); + *ber = amr_calc_ber(cB+4, test+4, 212); + } + + break; + default: + fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); + + return -1; + } + + /* change codec request / indication, if frame is valid */ + if (codec_mode_req) + *cmr = id; + else + *ft = id; + + return len; +} + +int tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, + uint8_t cmr) +{ + ubit_t iB[912], cB[456], h; + ubit_t d[244], p[6], conv[135]; + int i; + uint8_t id; + + if (len == 23) { /* FACCH */ + _xcch_encode_cB(cB, tch_data); + + h = 1; + + gsm0503_tch_fr_interleave(cB, iB); + + for (i=0; i<6; i++) + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], + &h, i>>2); + for (i=2; i<4; i++) + gsm0503_tch_burst_map(&iB[i * 114 + 456], + &bursts[i * 116], &h, 1); + + return 0; + } + + h = 0; + + if (codec_mode_req) { + if (cmr >= codecs) { + fprintf(stderr, "FIXME: CMR ID %d not in codec list!\n", + cmr); + return -1; + } + id = cmr; + } else { + if (ft >= codecs) { + fprintf(stderr, "FIXME: FT ID %d not in codec list!\n", + ft); + return -1; + } + id = ft; + } + + switch (codec[ft]) { + case 5: /* TCH/AHS7.95 */ + if (len != 20) { +invalid_length: + fprintf(stderr, "FIXME: payload length %d does not " + "comply with codec type %d!\n", len, ft); + return -1; + } + + tch_amr_disassemble(d, tch_data, 159); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 67, p); + + tch_amr_merge(conv, d, p, 123, 67); + + osmo_conv_encode(&gsm0503_conv_tch_ahs_7_95, conv, cB+4); + + memcpy(cB+192, d+123, 36); + + break; + case 4: /* TCH/AHS7.4 */ + if (len != 19) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 148); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 61, p); + + tch_amr_merge(conv, d, p, 120, 61); + + osmo_conv_encode(&gsm0503_conv_tch_ahs_7_4, conv, cB+4); + + memcpy(cB+200, d+120, 28); + + break; + case 3: /* TCH/AHS6.7 */ + if (len != 17) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 134); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p); + + tch_amr_merge(conv, d, p, 110, 55); + + osmo_conv_encode(&gsm0503_conv_tch_ahs_6_7, conv, cB+4); + + memcpy(cB+204, d+110, 24); + + break; + case 2: /* TCH/AHS5.9 */ + if (len != 15) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 118); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 55, p); + + tch_amr_merge(conv, d, p, 102, 55); + + osmo_conv_encode(&gsm0503_conv_tch_ahs_5_9, conv, cB+4); + + memcpy(cB+212, d+102, 16); + + break; + case 1: /* TCH/AHS5.15 */ + if (len != 13) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 103); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 49, p); + + tch_amr_merge(conv, d, p, 91, 49); + + osmo_conv_encode(&gsm0503_conv_tch_ahs_5_15, conv, cB+4); + + memcpy(cB+216, d+91, 12); + + break; + case 0: /* TCH/AHS4.75 */ + if (len != 12) + goto invalid_length; + + tch_amr_disassemble(d, tch_data, 95); + + osmo_crc8gen_set_bits(&gsm0503_amr_crc6, d, 39, p); + + tch_amr_merge(conv, d, p, 83, 39); + + osmo_conv_encode(&gsm0503_conv_tch_ahs_4_75, conv, cB+4); + + memcpy(cB+216, d+83, 12); + + break; + default: + fprintf(stderr, "FIXME: FT %d not supported!\n", ft); + + return -1; + } + + memcpy(cB, gsm0503_afs_ic_ubit[id], 4); + + gsm0503_tch_hr_interleave(cB, iB); + + for (i=0; i<4; i++) + gsm0503_tch_burst_map(&iB[i * 114], &bursts[i * 116], &h, i>>1); + + return 0; +} + /* * GSM RACH transcoding */ diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 6bc7dd0e..33b58d3b 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -14,6 +14,12 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr); +int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, + uint8_t *cmr, float *ber); +int tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len, + int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, + uint8_t cmr); int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic); int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic); int sch_decode(uint8_t *sb_info, sbit_t *burst); diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index 71738969..2a814ca3 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -639,3 +639,312 @@ const struct osmo_conv_code gsm0503_conv_tch_afs_4_75 = { }; +/* TCH/AHS7.95 */ +/* ----------- */ + +static const uint8_t conv_tch_ahs_7_95_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_ahs_7_95_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 }, + { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_ahs_7_95_next_term_output[] = { + 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1, +}; + +static const uint8_t conv_tch_ahs_7_95_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_ahs_7_95_puncture[] = { + 1, 3, 5, 7, 11, 15, 19, 23, 27, 31, 35, 43, + 47, 51, 55, 59, 63, 67, 71, 79, 83, 87, 91, 95, + 99, 103, 107, 115, 119, 123, 127, 131, 135, 139, 143, 151, + 155, 159, 163, 167, 171, 175, 177, 179, 183, 185, 187, 191, + 193, 195, 197, 199, 203, 205, 207, 211, 213, 215, 219, 221, + 223, 227, 229, 231, 233, 235, 239, 241, 243, 247, 249, 251, + 255, 257, 259, 261, 263, 265, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95 = { + .N = 2, + .K = 5, + .len = 129, + .next_output = conv_tch_ahs_7_95_next_output, + .next_state = conv_tch_ahs_7_95_next_state, + .next_term_output = conv_tch_ahs_7_95_next_term_output, + .next_term_state = conv_tch_ahs_7_95_next_term_state, + .puncture = conv_tch_ahs_7_95_puncture, +}; + + +/* TCH/AHS7.4 */ +/* ---------- */ + +static const uint8_t conv_tch_ahs_7_4_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_ahs_7_4_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 }, + { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_ahs_7_4_next_term_output[] = { + 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1, +}; + +static const uint8_t conv_tch_ahs_7_4_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_ahs_7_4_puncture[] = { + 1, 3, 7, 11, 19, 23, 27, 35, 39, 43, 51, 55, + 59, 67, 71, 75, 83, 87, 91, 99, 103, 107, 115, 119, + 123, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, + 175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, + 221, 223, 227, 229, 231, 235, 237, 239, 243, 245, 247, 251, + 253, 255, 257, 259, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4 = { + .N = 2, + .K = 5, + .len = 126, + .next_output = conv_tch_ahs_7_4_next_output, + .next_state = conv_tch_ahs_7_4_next_state, + .next_term_output = conv_tch_ahs_7_4_next_term_output, + .next_term_state = conv_tch_ahs_7_4_next_term_state, + .puncture = conv_tch_ahs_7_4_puncture, +}; + + +/* TCH/AHS6.7 */ +/* ---------- */ + +static const uint8_t conv_tch_ahs_6_7_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_ahs_6_7_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 }, + { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_ahs_6_7_next_term_output[] = { + 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1, +}; + +static const uint8_t conv_tch_ahs_6_7_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_ahs_6_7_puncture[] = { + 1, 3, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99, + 109, 119, 129, 139, 149, 159, 167, 169, 177, 179, 187, 189, + 197, 199, 203, 207, 209, 213, 217, 219, 223, 227, 229, 231, + 233, 235, 237, 239, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7 = { + .N = 2, + .K = 5, + .len = 116, + .next_output = conv_tch_ahs_6_7_next_output, + .next_state = conv_tch_ahs_6_7_next_state, + .next_term_output = conv_tch_ahs_6_7_next_term_output, + .next_term_state = conv_tch_ahs_6_7_next_term_state, + .puncture = conv_tch_ahs_6_7_puncture, +}; + + +/* TCH/AHS5.9 */ +/* ---------- */ + +static const uint8_t conv_tch_ahs_5_9_next_output[][2] = { + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, + { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 }, +}; + +static const uint8_t conv_tch_ahs_5_9_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, + { 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 }, + { 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 }, + { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_ahs_5_9_next_term_output[] = { + 0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1, +}; + +static const uint8_t conv_tch_ahs_5_9_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_ahs_5_9_puncture[] = { + 1, 15, 71, 127, 139, 151, 163, 175, 187, 195, 203, 211, + 215, 219, 221, 223, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9 = { + .N = 2, + .K = 5, + .len = 108, + .next_output = conv_tch_ahs_5_9_next_output, + .next_state = conv_tch_ahs_5_9_next_state, + .next_term_output = conv_tch_ahs_5_9_next_term_output, + .next_term_state = conv_tch_ahs_5_9_next_term_state, + .puncture = conv_tch_ahs_5_9_puncture, +}; + + +/* TCH/AHS5.15 */ +/* ----------- */ + +static const uint8_t conv_tch_ahs_5_15_next_output[][2] = { + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, + { 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 }, + { 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 }, +}; + +static const uint8_t conv_tch_ahs_5_15_next_state[][2] = { + { 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 }, + { 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 }, + { 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 }, + { 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 }, +}; + +static const uint8_t conv_tch_ahs_5_15_next_term_output[] = { + 0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4, +}; + +static const uint8_t conv_tch_ahs_5_15_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14, +}; + +static int conv_tch_ahs_5_15_puncture[] = { + 0, 1, 3, 4, 6, 9, 12, 15, 18, 21, 27, 33, + 39, 45, 51, 54, 57, 63, 69, 75, 81, 87, 90, 93, + 99, 105, 111, 117, 123, 126, 129, 135, 141, 147, 153, 159, + 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195, + 198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, + 234, 237, 240, 243, 244, 246, 249, 252, 255, 256, 258, 261, + 264, 267, 268, 270, 273, 276, 279, 280, 282, 285, 288, 289, + 291, 294, 295, 297, 298, 300, 301, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15 = { + .N = 3, + .K = 5, + .len = 97, + .next_output = conv_tch_ahs_5_15_next_output, + .next_state = conv_tch_ahs_5_15_next_state, + .next_term_output = conv_tch_ahs_5_15_next_term_output, + .next_term_state = conv_tch_ahs_5_15_next_term_state, + .puncture = conv_tch_ahs_5_15_puncture, +}; + + +/* TCH/AHS4.75 */ +/* ----------- */ + +static const uint8_t conv_tch_ahs_4_75_next_output[][2] = { + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 }, + { 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 }, + { 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 }, + { 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 }, +}; + +static const uint8_t conv_tch_ahs_4_75_next_state[][2] = { + { 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 }, + { 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 }, + { 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 }, + { 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 }, + { 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 }, + { 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 }, + { 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 }, + { 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 }, + { 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 }, + { 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 }, + { 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 }, + { 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 }, + { 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 }, + { 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 }, + { 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 }, + { 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 }, +}; + +static const uint8_t conv_tch_ahs_4_75_next_term_output[] = { + 0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0, + 4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4, + 7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7, + 3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3, +}; + +static const uint8_t conv_tch_ahs_4_75_next_term_state[] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, +}; + +static int conv_tch_ahs_4_75_puncture[] = { + 1, 2, 4, 5, 7, 8, 10, 13, 16, 22, 28, 34, + 40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106, + 112, 118, 124, 130, 136, 142, 148, 151, 154, 160, 163, 166, + 172, 175, 178, 184, 187, 190, 196, 199, 202, 208, 211, 214, + 220, 223, 226, 232, 235, 238, 241, 244, 247, 250, 253, 256, + 259, 262, 265, 268, 271, 274, 275, 277, 278, 280, 281, 283, + 284, + -1, /* end */ +}; + +const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75 = { + .N = 3, + .K = 7, + .len = 89, + .next_output = conv_tch_ahs_4_75_next_output, + .next_state = conv_tch_ahs_4_75_next_state, + .next_term_output = conv_tch_ahs_4_75_next_term_output, + .next_term_state = conv_tch_ahs_4_75_next_term_state, + .puncture = conv_tch_ahs_4_75_puncture, +}; + diff --git a/src/osmo-bts-trx/gsm0503_conv.h b/src/osmo-bts-trx/gsm0503_conv.h index 045a0959..68ad4844 100644 --- a/src/osmo-bts-trx/gsm0503_conv.h +++ b/src/osmo-bts-trx/gsm0503_conv.h @@ -16,5 +16,11 @@ extern const struct osmo_conv_code gsm0503_conv_tch_afs_6_7; extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_9; extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_15; extern const struct osmo_conv_code gsm0503_conv_tch_afs_4_75; +extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95; +extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4; +extern const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7; +extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9; +extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15; +extern const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75; #endif /* _0503_CONV_H */ diff --git a/src/osmo-bts-trx/gsm0503_tables.c b/src/osmo-bts-trx/gsm0503_tables.c index 1c272d8f..a3817842 100644 --- a/src/osmo-bts-trx/gsm0503_tables.c +++ b/src/osmo-bts-trx/gsm0503_tables.c @@ -136,6 +136,20 @@ const sbit_t gsm0503_afs_ic_sbit[4][8] = { { -127,-127,-127, 127, 127,-127,-127,-127 }, }; +const ubit_t gsm0503_ahs_ic_ubit[4][4] = { + { 0,0,0,0 }, + { 1,0,0,1 }, + { 1,1,1,0 }, + { 0,1,1,1 }, +}; + +const sbit_t gsm0503_ahs_ic_sbit[4][4] = { + { 127, 127, 127, 127 }, + { -127, 127, 127,-127 }, + { -127,-127,-127, 127 }, + { 127,-127,-127,-127 }, +}; + const uint8_t gsm0503_tch_hr_interleaving[228][2] = { { 0 ,0 }, { 1 ,2 }, { 78 ,1 }, { 79 ,3 }, { 48 ,0 }, { 49 ,2 }, { 54 ,1 }, { 55 ,3 }, { 24 ,0 }, { 25 ,2 }, { 30 ,1 }, { 31 ,3 }, diff --git a/src/osmo-bts-trx/gsm0503_tables.h b/src/osmo-bts-trx/gsm0503_tables.h index b068fdd6..63b45a4d 100644 --- a/src/osmo-bts-trx/gsm0503_tables.h +++ b/src/osmo-bts-trx/gsm0503_tables.h @@ -12,6 +12,8 @@ extern const uint8_t gsm0503_gsm_fr_map[76]; extern const uint8_t gsm0503_gsm_efr_protected_bits[65]; extern const ubit_t gsm0503_afs_ic_ubit[4][8]; extern const sbit_t gsm0503_afs_ic_sbit[4][8]; +extern const ubit_t gsm0503_ahs_ic_ubit[4][4]; +extern const sbit_t gsm0503_ahs_ic_sbit[4][4]; extern const uint8_t gsm0503_tch_hr_interleaving[228][2]; #endif /* _0503_TABLES_H */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 65da30eb..9126e342 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1065,6 +1065,7 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, { struct msgb *msg_tch = NULL, *msg_facch = NULL; struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan]; + uint8_t tch_mode = chan_state->tch_mode; ubit_t *burst, **bursts_p = &chan_state->dl_bursts; static ubit_t bits[148]; @@ -1118,6 +1119,15 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, chan_state->dl_ongoing_facch = 1; /* first of two tch frames */ } else if (chan_state->dl_ongoing_facch) /* second of two tch frames */ chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */ + else if (tch_mode == GSM48_CMODE_SPEECH_AMR) + /* the first FN 4,13,21 or 5,14,22 defines that CMI is included + * in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is + * included in frame. */ + tch_ahs_encode(*bursts_p, msg_tch->l2h + 2, + msgb_l2len(msg_tch) - 2, (((fn + 4) % 26) >> 2) & 1, + chan_state->codec, chan_state->codecs, + chan_state->dl_ft, + chan_state->dl_cmr); else tch_hr_encode(*bursts_p, msg_tch->l2h, msgb_l2len(msg_tch)); @@ -1486,6 +1496,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ int rc, amr = 0; + float ber; LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1544,6 +1555,28 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, rc = tch_hr_decode(tch_data, *bursts_p, (((fn + 26 - 10) % 26) >> 2) & 1); break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* the first FN 0,8,17 or 1,9,18 defines that CMI is included + * in frame, the first FN 4,13,21 or 5,14,22 defines that CMR + * is included in frame. + */ + rc = tch_ahs_decode(tch_data + 2, *bursts_p, + (((fn + 26 - 10) % 26) >> 2) & 1, + (((fn + 26 - 10) % 26) >> 2) & 1, chan_state->codec, + chan_state->codecs, &chan_state->ul_ft, + &chan_state->ul_cmr, &ber); + if (rc) + trx_loop_amr_input(l1h, + trx_chan_desc[chan].chan_nr | tn, chan_state, + ber); + amr = 2; /* we store tch_data + 2 two */ + /* only good speech frames get rtp header */ + if (rc != 23 && rc >= 4) { + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->ul_cmr], + chan_state->codec[chan_state->ul_ft], 0); + } + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode %u invalid, please fix!\n", tch_mode); @@ -1578,6 +1611,15 @@ bfi: memset(tch_data + 1, 0, 14); rc = 15; break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + rc = amr_compose_payload(tch_data, + chan_state->codec[chan_state->dl_cmr], + chan_state->codec[chan_state->dl_ft], + 1); + if (rc < 2) + break; + memset(tch_data + 2, 0, rc - 2); + break; default: LOGP(DL1C, LOGL_ERROR, "TCH mode invalid, " "please fix!\n"); -- cgit v1.2.3 From 6508f2113042d1fbd1cb6d7154c7701422f6b7c9 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 6 Apr 2013 12:26:03 +0200 Subject: TRX: Reset ciphering state when closing channel --- src/osmo-bts-trx/l1_if.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 9dbec31b..676a6762 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -516,6 +516,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) /* confirm only on dedicated channel */ mph_info_chan_confirm(l1h, chan_nr, PRIM_INFO_DEACTIVATE, 0); + lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */ } break; default: -- cgit v1.2.3 From 2e4a26a0e942e227a96819271ea0ea2b03566928 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 7 Apr 2013 12:49:52 +0200 Subject: TRX: Add VTY options to enable and disable SETTSC and SETBSIC --- src/osmo-bts-trx/main.c | 2 ++ src/osmo-bts-trx/trx_if.c | 14 +++++++++++-- src/osmo-bts-trx/trx_if.h | 3 +++ src/osmo-bts-trx/trx_vty.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 7701c2c9..7f368172 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -334,6 +334,8 @@ int main(int argc, char **argv) config_file); exit(1); } + if (!settsc_enabled && !setbsic_enabled) + settsc_enabled = setbsic_enabled = 1; write_pid_file("osmo-bts"); diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index bca9cdca..e6b70134 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -46,6 +46,8 @@ int tranceiver_available = 0; const char *tranceiver_ip = "127.0.0.1"; +int settsc_enabled = 0; +int setbsic_enabled = 0; /* * socket @@ -230,12 +232,20 @@ int trx_if_cmd_poweron(struct trx_l1h *l1h) int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc) { - return trx_ctrl_cmd(l1h, 0, "SETTSC", "%d", tsc); + if (!settsc_enabled) + return 0; + /* if TSC is enabled only, the positive response is mandatory */ + return trx_ctrl_cmd(l1h, (setbsic_enabled) ? 0 : 1, "SETTSC", "%d", + tsc); } int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic) { - return trx_ctrl_cmd(l1h, 0, "SETBSIC", "%d", bsic); + if (!setbsic_enabled) + return 0; + /* if BSIC is enabled only, the positive response is mandatory */ + return trx_ctrl_cmd(l1h, (settsc_enabled) ? 0 : 1, "SETBSIC", "%d", + bsic); } int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db) diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h index 8ec071fe..21812248 100644 --- a/src/osmo-bts-trx/trx_if.h +++ b/src/osmo-bts-trx/trx_if.h @@ -3,6 +3,9 @@ extern int tranceiver_available; extern const char *tranceiver_ip; +extern int settsc_enabled; +extern int setbsic_enabled; + struct trx_ctrl_msg { struct llist_head list; diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c index fa08df4d..207930a8 100644 --- a/src/osmo-bts-trx/trx_vty.c +++ b/src/osmo-bts-trx/trx_vty.c @@ -164,6 +164,50 @@ DEFUN(cfg_bts_no_timing_advance_loop, cfg_bts_no_timing_advance_loop_cmd, return CMD_SUCCESS; } +DEFUN(cfg_bts_settsc, cfg_bts_settsc_cmd, + "settsc", + "Use SETTSC to configure transceiver\n") +{ + settsc_enabled = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_setbsic, cfg_bts_setbsic_cmd, + "setbsic", + "Use SETBSIC to configure transceiver\n") +{ + setbsic_enabled = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_no_settsc, cfg_bts_no_settsc_cmd, + "no settsc", + NO_STR "Disable SETTSC to configure transceiver\n") +{ + settsc_enabled = 0; + if (!setbsic_enabled) { + vty_out(vty, "%% Auto enabling SETBSIC.%s", VTY_NEWLINE); + setbsic_enabled = 1; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_no_setbsic, cfg_bts_no_setbsic_cmd, + "no setbsic", + NO_STR "Disable SETBSIC to configure transceiver\n") +{ + setbsic_enabled = 0; + if (!settsc_enabled) { + vty_out(vty, "%% Auto enabling SETTSC.%s", VTY_NEWLINE); + settsc_enabled = 1; + } + + return CMD_SUCCESS; +} + DEFUN(cfg_trx_rxgain, cfg_trx_rxgain_cmd, "rxgain <0-50>", "Set the receiver gain in dB\n" @@ -282,6 +326,10 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts) vty_out(vty, " no ms-power-loop%s", VTY_NEWLINE); vty_out(vty, " %stiming-advance-loop%s", (trx_ta_loop) ? "":"no ", VTY_NEWLINE); + if (settsc_enabled) + vty_out(vty, " settsc%s", VTY_NEWLINE); + if (setbsic_enabled) + vty_out(vty, " setbsic%s", VTY_NEWLINE); } void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx) @@ -318,6 +366,10 @@ int bts_model_vty_init(struct gsm_bts *bts) install_element(BTS_NODE, &cfg_bts_no_ms_power_loop_cmd); install_element(BTS_NODE, &cfg_bts_timing_advance_loop_cmd); install_element(BTS_NODE, &cfg_bts_no_timing_advance_loop_cmd); + install_element(BTS_NODE, &cfg_bts_settsc_cmd); + install_element(BTS_NODE, &cfg_bts_setbsic_cmd); + install_element(BTS_NODE, &cfg_bts_no_settsc_cmd); + install_element(BTS_NODE, &cfg_bts_no_setbsic_cmd); install_element(TRX_NODE, &cfg_trx_rxgain_cmd); install_element(TRX_NODE, &cfg_trx_power_cmd); -- cgit v1.2.3 From c2ee307fd42975b63317e9d4944e5822286cc69b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 7 Apr 2013 13:11:50 +0200 Subject: Allow one or more TRX to configure via VTY --- include/osmo-bts/vty.h | 2 +- src/common/vty.c | 6 ++++-- src/osmo-bts-sysmo/main.c | 2 +- src/osmo-bts-trx/main.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/osmo-bts/vty.h b/include/osmo-bts/vty.h index 510abab1..9fcc5b5d 100644 --- a/include/osmo-bts/vty.h +++ b/include/osmo-bts/vty.h @@ -15,7 +15,7 @@ extern struct cmd_element ournode_end_cmd; enum node_type bts_vty_go_parent(struct vty *vty); int bts_vty_is_config_node(struct vty *vty, int node); -int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat); +int bts_vty_init(struct gsm_bts *bts, int trx_num, const struct log_info *cat); extern struct vty_app_info bts_vty_info; diff --git a/src/common/vty.c b/src/common/vty.c index e3fd57df..bca839bb 100644 --- a/src/common/vty.c +++ b/src/common/vty.c @@ -140,8 +140,9 @@ static struct cmd_node trx_node = { 1, }; +static char cfg_bts_trx_cmd_string[16]; DEFUN(cfg_bts_trx, cfg_bts_trx_cmd, - "trx <0-0>", + cfg_bts_trx_cmd_string, "Select a TRX to configure\n" "TRX number\n") { int trx_nr = atoi(argv[0]); @@ -762,7 +763,7 @@ DEFUN(no_bts_t_t_l_loopback, return CMD_SUCCESS; } -int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat) +int bts_vty_init(struct gsm_bts *bts, int trx_num, const struct log_info *cat) { cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names, "gsmtap-sapi (", @@ -802,6 +803,7 @@ int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat) install_element(BTS_NODE, &cfg_trx_no_gsmtap_sapi_cmd); /* add and link to TRX config node */ + sprintf(cfg_bts_trx_cmd_string, "trx <0-%d>", trx_num - 1); install_element(BTS_NODE, &cfg_bts_trx_cmd); install_node(&trx_node, config_write_dummy); install_default(TRX_NODE); diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c index bee5bda2..9913708f 100644 --- a/src/osmo-bts-sysmo/main.c +++ b/src/osmo-bts-sysmo/main.c @@ -320,7 +320,7 @@ int main(int argc, char **argv) bts = gsm_bts_alloc(tall_bts_ctx); vty_init(&bts_vty_info); e1inp_vty_init(); - bts_vty_init(bts, &bts_log_info); + bts_vty_init(bts, 1, &bts_log_info); handle_options(argc, argv); diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 7f368172..0879f2ee 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -310,7 +310,7 @@ int main(int argc, char **argv) vty_init(&bts_vty_info); e1inp_vty_init(); - bts_vty_init(bts, &bts_log_info); + bts_vty_init(bts, trx_num, &bts_log_info); if (bts_init(bts) < 0) { fprintf(stderr, "unable to to open bts\n"); -- cgit v1.2.3 From 82676c13ee9ab2b73f089acf811239f8e37fa0fd Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 9 Apr 2013 10:44:58 +0200 Subject: TRX: Fix: Cleanly free TRX instances during initialization in case of an error --- src/osmo-bts-trx/l1_if.c | 1 + src/osmo-bts-trx/trx_if.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 676a6762..8b00856a 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -80,6 +80,7 @@ struct trx_l1h *l1if_open(struct gsm_bts_trx *trx) err: l1if_close(l1h); + trx->role_bts.l1h = NULL; return NULL; } diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index e6b70134..1af10b5c 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -473,6 +473,9 @@ int trx_if_open(struct trx_l1h *l1h) LOGP(DTRX, LOGL_NOTICE, "Open tranceiver for trx=%u\n", l1h->trx->nr); + /* initialize ctrl queue */ + INIT_LLIST_HEAD(&l1h->trx_ctrl_list); + /* open sockets */ if (l1h->trx->nr == 0) { rc = trx_udp_open(NULL, &trx_ofd_clk, base_port_local, @@ -490,9 +493,6 @@ int trx_if_open(struct trx_l1h *l1h) if (rc < 0) goto err; - /* initialize ctrl queue */ - INIT_LLIST_HEAD(&l1h->trx_ctrl_list); - /* enable all slots */ l1h->config.slotmask = 0xff; -- cgit v1.2.3 From 05597a7ddbf9e838caaeb36980cefaa728ab8eec Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 9 Apr 2013 10:55:37 +0200 Subject: TRX: Fixed typos tranceiver -> transceiver --- src/osmo-bts-trx/l1_if.c | 50 +++++++++++++++--------------- src/osmo-bts-trx/l1_if.h | 8 ++--- src/osmo-bts-trx/main.c | 8 ++--- src/osmo-bts-trx/scheduler.c | 74 ++++++++++++++++++++++---------------------- src/osmo-bts-trx/scheduler.h | 2 +- src/osmo-bts-trx/trx_if.c | 24 +++++++------- src/osmo-bts-trx/trx_if.h | 4 +-- src/osmo-bts-trx/trx_vty.c | 22 ++++++------- 8 files changed, 96 insertions(+), 96 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 8b00856a..116b4fbb 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -40,7 +40,7 @@ #include "scheduler.h" -static const uint8_t tranceiver_chan_types[_GSM_PCHAN_MAX] = { +static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = { [GSM_PCHAN_NONE] = 8, [GSM_PCHAN_CCCH] = 6, [GSM_PCHAN_CCCH_SDCCH4] = 5, @@ -95,13 +95,13 @@ void l1if_reset(struct trx_l1h *l1h) { } -static void check_tranceiver_availability_trx(struct trx_l1h *l1h, int avail) +static void check_transceiver_availability_trx(struct trx_l1h *l1h, int avail) { struct gsm_bts_trx *trx = l1h->trx; uint8_t tn; /* HACK, we should change state when we receive first clock from - * tranceiver */ + * transceiver */ if (avail) { /* signal availability */ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK); @@ -126,27 +126,27 @@ static void check_tranceiver_availability_trx(struct trx_l1h *l1h, int avail) } } -int check_tranceiver_availability(struct gsm_bts *bts, int avail) +int check_transceiver_availability(struct gsm_bts *bts, int avail) { struct gsm_bts_trx *trx; struct trx_l1h *l1h; llist_for_each_entry(trx, &bts->trx_list, list) { l1h = trx_l1h_hdl(trx); - check_tranceiver_availability_trx(l1h, avail); + check_transceiver_availability_trx(l1h, avail); } return 0; } /* - * tranceiver provisioning + * transceiver provisioning */ -int l1if_provision_tranceiver_trx(struct trx_l1h *l1h) +int l1if_provision_transceiver_trx(struct trx_l1h *l1h) { uint8_t tn; - if (!tranceiver_available) + if (!transceiver_available) return -EIO; if (l1h->config.poweron @@ -210,7 +210,7 @@ int l1if_provision_tranceiver_trx(struct trx_l1h *l1h) return 0; } -int l1if_provision_tranceiver(struct gsm_bts *bts) +int l1if_provision_transceiver(struct gsm_bts *bts) { struct gsm_bts_trx *trx; struct trx_l1h *l1h; @@ -227,13 +227,13 @@ int l1if_provision_tranceiver(struct gsm_bts *bts) l1h->config.maxdly_sent = 0; for (tn = 0; tn < 8; tn++) l1h->config.slottype_sent[tn] = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } return 0; } /* - * activation/configuration/deactivation of tranceiver's TRX + * activation/configuration/deactivation of transceiver's TRX */ /* initialize the layer1 */ @@ -241,11 +241,11 @@ static int trx_init(struct gsm_bts_trx *trx) { struct trx_l1h *l1h = trx_l1h_hdl(trx); - /* power on tranceiver, if not already */ + /* power on transceiver, if not already */ if (!l1h->config.poweron) { l1h->config.poweron = 1; l1h->config.poweron_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } if (trx == trx->bts->c0) @@ -258,7 +258,7 @@ static int trx_init(struct gsm_bts_trx *trx) return oml_mo_opstart_ack(&trx->mo); } -/* deactivate tranceiver */ +/* deactivate transceiver */ static int trx_close(struct gsm_bts_trx *trx) { struct trx_l1h *l1h = trx_l1h_hdl(trx); @@ -266,15 +266,15 @@ static int trx_close(struct gsm_bts_trx *trx) /* close all logical channels and reset timeslots */ trx_sched_reset(l1h); - /* power off tranceiver, if not already */ + /* power off transceiver, if not already */ if (l1h->config.poweron) { l1h->config.poweron = 0; l1h->config.poweron_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } /* Set to Operational State: Disabled */ - check_tranceiver_availability_trx(l1h, 0); + check_transceiver_availability_trx(l1h, 0); return 0; } @@ -298,10 +298,10 @@ static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr) l1h->config.bsic = bsic; l1h->config.bsic_valid = 1; l1h->config.bsic_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } } - check_tranceiver_availability(bts, tranceiver_available); + check_transceiver_availability(bts, transceiver_available); return 0; @@ -317,7 +317,7 @@ static uint8_t trx_set_trx(struct gsm_bts_trx *trx) l1h->config.arfcn = arfcn; l1h->config.arfcn_valid = 1; l1h->config.arfcn_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } return 0; @@ -333,14 +333,14 @@ static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts) uint8_t slottype; int rc; - /* all TSC of all timeslots must be equal, because tranceiver only + /* all TSC of all timeslots must be equal, because transceiver only * supports one TSC per TRX */ if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) { l1h->config.tsc = tsc; l1h->config.tsc_valid = 1; l1h->config.tsc_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } /* set physical channel */ @@ -348,14 +348,14 @@ static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts) if (rc) return NM_NACK_RES_NOTAVAIL; - slottype = tranceiver_chan_types[pchan]; + slottype = transceiver_chan_types[pchan]; if (l1h->config.slottype[tn] != slottype || !l1h->config.slottype_valid[tn]) { l1h->config.slottype[tn] = slottype; l1h->config.slottype_valid[tn] = 1; l1h->config.slottype_sent[tn] = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); } return 0; @@ -584,7 +584,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, switch (mo->obj_class) { case NM_OC_RADIO_CARRIER: - /* activate tranceiver */ + /* activate transceiver */ rc = trx_init(obj); break; case NM_OC_CHANNEL: diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 0b5d0855..deae874c 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -144,7 +144,7 @@ struct trx_l1h { struct osmo_timer_list trx_ctrl_timer; struct osmo_fd trx_ofd_data; - /* tranceiver config */ + /* transceiver config */ struct trx_config config; uint8_t mf_index[8]; /* selected multiframe index */ @@ -160,9 +160,9 @@ struct trx_l1h { struct trx_l1h *l1if_open(struct gsm_bts_trx *trx); void l1if_close(struct trx_l1h *l1h); void l1if_reset(struct trx_l1h *l1h); -int check_tranceiver_availability(struct gsm_bts *bts, int avail); -int l1if_provision_tranceiver_trx(struct trx_l1h *l1h); -int l1if_provision_tranceiver(struct gsm_bts *bts); +int check_transceiver_availability(struct gsm_bts *bts, int avail); +int l1if_provision_transceiver_trx(struct trx_l1h *l1h); +int l1if_provision_transceiver(struct gsm_bts *bts); int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); #endif /* L1_IF_H_TRX */ diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 0879f2ee..ab71c861 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -155,8 +155,8 @@ static void print_help() " -t --trx-num Set number of TRX (default=%d)\n" " -i --gsmtap-ip The destination IP used for GSMTAP.\n" " -H --high-prio Set realtime scheduler with maximum prio\n" - " -I --tranceiver-ip Set IP of tranceiver (default=%s)\n" - ,trx_num, tranceiver_ip); + " -I --local-trx-ip Local IP for transceiver to connect (default=%s)\n" + ,trx_num, transceiver_ip); } /* FIXME: finally get some option parsing code into libosmocore */ @@ -177,7 +177,7 @@ static void handle_options(int argc, char **argv) { "trx-num", 1, 0, 't' }, { "gsmtap-ip", 1, 0, 'i' }, { "high-prio", 0, 0, 'H' }, - { "tranceiver-ip", 1, 0, 'I' }, + { "local-trx-ip", 1, 0, 'I' }, { 0, 0, 0, 0 } }; @@ -225,7 +225,7 @@ static void handle_options(int argc, char **argv) high_prio = 1; break; case 'I': - tranceiver_ip = strdup(optarg); + transceiver_ip = strdup(optarg); break; default: break; diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9126e342..9d6a8a97 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -53,12 +53,12 @@ void *tall_bts_ctx; static struct gsm_bts *bts; /* clock states */ -static uint32_t tranceiver_lost; -uint32_t tranceiver_last_fn; -static struct timeval tranceiver_clock_tv; -static struct osmo_timer_list tranceiver_clock_timer; +static uint32_t transceiver_lost; +uint32_t transceiver_last_fn; +static struct timeval transceiver_clock_tv; +static struct osmo_timer_list transceiver_clock_timer; -/* clock advance for the tranceiver */ +/* clock advance for the transceiver */ uint32_t trx_clock_advance = 20; /* advance RTS to give some time for data processing. (especially PCU) */ @@ -2729,7 +2729,7 @@ static int trx_sched_fn(uint32_t fn) /* send time indication */ l1if_mph_time_ind(bts, fn); - /* advance frame number, so the tranceiver has more time until + /* advance frame number, so the transceiver has more time until * it must be transmitted. */ fn = (fn + trx_clock_advance) % 2715648; @@ -2777,17 +2777,17 @@ extern int quit; /* this timer fires for every FN to be processed */ static void trx_ctrl_timer_cb(void *data) { - struct timeval tv_now, *tv_clock = &tranceiver_clock_tv; + struct timeval tv_now, *tv_clock = &transceiver_clock_tv; int32_t elapsed; - /* check if tranceiver is still alive */ - if (tranceiver_lost++ == TRX_LOSS_FRAMES) { + /* check if transceiver is still alive */ + if (transceiver_lost++ == TRX_LOSS_FRAMES) { struct gsm_bts_trx *trx; LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); no_clock: - tranceiver_available = 0; + transceiver_available = 0; /* flush pending messages of transceiver */ /* close all logical channels and reset timeslots */ @@ -2799,7 +2799,7 @@ no_clock: } /* tell BSC */ - check_tranceiver_availability(bts, 0); + check_transceiver_availability(bts, 0); return; } @@ -2823,19 +2823,19 @@ no_clock: tv_clock->tv_sec++; tv_clock->tv_usec -= 1000000; } - tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; - trx_sched_fn(tranceiver_last_fn); + transceiver_last_fn = (transceiver_last_fn + 1) % 2715648; + trx_sched_fn(transceiver_last_fn); elapsed -= FRAME_DURATION_uS; } - osmo_timer_schedule(&tranceiver_clock_timer, 0, + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS - elapsed); } -/* receive clock from tranceiver */ +/* receive clock from transceiver */ int trx_sched_clock(uint32_t fn) { - struct timeval tv_now, *tv_clock = &tranceiver_clock_tv; + struct timeval tv_now, *tv_clock = &transceiver_clock_tv; int32_t elapsed; int32_t elapsed_fn; @@ -2843,54 +2843,54 @@ int trx_sched_clock(uint32_t fn) return 0; /* reset lost counter */ - tranceiver_lost = 0; + transceiver_lost = 0; gettimeofday(&tv_now, NULL); /* clock becomes valid */ - if (!tranceiver_available) { + if (!transceiver_available) { LOGP(DL1C, LOGL_NOTICE, "initial GSM clock received: fn=%u\n", fn); - tranceiver_available = 1; + transceiver_available = 1; - /* start provisioning tranceiver */ - l1if_provision_tranceiver(bts); + /* start provisioning transceiver */ + l1if_provision_transceiver(bts); /* tell BSC */ - check_tranceiver_availability(bts, 1); + check_transceiver_availability(bts, 1); new_clock: - tranceiver_last_fn = fn; - trx_sched_fn(tranceiver_last_fn); + transceiver_last_fn = fn; + trx_sched_fn(transceiver_last_fn); /* schedule first FN clock */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); - memset(&tranceiver_clock_timer, 0, - sizeof(tranceiver_clock_timer)); - tranceiver_clock_timer.cb = trx_ctrl_timer_cb; - tranceiver_clock_timer.data = bts; - osmo_timer_schedule(&tranceiver_clock_timer, 0, + memset(&transceiver_clock_timer, 0, + sizeof(transceiver_clock_timer)); + transceiver_clock_timer.cb = trx_ctrl_timer_cb; + transceiver_clock_timer.data = bts; + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS); return 0; } - osmo_timer_del(&tranceiver_clock_timer); + osmo_timer_del(&transceiver_clock_timer); /* calculate elapsed time since last_fn */ elapsed = (tv_now.tv_sec - tv_clock->tv_sec) * 1000000 + (tv_now.tv_usec - tv_clock->tv_usec); /* how much frames have been elapsed since last fn processed */ - elapsed_fn = (fn + 2715648 - tranceiver_last_fn) % 2715648; + elapsed_fn = (fn + 2715648 - transceiver_last_fn) % 2715648; if (elapsed_fn >= 135774) elapsed_fn -= 2715648; /* check for max clock skew */ if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) { LOGP(DL1C, LOGL_NOTICE, "GSM clock skew: old fn=%u, " - "new fn=%u\n", tranceiver_last_fn, fn); + "new fn=%u\n", transceiver_last_fn, fn); goto new_clock; } @@ -2909,21 +2909,21 @@ new_clock: tv_clock->tv_usec -= 1000000; } /* set time to the time our next FN hast to be transmitted */ - osmo_timer_schedule(&tranceiver_clock_timer, 0, + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS * (1 - elapsed_fn)); return 0; } /* transmit what we still need to transmit */ - while (fn != tranceiver_last_fn) { - tranceiver_last_fn = (tranceiver_last_fn + 1) % 2715648; - trx_sched_fn(tranceiver_last_fn); + while (fn != transceiver_last_fn) { + transceiver_last_fn = (transceiver_last_fn + 1) % 2715648; + trx_sched_fn(transceiver_last_fn); } /* schedule next FN to be transmitted */ memcpy(tv_clock, &tv_now, sizeof(struct timeval)); - osmo_timer_schedule(&tranceiver_clock_timer, 0, FRAME_DURATION_uS); + osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS); return 0; } diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index 2ab7abeb..f2873a11 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -2,7 +2,7 @@ #define TRX_SCHEDULER_H extern uint32_t trx_clock_advance; -extern uint32_t tranceiver_last_fn; +extern uint32_t transceiver_last_fn; int trx_sched_init(struct trx_l1h *l1h); diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 1af10b5c..746e95bb 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -44,8 +44,8 @@ /* enable to print RSSI level graph */ //#define TOA_RSSI_DEBUG -int tranceiver_available = 0; -const char *tranceiver_ip = "127.0.0.1"; +int transceiver_available = 0; +const char *transceiver_ip = "127.0.0.1"; int settsc_enabled = 0; int setbsic_enabled = 0; @@ -71,7 +71,7 @@ static int trx_udp_open(void *priv, struct osmo_fd *ofd, uint16_t port, ofd->data = priv; /* Listen / Binds */ - rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, tranceiver_ip, + rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, transceiver_ip, port, OSMO_SOCK_F_BIND); if (rc < 0) return rc; @@ -175,7 +175,7 @@ static void trx_ctrl_timer_cb(void *data) { struct trx_l1h *l1h = data; - LOGP(DTRX, LOGL_NOTICE, "No response from tranceiver\n"); + LOGP(DTRX, LOGL_NOTICE, "No response from transceiver\n"); trx_ctrl_send(l1h); } @@ -188,9 +188,9 @@ static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd, va_list ap; int l, pending = 0; - if (!tranceiver_available && !!strcmp(cmd, "POWEROFF")) { + if (!transceiver_available && !!strcmp(cmd, "POWEROFF")) { LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from " - "tranceiver, please fix!\n"); + "transceiver, please fix!\n"); return -EIO; } @@ -348,7 +348,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) sscanf(p + 1, "%d", &resp); if (resp) { LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE, - "Tranceiver rejected TRX command with " + "transceiver rejected TRX command with " "response: '%s'\n", buf); rsp_error: if (tcm->critical) { @@ -453,10 +453,10 @@ int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr, /* we must be sure that we have clock, and we have sent all control * data */ - if (tranceiver_available && llist_empty(&l1h->trx_ctrl_list)) { + if (transceiver_available && llist_empty(&l1h->trx_ctrl_list)) { send(l1h->trx_ofd_data.fd, buf, 154, 0); } else - LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, tranceiver " + LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, transceiver " "offline.\n"); return 0; @@ -471,7 +471,7 @@ int trx_if_open(struct trx_l1h *l1h) { int rc; - LOGP(DTRX, LOGL_NOTICE, "Open tranceiver for trx=%u\n", l1h->trx->nr); + LOGP(DTRX, LOGL_NOTICE, "Open transceiver for trx=%u\n", l1h->trx->nr); /* initialize ctrl queue */ INIT_LLIST_HEAD(&l1h->trx_ctrl_list); @@ -482,7 +482,7 @@ int trx_if_open(struct trx_l1h *l1h) trx_clk_read_cb); if (rc < 0) return rc; - LOGP(DTRX, LOGL_NOTICE, "Waiting for tranceiver send clock\n"); + LOGP(DTRX, LOGL_NOTICE, "Waiting for transceiver send clock\n"); } rc = trx_udp_open(l1h, &l1h->trx_ofd_ctrl, base_port_local + (l1h->trx->nr << 1) + 1, trx_ctrl_read_cb); @@ -522,7 +522,7 @@ void trx_if_flush(struct trx_l1h *l1h) void trx_if_close(struct trx_l1h *l1h) { - LOGP(DTRX, LOGL_NOTICE, "Close tranceiver for trx=%u\n", l1h->trx->nr); + LOGP(DTRX, LOGL_NOTICE, "Close transceiver for trx=%u\n", l1h->trx->nr); trx_if_flush(l1h); diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h index 21812248..f041c933 100644 --- a/src/osmo-bts-trx/trx_if.h +++ b/src/osmo-bts-trx/trx_if.h @@ -1,8 +1,8 @@ #ifndef TRX_IF_H #define TRX_IF_H -extern int tranceiver_available; -extern const char *tranceiver_ip; +extern int transceiver_available; +extern const char *transceiver_ip; extern int settsc_enabled; extern int setbsic_enabled; diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c index 207930a8..4e675921 100644 --- a/src/osmo-bts-trx/trx_vty.c +++ b/src/osmo-bts-trx/trx_vty.c @@ -47,19 +47,19 @@ static struct gsm_bts *vty_bts; -DEFUN(show_tranceiver, show_tranceiver_cmd, "show tranceiver", - SHOW_STR "Display information about tranceivers\n") +DEFUN(show_transceiver, show_transceiver_cmd, "show transceiver", + SHOW_STR "Display information about transceivers\n") { struct gsm_bts *bts = vty_bts; struct gsm_bts_trx *trx; struct trx_l1h *l1h; uint8_t tn; - if (!tranceiver_available) { - vty_out(vty, "Tranceiver is not connected%s", VTY_NEWLINE); + if (!transceiver_available) { + vty_out(vty, "transceiver is not connected%s", VTY_NEWLINE); } else { - vty_out(vty, "Tranceiver is connected, current fn=%u%s", - tranceiver_last_fn, VTY_NEWLINE); + vty_out(vty, "transceiver is connected, current fn=%u%s", + transceiver_last_fn, VTY_NEWLINE); } llist_for_each_entry(trx, &bts->trx_list, list) { @@ -129,7 +129,7 @@ DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd, DEFUN(cfg_bts_ms_power_loop, cfg_bts_ms_power_loop_cmd, "ms-power-loop <-127-127>", - "Enable MS power control loop\nTarget RSSI value (tranceiver specific, " + "Enable MS power control loop\nTarget RSSI value (transceiver specific, " "should be 6dB or more above noise floor)\n") { trx_ms_power_loop = 1; @@ -219,7 +219,7 @@ DEFUN(cfg_trx_rxgain, cfg_trx_rxgain_cmd, l1h->config.rxgain = atoi(argv[0]); l1h->config.rxgain_valid = 1; l1h->config.rxgain_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); return CMD_SUCCESS; } @@ -235,7 +235,7 @@ DEFUN(cfg_trx_power, cfg_trx_power_cmd, l1h->config.power = atoi(argv[0]); l1h->config.power_valid = 1; l1h->config.power_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); return CMD_SUCCESS; } @@ -251,7 +251,7 @@ DEFUN(cfg_trx_maxdly, cfg_trx_maxdly_cmd, l1h->config.maxdly = atoi(argv[0]); l1h->config.maxdly_valid = 1; l1h->config.maxdly_sent = 0; - l1if_provision_tranceiver_trx(l1h); + l1if_provision_transceiver_trx(l1h); return CMD_SUCCESS; } @@ -359,7 +359,7 @@ int bts_model_vty_init(struct gsm_bts *bts) { vty_bts = bts; - install_element_ve(&show_tranceiver_cmd); + install_element_ve(&show_transceiver_cmd); install_element(BTS_NODE, &cfg_bts_fn_advance_cmd); install_element(BTS_NODE, &cfg_bts_ms_power_loop_cmd); -- cgit v1.2.3 From fb04746bce94a5cdd3e95500c59ad019e552f9c0 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 6 Jun 2013 13:24:40 +0200 Subject: TRX: Report measurements --- src/osmo-bts-trx/l1_if.c | 22 ++++++++++++++++++++++ src/osmo-bts-trx/l1_if.h | 6 +++++- src/osmo-bts-trx/scheduler.c | 35 ++++++++++++++++++++++++++++------- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 116b4fbb..a09fa199 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -425,6 +425,23 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn) return l1sap_up(bts->c0, &l1sap); } +int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr, float qta, + float ber, float rssi) +{ + struct osmo_phsap_prim l1sap; + + memset(&l1sap, 0, sizeof(l1sap)); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, + PRIM_OP_INDICATION, NULL); + l1sap.u.info.type = PRIM_INFO_MEAS; + l1sap.u.info.u.meas_ind.chan_nr = chan_nr; + l1sap.u.info.u.meas_ind.ta_offs_qbits = qta; + l1sap.u.info.u.meas_ind.ber10k = (unsigned int) (ber * 100); + l1sap.u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1); + + return l1sap_up(trx, &l1sap); +} + /* primitive from common part */ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) @@ -487,6 +504,8 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) amr_get_initial_mode(lchan)); /* init lapdm */ lchan_init_lapdm(lchan); + /* set lchan active */ + lchan_set_state(lchan, LCHAN_S_ACTIVE); /* confirm */ mph_info_chan_confirm(l1h, chan_nr, PRIM_INFO_ACTIVATE, 0); @@ -511,6 +530,9 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) } /* deactivate assoicated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x40, 0); + /* set lchan inactive + * (also if only sacch, so no meaurement is done) */ + lchan_set_state(lchan, LCHAN_S_NONE); /* deactivate dedicated channel */ if (!l1sap->u.info.u.act_req.sacch_only) { trx_sched_set_lchan(l1h, chan_nr, 0x00, 0); diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index deae874c..9e4a3f47 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -55,9 +55,11 @@ struct trx_chan_state { uint32_t ul_first_fn; /* fn of first burst */ uint8_t ul_mask; /* mask of received bursts */ - /* RSSI */ + /* RSSI / TOA */ uint8_t rssi_num; /* number of RSSI values */ float rssi_sum; /* sum of RSSI values */ + uint8_t toa_num; /* number of TOA values */ + float toa_sum; /* sum of TOA values */ /* loss detection */ uint8_t lost; /* (SACCH) loss detection */ @@ -164,5 +166,7 @@ int check_transceiver_availability(struct gsm_bts *bts, int avail); int l1if_provision_transceiver_trx(struct trx_l1h *l1h); int l1if_provision_transceiver(struct gsm_bts *bts); int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); +int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr, float qta, + float ber, float rssi); #endif /* L1_IF_H_TRX */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9d6a8a97..20cafade 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -559,17 +559,19 @@ found_msg: } static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float toa, + float ber, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; + uint8_t chan_nr = trx_chan_desc[chan].chan_nr | tn; /* compose primitive */ msg = l1sap_msgb_alloc(l2_len); l1sap = msgb_l1sap_prim(msg); osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_PH_DATA, PRIM_OP_INDICATION, msg); - l1sap->u.data.chan_nr = trx_chan_desc[chan].chan_nr | tn; + l1sap->u.data.chan_nr = chan_nr; l1sap->u.data.link_id = trx_chan_desc[chan].link_id; l1sap->u.data.fn = fn; l1sap->u.data.rssi = (int8_t) (rssi); @@ -583,6 +585,12 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* forward primitive */ l1sap_up(l1h->trx, l1sap); + /* process measurement */ + if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) + l1if_process_meas_res(l1h->trx, chan_nr, + (l1h->trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)].rqd_ta + toa) * 4, + ber, rssi); + return 0; } @@ -657,7 +665,8 @@ got_msg: if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { /* count and send BFI */ if (++(l1h->chan_states[tn][chan].lost) > 1) - compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -128); + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, 0, 0, + -110); } /* alloc burst memory, if not already */ @@ -1205,6 +1214,8 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t *mask = &chan_state->ul_mask; float *rssi_sum = &chan_state->rssi_sum; uint8_t *rssi_num = &chan_state->rssi_num; + float *toa_sum = &chan_state->toa_sum; + uint8_t *toa_num = &chan_state->toa_num; uint8_t l2[23], l2_len; int rc; @@ -1225,12 +1236,16 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *first_fn = fn; *rssi_sum = 0; *rssi_num = 0; + *toa_sum = 0; + *toa_num = 0; } /* update mask + rssi */ *mask |= (1 << bid); *rssi_sum += rssi; (*rssi_num)++; + *toa_sum += toa; + (*toa_num)++; /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -1274,7 +1289,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2_len = 23; return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, - *rssi_sum / *rssi_num); + *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); } static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1286,6 +1301,8 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t *mask = &chan_state->ul_mask; float *rssi_sum = &chan_state->rssi_sum; uint8_t *rssi_num = &chan_state->rssi_num; + float *toa_sum = &chan_state->toa_sum; + uint8_t *toa_num = &chan_state->toa_num; uint8_t l2[54+1]; int rc; @@ -1305,12 +1322,16 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *mask = 0x0; *rssi_sum = 0; *rssi_num = 0; + *toa_sum = 0; + *toa_num = 0; } /* update mask + rssi */ *mask |= (1 << bid); *rssi_sum += rssi; (*rssi_num)++; + *toa_sum += toa; + (*toa_num)++; /* copy burst to buffer of 4 bursts */ burst = *bursts_p + bid * 116; @@ -1342,7 +1363,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, - l2, rc + 1, *rssi_sum / *rssi_num); + l2, rc + 1, *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1447,7 +1468,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data + amr, 23, rssi); + tch_data + amr, 23, 0, 0, 0); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1601,7 +1622,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, chan_state->ul_ongoing_facch = 1; compose_ph_data_ind(l1h, tn, (fn + 2715648 - 10 - ((fn % 26) >= 19)) % 2715648, chan, - tch_data + amr, 23, rssi); + tch_data + amr, 23, 0, 0, 0); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ -- cgit v1.2.3 From 6527dffc94686672add10fe6b66f898c436ea34c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 09:08:44 +0200 Subject: TRX: Clear lchan state when resetting TRX --- src/osmo-bts-trx/scheduler.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 20cafade..bcde7f30 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -248,6 +248,9 @@ void trx_sched_exit(struct trx_l1h *l1h) chan_state->ul_bursts = NULL; } } + /* clear lchan channel states */ + for (i = 0; i < 8; i++) + l1h->trx->ts[tn].lchan[i].state = LCHAN_S_NONE; } } -- cgit v1.2.3 From b9a917a13880f9d6274409b9d3c9b56de484125f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 09:12:04 +0200 Subject: TRX: Handover access burst support --- src/osmo-bts-trx/l1_if.c | 6 ++++-- src/osmo-bts-trx/l1_if.h | 4 ++++ src/osmo-bts-trx/scheduler.c | 47 +++++++++++++++++++++++++++++++++++++------- src/osmo-bts-trx/scheduler.h | 3 ++- src/osmo-bts-trx/trx_if.c | 10 ++++++++++ src/osmo-bts-trx/trx_if.h | 2 ++ 6 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index a09fa199..986c9889 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -501,7 +501,8 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) lchan->tch.amr_mr.mode[1].mode, lchan->tch.amr_mr.mode[2].mode, lchan->tch.amr_mr.mode[3].mode, - amr_get_initial_mode(lchan)); + amr_get_initial_mode(lchan), + (lchan->ho.active == 1)); /* init lapdm */ lchan_init_lapdm(lchan); /* set lchan active */ @@ -520,7 +521,8 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) lchan->tch.amr_mr.mode[1].mode, lchan->tch.amr_mr.mode[2].mode, lchan->tch.amr_mr.mode[3].mode, - amr_get_initial_mode(lchan)); + amr_get_initial_mode(lchan), + 0); break; } if ((chan_nr & 0x80)) { diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 9e4a3f47..5b4dce3c 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -100,6 +100,9 @@ struct trx_chan_state { float toa_sum; /* sum of TOA values */ int toa_num; /* number of TOA value */ } meas; + + /* handover */ + uint8_t ho_rach_detect; /* if rach detection is on */ }; struct trx_config { @@ -157,6 +160,7 @@ struct trx_l1h { /* Channel states for all channels on all timeslots */ struct trx_chan_state chan_states[8][_TRX_CHAN_MAX]; struct llist_head dl_prims[8]; /* Queue primitves for TX */ + uint8_t ho_rach_detect[8][8]; }; struct trx_l1h *l1if_open(struct gsm_bts_trx *trx); diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index bcde7f30..13e14e8b 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1173,17 +1173,20 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi, float toa) { + uint8_t chan_nr; struct osmo_phsap_prim l1sap; uint8_t ra; int rc; - LOGP(DL1C, LOGL_NOTICE, "Received %s fn=%u toa=%.2f\n", + chan_nr = trx_chan_desc[chan].chan_nr | tn; + + LOGP(DL1C, LOGL_NOTICE, "Received Access Burst on %s fn=%u toa=%.2f\n", trx_chan_desc[chan].name, fn, toa); /* decode */ rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic); if (rc) { - LOGP(DL1C, LOGL_NOTICE, "Received bad rach frame at fn=%u " + LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u " "(%u/51)\n", fn, fn % 51); return 0; } @@ -1193,6 +1196,7 @@ static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(&l1sap, 0, sizeof(l1sap)); osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL); + l1sap.u.rach_ind.chan_nr = chan_nr; l1sap.u.rach_ind.ra = ra; #ifdef TA_TEST #warning TIMING ADVANCE TEST-HACK IS ENABLED!!! @@ -1222,6 +1226,10 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t l2[23], l2_len; int rc; + /* handle rach, if handover rach detection is turned on */ + if (chan_state->ho_rach_detect == 1) + return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa); + LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1382,6 +1390,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, int rc, amr = 0; float ber; + /* handle rach, if handover rach detection is turned on */ + if (chan_state->ho_rach_detect == 1) + return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa); + LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -1522,6 +1534,10 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, int rc, amr = 0; float ber; + /* handle rach, if handover rach detection is turned on */ + if (chan_state->ho_rach_detect == 1) + return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa); + LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n", trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid); @@ -2487,9 +2503,10 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, - uint8_t codec2, uint8_t codec3, uint8_t initial_id) + uint8_t codec2, uint8_t codec3, uint8_t initial_id, uint8_t handover) { uint8_t tn = L1SAP_CHAN2TS(chan_nr); + uint8_t ss = l1sap_chan2ss(chan_nr); int i; int rc = -EINVAL; struct trx_chan_state *chan_state; @@ -2499,11 +2516,13 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) && trx_chan_desc[i].link_id == 0x00) { chan_state = &l1h->chan_states[tn][i]; - LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u on " - "%s of trx=%d ts=%d\n", rsl_cmode, tch_mode, - trx_chan_desc[i].name, l1h->trx->nr, tn); + LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u, handover %u " + "on %s of trx=%d ts=%d\n", rsl_cmode, tch_mode, + handover, trx_chan_desc[i].name, l1h->trx->nr, + tn); chan_state->rsl_cmode = rsl_cmode; chan_state->tch_mode = tch_mode; + chan_state->ho_rach_detect = handover; if (rsl_cmode == RSL_CMOD_SPD_SPEECH && tch_mode == GSM48_CMODE_SPEECH_AMR) { chan_state->codecs = codecs; @@ -2522,6 +2541,19 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, } } + /* command rach detection + * always enable handover, even if state is still set (due to loss + * of transceiver link). + * disable handover, if state is still set, since we might not know + * the actual state of transceiver (due to loss of link) */ + if (handover) { + l1h->ho_rach_detect[tn][ss] = 1; + trx_if_cmd_handover(l1h, tn, ss); + } else if (l1h->ho_rach_detect[tn][ss]) { + l1h->ho_rach_detect[tn][ss] = 0; + trx_if_cmd_nohandover(l1h, tn, ss); + } + return rc; } @@ -2721,7 +2753,8 @@ int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn, } func(l1h, tn, fn, chan, bid, bits, rssi, toa); - } else if (chan != TRXC_RACH) { + } else if (chan != TRXC_RACH + && !l1h->chan_states[tn][chan].ho_rach_detect) { sbit_t spare[148]; memset(spare, 0, 148); diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index f2873a11..acc2cebe 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -29,7 +29,8 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, /* setting all logical channels given attributes to active/inactive */ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1, - uint8_t codec2, uint8_t codec3, uint8_t initial_codec); + uint8_t codec2, uint8_t codec3, uint8_t initial_codec, + uint8_t handover); /* setting cipher on logical channels */ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 746e95bb..e798ca53 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -294,6 +294,16 @@ int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn) return trx_ctrl_cmd(l1h, 1, "TXTUNE", "%d", freq10 * 100); } +int trx_if_cmd_handover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss) +{ + return trx_ctrl_cmd(l1h, 1, "HANDOVER", "%d %d", tn, ss); +} + +int trx_if_cmd_nohandover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss) +{ + return trx_ctrl_cmd(l1h, 1, "NOHANDOVER", "%d %d", tn, ss); +} + /* get response from ctrl socket */ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) { diff --git a/src/osmo-bts-trx/trx_if.h b/src/osmo-bts-trx/trx_if.h index f041c933..ac0ee42c 100644 --- a/src/osmo-bts-trx/trx_if.h +++ b/src/osmo-bts-trx/trx_if.h @@ -24,6 +24,8 @@ int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly); int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type); int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn); int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn); +int trx_if_cmd_handover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss); +int trx_if_cmd_nohandover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss); int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr, const ubit_t *bits); int trx_if_open(struct trx_l1h *l1h); -- cgit v1.2.3 From 86c936cbb104cbac0994895bef03d8808ba032d4 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 12:36:48 +0200 Subject: TRX: Use correct slot type for GSM_PHCAN_BCCH --- src/osmo-bts-trx/l1_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 986c9889..7e09a01b 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -42,7 +42,7 @@ static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = { [GSM_PCHAN_NONE] = 8, - [GSM_PCHAN_CCCH] = 6, + [GSM_PCHAN_CCCH] = 4, [GSM_PCHAN_CCCH_SDCCH4] = 5, [GSM_PCHAN_TCH_F] = 1, [GSM_PCHAN_TCH_H] = 2, -- cgit v1.2.3 From db0b93ac3967f12b519ca4e2528e30aacd59ce72 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 12 Jun 2013 16:53:55 +0200 Subject: TRX: Disable handover burst detection when closing channel during detection --- src/osmo-bts-trx/scheduler.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 13e14e8b..9aa05390 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2471,6 +2471,7 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, int active) { uint8_t tn = L1SAP_CHAN2TS(chan_nr); + uint8_t ss = l1sap_chan2ss(chan_nr); int i; int rc = -EINVAL; struct trx_chan_state *chan_state; @@ -2497,6 +2498,12 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, } } + /* disable handover detection (on deactivation) */ + if (l1h->ho_rach_detect[tn][ss]) { + l1h->ho_rach_detect[tn][ss] = 0; + trx_if_cmd_nohandover(l1h, tn, ss); + } + return rc; } -- cgit v1.2.3 From 798c1bba9d48896dc6baa4340e252cda27cb30d2 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 16 Jun 2013 15:25:37 +0200 Subject: TRX: Process real time scheduling option is now similar to sysmobts --- src/osmo-bts-trx/main.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index ab71c861..dbf7c69a 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -63,7 +63,7 @@ int quit = 0; static const char *config_file = "osmo-bts.cfg"; static int daemonize = 0; static char *gsmtap_ip = 0; -static int high_prio = 0; +static int rt_prio = -1; static int trx_num = 1; char *software_version = "0.0"; uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 }; @@ -154,7 +154,7 @@ static void print_help() " -e --log-level Set a global log-level\n" " -t --trx-num Set number of TRX (default=%d)\n" " -i --gsmtap-ip The destination IP used for GSMTAP.\n" - " -H --high-prio Set realtime scheduler with maximum prio\n" + " -r --realtime PRIO Set realtime scheduler with given prio\n" " -I --local-trx-ip Local IP for transceiver to connect (default=%s)\n" ,trx_num, transceiver_ip); } @@ -176,12 +176,12 @@ static void handle_options(int argc, char **argv) { "log-level", 1, 0, 'e' }, { "trx-num", 1, 0, 't' }, { "gsmtap-ip", 1, 0, 'i' }, - { "high-prio", 0, 0, 'H' }, + { "realtime", 1, 0, 'r' }, { "local-trx-ip", 1, 0, 'I' }, { 0, 0, 0, 0 } }; - c = getopt_long(argc, argv, "hc:d:Dc:sTVe:t:i:HI:", + c = getopt_long(argc, argv, "hc:d:Dc:sTVe:t:i:r:I:", long_options, &option_idx); if (c == -1) break; @@ -221,8 +221,8 @@ static void handle_options(int argc, char **argv) case 'i': gsmtap_ip = optarg; break; - case 'H': - high_prio = 1; + case 'r': + rt_prio = atoi(optarg); break; case 'I': transceiver_ip = strdup(optarg); @@ -375,16 +375,16 @@ int main(int argc, char **argv) } } - if (high_prio) { + if (rt_prio != -1) { struct sched_param schedp; /* high priority scheduling required for handling bursts */ - rc = sched_get_priority_max(SCHED_RR); memset(&schedp, 0, sizeof(schedp)); - schedp.sched_priority = rc; + schedp.sched_priority = rt_prio; rc = sched_setscheduler(0, SCHED_RR, &schedp); if (rc) { - fprintf(stderr, "Error setting scheduler\n"); + fprintf(stderr, "Error setting SCHED_RR with prio %d\n", + rt_prio); } } -- cgit v1.2.3 From da0c44a9db88b866056f19f13364e1362c92773a Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 17 Jun 2013 08:23:04 +0200 Subject: Add test case for successful handover and unsuccessful handover --- .gitignore | 1 + configure.ac | 1 + tests/Makefile.am | 2 +- tests/handover/Makefile.am | 8 ++ tests/handover/handover_test.c | 277 ++++++++++++++++++++++++++++++++++++++++ tests/handover/handover_test.ok | 1 + tests/testsuite.at | 6 + 7 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 tests/handover/Makefile.am create mode 100644 tests/handover/handover_test.c create mode 100644 tests/handover/handover_test.ok diff --git a/.gitignore b/.gitignore index caa6cd77..8f6babc5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ tests/cipher/cipher_test tests/sysmobts/sysmobts_test tests/misc/misc_test tests/bursts/bursts_test +tests/handover/handover_test tests/testsuite tests/testsuite.log diff --git a/configure.ac b/configure.ac index 60421b82..56576633 100644 --- a/configure.ac +++ b/configure.ac @@ -89,4 +89,5 @@ AC_OUTPUT( tests/sysmobts/Makefile tests/misc/Makefile tests/bursts/Makefile + tests/handover/Makefile Makefile) diff --git a/tests/Makefile.am b/tests/Makefile.am index 70055909..54ff0297 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = paging cipher agch misc bursts +SUBDIRS = paging cipher agch misc bursts handover if ENABLE_SYSMOBTS SUBDIRS += sysmobts diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am new file mode 100644 index 00000000..a07a527d --- /dev/null +++ b/tests/handover/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) +AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)$(LIBOSMOTRAU_CFLAGS) +LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) +noinst_PROGRAMS = handover_test +EXTRA_DIST = handover_test.ok + +handover_test_SOURCES = handover_test.c +handover_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c new file mode 100644 index 00000000..ed4bedd0 --- /dev/null +++ b/tests/handover/handover_test.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint8_t phys_info[] = { 0x03, 0x03, 0x0d, 0x06, 0x2d, 0x00, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b }; + +static struct gsm_bts *bts; +struct gsm_bts_trx *trx; +int quit = 0; +uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 }; +const int pcu_direct = 0; +int modify_count = 0; + +static void expect_phys_info(struct lapdm_entity *le) +{ + struct osmo_phsap_prim pp; + int rc; + + rc = lapdm_phsap_dequeue_prim(le, &pp); + OSMO_ASSERT(rc == 0); + OSMO_ASSERT(sizeof(phys_info) == pp.oph.msg->len); + OSMO_ASSERT(!memcmp(phys_info, pp.oph.msg->data, pp.oph.msg->len)); + msgb_free(pp.oph.msg); +} + +int main(int argc, char **argv) +{ + struct gsm_bts_role_bts *btsb; + void *tall_bts_ctx; + void *tall_msgb_ctx; + struct e1inp_line *line; + struct gsm_lchan *lchan; + struct osmo_phsap_prim nl1sap; + struct msgb *msg; + struct abis_rsl_dchan_hdr *rslh; + int i; + + tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context"); + tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb"); + msgb_set_talloc_ctx(tall_msgb_ctx); + + bts_log_init(NULL); + osmo_stderr_target->categories[DHO].loglevel = LOGL_DEBUG; + + bts = gsm_bts_alloc(tall_bts_ctx); + if (!bts) { + fprintf(stderr, "Failed to create BTS structure\n"); + exit(1); + } + trx = gsm_bts_trx_alloc(bts); + if (!trx) { + fprintf(stderr, "Failed to TRX structure\n"); + exit(1); + } + + if (bts_init(bts) < 0) { + fprintf(stderr, "unable to to open bts\n"); + exit(1); + } + + btsb = bts_role_bts(bts); + + libosmo_abis_init(NULL); + + line = e1inp_line_create(0, "ipa"); + OSMO_ASSERT(line); + + e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL-1], line); + trx->rsl_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL-1], E1INP_SIGN_RSL, NULL, 0, 0); + OSMO_ASSERT(trx->rsl_link); + trx->rsl_link->trx = trx; + + fprintf(stderr, "test 1: without timeout\n"); + + /* create two lchans for handover */ + lchan = &trx->ts[1].lchan[0]; + l1sap_chan_act(lchan->ts->trx, 0x09, NULL); + lchan = &trx->ts[2].lchan[0]; + lchan->ho.active = HANDOVER_ENABLED; + lchan->ho.ref = 23; + l1sap_chan_act(lchan->ts->trx, 0x0a, NULL); + OSMO_ASSERT(msgb_dequeue(&trx->rsl_link->tx_list)); + OSMO_ASSERT(msgb_dequeue(&trx->rsl_link->tx_list)); + OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list)); + + /* send access burst with wrong ref */ + memset(&nl1sap, 0, sizeof(nl1sap)); + osmo_prim_init(&nl1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL); + nl1sap.u.rach_ind.chan_nr = 0x0a; + nl1sap.u.rach_ind.ra = 42; + l1sap_up(trx, &nl1sap); + + /* expect no action */ + OSMO_ASSERT(modify_count == 0); + OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list)); + + /* send access burst with correct ref */ + nl1sap.u.rach_ind.ra = 23; + l1sap_up(trx, &nl1sap); + OSMO_ASSERT(modify_count == 1); + + /* expect PHYS INFO */ + expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch); + + /* expect exactly one HO.DET */ + OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list)); + rslh = msgb_l2(msg); + OSMO_ASSERT(rslh->c.msg_type == RSL_MT_HANDO_DET); + OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list)); + + /* expect T3105 running */ + OSMO_ASSERT(osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105)) + + /* indicate frame */ + handover_frame(&trx->ts[2].lchan[0]); + + /* expect T3105 not running */ + OSMO_ASSERT(!osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105)) + + fprintf(stderr, "test 2: with timeout\n"); + + /* enable handover again */ + lchan = &trx->ts[2].lchan[0]; + lchan->ho.active = HANDOVER_ENABLED; + lchan->ho.ref = 23; + modify_count = 0; + + /* send access burst with correct ref */ + nl1sap.u.rach_ind.ra = 23; + l1sap_up(trx, &nl1sap); + OSMO_ASSERT(modify_count == 1); + + /* expect PHYS INFO */ + expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch); + + /* expect exactly one HO.DET */ + OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list)); + rslh = msgb_l2(msg); + OSMO_ASSERT(rslh->c.msg_type == RSL_MT_HANDO_DET); + OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list)); + + for (i = 0; i < btsb->ny1 - 1; i++) { + /* expect T3105 running */ + OSMO_ASSERT(osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105)) + + /* timeout T3105 */ + gettimeofday(&trx->ts[2].lchan[0].ho.t3105.timeout, NULL); + osmo_select_main(0); + + /* expect PHYS INFO */ + expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch); + } + + /* timeout T3105 */ + gettimeofday(&trx->ts[2].lchan[0].ho.t3105.timeout, NULL); + osmo_select_main(0); + + /* expect T3105 not running */ + OSMO_ASSERT(!osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105)) + + /* expect exactly one CONN.FAIL */ + OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list)); + rslh = msgb_l2(msg); + OSMO_ASSERT(rslh->c.msg_type == RSL_MT_CONN_FAIL); + OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list)); + +#if 0 + while (!quit) { + log_reset_context(); + osmo_select_main(0); + } +#endif + + printf("Success\n"); + + return 0; +} + +void bts_model_abis_close(struct gsm_bts *bts) +{ +} + +int bts_model_oml_estab(struct gsm_bts *bts) +{ + return 0; +} + +int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) +{ + int rc = 0; + uint8_t chan_nr; + uint8_t tn, ss; + struct gsm_lchan *lchan; + struct msgb *msg = l1sap->oph.msg; + struct osmo_phsap_prim nl1sap; + + switch (OSMO_PRIM_HDR(&l1sap->oph)) { + case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST): + switch (l1sap->u.info.type) { + case PRIM_INFO_ACTIVATE: + chan_nr = l1sap->u.info.u.act_req.chan_nr; + tn = L1SAP_CHAN2TS(chan_nr); + ss = l1sap_chan2ss(chan_nr); + lchan = &trx->ts[tn].lchan[ss]; + + lchan_init_lapdm(lchan); + + lchan_set_state(lchan, LCHAN_S_ACTIVE); + + memset(&nl1sap, 0, sizeof(nl1sap)); + osmo_prim_init(&nl1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM, NULL); + nl1sap.u.info.type = PRIM_INFO_ACTIVATE; + nl1sap.u.info.u.act_cnf.chan_nr = chan_nr; + return l1sap_up(trx, &nl1sap); + case PRIM_INFO_MODIFY: + modify_count++; + break; + default: + LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n", + l1sap->u.info.type); + rc = -EINVAL; + goto done; + } + break; + default: + LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n", + l1sap->oph.primitive, l1sap->oph.operation); + rc = -EINVAL; + goto done; + } + +done: + if (msg) + msgb_free(msg); + return rc; +} + +int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type, struct tlv_parsed *old_attr, struct tlv_parsed *new_attr, void *obj) { return 0; } +int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg, struct tlv_parsed *new_attr, int obj_kind, void *obj) { return 0; } +int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj) { return 0; } +int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj, uint8_t adm_state) { return 0; } +int bts_model_init(struct gsm_bts *bts) { return 0; } +int bts_model_trx_deact_rf(struct gsm_bts_trx *trx) { return 0; } +int bts_model_trx_close(struct gsm_bts_trx *trx) { return 0; } +void trx_get_hlayer1(void) {} diff --git a/tests/handover/handover_test.ok b/tests/handover/handover_test.ok new file mode 100644 index 00000000..35821117 --- /dev/null +++ b/tests/handover/handover_test.ok @@ -0,0 +1 @@ +Success diff --git a/tests/testsuite.at b/tests/testsuite.at index 7c449bb8..171fade5 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -30,3 +30,9 @@ AT_KEYWORDS([bursts]) cat $abs_srcdir/bursts/bursts_test.ok > expout AT_CHECK([$abs_top_builddir/tests/bursts/bursts_test], [], [expout], [ignore]) AT_CLEANUP + +AT_SETUP([handover]) +AT_KEYWORDS([handover]) +cat $abs_srcdir/handover/handover_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/handover/handover_test], [], [expout], [ignore]) +AT_CLEANUP -- cgit v1.2.3 From 8c8998e55156629679dbe1ca42684bc451d06262 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 19 Jun 2013 15:20:12 +0200 Subject: TRX: Set ciphering to an initial state when activating channel Handover and assignment may activate channels with ciphering already set, so we need to tell scheduler to enable/disable ciphering and set the correct cipher state. --- src/osmo-bts-trx/l1_if.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 7e09a01b..f3921f3b 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -507,6 +507,14 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) lchan_init_lapdm(lchan); /* set lchan active */ lchan_set_state(lchan, LCHAN_S_ACTIVE); + /* set initial ciphering */ + l1if_set_ciphering(l1h, lchan, chan_nr, 0); + l1if_set_ciphering(l1h, lchan, chan_nr, 1); + if (lchan->encr.alg_id) + lchan->ciph_state = LCHAN_CIPH_TXRX_CONF; + else + lchan->ciph_state = LCHAN_CIPH_NONE; + /* confirm */ mph_info_chan_confirm(l1h, chan_nr, PRIM_INFO_ACTIVATE, 0); -- cgit v1.2.3 From f5aaf523c58dd7bc2f57f840b717da359d08bb5c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 20 Jun 2013 20:10:46 +0200 Subject: TRX: If no cipher algorithm is given, or if it is a5/0, reset cipher state --- src/osmo-bts-trx/scheduler.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 9aa05390..5a22c5c3 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2573,7 +2573,10 @@ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, int rc = -EINVAL; struct trx_chan_state *chan_state; - if (algo < 0 || key_len > 8 || (algo && key_len != 8)) { + /* no algorithm given means a5/0 */ + if (algo <= 0) + algo = 0; + else if (key_len != 8) { LOGP(DL1C, LOGL_ERROR, "Algo A5/%d not supported with given " "key len=%d\n", algo, key_len); return -ENOTSUP; -- cgit v1.2.3 From ee479133891317be37b7fc850e6cb945ef0c9f38 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 29 Jun 2013 21:42:58 +0200 Subject: TRX: No need to set mode and cipher for PDCH --- src/osmo-bts-trx/scheduler.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 5a22c5c3..bf5d7d96 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2518,6 +2518,10 @@ int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode, int rc = -EINVAL; struct trx_chan_state *chan_state; + /* no mode for PDCH */ + if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH) + return 0; + /* look for all matching chan_nr/link_id */ for (i = 0; i < _TRX_CHAN_MAX; i++) { if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8) @@ -2573,6 +2577,10 @@ int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink, int rc = -EINVAL; struct trx_chan_state *chan_state; + /* no cipher for PDCH */ + if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH) + return 0; + /* no algorithm given means a5/0 */ if (algo <= 0) algo = 0; -- cgit v1.2.3 From 3caf3b7c45258278b4aeebd992e801d42b10759d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 2 Jul 2013 11:04:11 +0200 Subject: TRX: Fixup ciphering state names after rebasing --- src/common/l1sap.c | 1 + src/osmo-bts-trx/l1_if.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/l1sap.c b/src/common/l1sap.c index b568cf01..d5dd8a60 100644 --- a/src/common/l1sap.c +++ b/src/common/l1sap.c @@ -96,6 +96,7 @@ static int check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan, /* only do this if we are in the right state */ switch (lchan->ciph_state) { case LCHAN_CIPH_NONE: + case LCHAN_CIPH_RX_REQ: break; default: return 0; diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index f3921f3b..e6621ccd 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -371,7 +371,7 @@ static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr, int downlink) { /* ciphering already enabled in both directions */ - if (lchan->ciph_state == LCHAN_CIPH_TXRX_CONF) + if (lchan->ciph_state == LCHAN_CIPH_RXTX_CONF) return -EINVAL; if (!downlink) { @@ -388,7 +388,7 @@ static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan, } trx_sched_set_cipher(l1h, chan_nr, 1, lchan->encr.alg_id - 1, lchan->encr.key, lchan->encr.key_len); - lchan->ciph_state = LCHAN_CIPH_TXRX_CONF; + lchan->ciph_state = LCHAN_CIPH_RXTX_CONF; } return 0; @@ -511,7 +511,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) l1if_set_ciphering(l1h, lchan, chan_nr, 0); l1if_set_ciphering(l1h, lchan, chan_nr, 1); if (lchan->encr.alg_id) - lchan->ciph_state = LCHAN_CIPH_TXRX_CONF; + lchan->ciph_state = LCHAN_CIPH_RXTX_CONF; else lchan->ciph_state = LCHAN_CIPH_NONE; -- cgit v1.2.3 From 578340c7a7af149dec0b33fde2db03b672e221b4 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 2 Jul 2013 11:05:04 +0200 Subject: TRX: Add bts_model_trx_close to TRX implementation --- src/osmo-bts-trx/l1_if.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index e6621ccd..110241d7 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -259,7 +259,7 @@ static int trx_init(struct gsm_bts_trx *trx) } /* deactivate transceiver */ -static int trx_close(struct gsm_bts_trx *trx) +int bts_model_trx_close(struct gsm_bts_trx *trx) { struct trx_l1h *l1h = trx_l1h_hdl(trx); @@ -655,7 +655,7 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo, int bts_model_trx_deact_rf(struct gsm_bts_trx *trx) { - return trx_close(trx); + return 0; } int bts_model_oml_estab(struct gsm_bts *bts) -- cgit v1.2.3 From 3cf28aa9240a43dcd4745bc82e27d1802cf63245 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 8 Jul 2013 18:40:31 +0200 Subject: TRX: Close TRX (shutdown all active channels) on ABIS link failure --- src/osmo-bts-trx/l1_if.c | 10 ++++++++++ src/osmo-bts-trx/main.c | 3 --- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 110241d7..0575a429 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "l1_if.h" #include "trx_if.h" @@ -279,6 +280,15 @@ int bts_model_trx_close(struct gsm_bts_trx *trx) return 0; } +/* on RSL failure, deactivate transceiver */ +void bts_model_abis_close(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) + bts_model_trx_close(trx); +} + /* set bts attributes */ static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr) { diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index dbf7c69a..6ddc27e9 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -402,6 +402,3 @@ int main(int argc, char **argv) return 0; } -void bts_model_abis_close(struct gsm_bts *bts) -{ -} -- cgit v1.2.3 From f0072a8de82b48bb8927994a07efec7218f91640 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 8 Jul 2013 18:42:48 +0200 Subject: TRX: Do not send burst on IDLE channels at TRX != C0 This is required, so the transceiver transmits no power. --- src/osmo-bts-trx/scheduler.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index bf5d7d96..3598d55c 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2827,9 +2827,14 @@ static int trx_sched_fn(uint32_t fn) /* get burst for FN */ bits = trx_sched_dl_burst(l1h, tn, fn); if (!bits) { +#if 0 /* if no bits, send dummy burst with no gain */ bits = dummy_burst; gain = 128; +#else + /* if no bits, send no burst */ + continue; +#endif } else gain = 0; trx_if_data(l1h, tn, fn, gain, bits); -- cgit v1.2.3 From ef6eb5442c865a42615d8dab4fc5c99decf4b4a8 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 8 Jul 2013 18:44:44 +0200 Subject: trx: Add option to set transmit power reduction via OML (BSC) --- src/osmo-bts-trx/l1_if.c | 7 +++++++ src/osmo-bts-trx/l1_if.h | 1 + src/osmo-bts-trx/trx_vty.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 0575a429..a135d442 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -330,6 +330,13 @@ static uint8_t trx_set_trx(struct gsm_bts_trx *trx) l1if_provision_transceiver_trx(l1h); } + if (l1h->config.power_oml) { + l1h->config.power = trx->max_power_red; + l1h->config.power_valid = 1; + l1h->config.power_sent = 0; + l1if_provision_transceiver_trx(l1h); + } + return 0; } diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 5b4dce3c..5110cde1 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -127,6 +127,7 @@ struct trx_config { int power_valid; int power; + int power_oml; int power_sent; int maxdly_valid; diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c index 4e675921..ebc7cfdb 100644 --- a/src/osmo-bts-trx/trx_vty.c +++ b/src/osmo-bts-trx/trx_vty.c @@ -233,6 +233,24 @@ DEFUN(cfg_trx_power, cfg_trx_power_cmd, struct trx_l1h *l1h = trx_l1h_hdl(trx); l1h->config.power = atoi(argv[0]); + l1h->config.power_oml = 0; + l1h->config.power_valid = 1; + l1h->config.power_sent = 0; + l1if_provision_transceiver_trx(l1h); + + return CMD_SUCCESS; +} + +DEFUN(cfg_trx_poweroml_, cfg_trx_power_oml_cmd, + "power oml", + "Set the transmitter power dampening\n" + "Given by NM_ATT_RF_MAXPOWR_R (max power reduction) via OML\n") +{ + struct gsm_bts_trx *trx = vty->index; + struct trx_l1h *l1h = trx_l1h_hdl(trx); + + l1h->config.power = trx->max_power_red; + l1h->config.power_oml = 1; l1h->config.power_valid = 1; l1h->config.power_sent = 0; l1if_provision_transceiver_trx(l1h); @@ -338,8 +356,13 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx) if (l1h->config.rxgain_valid) vty_out(vty, " rxgain %d%s", l1h->config.rxgain, VTY_NEWLINE); - if (l1h->config.power_valid) - vty_out(vty, " power %d%s", l1h->config.power, VTY_NEWLINE); + if (l1h->config.power_valid) { + if (l1h->config.power_oml) + vty_out(vty, " power oml%s", VTY_NEWLINE); + else + vty_out(vty, " power %d%s", l1h->config.power, + VTY_NEWLINE); + } if (l1h->config.maxdly_valid) vty_out(vty, " maxdly %d%s", l1h->config.maxdly, VTY_NEWLINE); if (l1h->config.slotmask != 0xff) @@ -373,6 +396,7 @@ int bts_model_vty_init(struct gsm_bts *bts) install_element(TRX_NODE, &cfg_trx_rxgain_cmd); install_element(TRX_NODE, &cfg_trx_power_cmd); + install_element(TRX_NODE, &cfg_trx_power_oml_cmd); install_element(TRX_NODE, &cfg_trx_maxdly_cmd); install_element(TRX_NODE, &cfg_trx_slotmask_cmd); install_element(TRX_NODE, &cfg_trx_no_rxgain_cmd); -- cgit v1.2.3 From ec6225e3e067221a3e2c19f6eda03453a2fe60e7 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 12 Aug 2013 09:29:40 +0200 Subject: TRX: Fixed chan_nr for SACCH/8(7) at scheduler --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 3598d55c..ef000144 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -193,7 +193,7 @@ struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = { { 0, TRXC_SACCH8_4, 0x60, 0x40, "SACCH/8(4)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, { 0, TRXC_SACCH8_5, 0x68, 0x40, "SACCH/8(5)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, { 0, TRXC_SACCH8_6, 0x70, 0x40, "SACCH/8(6)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, - { 0, TRXC_SACCH8_7, 0x68, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, + { 0, TRXC_SACCH8_7, 0x78, 0x40, "SACCH/8(7)", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, { 1, TRXC_PDTCH, 0x08, 0x00, "PDTCH", rts_data_fn, tx_pdtch_fn, rx_pdtch_fn, 0 }, { 1, TRXC_PTCCH, 0x08, 0x00, "PTCCH", rts_data_fn, tx_data_fn, rx_data_fn, 0 }, }; -- cgit v1.2.3 From 812fdd92c70679f4d5a46f9078a74e2a94cd74e4 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 12 Aug 2013 09:31:27 +0200 Subject: TRX: Changed logging of unserved primitives from LOGL_NOTICE to LOGL_INFO --- src/osmo-bts-trx/scheduler.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index ef000144..6652909f 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -642,7 +642,7 @@ static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " "trx=%u ts=%u at fn=%u to transmit.\n", trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); @@ -720,7 +720,7 @@ static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, if (msg) goto got_msg; - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " "trx=%u ts=%u at fn=%u to transmit.\n", trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); @@ -1029,7 +1029,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* mo message at all */ if (!msg_tch && !msg_facch) { - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " "trx=%u ts=%u at fn=%u to transmit.\n", trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); goto send_burst; @@ -1119,7 +1119,7 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* mo message at all */ if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { - LOGP(DL1C, LOGL_NOTICE, "%s has not been served !! No prim for " + LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " "trx=%u ts=%u at fn=%u to transmit.\n", trx_chan_desc[chan].name, l1h->trx->nr, tn, fn); goto send_burst; -- cgit v1.2.3 From b2482a8574a3a7ce4e628237f411ea0a4cd25f1b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 23 Aug 2013 21:58:29 +0200 Subject: Allow TRX 0..254 at VTY, even if less TRX are available Instead of limiting the number of TRX at VTY to the actual number of supported TRX, VTY allows to configure any possible number of TRX. If a TRX is configured, which is not supported by BTS model, an error message is returned, which states that the given TRX is not supported. --- include/osmo-bts/vty.h | 2 +- src/common/vty.c | 9 ++++----- src/osmo-bts-sysmo/main.c | 2 +- src/osmo-bts-trx/main.c | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/osmo-bts/vty.h b/include/osmo-bts/vty.h index 9fcc5b5d..510abab1 100644 --- a/include/osmo-bts/vty.h +++ b/include/osmo-bts/vty.h @@ -15,7 +15,7 @@ extern struct cmd_element ournode_end_cmd; enum node_type bts_vty_go_parent(struct vty *vty); int bts_vty_is_config_node(struct vty *vty, int node); -int bts_vty_init(struct gsm_bts *bts, int trx_num, const struct log_info *cat); +int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat); extern struct vty_app_info bts_vty_info; diff --git a/src/common/vty.c b/src/common/vty.c index bca839bb..0d52dd76 100644 --- a/src/common/vty.c +++ b/src/common/vty.c @@ -140,9 +140,8 @@ static struct cmd_node trx_node = { 1, }; -static char cfg_bts_trx_cmd_string[16]; DEFUN(cfg_bts_trx, cfg_bts_trx_cmd, - cfg_bts_trx_cmd_string, + "trx <0-254>", "Select a TRX to configure\n" "TRX number\n") { int trx_nr = atoi(argv[0]); @@ -151,7 +150,8 @@ DEFUN(cfg_bts_trx, cfg_bts_trx_cmd, trx = gsm_bts_trx_num(bts, trx_nr); if (!trx) { - vty_out(vty, "Unknown TRX %u%s", trx_nr, VTY_NEWLINE); + vty_out(vty, "Unknown TRX %u. Aavialable TRX are: 0..%d%s", + trx_nr, bts->num_trx - 1, VTY_NEWLINE); return CMD_WARNING; } @@ -763,7 +763,7 @@ DEFUN(no_bts_t_t_l_loopback, return CMD_SUCCESS; } -int bts_vty_init(struct gsm_bts *bts, int trx_num, const struct log_info *cat) +int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat) { cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names, "gsmtap-sapi (", @@ -803,7 +803,6 @@ int bts_vty_init(struct gsm_bts *bts, int trx_num, const struct log_info *cat) install_element(BTS_NODE, &cfg_trx_no_gsmtap_sapi_cmd); /* add and link to TRX config node */ - sprintf(cfg_bts_trx_cmd_string, "trx <0-%d>", trx_num - 1); install_element(BTS_NODE, &cfg_bts_trx_cmd); install_node(&trx_node, config_write_dummy); install_default(TRX_NODE); diff --git a/src/osmo-bts-sysmo/main.c b/src/osmo-bts-sysmo/main.c index 9913708f..bee5bda2 100644 --- a/src/osmo-bts-sysmo/main.c +++ b/src/osmo-bts-sysmo/main.c @@ -320,7 +320,7 @@ int main(int argc, char **argv) bts = gsm_bts_alloc(tall_bts_ctx); vty_init(&bts_vty_info); e1inp_vty_init(); - bts_vty_init(bts, 1, &bts_log_info); + bts_vty_init(bts, &bts_log_info); handle_options(argc, argv); diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index 6ddc27e9..c4a4c601 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -310,7 +310,7 @@ int main(int argc, char **argv) vty_init(&bts_vty_info); e1inp_vty_init(); - bts_vty_init(bts, trx_num, &bts_log_info); + bts_vty_init(bts, &bts_log_info); if (bts_init(bts) < 0) { fprintf(stderr, "unable to to open bts\n"); -- cgit v1.2.3 From 73d3f469945f7956717c05954e8e66b1796b86a3 Mon Sep 17 00:00:00 2001 From: Martin Hauke Date: Fri, 23 Aug 2013 23:05:47 +0200 Subject: tests: make tests for sysmobts conditional --- tests/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index 54ff0297..7904486d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,6 +4,10 @@ if ENABLE_SYSMOBTS SUBDIRS += sysmobts endif +if ENABLE_SYSMOBTS +SUBDIRS += sysmobts +endif + # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ -- cgit v1.2.3 From c9ddb2ba22f4b57cd6e86771cd292564d5e4fb7f Mon Sep 17 00:00:00 2001 From: Martin Hauke Date: Fri, 23 Aug 2013 23:08:23 +0200 Subject: build: Use AM_CPPFLAGS in Makefile.am Since automake 1.13 INCLUDES is depricated and causes a warning Inspired from similar patches by Alexander Huemer for other osmocom projects. --- src/osmo-bts-trx/Makefile.am | 2 +- tests/bursts/Makefile.am | 2 +- tests/handover/Makefile.am | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index 05056cef..dbe90f8e 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am index 992bbb14..970d9a47 100644 --- a/tests/bursts/Makefile.am +++ b/tests/bursts/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) noinst_PROGRAMS = bursts_test diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am index a07a527d..597ab2d9 100644 --- a/tests/handover/Makefile.am +++ b/tests/handover/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR) AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)$(LIBOSMOTRAU_CFLAGS) LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) noinst_PROGRAMS = handover_test -- cgit v1.2.3 From 178d618d5a7728c876ae66777351aea37f88cc5d Mon Sep 17 00:00:00 2001 From: Martin Hauke Date: Fri, 23 Aug 2013 23:10:16 +0200 Subject: TRX: fix some typos in comments --- src/osmo-bts-trx/l1_if.c | 6 +++--- src/osmo-bts-trx/scheduler.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index a135d442..85be4744 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -55,7 +55,7 @@ static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = { /* - * create destroy trx l1 instance + * create/destroy trx l1 instance */ struct trx_l1h *l1if_open(struct gsm_bts_trx *trx) @@ -508,7 +508,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) } /* activate dedicated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x00, 1); - /* activate assoicated channel */ + /* activate associated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x40, 1); /* set mode */ trx_sched_set_mode(l1h, chan_nr, @@ -555,7 +555,7 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) "chan_nr 0x%02x\n", chan_nr); break; } - /* deactivate assoicated channel */ + /* deactivate associated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x40, 0); /* set lchan inactive * (also if only sacch, so no meaurement is done) */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 6652909f..a02a2de1 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -44,7 +44,7 @@ #include "loops.h" /* Enable this to multiply TOA of RACH by 10. - * This usefull to check tenth of timing advances with RSSI test tool. + * This is usefull to check tenth of timing advances with RSSI test tool. * Note that regular phones will not work when using this test! */ //#define TA_TEST @@ -430,7 +430,7 @@ static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* - * TX on donlink + * TX on downlink */ /* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */ @@ -1027,7 +1027,7 @@ static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, memset(*bursts_p + 464, 0, 464); } - /* mo message at all */ + /* no message at all */ if (!msg_tch && !msg_facch) { LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " "trx=%u ts=%u at fn=%u to transmit.\n", @@ -1117,7 +1117,7 @@ static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } } - /* mo message at all */ + /* no message at all */ if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) { LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for " "trx=%u ts=%u at fn=%u to transmit.\n", @@ -2988,7 +2988,7 @@ new_clock: tv_clock->tv_sec++; tv_clock->tv_usec -= 1000000; } - /* set time to the time our next FN hast to be transmitted */ + /* set time to the time our next FN has to be transmitted */ osmo_timer_schedule(&transceiver_clock_timer, 0, FRAME_DURATION_uS * (1 - elapsed_fn)); -- cgit v1.2.3 From c241afa87c2fa8713a1fae89e6b599e011d45c28 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 29 Aug 2013 16:00:31 +0200 Subject: TRX: Add VTY option to allow setting RTS advance in frames RTS (ready-to-send) must be issued in advance, so BTS core and especially osmo-pcu can provide downlink data frames early enough. In some cases PCU might provide frames too late, so they must be dropped. If PCU provides frames too late, due to high system load, this "RTS advance" setting must be increased. --- src/osmo-bts-trx/scheduler.c | 7 ++++--- src/osmo-bts-trx/scheduler.h | 1 + src/osmo-bts-trx/trx_vty.c | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index a02a2de1..4cf6a683 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -527,11 +527,12 @@ free_msg: default: goto wrong_type; } - if (prim_fn > 20) { + if (prim_fn > 100) { LOGP(DL1C, LOGL_NOTICE, "Prim for trx=%u ts=%u at fn=%u " "is out of range, or channel already disabled. " - "(current fn=%u)\n", l1h->trx->nr, tn, prim_fn, - fn); + "If this happens in conjunction with PCU, " + "increase 'rts-advance' by 5. (current fn=%u)\n", + l1h->trx->nr, tn, l1sap->u.data.fn, fn); /* unlink and free message */ llist_del(&msg->list); msgb_free(msg); diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h index acc2cebe..ce7ddad8 100644 --- a/src/osmo-bts-trx/scheduler.h +++ b/src/osmo-bts-trx/scheduler.h @@ -2,6 +2,7 @@ #define TRX_SCHEDULER_H extern uint32_t trx_clock_advance; +extern uint32_t trx_rts_advance; extern uint32_t transceiver_last_fn; diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c index ebc7cfdb..e3e9c364 100644 --- a/src/osmo-bts-trx/trx_vty.c +++ b/src/osmo-bts-trx/trx_vty.c @@ -119,7 +119,8 @@ DEFUN(show_transceiver, show_transceiver_cmd, "show transceiver", DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd, "fn-advance <0-30>", - "Set the number of frames to be transmitted in advance of current FN\n" + "Set the number of frames to be transmitted to transceiver in advance " + "of current FN\n" "Advance in frames\n") { trx_clock_advance = atoi(argv[0]); @@ -127,6 +128,17 @@ DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd, return CMD_SUCCESS; } +DEFUN(cfg_bts_rts_advance, cfg_bts_rts_advance_cmd, + "rts-advance <0-30>", + "Set the number of frames to be requested (PCU) in advance of current " + "FN. Do not change this, unless you have a good reason!\n" + "Advance in frames\n") +{ + trx_rts_advance = atoi(argv[0]); + + return CMD_SUCCESS; +} + DEFUN(cfg_bts_ms_power_loop, cfg_bts_ms_power_loop_cmd, "ms-power-loop <-127-127>", "Enable MS power control loop\nTarget RSSI value (transceiver specific, " @@ -336,6 +348,7 @@ DEFUN(cfg_trx_no_maxdly, cfg_trx_no_maxdly_cmd, void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts) { vty_out(vty, " fn-advance %d%s", trx_clock_advance, VTY_NEWLINE); + vty_out(vty, " rts-advance %d%s", trx_rts_advance, VTY_NEWLINE); if (trx_ms_power_loop) vty_out(vty, " ms-power-loop %d%s", trx_target_rssi, @@ -385,6 +398,7 @@ int bts_model_vty_init(struct gsm_bts *bts) install_element_ve(&show_transceiver_cmd); install_element(BTS_NODE, &cfg_bts_fn_advance_cmd); + install_element(BTS_NODE, &cfg_bts_rts_advance_cmd); install_element(BTS_NODE, &cfg_bts_ms_power_loop_cmd); install_element(BTS_NODE, &cfg_bts_no_ms_power_loop_cmd); install_element(BTS_NODE, &cfg_bts_timing_advance_loop_cmd); -- cgit v1.2.3 From f66f5b3ddc5c894ae7ad4909cbd261c6920260b0 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 9 Sep 2013 16:17:37 +0200 Subject: TRX: Free bust buffer memory to when changing lchan type --- src/osmo-bts-trx/scheduler.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 4cf6a683..f4b0d9cc 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2496,6 +2496,15 @@ int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id, if (active) memset(chan_state, 0, sizeof(*chan_state)); chan_state->active = active; + /* free burst memory, to cleanly start with burst 0 */ + if (chan_state->dl_bursts) { + talloc_free(chan_state->dl_bursts); + chan_state->dl_bursts = NULL; + } + if (chan_state->ul_bursts) { + talloc_free(chan_state->ul_bursts); + chan_state->ul_bursts = NULL; + } } } -- cgit v1.2.3 From f39c739bd6ae73596b3a72e2b240f012b669e5a7 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 13 Jan 2014 14:52:05 +0100 Subject: TRX: Activate LCHAN of CCCH when CCCH is configured on time slot --- src/osmo-bts-trx/l1_if.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 85be4744..56cba4f0 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -263,10 +263,16 @@ static int trx_init(struct gsm_bts_trx *trx) int bts_model_trx_close(struct gsm_bts_trx *trx) { struct trx_l1h *l1h = trx_l1h_hdl(trx); + enum gsm_phys_chan_config pchan = trx->ts[0].pchan; /* close all logical channels and reset timeslots */ trx_sched_reset(l1h); + /* deactivate lchan for CCCH */ + if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) { + lchan_set_state(&trx->ts[0].lchan[4], LCHAN_S_INACTIVE); + } + /* power off transceiver, if not already */ if (l1h->config.poweron) { l1h->config.poweron = 0; @@ -365,6 +371,12 @@ static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts) if (rc) return NM_NACK_RES_NOTAVAIL; + /* activate lchan for CCCH */ + if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) { + ts->lchan[4].rel_act_kind = LCHAN_REL_ACT_OML; + lchan_set_state(&ts->lchan[4], LCHAN_S_ACTIVE); + } + slottype = transceiver_chan_types[pchan]; if (l1h->config.slottype[tn] != slottype -- cgit v1.2.3 From a7d0c5ef5af04bb698d05785f0c9b1f20e687bca Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Tue, 14 Jan 2014 12:10:44 +0100 Subject: trx: Set lchan inactive, only if the dedicated channel is deactivated --- src/osmo-bts-trx/l1_if.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 56cba4f0..3f2a565a 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -569,11 +569,10 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) } /* deactivate associated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x40, 0); - /* set lchan inactive - * (also if only sacch, so no meaurement is done) */ - lchan_set_state(lchan, LCHAN_S_NONE); - /* deactivate dedicated channel */ if (!l1sap->u.info.u.act_req.sacch_only) { + /* set lchan inactive */ + lchan_set_state(lchan, LCHAN_S_NONE); + /* deactivate dedicated channel */ trx_sched_set_lchan(l1h, chan_nr, 0x00, 0); /* confirm only on dedicated channel */ mph_info_chan_confirm(l1h, chan_nr, -- cgit v1.2.3 From 3cfc9d5fa34bf144c7390c9b6d9ce867b4e5c2e4 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 20 Jan 2014 13:04:13 +0100 Subject: TRX: Show which TRX does not respond or rejects a command --- src/osmo-bts-trx/trx_if.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index e798ca53..bc80150b 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -175,7 +175,8 @@ static void trx_ctrl_timer_cb(void *data) { struct trx_l1h *l1h = data; - LOGP(DTRX, LOGL_NOTICE, "No response from transceiver\n"); + LOGP(DTRX, LOGL_NOTICE, "No response from transceiver for trx=%d\n", + l1h->trx->nr); trx_ctrl_send(l1h); } @@ -358,8 +359,8 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) sscanf(p + 1, "%d", &resp); if (resp) { LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE, - "transceiver rejected TRX command with " - "response: '%s'\n", buf); + "transceiver (trx=%d) rejected TRX command " + "with response: '%s'\n", l1h->trx->nr, buf); rsp_error: if (tcm->critical) { bts_shutdown(l1h->trx->bts, "SIGINT"); -- cgit v1.2.3 From deb01a26528a0f61ae0e20a3b793db560d4c81dc Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 6 Apr 2014 14:39:23 +0200 Subject: TRX: Check if Transceiver indicates an out of range clock If frame number is out of range (>= 2715648), the scheduler's process would end up in an infinite loop. This is because the loop would schedule bursts until the indicated frame number is reached, which would not be possible. The openbts, calypso-bts and osmo-trx might send out out of range clock indications every 3.5 hour. --- src/osmo-bts-trx/trx_if.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index bc80150b..8993de64 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -123,21 +123,29 @@ static int trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what) { char buf[1500]; int len; + uint32_t fn; len = recv(ofd->fd, buf, sizeof(buf) - 1, 0); if (len <= 0) return len; buf[len] = '\0'; - if (!strncmp(buf, "IND CLOCK ", 10)) { - uint32_t fn; - - sscanf(buf, "IND CLOCK %u", &fn); - LOGP(DTRX, LOGL_INFO, "Clock indication: fn=%u\n", fn); - trx_sched_clock(fn); - } else + if (!!strncmp(buf, "IND CLOCK ", 10)) { LOGP(DTRX, LOGL_NOTICE, "Unknown message on clock port: %s\n", buf); + return 0; + } + + sscanf(buf, "IND CLOCK %u", &fn); + LOGP(DTRX, LOGL_INFO, "Clock indication: fn=%u\n", fn); + + if (fn >= 2715648) { + fn %= 2715648; + LOGP(DTRX, LOGL_ERROR, "Indicated clock's FN is not wrapping " + "correctly, correcting to fn=%u\n", fn); + } + + trx_sched_clock(fn); return 0; } -- cgit v1.2.3 From ddc0bf14d5d5c8b3248eab0463399ae0a5bb8e3f Mon Sep 17 00:00:00 2001 From: Thomas Tsou Date: Thu, 8 May 2014 13:41:17 -0400 Subject: TRX: Remove extra TCH/HS puncturing value 3GPP TS 05.03 "Channel coding" specifies the puncturing matrix (1,0,1) for class 1 information bits and tail bits valued u(0) to u(103) for a maximum puncturing index of 311. The puncturing index 313 exceeds the maximum index and causes osmo_conv_get_output_length() to output the improper length of 210 instead of 211. Signed-off-by: Thomas Tsou --- src/osmo-bts-trx/gsm0503_conv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/gsm0503_conv.c b/src/osmo-bts-trx/gsm0503_conv.c index 2a814ca3..dc50e6c2 100644 --- a/src/osmo-bts-trx/gsm0503_conv.c +++ b/src/osmo-bts-trx/gsm0503_conv.c @@ -139,7 +139,7 @@ static const int conv_tch_hr_puncture[] = { 253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283, /* Tail bits */ - 295, 298, 301, 304, 307, 310, 313, + 295, 298, 301, 304, 307, 310, /* End */ -1, -- cgit v1.2.3 From 6fceaca584aa84214ccf747257344f1fe95caeee Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Mon, 6 Apr 2015 00:12:02 +0300 Subject: trx: Implement BER calculations. A known issue with this code is that BER is not updated for lost TCH frames, because osmo-trx doesn't send any indication for them and we don't have a callback to handle this. Otherwise the code seem to work fine. --- src/osmo-bts-trx/gsm0503_coding.c | 234 ++++++++++++++++---------------------- src/osmo-bts-trx/gsm0503_coding.h | 37 +++++- src/osmo-bts-trx/l1_if.c | 33 ++++-- src/osmo-bts-trx/l1_if.h | 6 +- src/osmo-bts-trx/scheduler.c | 78 ++++++++----- 5 files changed, 208 insertions(+), 180 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index c3ce9e57..e98301b4 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -1,9 +1,29 @@ +/* (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ #include #include #include #include +#include #include #include #include @@ -16,12 +36,37 @@ #include "gsm0503_tables.h" #include "gsm0503_coding.h" -static int _xcch_decode_cB(uint8_t *l2_data, sbit_t *cB) +int osmo_conv_decode_ber(const struct osmo_conv_code *code, + const sbit_t *input, ubit_t *output, + int *n_errors, int *n_bits_total) +{ + int res, i; + ubit_t recoded[1024]; /* TODO: We can do smaller, I guess */ + + res = osmo_conv_decode(code, input, output); + + *n_bits_total = osmo_conv_encode(code, output, recoded); + OSMO_ASSERT(sizeof(recoded)/sizeof(recoded[0]) >= *n_bits_total); + + /* Count bit errors */ + *n_errors = 0; + for (i=0; i< *n_bits_total; i++) { + if (! ((recoded[i] && input[i]<0) || + (!recoded[i] && input[i]>0)) ) + *n_errors += 1; + } + + return res; +} + + +static int _xcch_decode_cB(uint8_t *l2_data, sbit_t *cB, + int *n_errors, int *n_bits_total) { ubit_t conv[224]; int rv; - osmo_conv_decode(&gsm0503_conv_xcch, cB, conv); + osmo_conv_decode_ber(&gsm0503_conv_xcch, cB, conv, n_errors, n_bits_total); rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40, conv, 184, conv+184); if (rv) @@ -50,7 +95,8 @@ static int _xcch_encode_cB(ubit_t *cB, uint8_t *l2_data) * GSM xCCH block transcoding */ -int xcch_decode(uint8_t *l2_data, sbit_t *bursts) +int xcch_decode(uint8_t *l2_data, sbit_t *bursts, + int *n_errors, int *n_bits_total) { sbit_t iB[456], cB[456]; int i; @@ -61,7 +107,7 @@ int xcch_decode(uint8_t *l2_data, sbit_t *bursts) gsm0503_xcch_deinterleave(cB, iB); - return _xcch_decode_cB(l2_data, cB); + return _xcch_decode_cB(l2_data, cB, n_errors, n_bits_total); } int xcch_encode(ubit_t *bursts, uint8_t *l2_data) @@ -85,7 +131,8 @@ int xcch_encode(ubit_t *bursts, uint8_t *l2_data) * GSM PDTCH block transcoding */ -int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) +int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, + int *n_errors, int *n_bits_total) { sbit_t iB[456], cB[676], hl_hn[8]; ubit_t conv[456]; @@ -109,7 +156,7 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) switch (cs) { case 1: - osmo_conv_decode(&gsm0503_conv_xcch, cB, conv); + osmo_conv_decode_ber(&gsm0503_conv_xcch, cB, conv, n_errors, n_bits_total); rv = osmo_crc64gen_check_bits(&gsm0503_fire_crc40, conv, 184, conv+184); @@ -126,7 +173,7 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) else cB[i] = 0; - osmo_conv_decode(&gsm0503_conv_cs2, cB, conv); + osmo_conv_decode_ber(&gsm0503_conv_cs2, cB, conv, n_errors, n_bits_total); for (i=0; i<8; i++) { for (j=0, k=0; j<6; j++) @@ -159,7 +206,7 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) else cB[i] = 0; - osmo_conv_decode(&gsm0503_conv_cs3, cB, conv); + osmo_conv_decode_ber(&gsm0503_conv_cs3, cB, conv, n_errors, n_bits_total); for (i=0; i<8; i++) { for (j=0, k=0; j<6; j++) @@ -207,12 +254,22 @@ int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p) rv = osmo_crc16gen_check_bits(&gsm0503_cs234_crc16, conv+9, 431, conv+9+431); - if (rv) + if (rv) { + *n_bits_total = 456-12; + *n_errors = *n_bits_total; return -1; + } + + *n_bits_total = 456-12; + *n_errors = 0; osmo_ubit2pbit_ext(l2_data, 0, conv, 9, 431, 1); return 54; + default: + *n_bits_total = 0; + *n_errors = 0; + break; } return -1; @@ -608,7 +665,8 @@ static void tch_amr_unmerge(ubit_t *d, ubit_t *p, ubit_t *u, int len, int prot) memcpy(d+prot, u+prot+6, len-prot); } -int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr) +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr, + int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; ubit_t conv[185], s[244], w[260], b[65], d[260], p[8]; @@ -623,14 +681,14 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr) gsm0503_tch_fr_deinterleave(cB, iB); if (steal > 0) { - rv = _xcch_decode_cB(tch_data, cB); + rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); if (rv) return -1; return 23; } - osmo_conv_decode(&gsm0503_conv_tch_fr, cB, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_fr, cB, conv, n_errors, n_bits_total); tch_fr_unreorder(d, p, conv); @@ -723,7 +781,8 @@ coding_efr_fr: return 0; } -int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd) +int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, + int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; ubit_t conv[98], b[112], d[112], p[3]; @@ -752,7 +811,7 @@ int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd) gsm0503_tch_fr_deinterleave(cB, iB); - rv = _xcch_decode_cB(tch_data, cB); + rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); if (rv) return -1; @@ -765,7 +824,7 @@ int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd) gsm0503_tch_hr_deinterleave(cB, iB); - osmo_conv_decode(&gsm0503_conv_tch_hr, cB, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_hr, cB, conv, n_errors, n_bits_total); tch_hr_unreorder(d, p, conv); @@ -834,29 +893,14 @@ int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len) return 0; } -static float amr_calc_ber(sbit_t *orig, ubit_t *test, int len) -{ - int i, err = 0; - - /* count number of wrong bits (sbits with 0-value are omitted) */ - for (i=0; i 0 && (*test)) - err++; - else if ((*orig) < 0 && !(*test)) - err++; - orig++; - test++; - } - - return (float)err / (float)len; -} - int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, - uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, float *ber) + uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, + int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; ubit_t test[456], d[244], p[6], conv[250]; int i, j, k, best = 0, rv, len, steal = 0, id = 0; + *n_errors = 0; *n_bits_total = 0; for (i=0; i<8; i++) { gsm0503_tch_burst_unmap(&iB[i * 114], &bursts[i * 116], &h, @@ -867,7 +911,7 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, gsm0503_tch_fr_deinterleave(cB, iB); if (steal > 0) { - rv = _xcch_decode_cB(tch_data, cB); + rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); if (rv) return -1; @@ -892,7 +936,7 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, switch ((codec_mode_req) ? codec[*ft] : codec[id]) { case 7: /* TCH/AFS12.2 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_12_2, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_12_2, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 244, 81); @@ -904,15 +948,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 31; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_12_2, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 6: /* TCH/AFS10.2 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_10_2, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_10_2, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 204, 65); @@ -924,15 +962,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 26; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_10_2, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 5: /* TCH/AFS7.95 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_7_95, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_7_95, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 159, 75); @@ -944,15 +976,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 20; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_7_95, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 4: /* TCH/AFS7.4 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_7_4, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_7_4, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 148, 61); @@ -964,15 +990,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 19; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_7_4, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 3: /* TCH/AFS6.7 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_6_7, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_6_7, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 134, 55); @@ -984,15 +1004,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 17; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_6_7, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 2: /* TCH/AFS5.9 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_5_9, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_5_9, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 118, 55); @@ -1004,15 +1018,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 15; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_5_9, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 1: /* TCH/AFS5.15 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_5_15, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_5_15, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 103, 49); @@ -1024,15 +1032,9 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 13; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_5_15, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; case 0: /* TCH/AFS4.75 */ - osmo_conv_decode(&gsm0503_conv_tch_afs_4_75, cB+8, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_afs_4_75, cB+8, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 95, 39); @@ -1044,16 +1046,11 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, len = 12; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_afs_4_75, conv, - test+8); - *ber = amr_calc_ber(cB+8, test+8, 448); - } - break; default: fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); - + *n_bits_total = 448; + *n_errors = *n_bits_total; return -1; } @@ -1229,7 +1226,7 @@ facch: int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, - uint8_t *cmr, float *ber) + uint8_t *cmr, int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; ubit_t test[456], d[244], p[6], conv[135]; @@ -1258,7 +1255,7 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, gsm0503_tch_fr_deinterleave(cB, iB); - rv = _xcch_decode_cB(tch_data, cB); + rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); if (rv) return -1; @@ -1289,7 +1286,7 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, switch ((codec_mode_req) ? codec[*ft] : codec[id]) { case 5: /* TCH/AHS7.95 */ - osmo_conv_decode(&gsm0503_conv_tch_ahs_7_95, cB+4, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_ahs_7_95, cB+4, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 123, 67); @@ -1304,15 +1301,9 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, len = 20; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_ahs_7_95, conv, - test+4); - *ber = amr_calc_ber(cB+4, test+4, 188); - } - break; case 4: /* TCH/AHS7.4 */ - osmo_conv_decode(&gsm0503_conv_tch_ahs_7_4, cB+4, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_ahs_7_4, cB+4, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 120, 61); @@ -1327,15 +1318,9 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, len = 19; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_ahs_7_4, conv, - test+4); - *ber = amr_calc_ber(cB+4, test+4, 196); - } - break; case 3: /* TCH/AHS6.7 */ - osmo_conv_decode(&gsm0503_conv_tch_ahs_6_7, cB+4, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_ahs_6_7, cB+4, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 110, 55); @@ -1350,15 +1335,9 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, len = 17; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_ahs_6_7, conv, - test+4); - *ber = amr_calc_ber(cB+4, test+4, 200); - } - break; case 2: /* TCH/AHS5.9 */ - osmo_conv_decode(&gsm0503_conv_tch_ahs_5_9, cB+4, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_ahs_5_9, cB+4, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 102, 55); @@ -1373,15 +1352,9 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, len = 15; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_ahs_5_9, conv, - test+4); - *ber = amr_calc_ber(cB+4, test+4, 208); - } - break; case 1: /* TCH/AHS5.15 */ - osmo_conv_decode(&gsm0503_conv_tch_ahs_5_15, cB+4, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_ahs_5_15, cB+4, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 91, 49); @@ -1396,15 +1369,9 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, len = 13; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_ahs_5_15, conv, - test+4); - *ber = amr_calc_ber(cB+4, test+4, 212); - } - break; case 0: /* TCH/AHS4.75 */ - osmo_conv_decode(&gsm0503_conv_tch_ahs_4_75, cB+4, conv); + osmo_conv_decode_ber(&gsm0503_conv_tch_ahs_4_75, cB+4, conv, n_errors, n_bits_total); tch_amr_unmerge(d, p, conv, 83, 39); @@ -1419,16 +1386,11 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, len = 12; - if (ber) { - osmo_conv_encode(&gsm0503_conv_tch_ahs_4_75, conv, - test+4); - *ber = amr_calc_ber(cB+4, test+4, 212); - } - break; default: fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); - + *n_bits_total = 159; + *n_errors = *n_bits_total; return -1; } diff --git a/src/osmo-bts-trx/gsm0503_coding.h b/src/osmo-bts-trx/gsm0503_coding.h index 33b58d3b..a454d8f4 100644 --- a/src/osmo-bts-trx/gsm0503_coding.h +++ b/src/osmo-bts-trx/gsm0503_coding.h @@ -1,22 +1,47 @@ +/* (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License + * along with this program. If not, see . + * + */ + #ifndef _0503_CODING_H #define _0503_CODING_H -int xcch_decode(uint8_t *l2_data, sbit_t *bursts); +int xcch_decode(uint8_t *l2_data, sbit_t *bursts, + int *n_errors, int *n_bits_total); int xcch_encode(ubit_t *bursts, uint8_t *l2_data); -int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p); +int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p, + int *n_errors, int *n_bits_total); int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len); -int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr); +int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, + int efr, int *n_errors, int *n_bits_total); int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order); -int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd); +int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, + int *n_errors, int *n_bits_total); int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len); int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, - uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, float *ber); + uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr, + int *n_errors, int *n_bits_total); int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr); int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft, - uint8_t *cmr, float *ber); + uint8_t *cmr, int *n_errors, int *n_bits_total); int tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len, int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft, uint8_t cmr); diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 3f2a565a..6f8cd845 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -2,6 +2,7 @@ * layer 1 primitive handling and interface * * Copyright (C) 2013 Andreas Eversberg + * Copyright (C) 2015 Alexander Chemeris * * All Rights Reserved * @@ -454,19 +455,33 @@ int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn) return l1sap_up(bts->c0, &l1sap); } -int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr, float qta, + +void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta, float ber, float rssi) { + memset(l1sap, 0, sizeof(*l1sap)); + osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_MPH_INFO, + PRIM_OP_INDICATION, NULL); + l1sap->u.info.type = PRIM_INFO_MEAS; + l1sap->u.info.u.meas_ind.chan_nr = chan_nr; + l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta*4); + l1sap->u.info.u.meas_ind.ber10k = (unsigned int) (ber * 10000); + l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1); +} + +int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr, + int n_errors, int n_bits_total, float rssi, float toa) +{ + struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)]; struct osmo_phsap_prim l1sap; + float ber = (float)n_errors / (float)n_bits_total; - memset(&l1sap, 0, sizeof(l1sap)); - osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, - PRIM_OP_INDICATION, NULL); - l1sap.u.info.type = PRIM_INFO_MEAS; - l1sap.u.info.u.meas_ind.chan_nr = chan_nr; - l1sap.u.info.u.meas_ind.ta_offs_qbits = qta; - l1sap.u.info.u.meas_ind.ber10k = (unsigned int) (ber * 100); - l1sap.u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1); + LOGP(DMEAS, LOGL_DEBUG, "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS " + "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n", + gsm_lchan_name(lchan), fn, chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), + rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info[1], lchan->rqd_ta, toa); + + l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi); return l1sap_up(trx, &l1sap); } diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index 5110cde1..278537e1 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -171,7 +171,9 @@ int check_transceiver_availability(struct gsm_bts *bts, int avail); int l1if_provision_transceiver_trx(struct trx_l1h *l1h); int l1if_provision_transceiver(struct gsm_bts *bts); int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); -int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t chan_nr, float qta, - float ber, float rssi); +void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta, + float ber, float rssi); +int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr, + int n_errors, int n_bits_total, float rssi, float toa); #endif /* L1_IF_H_TRX */ diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index f4b0d9cc..23bfebc0 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -1,6 +1,7 @@ /* Scheduler for OsmoBTS-TRX */ /* (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris * * All Rights Reserved * @@ -563,8 +564,7 @@ found_msg: } static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, - enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float toa, - float ber, float rssi) + enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi) { struct msgb *msg; struct osmo_phsap_prim *l1sap; @@ -589,12 +589,6 @@ static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* forward primitive */ l1sap_up(l1h->trx, l1sap); - /* process measurement */ - if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) - l1if_process_meas_res(l1h->trx, chan_nr, - (l1h->trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)].rqd_ta + toa) * 4, - ber, rssi); - return 0; } @@ -668,9 +662,16 @@ got_msg: /* handle loss detection of sacch */ if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) { /* count and send BFI */ - if (++(l1h->chan_states[tn][chan].lost) > 1) - compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, 0, 0, - -110); + if (++(l1h->chan_states[tn][chan].lost) > 1) { + /* TODO: Should we pass old TOA here? Otherwise we risk + * unnecessary decreasing TA */ + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, + 456, 456, -110, 0); + + compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -110); + } } /* alloc burst memory, if not already */ @@ -1225,6 +1226,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, float *toa_sum = &chan_state->toa_sum; uint8_t *toa_num = &chan_state->toa_num; uint8_t l2[23], l2_len; + int n_errors, n_bits_total; int rc; /* handle rach, if handover rach detection is turned on */ @@ -1290,7 +1292,7 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *mask = 0x0; /* decode */ - rc = xcch_decode(l2, *bursts_p); + rc = xcch_decode(l2, *bursts_p, &n_errors, &n_bits_total); if (rc) { LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u " "(%u/%u) for %s\n", *first_fn, @@ -1300,8 +1302,11 @@ static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } else l2_len = 23; - return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, - *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, + n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num); + + return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, *rssi_sum / *rssi_num); } static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1316,6 +1321,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, float *toa_sum = &chan_state->toa_sum; uint8_t *toa_num = &chan_state->toa_num; uint8_t l2[54+1]; + int n_errors, n_bits_total; int rc; LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n", @@ -1364,7 +1370,12 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, *mask = 0x0; /* decode */ - rc = pdtch_decode(l2 + 1, *bursts_p, NULL); + rc = pdtch_decode(l2 + 1, *bursts_p, NULL, &n_errors, &n_bits_total); + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn, + n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num); + if (rc <= 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block ending at " "fn=%u (%u/%u) for %s\n", fn, fn % l1h->mf_period[tn], @@ -1375,7 +1386,7 @@ static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, l2[0] = 7; /* valid frame */ return compose_ph_data_ind(l1h, tn, (fn + 2715648 - 3) % 2715648, chan, - l2, rc + 1, *toa_sum / *toa_num, 0, *rssi_sum / *rssi_num); + l2, rc + 1, *rssi_sum / *rssi_num); } static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, @@ -1389,7 +1400,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ int rc, amr = 0; - float ber; + int n_errors, n_bits_total; /* handle rach, if handover rach detection is turned on */ if (chan_state->ho_rach_detect == 1) @@ -1437,10 +1448,10 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, switch ((rsl_cmode != RSL_CMOD_SPD_SPEECH) ? GSM48_CMODE_SPEECH_V1 : tch_mode) { case GSM48_CMODE_SPEECH_V1: /* FR */ - rc = tch_fr_decode(tch_data, *bursts_p, 1, 0); + rc = tch_fr_decode(tch_data, *bursts_p, 1, 0, &n_errors, &n_bits_total); break; case GSM48_CMODE_SPEECH_EFR: /* EFR */ - rc = tch_fr_decode(tch_data, *bursts_p, 1, 1); + rc = tch_fr_decode(tch_data, *bursts_p, 1, 1, &n_errors, &n_bits_total); break; case GSM48_CMODE_SPEECH_AMR: /* AMR */ /* the first FN 0,8,17 defines that CMI is included in frame, @@ -1450,11 +1461,11 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, rc = tch_afs_decode(tch_data + 2, *bursts_p, (((fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec, chan_state->codecs, &chan_state->ul_ft, - &chan_state->ul_cmr, &ber); + &chan_state->ul_cmr, &n_errors, &n_bits_total); if (rc) trx_loop_amr_input(l1h, trx_chan_desc[chan].chan_nr | tn, chan_state, - ber); + (float)n_errors/(float)n_bits_total); amr = 2; /* we store tch_data + 2 header bytes */ /* only good speech frames get rtp header */ if (rc != 23 && rc >= 4) { @@ -1469,6 +1480,12 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, return -EINVAL; } memcpy(*bursts_p, *bursts_p + 464, 464); + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn, + n_errors, n_bits_total, rssi, toa); + + /* Check if the frame is bad */ if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " "fn=%u for %s\n", fn, trx_chan_desc[chan].name); @@ -1484,7 +1501,7 @@ static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, /* FACCH */ if (rc == 23) { compose_ph_data_ind(l1h, tn, (fn + 2715648 - 7) % 2715648, chan, - tch_data + amr, 23, 0, 0, 0); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ @@ -1533,7 +1550,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t tch_mode = chan_state->tch_mode; uint8_t tch_data[128]; /* just to be safe */ int rc, amr = 0; - float ber; + int n_errors, n_bits_total; /* handle rach, if handover rach detection is turned on */ if (chan_state->ho_rach_detect == 1) @@ -1594,7 +1611,8 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, * Even FN ending at: 10,11,19,20,2,3 */ rc = tch_hr_decode(tch_data, *bursts_p, - (((fn + 26 - 10) % 26) >> 2) & 1); + (((fn + 26 - 10) % 26) >> 2) & 1, + &n_errors, &n_bits_total); break; case GSM48_CMODE_SPEECH_AMR: /* AMR */ /* the first FN 0,8,17 or 1,9,18 defines that CMI is included @@ -1605,11 +1623,11 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, (((fn + 26 - 10) % 26) >> 2) & 1, (((fn + 26 - 10) % 26) >> 2) & 1, chan_state->codec, chan_state->codecs, &chan_state->ul_ft, - &chan_state->ul_cmr, &ber); + &chan_state->ul_cmr, &n_errors, &n_bits_total); if (rc) trx_loop_amr_input(l1h, trx_chan_desc[chan].chan_nr | tn, chan_state, - ber); + (float)n_errors/(float)n_bits_total); amr = 2; /* we store tch_data + 2 two */ /* only good speech frames get rtp header */ if (rc != 23 && rc >= 4) { @@ -1625,6 +1643,12 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, } memcpy(*bursts_p, *bursts_p + 232, 232); memcpy(*bursts_p + 232, *bursts_p + 464, 232); + + /* Send uplnk measurement information to L2 */ + l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn, + n_errors, n_bits_total, rssi, toa); + + /* Check if the frame is bad */ if (rc < 0) { LOGP(DL1C, LOGL_NOTICE, "Received bad TCH frame ending at " "fn=%u for %s\n", fn, trx_chan_desc[chan].name); @@ -1642,7 +1666,7 @@ static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, chan_state->ul_ongoing_facch = 1; compose_ph_data_ind(l1h, tn, (fn + 2715648 - 10 - ((fn % 26) >= 19)) % 2715648, chan, - tch_data + amr, 23, 0, 0, 0); + tch_data + amr, 23, rssi); bfi: if (rsl_cmode == RSL_CMOD_SPD_SPEECH) { /* indicate bad frame */ -- cgit v1.2.3 From cf18dcd5fdf42330e868e9de8effedbbeb5df223 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Tue, 7 Apr 2015 21:17:01 +0300 Subject: tests: Update bursts_test to accommodate BER calculations. --- tests/bursts/bursts_test.c | 39 ++++++++++++++++++++++++++++++--------- tests/bursts/bursts_test.ok | 20 ++++++++++++++++++++ 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index 608baacb..ef703a8a 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -1,4 +1,5 @@ /* (C) 2013 by Andreas Eversberg + * (C) 2015 by Alexander Chemeris * * All Rights Reserved * @@ -63,6 +64,7 @@ static void test_xcch(uint8_t *l2) uint8_t result[23]; ubit_t bursts_u[116 * 4]; sbit_t bursts_s[116 * 4]; + int n_errors, n_bits_total; printd("Encoding: %s\n", osmo_hexdump(l2, 23)); @@ -102,9 +104,13 @@ static void test_xcch(uint8_t *l2) memset(bursts_s + 116, 0, 30); /* decode */ - xcch_decode(result, bursts_s); + xcch_decode(result, bursts_s, &n_errors, &n_bits_total); + + ASSERT_TRUE(n_bits_total == 456); printd("Decoded: %s\n", osmo_hexdump(result, 23)); + printf("xcch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n", + n_errors, n_bits_total, (float)n_errors/n_bits_total); ASSERT_TRUE(!memcmp(l2, result, 23)); @@ -180,6 +186,7 @@ static void test_fr(uint8_t *speech, int len) uint8_t result[33]; ubit_t bursts_u[116 * 8]; sbit_t bursts_s[116 * 8]; + int n_errors, n_bits_total; int rc; memset(bursts_u, 0x23, sizeof(bursts_u)); @@ -242,12 +249,17 @@ static void test_fr(uint8_t *speech, int len) (uint8_t)bursts_s[57 + 812], (uint8_t)bursts_s[58 + 812]); printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 812, 57)); + /* destroy */ + memset(bursts_s + 6, 0, 20); + /* decode */ - rc = tch_fr_decode(result, bursts_s, 1, len == 31); + rc = tch_fr_decode(result, bursts_s, 1, len == 31, &n_errors, &n_bits_total); ASSERT_TRUE(rc == len); printd("Decoded: %s\n", osmo_hexdump(result, len)); + printf("tch_fr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n", + n_errors, n_bits_total, (float)n_errors/n_bits_total); ASSERT_TRUE(!memcmp(speech, result, len)); @@ -259,6 +271,7 @@ static void test_hr(uint8_t *speech, int len) uint8_t result[23]; ubit_t bursts_u[116 * 6]; sbit_t bursts_s[116 * 6]; + int n_errors, n_bits_total; int rc; memset(bursts_u, 0x23, sizeof(bursts_u)); @@ -309,12 +322,17 @@ static void test_hr(uint8_t *speech, int len) (uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]); printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57)); + /* destroy */ + memset(bursts_s + 6, 0, 20); + /* decode */ - rc = tch_hr_decode(result, bursts_s, 0); + rc = tch_hr_decode(result, bursts_s, 0, &n_errors, &n_bits_total); ASSERT_TRUE(rc == len); printd("Decoded: %s\n", osmo_hexdump(result, len)); + printf("tch_hr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n", + n_errors, n_bits_total, (float)n_errors/n_bits_total); ASSERT_TRUE(!memcmp(speech, result, len)); @@ -326,6 +344,7 @@ static void test_pdtch(uint8_t *l2, int len) uint8_t result[len]; ubit_t bursts_u[116 * 4]; sbit_t bursts_s[116 * 4]; + int n_errors, n_bits_total; int rc; /* zero the not coded tail bits */ @@ -375,11 +394,13 @@ static void test_pdtch(uint8_t *l2, int len) printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57)); /* decode */ - rc = pdtch_decode(result, bursts_s, NULL); + rc = pdtch_decode(result, bursts_s, NULL, &n_errors, &n_bits_total); ASSERT_TRUE(rc == len); printd("Decoded: %s\n", osmo_hexdump(result, len)); + printf("pdtch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n", + n_errors, n_bits_total, (float)n_errors/n_bits_total); ASSERT_TRUE(!memcmp(l2, result, len)); @@ -388,22 +409,22 @@ static void test_pdtch(uint8_t *l2, int len) uint8_t test_l2[][23] = { /* dummy frame */ - { 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + { 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* random frame */ - { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, + { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92, 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8 }, /* jolly frame */ - { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, }; uint8_t test_macblock[][54] = { /* random frame */ - { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, + { 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92, 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8, 0x42, 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab, @@ -411,7 +432,7 @@ uint8_t test_macblock[][54] = { 0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92, 0xfa, 0x27, 0x5d, 0x71, 0x7a, 0xa8 }, /* jolly frame */ - { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, }; diff --git a/tests/bursts/bursts_test.ok b/tests/bursts/bursts_test.ok index 35821117..41369055 100644 --- a/tests/bursts/bursts_test.ok +++ b/tests/bursts/bursts_test.ok @@ -1 +1,21 @@ +xcch_decode: n_errors=60 n_bits_total=456 ber=0.13 +xcch_decode: n_errors=60 n_bits_total=456 ber=0.13 +xcch_decode: n_errors=60 n_bits_total=456 ber=0.13 +tch_fr_decode: n_errors=8 n_bits_total=378 ber=0.02 +tch_fr_decode: n_errors=8 n_bits_total=378 ber=0.02 +tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02 +tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02 +tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02 +tch_hr_decode: n_errors=11 n_bits_total=211 ber=0.05 +tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02 +tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02 +tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02 +pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00 +pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22 +pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33 +pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00 +pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00 +pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22 +pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33 +pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00 Success -- cgit v1.2.3 From 391ff14977d4e031562b6840d1ac0bf45ed4cfef Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Thu, 9 Apr 2015 13:24:15 +0300 Subject: trx: More logging for voice frame decoding functions. --- src/osmo-bts-trx/gsm0503_coding.c | 88 +++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index e98301b4..468e5321 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -29,6 +29,8 @@ #include #include +#include + #include "gsm0503_conv.h" #include "gsm0503_parity.h" #include "gsm0503_mapping.h" @@ -682,8 +684,10 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr, if (steal > 0) { rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_fr_decode(): error decoding FACCH frame (%d/%d bits)\n", *n_errors, *n_bits_total); return -1; + } return 23; } @@ -696,8 +700,10 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr, d[i+182] = (cB[i+378] < 0) ? 1:0; rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d, 50, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_fr_decode(): error checking CRC8 for the FR part of an %s frame\n", efr?"EFR":"FR"); return -1; + } if (efr) { @@ -709,8 +715,10 @@ int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order, int efr, rv = osmo_crc8gen_check_bits(&gsm0503_tch_efr_crc8, b, 65, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_fr_decode(): error checking CRC8 for the EFR part of an EFR frame\n"); return -1; + } tch_efr_reassemble(tch_data, s); @@ -812,8 +820,10 @@ int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, gsm0503_tch_fr_deinterleave(cB, iB); rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_hr_decode(): error decoding FACCH frame (%d/%d bits)\n", *n_errors, *n_bits_total); return -1; + } return 23; } @@ -832,8 +842,10 @@ int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd, d[i+95] = (cB[i+211] < 0) ? 1:0; rv = osmo_crc8gen_check_bits(&gsm0503_tch_fr_crc3, d + 73, 22, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_fr_decode(): error checking CRC8 for an HR frame\n"); return -1; + } tch_hr_d_to_b(b, d); @@ -912,8 +924,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, if (steal > 0) { rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error decoding FACCH frame (%d/%d bits)\n", *n_errors, *n_bits_total); return -1; + } return 23; } @@ -941,8 +955,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 244, 81); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 81, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 12.2 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 244); @@ -955,8 +971,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 204, 65); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 65, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 10.2 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 204); @@ -969,8 +987,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 159, 75); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 75, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 7.95 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 159); @@ -983,8 +1003,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 148, 61); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 7.4 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 148); @@ -997,8 +1019,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 134, 55); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 6.7 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 134); @@ -1011,8 +1035,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 118, 55); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 5.9 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 118); @@ -1025,8 +1051,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 103, 49); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 5.15 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 103); @@ -1039,8 +1067,10 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, tch_amr_unmerge(d, p, conv, 95, 39); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_afs_decode(): error checking CRC8 for an AMR 4.75 frame\n"); return -1; + } tch_amr_reassemble(tch_data, d, 95); @@ -1048,6 +1078,7 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, break; default: + LOGP(DL1C, LOGL_ERROR, "tch_afs_decode(): Unknown frame type\n"); fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); *n_bits_total = 448; *n_errors = *n_bits_total; @@ -1256,8 +1287,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, gsm0503_tch_fr_deinterleave(cB, iB); rv = _xcch_decode_cB(tch_data, cB, n_errors, n_bits_total); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error decoding FACCH frame (%d/%d bits)\n", *n_errors, *n_bits_total); return -1; + } return 23; } @@ -1291,8 +1324,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, tch_amr_unmerge(d, p, conv, 123, 67); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 67, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error checking CRC8 for an AMR 7.95 frame\n"); return -1; + } for (i=0; i<36;i++) d[i+123] = (cB[i+192] < 0) ? 1:0; @@ -1308,8 +1343,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, tch_amr_unmerge(d, p, conv, 120, 61); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 61, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error checking CRC8 for an AMR 7.4 frame\n"); return -1; + } for (i=0; i<28;i++) d[i+120] = (cB[i+200] < 0) ? 1:0; @@ -1325,8 +1362,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, tch_amr_unmerge(d, p, conv, 110, 55); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error checking CRC8 for an AMR 6.7 frame\n"); return -1; + } for (i=0; i<24;i++) d[i+110] = (cB[i+204] < 0) ? 1:0; @@ -1342,8 +1381,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, tch_amr_unmerge(d, p, conv, 102, 55); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 55, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error checking CRC8 for an AMR 5.9 frame\n"); return -1; + } for (i=0; i<16;i++) d[i+102] = (cB[i+212] < 0) ? 1:0; @@ -1359,8 +1400,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, tch_amr_unmerge(d, p, conv, 91, 49); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 49, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error checking CRC8 for an AMR 5.15 frame\n"); return -1; + } for (i=0; i<12;i++) d[i+91] = (cB[i+216] < 0) ? 1:0; @@ -1376,8 +1419,10 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, tch_amr_unmerge(d, p, conv, 83, 39); rv = osmo_crc8gen_check_bits(&gsm0503_amr_crc6, d, 39, p); - if (rv) + if (rv) { + LOGP(DL1C, LOGL_NOTICE, "tch_ahs_decode(): error checking CRC8 for an AMR 4.75 frame\n"); return -1; + } for (i=0; i<12;i++) d[i+83] = (cB[i+216] < 0) ? 1:0; @@ -1388,6 +1433,7 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, break; default: + LOGP(DL1C, LOGL_ERROR, "tch_afs_decode(): Unknown frame type\n"); fprintf(stderr, "FIXME: FT %d not supported!\n", *ft); *n_bits_total = 159; *n_errors = *n_bits_total; -- cgit v1.2.3 From 68e8b2b1d5ea6e6236c277641cf565e7f99d7527 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Fri, 5 Jun 2015 00:54:03 -0400 Subject: trx: Fix typo in a log message. --- src/osmo-bts-trx/scheduler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c index 23bfebc0..8d0c8343 100644 --- a/src/osmo-bts-trx/scheduler.c +++ b/src/osmo-bts-trx/scheduler.c @@ -2898,7 +2898,7 @@ static void trx_ctrl_timer_cb(void *data) if (transceiver_lost++ == TRX_LOSS_FRAMES) { struct gsm_bts_trx *trx; - LOGP(DL1C, LOGL_NOTICE, "No more clock from traneiver\n"); + LOGP(DL1C, LOGL_NOTICE, "No more clock from transceiver\n"); no_clock: transceiver_available = 0; -- cgit v1.2.3 From 17be7fa73bd3ea0e25bafbbd88b79f4f5a0762e9 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Fri, 5 Jun 2015 01:12:21 -0400 Subject: trx: Remove unused variables. --- src/osmo-bts-trx/gsm0503_coding.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/osmo-bts-trx/gsm0503_coding.c b/src/osmo-bts-trx/gsm0503_coding.c index 468e5321..fea25f9c 100644 --- a/src/osmo-bts-trx/gsm0503_coding.c +++ b/src/osmo-bts-trx/gsm0503_coding.c @@ -910,7 +910,7 @@ int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req, int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; - ubit_t test[456], d[244], p[6], conv[250]; + ubit_t d[244], p[6], conv[250]; int i, j, k, best = 0, rv, len, steal = 0, id = 0; *n_errors = 0; *n_bits_total = 0; @@ -1260,7 +1260,7 @@ int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd, uint8_t *cmr, int *n_errors, int *n_bits_total) { sbit_t iB[912], cB[456], h; - ubit_t test[456], d[244], p[6], conv[135]; + ubit_t d[244], p[6], conv[135]; int i, j, k, best = 0, rv, len, steal = 0, id = 0; /* only unmap the stealing bits */ -- cgit v1.2.3 From e9abc5a4f33d8432d81ab896189b7492b07a05cf Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Wed, 10 Jun 2015 00:26:14 -0400 Subject: trx: Cleanup unused parts of loops.c --- src/osmo-bts-trx/loops.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c index 8b6844be..fa380555 100644 --- a/src/osmo-bts-trx/loops.c +++ b/src/osmo-bts-trx/loops.c @@ -90,8 +90,7 @@ static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan, return 0; } -static int ms_power_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, - uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t rssi) +static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi) { /* ignore inserted dummy frames, treat as lost frames */ if (rssi < -127) @@ -101,10 +100,6 @@ static int ms_power_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, chan_state->meas.rssi_count++; - /* check if the current L1 header compares to the current ordered TA */ -// if ((lchan->meas.l1_info[0] >> 3) != lchan->ms_power) -// return 0; - chan_state->meas.rssi_got_burst = 1; /* store and process RSSI */ @@ -222,7 +217,7 @@ int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr, .lchan[l1sap_chan2ss(chan_nr)]; if (trx_ms_power_loop) - ms_power_val(l1h, lchan, chan_nr, chan_state, rssi); + ms_power_val(chan_state, rssi); if (trx_ta_loop) ta_val(l1h, lchan, chan_nr, chan_state, toa); -- cgit v1.2.3 From 29ea40f5387c18a0aeb906039750bbf3a9828e7e Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Wed, 10 Jun 2015 00:27:18 -0400 Subject: trx: Assume 100% BER if total decoded bits is 0 in l1if_process_meas_res() --- src/osmo-bts-trx/l1_if.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index 6f8cd845..d0cf56d2 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -474,7 +474,8 @@ int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint { struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)]; struct osmo_phsap_prim l1sap; - float ber = (float)n_errors / (float)n_bits_total; + /* 100% BER is n_bits_total is 0 */ + float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total; LOGP(DMEAS, LOGL_DEBUG, "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS " "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n", -- cgit v1.2.3 From ae525a8761d9651e2824fd7c84f0c7db8aaa37fd Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Mon, 15 Jun 2015 00:33:37 -0400 Subject: trx: Send POWERON/OFF commands to osmo-bts only for the first channel. osmo-trx never supported separate power control for trx's, but now it started to be more strict about it. --- src/osmo-bts-trx/trx_if.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 8993de64..5b1125ad 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -231,12 +231,18 @@ static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd, int trx_if_cmd_poweroff(struct trx_l1h *l1h) { - return trx_ctrl_cmd(l1h, 1, "POWEROFF", ""); + if (l1h->trx->nr == 0) + return trx_ctrl_cmd(l1h, 1, "POWEROFF", ""); + else + return 0; } int trx_if_cmd_poweron(struct trx_l1h *l1h) { - return trx_ctrl_cmd(l1h, 1, "POWERON", ""); + if (l1h->trx->nr == 0) + return trx_ctrl_cmd(l1h, 1, "POWERON", ""); + else + return 0; } int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc) -- cgit v1.2.3 From b812839dfab3208928b6b8f45368abcb6b9ff96a Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Mon, 15 Jun 2015 00:40:27 -0400 Subject: trx: fix potential use of uninitialized toa variable. Not really a bug, as we're smart about it down the stream, but it's better to be strict here as well. --- src/osmo-bts-trx/trx_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 5b1125ad..69ad8415 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -407,7 +407,7 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what) int len; uint8_t tn; int8_t rssi; - float toa; + float toa = 0.0; uint32_t fn; sbit_t bits[148]; int i; -- cgit v1.2.3 From 5becc4613a34c5fce7c261268cef679821387e4a Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Mon, 20 Jul 2015 20:04:02 -0400 Subject: tests: Update busrsts_test build. We've added logging calls to the bursts processing. Add logging facility initializatoin to the test code. --- tests/bursts/Makefile.am | 2 +- tests/bursts/bursts_test.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/bursts/Makefile.am b/tests/bursts/Makefile.am index 970d9a47..462b7288 100644 --- a/tests/bursts/Makefile.am +++ b/tests/bursts/Makefile.am @@ -11,4 +11,4 @@ bursts_test_SOURCES = bursts_test.c \ $(top_builddir)/src/osmo-bts-trx/gsm0503_mapping.c \ $(top_builddir)/src/osmo-bts-trx/gsm0503_tables.c \ $(top_builddir)/src/osmo-bts-trx/gsm0503_parity.c -bursts_test_LDADD = $(LDADD) +bursts_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD) diff --git a/tests/bursts/bursts_test.c b/tests/bursts/bursts_test.c index ef703a8a..cc0eacff 100644 --- a/tests/bursts/bursts_test.c +++ b/tests/bursts/bursts_test.c @@ -29,6 +29,7 @@ #include "../../src/osmo-bts-trx/gsm0503_coding.h" +#include #define ASSERT_TRUE(rc) \ if (!(rc)) { \ @@ -445,6 +446,8 @@ int main(int argc, char **argv) { int i; + bts_log_init(NULL); + for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++) test_xcch(test_l2[i]); -- cgit v1.2.3 From 307bfc81c1d4eb782339946c0dbccab6095aa10f Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 6 Sep 2015 14:11:31 +0200 Subject: fixup tests after bts_model_adjst_ms_pwr --- tests/handover/handover_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c index ed4bedd0..6d7e6d1e 100644 --- a/tests/handover/handover_test.c +++ b/tests/handover/handover_test.c @@ -275,3 +275,4 @@ int bts_model_init(struct gsm_bts *bts) { return 0; } int bts_model_trx_deact_rf(struct gsm_bts_trx *trx) { return 0; } int bts_model_trx_close(struct gsm_bts_trx *trx) { return 0; } void trx_get_hlayer1(void) {} +int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) { return 0; } -- cgit v1.2.3 From 88a31e2a990dfbacfc9d6b7cbfd03edc99e68687 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 6 Sep 2015 14:12:17 +0200 Subject: make osmo-bts-trx provide bts_model_adjst_ms_pwr() --- src/osmo-bts-trx/l1_if.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index d0cf56d2..42cb17b8 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -296,6 +296,13 @@ void bts_model_abis_close(struct gsm_bts *bts) bts_model_trx_close(trx); } +int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) +{ + /* we always implement the power control loop in osmo-bts software, as + * there is no automatism in the underlying osmo-trx */ + return 0; +} + /* set bts attributes */ static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr) { -- cgit v1.2.3 From caa648d92e48a05e676e87b48c21cb0b151c9b4e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 12 Sep 2015 11:00:23 +0200 Subject: TRX: Add missing call to abis_init() This somehow got lost during the latest rebase. --- src/osmo-bts-trx/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c index c4a4c601..6f2dd4bc 100644 --- a/src/osmo-bts-trx/main.c +++ b/src/osmo-bts-trx/main.c @@ -328,6 +328,8 @@ int main(int argc, char **argv) gsmtap_source_add_sink(gsmtap); } + abis_init(bts); + rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", -- cgit v1.2.3