diff options
460 files changed, 30238 insertions, 14329 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..7592debf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: osmocom diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh index fafbdfba..31278737 100755 --- a/contrib/jenkins.sh +++ b/contrib/jenkins.sh @@ -17,14 +17,17 @@ osmo-clean-workspace.sh mkdir "$deps" || true -osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false - -# TODO: ask whether fail is expected, because osmocom-bb build succeeds? -#"$deps"/libosmocore/contrib/verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") +# exclude ancient local copy of libosmocore.git +verify_value_string_arrays_are_terminated.py \ + $(find . -path ./src/shared/libosmocore -prune -o -name '*.[hc]' -print) export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" export LD_LIBRARY_PATH="$inst/lib" +osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false +osmo-build-dep.sh libosmo-gprs +osmo-build-dep.sh gapk + set +x echo echo @@ -35,19 +38,21 @@ set -x # building those sub-projects where 'distcheck' is known-working -for dir in gprsdecode layer23; do +for dir in gprsdecode layer23 trxcon virt_phy; do cd $base/src/host/$dir autoreconf -fi - ./configure - make distcheck + ./configure --enable-werror + $MAKE $PARALLEL_MAKE + DISTCHECK_CONFIGURE_FLAGS="--enable-werror" $MAKE $PARALLEL_MAKE distcheck done # TODO: make sure 'distcheck' passes also for these -for dir in gsmmap osmocon trxcon virt_phy; do +# TODO: make sure '--enable-werror' passes also for these +for dir in osmocon; do cd $base/src/host/$dir autoreconf -fi ./configure - make + $MAKE $PARALLEL_MAKE done # Build and publish manuals @@ -61,7 +66,7 @@ if [ "$WITH_MANUALS" = "1" ]; then fi # Test 'maintainer-clean' -for dir in gprsdecode layer23 gsmmap osmocon trxcon virt_phy; do +for dir in gprsdecode layer23 osmocon trxcon virt_phy; do cd "$base/src/host/$dir" make maintainer-clean done diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..2335d186 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +osmocom-bb (0.1.0) unstable; urgency=medium + + * Initial debian packaging for layer23 + + -- Oliver Smith <osmith@sysmocom.de> Mon, 19 Mar 2024 11:38:22 +0100 diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..f599e28b --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..c90b6a7c --- /dev/null +++ b/debian/control @@ -0,0 +1,29 @@ +Source: osmocom-bb +Section: net +Priority: optional +Maintainer: Osmocom team <openbsc@lists.osmocom.org> +Build-Depends: debhelper (>= 10), + dh-autoreconf, + autotools-dev, + autoconf, + automake, + libtool, + pkg-config, + libosmocore-dev (>= 1.5.0), + libosmo-csn1-dev, + libosmo-gprs-gmm-dev, + libosmo-gprs-llc-dev, + libosmo-gprs-rlcmac-dev, + libosmo-gprs-sm-dev, + libosmo-gprs-sndcp-dev, + libosmogapk-dev +Standards-Version: 4.1.4 +Vcs-Browser: https://gitea.osmocom.org/phone-side/osmocom-bb +Vcs-Git: https://gerrit.osmocom.org/osmocom-bb +Homepage: https://osmocom.org/projects/baseband/wiki + +Package: osmocom-bb-layer23 +Architecture: any +Multi-Arch: foreign +Depends: ${misc:Depends}, ${shlibs:Depends} +Description: MS-side implementation of L2 and L3 GSM protocols diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..c9ee2c19 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,23 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: osmocom-bb +Source: https://gitea.osmocom.org/phone-side/osmocom-bb + +Files: * +License: GPL-2.0+ + +License: GPL-2.0+ + This package is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/debian/osmocom-bb.install b/debian/osmocom-bb.install new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/debian/osmocom-bb.install diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..8dd0df8d --- /dev/null +++ b/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f + +%: + dh $@ -D src/host/layer23 --with-autoreconf + +override_dh_auto_configure: + dh_auto_configure -D src/host/layer23 -- --with-gapk-io diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 00000000..89ae9db8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/doc/examples/mobile/default.cfg b/doc/examples/mobile/default.cfg index f14e9009..676c76fc 100644 --- a/doc/examples/mobile/default.cfg +++ b/doc/examples/mobile/default.cfg @@ -14,6 +14,8 @@ no hide-default ms 1 layer2-socket /tmp/osmocom_l2 sap-socket /tmp/osmocom_sap + mncc-socket /tmp/ms_mncc_1 + mncc-handler internal sim reader network-selection-mode auto imei 000000000000000 0 @@ -51,6 +53,12 @@ ms 1 full-speech-v1 full-speech-v2 half-speech-v1 + no full-data-14400 + full-data-9600 + full-data-4800 + half-data-4800 + full-data-2400 + half-data-2400 min-rxlev -106 dsc-max 90 no skip-max-per-band @@ -59,6 +67,19 @@ ms 1 ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 no barred-access rplmn 001 01 - audio - io-handler none + tch-data + io-handler unix-sock + io-tch-format ti + unix-socket /tmp/ms_data_1 + call-params type-rate 71 + call-params ce transparent + call-params async + call-params async nr-stop-bits 1 + call-params async nr-data-bits 8 + call-params async parity none + tch-voice + io-handler l1phy +! io-format ti + alsa-output-dev default + alsa-input-dev default no shutdown diff --git a/doc/examples/mobile/multi_ms.cfg b/doc/examples/mobile/multi_ms.cfg index c72817fd..48cdfb13 100644 --- a/doc/examples/mobile/multi_ms.cfg +++ b/doc/examples/mobile/multi_ms.cfg @@ -14,6 +14,8 @@ no hide-default ms one layer2-socket /tmp/osmocom_l2.one sap-socket /tmp/osmocom_sap.one + mncc-socket /tmp/ms_mncc_one + mncc-handler internal sim reader network-selection-mode auto imei 000000000000000 0 @@ -59,13 +61,22 @@ ms one ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 no barred-access rplmn 001 01 - audio - io-handler none + tch-data + io-handler unix-sock + io-tch-format ti + unix-socket /tmp/ms_data.one + tch-voice + io-handler l1phy +! io-format ti + alsa-output-dev default + alsa-input-dev default no shutdown ! ms two layer2-socket /tmp/osmocom_l2.two sap-socket /tmp/osmocom_sap.two + mncc-socket /tmp/ms_mncc_two + mncc-handler internal sim reader network-selection-mode auto imei 000000000000000 0 @@ -111,6 +122,13 @@ ms two ki comp128 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff no barred-access rplmn 001 01 - audio - io-handler none + tch-data + io-handler unix-sock + io-tch-format ti + unix-socket /tmp/ms_data.two + tch-voice + io-handler l1phy +! io-format ti + alsa-output-dev default + alsa-input-dev default no shutdown diff --git a/doc/examples/modem/modem.cfg b/doc/examples/modem/modem.cfg new file mode 100644 index 00000000..af98082c --- /dev/null +++ b/doc/examples/modem/modem.cfg @@ -0,0 +1,24 @@ +! +! OsmocomBB example configuration for modem application +!! +! +line vty + no login +! +ms 1 + imei 000000000000000 0 + imei-fixed + sim test + test-sim + imsi 001010000000000 + ki comp128 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + no barred-access + rplmn 001 01 + apn internet + tun-device modem4 +! tun-netns netns_modem4 + type-support v4 + no shutdown + no shutdown +cpu-sched + policy rr 1 diff --git a/doc/manuals/Makefile b/doc/manuals/Makefile index 603f4ba6..53bb375b 100644 --- a/doc/manuals/Makefile +++ b/doc/manuals/Makefile @@ -5,4 +5,5 @@ ASCIIDOC = osmocombb-usermanual.adoc ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc +OSMO_REPOSITORY = osmocom-bb include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h index cf41ac74..79feabab 100644 --- a/include/l1ctl_proto.h +++ b/include/l1ctl_proto.h @@ -15,58 +15,52 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef __L1CTL_PROTO_H__ #define __L1CTL_PROTO_H__ enum { - _L1CTL_NONE = 0, - L1CTL_FBSB_REQ, - L1CTL_FBSB_CONF, - L1CTL_DATA_IND, - L1CTL_RACH_REQ, - L1CTL_DM_EST_REQ, - L1CTL_DATA_REQ, - L1CTL_RESET_IND, - L1CTL_PM_REQ, /* power measurement */ - L1CTL_PM_CONF, /* power measurement */ - L1CTL_ECHO_REQ, - L1CTL_ECHO_CONF, - L1CTL_RACH_CONF, - L1CTL_RESET_REQ, - L1CTL_RESET_CONF, - L1CTL_DATA_CONF, - L1CTL_CCCH_MODE_REQ, - L1CTL_CCCH_MODE_CONF, - L1CTL_DM_REL_REQ, - L1CTL_PARAM_REQ, - L1CTL_DM_FREQ_REQ, - L1CTL_CRYPTO_REQ, - L1CTL_SIM_REQ, - L1CTL_SIM_CONF, - L1CTL_TCH_MODE_REQ, - L1CTL_TCH_MODE_CONF, - L1CTL_NEIGH_PM_REQ, - L1CTL_NEIGH_PM_IND, - L1CTL_TRAFFIC_REQ, - L1CTL_TRAFFIC_CONF, - L1CTL_TRAFFIC_IND, - L1CTL_BURST_IND, - - /* configure TBF for uplink/downlink */ - L1CTL_TBF_CFG_REQ, - L1CTL_TBF_CFG_CONF, - - L1CTL_DATA_TBF_REQ, - L1CTL_DATA_TBF_CONF, - + _L1CTL_NONE = 0x00, + L1CTL_FBSB_REQ = 0x01, + L1CTL_FBSB_CONF = 0x02, + L1CTL_DATA_IND = 0x03, + L1CTL_RACH_REQ = 0x04, + L1CTL_DM_EST_REQ = 0x05, + L1CTL_DATA_REQ = 0x06, + L1CTL_RESET_IND = 0x07, + L1CTL_PM_REQ = 0x08, /* power measurement */ + L1CTL_PM_CONF = 0x09, /* power measurement */ + L1CTL_ECHO_REQ = 0x0a, + L1CTL_ECHO_CONF = 0x0b, + L1CTL_RACH_CONF = 0x0c, + L1CTL_RESET_REQ = 0x0d, + L1CTL_RESET_CONF = 0x0e, + L1CTL_DATA_CONF = 0x0f, + L1CTL_CCCH_MODE_REQ = 0x10, + L1CTL_CCCH_MODE_CONF = 0x11, + L1CTL_DM_REL_REQ = 0x12, + L1CTL_PARAM_REQ = 0x13, + L1CTL_DM_FREQ_REQ = 0x14, + L1CTL_CRYPTO_REQ = 0x15, + L1CTL_SIM_REQ = 0x16, + L1CTL_SIM_CONF = 0x17, + L1CTL_TCH_MODE_REQ = 0x18, + L1CTL_TCH_MODE_CONF = 0x19, + L1CTL_NEIGH_PM_REQ = 0x1a, + L1CTL_NEIGH_PM_IND = 0x1b, + L1CTL_TRAFFIC_REQ = 0x1c, + L1CTL_TRAFFIC_CONF = 0x1d, + L1CTL_TRAFFIC_IND = 0x1e, + L1CTL_BURST_IND = 0x1f, + L1CTL_GPRS_UL_TBF_CFG_REQ = 0x20, + L1CTL_GPRS_DL_TBF_CFG_REQ = 0x21, + L1CTL_GPRS_UL_BLOCK_REQ = 0x22, + L1CTL_GPRS_DL_BLOCK_IND = 0x23, /* Extended (11-bit) RACH (see 3GPP TS 05.02, section 5.2.7) */ - L1CTL_EXT_RACH_REQ, + L1CTL_EXT_RACH_REQ = 0x24, + L1CTL_GPRS_RTS_IND = 0x25, + L1CTL_GPRS_UL_BLOCK_CNF = 0x26, }; enum ccch_mode { @@ -82,23 +76,6 @@ enum neigh_mode { NEIGH_MODE_SB, }; -enum l1ctl_coding_scheme { - L1CTL_CS_NONE, - L1CTL_CS1, - L1CTL_CS2, - L1CTL_CS3, - L1CTL_CS4, - L1CTL_MCS1, - L1CTL_MCS2, - L1CTL_MCS3, - L1CTL_MCS4, - L1CTL_MCS5, - L1CTL_MCS6, - L1CTL_MCS7, - L1CTL_MCS8, - L1CTL_MCS9, -}; - /* * NOTE: struct size. We do add manual padding out of the believe * that it will avoid some unaligned access. @@ -166,7 +143,11 @@ struct l1ctl_tch_mode_conf { uint8_t tch_mode; /* enum tch_mode */ uint8_t audio_mode; uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */ - uint8_t padding[1]; + struct { /* 3GPP TS 08.58 9.3.52, 3GPP TS 44.018 10.5.2.21aa */ + uint8_t start_codec; + uint8_t codecs_bitmask; + } amr; + uint8_t tch_flags; } __attribute__((packed)); /* data on the CCCH was found. This is following the header */ @@ -192,15 +173,6 @@ struct l1ctl_info_ul { uint8_t payload[0]; } __attribute__((packed)); -struct l1ctl_info_ul_tbf { - /* references l1ctl_tbf_cfg_req.tbf_nr */ - uint8_t tbf_nr; - uint8_t coding_scheme; - uint8_t padding[2]; - /* RLC/MAC block, size determines CS */ - uint8_t payload[0]; -} __attribute__((packed)); - /* * msg for FBSB_REQ * the l1_info_ul header is in front @@ -245,14 +217,21 @@ struct l1ctl_tch_mode_req { #define AUDIO_RX_TRAFFIC_IND (1<<3) uint8_t audio_mode; uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */ - uint8_t padding[1]; + struct { /* 3GPP TS 08.58 9.3.52, 3GPP TS 44.018 10.5.2.21aa */ + uint8_t start_codec; + uint8_t codecs_bitmask; + } amr; + uint8_t tch_flags; } __attribute__((packed)); +#define L1CTL_TCH_FLAG_RXONLY (1<<0) /* TX disabled */ + /* the l1_info_ul header is in front */ struct l1ctl_rach_req { uint8_t ra; uint8_t combined; uint16_t offset; + uint8_t uic; } __attribute__((packed)); @@ -292,6 +271,7 @@ struct l1ctl_dm_est_req { }; uint8_t tch_mode; uint8_t audio_mode; + uint8_t tch_flags; } __attribute__((packed)); struct l1ctl_dm_freq_req { @@ -373,15 +353,60 @@ struct l1ctl_traffic_req { uint8_t data[0]; } __attribute__((packed)); -struct l1ctl_tbf_cfg_req { - /* future support for multiple concurrent TBFs. 0 for now */ - uint8_t tbf_nr; - /* is this about an UL TBF (1) or DL (0) */ - uint8_t is_uplink; +/* payload of L1CTL_GPRS_UL_TBF_CFG_REQ */ +struct l1ctl_gprs_ul_tbf_cfg_req { + uint8_t tbf_ref; + uint8_t slotmask; uint8_t padding[2]; + uint32_t start_fn; /* TBF Starting Time (absolute Fn) */ +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_DL_TBF_CFG_REQ */ +struct l1ctl_gprs_dl_tbf_cfg_req { + uint8_t tbf_ref; + uint8_t slotmask; + uint8_t dl_tfi; + uint8_t padding[1]; + uint32_t start_fn; /* TBF Starting Time (absolute Fn) */ +} __attribute__((packed)); - /* one USF for each TN, or 255 for invalid/unused */ - uint8_t usf[8]; +/* part of L1CTL_GPRS_{UL,DL}_BLOCK_{REQ,IND} */ +struct l1ctl_gprs_block_hdr { + uint32_t fn; + uint8_t tn; + uint8_t padding[3]; +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_UL_BLOCK_REQ */ +struct l1ctl_gprs_ul_block_req { + struct l1ctl_gprs_block_hdr hdr; + uint8_t data[0]; +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_DL_BLOCK_IND */ +struct l1ctl_gprs_dl_block_ind { + struct l1ctl_gprs_block_hdr hdr; + struct __attribute__((packed)) { + uint16_t ber10k; /* Bit Error Rate */ + int16_t ci_cb; /* C/I in centiBels */ + uint8_t rx_lev; /* RxLev 0..63 */ + } meas; + uint8_t usf; + uint8_t data[0]; +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_RTS_IND */ +struct l1ctl_gprs_rts_ind { + uint32_t fn; + uint8_t tn; + uint8_t usf; +} __attribute__((packed)); + +/* payload of L1CTL_GPRS_UL_BLOCK_CNF */ +struct l1ctl_gprs_ul_block_cnf { + uint32_t fn; + uint8_t tn; + uint8_t data[0]; } __attribute__((packed)); #endif /* __L1CTL_PROTO_H__ */ diff --git a/include/l1gprs.h b/include/l1gprs.h new file mode 100644 index 00000000..6a18da1f --- /dev/null +++ b/include/l1gprs.h @@ -0,0 +1,120 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#include <osmocom/core/linuxlist.h> + +struct l1gprs_state; +struct msgb; + +struct l1gprs_tbf_pending_req { + /*! Item in l1gprs_state->tbf_list_pending */ + struct llist_head list; + /*! Uplink or Downlink */ + bool uplink; + /*! TBF reference number (not index) */ + uint8_t tbf_ref; + /*! PDCH timeslots used by this TBF */ + uint8_t slotmask; + /*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */ + uint8_t dl_tfi; + /*! TBF starting time (absolute TDMA Fn) */ + uint32_t start_fn; +}; + +struct l1gprs_tbf { + /*! Item in l1gprs_state->tbf_list */ + struct llist_head list; + /*! Uplink or Downlink */ + bool uplink; + /*! TBF reference number (not index) */ + uint8_t tbf_ref; + /*! PDCH timeslots used by this TBF */ + uint8_t slotmask; + /*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */ + uint8_t dl_tfi; +}; + +struct l1gprs_pdch { + /*! Timeslot number */ + uint8_t tn; + /*! Backpointer to l1gprs_state we belong to */ + struct l1gprs_state *gprs; + /*! UL TBF count */ + uint8_t ul_tbf_count; + /*! DL TBF count */ + uint8_t dl_tbf_count; + /*! DL TFI mask */ + uint32_t dl_tfi_mask; + /*! Pending UL TBF count */ + uint8_t pending_ul_tbf_count; + /*! Pending DL TBF count */ + uint8_t pending_dl_tbf_count; +}; + +static inline size_t l1gprs_pdch_use_count(const struct l1gprs_pdch *pdch) +{ + return pdch->ul_tbf_count + pdch->dl_tbf_count + + pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count; +} + + +typedef void (*l1gprs_pdch_changed_t)(struct l1gprs_pdch *pdch, bool active); + +struct l1gprs_state { + /*! PDCH state for each timeslot */ + struct l1gprs_pdch pdch[8]; + /*! Uplink and Downlink TBFs (active), struct l1gprs_pending_tbf */ + struct llist_head tbf_list; + /*! Uplink and Downlink TBFs (pending), struct l1gprs_tbf_pending_req */ + struct llist_head tbf_list_pending; + /*! Logging context (used as prefix for messages) */ + char *log_prefix; + /*! Some private data for API user */ + void *priv; + /*! Callback triggered to signal lower layers when a PDCH TS has to be activated/deactivated */ + l1gprs_pdch_changed_t pdch_changed_cb; +}; + +void l1gprs_logging_init(int logc); +struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv); +void l1gprs_state_free(struct l1gprs_state *gprs); +void l1gprs_state_set_pdch_changed_cb(struct l1gprs_state *gprs, l1gprs_pdch_changed_t pdch_changed_cb); + +int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg); +int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg); + +struct l1gprs_prim_block_hdr { + uint32_t fn; + uint8_t tn; +}; + +struct l1gprs_prim_ul_block_req { + struct l1gprs_prim_block_hdr hdr; + size_t data_len; + const uint8_t *data; +}; + +struct l1gprs_prim_dl_block_ind { + struct l1gprs_prim_block_hdr hdr; + struct { + uint16_t ber10k; + int16_t ci_cb; + uint8_t rx_lev; + } meas; + size_t data_len; + const uint8_t *data; +}; + +int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, + struct l1gprs_prim_ul_block_req *req, + const struct msgb *msg); +struct msgb *l1gprs_handle_ul_block_cnf(struct l1gprs_state *gprs, + uint32_t fn, uint8_t tn, + const uint8_t *data, + size_t data_len); +struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs, + const struct l1gprs_prim_dl_block_ind *ind, uint8_t *usf); +struct msgb *l1gprs_handle_rts_ind(struct l1gprs_state *gprs, uint32_t fn, uint8_t tn, uint8_t usf); diff --git a/src/Makefile b/src/Makefile index d92acbc1..0835a855 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ TOPDIR=$(shell pwd) all: libosmocore-target nofirmware firmware mtk-firmware -nofirmware: layer23 osmocon trxcon gsmmap gprsdecode virtphy +nofirmware: layer23 osmocon trxcon gprsdecode virtphy libosmocore-target: shared/libosmocore/build-target/src/.libs/libosmocore.a @@ -71,19 +71,6 @@ host/trxcon/trxcon: host/trxcon/Makefile make -C host/trxcon -.PHONY: gsmmap -gsmmap: host/gsmmap/gsmmap - -host/gsmmap/configure: host/gsmmap/configure.ac - cd host/gsmmap && autoreconf -i - -host/gsmmap/Makefile: host/gsmmap/configure - cd host/gsmmap && ./configure $(HOST_CONFARGS) - -host/gsmmap/gsmmap: host/gsmmap/Makefile - make -C host/gsmmap - - .PHONY: layer23 layer23: host/layer23/layer23 @@ -121,7 +108,6 @@ clean: make -C shared/libosmocore/build-target $@ make -C host/layer23 $@ make -C host/osmocon $@ - make -C host/gsmmap $@ make -C host/gprsdecode $@ make -C host/virt_phy $@ make -C host/trxcon $@ @@ -132,7 +118,6 @@ distclean: rm -rf shared/libosmocore/build-target make -C host/layer23 $@ make -C host/osmocon $@ - make -C host/gsmmap $@ make -C host/gprsdecode $@ make -C host/virt_phy $@ make -C host/trxcon $@ diff --git a/src/README.development b/src/README.development index 64821438..e09793b4 100644 --- a/src/README.development +++ b/src/README.development @@ -33,7 +33,7 @@ handling, timers - as well as some more specifically GSM related things like a TLV parser, a Comp128V1 implementation and utility functions for RSL (TS 08.58) and CC/MM/RR (TS 04.08). -libosmocore is maintained in git://git.osmocom.org/libosmocore.git, so +libosmocore is maintained in https://gitea.osmocom.org/osmocom/libosmocore, so DO NOT DIRECTLY COMMIT TO libosmocore IN THIS REPOSITORY! diff --git a/src/host/fb_tools/bdf_to_c.py b/src/host/fb_tools/bdf_to_c.py index ebeb7f9c..f808f508 100755 --- a/src/host/fb_tools/bdf_to_c.py +++ b/src/host/fb_tools/bdf_to_c.py @@ -19,10 +19,6 @@ selected glyphs in the format defined by the <fb/font.h> header. # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from optparse import OptionParser import sys diff --git a/src/host/gprsdecode/Makefile.am b/src/host/gprsdecode/Makefile.am index 542d54b6..b5c8b12a 100644 --- a/src/host/gprsdecode/Makefile.am +++ b/src/host/gprsdecode/Makefile.am @@ -6,8 +6,7 @@ AM_CPPFLAGS = \ $(NULL) AM_CFLAGS = \ - -Wall -O3 \ - -Wno-missing-braces \ + -Wall \ $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOCODING_CFLAGS) \ diff --git a/src/host/gprsdecode/configure.ac b/src/host/gprsdecode/configure.ac index 8da68c80..734cbd30 100644 --- a/src/host/gprsdecode/configure.ac +++ b/src/host/gprsdecode/configure.ac @@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script AC_INIT([gprsdecode], [0.0.0]) AM_INIT_AUTOMAKE +CFLAGS="$CFLAGS -std=gnu11" + dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) diff --git a/src/host/gprsdecode/gprs.c b/src/host/gprsdecode/gprs.c index ae59cf90..4ba91ea0 100644 --- a/src/host/gprsdecode/gprs.c +++ b/src/host/gprsdecode/gprs.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/host/gprsdecode/gsmtap.c b/src/host/gprsdecode/gsmtap.c index 5c124b23..ec816b35 100644 --- a/src/host/gprsdecode/gsmtap.c +++ b/src/host/gprsdecode/gsmtap.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -100,6 +96,6 @@ void gsmtap_send_llc(uint8_t *data, size_t len, bool ul) memcpy(dst, data, len); /* Finally, send to the sink */ - gsmtap_sendmsg(gti, msg); + gsmtap_sendmsg_free(gti, msg); } diff --git a/src/host/gprsdecode/main.c b/src/host/gprsdecode/main.c index 7e094896..49484e29 100644 --- a/src/host/gprsdecode/main.c +++ b/src/host/gprsdecode/main.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/host/gprsdecode/rlcmac.c b/src/host/gprsdecode/rlcmac.c index d33cb1eb..dc11fd30 100644 --- a/src/host/gprsdecode/rlcmac.c +++ b/src/host/gprsdecode/rlcmac.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/host/gprsdecode/rlcmac.h b/src/host/gprsdecode/rlcmac.h index 2381d771..41c0decd 100644 --- a/src/host/gprsdecode/rlcmac.h +++ b/src/host/gprsdecode/rlcmac.h @@ -2,6 +2,7 @@ #include <stdint.h> #include <stdbool.h> +#include <osmocom/core/endian.h> #define OLD_TIME 2000 @@ -16,10 +17,16 @@ struct gprs_message { }; struct gprs_lime { +#if OSMO_IS_LITTLE_ENDIAN uint8_t li:6, m:1, e:1; uint8_t used; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t e:1, m:1, li:6; + uint8_t used; +#endif } __attribute__ ((packed)); struct gprs_frag { diff --git a/src/host/gsmmap/.gitignore b/src/host/gsmmap/.gitignore deleted file mode 100644 index 661fd133..00000000 --- a/src/host/gsmmap/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -# autoreconf by-products -*.in - -aclocal.m4 -autom4te.cache/ -configure -depcomp -install-sh -missing - -# configure by-products -.deps/ -Makefile - -config.status -version.h - -# build by-products -*.o - -gsmmap - -# various -.version -.tarball-version - -# IDA file -*.id* -*.nam -*.til - -# Other test files -*.dump -*.bin -*.log diff --git a/src/host/gsmmap/Makefile.am b/src/host/gsmmap/Makefile.am deleted file mode 100644 index 29be15c5..00000000 --- a/src/host/gsmmap/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 - -# versioning magic -BUILT_SOURCES = $(top_srcdir)/.version -$(top_srcdir)/.version: - echo $(VERSION) > $@-t && mv $@-t $@ -dist-hook: - echo $(VERSION) > $(distdir)/.tarball-version - -INCLUDES = $(all_includes) -I../layer23/include -DHOST_BUILD -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) - -sbin_PROGRAMS = gsmmap - -gsmmap_SOURCES = gsmmap.c geo.c locate.c log.c ../layer23/src/common/sysinfo.c ../layer23/src/common/networks.c ../layer23/src/common/logging.c -gsmmap_LDADD = $(LIBOSMOGSM_LIBS) $(LIBOSMOCORE_LIBS) -lm - diff --git a/src/host/gsmmap/configure.ac b/src/host/gsmmap/configure.ac deleted file mode 100644 index 3a42d4c3..00000000 --- a/src/host/gsmmap/configure.ac +++ /dev/null @@ -1,26 +0,0 @@ -dnl Process this file with autoconf to produce a configure script -AC_INIT([gsmmap], - m4_esyscmd([./git-version-gen .tarball-version]), - [baseband-devel@lists.osmocom.org]) - -AM_INIT_AUTOMAKE([dist-bzip2]) - -dnl kernel style compile messages -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) - -dnl checks for programs -AC_PROG_MAKE_SET -AC_PROG_CC -AC_PROG_INSTALL - -dnl checks for libraries -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) -PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) - -dnl checks for header files -AC_HEADER_STDC - -dnl Checks for typedefs, structures and compiler characteristics - -AC_OUTPUT( - Makefile) diff --git a/src/host/gsmmap/geo.c b/src/host/gsmmap/geo.c deleted file mode 100644 index 65633d2c..00000000 --- a/src/host/gsmmap/geo.c +++ /dev/null @@ -1,47 +0,0 @@ -#include <math.h> -#include "geo.h" - -void geo2space(double *x, double *y, double *z, double lon, double lat) -{ - *z = sin(lat / 180.0 * PI) * POLE_RADIUS; - *x = sin(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS; - *y = -cos(lon / 180.0 * PI) * cos(lat / 180.0 * PI) * EQUATOR_RADIUS; -} - -void space2geo(double *lon, double *lat, double x, double y, double z) -{ - double r; - - /* bring geoid to 1m radius */ - z = z / POLE_RADIUS; - x = x / EQUATOR_RADIUS; - y = y / EQUATOR_RADIUS; - - /* normalize */ - r = sqrt(x * x + y * y + z * z); - z = z / r; - x = x / r; - y = y / r; - - *lat = asin(z) / PI * 180; - *lon = atan2(x, -y) / PI * 180; -} - -double distinspace(double x1, double y1, double z1, double x2, double y2, - double z2) -{ - double x = x1 - x2; - double y = y1 - y2; - double z = z1 - z2; - - return sqrt(x * x + y * y + z * z); -} - -double distonplane(double x1, double y1, double x2, double y2) -{ - double x = x1 - x2; - double y = y1 - y2; - - return sqrt(x * x + y * y); -} - diff --git a/src/host/gsmmap/git-version-gen b/src/host/gsmmap/git-version-gen deleted file mode 100755 index 652fac68..00000000 --- a/src/host/gsmmap/git-version-gen +++ /dev/null @@ -1,151 +0,0 @@ -#!/bin/sh -# Print a version string. -scriptversion=2010-01-28.01 - -# Copyright (C) 2007-2010 Free Software Foundation, Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 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 General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. -# It may be run two ways: -# - from a git repository in which the "git describe" command below -# produces useful output (thus requiring at least one signed tag) -# - from a non-git-repo directory containing a .tarball-version file, which -# presumes this script is invoked like "./git-version-gen .tarball-version". - -# In order to use intra-version strings in your project, you will need two -# separate generated version string files: -# -# .tarball-version - present only in a distribution tarball, and not in -# a checked-out repository. Created with contents that were learned at -# the last time autoconf was run, and used by git-version-gen. Must not -# be present in either $(srcdir) or $(builddir) for git-version-gen to -# give accurate answers during normal development with a checked out tree, -# but must be present in a tarball when there is no version control system. -# Therefore, it cannot be used in any dependencies. GNUmakefile has -# hooks to force a reconfigure at distribution time to get the value -# correct, without penalizing normal development with extra reconfigures. -# -# .version - present in a checked-out repository and in a distribution -# tarball. Usable in dependencies, particularly for files that don't -# want to depend on config.h but do want to track version changes. -# Delete this file prior to any autoconf run where you want to rebuild -# files to pick up a version string change; and leave it stale to -# minimize rebuild time after unrelated changes to configure sources. -# -# It is probably wise to add these two files to .gitignore, so that you -# don't accidentally commit either generated file. -# -# Use the following line in your configure.ac, so that $(VERSION) will -# automatically be up-to-date each time configure is run (and note that -# since configure.ac no longer includes a version string, Makefile rules -# should not depend on configure.ac for version updates). -# -# AC_INIT([GNU project], -# m4_esyscmd([build-aux/git-version-gen .tarball-version]), -# [bug-project@example]) -# -# Then use the following lines in your Makefile.am, so that .version -# will be present for dependencies, and so that .tarball-version will -# exist in distribution tarballs. -# -# BUILT_SOURCES = $(top_srcdir)/.version -# $(top_srcdir)/.version: -# echo $(VERSION) > $@-t && mv $@-t $@ -# dist-hook: -# echo $(VERSION) > $(distdir)/.tarball-version - -case $# in - 1) ;; - *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; -esac - -tarball_version_file=$1 -nl=' -' - -# First see if there is a tarball-only version file. -# then try "git describe", then default. -if test -f $tarball_version_file -then - v=`cat $tarball_version_file` || exit 1 - case $v in - *$nl*) v= ;; # reject multi-line output - [0-9]*) ;; - *) v= ;; - esac - test -z "$v" \ - && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 -fi - -if test -n "$v" -then - : # use $v -elif - v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \ - || git describe --abbrev=4 HEAD 2>/dev/null` \ - && case $v in - osmocon_[0-9]*) ;; - osmocon_v[0-9]*) ;; - *) (exit 1) ;; - esac -then - # Is this a new git that lists number of commits since the last - # tag or the previous older version that did not? - # Newer: v6.10-77-g0f8faeb - # Older: v6.10-g0f8faeb - case $v in - *-*-*) : git describe is okay three part flavor ;; - *-*) - : git describe is older two part flavor - # Recreate the number of commits and rewrite such that the - # result is the same as if we were using the newer version - # of git describe. - vtag=`echo "$v" | sed 's/-.*//'` - numcommits=`git rev-list "$vtag"..HEAD | wc -l` - v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; - ;; - esac - - # Change the first '-' to a '.', so version-comparing tools work properly. - # Remove the "g" in git describe's output string, to save a byte. - v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`; -else - v="UNKNOWN" -fi - -v=`echo "$v" |sed 's/^v//'` - -# Don't declare a version "dirty" merely because a time stamp has changed. -git status > /dev/null 2>&1 - -dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= -case "$dirty" in - '') ;; - *) # Append the suffix only if there isn't one already. - case $v in - *-dirty) ;; - *) v="$v-dirty" ;; - esac ;; -esac - -# Omit the trailing newline, so that m4_esyscmd can use the result directly. -echo "$v" | tr -d '\012' - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" -# End: diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore index 8fb93f73..333c28c5 100644 --- a/src/host/layer23/.gitignore +++ b/src/host/layer23/.gitignore @@ -33,4 +33,6 @@ src/misc/echo_test src/misc/cbch_sniff src/misc/ccch_scan src/misc/layer23 +src/misc/gsmmap src/mobile/mobile +src/modem/modem diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac index 3e696103..7fb8bf16 100644 --- a/src/host/layer23/configure.ac +++ b/src/host/layer23/configure.ac @@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script AC_INIT([layer23], [0.0.0]) AM_INIT_AUTOMAKE +CFLAGS="$CFLAGS -std=gnu11" + dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) @@ -32,6 +34,7 @@ AC_ARG_ENABLE(werror, if test x"$werror" = x"yes" then WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition" WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" CFLAGS="$CFLAGS $WERROR_FLAGS" @@ -39,36 +42,72 @@ then fi dnl checks for libraries -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) +PKG_CHECK_MODULES(LIBOSMOISDN, libosmoisdn) PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec) +PKG_CHECK_MODULES(LIBOSMOGPRSRLCMAC, libosmo-gprs-rlcmac) +PKG_CHECK_MODULES(LIBOSMOGPRSLLC, libosmo-gprs-llc) +PKG_CHECK_MODULES(LIBOSMOGPRSSNDCP, libosmo-gprs-sndcp) +PKG_CHECK_MODULES(LIBOSMOGPRSGMM, libosmo-gprs-gmm) +PKG_CHECK_MODULES(LIBOSMOGPRSSM, libosmo-gprs-sm) AC_CHECK_LIB(gps, gps_waiting, LIBGPS_CFLAGS=" -D_HAVE_GPSD" LIBGPS_LIBS=" -lgps ",,) AC_SUBST([LIBGPS_CFLAGS]) AC_SUBST([LIBGPS_LIBS]) dnl optional dependencies -PKG_CHECK_MODULES(LIBLUA, lua53, [ - WITH_LUA=1], [ - WITH_LUA=0]) -AC_SUBST([WITH_LUA]) -AM_CONDITIONAL([BUILD_LUA], test "x$WITH_LUA" = "x1") +AC_ARG_WITH([lua53], [ + AS_HELP_STRING([--with-lua53], + [Enable LUA scripting support @<:@default=check@:>@]) +]) + +AC_ARG_WITH([gapk_io], [ + AS_HELP_STRING([--with-gapk-io], + [Enable GAPK I/O support @<:@default=check@:>@]) +]) + +found_lua53=no +AS_IF([test "x$with_lua53" != "xno"], [ + PKG_CHECK_MODULES(LIBLUA, lua53, [found_lua53=yes], [found_lua53=no]) + AS_IF([test "x$with_lua53" = "xyes" -a "x$found_lua53" = "xno"], [ + AC_MSG_ERROR([lua53 support requested but pkg-config is unable to find it]) + ]) +]) +AM_CONDITIONAL([BUILD_LUA], test "x$found_lua53" = "xyes") + +found_gapk=no +AS_IF([test "x$with_gapk_io" != "xno"], [ + PKG_CHECK_MODULES(LIBOSMOGAPK, libosmogapk, [found_gapk=yes], [found_gapk=no]) + AS_IF([test "x$with_gapk_io" = "xyes" -a "x$found_gapk" = "xno"], [ + AC_MSG_ERROR([GAPK I/O support requested but pkg-config is unable to find it]) + ]) +]) +AM_CONDITIONAL([BUILD_GAPK], test "x$found_gapk" = "xyes") dnl checks for header files AC_HEADER_STDC dnl Checks for typedefs, structures and compiler characteristics +AC_MSG_RESULT([CFLAGS="$CFLAGS"]) +AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) + +dnl Generate the output +AM_CONFIG_HEADER(config.h) + AC_OUTPUT( src/Makefile src/common/Makefile src/misc/Makefile src/mobile/Makefile + src/modem/Makefile include/Makefile include/osmocom/Makefile include/osmocom/bb/Makefile include/osmocom/bb/common/Makefile include/osmocom/bb/misc/Makefile include/osmocom/bb/mobile/Makefile + include/osmocom/bb/modem/Makefile Makefile) diff --git a/src/host/layer23/include/osmocom/bb/Makefile.am b/src/host/layer23/include/osmocom/bb/Makefile.am index 58a5f7fb..3b6a4d8b 100644 --- a/src/host/layer23/include/osmocom/bb/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/Makefile.am @@ -1 +1 @@ -SUBDIRS = common misc mobile +SUBDIRS = common misc mobile modem diff --git a/src/host/layer23/include/osmocom/bb/common/Makefile.am b/src/host/layer23/include/osmocom/bb/common/Makefile.am index f9364cd9..7c0fa972 100644 --- a/src/host/layer23/include/osmocom/bb/common/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/common/Makefile.am @@ -1,3 +1,22 @@ -noinst_HEADERS = l1ctl.h l1l2_interface.h l23_app.h logging.h \ - networks.h gps.h sysinfo.h osmocom_data.h utils.h \ - sap_proto.h sap_fsm.h sap_interface.h sim.h +noinst_HEADERS = \ + apn.h \ + apn_fsm.h \ + l1ctl.h \ + l1l2_interface.h \ + l23_app.h \ + logging.h \ + ms.h \ + networks.h \ + gps.h \ + sysinfo.h \ + osmocom_data.h \ + utils.h \ + sap_proto.h \ + sap_fsm.h \ + sap_interface.h \ + settings.h \ + sim.h \ + subscriber.h \ + support.h \ + vty.h \ + $(NULL) diff --git a/src/host/layer23/include/osmocom/bb/common/apn.h b/src/host/layer23/include/osmocom/bb/common/apn.h new file mode 100644 index 00000000..0adb8de7 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/apn.h @@ -0,0 +1,82 @@ +/* APN Context + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#pragma once + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/select.h> +#include <osmocom/core/tun.h> + +#include <osmocom/gprs/sm/sm.h> + +#include <osmocom/bb/common/apn_fsm.h> + +struct osmocom_ms; + +#define APN_TYPE_IPv4 0x01 /* v4-only */ +#define APN_TYPE_IPv6 0x02 /* v6-only */ +#define APN_TYPE_IPv4v6 0x04 /* v4v6 dual-stack */ + +struct osmobb_pdp_ctx { + uint8_t nsapi; + uint8_t llc_sapi; + uint8_t qos[OSMO_GPRS_SM_QOS_MAXLEN]; + uint8_t qos_len; + uint8_t pco[OSMO_GPRS_SM_PCO_MAXLEN]; + uint8_t pco_len; + enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type; + struct osmo_sockaddr pdp_addr_v4; + struct osmo_sockaddr pdp_addr_v6; +}; + +struct osmobb_apn { + /* list of APNs inside MS */ + struct llist_head list; + /* back-pointer to MS */ + struct osmocom_ms *ms; + + bool started; + + struct { + /* Primary name */ + char *name; + /* name of the network device */ + char *dev_name; + /* netns name of the network device, NULL = default netns */ + char *dev_netns_name; + /* types supported address types on this APN */ + uint32_t apn_type_mask; + /* administratively shutdown (true) or not (false) */ + bool shutdown; + /* transmit G-PDU sequence numbers (true) or not (false) */ + bool tx_gpdu_seq; + } cfg; + struct osmo_tundev *tun; + struct apn_fsm_ctx fsm; + struct osmobb_pdp_ctx pdp; +}; + +struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name); +void apn_free(struct osmobb_apn *apn); +int apn_start(struct osmobb_apn *apn); +int apn_stop(struct osmobb_apn *apn); + +#define LOGPAPN(level, apn, fmt, args...) \ + LOGP(DTUN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args) + +#define LOGTUN(level, tun, fmt, args...) \ + LOGP(DTUN, level, "TUN(%s): " fmt, osmo_tundev_get_name(tun), ## args) diff --git a/src/host/layer23/include/osmocom/bb/common/apn_fsm.h b/src/host/layer23/include/osmocom/bb/common/apn_fsm.h new file mode 100644 index 00000000..890267c9 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/apn_fsm.h @@ -0,0 +1,29 @@ +#pragma once + +#include <osmocom/core/fsm.h> + +struct osmobb_apn; + +enum apn_fsm_states { + APN_ST_DISABLED, + APN_ST_INACTIVE, + APN_ST_ACTIVATING, + APN_ST_ACTIVE, +}; + +enum apn_fsm_events { + APN_EV_GPRS_ALLOWED, /* data: bool *allowed */ + APN_EV_GMM_ATTACHED, + APN_EV_GMM_DETACHED, + APN_EV_RX_SM_ACT_PDP_CTX_REJ, /* data: enum gsm48_gsm_cause *cause */ + APN_EV_RX_SM_ACT_PDP_CTX_ACC, + APN_EV_RX_SM_DEACT_PDP_CTX_ACC, +}; + +struct apn_fsm_ctx { + struct osmo_fsm_inst *fi; + struct osmobb_apn *apn; +}; + +int apn_fsm_ctx_init(struct apn_fsm_ctx *ctx, struct osmobb_apn *apn); +void apn_fsm_ctx_release(struct apn_fsm_ctx *ctx); diff --git a/src/host/layer23/include/osmocom/bb/common/gps.h b/src/host/layer23/include/osmocom/bb/common/gps.h index 58c0c536..e7ce915c 100644 --- a/src/host/layer23/include/osmocom/bb/common/gps.h +++ b/src/host/layer23/include/osmocom/bb/common/gps.h @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ enum { diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h index 02ffa343..a74c9c37 100644 --- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h +++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h @@ -2,6 +2,7 @@ #define osmocom_l1ctl_h #include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> #include <osmocom/bb/common/osmocom_data.h> struct osmocom_ms; @@ -20,15 +21,15 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t algo, uint8_t *key, uint8_t len); /* Transmit L1CTL_RACH_REQ */ -int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, - uint8_t combined); +int l1ctl_tx_rach_req(struct osmocom_ms *ms, + uint8_t chan_nr, uint8_t link_id, + uint8_t ra, uint16_t offset, uint8_t combined, uint8_t uic); /* Transmit L1CTL_DM_EST_REQ */ -int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, - uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode); -int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, - uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc, - uint8_t tch_mode, uint8_t audio_mode); +int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, uint8_t chan_nr, uint8_t tsc, + uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags); +int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, uint16_t *ma, uint8_t ma_len, + uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags); /* Transmit L1CTL_DM_FREQ_REQ */ int l1ctl_tx_dm_freq_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, @@ -48,8 +49,8 @@ int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn, int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode); /* Transmit TCH_MODE_REQ */ -int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, - uint8_t audio_mode, uint8_t tch_loop_mode); +int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags, + uint8_t tch_loop_mode); /* Transmit ECHO_REQ */ int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len); @@ -73,4 +74,17 @@ int l1ctl_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx); /* Transmit L1CTL_NEIGH_PM_REQ */ int l1ctl_tx_neigh_pm_req(struct osmocom_ms *ms, int num, uint16_t *arfcn); +/* Transmit L1CTL_GPRS_UL_BLOCK_REQ */ +int l1ctl_tx_gprs_ul_block_req(struct osmocom_ms *ms, uint32_t fn, uint8_t tn, + const uint8_t *data, size_t data_len); + +/* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */ +int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn); + +/* Transmit L1CTL_GPRS_DL_TBF_CFG_REQ */ +int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn, + uint8_t dl_tfi); + #endif diff --git a/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h b/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h index 41403d87..9cb993c8 100644 --- a/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h +++ b/src/host/layer23/include/osmocom/bb/common/l1l2_interface.h @@ -1,6 +1,10 @@ #ifndef _L1L2_INTERFACE_H #define _L1L2_INTERFACE_H +#include <osmocom/core/msgb.h> + +#define L2_DEFAULT_SOCKET_PATH "/tmp/osmocom_l2" + int layer2_open(struct osmocom_ms *ms, const char *socket_path); int layer2_close(struct osmocom_ms *ms); int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg); diff --git a/src/host/layer23/include/osmocom/bb/common/l23_app.h b/src/host/layer23/include/osmocom/bb/common/l23_app.h index 0b9994c3..6cf0b7e9 100644 --- a/src/host/layer23/include/osmocom/bb/common/l23_app.h +++ b/src/host/layer23/include/osmocom/bb/common/l23_app.h @@ -1,36 +1,77 @@ #ifndef _L23_APP_H #define _L23_APP_H +#include <osmocom/core/tun.h> +#include <osmocom/core/gsmtap.h> + struct option; +struct vty_app_info; /* Options supported by the l23 app */ enum { - L23_OPT_SAP = 1, - L23_OPT_ARFCN = 2, - L23_OPT_TAP = 4, - L23_OPT_VTY = 8, - L23_OPT_DBG = 16, - L23_OPT_VTYIP = 32, + L23_OPT_SAP = 1 << 0, + L23_OPT_ARFCN = 1 << 1, + L23_OPT_TAP = 1 << 2, + L23_OPT_VTY = 1 << 3, + L23_OPT_DBG = 1 << 4, }; -/* initialization, called once when starting the app, before entering - * select loop */ -extern int l23_app_init(struct osmocom_ms *ms); -extern int (*l23_app_work) (struct osmocom_ms *ms); -extern int (*l23_app_exit) (struct osmocom_ms *ms); +/* see (struct l23_global_config)->gsmtap.categ_gprs_mask */ +enum l23_gsmtap_gprs_category { + L23_GSMTAP_GPRS_C_DL_UNKNOWN = 0, /* unknown or undecodable downlink blocks */ + L23_GSMTAP_GPRS_C_DL_DUMMY = 1, /* downlink dummy blocks */ + L23_GSMTAP_GPRS_C_DL_CTRL = 2, /* downlink control blocks */ + L23_GSMTAP_GPRS_C_DL_DATA_GPRS = 3, /* downlink GPRS data blocks */ + L23_GSMTAP_GPRS_C_DL_DATA_EGPRS = 4, /* downlink EGPRS data blocks */ + + L23_GSMTAP_GPRS_C_UL_UNKNOWN = 5, /* unknown or undecodable uplink blocks */ + L23_GSMTAP_GPRS_C_UL_DUMMY = 6, /* uplink dummy blocks */ + L23_GSMTAP_GPRS_C_UL_CTRL = 7, /* uplink control blocks */ + L23_GSMTAP_GPRS_C_UL_DATA_GPRS = 8, /* uplink GPRS data blocks */ + L23_GSMTAP_GPRS_C_UL_DATA_EGPRS = 9, /* uplink EGPRS data blocks */ +}; + +struct l23_global_config { + struct { + char *remote_host; + char *local_host; + uint32_t lchan_mask; /* see l23_gsmtap_gprs_category */ + uint32_t lchan_acch_mask; /* see l23_gsmtap_gprs_category */ + bool lchan_acch; + uint32_t categ_gprs_mask; + struct gsmtap_inst *inst; + } gsmtap; +}; +extern struct l23_global_config l23_cfg; + +extern void *l23_ctx; + +/* initialization, called once when starting the app, before reading VTY config */ +extern int l23_app_init(void); + +/* Start work after reading VTY config and starting layer23 components, + * immediately before entering main select loop */ +extern int (*l23_app_start)(void); + +extern int (*l23_app_work)(void); +extern int (*l23_app_exit)(void); /* configuration options */ struct l23_app_info { const char *copyright; const char *contribution; + struct vty_app_info *vty_info; /* L23_OPT_VTY */ char *getopt_string; - int (*cfg_supported)(); + uint32_t opt_supported; /* mask of L23_OPT_* */ int (*cfg_print_help)(); int (*cfg_getopt_opt)(struct option **options); int (*cfg_handle_opt)(int c,const char *optarg); + int (*vty_init)(void); + osmo_tundev_data_ind_cb_t tun_data_ind_cb; }; -extern struct l23_app_info *l23_app_info(); +/* all l23 apps must define this structure */ +extern const struct l23_app_info l23_app_info; #endif /* _L23_APP_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/logging.h b/src/host/layer23/include/osmocom/bb/common/logging.h index bf6e6aa0..3920d2e0 100644 --- a/src/host/layer23/include/osmocom/bb/common/logging.h +++ b/src/host/layer23/include/osmocom/bb/common/logging.h @@ -12,6 +12,8 @@ enum { DNB, DMM, DCC, + DGCC, + DBCC, DSS, DSMS, DMNCC, @@ -25,6 +27,14 @@ enum { DMOB, DPRIM, DLUA, + DGAPK, + DCSD, + DTUN, + DRLCMAC, + DLLC, + DSNDCP, + DGMM, + DSM }; extern const struct log_info log_info; diff --git a/src/host/layer23/include/osmocom/bb/common/ms.h b/src/host/layer23/include/osmocom/bb/common/ms.h new file mode 100644 index 00000000..36d3e3ba --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/ms.h @@ -0,0 +1,116 @@ +/* Mobile Station */ +#pragma once + +#include <osmocom/core/select.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/core/write_queue.h> + +#include <osmocom/gsm/lapdm.h> +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/subscriber.h> +#include <osmocom/bb/common/support.h> +#include <osmocom/bb/common/sap_interface.h> +#include <osmocom/bb/common/sap_proto.h> +#include <osmocom/bb/mobile/gsm48_rr.h> +#include <osmocom/bb/common/sysinfo.h> +#include <osmocom/bb/mobile/gsm322.h> +#include <osmocom/bb/mobile/gsm48_mm.h> +#include <osmocom/bb/mobile/gsm48_cc.h> +#include <osmocom/bb/mobile/mncc_sock.h> +#include <osmocom/bb/common/sim.h> +#include <osmocom/bb/common/l1ctl.h> + +struct osmobb_ms_gmm_layer { + uint8_t ac_ref_nr; + uint8_t key_seq; + uint8_t rand[16]; + uint32_t tlli; +}; + +struct osmosap_entity { + struct osmo_fsm_inst *fi; + uint16_t max_msg_size; + + /* Current state of remote SIM card */ + enum sap_card_status_type card_status; + + /* Optional SAP message call-back */ + sap_msg_cb_t sap_msg_cb; + /* Optional response call-back */ + sap_rsp_cb_t sap_rsp_cb; +}; + +struct osmol1_entity { + int (*l1_traffic_ind)(struct osmocom_ms *ms, struct msgb *msg); + int (*l1_gprs_ul_block_cnf)(struct osmocom_ms *ms, struct msgb *msg); + int (*l1_gprs_dl_block_ind)(struct osmocom_ms *ms, struct msgb *msg); + int (*l1_gprs_rts_ind)(struct osmocom_ms *ms, struct msgb *msg); +}; + +struct osmomncc_entity { + int (*mncc_recv)(struct osmocom_ms *ms, int msg_type, void *arg); + struct mncc_sock_state *sock_state; + uint32_t ref; +}; + +/* RX measurement statistics */ +struct rx_meas_stat { + uint32_t last_fn; + + /* cumulated values of current cell from SACCH dl */ + uint32_t frames; + uint32_t snr; + uint32_t berr; + uint32_t rxlev; + + /* counters loss criterion */ + int16_t dsc, ds_fail; + int16_t s, rl_fail; +}; + +enum osmobb_ms_shutdown_st { + MS_SHUTDOWN_NONE = 0, + MS_SHUTDOWN_IMSI_DETACH = 1, + MS_SHUTDOWN_WAIT_RESET = 2, + MS_SHUTDOWN_COMPL = 3, +}; + +struct osmocom_ms { + struct llist_head entity; + char *name; + struct osmo_wqueue l2_wq, sap_wq; + uint16_t test_arfcn; + struct osmol1_entity l1_entity; + + bool started, deleting; + enum osmobb_ms_shutdown_st shutdown; + struct gsm_support support; + struct gsm_settings settings; + struct gsm_subscriber subscr; + struct gsm_sim sim; + struct lapdm_channel lapdm_channel; + struct osmosap_entity sap_entity; + struct rx_meas_stat meas; + struct gsm48_rrlayer rrlayer; + struct gsm322_plmn plmn; + struct gsm322_cellsel cellsel; + struct gsm48_mmlayer mmlayer; + struct gsm48_cclayer cclayer; + struct osmomncc_entity mncc_entity; + struct llist_head trans_list; + + /* GPRS */ + struct gprs_settings gprs; + struct osmobb_ms_gmm_layer gmmlayer; + struct osmo_fsm_inst *grr_fi; + + struct tch_state *tch_state; + + void *lua_state; + int lua_cb_ref; + char *lua_script; +}; + +struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name); + +extern uint16_t cfg_test_arfcn; diff --git a/src/host/layer23/include/osmocom/bb/common/networks.h b/src/host/layer23/include/osmocom/bb/common/networks.h index d34f3162..408e17d0 100644 --- a/src/host/layer23/include/osmocom/bb/common/networks.h +++ b/src/host/layer23/include/osmocom/bb/common/networks.h @@ -1,24 +1,27 @@ #ifndef _NETWORKS_H #define _NETWORKS_H +#include <osmocom/gsm/gsm23003.h> -#define GSM_INPUT_INVALID 0xffff - +/* Instead of handling numerical MCC and MNC, they stored and handled + * hexadecimal. This makes it possible + * to correctly handle 2 and 3 digits MNC. Internally 2 digit MNCs are stored + * as 0xXXf, and 3 digits MNC are stored as 0xXXX, where X is the digit 0..9. +*/ struct gsm_networks { - uint16_t mcc; - int16_t mnc; + uint16_t mcc_hex; + int16_t mnc_hex; const char *name; }; int gsm_match_mcc(uint16_t mcc, char *imsi); -int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi); -const char *gsm_print_mcc(uint16_t mcc); -const char *gsm_print_mnc(uint16_t mcc); +int gsm_match_mnc(uint16_t mcc, uint16_t mnc, bool mnc_3_digits, char *imsi); const char *gsm_get_mcc(uint16_t mcc); -const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc); +const char *gsm_get_mnc(const struct osmo_plmn_id *plmn); const char *gsm_imsi_mcc(char *imsi); const char *gsm_imsi_mnc(char *imsi); -const uint16_t gsm_input_mcc(char *string); -const uint16_t gsm_input_mnc(char *string); + +uint16_t gsm_mcc_to_hex(uint16_t mcc); +uint16_t gsm_mnc_to_hex(uint16_t mnc, bool mnc_3_digits); #endif /* _NETWORKS_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h index 14e594cb..935c02b2 100644 --- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h +++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h @@ -1,107 +1,17 @@ -#ifndef osmocom_data_h -#define osmocom_data_h +#pragma once -#include <osmocom/core/select.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/core/write_queue.h> +#include <stdint.h> +#include <stdbool.h> struct osmocom_ms; - - /* FIXME no 'mobile' specific stuff should be here */ -#include <osmocom/bb/mobile/support.h> -#include <osmocom/bb/mobile/settings.h> -#include <osmocom/bb/mobile/subscriber.h> -#include <osmocom/gsm/lapdm.h> -#include <osmocom/bb/common/sap_interface.h> -#include <osmocom/bb/common/sap_proto.h> -#include <osmocom/bb/mobile/gsm48_rr.h> -#include <osmocom/bb/common/sysinfo.h> -#include <osmocom/bb/mobile/gsm322.h> -#include <osmocom/bb/mobile/gsm48_mm.h> -#include <osmocom/bb/mobile/gsm48_cc.h> -#include <osmocom/bb/mobile/mncc_sock.h> -#include <osmocom/bb/common/sim.h> -#include <osmocom/bb/common/l1ctl.h> - -struct osmosap_entity { - struct osmo_fsm_inst *fi; - uint16_t max_msg_size; - - /* Current state of remote SIM card */ - enum sap_card_status_type card_status; - - /* Optional SAP message call-back */ - sap_msg_cb_t sap_msg_cb; - /* Optional response call-back */ - sap_rsp_cb_t sap_rsp_cb; -}; - -struct osmol1_entity { - int (*l1_traffic_ind)(struct osmocom_ms *ms, struct msgb *msg); -}; - -struct osmomncc_entity { - int (*mncc_recv)(struct osmocom_ms *ms, int msg_type, void *arg); - struct mncc_sock_state *sock_state; - uint32_t ref; -}; - - -/* RX measurement statistics */ -struct rx_meas_stat { - uint32_t last_fn; - - /* cumulated values of current cell from SACCH dl */ - uint32_t frames; - uint32_t snr; - uint32_t berr; - uint32_t rxlev; - - /* counters loss criterion */ - int16_t dsc, ds_fail; - int16_t s, rl_fail; -}; - -enum { - MS_SHUTDOWN_NONE = 0, - MS_SHUTDOWN_IMSI_DETACH = 1, - MS_SHUTDOWN_WAIT_RESET = 2, - MS_SHUTDOWN_COMPL = 3, -}; - -/* One Mobilestation for osmocom */ -struct osmocom_ms { - struct llist_head entity; - char *name; - struct osmo_wqueue l2_wq, sap_wq; - uint16_t test_arfcn; - struct osmol1_entity l1_entity; - - bool started, deleting; - uint8_t shutdown; - struct gsm_support support; - struct gsm_settings settings; - struct gsm_subscriber subscr; - struct gsm_sim sim; - struct lapdm_channel lapdm_channel; - struct osmosap_entity sap_entity; - struct rx_meas_stat meas; - struct gsm48_rrlayer rrlayer; - struct gsm322_plmn plmn; - struct gsm322_cellsel cellsel; - struct gsm48_mmlayer mmlayer; - struct gsm48_cclayer cclayer; - struct osmomncc_entity mncc_entity; - struct llist_head trans_list; - - void *lua_state; - int lua_cb_ref; - char *lua_script; -}; +struct vty; enum osmobb_sig_subsys { SS_L1CTL, SS_GLOBAL, + SS_L23_VTY, + SS_L23_SUBSCR, + SS_L23_TRANS, }; enum osmobb_l1ctl_sig { @@ -120,6 +30,43 @@ enum osmobb_global_sig { S_GLOBAL_SHUTDOWN, }; +enum osmobb_l23_vty_sig { + S_L23_VTY_MS_START, + S_L23_VTY_MS_STOP, +}; + +enum osmobb_l23_subscriber { + S_L23_SUBSCR_SIM_ATTACHED, + S_L23_SUBSCR_SIM_DETACHED, + S_L23_SUBSCR_SIM_AUTH_RESP, +}; + +enum osmobb_l23_trans_sig { + S_L23_CC_TRANS_ALLOC, /* new transaction has been allocated */ + S_L23_CC_TRANS_FREE, /* transaction is about to be free()d */ + S_L23_CC_TRANS_STATE_CHG, /* transaction state has been changed */ +}; + +struct osmobb_l23_vty_sig_data { + struct vty *vty; + union { + struct { + struct osmocom_ms *ms; + int rc; /* CMD_SUCCESS/CMD_WARNING */ + } ms_start; + struct { + struct osmocom_ms *ms; + bool force; + int rc; /* CMD_SUCCESS/CMD_WARNING */ + } ms_stop; + }; +}; + +struct osmobb_l23_subscr_sim_auth_resp_sig_data { + struct osmocom_ms *ms; + uint8_t sres[4]; +}; + struct osmobb_fbsb_res { struct osmocom_ms *ms; int8_t snr; @@ -142,6 +89,7 @@ struct osmobb_tch_mode_conf { struct osmocom_ms *ms; uint8_t tch_mode; uint8_t audio_mode; + uint8_t tch_flags; }; struct osmobb_neigh_pm_ind { @@ -149,5 +97,3 @@ struct osmobb_neigh_pm_ind { uint16_t band_arfcn; uint8_t rx_lev; }; - -#endif diff --git a/src/host/layer23/include/osmocom/bb/common/settings.h b/src/host/layer23/include/osmocom/bb/common/settings.h new file mode 100644 index 00000000..1d3e53df --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/settings.h @@ -0,0 +1,348 @@ +#ifndef _settings_h +#define _settings_h + +#include <stdbool.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm23003.h> +#include <osmocom/gsm/gsm48.h> + +#include <osmocom/bb/common/sim.h> + +struct osmocom_ms; +struct osmobb_apn; + +#define MOB_C7_DEFLT_ANY_TIMEOUT 30 + +/* CC (Call Control) message handling entity */ +enum mncc_handler_t { + /* Built-in mobile's MNCC */ + MNCC_HANDLER_INTERNAL, + /* External MNCC application via UNIX-socket */ + MNCC_HANDLER_EXTERNAL, + /* No call support */ + MNCC_HANDLER_DUMMY, +}; + +/* TCH I/O handler for voice calls */ +enum tch_voice_io_handler { + /* No handler, drop frames */ + TCH_VOICE_IOH_NONE = 0, + /* libosmo-gapk based handler */ + TCH_VOICE_IOH_GAPK, + /* L1 PHY (e.g. Calypso DSP) */ + TCH_VOICE_IOH_L1PHY, + /* External MNCC app (via MNCC socket) */ + TCH_VOICE_IOH_MNCC_SOCK, + /* Return to sender */ + TCH_VOICE_IOH_LOOPBACK, +}; + +extern const struct value_string tch_voice_io_handler_names[]; +static inline const char *tch_voice_io_handler_name(enum tch_voice_io_handler val) +{ return get_value_string(tch_voice_io_handler_names, val); } + +/* TCH I/O handler for data calls */ +enum tch_data_io_handler { + /* No handler, drop frames */ + TCH_DATA_IOH_NONE = 0, + /* UNIX socket */ + TCH_DATA_IOH_UNIX_SOCK, + /* Return to sender */ + TCH_DATA_IOH_LOOPBACK, +}; + +extern const struct value_string tch_data_io_handler_names[]; +static inline const char *tch_data_io_handler_name(enum tch_data_io_handler val) +{ return get_value_string(tch_data_io_handler_names, val); } + +/* TCH I/O format for voice calls */ +enum tch_voice_io_format { + /* RFC3551 for FR/EFR, RFC5993 for HR, RFC4867 for AMR */ + TCH_VOICE_IOF_RTP, + /* Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx) */ + TCH_VOICE_IOF_TI, +}; + +extern const struct value_string tch_voice_io_format_names[]; +static inline const char *tch_voice_io_format_name(enum tch_voice_io_format val) +{ return get_value_string(tch_voice_io_format_names, val); } + +/* TCH I/O format for data calls */ +enum tch_data_io_format { + /* Osmocom format, used by trxcon and virtphy */ + TCH_DATA_IOF_OSMO, + /* Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx) */ + TCH_DATA_IOF_TI, +}; + +extern const struct value_string tch_data_io_format_names[]; +static inline const char *tch_data_io_format_name(enum tch_data_io_format val) +{ return get_value_string(tch_data_io_format_names, val); } + +struct tch_voice_settings { + enum tch_voice_io_handler io_handler; + enum tch_voice_io_format io_format; + char alsa_output_dev[128]; + char alsa_input_dev[128]; +}; + +struct tch_data_settings { + enum tch_data_io_handler io_handler; + enum tch_data_io_format io_format; + char unix_socket_path[128]; +}; + +struct test_sim_settings { + char imsi[OSMO_IMSI_BUF_SIZE]; + uint32_t tmsi; + uint8_t ki_type; + uint8_t ki[16]; /* 128 bit max */ + bool barr; + bool rplmn_valid; + struct osmo_plmn_id rplmn; + uint16_t lac; + bool imsi_attached; + bool always_search_hplmn; + struct { + bool valid; + uint32_t ptmsi; /* invalid tmsi: GSM_RESERVED_TMSI */ + uint32_t ptmsi_sig; /* P-TMSI signature, 3 bytes */ + struct gprs_ra_id rai; + enum gsm1111_ef_locigprs_rau_status gu_state; /* GU, TS 24.008 */ + bool imsi_attached; + } locigprs; +}; + +/* Data (CSD) call type and rate, values like in the '<speed>' part of 'AT+CBST'. + * See 3GPP TS 27.007, section 6.7 "Select bearer service type +CBST". */ +enum data_call_type_rate { + DATA_CALL_TR_AUTO = 0, + DATA_CALL_TR_V21_300 = 1, + DATA_CALL_TR_V22_1200 = 2, + DATA_CALL_TR_V23_1200_75 = 3, + DATA_CALL_TR_V22bis_2400 = 4, + DATA_CALL_TR_V26ter_2400 = 5, + DATA_CALL_TR_V32_4800 = 6, + DATA_CALL_TR_V32_9600 = 7, + DATA_CALL_TR_V34_9600 = 12, + DATA_CALL_TR_V34_14400 = 14, + DATA_CALL_TR_V34_19200 = 15, + DATA_CALL_TR_V34_28800 = 16, + DATA_CALL_TR_V34_33600 = 17, + DATA_CALL_TR_V120_1200 = 34, + DATA_CALL_TR_V120_2400 = 36, + DATA_CALL_TR_V120_4800 = 38, + DATA_CALL_TR_V120_9600 = 39, + DATA_CALL_TR_V120_14400 = 43, + DATA_CALL_TR_V120_19200 = 47, + DATA_CALL_TR_V120_28800 = 48, + DATA_CALL_TR_V120_38400 = 49, + DATA_CALL_TR_V120_48000 = 50, + DATA_CALL_TR_V120_56000 = 51, + DATA_CALL_TR_V110_300 = 65, + DATA_CALL_TR_V110_1200 = 66, + DATA_CALL_TR_V110_2400 = 68, + DATA_CALL_TR_V110_4800 = 70, + DATA_CALL_TR_V110_9600 = 71, + DATA_CALL_TR_V110_14400 = 75, + DATA_CALL_TR_V110_19200 = 79, + DATA_CALL_TR_V110_28800 = 80, + DATA_CALL_TR_V110_38400 = 81, + DATA_CALL_TR_V110_48000 = 82, + DATA_CALL_TR_V110_56000 = 83, + DATA_CALL_TR_V110_64000 = 84, + DATA_CALL_TR_BTR_56000 = 115, + DATA_CALL_TR_BTR_64000 = 116, + DATA_CALL_TR_PIAFS32k_32000 = 120, + DATA_CALL_TR_PIAFS64k_64000 = 121, + DATA_CALL_TR_MMEDIA_28800 = 130, + DATA_CALL_TR_MMEDIA_32000 = 131, + DATA_CALL_TR_MMEDIA_33600 = 132, + DATA_CALL_TR_MMEDIA_56000 = 133, + DATA_CALL_TR_MMEDIA_64000 = 134, +}; + +/* Data (CSD) call parameters */ +struct data_call_params { + enum data_call_type_rate type_rate; + enum gsm48_bcap_transp transp; + + /* async call parameters */ + bool is_async; + unsigned int nr_stop_bits; + unsigned int nr_data_bits; + enum gsm48_bcap_parity parity; +}; + +struct gsm_settings { + char layer2_socket_path[128]; + char sap_socket_path[128]; + char mncc_socket_path[128]; + + /* MNCC handler */ + enum mncc_handler_t mncc_handler; + + /* TCH settings */ + struct tch_voice_settings tch_voice; + struct tch_data_settings tch_data; + + /* IMEI */ + char imei[GSM23003_IMEI_NUM_DIGITS + 1]; + char imeisv[GSM23003_IMEISV_NUM_DIGITS + 1]; + char imei_random; + + /* network search */ + int plmn_mode; /* PLMN_MODE_* */ + + /* SIM */ + int sim_type; /* enum gsm_subscriber_sim_type, + * selects card on power on */ + char emergency_imsi[OSMO_IMSI_BUF_SIZE]; + + /* SMS */ + char sms_sca[22]; + bool store_sms; + + /* test card simulator settings */ + struct test_sim_settings test_sim; + + /* call related settings */ + uint8_t cw; /* set if call-waiting is allowed */ + uint8_t auto_answer; + uint8_t clip, clir; + uint8_t half, half_prefer; + + /* changing default behavior */ + uint8_t alter_tx_power; + uint8_t alter_tx_power_value; + int8_t alter_delay; + uint8_t stick; + uint16_t stick_arfcn; + uint8_t skip_max_per_band; + uint8_t no_lupd; + uint8_t no_neighbour; + + /* supported by configuration */ + uint8_t cc_dtmf; + uint8_t sms_ptp; + uint8_t a5_1; + uint8_t a5_2; + uint8_t a5_3; + uint8_t a5_4; + uint8_t a5_5; + uint8_t a5_6; + uint8_t a5_7; + uint8_t p_gsm; + uint8_t e_gsm; + uint8_t r_gsm; + uint8_t dcs; + uint8_t gsm_850; + uint8_t pcs; + uint8_t gsm_480; + uint8_t gsm_450; + uint8_t class_900; + uint8_t class_dcs; + uint8_t class_850; + uint8_t class_pcs; + uint8_t class_400; + uint8_t freq_map[128+38]; + uint8_t full_v1; + uint8_t full_v2; + uint8_t full_v3; + uint8_t half_v1; + uint8_t half_v3; + uint8_t ch_cap; /* channel capability */ + int8_t min_rxlev_dbm; /* min dBm to access */ + + /* CSD modes */ + bool csd_tch_f144; + bool csd_tch_f96; + bool csd_tch_f48; + bool csd_tch_h48; + bool csd_tch_f24; + bool csd_tch_h24; + + /* support for ASCI */ + bool vgcs; /* support of VGCS */ + bool vbs; /* support of VBS */ + + /* radio */ + uint16_t dsc_max; + uint8_t force_rekey; + + /* dialing */ + struct llist_head abbrev; + + /* EDGE / UMTS / CDMA */ + uint8_t edge_ms_sup; + uint8_t edge_psk_sup; + uint8_t edge_psk_uplink; + uint8_t class_900_edge; + uint8_t class_dcs_pcs_edge; + uint8_t umts_fdd; + uint8_t umts_tdd; + uint8_t cdma_2000; + uint8_t dtm; + uint8_t class_dtm; + uint8_t dtm_mac; + uint8_t dtm_egprs; + + /* Timeout for GSM 03.22 C7 state */ + uint8_t any_timeout; + + /* ASCI settings */ + bool uplink_release_local; + bool asci_allow_any; + + /* call parameters */ + struct { + struct data_call_params data; + } call_params; +}; + +struct gsm_settings_abbrev { + struct llist_head list; + char abbrev[4]; + char number[32]; + char name[32]; +}; + +int gsm_settings_arfcn(struct osmocom_ms *ms); +int gsm_settings_init(struct osmocom_ms *ms); +int gsm_settings_exit(struct osmocom_ms *ms); +char *gsm_check_imei(const char *imei, const char *sv); +int gsm_random_imei(struct gsm_settings *set); + +struct gprs_settings { + struct llist_head apn_list; + + /* RFC1144 TCP/IP header compression */ + struct { + int active; + int passive; + int s01; + } pcomp_rfc1144; + + /* V.42vis data compression */ + struct { + int active; + int passive; + int p0; + int p1; + int p2; + } dcomp_v42bis; +}; + +int gprs_settings_init(struct osmocom_ms *ms); +int gprs_settings_fi(struct osmocom_ms *ms); +struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char *apn_name); +int ms_dispatch_all_apn(struct osmocom_ms *ms, uint32_t event, void *data); + +extern char *layer2_socket_path; + +#endif /* _settings_h */ + diff --git a/src/host/layer23/include/osmocom/bb/common/sim.h b/src/host/layer23/include/osmocom/bb/common/sim.h index 8b1f830c..8aae1fc8 100644 --- a/src/host/layer23/include/osmocom/bb/common/sim.h +++ b/src/host/layer23/include/osmocom/bb/common/sim.h @@ -13,12 +13,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ +#pragma once + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/core/endian.h> + +struct osmocom_ms; /* 9.2 commands */ #define GSM1111_CLASS_GSM 0xa0 @@ -212,6 +214,7 @@ struct gsm1111_response_mfdf { } __attribute__ ((packed)); struct gsm1111_response_mfdf_gsm { +#if OSMO_IS_LITTLE_ENDIAN uint8_t file_char; uint8_t num_df; uint8_t num_ef; @@ -230,10 +233,24 @@ struct gsm1111_response_mfdf_gsm { rfu5:3, unblk2_init:1; uint8_t more_data[0]; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint8_t file_char; + uint8_t num_df; + uint8_t num_ef; + uint8_t num_codes; + uint8_t rfu1; + uint8_t chv1_init:1, rfu2:3, chv1_remain:4; + uint8_t unblk1_init:1, rfu3:3, unblk1_remain:4; + uint8_t chv2_init:1, rfu4:3, chv2_remain:4; + uint8_t unblk2_init:1, rfu5:3, unblk2_remain:4; + uint8_t more_data[0]; +#endif } __attribute__ ((packed)); /* Section 9.2.1 (response to selecting EF) */ struct gsm1111_response_ef { +#if OSMO_IS_LITTLE_ENDIAN uint16_t rfu1; uint16_t file_size; uint16_t file_id; @@ -251,6 +268,20 @@ struct gsm1111_response_ef { rfu4:5; uint8_t length; uint8_t structure; +#elif OSMO_IS_BIG_ENDIAN +/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ + uint16_t rfu1; + uint16_t file_size; + uint16_t file_id; + uint8_t tof; + uint8_t inc_allowed; + uint8_t acc_read:4, acc_update:4; + uint8_t acc_inc:4, rfu2:4; + uint8_t acc_reha:4, acc_inval:4; + uint8_t rfu4:5, ru_inval:1, rfu3:1, not_inval:1; + uint8_t length; + uint8_t structure; +#endif } __attribute__ ((packed)); /* Section 10.3.17 */ @@ -258,9 +289,34 @@ struct gsm1111_ef_loci { uint32_t tmsi; struct gsm48_loc_area_id lai; uint8_t tmsi_time; - uint8_t lupd_status; + uint8_t lupd_status; /* enum gsm1111_ef_loci_lupd_status */ } __attribute__ ((packed)); +enum gsm1111_ef_loci_lupd_status { + GSM1111_EF_LOCI_LUPD_ST_UPDATED = 0, + GSM1111_EF_LOCI_LUPD_ST_NOT_UPDATED = 1, + GSM1111_EF_LOCI_LUPD_ST_PLMN_NOT_ALLOWED = 2, + GSM1111_EF_LOCI_LUPD_ST_LA_NOT_ALLOWED = 3, + GSM1111_EF_LOCI_LUPD_ST_RESERVED = 7, +}; + +/* Section 10.3.33 */ +struct gsm1111_ef_locigprs { + uint32_t ptmsi; + uint16_t ptmsi_sig_hi; + uint8_t ptmsi_sig_lo; + struct gsm48_ra_id rai; + uint8_t rau_status; /* enum gsm1111_ef_locigprs_rau_status */ +} __attribute__ ((packed)); + +enum gsm1111_ef_locigprs_rau_status { + GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED = 0, + GSM1111_EF_LOCIGPRS_RAU_ST_NOT_UPDATED = 1, + GSM1111_EF_LOCIGPRS_RAU_ST_PLMN_NOT_ALLOWED = 2, + GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED = 3, + GSM1111_EF_LOCIGPRS_RAU_ST_RESERVED = 7, +}; + /* Section 10.5.1 */ struct gsm1111_ef_adn { uint8_t len_bcd; diff --git a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h b/src/host/layer23/include/osmocom/bb/common/subscriber.h index 698b0fdc..d36b4c6c 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/subscriber.h +++ b/src/host/layer23/include/osmocom/bb/common/subscriber.h @@ -1,29 +1,54 @@ #ifndef _SUBSCRIBER_H #define _SUBSCRIBER_H +#include <stdbool.h> + +#include <osmocom/core/utils.h> +#include <osmocom/gsm/protocol/gsm_23_003.h> +#include <osmocom/gsm/gsm23003.h> +#include <osmocom/gsm/gsm48.h> + /* GSM 04.08 4.1.2.2 SIM update status */ -#define GSM_SIM_U0_NULL 0 -#define GSM_SIM_U1_UPDATED 1 -#define GSM_SIM_U2_NOT_UPDATED 2 -#define GSM_SIM_U3_ROAMING_NA 3 +enum gsm_sub_sim_ustate { + GSM_SIM_U0_NULL, + GSM_SIM_U1_UPDATED, + GSM_SIM_U2_NOT_UPDATED, + GSM_SIM_U3_ROAMING_NA, +}; +extern const struct value_string gsm_sub_sim_ustate_names[]; +static inline const char *gsm_sub_sim_ustate_name(enum gsm_sub_sim_ustate val) +{ + return get_value_string(gsm_sub_sim_ustate_names, val); +} + +/* 3GPP TS 24.008 4.1.3.2 GPRS update status */ +enum gsm_sub_sim_gustate { + GSM_SIM_GU0_NULL, + GSM_SIM_GU1_UPDATED, + GSM_SIM_GU2_NOT_UPDATED, + GSM_SIM_GU3_ROAMING_NA, +}; +extern const struct value_string gsm_sub_sim_gustate_names[]; +static inline const char *gsm_sub_sim_gustate_name(enum gsm_sub_sim_gustate val) +{ + return get_value_string(gsm_sub_sim_gustate_names, val); +} struct gsm_sub_plmn_list { struct llist_head entry; - uint16_t mcc, mnc; + struct osmo_plmn_id plmn; }; struct gsm_sub_plmn_na { struct llist_head entry; - uint16_t mcc, mnc; + struct osmo_plmn_id plmn; uint8_t cause; }; -#define GSM_IMSI_LENGTH 16 - #define GSM_SIM_IS_READER(type) \ (type == GSM_SIM_TYPE_L1PHY || type == GSM_SIM_TYPE_SAP) -enum { +enum gsm_subscriber_sim_type { GSM_SIM_TYPE_NONE = 0, GSM_SIM_TYPE_L1PHY, GSM_SIM_TYPE_TEST, @@ -34,19 +59,19 @@ struct gsm_subscriber { struct osmocom_ms *ms; /* status */ - uint8_t sim_type; /* type of sim */ - uint8_t sim_valid; /* sim inserted and valid */ - uint8_t ustate; /* update status */ - uint8_t imsi_attached; /* attached state */ + enum gsm_subscriber_sim_type sim_type; /* type of sim */ + bool sim_valid; /* sim inserted and valid */ + enum gsm_sub_sim_ustate ustate; /* update status */ + bool imsi_attached; /* attached state */ /* IMSI & co */ - char imsi[GSM_IMSI_LENGTH]; + char imsi[OSMO_IMSI_BUF_SIZE]; char msisdn[31]; /* may include access codes */ char iccid[21]; /* 20 + termination */ /* TMSI / LAI */ - uint32_t tmsi; /* invalid tmsi: 0xffffffff */ - uint16_t mcc, mnc, lac; /* invalid lac: 0x0000 */ + uint32_t tmsi; /* invalid tmsi: GSM_RESERVED_TMSI */ + struct osmo_location_area_id lai; /* invalid lac: 0x0000 */ /* key */ @@ -59,7 +84,7 @@ struct gsm_subscriber { uint8_t t6m_hplmn; /* timer for hplmn search */ /* special things */ - uint8_t always_search_hplmn; + bool always_search_hplmn; /* search hplmn in other countries also (for test cards) */ uint8_t any_timeout; /* timer to restart 'any cell selection' */ @@ -67,16 +92,16 @@ struct gsm_subscriber { char sim_spn[17]; /* name of service privider */ /* PLMN last registered */ - uint8_t plmn_valid; - uint16_t plmn_mcc, plmn_mnc; + bool plmn_valid; + struct osmo_plmn_id plmn; /* our access */ - uint8_t acc_barr; /* if we may access, if cell barred */ + bool acc_barr; /* if we may access, if cell barred */ uint16_t acc_class; /* bitmask of what we may access */ /* talk to SIM */ uint8_t sim_state; - uint8_t sim_pin_required; /* state: wait for PIN */ + bool sim_pin_required; /* state: wait for PIN */ uint8_t sim_file_index; uint32_t sim_handle_query; uint32_t sim_handle_update; @@ -84,35 +109,38 @@ struct gsm_subscriber { /* SMS */ char sms_sca[22]; + + struct { + uint8_t gu_state; /* GU, TS 24.008 */ + bool rai_valid; + struct gprs_ra_id rai; + uint32_t ptmsi; /* invalid tmsi: GSM_RESERVED_TMSI */ + uint32_t ptmsi_sig; /* P-TMSI signature, 3 bytes */ + bool imsi_attached; + } gprs; }; int gsm_subscr_init(struct osmocom_ms *ms); int gsm_subscr_exit(struct osmocom_ms *ms); -int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, - uint16_t lac, uint32_t tmsi, uint8_t imsi_attached); +int gsm_subscr_insert(struct osmocom_ms *ms); +int gsm_subscr_remove(struct osmocom_ms *ms); + int gsm_subscr_sap_rsp_cb(struct osmocom_ms *ms, int res_code, uint8_t res_type, uint16_t param_len, const uint8_t *param_val); -int gsm_subscr_sapcard(struct osmocom_ms *ms); -int gsm_subscr_remove_sapcard(struct osmocom_ms *ms); -int gsm_subscr_simcard(struct osmocom_ms *ms); -void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, - int8_t mode); +int gsm_subscr_sim_pin(struct osmocom_ms *ms, const char *pin1, const char *pin2, + int8_t mode); int gsm_subscr_write_loci(struct osmocom_ms *ms); -int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, - uint8_t *rand, uint8_t no_sim); -int gsm_subscr_remove(struct osmocom_ms *ms); +int gsm_subscr_write_locigprs(struct osmocom_ms *ms); +int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, const uint8_t *rand, + bool no_sim); void new_sim_ustate(struct gsm_subscriber *subscr, int state); -int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc); -int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc, uint8_t cause); -int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc); +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn); +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn, uint8_t cause); +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn); int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv); void gsm_subscr_dump(struct gsm_subscriber *subscr, void (*print)(void *, const char *, ...), void *priv); -char *gsm_check_imsi(const char *imsi); int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr); #endif /* _SUBSCRIBER_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/support.h b/src/host/layer23/include/osmocom/bb/common/support.h index c56c78e8..b0c71f5a 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/support.h +++ b/src/host/layer23/include/osmocom/bb/common/support.h @@ -91,6 +91,14 @@ struct gsm_support { uint8_t half_v1; uint8_t half_v3; + /* CSD modes */ + uint8_t csd_tch_f144; + uint8_t csd_tch_f96; + uint8_t csd_tch_f48; + uint8_t csd_tch_h48; + uint8_t csd_tch_f24; + uint8_t csd_tch_h24; + /* EDGE / UMTS / CDMA */ uint8_t edge_ms_sup; uint8_t edge_psk_sup; @@ -110,7 +118,7 @@ struct gsm_support_scan_max { uint16_t start; uint16_t end; uint16_t max; - uint16_t temp; + uint16_t temp; }; extern struct gsm_support_scan_max gsm_sup_smax[]; diff --git a/src/host/layer23/include/osmocom/bb/common/sysinfo.h b/src/host/layer23/include/osmocom/bb/common/sysinfo.h index 7fea2daa..c31cf9d6 100644 --- a/src/host/layer23/include/osmocom/bb/common/sysinfo.h +++ b/src/host/layer23/include/osmocom/bb/common/sysinfo.h @@ -2,6 +2,7 @@ #define _SYSINFO_H #include <osmocom/gsm/gsm48_ie.h> +#include <osmocom/gsm/gsm23003.h> /* collection of system information of the current cell */ @@ -17,11 +18,27 @@ #define FREQ_TYPE_REP_5bis 0x40 /* sub channel of SI 5bis */ #define FREQ_TYPE_REP_5ter 0x80 /* sub channel of SI 5ter */ +struct si10_cell_info { + uint8_t index; /* frequency index of the frequencies received in SI5* */ + int16_t arfcn; /* ARFCN or -1 (if not found in SI5*) */ + uint8_t bsic; + bool barred; /* Cell is barred, values below are invalid. */ + bool la_different; /* Location area is different, so CRH is valid. */ + uint8_t cell_resel_hyst_db; + uint8_t ms_txpwr_max_cch; + uint8_t rxlev_acc_min_db; + uint8_t cell_resel_offset; + uint8_t temp_offset; + uint8_t penalty_time; +}; + /* structure of all received system information */ struct gsm48_sysinfo { /* flags of available information */ uint8_t si1, si2, si2bis, si2ter, si3, - si4, si5, si5bis, si5ter, si6; + si4, si5, si5bis, si5ter, si6, + si13; + bool si10; /* memory maps to simply detect change in system info messages */ uint8_t si1_msg[23]; @@ -34,6 +51,8 @@ struct gsm48_sysinfo { uint8_t si5b_msg[18]; uint8_t si5t_msg[18]; uint8_t si6_msg[18]; + uint8_t si10_msg[21]; + uint8_t si13_msg[23]; struct gsm_sysinfo_freq freq[1024]; /* all frequencies */ uint16_t hopping[64]; /* hopping arfcn */ @@ -42,7 +61,7 @@ struct gsm48_sysinfo { /* serving cell */ uint8_t bsic; uint16_t cell_id; - uint16_t mcc, mnc, lac; /* LAI */ + struct osmo_location_area_id lai; uint8_t max_retrans; /* decoded */ uint8_t tx_integer; /* decoded */ uint8_t reest_denied; /* 1 = denied */ @@ -52,7 +71,7 @@ struct gsm48_sysinfo { /* si1 rest */ uint8_t nch; uint8_t nch_position; - uint8_t band_ind; /* set for DCS */ + bool band_ind; /* set for DCS */ /* si3 rest */ uint8_t sp; @@ -66,9 +85,46 @@ struct gsm48_sysinfo { uint8_t ecsm; uint8_t sched; uint8_t sched_where; - uint8_t gprs; - uint8_t gprs_ra_colour; - uint8_t gprs_si13_pos; + + struct { + /* si3/si4 rest */ + uint8_t supported; + uint8_t ra_colour; + uint8_t si13_pos; + + /* si13 rest */ + uint8_t hopping; + uint8_t hsn; + uint8_t rfl_num_len; + uint8_t rfl_num[4]; + + uint8_t ma_bitlen; + uint8_t ma_bitmap[64 / 8]; + uint8_t arfcn_idx_len; + uint8_t arfcn_idx[16]; + + /* PBCCH is not present */ + uint8_t rac; + uint8_t prio_acc_thresh; + uint8_t nco; + + /* GPRS Cell Options */ + uint8_t nmo; + uint8_t T3168; + uint8_t T3192; + uint8_t ab_type; + uint8_t ctrl_ack_type_use_block; + uint8_t bs_cv_max; + uint8_t pan_params_present; + uint8_t pan_dec; + uint8_t pan_inc; + uint8_t pan_max; + + /* EGPRS Cell Options */ + uint8_t egprs_supported; + uint8_t egprs_pkt_chan_req; + uint8_t egprs_bep_period; + } gprs; /* cell selection */ int8_t ms_txpwr_max_cch; @@ -118,45 +174,52 @@ struct gsm48_sysinfo { uint8_t nb_reest_denied; /* 1 = denied */ uint8_t nb_cell_barr; /* 1 = barred */ uint16_t nb_class_barr; /* bit 10 is emergency */ + + /* SI 10 */ + uint8_t si10_cell_num; /* number neighbor cells found in SI 10 */ + struct si10_cell_info si10_cell[32]; /* 32 neighbor cell descriptions */ }; char *gsm_print_arfcn(uint16_t arfcn); -uint8_t gsm_refer_pcs(uint16_t arfcn, struct gsm48_sysinfo *s); -int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, - void (*print)(void *, const char *, ...), void *priv, - uint8_t *freq_map); +bool gsm_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s); +uint16_t gsm_arfcn_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s, uint16_t arfcn); +int gsm48_sysinfo_dump(const struct gsm48_sysinfo *s, uint16_t arfcn, + void (*print)(void *, const char *, ...), + void *priv, uint8_t *freq_map); +int gsm48_si10_dump(const struct gsm48_sysinfo *s, void (*print)(void *, const char *, ...), void *priv); int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, uint16_t *mnc, uint16_t *lac); -int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, - uint16_t *arfcn); -int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc, - uint8_t *maio, uint8_t *hsn); +int gsm48_decode_chan_h0(const struct gsm48_chan_desc *cd, + uint8_t *tsc, uint16_t *arfcn); +int gsm48_decode_chan_h1(const struct gsm48_chan_desc *cd, + uint8_t *tsc, uint8_t *maio, uint8_t *hsn); int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_1 *si, int len); + const struct gsm48_system_information_type_1 *si, int len); int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_2 *si, int len); + const struct gsm48_system_information_type_2 *si, int len); int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_2bis *si, int len); + const struct gsm48_system_information_type_2bis *si, int len); int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_2ter *si, int len); + const struct gsm48_system_information_type_2ter *si, int len); int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_3 *si, int len); + const struct gsm48_system_information_type_3 *si, int len); int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_4 *si, int len); + const struct gsm48_system_information_type_4 *si, int len); int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_5 *si, int len); + const struct gsm48_system_information_type_5 *si, int len); int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_5bis *si, int len); + const struct gsm48_system_information_type_5bis *si, int len); int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_5ter *si, int len); + const struct gsm48_system_information_type_5ter *si, int len); int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_6 *si, int len); + const struct gsm48_system_information_type_6 *si, int len); +int gsm48_decode_sysinfo10(struct gsm48_sysinfo *s, + const struct gsm48_system_information_type_10 *si, int len); +int gsm48_decode_sysinfo13(struct gsm48_sysinfo *s, + const struct gsm48_system_information_type_13 *si, int len); int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq, - uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, - int si4); -int gsm48_encode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t mcc, - uint16_t mnc, uint16_t lac); -int gsm48_decode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t *mcc, - uint16_t *mnc, uint16_t *lac); + const uint8_t *ma, uint8_t len, + uint16_t *hopping, uint8_t *hopp_len, int si4); +int16_t arfcn_from_freq_index(const struct gsm48_sysinfo *s, uint16_t index); #endif /* _SYSINFO_H */ diff --git a/src/host/layer23/include/osmocom/bb/common/vty.h b/src/host/layer23/include/osmocom/bb/common/vty.h new file mode 100644 index 00000000..b2a52c01 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/common/vty.h @@ -0,0 +1,35 @@ +#pragma once + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/command.h> +#include <osmocom/core/signal.h> + +struct osmocom_ms; + +enum l23_vty_node { + MS_NODE = _LAST_OSMOVTY_NODE + 1, + TESTSIM_NODE, + GSMTAP_NODE, + _LAST_L23VTY_NODE, +}; + +int l23_vty_init(int (*config_write_ms_node_cb)(struct vty *), osmo_signal_cbfn *l23_vty_signal_cb); + +struct osmocom_ms *l23_vty_get_ms(const char *name, struct vty *vty); +void l23_ms_dump(struct osmocom_ms *ms, struct vty *vty); +void l23_vty_config_write_ms_node(struct vty *vty, const struct osmocom_ms *ms, const char *prefix); +void l23_vty_config_write_ms_node_contents(struct vty *vty, const struct osmocom_ms *ms, const char *prefix); +void l23_vty_config_write_ms_node_contents_final(struct vty *vty, const struct osmocom_ms *ms, const char *prefix); + +void l23_vty_ms_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +void l23_vty_printf(void *priv, const char *fmt, ...); + +extern bool l23_vty_reading; +extern bool l23_vty_hide_default; + +extern struct llist_head ms_list; + +extern struct cmd_element l23_show_ms_cmd; +extern struct cmd_element l23_cfg_ms_cmd; diff --git a/src/host/layer23/include/osmocom/bb/misc/Makefile.am b/src/host/layer23/include/osmocom/bb/misc/Makefile.am index 59760906..14d3af95 100644 --- a/src/host/layer23/include/osmocom/bb/misc/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/misc/Makefile.am @@ -1 +1,8 @@ -noinst_HEADERS = layer3.h rslms.h cell_log.h +noinst_HEADERS = \ + cell_log.h \ + geo.h \ + layer3.h \ + locate.h \ + log.h \ + rslms.h \ + $(NULL) diff --git a/src/host/layer23/include/osmocom/bb/misc/cell_log.h b/src/host/layer23/include/osmocom/bb/misc/cell_log.h index bce066eb..bef6e6ee 100644 --- a/src/host/layer23/include/osmocom/bb/misc/cell_log.h +++ b/src/host/layer23/include/osmocom/bb/misc/cell_log.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ int scan_init(struct osmocom_ms *_ms); diff --git a/src/host/gsmmap/geo.h b/src/host/layer23/include/osmocom/bb/misc/geo.h index 25e26cba..25e26cba 100644 --- a/src/host/gsmmap/geo.h +++ b/src/host/layer23/include/osmocom/bb/misc/geo.h diff --git a/src/host/gsmmap/locate.h b/src/host/layer23/include/osmocom/bb/misc/locate.h index 26133452..26133452 100644 --- a/src/host/gsmmap/locate.h +++ b/src/host/layer23/include/osmocom/bb/misc/locate.h diff --git a/src/host/gsmmap/log.h b/src/host/layer23/include/osmocom/bb/misc/log.h index d1520101..3183f876 100644 --- a/src/host/gsmmap/log.h +++ b/src/host/layer23/include/osmocom/bb/misc/log.h @@ -1,3 +1,6 @@ +#pragma once + +#include <osmocom/bb/common/sysinfo.h> enum { LOG_TYPE_NONE = 0, @@ -26,6 +29,7 @@ struct node_mcc { struct node_mnc { struct node_mnc *next; uint16_t mnc; + bool mnc_3_digits; struct node_lac *lac; }; @@ -72,7 +76,7 @@ struct node_meas { }; struct node_mcc *get_node_mcc(uint16_t mcc); -struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc); +struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc, bool mnc_3_digits); struct node_lac *get_node_lac(struct node_mnc *mnc, uint16_t lac); struct node_cell *get_node_cell(struct node_lac *lac, uint16_t cellid); struct node_meas *add_node_meas(struct node_cell *cell); diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am index 623964fd..5d2eeaf8 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am @@ -1,4 +1,4 @@ noinst_HEADERS = gsm322.h gsm480_ss.h gsm411_sms.h gsm48_cc.h gsm48_mm.h \ - gsm48_rr.h mncc.h settings.h subscriber.h support.h \ - transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \ - app_mobile.h voice.h + gsm48_rr.h mncc.h gsm44068_gcc_bcc.h \ + tch.h transaction.h vty.h mncc_sock.h mncc_ms.h primitives.h \ + app_mobile.h gapk_io.h diff --git a/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h index 191f4baf..940dbce7 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h +++ b/src/host/layer23/include/osmocom/bb/mobile/app_mobile.h @@ -8,10 +8,6 @@ extern char *config_dir; struct osmocom_ms; struct vty; -int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *), - const char *config_file); -int l23_app_exit(void); -int l23_app_work(int *quit); int mobile_delete(struct osmocom_ms *ms, int force); struct osmocom_ms *mobile_new(char *name); int mobile_work(struct osmocom_ms *ms); diff --git a/src/host/layer23/include/osmocom/bb/mobile/gapk_io.h b/src/host/layer23/include/osmocom/bb/mobile/gapk_io.h new file mode 100644 index 00000000..ab8b6579 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gapk_io.h @@ -0,0 +1,47 @@ +#pragma once + +#ifdef WITH_GAPK_IO + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/gapk/procqueue.h> +#include <osmocom/gapk/codecs.h> + +#define GAPK_ULDL_QUEUE_LIMIT 8 + +/* Forward declarations */ +struct osmocom_ms; +struct msgb; + +struct gapk_io_state { + /* src/alsa -> proc/codec -> sink/tch_fb */ + struct osmo_gapk_pq *pq_source; + /* src/tch_fb -> proc/codec -> sink/alsa */ + struct osmo_gapk_pq *pq_sink; + + /* Description of currently used codec / format */ + const struct osmo_gapk_format_desc *phy_fmt_desc; + const struct osmo_gapk_codec_desc *codec_desc; + + /* DL TCH frame buffer (received, to be played) */ + struct llist_head tch_dl_fb; + unsigned int tch_dl_fb_len; + /* UL TCH frame buffer (captured, to be sent) */ + struct llist_head tch_ul_fb; + unsigned int tch_ul_fb_len; +}; + +struct gapk_io_state * +gapk_io_state_alloc(struct osmocom_ms *ms, + enum osmo_gapk_codec_type codec); +struct gapk_io_state * +gapk_io_state_alloc_mode_rate(struct osmocom_ms *ms, + enum gsm48_chan_mode ch_mode, + bool full_rate); +void gapk_io_state_free(struct gapk_io_state *state); + +void gapk_io_enqueue_dl(struct gapk_io_state *state, struct msgb *msg); +void gapk_io_dequeue_ul(struct osmocom_ms *ms, struct gapk_io_state *state); + +#endif /* WITH_GAPK_IO */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h index b332778d..4b20a1a1 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm322.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm322.h @@ -1,10 +1,11 @@ #ifndef _GSM322_H #define _GSM322_H -#include <osmocom/bb/common/sysinfo.h> - #include <osmocom/core/linuxlist.h> #include <osmocom/core/timer.h> +#include <osmocom/gsm/gsm23003.h> + +#include <osmocom/bb/common/sysinfo.h> /* 4.3.1.1 List of states for PLMN selection process (automatic mode) */ #define GSM322_A0_NULL 0 @@ -42,7 +43,7 @@ /* GSM 03.22 events */ #define GSM322_EVENT_SWITCH_ON 1 -#define GSM322_EVENT_SWITCH_OFF 2 +#define GSM322_EVENT_SWITCH_OFF 2 #define GSM322_EVENT_SIM_INSERT 3 #define GSM322_EVENT_SIM_REMOVE 4 #define GSM322_EVENT_REG_SUCCESS 5 @@ -74,7 +75,7 @@ enum { /* node for each PLMN */ struct gsm322_plmn_list { struct llist_head entry; - uint16_t mcc, mnc; + struct osmo_plmn_id plmn; uint8_t rxlev; /* rx level in range format */ uint8_t cause; /* cause value, if PLMN is not allowed */ }; @@ -82,14 +83,14 @@ struct gsm322_plmn_list { /* node for each forbidden LA */ struct gsm322_la_list { struct llist_head entry; - uint16_t mcc, mnc, lac; + struct osmo_location_area_id lai; uint8_t cause; }; /* node for each BA-List */ struct gsm322_ba_list { struct llist_head entry; - uint16_t mcc, mnc; + struct osmo_plmn_id plmn; /* Band allocation for 1024+299 frequencies. * First bit of first index is frequency 0. */ @@ -124,7 +125,7 @@ struct gsm322_plmn { struct osmo_timer_list timer; int plmn_curr; /* current index in sorted_plmn */ - uint16_t mcc, mnc; /* current network selected */ + struct osmo_plmn_id plmn; /* current network selected */ }; /* state of CCCH activation */ @@ -171,7 +172,7 @@ struct gsm322_cellsel { /* cell selection list per frequency. */ /* scan and tune state */ struct osmo_timer_list timer; /* cell selection timer */ - uint16_t mcc, mnc; /* current network to search for */ + struct osmo_plmn_id plmn; /* current network to search for */ uint8_t powerscan; /* currently scanning for power */ uint8_t ccch_state; /* special state of current ccch */ uint32_t scan_state; /* special state of current scan */ @@ -188,7 +189,7 @@ struct gsm322_cellsel { uint8_t selected; /* if a cell is selected */ uint16_t sel_arfcn; /* current selected serving cell! */ struct gsm48_sysinfo sel_si; /* copy of selected cell, will update */ - uint16_t sel_mcc, sel_mnc, sel_lac, sel_id; + struct osmo_cell_global_id sel_cgi; /* cell re-selection */ struct llist_head nb_list; /* list of neighbour cells */ @@ -209,7 +210,7 @@ struct gsm322_cellsel { /* GSM 03.22 message */ struct gsm322_msg { int msg_type; - uint16_t mcc, mnc; + struct osmo_plmn_id plmn; uint8_t sysinfo; /* system information type */ uint8_t same_cell; /* select same cell when RET_IDLE */ uint8_t reject; /* location update reject cause */ @@ -229,18 +230,15 @@ int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg); int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg); int gsm322_plmn_dequeue(struct osmocom_ms *ms); int gsm322_cs_dequeue(struct osmocom_ms *ms); -int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc, uint16_t lac, uint8_t cause); -int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc, uint16_t lac); -int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, - uint16_t lac); +int gsm322_add_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai, uint8_t cause); +int gsm322_del_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai); +int gsm322_is_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai); int gsm322_dump_sorted_plmn(struct osmocom_ms *ms); int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, void (*print)(void *, const char *, ...), void *priv); int gsm322_dump_forbidden_la(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv); -int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn, void (*print)(void *, const char *, ...), void *priv); int gsm322_dump_nb_list(struct gsm322_cellsel *cs, void (*print)(void *, const char *, ...), void *priv); diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h b/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h new file mode 100644 index 00000000..e9889b32 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm44068_gcc_bcc.h @@ -0,0 +1,43 @@ +/* VGCS/VBS call control */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Andreas Eversberg + * + * 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 <http://www.gnu.org/licenses/>. + */ +#pragma once + +#define GSM44068_ALLOC_SIZE 2048 +#define GSM44068_ALLOC_HEADROOM 256 + +static inline struct msgb *gsm44068_msgb_alloc_name(const char *name) +{ + return msgb_alloc_headroom(GSM44068_ALLOC_SIZE, GSM44068_ALLOC_HEADROOM, name); +} + +int gsm44068_gcc_init(struct osmocom_ms *ms); +int gsm44068_gcc_exit(struct osmocom_ms *ms); +int gsm44068_rcv_gcc_bcc(struct osmocom_ms *ms, struct msgb *msg); +int gsm44068_rcv_mm_idle(struct osmocom_ms *ms); +struct gsm_trans *trans_find_ongoing_gcc_bcc(struct osmocom_ms *ms); +int gcc_bcc_call(struct osmocom_ms *ms, uint8_t protocol, const char *number); +int gcc_leave(struct osmocom_ms *ms); +int gcc_bcc_hangup(struct osmocom_ms *ms); +int gcc_talk(struct osmocom_ms *ms); +int gcc_listen(struct osmocom_ms *ms); +int gsm44068_dump_calls(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv); diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h index bf3aa257..1f0db785 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_mm.h @@ -1,11 +1,16 @@ #ifndef _GSM48_MM_H #define _GSM48_MM_H +struct gsm_settings; + /* GSM 04.07 9.2.2 */ #define GSM48_MMXX_MASK 0xf00 #define GSM48_MMCC_CLASS 0x100 #define GSM48_MMSS_CLASS 0x200 #define GSM48_MMSMS_CLASS 0x300 +#define GSM48_MMGCC_CLASS 0x500 +#define GSM48_MMBCC_CLASS 0x600 +#define GSM48_MMXX_REL_IND 0x022 #define GSM48_MMCC_EST_REQ 0x110 #define GSM48_MMCC_EST_IND 0x112 #define GSM48_MMCC_EST_CNF 0x111 @@ -49,18 +54,66 @@ #define GSM48_MMSMS_ERR_IND 0x372 #define GSM48_MMSMS_PROMPT_IND 0x382 #define GSM48_MMSMS_PROMPT_REJ 0x384 +/* MM messages for Voice Group/Broadcast Calls */ +#define GSM48_MMGCC_EST_REQ 0x510 +#define GSM48_MMGCC_EST_CNF 0x511 +#define GSM48_MMGCC_REL_REQ 0x520 +#define GSM48_MMGCC_REL_IND 0x522 +#define GSM48_MMGCC_DATA_REQ 0x530 +#define GSM48_MMGCC_DATA_IND 0x532 +#define GSM48_MMGCC_UNIT_DATA_REQ 0x540 +#define GSM48_MMGCC_UNIT_DATA_IND 0x542 +#define GSM48_MMGCC_REEST_REQ 0x560 +#define GSM48_MMGCC_REEST_CNF 0x561 +#define GSM48_MMGCC_ERR_IND 0x572 +#define GSM48_MMGCC_NOTIF_IND 0x582 +#define GSM48_MMGCC_GROUP_REQ 0x590 +#define GSM48_MMGCC_GROUP_CNF 0x591 +#define GSM48_MMGCC_UPLINK_REQ 0x5a0 +#define GSM48_MMGCC_UPLINK_CNF 0x5a1 +#define GSM48_MMGCC_UPLINK_REL_REQ 0x5a8 +#define GSM48_MMGCC_UPLINK_REL_IND 0x5aa +#define GSM48_MMGCC_UPLINK_FREE_IND 0x5b2 +#define GSM48_MMGCC_UPLINK_BUSY_IND 0x5b6 +#define GSM48_MMBCC_EST_REQ 0x610 +#define GSM48_MMBCC_EST_CNF 0x611 +#define GSM48_MMBCC_REL_REQ 0x620 +#define GSM48_MMBCC_REL_IND 0x622 +#define GSM48_MMBCC_DATA_REQ 0x630 +#define GSM48_MMBCC_DATA_IND 0x632 +#define GSM48_MMBCC_UNIT_DATA_REQ 0x640 +#define GSM48_MMBCC_UNIT_DATA_IND 0x642 +#define GSM48_MMBCC_REEST_REQ 0x660 +#define GSM48_MMBCC_REEST_CNF 0x661 +#define GSM48_MMBCC_ERR_IND 0x672 +#define GSM48_MMBCC_NOTIF_IND 0x682 +#define GSM48_MMBCC_GROUP_REQ 0x690 +#define GSM48_MMBCC_GROUP_CNF 0x691 +#define GSM48_MMBCC_UPLINK_REQ 0x6a0 +#define GSM48_MMBCC_UPLINK_CNF 0x6a1 +#define GSM48_MMBCC_UPLINK_REL_REQ 0x6a8 +#define GSM48_MMBCC_UPLINK_REL_IND 0x6aa +#define GSM48_MMBCC_UPLINK_FREE_IND 0x6b2 +#define GSM48_MMBCC_UPLINK_BUSY_IND 0x6b6 + #define MMXX_ALLOC_SIZE 256 #define MMXX_ALLOC_HEADROOM 64 +#define MMXX_NOTIFY_SETUP 0 +#define MMXX_NOTIFY_RELEASE 1 + /* MMxx-SAP header */ struct gsm48_mmxx_hdr { - uint16_t msg_type; /* MMxx_* primitive */ - uint32_t ref; /* reference to transaction */ - uint32_t transaction_id; /* transaction identifier */ - uint8_t sapi; /* sapi */ - uint8_t emergency; /* emergency type of call */ - uint8_t cause; /* cause used for release */ + uint16_t msg_type; /* MMxx_* primitive */ + uint32_t ref; /* reference to transaction */ + uint32_t transaction_id; /* transaction identifier */ + uint8_t sapi; /* sapi */ + uint8_t emergency; /* emergency type of call */ + uint8_t cause; /* cause used for release */ + uint8_t notify; /* notify ongoing ASCI call */ + bool ch_desc_present; /* notifies channel */ + struct gsm48_chan_desc ch_desc; /* group channel */ } __attribute__((packed)); /* GSM 6.1.2 */ @@ -133,12 +186,25 @@ struct gsm48_mmr { #define GSM48_MM_EVENT_SYSINFO 14 #define GSM48_MM_EVENT_USER_PLMN_SEL 15 #define GSM48_MM_EVENT_LOST_COVERAGE 16 +#define GSM48_MM_EVENT_NOTIFICATION 17 +#define GSM48_MM_EVENT_UPLINK_FREE 18 +#define GSM48_MM_EVENT_UPLINK_BUSY 19 /* message for MM events */ struct gsm48_mm_event { - uint32_t msg_type; + uint32_t msg_type; - uint8_t sres[4]; + union { + /* GSM48_MM_EVENT_AUTH_RESPONSE */ + uint8_t sres[4]; + /* GSM48_MM_EVENT_NOTIFICATION */ + struct { + uint8_t gcr[5]; + bool ch_desc_present; + struct gsm48_chan_desc ch_desc; + bool gone; + } __attribute__((packed)) notification; + }; } __attribute__((packed)); /* GSM 04.08 MM timers */ @@ -184,7 +250,7 @@ struct gsm48_mmlayer { uint8_t lupd_rej_cause; /* cause of last reject */ uint8_t lupd_periodic; /* periodic update pending */ uint8_t lupd_retry; /* pending T3211/T3213 to */ - uint16_t lupd_mcc, lupd_mnc, lupd_lac; + struct osmo_location_area_id lupd_lai; /* imsi detach */ uint8_t delay_detach; /* do detach when possible */ @@ -196,6 +262,14 @@ struct gsm48_mmlayer { /* sapi 3 */ int sapi3_link; + + /* VGCS additional states */ + struct { + bool enabled; /* We are in group/broadcast mode. */ + bool group_call; /* This is a group call, not a broadcast call. */ + uint32_t callref; /* Callref of this call. */ + bool normal_service; /* Service state before group transmit mode. */ + } vgcs; }; /* MM connection entry */ @@ -203,7 +277,7 @@ struct gsm48_mm_conn { struct llist_head list; struct gsm48_mmlayer *mm; - /* ref and type form a unique tuple */ + /* ref and protocol form a unique tuple */ uint32_t ref; /* reference to trans */ uint8_t protocol; uint8_t transaction_id; @@ -212,6 +286,8 @@ struct gsm48_mm_conn { int state; }; +int gsm48_encode_mi_lv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi); +int gsm48_encode_mi_tlv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi); uint8_t gsm48_current_pwr_lev(struct gsm_settings *set, uint16_t arfcn); int gsm48_mm_init(struct osmocom_ms *ms); int gsm48_mm_exit(struct osmocom_ms *ms); diff --git a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h index 9b499a6b..112e85a3 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h +++ b/src/host/layer23/include/osmocom/bb/mobile/gsm48_rr.h @@ -1,6 +1,7 @@ #ifndef _GSM48_RR_H #define _GSM48_RR_H +#include <osmocom/core/timer.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #define GSM_TA_CM 55385 @@ -9,6 +10,10 @@ #define T200_DCCH_SHARED 2 /* SDCCH shares SAPI 0 and 3 */ #define T200_ACCH 2 /* SACCH SAPI 3 */ +/* GSM 04.08 RR timers */ +#define GSM_T3128_MS 1, 0 /* Uplink investigation timer. */ +#define GSM_T3130_MS 5, 0 /* Uplink access timeout. */ + /* GSM 04.07 9.1.2 */ #define GSM48_RR_EST_REQ 0x10 @@ -22,6 +27,15 @@ #define GSM48_RR_ABORT_REQ 0x60 #define GSM48_RR_ABORT_IND 0x62 #define GSM48_RR_ACT_REQ 0x70 +/* These are non-stadard primitives, used for group receive/transmit modes. */ +#define GSM48_RR_GROUP_REQ 0x80 /* Join a group channel in group receive mode. */ +#define GSM48_RR_GROUP_CNF 0x81 /* Group channel has been joined. */ +#define GSM48_RR_GROUP_REL_REQ 0x84 /* Release group channel. */ +#define GSM48_RR_GROUP_REL_IND 0x86 /* Group channel has been released or failed. */ +#define GSM48_RR_UPLINK_REQ 0x90 /* Request uplink for group transmit mode. */ +#define GSM48_RR_UPLINK_CNF 0x91 /* Access granted. */ +#define GSM48_RR_UPLINK_REL_REQ 0x94 /* Release uplink for group receive mode. */ +#define GSM48_RR_UPLINK_REL_IND 0x96 /* Access denied or failed or uplink released. */ #define RR_EST_CAUSE_EMERGENCY 1 #define RR_EST_CAUSE_REESTAB_TCH_F 2 @@ -44,6 +58,8 @@ #define RR_REL_CAUSE_EMERGENCY_ONLY 6 #define RR_REL_CAUSE_LOST_SIGNAL 7 #define RR_REL_CAUSE_LINK_FAILURE 8 +#define RR_REL_CAUSE_UPLINK_BUSY 9 +#define RR_REL_CAUSE_UPLINK_REJECTED 10 #define RR_SYNC_CAUSE_CIPHERING 1 @@ -69,6 +85,13 @@ struct gsm48_rr_hdr { #define GSM48_RR_ST_DEDICATED 2 #define GSM48_RR_ST_REL_PEND 3 +/* group states (VGCS) */ +enum gsm48_rr_gstate { + GSM48_RR_GST_OFF = 0, + GSM48_RR_GST_RECEIVE, + GSM48_RR_GST_TRANSMIT, +}; + /* special states for SAPI 3 link */ #define GSM48_RR_SAPI3ST_IDLE 0 #define GSM48_RR_SAPI3ST_WAIT_EST 1 @@ -100,6 +123,7 @@ struct gsm48_rr_cd { uint8_t start; /* start time available */ struct gsm_time start_tm; /* start time */ uint8_t mode; /* mode of channel */ + uint8_t tch_flags; /* E.g. L1CTL_TCH_FLAG_RXONLY */ uint8_t cipher; /* ciphering of channel */ }; @@ -149,7 +173,7 @@ struct gsm48_rrlayer { /* channel request states */ uint8_t wait_assign; /* waiting for assignment state */ uint8_t n_chan_req; /* number left, incl. current */ - uint8_t chan_req_val; /* current request value */ + uint8_t chan_req_val; /* current request value */ uint8_t chan_req_mask; /* mask of random bits */ /* state of dedicated mdoe */ @@ -157,7 +181,7 @@ struct gsm48_rrlayer { /* cr_hist */ uint8_t cr_ra; /* stores requested ra until confirmed */ - struct gsm48_cr_hist cr_hist[3]; + struct gsm48_cr_hist cr_hist[5]; /* V(SD) sequence numbers */ uint16_t v_sd; /* 16 PD 1-bit sequence numbers packed */ @@ -194,6 +218,21 @@ struct gsm48_rrlayer { /* sapi 3 */ uint8_t sapi3_state; uint8_t sapi3_link_id; + + /* group call */ + struct { + struct llist_head notif_list; /* list of received call notifications */ + enum gsm48_rr_gstate group_state; /* extension to RR state for group transmit/receive modes */ + struct gsm48_rr_cd cd_group; /* channel description of group call channel */ + bool uplink_free; /* Is set, if uplink is currently free. */ + uint8_t uic; /* UIC to use for access burst (-1 for BSIC) */ + bool uplink_access; /* The network wants us to send listener access bursts. */ + struct osmo_timer_list t_ul_free; /* Uplink free timer. (480ms timer) */ + struct osmo_timer_list t3128; /* Uplink investigation timer. */ + struct osmo_timer_list t3130; /* Uplink access timer. */ + uint8_t uplink_tries; /* Counts number of tries to access the uplink. */ + uint8_t uplink_counter; /* Counts number of access bursts per 'try'. */ + } vgcs; }; const char *get_rr_name(int value); @@ -212,7 +251,7 @@ extern const char *gsm48_rr_state_names[]; int gsm48_rr_start_monitor(struct osmocom_ms *ms); int gsm48_rr_stop_monitor(struct osmocom_ms *ms); int gsm48_rr_alter_delay(struct osmocom_ms *ms); -int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_rr_tx_traffic(struct osmocom_ms *ms, struct msgb *msg); int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode); #endif /* _GSM48_RR_H */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc.h b/src/host/layer23/include/osmocom/bb/mobile/mncc.h index 5e976cc2..d6facbbd 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/mncc.h +++ b/src/host/layer23/include/osmocom/bb/mobile/mncc.h @@ -1,4 +1,4 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ /* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> @@ -17,35 +17,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ -#ifndef _MNCC_H -#define _MNCC_H +#pragma once -#include <osmocom/core/linuxlist.h> #include <osmocom/gsm/mncc.h> -struct gsm_call { - struct llist_head entry; - - struct osmocom_ms *ms; - - uint32_t callref; - - bool init; /* call initiated, no response yet */ - bool hold; /* call on hold */ - bool ring; /* call ringing/knocking */ - - struct osmo_timer_list dtmf_timer; - uint8_t dtmf_state; - uint8_t dtmf_index; - char dtmf[32]; /* dtmf sequence */ -}; - #define DTMF_ST_IDLE 0 /* no DTMF active */ #define DTMF_ST_START 1 /* DTMF started, waiting for resp. */ #define DTMF_ST_MARK 2 /* wait tone duration */ @@ -108,6 +85,9 @@ struct gsm_call { #define GSM_TCHF_FRAME 0x0300 #define GSM_TCHF_FRAME_EFR 0x0301 +#define GSM_TCHH_FRAME 0x0302 +#define GSM_TCH_FRAME_AMR 0x0303 +#define GSM_BAD_FRAME 0x03ff #define GSM_MAX_FACILITY 128 #define GSM_MAX_SSVERSION 128 @@ -128,6 +108,8 @@ struct gsm_call { #define MNCC_F_KEYPAD 0x1000 #define MNCC_F_SIGNAL 0x2000 +struct osmocom_ms; + struct gsm_mncc { /* context based information */ uint32_t msg_type; @@ -174,6 +156,3 @@ struct gsm_data_frame { const char *get_mncc_name(int value); int mncc_recv(struct osmocom_ms *ms, int msg_type, void *arg); void mncc_set_cause(struct gsm_mncc *data, int loc, int val); - -#endif - diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h b/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h index 49ce1a42..05c539b0 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h +++ b/src/host/layer23/include/osmocom/bb/mobile/mncc_ms.h @@ -1,6 +1,39 @@ #pragma once -int mncc_call(struct osmocom_ms *ms, char *number); +#include <stdint.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> + +struct osmocom_ms; + +enum gsm_call_type { + GSM_CALL_T_UNKNOWN = 0, + GSM_CALL_T_VOICE, + GSM_CALL_T_DATA, /* UDI or 3.1 kHz audio */ + GSM_CALL_T_DATA_FAX, +}; + +struct gsm_call { + struct llist_head entry; + + struct osmocom_ms *ms; + + uint32_t callref; + enum gsm_call_type type; + + bool init; /* call initiated, no response yet */ + bool hold; /* call on hold */ + bool ring; /* call ringing/knocking */ + + struct osmo_timer_list dtmf_timer; + uint8_t dtmf_state; + uint8_t dtmf_index; + char dtmf[32]; /* dtmf sequence */ +}; + +int mncc_call(struct osmocom_ms *ms, const char *number, + enum gsm_call_type call_type); int mncc_hangup(struct osmocom_ms *ms); int mncc_answer(struct osmocom_ms *ms); int mncc_hold(struct osmocom_ms *ms); diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h deleted file mode 100644 index 57f23ee5..00000000 --- a/src/host/layer23/include/osmocom/bb/mobile/settings.h +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef _settings_h -#define _settings_h - -#define MOB_C7_DEFLT_ANY_TIMEOUT 30 - -/* TCH frame I/O handler */ -enum audio_io_handler { - /* No handler, drop frames */ - AUDIO_IOH_NONE = 0, - /* Return to sender */ - AUDIO_IOH_LOOPBACK, -}; - -extern const struct value_string audio_io_handler_names[]; -static inline const char *audio_io_handler_name(enum audio_io_handler val) -{ return get_value_string(audio_io_handler_names, val); } - -struct audio_settings { - enum audio_io_handler io_handler; -}; - -struct gsm_settings { - char layer2_socket_path[128]; - char sap_socket_path[128]; - - /* Audio settings */ - struct audio_settings audio; - - /* IMEI */ - char imei[16]; - char imeisv[17]; - char imei_random; - - /* network search */ - int plmn_mode; /* PLMN_MODE_* */ - - /* SIM */ - int sim_type; /* selects card on power on */ - char emergency_imsi[16]; - - /* SMS */ - char sms_sca[22]; - bool store_sms; - - /* test card simulator settings */ - char test_imsi[16]; - uint32_t test_tmsi; - uint8_t test_ki_type; - uint8_t test_ki[16]; /* 128 bit max */ - uint8_t test_barr; - uint8_t test_rplmn_valid; - uint16_t test_rplmn_mcc, test_rplmn_mnc; - uint16_t test_lac; - uint8_t test_imsi_attached; - uint8_t test_always; /* ...search hplmn... */ - - /* call related settings */ - uint8_t cw; /* set if call-waiting is allowed */ - uint8_t auto_answer; - uint8_t clip, clir; - uint8_t half, half_prefer; - - /* changing default behavior */ - uint8_t alter_tx_power; - uint8_t alter_tx_power_value; - int8_t alter_delay; - uint8_t stick; - uint16_t stick_arfcn; - uint8_t skip_max_per_band; - uint8_t no_lupd; - uint8_t no_neighbour; - - /* supported by configuration */ - uint8_t cc_dtmf; - uint8_t sms_ptp; - uint8_t a5_1; - uint8_t a5_2; - uint8_t a5_3; - uint8_t a5_4; - uint8_t a5_5; - uint8_t a5_6; - uint8_t a5_7; - uint8_t p_gsm; - uint8_t e_gsm; - uint8_t r_gsm; - uint8_t dcs; - uint8_t gsm_850; - uint8_t pcs; - uint8_t gsm_480; - uint8_t gsm_450; - uint8_t class_900; - uint8_t class_dcs; - uint8_t class_850; - uint8_t class_pcs; - uint8_t class_400; - uint8_t freq_map[128+38]; - uint8_t full_v1; - uint8_t full_v2; - uint8_t full_v3; - uint8_t half_v1; - uint8_t half_v3; - uint8_t ch_cap; /* channel capability */ - int8_t min_rxlev_dbm; /* min dBm to access */ - - /* radio */ - uint16_t dsc_max; - uint8_t force_rekey; - - /* dialing */ - struct llist_head abbrev; - - /* EDGE / UMTS / CDMA */ - uint8_t edge_ms_sup; - uint8_t edge_psk_sup; - uint8_t edge_psk_uplink; - uint8_t class_900_edge; - uint8_t class_dcs_pcs_edge; - uint8_t umts_fdd; - uint8_t umts_tdd; - uint8_t cdma_2000; - uint8_t dtm; - uint8_t class_dtm; - uint8_t dtm_mac; - uint8_t dtm_egprs; - - /* Timeout for GSM 03.22 C7 state */ - uint8_t any_timeout; -}; - -struct gsm_settings_abbrev { - struct llist_head list; - char abbrev[4]; - char number[32]; - char name[32]; -}; - -int gsm_settings_arfcn(struct osmocom_ms *ms); -int gsm_settings_init(struct osmocom_ms *ms); -int gsm_settings_exit(struct osmocom_ms *ms); -char *gsm_check_imei(const char *imei, const char *sv); -int gsm_random_imei(struct gsm_settings *set); - -#endif /* _settings_h */ - diff --git a/src/host/layer23/include/osmocom/bb/mobile/tch.h b/src/host/layer23/include/osmocom/bb/mobile/tch.h new file mode 100644 index 00000000..b026bd5d --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/tch.h @@ -0,0 +1,28 @@ +#pragma once + +struct osmocom_ms; +struct gsm_data_frame; +struct msgb; + +struct tch_state { + bool rx_only; /* Rx TCH frames, but Tx nothing */ + bool is_voice; /* voice (true) or data (false) */ + union { + struct tch_voice_state { + enum tch_voice_io_handler handler; + struct gapk_io_state *gapk_io; + } voice; + struct tch_data_state { + enum tch_data_io_handler handler; + struct tch_csd_sock_state *sock; + struct osmo_v110_ta *v110_ta; + struct osmo_soft_uart *suart; + unsigned int num_tx; + uint8_t e1e2e3[3]; + } data; + }; +}; + +int tch_init(struct osmocom_ms *ms); +int tch_send_msg(struct osmocom_ms *ms, struct msgb *msg); +int tch_send_mncc_frame(struct osmocom_ms *ms, const struct gsm_data_frame *frame); diff --git a/src/host/layer23/include/osmocom/bb/mobile/transaction.h b/src/host/layer23/include/osmocom/bb/mobile/transaction.h index 8c06d5d9..c1e94298 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/transaction.h +++ b/src/host/layer23/include/osmocom/bb/mobile/transaction.h @@ -27,7 +27,6 @@ struct gsm_trans { union { struct { - /* current call state */ int state; @@ -39,6 +38,7 @@ struct gsm_trans { int T308_second; /* used to send release again */ struct osmo_timer_list timer; struct gsm_mncc msg; /* stores setup/disconnect/release message */ + struct gsm_mncc_bearer_cap *bcap; } cc; struct { /* current supp.serv. state */ @@ -55,6 +55,26 @@ struct gsm_trans { struct gsm_sms *sms; } sms; + struct { + /* VGCS/VBS state machine */ + struct osmo_fsm_inst *fi; + + /* Call State (See Table 9.3 of TS 144.068) */ + uint8_t call_state; + + /* State attributes (See Table 9.7 of TS 144.068) */ + uint8_t d_att, u_att, comm, orig; + + /* Channel description last received via notification */ + bool ch_desc_present; + struct gsm48_chan_desc ch_desc; + + /* Flag to store termination request from upper layer. */ + bool termination; + + /* Flag to tell the state machine that call changes from separate link to group receive mode. */ + bool receive_after_sl; + } gcc; }; }; @@ -62,7 +82,7 @@ struct gsm_trans { struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, uint8_t proto, uint8_t trans_id); -struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, uint8_t protocol, uint32_t callref); struct gsm_trans *trans_alloc(struct osmocom_ms *ms, diff --git a/src/host/layer23/include/osmocom/bb/mobile/voice.h b/src/host/layer23/include/osmocom/bb/mobile/voice.h deleted file mode 100644 index a0524183..00000000 --- a/src/host/layer23/include/osmocom/bb/mobile/voice.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _voice_h -#define _voice_h - -int gsm_voice_init(struct osmocom_ms *ms); -int gsm_send_voice(struct osmocom_ms *ms, struct gsm_data_frame *data); - -#endif /* _voice_h */ diff --git a/src/host/layer23/include/osmocom/bb/mobile/vty.h b/src/host/layer23/include/osmocom/bb/mobile/vty.h index d0668041..f03012e5 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/vty.h +++ b/src/host/layer23/include/osmocom/bb/mobile/vty.h @@ -6,16 +6,16 @@ #include <osmocom/vty/buffer.h> #include <osmocom/vty/command.h> +#include <osmocom/bb/common/vty.h> + enum ms_vty_node { - MS_NODE = _LAST_OSMOVTY_NODE + 1, - TESTSIM_NODE, - SUPPORT_NODE, - AUDIO_NODE, + SUPPORT_NODE = _LAST_L23VTY_NODE + 1, + TCH_VOICE_NODE, + TCH_DATA_NODE, + VGCS_NODE, + VBS_NODE, }; -int ms_vty_go_parent(struct vty *vty); int ms_vty_init(void); -extern void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - #endif diff --git a/src/host/layer23/include/osmocom/bb/modem/Makefile.am b/src/host/layer23/include/osmocom/bb/modem/Makefile.am new file mode 100644 index 00000000..2f69d195 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/Makefile.am @@ -0,0 +1,10 @@ +noinst_HEADERS = \ + modem.h \ + gmm.h \ + grr.h \ + llc.h \ + rlcmac.h \ + sm.h \ + sndcp.h \ + vty.h \ + $(NULL) diff --git a/src/host/layer23/include/osmocom/bb/modem/gmm.h b/src/host/layer23/include/osmocom/bb/modem/gmm.h new file mode 100644 index 00000000..41e1e7e6 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/gmm.h @@ -0,0 +1,13 @@ +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +struct osmocom_ms; + +int modem_gmm_init(struct osmocom_ms *ms); + +int modem_gmm_gmmreg_attach_req(const struct osmocom_ms *ms); +int modem_gmm_gmmreg_detach_req(const struct osmocom_ms *ms); +int modem_gmm_gmmreg_sim_auth_rsp(const struct osmocom_ms *ms, + uint8_t *sres, uint8_t *kc, uint8_t kc_len); diff --git a/src/host/layer23/include/osmocom/bb/modem/grr.h b/src/host/layer23/include/osmocom/bb/modem/grr.h new file mode 100644 index 00000000..a86a089b --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/grr.h @@ -0,0 +1,35 @@ +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +struct msgb; +struct osmocom_ms; +struct lapdm_entity; +struct osmo_fsm; + +enum grr_fsm_state { + GRR_ST_PACKET_NOT_READY, + GRR_ST_PACKET_IDLE, + GRR_ST_PACKET_ACCESS, + GRR_ST_PACKET_TRANSFER, +}; + +enum grr_fsm_event { + GRR_EV_BCCH_BLOCK_IND, + GRR_EV_PCH_AGCH_BLOCK_IND, + GRR_EV_CHAN_ACCESS_REQ, + GRR_EV_CHAN_ACCESS_CNF, + GRR_EV_PDCH_ESTABLISH_REQ, + GRR_EV_PDCH_RELEASE_REQ, + GRR_EV_PDCH_UL_TBF_CFG_REQ, + GRR_EV_PDCH_DL_TBF_CFG_REQ, + GRR_EV_PDCH_BLOCK_REQ, + GRR_EV_PDCH_BLOCK_CNF, + GRR_EV_PDCH_BLOCK_IND, + GRR_EV_PDCH_RTS_IND, +}; + +extern struct osmo_fsm grr_fsm_def; + +int modem_grr_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx); diff --git a/src/host/layer23/include/osmocom/bb/modem/llc.h b/src/host/layer23/include/osmocom/bb/modem/llc.h new file mode 100644 index 00000000..5fe5d49f --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/llc.h @@ -0,0 +1,8 @@ +#pragma once + +#include <stdbool.h> + +struct osmocom_ms; + +int modem_llc_init(struct osmocom_ms *ms, const char *cipher_plugin_path); + diff --git a/src/host/layer23/include/osmocom/bb/modem/modem.h b/src/host/layer23/include/osmocom/bb/modem/modem.h new file mode 100644 index 00000000..7135bf71 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/modem.h @@ -0,0 +1,19 @@ +#pragma once + +#include <stdbool.h> + +int modem_start(void); +int modem_gprs_attach_if_needed(struct osmocom_ms *ms); +int modem_sync_to_cell(struct osmocom_ms *ms); + +enum modem_state { + MODEM_ST_IDLE, + MODEM_ST_ATTACHING, + MODEM_ST_ATTACHED +}; + +struct modem_app { + struct osmocom_ms *ms; + enum modem_state modem_state; +}; +extern struct modem_app app_data; diff --git a/src/host/layer23/include/osmocom/bb/modem/rlcmac.h b/src/host/layer23/include/osmocom/bb/modem/rlcmac.h new file mode 100644 index 00000000..d1b054f8 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/rlcmac.h @@ -0,0 +1,8 @@ +#pragma once + +#include <stdbool.h> + +struct osmocom_ms; + +int modem_rlcmac_init(struct osmocom_ms *ms); + diff --git a/src/host/layer23/include/osmocom/bb/modem/sm.h b/src/host/layer23/include/osmocom/bb/modem/sm.h new file mode 100644 index 00000000..b1a5df5a --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/sm.h @@ -0,0 +1,8 @@ +#pragma once + +#include <stdbool.h> + +struct osmocom_ms; + +int modem_sm_init(struct osmocom_ms *ms); +int modem_sm_smreg_pdp_act_req(const struct osmocom_ms *ms, const struct osmobb_apn *apn); diff --git a/src/host/layer23/include/osmocom/bb/modem/sndcp.h b/src/host/layer23/include/osmocom/bb/modem/sndcp.h new file mode 100644 index 00000000..b2e6f0fd --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/sndcp.h @@ -0,0 +1,10 @@ +#pragma once + +#include <stdbool.h> + +struct osmocom_ms; +struct osmobb_apn; + +int modem_sndcp_init(struct osmocom_ms *ms); +int modem_sndcp_sn_xid_req(struct osmobb_apn *apn); +int modem_sndcp_sn_unitdata_req(struct osmobb_apn *apn, uint8_t *npdu, size_t npdu_len); diff --git a/src/host/layer23/include/osmocom/bb/modem/vty.h b/src/host/layer23/include/osmocom/bb/modem/vty.h new file mode 100644 index 00000000..5b69af60 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/modem/vty.h @@ -0,0 +1,10 @@ +#pragma once + +#include <osmocom/bb/common/vty.h> + +enum modem_vty_node { + APN_NODE = _LAST_L23VTY_NODE + 1, +}; + +int modem_vty_init(void); +int modem_vty_go_parent(struct vty *vty); diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am index 58a5f7fb..3b6a4d8b 100644 --- a/src/host/layer23/src/Makefile.am +++ b/src/host/layer23/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = common misc mobile +SUBDIRS = common misc mobile modem diff --git a/src/host/layer23/src/common/Makefile.am b/src/host/layer23/src/common/Makefile.am index a8d9f7e7..8f5aebaa 100644 --- a/src/host/layer23/src/common/Makefile.am +++ b/src/host/layer23/src/common/Makefile.am @@ -1,6 +1,37 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS) +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOGPRSRLCMAC_CFLAGS) \ + $(LIBOSMOGPRSLLC_CFLAGS) \ + $(LIBOSMOGPRSSNDCP_CFLAGS) \ + $(LIBGPS_CFLAGS) \ + $(NULL) noinst_LIBRARIES = liblayer23.a -liblayer23_a_SOURCES = l1ctl.c l1l2_interface.c sap_fsm.c sap_proto.c sap_interface.c \ - logging.c networks.c sim.c sysinfo.c gps.c l1ctl_lapdm_glue.c utils.c +liblayer23_a_SOURCES = \ + apn.c \ + apn_fsm.c \ + gps.c \ + l1ctl.c \ + l1l2_interface.c \ + l1ctl_lapdm_glue.c \ + logging.c \ + ms.c \ + networks.c \ + sap_fsm.c \ + sap_proto.c \ + sap_interface.c \ + settings.c \ + sim.c \ + subscriber.c \ + support.c \ + sysinfo.c \ + utils.c \ + vty.c \ + $(NULL) diff --git a/src/host/layer23/src/common/apn.c b/src/host/layer23/src/common/apn.c new file mode 100644 index 00000000..cefa7641 --- /dev/null +++ b/src/host/layer23/src/common/apn.c @@ -0,0 +1,121 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#include <talloc.h> + +#include <osmocom/gprs/sm/sm.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/l23_app.h> + +struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name) +{ + struct osmobb_apn *apn; + apn = talloc_zero(ms, struct osmobb_apn); + if (!apn) + return NULL; + + if (apn_fsm_ctx_init(&apn->fsm, apn) != 0) + goto ret_free; + + talloc_set_name(apn, "apn_%s", name); + apn->cfg.name = talloc_strdup(apn, name); + apn->cfg.shutdown = true; + apn->cfg.tx_gpdu_seq = true; + + apn->tun = osmo_tundev_alloc(apn, name); + if (!apn->tun) + goto ret_free_fsm; + osmo_tundev_set_priv_data(apn->tun, apn); + + apn->ms = ms; + /* FIXME: may want to configure or pick free one in the future: */ + apn->pdp.nsapi = 6; + apn->pdp.llc_sapi = OSMO_GPRS_SM_LLC_SAPI_SAPI3; + + /* QoS zeroed to 14 bytes is a valid QoS seen sent by some phones. Use + * that as default for now. */ + apn->pdp.qos_len = 14; + + llist_add_tail(&apn->list, &ms->gprs.apn_list); + return apn; + +ret_free_fsm: + apn_fsm_ctx_release(&apn->fsm); +ret_free: + talloc_free(apn); + return NULL; +} + +void apn_free(struct osmobb_apn *apn) +{ + apn_fsm_ctx_release(&apn->fsm); + llist_del(&apn->list); + osmo_tundev_free(apn->tun); + talloc_free(apn); +} + +int apn_start(struct osmobb_apn *apn) +{ + int rc; + + if (apn->started) + return 0; + + LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->cfg.dev_name); + /* Set TUN library callback. Must have been configured by the app: */ + OSMO_ASSERT(l23_app_info.tun_data_ind_cb); + osmo_tundev_set_data_ind_cb(apn->tun, l23_app_info.tun_data_ind_cb); + osmo_tundev_set_dev_name(apn->tun, apn->cfg.dev_name); + osmo_tundev_set_netns_name(apn->tun, apn->cfg.dev_netns_name); + + rc = osmo_tundev_open(apn->tun); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n"); + return -1; + } + + LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", osmo_tundev_get_dev_name(apn->tun)); + + LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n"); + apn->started = true; + return 0; +} + +int apn_stop(struct osmobb_apn *apn) +{ + LOGPAPN(LOGL_NOTICE, apn, "Stopping\n"); + + /* shutdown whatever old state might be left */ + if (apn->tun) { + /* release tun device */ + LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", + osmo_tundev_get_dev_name(apn->tun)); + osmo_tundev_close(apn->tun); + } + + apn->started = false; + return 0; +} diff --git a/src/host/layer23/src/common/apn_fsm.c b/src/host/layer23/src/common/apn_fsm.c new file mode 100644 index 00000000..2b523f1d --- /dev/null +++ b/src/host/layer23/src/common/apn_fsm.c @@ -0,0 +1,244 @@ +/* Lifecycle of an APN */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include <errno.h> +#include <osmocom/core/tdef.h> +#include <osmocom/core/utils.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/apn_fsm.h> +#include <osmocom/bb/common/apn.h> +#define X(s) (1 << (s)) + +static struct osmo_tdef T_defs_apn[] = { + { .T=1, .default_val=65, .desc = "Activating timeout (s)" }, + { 0 } /* empty item at the end */ +}; + +static const struct osmo_tdef_state_timeout apn_fsm_timeouts[32] = { + [APN_ST_DISABLED] = {}, + [APN_ST_INACTIVE] = {}, + [APN_ST_ACTIVATING] = { .T=1 }, + [APN_ST_ACTIVE] = {}, +}; + +#define apn_fsm_state_chg(fi, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, apn_fsm_timeouts, T_defs_apn, -1) + +static void st_apn_disabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv; + + apn_stop(ctx->apn); +} + +static void st_apn_disabled(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case APN_EV_GPRS_ALLOWED: + if (*((bool *)data) == true) + apn_fsm_state_chg(fi, APN_ST_INACTIVE); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_apn_inactive_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv; + + int rc = apn_start(ctx->apn); + if (rc < 0) + apn_fsm_state_chg(fi, APN_ST_DISABLED); + + /* FIXME: Here once we find a way to store whether the ms object is GMM + attached, we can transition directly to ACTIVATING. */ +} + +static void st_apn_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case APN_EV_GPRS_ALLOWED: + if (*((bool *)data) == false) + apn_fsm_state_chg(fi, APN_ST_DISABLED); + break; + case APN_EV_GMM_ATTACHED: + apn_fsm_state_chg(fi, APN_ST_ACTIVATING); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_apn_activating_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + /* FIXME: We could send SMREG-PDP_ACT.req from here. Right now that's done by the app. */ +} + +static void st_apn_activating(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case APN_EV_GPRS_ALLOWED: + /* TODO: Tx PDP DEACT ACC */ + apn_fsm_state_chg(fi, APN_ST_DISABLED); + break; + case APN_EV_GMM_DETACHED: + apn_fsm_state_chg(fi, APN_ST_INACTIVE); + break; + case APN_EV_RX_SM_ACT_PDP_CTX_REJ: + apn_fsm_state_chg(fi, APN_ST_INACTIVE); + break; + case APN_EV_RX_SM_ACT_PDP_CTX_ACC: + apn_fsm_state_chg(fi, APN_ST_ACTIVE); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_apn_active_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct apn_fsm_ctx *ctx = (struct apn_fsm_ctx *)fi->priv; + struct osmo_netdev *netdev; + + netdev = osmo_tundev_get_netdev(ctx->apn->tun); + osmo_netdev_ifupdown(netdev, true); +} + +static void st_apn_active(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case APN_EV_GPRS_ALLOWED: + /* TODO: Tx PDP DEACT ACC */ + apn_fsm_state_chg(fi, APN_ST_DISABLED); + break; + case APN_EV_GMM_DETACHED: + apn_fsm_state_chg(fi, APN_ST_INACTIVE); + break; + case APN_EV_RX_SM_DEACT_PDP_CTX_ACC: + apn_fsm_state_chg(fi, APN_ST_INACTIVE); + break; + default: + OSMO_ASSERT(0); + } +} + +static int apn_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + switch (fi->T) { + case 1: + apn_fsm_state_chg(fi, APN_ST_INACTIVE); + break; + default: + OSMO_ASSERT(0); + } + return 0; +} + +static struct osmo_fsm_state apn_fsm_states[] = { + [APN_ST_DISABLED] = { + .in_event_mask = + X(APN_EV_GPRS_ALLOWED), + .out_state_mask = + X(APN_ST_INACTIVE), + .name = "DISABLED", + .onenter = st_apn_disabled_on_enter, + .action = st_apn_disabled, + }, + [APN_ST_INACTIVE] = { + .in_event_mask = + X(APN_EV_GPRS_ALLOWED) | + X(APN_EV_GMM_ATTACHED), + .out_state_mask = + X(APN_ST_ACTIVATING), + .name = "INACTIVE", + .onenter = st_apn_inactive_on_enter, + .action = st_apn_inactive, + }, + [APN_ST_ACTIVATING] = { + .in_event_mask = + X(APN_EV_GPRS_ALLOWED) | + X(APN_EV_GMM_DETACHED) | + X(APN_EV_RX_SM_ACT_PDP_CTX_REJ) | + X(APN_EV_RX_SM_ACT_PDP_CTX_ACC), + .out_state_mask = + X(APN_ST_DISABLED) | + X(APN_ST_INACTIVE) | + X(APN_ST_ACTIVE), + .name = "ACTIVATING", + .onenter = st_apn_activating_on_enter, + .action = st_apn_activating, + }, + [APN_ST_ACTIVE] = { + .in_event_mask = + X(APN_EV_GPRS_ALLOWED) | + X(APN_EV_GMM_DETACHED)| + X(APN_EV_RX_SM_DEACT_PDP_CTX_ACC), + .out_state_mask = + X(APN_ST_DISABLED) | + X(APN_ST_INACTIVE), + .name = "ACTIVE", + .onenter = st_apn_active_on_enter, + .action = st_apn_active, + }, +}; + +const struct value_string apn_fsm_event_names[] = { + { APN_EV_GPRS_ALLOWED, "GPRS_ALLOWED" }, + { APN_EV_GMM_ATTACHED, "GMM_ATTACHED" }, + { APN_EV_GMM_DETACHED, "GMM_DETACHED" }, + { APN_EV_RX_SM_ACT_PDP_CTX_REJ, "ACT_PDP_CTX_REJ" }, + { APN_EV_RX_SM_ACT_PDP_CTX_ACC, "ACT_PDP_CTX_ACC" }, + { APN_EV_RX_SM_DEACT_PDP_CTX_ACC, "DEACT_PDP_CTX_ACC" }, + { 0, NULL } +}; + +struct osmo_fsm apn_fsm = { + .name = "APN", + .states = apn_fsm_states, + .num_states = ARRAY_SIZE(apn_fsm_states), + .timer_cb = apn_fsm_timer_cb, + .event_names = apn_fsm_event_names, + .log_subsys = DTUN, +}; + +int apn_fsm_ctx_init(struct apn_fsm_ctx *ctx, struct osmobb_apn *apn) +{ + ctx->apn = apn; + ctx->fi = osmo_fsm_inst_alloc(&apn_fsm, apn, ctx, LOGL_INFO, NULL); + if (!ctx->fi) + return -ENODATA; + + return 0; +} + +void apn_fsm_ctx_release(struct apn_fsm_ctx *ctx) +{ + osmo_fsm_inst_free(ctx->fi); +} + +static __attribute__((constructor)) void apn_fsm_init(void) +{ + OSMO_ASSERT(osmo_fsm_register(&apn_fsm) == 0); + osmo_tdefs_reset(T_defs_apn); +} diff --git a/src/host/layer23/src/common/gps.c b/src/host/layer23/src/common/gps.c index 5225fe0e..3c69352c 100644 --- a/src/host/layer23/src/common/gps.c +++ b/src/host/layer23/src/common/gps.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -33,6 +29,7 @@ #endif #include <osmocom/core/utils.h> +#include <osmocom/core/select.h> #include <osmocom/bb/common/osmocom_data.h> #include <osmocom/bb/common/logging.h> diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index da30767c..94979103 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -41,44 +37,20 @@ #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/lapdm.h> +#include <osmocom/gsm/gsm0502.h> #include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1l2_interface.h> -#include <osmocom/gsm/lapdm.h> #include <osmocom/bb/common/logging.h> -extern struct gsmtap_inst *gsmtap_inst; - -#define CB_FCCH -1 -#define CB_SCH -2 -#define CB_BCCH -3 -#define CB_IDLE -4 - -/* according to TS 05.02 Clause 7 Table 3 of 9 an Figure 8a */ -static const int ccch_block_table[51] = { - CB_FCCH, CB_SCH,/* 0..1 */ - CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */ - 0, 0, 0, 0, /* 6..9: B0 */ - CB_FCCH, CB_SCH,/* 10..11 */ - 1, 1, 1, 1, /* 12..15: B1 */ - 2, 2, 2, 2, /* 16..19: B2 */ - CB_FCCH, CB_SCH,/* 20..21 */ - 3, 3, 3, 3, /* 22..25: B3 */ - 4, 4, 4, 4, /* 26..29: B4 */ - CB_FCCH, CB_SCH,/* 30..31 */ - 5, 5, 5, 5, /* 32..35: B5 */ - 6, 6, 6, 6, /* 36..39: B6 */ - CB_FCCH, CB_SCH,/* 40..41 */ - 7, 7, 7, 7, /* 42..45: B7 */ - 8, 8, 8, 8, /* 46..49: B8 */ - -4 /* 50: Idle */ -}; - /* determine the CCCH block number based on the frame number */ static unsigned int fn2ccch_block(uint32_t fn) { - int rc = ccch_block_table[fn%51]; + int rc = gsm0502_fn2ccch_block(fn); /* if FN is negative, we were called for something that's not CCCH! */ OSMO_ASSERT(rc >= 0); return rc; @@ -86,7 +58,7 @@ static unsigned int fn2ccch_block(uint32_t fn) static uint8_t chantype_rsl2gsmtap_ext(uint8_t rsl_chantype, uint8_t link_id, uint32_t fn, uint8_t num_agch) { - uint8_t ret = chantype_rsl2gsmtap(rsl_chantype, link_id); + uint8_t ret = chantype_rsl2gsmtap2(rsl_chantype, link_id, false); if (ret != GSMTAP_CHANNEL_PCH) return ret; @@ -233,7 +205,13 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) ccch = (struct l1ctl_data_ind *) msg->l2h; gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr)); - rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts); + if (rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts) != 0) { + LOGP(DL1C, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, dl->chan_nr); + return -EINVAL; + } + DEBUGP(DL1C, "%s (%.4u/%.2u/%.2u) %d dBm: %s\n", rsl_chan_nr_str(dl->chan_nr), tm.t1, tm.t2, tm.t3, (int)dl->rx_level-110, @@ -314,7 +292,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) * to clog up your logs */ if (!is_fill_frame(gsmtap_chan_type, ccch->data)) { /* send CCCH data via GSMTAP */ - gsmtap_send(gsmtap_inst, ntohs(dl->band_arfcn), chan_ts, + gsmtap_send(l23_cfg.gsmtap.inst, ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss, tm.fn, dl->rx_level-110, dl->snr, ccch->data, sizeof(ccch->data)); } @@ -339,6 +317,7 @@ static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) PRIM_OP_INDICATION, msg); pp.u.data.chan_nr = dl->chan_nr; pp.u.data.link_id = dl->link_id; + pp.u.data.fn = tm.fn; /* send it up into LAPDm */ return lapdm_phsap_up(&pp.oph, le); @@ -378,7 +357,6 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, struct l1ctl_hdr *l1h; struct l1ctl_info_ul *l1i_ul; uint8_t chan_type, chan_ts, chan_ss; - uint8_t gsmtap_chan_type; DEBUGP(DL1C, "(%s)\n", osmo_hexdump(msg->l2h, msgb_l2len(msg))); @@ -390,11 +368,16 @@ int l1ctl_tx_data_req(struct osmocom_ms *ms, struct msgb *msg, } /* send copy via GSMTAP */ - rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts); - gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id); - gsmtap_send(gsmtap_inst, ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK, - chan_ts, gsmtap_chan_type, chan_ss, 0, 127, 255, - msg->l2h, msgb_l2len(msg)); + if (rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts) == 0) { + uint8_t gsmtap_chan_type = chantype_rsl2gsmtap2(chan_type, link_id, false); + gsmtap_send(l23_cfg.gsmtap.inst, ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK, + chan_ts, gsmtap_chan_type, chan_ss, 0, 127, 0, + msg->l2h, msgb_l2len(msg)); + } else { + LOGP(DL1C, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, chan_nr); + } /* prepend uplink info header */ l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul)); @@ -460,8 +443,8 @@ int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode) } /* Transmit L1CTL_TCH_MODE_REQ */ -int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, - uint8_t audio_mode, uint8_t tch_loop_mode) +int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags, + uint8_t tch_loop_mode) { struct msgb *msg; struct l1ctl_tch_mode_req *req; @@ -475,7 +458,9 @@ int l1ctl_tx_tch_mode_req(struct osmocom_ms *ms, uint8_t tch_mode, req = (struct l1ctl_tch_mode_req *) msgb_put(msg, sizeof(*req)); req->tch_mode = tch_mode; req->audio_mode = audio_mode; + req->tch_flags = tch_flags; req->tch_loop_mode = tch_loop_mode; + /* TODO: Set AMR codec in req if req->tch_mode==GSM48_CMODE_SPEECH_AMR */ return osmo_send_l1(ms, msg); } @@ -527,8 +512,9 @@ int l1ctl_tx_crypto_req(struct osmocom_ms *ms, uint8_t chan_nr, } /* Transmit L1CTL_RACH_REQ */ -int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, - uint8_t combined) +int l1ctl_tx_rach_req(struct osmocom_ms *ms, + uint8_t chan_nr, uint8_t link_id, + uint8_t ra, uint16_t offset, uint8_t combined, uint8_t uic) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -538,20 +524,22 @@ int l1ctl_tx_rach_req(struct osmocom_ms *ms, uint8_t ra, uint16_t offset, if (!msg) return -1; - DEBUGP(DL1C, "RACH Req. offset=%d combined=%d\n", offset, combined); + DEBUGP(DL1C, "RACH Req. offset=%d combined=%d uic=0x%02x\n", offset, combined, uic); ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = chan_nr; + ul->link_id = link_id; req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req)); req->ra = ra; req->offset = htons(offset); req->combined = combined; + req->uic = uic; return osmo_send_l1(ms, msg); } /* Transmit L1CTL_DM_EST_REQ */ -int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, - uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, - uint8_t audio_mode) +int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, + uint8_t audio_mode, uint8_t tch_flags) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -574,14 +562,13 @@ int l1ctl_tx_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, req->h0.band_arfcn = htons(band_arfcn); req->tch_mode = tch_mode; req->audio_mode = audio_mode; + req->tch_flags = tch_flags; return osmo_send_l1(ms, msg); } -int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, - uint16_t *ma, uint8_t ma_len, - uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, - uint8_t audio_mode) +int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, uint16_t *ma, uint8_t ma_len, + uint8_t chan_nr, uint8_t tsc, uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags) { struct msgb *msg; struct l1ctl_info_ul *ul; @@ -609,6 +596,7 @@ int l1ctl_tx_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, req->h1.ma[i] = htons(ma[i]); req->tch_mode = tch_mode; req->audio_mode = audio_mode; + req->tch_flags = tch_flags; return osmo_send_l1(ms, msg); } @@ -726,10 +714,7 @@ int l1ctl_tx_sim_req(struct osmocom_ms *ms, uint8_t *data, uint16_t length) /* just forward the SIM response to the SIM handler */ static int rx_l1_sim_conf(struct osmocom_ms *ms, struct msgb *msg) { - uint16_t len = msgb_l2len(msg); - uint8_t *data = msg->data; - - LOGP(DL1C, LOGL_INFO, "SIM %s\n", osmo_hexdump(data, len)); + LOGP(DL1C, LOGL_INFO, "SIM %s\n", msgb_hexdump_l1(msg)); sim_apdu_resp(ms, msg); @@ -847,6 +832,7 @@ static int rx_l1_tch_mode_conf(struct osmocom_ms *ms, struct msgb *msg) mc.tch_mode = conf->tch_mode; mc.audio_mode = conf->audio_mode; + mc.tch_flags = conf->tch_flags; mc.ms = ms; osmo_signal_dispatch(SS_L1CTL, S_L1CTL_TCH_MODE_CONF, &mc); @@ -858,7 +844,6 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg) { struct l1ctl_info_dl *dl; struct l1ctl_traffic_ind *ti; - size_t frame_len; uint8_t *frame; if (msgb_l1len(msg) < sizeof(*dl)) { @@ -875,11 +860,8 @@ static int rx_l1_traffic_ind(struct osmocom_ms *ms, struct msgb *msg) msg->l2h = dl->payload; msg->l3h = frame; - /* Calculate the frame length */ - frame_len = msgb_l3len(msg); - - DEBUGP(DL1C, "TRAFFIC IND len=%zu (%s)\n", frame_len, - osmo_hexdump(frame, frame_len)); + LOGP(DL1C, LOGL_DEBUG, "Rx TRAFFIC.ind (fn=%u, chan_nr=0x%02x, len=%u): %s\n", + ntohl(dl->frame_nr), dl->chan_nr, msgb_l3len(msg), msgb_hexdump_l3(msg)); /* distribute or drop */ if (ms->l1_entity.l1_traffic_ind) @@ -896,7 +878,6 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, struct l1ctl_hdr *l1h; struct l1ctl_info_ul *l1i_ul; struct l1ctl_traffic_req *tr; - size_t frame_len; uint8_t *frame; /* Header handling */ @@ -904,13 +885,8 @@ int l1ctl_tx_traffic_req(struct osmocom_ms *ms, struct msgb *msg, frame = (uint8_t *) tr->data; msg->l3h = frame; - /* Calculate the frame length */ - frame_len = msgb_l3len(msg); - - DEBUGP(DL1C, "TRAFFIC REQ len=%zu (%s)\n", frame_len, - osmo_hexdump(frame, frame_len)); - -// printf("TX %s\n", osmo_hexdump(frame, frame_len)); + LOGP(DL1C, LOGL_DEBUG, "Tx TRAFFIC.req (chan_nr=0x%02x, len=%u): %s\n", + chan_nr, msgb_l3len(msg), msgb_hexdump_l3(msg)); /* prepend uplink info header */ l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul)); @@ -973,6 +949,190 @@ static int rx_l1_neigh_pm_ind(struct osmocom_ms *ms, struct msgb *msg) return 0; } +/* Receive L1CTL_GPRS_UL_BLOCK_CNF */ +static int rx_l1_gprs_ul_block_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_ul_block_cnf *cnf = (void *)msg->l1h; + + if (msgb_l1len(msg) < sizeof(*cnf)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS UL BLOCK.cnf (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*cnf)); + return -EINVAL; + } + if (OSMO_UNLIKELY(cnf->tn >= 8)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS UL BLOCK.cnf (tn=%u)\n", cnf->tn); + return -EINVAL; + } + + msg->l2h = (void *)&cnf->data[0]; + + DEBUGP(DL1C, "Rx GPRS UL BLOCK.cnf (fn=%u, tn=%u, len=%u): %s\n", + ntohl(cnf->fn), cnf->tn, msgb_l2len(msg), msgb_hexdump_l2(msg)); + + /* distribute or drop */ + if (ms->l1_entity.l1_gprs_ul_block_cnf) + return ms->l1_entity.l1_gprs_ul_block_cnf(ms, msg); + + msgb_free(msg); + return 0; +} + +/* Receive L1CTL_GPRS_DL_BLOCK_IND */ +static int rx_l1_gprs_dl_block_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_dl_block_ind *ind = (void *)msg->l1h; + uint8_t gsmtap_chan; + uint32_t fn; + + if (msgb_l1len(msg) < sizeof(*ind)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS DL BLOCK.ind (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*ind)); + return -EINVAL; + } + if (OSMO_UNLIKELY(ind->hdr.tn >= 8)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS DL BLOCK.ind (tn=%u)\n", + ind->hdr.tn); + return -EINVAL; + } + + msg->l2h = (void *)&ind->data[0]; + + fn = ntohl(ind->hdr.fn); + if ((fn % 104) == 12) + gsmtap_chan = GSMTAP_CHANNEL_PTCCH; + else + gsmtap_chan = GSMTAP_CHANNEL_PDTCH; + + gsmtap_send(l23_cfg.gsmtap.inst, + ms->rrlayer.cd_now.arfcn, + ind->hdr.tn, gsmtap_chan, 0, fn, + rxlev2dbm(ind->meas.rx_lev), 0, + msgb_l2(msg), msgb_l2len(msg)); + + DEBUGP(DL1C, "Rx GPRS DL BLOCK.ind (fn=%u, tn=%u, len=%u): %s\n", + fn, ind->hdr.tn, msgb_l2len(msg), msgb_hexdump_l2(msg)); + + /* distribute or drop */ + if (ms->l1_entity.l1_gprs_dl_block_ind) + return ms->l1_entity.l1_gprs_dl_block_ind(ms, msg); + + msgb_free(msg); + return 0; +} + +/* Receive L1CTL_GPRS_RTS_IND */ +static int rx_l1_gprs_rts_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_rts_ind *ind = (void *)msg->l1h; + + if (msgb_l1len(msg) < sizeof(*ind)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS RTS.ind (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*ind)); + return -EINVAL; + } + if (OSMO_UNLIKELY(ind->tn >= 8)) { + LOGP(DL1C, LOGL_ERROR, + "Rx malformed GPRS RTS.ind (tn=%u)\n", + ind->tn); + return -EINVAL; + } + + DEBUGP(DL1C, "Rx GPRS RTS.ind (fn=%u, tn=%u, usf=%u)\n", + ntohl(ind->fn), ind->tn, ind->usf); + + /* distribute or drop */ + if (ms->l1_entity.l1_gprs_rts_ind) + return ms->l1_entity.l1_gprs_rts_ind(ms, msg); + + msgb_free(msg); + return 0; +} + +/* Transmit L1CTL_GPRS_UL_BLOCK_REQ */ +int l1ctl_tx_gprs_ul_block_req(struct osmocom_ms *ms, uint32_t fn, uint8_t tn, + const uint8_t *data, size_t data_len) +{ + struct l1ctl_gprs_ul_block_req *req; + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_GPRS_UL_BLOCK_REQ); + if (!msg) + return -ENOMEM; + + req = (void *)msgb_put(msg, sizeof(*req)); + req->hdr.fn = htonl(fn); + req->hdr.tn = tn; + if (data_len > 0) + memcpy(msgb_put(msg, data_len), data, data_len); + + DEBUGP(DL1C, "Tx GPRS UL block (fn=%u, tn=%u, len=%zu): %s\n", + fn, tn, data_len, osmo_hexdump(data, data_len)); + + gsmtap_send(l23_cfg.gsmtap.inst, + ms->rrlayer.cd_now.arfcn | GSMTAP_ARFCN_F_UPLINK, + tn, GSMTAP_CHANNEL_PDTCH, 0, fn, 127, 0, + data, data_len); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */ +int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn) +{ + struct l1ctl_gprs_ul_tbf_cfg_req *req; + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_GPRS_UL_TBF_CFG_REQ); + if (!msg) + return -ENOMEM; + + req = (void *)msgb_put(msg, sizeof(*req)); + *req = (struct l1ctl_gprs_ul_tbf_cfg_req) { + .tbf_ref = tbf_ref, + .slotmask = slotmask, + .start_fn = htonl(start_fn), + }; + + DEBUGP(DL1C, "Tx GPRS UL TBF CFG: " + "tbf_ref=%u, slotmask=0x%02x, start_fn=%u\n", + tbf_ref, slotmask, start_fn); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_GPRS_DL_TBF_CFG_REQ */ +int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn, + uint8_t dl_tfi) +{ + struct l1ctl_gprs_dl_tbf_cfg_req *req; + struct msgb *msg; + + msg = osmo_l1_alloc(L1CTL_GPRS_DL_TBF_CFG_REQ); + if (!msg) + return -ENOMEM; + + req = (void *)msgb_put(msg, sizeof(*req)); + *req = (struct l1ctl_gprs_dl_tbf_cfg_req) { + .tbf_ref = tbf_ref, + .slotmask = slotmask, + .start_fn = htonl(start_fn), + .dl_tfi = dl_tfi, + }; + + DEBUGP(DL1C, "Tx GPRS DL TBF CFG: " + "tbf_ref=%u, slotmask=0x%02x, start_fn=%u, dl_tfi=%u)\n", + tbf_ref, slotmask, start_fn, dl_tfi); + + return osmo_send_l1(ms, msg); +} + /* Receive incoming data from L1 using L1CTL format */ int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) { @@ -1041,6 +1201,15 @@ int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) case L1CTL_TRAFFIC_CONF: msgb_free(msg); break; + case L1CTL_GPRS_UL_BLOCK_CNF: + rc = rx_l1_gprs_ul_block_cnf(ms, msg); + break; + case L1CTL_GPRS_DL_BLOCK_IND: + rc = rx_l1_gprs_dl_block_ind(ms, msg); + break; + case L1CTL_GPRS_RTS_IND: + rc = rx_l1_gprs_rts_ind(ms, msg); + break; default: LOGP(DL1C, LOGL_ERROR, "Unknown MSG: %u\n", hdr->msg_type); msgb_free(msg); diff --git a/src/host/layer23/src/common/l1ctl_lapdm_glue.c b/src/host/layer23/src/common/l1ctl_lapdm_glue.c index 0b2a8ed5..458cc81f 100644 --- a/src/host/layer23/src/common/l1ctl_lapdm_glue.c +++ b/src/host/layer23/src/common/l1ctl_lapdm_glue.c @@ -14,16 +14,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> +#include <errno.h> #include <l1ctl_proto.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/gsm/prim.h> #include <osmocom/bb/common/l1ctl.h> @@ -50,9 +48,12 @@ int l1ctl_ph_prim_cb(struct osmo_prim_hdr *oph, void *ctx) case PRIM_PH_RACH: l1ctl_tx_param_req(ms, pp->u.rach_req.ta, pp->u.rach_req.tx_power); - rc = l1ctl_tx_rach_req(ms, pp->u.rach_req.ra, + rc = l1ctl_tx_rach_req(ms, + RSL_CHAN_RACH, 0x00, + pp->u.rach_req.ra, pp->u.rach_req.offset, - pp->u.rach_req.is_combined_ccch); + pp->u.rach_req.is_combined_ccch, + 0xff); break; default: rc = -EINVAL; diff --git a/src/host/layer23/src/common/l1l2_interface.c b/src/host/layer23/src/common/l1l2_interface.c index cd5f9106..8e270522 100644 --- a/src/host/layer23/src/common/l1l2_interface.c +++ b/src/host/layer23/src/common/l1l2_interface.c @@ -15,19 +15,17 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/l1l2_interface.h> #include <osmocom/core/utils.h> #include <osmocom/core/socket.h> +#include <osmocom/core/select.h> #include <sys/socket.h> #include <sys/un.h> @@ -128,9 +126,9 @@ int layer2_close(struct osmocom_ms *ms) if (ms->l2_wq.bfd.fd <= 0) return -EINVAL; + osmo_fd_unregister(&ms->l2_wq.bfd); close(ms->l2_wq.bfd.fd); ms->l2_wq.bfd.fd = -1; - osmo_fd_unregister(&ms->l2_wq.bfd); osmo_wqueue_clear(&ms->l2_wq); return 0; diff --git a/src/host/layer23/src/common/logging.c b/src/host/layer23/src/common/logging.c index ed799913..283b7f1a 100644 --- a/src/host/layer23/src/common/logging.c +++ b/src/host/layer23/src/common/logging.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ @@ -36,7 +32,7 @@ static const struct log_info_cat default_categories[] = { .name = "DCS", .description = "Cell selection", .color = "\033[34m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DNB] = { .name = "DNB", @@ -48,54 +44,66 @@ static const struct log_info_cat default_categories[] = { .name = "DPLMN", .description = "PLMN selection", .color = "\033[32m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DRR] = { .name = "DRR", .description = "Radio Resource", .color = "\033[1;34m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMM] = { .name = "DMM", .description = "Mobility Management", .color = "\033[1;32m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DCC] = { .name = "DCC", .description = "Call Control", .color = "\033[1;33m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DGCC] = { + .name = "DGCC", + .description = "Group Call Control", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DBCC] = { + .name = "DBCC", + .description = "Broadcast Call Control", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSS] = { .name = "DSS", .description = "Supplenmentary Services", .color = "\033[1;35m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSMS] = { .name = "DSMS", .description = "Short Message Service", .color = "\033[1;37m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMNCC] = { .name = "DMNCC", .description = "Mobile Network Call Control", .color = "\033[1;37m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMEAS] = { .name = "DMEAS", .description = "MEasurement Reporting", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DPAG] = { .name = "DPAG", .description = "Paging", .color = "\033[33m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DL1C] = { .name = "DL1C", @@ -107,37 +115,37 @@ static const struct log_info_cat default_categories[] = { .name = "DSAP", .description = "SAP Control", .color = "\033[1;31m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSUM] = { .name = "DSUM", .description = "Summary of Process", .color = "\033[1;37m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DSIM] = { .name = "DSIM", .description = "SIM client", .color = "\033[0;35m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DGPS] = { .name = "DGPS", .description = "GPS", .color = "\033[1;35m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DMOB] = { .name = "DMOB", .description = "Mobile", .color = "\033[1;35m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DPRIM] = { .name = "DPRIM", .description = "PRIM", .color = "\033[1;32m", - .enabled = 1, .loglevel = LOGL_DEBUG, + .enabled = 1, .loglevel = LOGL_NOTICE, }, [DLUA] = { .name = "DLUA", @@ -145,6 +153,54 @@ static const struct log_info_cat default_categories[] = { .color = "\033[1;32m", .enabled = 1, .loglevel = LOGL_DEBUG, }, + [DGAPK] = { + .name = "DGAPK", + .description = "GAPK audio", + .color = "\033[0;36m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DCSD] = { + .name = "DCSD", + .description = "Circuit Switched Data", + .color = "\033[0;36m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DTUN] = { + .name = "DTUN", + .description = "Tunnel interface", + .color = "\033[0;37m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DRLCMAC] = { + .name = "DRLCMAC", + .description = "Radio Link Control / Medium Access Control (RLC/MAC)", + .color = "\033[0;38m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DLLC] = { + .name = "DLLC", + .description = "GPRS Logical Link Control Protocol (LLC)", + .color = "\033[0;39m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DSNDCP] = { + .name = "DSNDCP", + .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", + .color = "\033[0;40m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DGMM] = { + .name = "DGMM", + .description = "GPRS Mobility Management (GMM)", + .color = "\033[0;32m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DSM] = { + .name = "DSM", + .description = "GPRS Session Management (SM)", + .color = "\033[0;31m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, }; const struct log_info log_info = { diff --git a/src/host/layer23/src/common/main.c b/src/host/layer23/src/common/main.c index 9d1c69ee..919a2315 100644 --- a/src/host/layer23/src/common/main.c +++ b/src/host/layer23/src/common/main.c @@ -15,27 +15,29 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/l1l2_interface.h> #include <osmocom/bb/common/sap_interface.h> #include <osmocom/bb/misc/layer3.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/vty.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/core/select.h> #include <osmocom/core/linuxlist.h> +#include <osmocom/core/application.h> #include <osmocom/core/gsmtap_util.h> -#include <osmocom/core/gsmtap.h> #include <osmocom/core/utils.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/ports.h> #include <arpa/inet.h> @@ -47,77 +49,56 @@ #include <fcntl.h> #include <signal.h> -struct log_target *stderr_target; +#include "config.h" void *l23_ctx = NULL; +struct l23_global_config l23_cfg; -static char *layer2_socket_path = "/tmp/osmocom_l2"; static char *sap_socket_path = "/tmp/osmocom_sap"; struct llist_head ms_list; -static struct osmocom_ms *ms = NULL; static char *gsmtap_ip = NULL; -static char *vty_ip = "127.0.0.1"; +static char *config_file = NULL; +static char *log_cat_mask = NULL; -unsigned short vty_port = 4247; -int (*l23_app_work) (struct osmocom_ms *ms) = NULL; -int (*l23_app_exit) (struct osmocom_ms *ms) = NULL; +int (*l23_app_start)(void) = NULL; +int (*l23_app_work)(void) = NULL; +int (*l23_app_exit)(void) = NULL; int quit = 0; -struct gsmtap_inst *gsmtap_inst; - -const char *openbsc_copyright = - "%s" - "%s\n" - "License GPLv2+: GNU GPL version 2 or later " - "<http://gnu.org/licenses/gpl.html>\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n\n"; static void print_usage(const char *app) { printf("Usage: %s\n", app); } -static void print_help() +static void print_help(void) { - int options = 0xff; - struct l23_app_info *app = l23_app_info(); - - if (app && app->cfg_supported != 0) - options = app->cfg_supported(); - printf(" Some help...\n"); printf(" -h --help this text\n"); printf(" -s --socket /tmp/osmocom_l2. Path to the unix " "domain socket (l2)\n"); - if (options & L23_OPT_SAP) + if (l23_app_info.opt_supported & L23_OPT_SAP) printf(" -S --sap /tmp/osmocom_sap. Path to the " "unix domain socket (BTSAP)\n"); - if (options & L23_OPT_ARFCN) + if (l23_app_info.opt_supported & L23_OPT_ARFCN) printf(" -a --arfcn NR The ARFCN to be used for layer2.\n"); - if (options & L23_OPT_TAP) + if (l23_app_info.opt_supported & L23_OPT_TAP) printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n"); - if (options & L23_OPT_VTY) - printf(" -v --vty-port The VTY port number to telnet " - "to. (default %u)\n", vty_port); + if (l23_app_info.opt_supported & L23_OPT_VTY) + printf(" -c --config-file The path to the VTY configuration file.\n"); - if (options & L23_OPT_DBG) + if (l23_app_info.opt_supported & L23_OPT_DBG) printf(" -d --debug Change debug flags.\n"); - if (options & L23_OPT_VTYIP) - printf(" -u --vty-ip The VTY IP to bind telnet to. " - "(default %s)\n", vty_ip); - - if (app && app->cfg_print_help) - app->cfg_print_help(); + if (l23_app_info.cfg_print_help != NULL) + l23_app_info.cfg_print_help(); } static void build_config(char **opt, struct option **option) { - struct l23_app_info *app; struct option *app_opp = NULL; int app_len = 0, len; @@ -127,19 +108,17 @@ static void build_config(char **opt, struct option **option) {"sap", 1, 0, 'S'}, {"arfcn", 1, 0, 'a'}, {"gsmtap-ip", 1, 0, 'i'}, - {"vty-ip", 1, 0, 'u'}, - {"vty-port", 1, 0, 'v'}, + {"config-file", 1, 0, 'c'}, {"debug", 1, 0, 'd'}, }; - app = l23_app_info(); - *opt = talloc_asprintf(l23_ctx, "hs:S:a:i:v:d:u:%s", - app && app->getopt_string ? app->getopt_string : ""); + *opt = talloc_asprintf(l23_ctx, "hs:S:a:i:c:d:%s", + l23_app_info.getopt_string ? l23_app_info.getopt_string : ""); len = ARRAY_SIZE(long_options); - if (app && app->cfg_getopt_opt) - app_len = app->cfg_getopt_opt(&app_opp); + if (l23_app_info.cfg_getopt_opt != NULL) + app_len = l23_app_info.cfg_getopt_opt(&app_opp); *option = talloc_zero_array(l23_ctx, struct option, len + app_len + 1); memcpy(*option, long_options, sizeof(long_options)); @@ -149,7 +128,6 @@ static void build_config(char **opt, struct option **option) static void handle_options(int argc, char **argv) { - struct l23_app_info *app = l23_app_info(); struct option *long_options; char *opt; @@ -170,29 +148,26 @@ static void handle_options(int argc, char **argv) exit(0); break; case 's': - layer2_socket_path = talloc_strdup(l23_ctx, optarg); + layer2_socket_path = optarg; break; case 'S': sap_socket_path = talloc_strdup(l23_ctx, optarg); break; case 'a': - ms->test_arfcn = atoi(optarg); + cfg_test_arfcn = atoi(optarg); break; case 'i': gsmtap_ip = optarg; break; - case 'u': - vty_ip = optarg; - break; - case 'v': - vty_port = atoi(optarg); + case 'c': + config_file = optarg; break; case 'd': - log_parse_category_mask(stderr_target, optarg); + log_cat_mask = optarg; break; default: - if (app && app->cfg_handle_opt) - app->cfg_handle_opt(c, optarg); + if (l23_app_info.cfg_handle_opt != NULL) + l23_app_info.cfg_handle_opt(c, optarg); break; } } @@ -210,19 +185,56 @@ void sighandler(int sigset) fprintf(stderr, "Signal %d received.\n", sigset); if (l23_app_exit) - rc = l23_app_exit(ms); + rc = l23_app_exit(); + + if (l23_app_info.opt_supported & L23_OPT_VTY) + telnet_exit(); if (rc != -EBUSY) exit (0); } -static void print_copyright() +static void print_copyright(void) { - struct l23_app_info *app; - app = l23_app_info(); - printf(openbsc_copyright, - app && app->copyright ? app->copyright : "", - app && app->contribution ? app->contribution : ""); + printf("%s" + "%s\n" + "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n\n", + l23_app_info.copyright ? l23_app_info.copyright : "", + l23_app_info.contribution ? l23_app_info.contribution : ""); +} + +static int _vty_init(void) +{ + int rc; + + OSMO_ASSERT(l23_app_info.vty_info != NULL); + l23_app_info.vty_info->tall_ctx = l23_ctx; + + vty_init(l23_app_info.vty_info); + logging_vty_add_cmds(); + + if (l23_app_info.vty_init != NULL) + l23_app_info.vty_init(); + if (config_file) { + LOGP(DLGLOBAL, LOGL_INFO, "Using configuration from '%s'\n", config_file); + l23_vty_reading = true; + rc = vty_read_config_file(config_file, NULL); + l23_vty_reading = false; + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_FATAL, + "Failed to parse the configuration file '%s'\n", config_file); + return rc; + } + } + rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB); + if (rc < 0) { + LOGP(DMOB, LOGL_FATAL, "Cannot init VTY on %s port %u: %s\n", + vty_get_bind_addr(), OSMO_VTY_PORT_BB, strerror(errno)); + return rc; + } + return rc; } int main(int argc, char **argv) @@ -230,52 +242,67 @@ int main(int argc, char **argv) int rc; INIT_LLIST_HEAD(&ms_list); - log_init(&log_info, NULL); - stderr_target = log_target_create_stderr(); - log_add_target(stderr_target); - log_set_all_filter(stderr_target, 1); l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); - ms = talloc_zero(l23_ctx, struct osmocom_ms); - if (!ms) { - fprintf(stderr, "Failed to allocate MS\n"); - exit(1); - } + osmo_init_logging2(l23_ctx, &log_info); - print_copyright(); + log_set_print_category_hex(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); + log_set_print_level(osmo_stderr_target, 1); - llist_add_tail(&ms->entity, &ms_list); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); + log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_HEADER_END); - ms->name = talloc_strdup(ms, "1"); - ms->test_arfcn = 871; + print_copyright(); handle_options(argc, argv); - rc = layer2_open(ms, layer2_socket_path); + rc = l23_app_init(); if (rc < 0) { - fprintf(stderr, "Failed during layer2_open()\n"); + fprintf(stderr, "Failed during l23_app_init()\n"); exit(1); } - ms->lapdm_channel.lapdm_dcch.l1_ctx = ms; - ms->lapdm_channel.lapdm_dcch.l3_ctx = ms; - ms->lapdm_channel.lapdm_acch.l1_ctx = ms; - ms->lapdm_channel.lapdm_acch.l3_ctx = ms; - lapdm_channel_init(&ms->lapdm_channel, LAPDM_MODE_MS); - lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms); + if (l23_app_info.opt_supported & L23_OPT_VTY) { + if (_vty_init() < 0) + exit(1); + } - rc = l23_app_init(ms); - if (rc < 0) - exit(1); + if (log_cat_mask != NULL) + log_parse_category_mask(osmo_stderr_target, log_cat_mask); + + if (l23_app_info.opt_supported & L23_OPT_TAP) { + if (gsmtap_ip) { + if (l23_cfg.gsmtap.remote_host != NULL) { + LOGP(DLGLOBAL, LOGL_NOTICE, + "Command line argument '-i %s' overrides node " + "'gsmtap' cmd 'remote-host %s' from the config file\n", + gsmtap_ip, l23_cfg.gsmtap.remote_host); + talloc_free(l23_cfg.gsmtap.remote_host); + } + l23_cfg.gsmtap.remote_host = talloc_strdup(l23_ctx, gsmtap_ip); + } + + if (l23_cfg.gsmtap.remote_host) { + LOGP(DLGLOBAL, LOGL_NOTICE, + "Setting up GSMTAP Um forwarding to '%s:%u'\n", + l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT); + l23_cfg.gsmtap.inst = gsmtap_source_init(l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT, 1); + if (!l23_cfg.gsmtap.inst) { + fprintf(stderr, "Failed during gsmtap_init()\n"); + exit(1); + } + gsmtap_source_add_sink(l23_cfg.gsmtap.inst); + } + } - if (gsmtap_ip) { - gsmtap_inst = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1); - if (!gsmtap_inst) { - fprintf(stderr, "Failed during gsmtap_init()\n"); + if (l23_app_start) { + rc = l23_app_start(); + if (rc < 0) { + fprintf(stderr, "Failed during l23_app_start()\n"); exit(1); } - gsmtap_source_add_sink(gsmtap_inst); } signal(SIGINT, sighandler); @@ -285,7 +312,7 @@ int main(int argc, char **argv) while (!quit) { if (l23_app_work) - l23_app_work(ms); + l23_app_work(); osmo_select_main(0); } diff --git a/src/host/layer23/src/common/ms.c b/src/host/layer23/src/common/ms.c new file mode 100644 index 00000000..a14cb1eb --- /dev/null +++ b/src/host/layer23/src/common/ms.c @@ -0,0 +1,80 @@ +/* Mobile Station */ +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <osmocom/gsm/gsm48.h> + +#include <osmocom/bb/common/ms.h> + +extern struct llist_head ms_list; + +/* Default value be configured by cmdline arg: */ +uint16_t cfg_test_arfcn = 871; + +static int osmocom_ms_talloc_destructor(struct osmocom_ms *ms) +{ + + if (ms->sap_wq.bfd.fd > -1) { + sap_close(ms); + ms->sap_wq.bfd.fd = -1; + } + + gprs_settings_fi(ms); + gsm_subscr_exit(ms); + gsm_sim_exit(ms); + return 0; +} + +struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name) +{ + struct osmocom_ms *ms; + + ms = talloc_zero(ctx, struct osmocom_ms); + if (!ms) + return NULL; + talloc_set_name(ms, "ms_%s", name); + talloc_set_destructor(ms, osmocom_ms_talloc_destructor); + + ms->name = talloc_strdup(ms, name); + ms->test_arfcn = cfg_test_arfcn; + ms->lapdm_channel.lapdm_dcch.l1_ctx = ms; + ms->lapdm_channel.lapdm_dcch.l3_ctx = ms; + ms->lapdm_channel.lapdm_acch.l1_ctx = ms; + ms->lapdm_channel.lapdm_acch.l3_ctx = ms; + lapdm_channel_init(&ms->lapdm_channel, LAPDM_MODE_MS); + lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms); + + ms->l2_wq.bfd.fd = -1; + ms->sap_wq.bfd.fd = -1; + + ms->gmmlayer.tlli = GSM_RESERVED_TMSI; + + /* Register a new MS */ + llist_add_tail(&ms->entity, &ms_list); + + gsm_support_init(ms); + gsm_settings_init(ms); + gprs_settings_init(ms); + /* init SAP client before SIM card starts up */ + sap_init(ms); + /* SAP response call-back */ + ms->sap_entity.sap_rsp_cb = &gsm_subscr_sap_rsp_cb; + gsm_sim_init(ms); + gsm_subscr_init(ms); + + return ms; +} diff --git a/src/host/layer23/src/common/networks.c b/src/host/layer23/src/common/networks.c index b4757e96..e2429ef8 100644 --- a/src/host/layer23/src/common/networks.c +++ b/src/host/layer23/src/common/networks.c @@ -2,11 +2,13 @@ #include <stdint.h> #include <stdio.h> +#include <osmocom/gsm/gsm23003.h> + #include <osmocom/bb/common/networks.h> /* list of networks */ -struct gsm_networks gsm_networks[] = { +static struct gsm_networks gsm_networks[] = { { 0x001, -1, "Test" }, { 0x001, 0x01f, "Test" }, { 0x412, -1, "Afghanistan" }, @@ -470,7 +472,7 @@ struct gsm_networks gsm_networks[] = { { 0x262, 0x07f, "O2" }, { 0x262, 0x08f, "O2" }, { 0x262, 0x09f, "Vodafone" }, - { 0x262, 0x10f, "DB Systel GSM-R" }, + { 0x262, 0x10f, "DB Netz AG GSM-R" }, { 0x262, 0x11f, "O2" }, { 0x262, 0x12f, "Dolphin Telecom" }, { 0x262, 0x13f, "Mobilcom Multimedia" }, @@ -1774,6 +1776,43 @@ struct gsm_networks gsm_networks[] = { { 0, 0, NULL } }; +/* param: numerically stored mcc as per osmo_plmn_id. */ +uint16_t gsm_mcc_to_hex(uint16_t mcc) +{ + uint8_t buf[3]; + uint16_t in = mcc; + + buf[2] = in % 10; + in = in / 10; + buf[1] = in % 10; + in = in / 10; + buf[0] = in % 10; + + return ((buf[0] << 8) + + (buf[1] << 4) + + buf[2]); +} + +/* param: numerically stored mnc as per osmo_plmn_id. */ +uint16_t gsm_mnc_to_hex(uint16_t mnc, bool mnc_3_digits) +{ + uint8_t buf[3]; + uint16_t in = mnc; + if (mnc_3_digits) { + buf[2] = in % 10; + in = in / 10; + } else { + buf[2] = 0x0f; + } + buf[1] = in % 10; + in = in / 10; + buf[0] = in % 10; + + return ((buf[0] << 8) + + (buf[1] << 4) + + buf[2]); +} + /* GSM 03.22 Annex A */ int gsm_match_mcc(uint16_t mcc, char *imsi) { @@ -1783,30 +1822,33 @@ int gsm_match_mcc(uint16_t mcc, char *imsi) + ((imsi[1] - '0') << 4) + imsi[2] - '0'; - return (mcc == sim_mcc); + return (gsm_mcc_to_hex(mcc) == sim_mcc); } /* GSM 03.22 Annex A */ -int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi) +int gsm_match_mnc(uint16_t mcc, uint16_t mnc, bool mnc_3_digits, char *imsi) { uint16_t sim_mnc; + uint16_t mnc_hex; /* 1. SIM-MCC = BCCH-MCC */ if (!gsm_match_mcc(mcc, imsi)) return 0; + mnc_hex = gsm_mnc_to_hex(mnc, mnc_3_digits); + /* 2. 3rd digit of BCCH-MNC is not 0xf */ - if ((mnc & 0x00f) != 0x00f) { + if ((mnc_hex & 0x00f) != 0x00f) { /* 3. 3 digit SIM-MNC = BCCH-MNC */ sim_mnc = ((imsi[3] - '0') << 8) + ((imsi[4] - '0') << 4) + imsi[5] - '0'; - return (mnc == sim_mnc); + return (mnc_hex == sim_mnc); } /* 4. BCCH-MCC in the range 310-316 */ - if (mcc >= 310 && mcc <= 316) { + if (gsm_mcc_to_hex(mcc) >= 310 && mnc_hex <= 316) { /* 5. 3rd diit of SIM-MNC is 0 */ if (imsi[5] != 0) return 0; @@ -1817,123 +1859,47 @@ int gsm_match_mnc(uint16_t mcc, uint16_t mnc, char *imsi) + ((imsi[4] - '0') << 4) + 0x00f; - return (mnc == sim_mnc); -} - -const char *gsm_print_mcc(uint16_t mcc) -{ - static char string[6] = "000"; - - snprintf(string, 5, "%03x", mcc); - return string; -} - -const char *gsm_print_mnc(uint16_t mnc) -{ - static char string[8]; - - /* invalid format: return hex value */ - if ((mnc & 0xf000) - || (mnc & 0x0f00) > 0x0900 - || (mnc & 0x00f0) > 0x0090 - || ((mnc & 0x000f) > 0x0009 && (mnc & 0x000f) < 0x000f)) { - snprintf(string, 7, "0x%03x", mnc); - return string; - } - - /* two digits */ - if ((mnc & 0x000f) == 0x000f) { - snprintf(string, 7, "%02x", mnc >> 4); - return string; - } - - /* three digits */ - snprintf(string, 7, "%03x", mnc); - return string; -} - -const uint16_t gsm_input_mcc(char *string) -{ - uint16_t mcc; - - if (strlen(string) != 3) - return GSM_INPUT_INVALID; - if (string[0] < '0' || string [0] > '9' - || string[1] < '0' || string [1] > '9' - || string[2] < '0' || string [2] > '9') - return GSM_INPUT_INVALID; - - mcc = ((string[0] - '0') << 8) - | ((string[1] - '0') << 4) - | ((string[2] - '0')); - - if (mcc == 0x000) - return GSM_INPUT_INVALID; - - return mcc; -} - -const uint16_t gsm_input_mnc(char *string) -{ - uint16_t mnc = 0; - - if (strlen(string) == 2) { - if (string[0] < '0' || string [0] > '9' - || string[1] < '0' || string [1] > '9') - return GSM_INPUT_INVALID; - - mnc = ((string[0] - '0') << 8) - | ((string[1] - '0') << 4) - | 0x00f; - } else - if (strlen(string) == 3) { - if (string[0] < '0' || string [0] > '9' - || string[1] < '0' || string [1] > '9' - || string[2] < '0' || string [2] > '9') - return GSM_INPUT_INVALID; - - mnc = ((string[0] - '0') << 8) - | ((string[1] - '0') << 4) - | ((string[2] - '0')); - } - - return mnc; + return (mnc_hex == sim_mnc); } const char *gsm_get_mcc(uint16_t mcc) { int i; + uint16_t mcc_hex = gsm_mcc_to_hex(mcc); for (i = 0; gsm_networks[i].name; i++) - if (gsm_networks[i].mnc < 0 && gsm_networks[i].mcc == mcc) + if (gsm_networks[i].mnc_hex < 0 && gsm_networks[i].mcc_hex == mcc_hex) return gsm_networks[i].name; - return gsm_print_mcc(mcc); + return osmo_mcc_name(mcc); } -const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc) +const char *gsm_get_mnc(const struct osmo_plmn_id *plmn) { int i; + uint16_t mcc_hex = gsm_mcc_to_hex(plmn->mcc); + uint16_t mnc_hex = gsm_mnc_to_hex(plmn->mnc, plmn->mnc_3_digits); for (i = 0; gsm_networks[i].name; i++) - if (gsm_networks[i].mcc == mcc && gsm_networks[i].mnc == mnc) + if (gsm_networks[i].mcc_hex == mcc_hex && + gsm_networks[i].mnc_hex == mnc_hex) return gsm_networks[i].name; - return gsm_print_mnc(mnc); + return osmo_mnc_name(plmn->mnc, plmn->mnc_3_digits); } /* get MCC from IMSI */ const char *gsm_imsi_mcc(char *imsi) { int i, found = 0; - uint16_t mcc; + uint16_t mcc_hex; - mcc = ((imsi[0] - '0') << 8) + mcc_hex = ((imsi[0] - '0') << 8) | ((imsi[1] - '0') << 4) | ((imsi[2] - '0')); for (i = 0; gsm_networks[i].name; i++) { - if (gsm_networks[i].mcc == mcc) { + if (gsm_networks[i].mcc_hex == mcc_hex) { found = 1; break; } @@ -1961,15 +1927,15 @@ const char *gsm_imsi_mnc(char *imsi) + imsi[5] - '0'; for (i = 0; gsm_networks[i].name; i++) { - if (gsm_networks[i].mcc != mcc) + if (gsm_networks[i].mcc_hex != mcc) continue; - if ((gsm_networks[i].mnc & 0x00f) == 0x00f) { - if (mnc2 == gsm_networks[i].mnc) { + if ((gsm_networks[i].mnc_hex & 0x00f) == 0x00f) { + if (mnc2 == gsm_networks[i].mnc_hex) { found++; position = i; } } else { - if (mnc3 == gsm_networks[i].mnc) { + if (mnc3 == gsm_networks[i].mnc_hex) { found++; position = i; } diff --git a/src/host/layer23/src/common/sap_fsm.c b/src/host/layer23/src/common/sap_fsm.c index 22658917..f189bc8a 100644 --- a/src/host/layer23/src/common/sap_fsm.c +++ b/src/host/layer23/src/common/sap_fsm.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <errno.h> @@ -31,8 +27,10 @@ #include <osmocom/core/socket.h> #include <osmocom/core/utils.h> #include <osmocom/core/fsm.h> +#include <osmocom/core/write_queue.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/sap_interface.h> diff --git a/src/host/layer23/src/common/sap_interface.c b/src/host/layer23/src/common/sap_interface.c index 0eac8962..0f9f8c27 100644 --- a/src/host/layer23/src/common/sap_interface.c +++ b/src/host/layer23/src/common/sap_interface.c @@ -18,10 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <unistd.h> @@ -38,7 +34,7 @@ #include <osmocom/bb/common/osmocom_data.h> #include <osmocom/bb/common/logging.h> - +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/sap_interface.h> #include <osmocom/bb/common/sap_proto.h> #include <osmocom/bb/common/sap_fsm.h> @@ -325,10 +321,9 @@ int _sap_close_sock(struct osmocom_ms *ms) if (ms->sap_wq.bfd.fd <= 0) return -EINVAL; + osmo_fd_unregister(&ms->sap_wq.bfd); close(ms->sap_wq.bfd.fd); ms->sap_wq.bfd.fd = -1; - - osmo_fd_unregister(&ms->sap_wq.bfd); osmo_wqueue_clear(&ms->sap_wq); return 0; diff --git a/src/host/layer23/src/common/sap_proto.c b/src/host/layer23/src/common/sap_proto.c index ecacfe05..2e891820 100644 --- a/src/host/layer23/src/common/sap_proto.c +++ b/src/host/layer23/src/common/sap_proto.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <string.h> diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/common/settings.c index 9742b1db..6bc56340 100644 --- a/src/host/layer23/src/mobile/settings.c +++ b/src/host/layer23/src/common/settings.c @@ -13,25 +13,29 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> #include <errno.h> #include <string.h> #include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsm48.h> -#include <osmocom/bb/mobile/app_mobile.h> +#include <osmocom/bb/common/settings.h> #include <osmocom/bb/common/utils.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> -#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/l1l2_interface.h> + +/* Used to set default path globally through cmdline */ +char *layer2_socket_path = L2_DEFAULT_SOCKET_PATH; -static char *layer2_socket_path = "/tmp/osmocom_l2"; static char *sap_socket_path = "/tmp/osmocom_sap"; +static char *mncc_socket_path = "/tmp/ms_mncc"; +static char *data_socket_path = "/tmp/ms_data"; +static char *alsa_dev_default = "default"; int gsm_settings_init(struct osmocom_ms *ms) { @@ -41,8 +45,25 @@ int gsm_settings_init(struct osmocom_ms *ms) strcpy(set->layer2_socket_path, layer2_socket_path); strcpy(set->sap_socket_path, sap_socket_path); - /* Audio settings: drop TCH frames by default */ - set->audio.io_handler = AUDIO_IOH_NONE; + /* Compose MNCC socket path using MS name */ + snprintf(set->mncc_socket_path, sizeof(set->mncc_socket_path) - 1, + "%s_%s", mncc_socket_path, ms->name); + + /* TCH voice: drop frames by default */ + set->tch_voice.io_handler = TCH_VOICE_IOH_NONE; + set->tch_voice.io_format = TCH_VOICE_IOF_RTP; + OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_output_dev, alsa_dev_default); + OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_input_dev, alsa_dev_default); + + /* TCH data: drop frames by default */ + set->tch_data.io_handler = TCH_DATA_IOH_NONE; + set->tch_data.io_format = TCH_DATA_IOF_OSMO; + snprintf(set->tch_data.unix_socket_path, + sizeof(set->tch_data.unix_socket_path) - 1, + "%s_%s", data_socket_path, ms->name); + + /* Built-in MNCC handler */ + set->mncc_handler = MNCC_HANDLER_INTERNAL; /* network search */ set->plmn_mode = PLMN_MODE_AUTO; @@ -55,10 +76,12 @@ int gsm_settings_init(struct osmocom_ms *ms) set->sim_type = GSM_SIM_TYPE_L1PHY; /* test SIM */ - strcpy(set->test_imsi, "001010000000000"); - set->test_rplmn_mcc = set->test_rplmn_mnc = 1; - set->test_lac = 0x0000; - set->test_tmsi = 0xffffffff; + OSMO_STRLCPY_ARRAY(set->test_sim.imsi, "001010000000000"); + set->test_sim.rplmn.mcc = 1; + set->test_sim.rplmn.mnc = 1; + set->test_sim.rplmn.mnc_3_digits = false; + set->test_sim.lac = 0x0000; + set->test_sim.tmsi = GSM_RESERVED_TMSI; /* set all supported features */ set->sms_ptp = sup->sms_ptp; @@ -87,6 +110,16 @@ int gsm_settings_init(struct osmocom_ms *ms) set->min_rxlev_dbm = sup->min_rxlev_dbm; set->dsc_max = sup->dsc_max; + set->csd_tch_f144 = sup->csd_tch_f144; + set->csd_tch_f96 = sup->csd_tch_f96; + set->csd_tch_f48 = sup->csd_tch_f48; + set->csd_tch_h48 = sup->csd_tch_h48; + set->csd_tch_f24 = sup->csd_tch_f24; + set->csd_tch_h24 = sup->csd_tch_h24; + + set->vgcs = sup->vgcs; + set->vbs = sup->vbs; + if (sup->half_v1 || sup->half_v3) set->half = 1; @@ -100,6 +133,19 @@ int gsm_settings_init(struct osmocom_ms *ms) INIT_LLIST_HEAD(&set->abbrev); + set->uplink_release_local = true; + + set->call_params.data = (struct data_call_params) { + .type_rate = DATA_CALL_TR_V110_9600, + .transp = GSM48_BCAP_TR_TRANSP, + + /* async call parameters (8-N-1) */ + .is_async = true, + .nr_stop_bits = 1, + .nr_data_bits = 8, + .parity = GSM48_BCAP_PAR_NONE, + }; + return 0; } @@ -151,9 +197,6 @@ int gsm_settings_exit(struct osmocom_ms *ms) llist_del(&abbrev->list); talloc_free(abbrev); } - - script_lua_close(ms); - return 0; } @@ -197,8 +240,72 @@ int gsm_random_imei(struct gsm_settings *set) return 0; } -const struct value_string audio_io_handler_names[] = { - { AUDIO_IOH_NONE, "none" }, - { AUDIO_IOH_LOOPBACK, "loopback" }, - { 0x00, NULL} +const struct value_string tch_voice_io_handler_names[] = { + { TCH_VOICE_IOH_NONE, "none" }, + { TCH_VOICE_IOH_GAPK, "gapk" }, + { TCH_VOICE_IOH_L1PHY, "l1phy" }, + { TCH_VOICE_IOH_MNCC_SOCK, "mncc-sock" }, + { TCH_VOICE_IOH_LOOPBACK, "loopback" }, + { 0, NULL } }; + +const struct value_string tch_data_io_handler_names[] = { + { TCH_DATA_IOH_NONE, "none" }, + { TCH_DATA_IOH_UNIX_SOCK, "unix-sock" }, + { TCH_DATA_IOH_LOOPBACK, "loopback" }, + { 0, NULL } +}; + +const struct value_string tch_voice_io_format_names[] = { + { TCH_VOICE_IOF_RTP, "rtp" }, + { TCH_VOICE_IOF_TI, "ti" }, + { 0, NULL } +}; + +const struct value_string tch_data_io_format_names[] = { + { TCH_DATA_IOF_OSMO, "osmo" }, + { TCH_DATA_IOF_TI, "ti" }, + { 0, NULL } +}; + +int gprs_settings_init(struct osmocom_ms *ms) +{ + struct gprs_settings *set = &ms->gprs; + INIT_LLIST_HEAD(&set->apn_list); + + return 0; +} + +int gprs_settings_fi(struct osmocom_ms *ms) +{ + struct gprs_settings *set = &ms->gprs; + struct osmobb_apn *apn; + while ((apn = llist_first_entry_or_null(&set->apn_list, struct osmobb_apn, list))) { + /* free calls llist_del(): */ + apn_free(apn); + } + return 0; +} + +struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char *apn_name) +{ + struct gprs_settings *set = &ms->gprs; + struct osmobb_apn *apn; + + llist_for_each_entry(apn, &set->apn_list, list) { + if (strcmp(apn->cfg.name, apn_name) == 0) + return apn; + } + return NULL; +} + +int ms_dispatch_all_apn(struct osmocom_ms *ms, uint32_t event, void *data) +{ + struct gprs_settings *set = &ms->gprs; + int rc = 0; + struct osmobb_apn *apn; + + llist_for_each_entry(apn, &set->apn_list, list) + rc |= osmo_fsm_inst_dispatch(apn->fsm.fi, event, data); + return rc; +} diff --git a/src/host/layer23/src/common/sim.c b/src/host/layer23/src/common/sim.c index 1e2bc513..4a4144ab 100644 --- a/src/host/layer23/src/common/sim.c +++ b/src/host/layer23/src/common/sim.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -28,11 +24,11 @@ #include <osmocom/core/gsmtap.h> #include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1ctl.h> -extern struct gsmtap_inst *gsmtap_inst; - static int sim_process_job(struct osmocom_ms *ms); /* @@ -243,7 +239,7 @@ int gsm_sim_job_dequeue(struct osmocom_ms *ms) sim_process_job(ms); return 1; /* work done */ } - + return 0; } @@ -877,7 +873,7 @@ int sim_apdu_resp(struct osmocom_ms *ms, struct msgb *msg) if ((ms->sim.apdu_len + length) <= sizeof(ms->sim.apdu_data)) { memcpy(ms->sim.apdu_data + ms->sim.apdu_len, data, length); ms->sim.apdu_len += length; - gsmtap_send_ex(gsmtap_inst, GSMTAP_TYPE_SIM, + gsmtap_send_ex(l23_cfg.gsmtap.inst, GSMTAP_TYPE_SIM, 0, 0, 0, 0, 0, 0, 0, ms->sim.apdu_data, ms->sim.apdu_len); } } diff --git a/src/host/layer23/src/mobile/subscriber.c b/src/host/layer23/src/common/subscriber.c index b2eacc59..f6aa05d5 100644 --- a/src/host/layer23/src/mobile/subscriber.c +++ b/src/host/layer23/src/common/subscriber.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -25,18 +21,59 @@ #include <arpa/inet.h> #include <osmocom/core/talloc.h> #include <osmocom/crypt/auth.h> +#include <osmocom/gsm/gsm23003.h> +#include <osmocom/gsm/gsm48.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/sap_interface.h> #include <osmocom/bb/common/sap_proto.h> #include <osmocom/bb/common/networks.h> -#include <osmocom/bb/mobile/vty.h> +#include <osmocom/bb/common/subscriber.h> +#include <osmocom/bb/common/vty.h> /* enable to get an empty list of forbidden PLMNs, even if stored on SIM. * if list is changed, the result is not written back to SIM */ //#define TEST_EMPTY_FPLMN +const struct value_string gsm_sub_sim_ustate_names[] = { + { GSM_SIM_U0_NULL, "U0_NULL" }, + { GSM_SIM_U1_UPDATED, "U1_UPDATED" }, + { GSM_SIM_U2_NOT_UPDATED, "U2_NOT_UPDATED" }, + { GSM_SIM_U3_ROAMING_NA, "U3_ROAMING_NA" }, + { 0, NULL } +}; + +const struct value_string gsm_sub_sim_gustate_names[] = { + { GSM_SIM_GU0_NULL, "GU0_NULL" }, + { GSM_SIM_GU1_UPDATED, "GU1_UPDATED" }, + { GSM_SIM_GU2_NOT_UPDATED, "GU2_NOT_UPDATED" }, + { GSM_SIM_GU3_ROAMING_NA, "GU3_ROAMING_NA" }, + { 0, NULL } +}; + +static int gsm_subscr_insert_simcard(struct osmocom_ms *ms); +static int gsm_subscr_insert_testcard(struct osmocom_ms *ms); +static int gsm_subscr_insert_sapcard(struct osmocom_ms *ms); + +static int gsm_subscr_remove_sapcard(struct osmocom_ms *ms); + +static int gsm_subscr_generate_kc_simcard(struct osmocom_ms *ms, uint8_t key_seq, + const uint8_t *rand, uint8_t no_sim); +static int gsm_subscr_generate_kc_testcard(struct osmocom_ms *ms, uint8_t key_seq, + const uint8_t *rand, uint8_t no_sim); + +static int gsm_subscr_write_loci_simcard(struct osmocom_ms *ms); + +static int gsm_subscr_write_locigprs_simcard(struct osmocom_ms *ms); +static int gsm_subscr_write_locigprs_testcard(struct osmocom_ms *ms); + +static int gsm_subscr_sim_pin_simcard(struct osmocom_ms *ms, const char *pin1, const char *pin2, + int8_t mode); + +static int subscr_write_plmn_na_simcard(struct osmocom_ms *ms); + static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg); static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg); static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg); @@ -45,21 +82,6 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg); * support */ -char *gsm_check_imsi(const char *imsi) -{ - int i; - - if (!imsi || strlen(imsi) != 15) - return "IMSI must have 15 digits!"; - - for (i = 0; i < strlen(imsi); i++) { - if (imsi[i] < '0' || imsi[i] > '9') - return "IMSI must have digits 0 to 9 only!"; - } - - return NULL; -} - static char *sim_decode_bcd(uint8_t *data, uint8_t length) { int i, j = 0; @@ -81,9 +103,9 @@ static char *sim_decode_bcd(uint8_t *data, uint8_t length) return result; } -/* - * init/exit - */ +/************************************** + * Generic backend-agnostic API + **************************************/ int gsm_subscr_init(struct osmocom_ms *ms) { @@ -93,8 +115,9 @@ int gsm_subscr_init(struct osmocom_ms *ms) subscr->ms = ms; /* set TMSI / LAC invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; + subscr->tmsi = GSM_RESERVED_TMSI; + subscr->gprs.ptmsi = GSM_RESERVED_TMSI; + subscr->lai.lac = 0x0000; /* set key invalid */ subscr->key_seq = 7; @@ -145,82 +168,554 @@ int gsm_subscr_exit(struct osmocom_ms *ms) return 0; } -/* - * test card - */ - -/* Attach test card, no SIM must be currently attached */ -int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, - uint16_t lac, uint32_t tmsi, uint8_t imsi_attached) +/* Insert card */ +int gsm_subscr_insert(struct osmocom_ms *ms) { struct gsm_settings *set = &ms->settings; struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - char *error; + int rc; if (subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " - "is detached.\n"); + LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card is removed.\n"); return -EBUSY; } - error = gsm_check_imsi(set->test_imsi); - if (error) { - LOGP(DMM, LOGL_ERROR, "%s\n", error); - return -EINVAL; - } - /* reset subscriber */ gsm_subscr_exit(ms); gsm_subscr_init(ms); + subscr->sim_valid = true; + + switch (set->sim_type) { + case GSM_SIM_TYPE_L1PHY: + /* trigger sim card reader process */ + rc = gsm_subscr_insert_simcard(ms); + break; + case GSM_SIM_TYPE_TEST: + rc = gsm_subscr_insert_testcard(ms); + break; + case GSM_SIM_TYPE_SAP: + rc = gsm_subscr_insert_sapcard(ms); + break; + default: + return -EINVAL; + } + + if (rc < 0) { + subscr->sim_valid = false; + return rc; + } + return rc; +} + +/* Detach card */ +int gsm_subscr_remove(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); + return -EINVAL; + } + + if (subscr->sim_type == GSM_SIM_TYPE_SAP) + gsm_subscr_remove_sapcard(ms); + + /* remove card */ + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_DETACHED, ms); + + return 0; +} + +/* change to new U state */ +void new_sim_ustate(struct gsm_subscriber *subscr, int state) +{ + LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, + gsm_sub_sim_ustate_name(subscr->ustate), + gsm_sub_sim_ustate_name(state)); + + subscr->ustate = state; +} + +/* enter PIN */ +int gsm_subscr_sim_pin(struct osmocom_ms *ms, const char *pin1, const char *pin2, + int8_t mode) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + /* skip, if no real valid SIM */ + if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid) + return 0; + + switch (subscr->sim_type) { + case GSM_SIM_TYPE_L1PHY: + case GSM_SIM_TYPE_SAP: + return gsm_subscr_sim_pin_simcard(ms, pin1, pin2, mode); + case GSM_SIM_TYPE_TEST: + LOGP(DMM, LOGL_NOTICE, "PIN on test SIM: not implemented!\n"); + return 0; /* TODO */ + default: + OSMO_ASSERT(0); + } +} + +int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, const uint8_t *rand, + bool no_sim) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct osmobb_l23_subscr_sim_auth_resp_sig_data sd; + int rc; + + if (no_sim || subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n"); + sd.ms = ms; + sd.sres[0] = 0x12; + sd.sres[1] = 0x34; + sd.sres[2] = 0x56; + sd.sres[3] = 0x78; + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_AUTH_RESP, &sd); + return 0; + } + + switch (subscr->sim_type) { + case GSM_SIM_TYPE_TEST: + rc = gsm_subscr_generate_kc_testcard(ms, key_seq, rand, no_sim); + break; + case GSM_SIM_TYPE_L1PHY: + case GSM_SIM_TYPE_SAP: + /* trigger sim card reader process */ + rc = gsm_subscr_generate_kc_simcard(ms, key_seq, rand, no_sim); + break; + default: + OSMO_ASSERT(0); + } + + return rc; +} + +/* update LOCI on SIM */ +int gsm_subscr_write_loci(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + /* skip, if no real valid SIM */ + if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid) + return 0; + + LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); + + switch (subscr->sim_type) { + case GSM_SIM_TYPE_L1PHY: + case GSM_SIM_TYPE_SAP: + return gsm_subscr_write_loci_simcard(ms); + case GSM_SIM_TYPE_TEST: + LOGP(DMM, LOGL_NOTICE, "Updating LOCI on test SIM: not implemented!\n"); + return 0; /* TODO */ + default: + OSMO_ASSERT(0); + } +} + +/* update LOCIGPRS on SIM */ +int gsm_subscr_write_locigprs(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + /* skip, if no real valid SIM */ + if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid) + return 0; + + LOGP(DMM, LOGL_INFO, "Updating LOCIGPRS on SIM\n"); + + switch (subscr->sim_type) { + case GSM_SIM_TYPE_L1PHY: + case GSM_SIM_TYPE_SAP: + return gsm_subscr_write_locigprs_simcard(ms); + case GSM_SIM_TYPE_TEST: + return gsm_subscr_write_locigprs_testcard(ms); + default: + OSMO_ASSERT(0); + } +} + +/* update plmn not allowed list on SIM */ +static int subscr_write_plmn_na(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + /* skip, if no real valid SIM */ + if (subscr->sim_type == GSM_SIM_TYPE_NONE || !subscr->sim_valid) + return 0; + + LOGP(DMM, LOGL_INFO, "Updating FPLMN on SIM\n"); + + switch (subscr->sim_type) { + case GSM_SIM_TYPE_L1PHY: + case GSM_SIM_TYPE_SAP: + return subscr_write_plmn_na_simcard(ms); + case GSM_SIM_TYPE_TEST: + LOGP(DMM, LOGL_NOTICE, "Updating FPLMN on test SIM: not implemented!\n"); + return 0; /* TODO */ + default: + OSMO_ASSERT(0); + } +} + +/* del forbidden PLMN. if PLMN is NULL, flush complete list */ +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn) +{ + struct gsm_sub_plmn_na *na, *na2; + int deleted = 0; + + llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) { + if (!plmn || (osmo_plmn_cmp(&na->plmn, plmn) == 0)) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden PLMNs (mcc-mnc=%s)\n", + osmo_plmn_name(&na->plmn)); + llist_del(&na->entry); + talloc_free(na); + deleted = 1; + if (plmn) + break; + } + } + + if (deleted) { + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + } + + return -EINVAL; +} + +/* add forbidden PLMN */ +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn, uint8_t cause) +{ + struct gsm_sub_plmn_na *na; + + /* if already in the list, remove and add to tail */ + gsm_subscr_del_forbidden_plmn(subscr, plmn); + + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " + "(mcc-mnc=%s)\n", osmo_plmn_name(plmn)); + na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na); + if (!na) + return -ENOMEM; + memcpy(&na->plmn, plmn, sizeof(struct osmo_plmn_id)); + na->cause = cause ? : -1; /* cause 0 is not allowed */ + llist_add_tail(&na->entry, &subscr->plmn_na); + + /* don't add Home PLMN to SIM */ + if (subscr->sim_valid && gsm_match_mnc(plmn->mcc, plmn->mnc, plmn->mnc_3_digits, subscr->imsi)) + return -EINVAL; + + /* update plmn not allowed list on SIM */ + subscr_write_plmn_na(subscr->ms); + + return 0; +} + +/* search forbidden PLMN */ +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, const struct osmo_plmn_id *plmn) +{ + struct gsm_sub_plmn_na *na; + + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (osmo_plmn_cmp(&na->plmn, plmn) == 0) + return 1; + } + + return 0; +} + +int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr) +{ + if (ms->settings.force_rekey) + return 7; + else + return subscr->key_seq; +} + +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_na *temp; + + print(priv, "MCC |MNC |cause\n"); + print(priv, "-------+-------+-------\n"); + llist_for_each_entry(temp, &subscr->plmn_na, entry) + print(priv, "%s |%-3s |#%d\n", + osmo_mcc_name(temp->plmn.mcc), + osmo_mnc_name(temp->plmn.mnc, temp->plmn.mnc_3_digits), + temp->cause); + + return 0; +} + +/* dump subscriber */ +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv) +{ + int i; + struct gsm_sub_plmn_list *plmn_list; + struct gsm_sub_plmn_na *plmn_na; + + print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); + + if (!subscr->sim_valid) { + print(priv, " No SIM present.\n"); + return; + } + + print(priv, " IMSI: %s\n", subscr->imsi); + if (subscr->iccid[0]) + print(priv, " ICCID: %s\n", subscr->iccid); + if (subscr->sim_spn[0]) + print(priv, " Service Provider Name: %s\n", subscr->sim_spn); + if (subscr->msisdn[0]) + print(priv, " MSISDN: %s\n", subscr->msisdn); + if (subscr->sms_sca[0]) + print(priv, " SMS Service Center Address: %s\n", + subscr->sms_sca); + + print(priv, " Status: %s IMSI %s", gsm_sub_sim_ustate_name(subscr->ustate), + (subscr->imsi_attached) ? "attached" : "detached"); + if (subscr->tmsi != GSM_RESERVED_TMSI) + print(priv, " TMSI 0x%08x", subscr->tmsi); + if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) { + print(priv, "\n"); + print(priv, " LAI: %s (%s, %s)\n", + osmo_lai_name(&subscr->lai), + gsm_get_mcc(subscr->lai.plmn.mcc), + gsm_get_mnc(&subscr->lai.plmn)); + } else { + print(priv, " LAI: invalid\n"); + } + + print(priv, " GPRS Status: %s IMSI %s", gsm_sub_sim_gustate_name(subscr->gprs.gu_state), + (subscr->gprs.imsi_attached) ? "attached" : "detached"); + if (subscr->gprs.ptmsi != GSM_RESERVED_TMSI) + print(priv, " PTMSI 0x%08x", subscr->tmsi); + if (subscr->gprs.ptmsi_sig != GSM_RESERVED_TMSI) + print(priv, " PTMSI-sig 0x%06x", subscr->gprs.ptmsi_sig); + if (subscr->gprs.rai.lac > 0x0000 && subscr->gprs.rai.lac < 0xfffe) { + struct osmo_plmn_id plmn = { + .mcc = subscr->gprs.rai.mcc, + .mnc = subscr->gprs.rai.mnc, + .mnc_3_digits = subscr->gprs.rai.mnc_3_digits, + }; + print(priv, "\n"); + print(priv, " RAI: %s (%s, %s)\n", + osmo_rai_name(&subscr->gprs.rai), + gsm_get_mcc(plmn.mcc), + gsm_get_mnc(&plmn)); + } else { + print(priv, " RAI: invalid\n"); + } + + if (subscr->gprs.ptmsi != GSM_RESERVED_TMSI) + print(priv, " P-TMSI 0x%08x", subscr->gprs.ptmsi); + if (subscr->key_seq != 7) { + print(priv, " Key: sequence %d ", subscr->key_seq); + for (i = 0; i < sizeof(subscr->key); i++) + print(priv, " %02x", subscr->key[i]); + print(priv, "\n"); + } + if (subscr->plmn_valid) + print(priv, " Registered PLMN: MCC-MNC %s (%s, %s)\n", + osmo_plmn_name(&subscr->plmn), + gsm_get_mcc(subscr->plmn.mcc), + gsm_get_mnc(&subscr->plmn)); + print(priv, " Access barred cells: %s\n", + (subscr->acc_barr) ? "yes" : "no"); + print(priv, " Access classes:"); + for (i = 0; i < 16; i++) + if ((subscr->acc_class & (1 << i))) + print(priv, " C%d", i); + print(priv, "\n"); + if (!llist_empty(&subscr->plmn_list)) { + print(priv, " List of preferred PLMNs:\n"); + print(priv, " MCC |MNC\n"); + print(priv, " -------+-------\n"); + llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) + print(priv, " %s |%s (%s, %s)\n", + osmo_mcc_name(plmn_list->plmn.mcc), + osmo_mnc_name(plmn_list->plmn.mnc, plmn_list->plmn.mnc_3_digits), + gsm_get_mcc(plmn_list->plmn.mcc), + gsm_get_mnc(&plmn_list->plmn)); + } + if (!llist_empty(&subscr->plmn_na)) { + print(priv, " List of forbidden PLMNs:\n"); + print(priv, " MCC |MNC |cause\n"); + print(priv, " -------+-------+-------\n"); + llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) + print(priv, " %s |%-3s |#%d (%s, %s)\n", + osmo_mcc_name(plmn_na->plmn.mcc), + osmo_mnc_name(plmn_na->plmn.mnc, plmn_na->plmn.mnc_3_digits), + plmn_na->cause, gsm_get_mcc(plmn_na->plmn.mcc), + gsm_get_mnc(&plmn_na->plmn)); + } +} + +/******************* + * testcard backend + *******************/ + +/* Attach test card, no SIM must be currently attached */ +int gsm_subscr_insert_testcard(struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_subscriber *subscr = &ms->subscr; + + if (!osmo_imsi_str_valid(set->test_sim.imsi)) { + LOGP(DMM, LOGL_ERROR, "Wrong IMSI format\n"); + return -EINVAL; + } + subscr->sim_type = GSM_SIM_TYPE_TEST; sprintf(subscr->sim_name, "test"); - subscr->sim_valid = 1; - if (imsi_attached && set->test_rplmn_valid) { - subscr->imsi_attached = imsi_attached; - subscr->ustate = GSM_SIM_U1_UPDATED; - } else - subscr->ustate = GSM_SIM_U2_NOT_UPDATED; - subscr->acc_barr = set->test_barr; /* we may access barred cell */ + subscr->imsi_attached = set->test_sim.imsi_attached; + subscr->acc_barr = set->test_sim.barr; /* we may access barred cell */ subscr->acc_class = 0xffff; /* we have any access class */ - subscr->plmn_valid = set->test_rplmn_valid; - subscr->plmn_mcc = mcc; - subscr->plmn_mnc = mnc; - subscr->mcc = mcc; - subscr->mnc = mnc; - subscr->lac = lac; - subscr->tmsi = tmsi; - subscr->always_search_hplmn = set->test_always; + subscr->plmn_valid = set->test_sim.rplmn_valid; + memcpy(&subscr->plmn, &set->test_sim.rplmn, sizeof(struct osmo_plmn_id)); + memcpy(&subscr->lai.plmn, &set->test_sim.rplmn, sizeof(struct osmo_plmn_id)); + subscr->lai.lac = set->test_sim.lac; + subscr->tmsi = set->test_sim.tmsi; + subscr->always_search_hplmn = set->test_sim.always_search_hplmn; subscr->t6m_hplmn = 1; /* try to find home network every 6 min */ - strcpy(subscr->imsi, set->test_imsi); + OSMO_STRLCPY_ARRAY(subscr->imsi, set->test_sim.imsi); + + if (subscr->imsi_attached && subscr->plmn_valid) + subscr->ustate = GSM_SIM_U1_UPDATED; + else + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + + /* GPRS related: */ + subscr->gprs.ptmsi = set->test_sim.locigprs.ptmsi; + subscr->gprs.ptmsi_sig = set->test_sim.locigprs.ptmsi_sig; + subscr->gprs.imsi_attached = set->test_sim.locigprs.imsi_attached; + subscr->gprs.rai_valid = set->test_sim.locigprs.valid; + memcpy(&subscr->gprs.rai, &set->test_sim.locigprs.rai, sizeof(subscr->gprs.rai)); + + if (subscr->gprs.imsi_attached && subscr->gprs.rai_valid) + subscr->ustate = GSM_SIM_U1_UPDATED; + else + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; - LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n", + LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s, %s, %s)\n", ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); if (subscr->plmn_valid) - LOGP(DMM, LOGL_INFO, "-> Test card registered to %s %s 0x%04x" - "(%s, %s)\n", gsm_print_mcc(mcc), - gsm_print_mnc(mnc), lac, gsm_get_mcc(mcc), - gsm_get_mnc(mcc, mnc)); + LOGP(DMM, LOGL_INFO, "-> Test card registered to %s" + " (%s, %s)\n", osmo_lai_name(&subscr->lai), + gsm_get_mcc(subscr->lai.plmn.mcc), + gsm_get_mnc(&subscr->lai.plmn)); else LOGP(DMM, LOGL_INFO, "-> Test card not registered\n"); if (subscr->imsi_attached) LOGP(DMM, LOGL_INFO, "-> Test card attached\n"); + + /* GPRS:*/ + if (subscr->gprs.rai_valid) + LOGP(DMM, LOGL_INFO, "-> Test card GPRS registered to %s\n", + osmo_rai_name(&subscr->gprs.rai)); + else + LOGP(DMM, LOGL_INFO, "-> Test card not GPRS registered\n"); + if (subscr->gprs.imsi_attached) + LOGP(DMM, LOGL_INFO, "-> Test card GPRS attached\n"); + /* insert card */ - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_ATTACHED, ms); + return 0; +} + +static int gsm_subscr_generate_kc_testcard(struct osmocom_ms *ms, uint8_t key_seq, + const uint8_t *rand, uint8_t no_sim) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct osmobb_l23_subscr_sim_auth_resp_sig_data sd; + + struct gsm_settings *set = &ms->settings; + static struct osmo_sub_auth_data2 auth = { + .type = OSMO_AUTH_TYPE_GSM + }; + struct osmo_auth_vector _vec; + struct osmo_auth_vector *vec = &_vec; + + auth.algo = set->test_sim.ki_type; + memcpy(auth.u.gsm.ki, set->test_sim.ki, sizeof(auth.u.gsm.ki)); + int ret = osmo_auth_gen_vec2(vec, &auth, rand); + if (ret < 0) + return ret; + + /* store sequence */ + subscr->key_seq = key_seq; + memcpy(subscr->key, vec->kc, 8); + + LOGP(DMM, LOGL_INFO, "Sending authentication response\n"); + sd.ms = ms; + memcpy(sd.sres, vec->sres, 4); + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_AUTH_RESP, &sd); + + return 0; +} + +/* update LOCIGPRS on test SIM */ +int gsm_subscr_write_locigprs_testcard(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm1111_ef_locigprs *locigprs; + + /* skip, if no real valid SIM */ + if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) + return 0; + + LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); + + /* write to SIM */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); if (!nmsg) return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f53; + locigprs = (struct gsm1111_ef_locigprs *)msgb_put(nmsg, sizeof(*locigprs)); + + /* P-TMSI, P-TMSI signature */ + locigprs->ptmsi = htonl(subscr->gprs.ptmsi); + locigprs->ptmsi_sig_hi = htonl(subscr->gprs.ptmsi) >> 8; + locigprs->ptmsi_sig_lo = htonl(subscr->gprs.ptmsi) & 0xff; + + /* RAI */ + gsm48_encode_ra(&locigprs->rai, &subscr->gprs.rai); + + /* location update status */ + switch (subscr->gprs.gu_state) { + case GSM_SIM_GU1_UPDATED: + locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED; + break; + case GSM_SIM_GU3_ROAMING_NA: + locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED; + break; + default: + locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_NOT_UPDATED; + } + + sim_job(ms, nmsg); return 0; } -/* - * sim card - */ +/******************** + * simcard backend + ********************/ static int subscr_sim_iccid(struct osmocom_ms *ms, uint8_t *data, uint8_t length) @@ -251,7 +746,7 @@ static int subscr_sim_imsi(struct osmocom_ms *ms, uint8_t *data, /* decode IMSI, skip first digit (parity) */ imsi = sim_decode_bcd(data + 1, length); - if (strlen(imsi) - 1 > GSM_IMSI_LENGTH - 1 || strlen(imsi) - 1 < 6) { + if (strlen(imsi) >= OSMO_IMSI_BUF_SIZE || strlen(imsi) - 1 < 6) { LOGP(DMM, LOGL_NOTICE, "IMSI invalid length = %zu\n", strlen(imsi) - 1); return -EINVAL; @@ -278,25 +773,60 @@ static int subscr_sim_loci(struct osmocom_ms *ms, uint8_t *data, subscr->tmsi = ntohl(loci->tmsi); /* LAI */ - gsm48_decode_lai_hex(&loci->lai, &subscr->mcc, &subscr->mnc, - &subscr->lac); + gsm48_decode_lai2(&loci->lai, &subscr->lai); /* location update status */ switch (loci->lupd_status & 0x07) { - case 0x00: + case GSM1111_EF_LOCI_LUPD_ST_UPDATED: subscr->ustate = GSM_SIM_U1_UPDATED; break; - case 0x02: - case 0x03: + case GSM1111_EF_LOCI_LUPD_ST_PLMN_NOT_ALLOWED: + case GSM1111_EF_LOCI_LUPD_ST_LA_NOT_ALLOWED: subscr->ustate = GSM_SIM_U3_ROAMING_NA; break; default: subscr->ustate = GSM_SIM_U2_NOT_UPDATED; } - LOGP(DMM, LOGL_INFO, "received LOCI from SIM (mcc=%s mnc=%s lac=0x%04x " - "U%d)\n", gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac, subscr->ustate); + LOGP(DMM, LOGL_INFO, "received LOCI from SIM (lai=%s U%d)\n", + osmo_lai_name(&subscr->lai), subscr->ustate); + + return 0; +} + +static int subscr_sim_locigprs(struct osmocom_ms *ms, uint8_t *data, + uint8_t length) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm1111_ef_locigprs *locigprs; + + if (length < 11) + return -EINVAL; + locigprs = (struct gsm1111_ef_locigprs *) data; + + /* P-TMSI, P-TMSI signature */ + subscr->gprs.ptmsi = ntohl(locigprs->ptmsi); + subscr->gprs.ptmsi_sig = (((uint32_t)locigprs->ptmsi_sig_hi) << 8) | locigprs->ptmsi_sig_lo; + + /* RAI */ + subscr->gprs.rai_valid = true; + gsm48_parse_ra(&subscr->gprs.rai, (uint8_t *)&locigprs->rai); + + /* routing area update status */ + switch (locigprs->rau_status & 0x07) { + case GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED: + subscr->gprs.gu_state = GSM_SIM_GU1_UPDATED; /* TODO: use proper enums here */ + break; + case GSM1111_EF_LOCIGPRS_RAU_ST_PLMN_NOT_ALLOWED: + case GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED: + subscr->gprs.gu_state = GSM_SIM_GU3_ROAMING_NA; + break; + default: + subscr->gprs.gu_state = GSM_SIM_GU2_NOT_UPDATED; + } + + LOGP(DMM, LOGL_INFO, "received LOCIGPRS from SIM (RAI=%s %s)\n", + osmo_rai_name(&subscr->gprs.rai), gsm_sub_sim_gustate_name(subscr->gprs.gu_state)); return 0; } @@ -384,8 +914,6 @@ static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data, struct gsm_subscriber *subscr = &ms->subscr; struct gsm_sub_plmn_list *plmn; struct llist_head *lh, *lh2; - uint8_t lai[5]; - uint16_t dummy_lac; /* flush list */ llist_for_each_safe(lh, lh2, &subscr->plmn_list) { @@ -402,16 +930,11 @@ static int subscr_sim_plmnsel(struct osmocom_ms *ms, uint8_t *data, plmn = talloc_zero(ms, struct gsm_sub_plmn_list); if (!plmn) return -ENOMEM; - lai[0] = data[0]; - lai[1] = data[1]; - lai[2] = data[2]; - gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, - &plmn->mcc, &plmn->mnc, &dummy_lac); + osmo_plmn_to_bcd(&data[0], &plmn->plmn); llist_add_tail(&plmn->entry, &subscr->plmn_list); - LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc=%s mnc=%s) " - "from SIM\n", - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc)); + LOGP(DMM, LOGL_INFO, "received PLMN selector (mcc-mnc=%s) from SIM\n", + osmo_plmn_name(&plmn->plmn)); data += 3; length -= 3; @@ -484,8 +1007,6 @@ static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, struct gsm_subscriber *subscr = &ms->subscr; struct gsm_sub_plmn_na *na; struct llist_head *lh, *lh2; - uint8_t lai[5]; - uint16_t dummy_lac; #ifdef TEST_EMPTY_FPLMN return 0; @@ -506,13 +1027,9 @@ static int subscr_sim_fplmn(struct osmocom_ms *ms, uint8_t *data, na = talloc_zero(ms, struct gsm_sub_plmn_na); if (!na) return -ENOMEM; - lai[0] = data[0]; - lai[1] = data[1]; - lai[2] = data[2]; - gsm48_decode_lai_hex((struct gsm48_loc_area_id *)lai, &na->mcc, - &na->mnc, &dummy_lac); - LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s %s from SIM\n", - gsm_print_mcc(na->mcc), gsm_print_mnc(na->mnc)); + osmo_plmn_to_bcd(&data[0], &na->plmn); + LOGP(DMM, LOGL_INFO, "received Forbidden PLMN %s from SIM\n", + osmo_plmn_name(&na->plmn)); na->cause = -1; /* must have a value, but SIM stores no cause */ llist_add_tail(&na->entry, &subscr->plmn_na); @@ -533,6 +1050,7 @@ static struct subscr_sim_file { { 1, { 0 }, 0x2fe2, SIM_JOB_READ_BINARY, subscr_sim_iccid }, { 1, { 0x7f20, 0 }, 0x6f07, SIM_JOB_READ_BINARY, subscr_sim_imsi }, { 1, { 0x7f20, 0 }, 0x6f7e, SIM_JOB_READ_BINARY, subscr_sim_loci }, + { 1, { 0x7f20, 0 }, 0x6f53, SIM_JOB_READ_BINARY, subscr_sim_locigprs }, { 0, { 0x7f20, 0 }, 0x6f20, SIM_JOB_READ_BINARY, subscr_sim_kc }, { 0, { 0x7f20, 0 }, 0x6f30, SIM_JOB_READ_BINARY, subscr_sim_plmnsel }, { 0, { 0x7f20, 0 }, 0x6f31, SIM_JOB_READ_BINARY, subscr_sim_hpplmn }, @@ -560,25 +1078,18 @@ static int subscr_sim_request(struct osmocom_ms *ms) gsm_imsi_mcc(subscr->imsi), gsm_imsi_mnc(subscr->imsi)); /* if LAI is valid, set RPLMN */ - if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { - subscr->plmn_valid = 1; - subscr->plmn_mcc = subscr->mcc; - subscr->plmn_mnc = subscr->mnc; - LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s %s " - "(%s, %s)\n", gsm_print_mcc(subscr->plmn_mcc), - gsm_print_mnc(subscr->plmn_mnc), - gsm_get_mcc(subscr->plmn_mcc), - gsm_get_mnc(subscr->plmn_mcc, - subscr->plmn_mnc)); + if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) { + subscr->plmn_valid = true; + memcpy(&subscr->plmn, &subscr->lai.plmn, sizeof(struct osmo_plmn_id)); + LOGP(DMM, LOGL_INFO, "-> SIM card registered to %s (%s, %s)\n", + osmo_plmn_name(&subscr->plmn), + gsm_get_mcc(subscr->plmn.mcc), + gsm_get_mnc(&subscr->plmn)); } else LOGP(DMM, LOGL_INFO, "-> SIM card not registered\n"); /* insert card */ - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); - + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_ATTACHED, ms); return 0; } @@ -611,7 +1122,6 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) uint16_t payload_len = msg->len - sizeof(*sh); int rc; struct subscr_sim_file *sf = &subscr_sim_files[subscr->sim_file_index]; - struct msgb *nmsg; /* error handling */ if (sh->job_type == SIM_JOB_ERROR) { @@ -623,29 +1133,29 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) LOGP(DMM, LOGL_INFO, "PIN is required, %d tries left\n", payload[1]); - vty_notify(ms, NULL); - vty_notify(ms, "Please give PIN for ICCID %s (you have " + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Please give PIN for ICCID %s (you have " "%d tries left)\n", subscr->iccid, payload[1]); - subscr->sim_pin_required = 1; + subscr->sim_pin_required = true; break; case SIM_CAUSE_PIN1_BLOCKED: LOGP(DMM, LOGL_NOTICE, "PIN is blocked\n"); - vty_notify(ms, NULL); - vty_notify(ms, "PIN is blocked\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "PIN is blocked\n"); if (payload[1]) { - vty_notify(ms, "Please give PUC for ICCID %s " + l23_vty_ms_notify(ms, "Please give PUC for ICCID %s " "(you have %d tries left)\n", subscr->iccid, payload[1]); } - subscr->sim_pin_required = 1; + subscr->sim_pin_required = true; break; case SIM_CAUSE_PUC_BLOCKED: LOGP(DMM, LOGL_NOTICE, "PUC is blocked\n"); - vty_notify(ms, NULL); - vty_notify(ms, "PUC is blocked\n"); - subscr->sim_pin_required = 1; + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "PUC is blocked\n"); + subscr->sim_pin_required = true; break; default: if (sf->func && !sf->mandatory) { @@ -655,15 +1165,12 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) } LOGP(DMM, LOGL_NOTICE, "SIM reading failed\n"); - vty_notify(ms, NULL); - vty_notify(ms, "SIM failed, replace SIM!\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "SIM failed, replace SIM!\n"); /* detach simcard */ subscr->sim_valid = 0; - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); - if (!nmsg) - return; - gsm48_mmr_downmsg(ms, nmsg); + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_DETACHED, ms); } msgb_free(msg); @@ -672,7 +1179,7 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) /* if pin was successfully unlocked, then resend request */ if (subscr->sim_pin_required) { - subscr->sim_pin_required = 0; + subscr->sim_pin_required = false; subscr_sim_request(ms); return; } @@ -686,8 +1193,8 @@ static void subscr_sim_query_cb(struct osmocom_ms *ms, struct msgb *msg) if (rc) { LOGP(DMM, LOGL_NOTICE, "SIM reading failed, file invalid\n"); if (subscr_sim_files[subscr->sim_file_index].mandatory) { - vty_notify(ms, NULL); - vty_notify(ms, "SIM failed, data invalid, replace " + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "SIM failed, data invalid, replace " "SIM!\n"); msgb_free(msg); @@ -704,17 +1211,13 @@ ignore: } /* enter PIN */ -void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, - int8_t mode) +static int gsm_subscr_sim_pin_simcard(struct osmocom_ms *ms, const char *pin1, const char *pin2, + int8_t mode) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; uint8_t job; - /* skip, if no real valid SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type)) - return; - switch (mode) { case -1: job = SIM_JOB_PIN1_DISABLE; @@ -736,7 +1239,7 @@ void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, default: if (!subscr->sim_pin_required) { LOGP(DMM, LOGL_ERROR, "No PIN required now\n"); - return; + return 0; } LOGP(DMM, LOGL_INFO, "entering PIN %s\n", pin1); job = SIM_JOB_PIN1_UNLOCK; @@ -744,30 +1247,20 @@ void gsm_subscr_sim_pin(struct osmocom_ms *ms, char *pin1, char *pin2, nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_query, job); if (!nmsg) - return; + return -ENOMEM; memcpy(msgb_put(nmsg, strlen(pin1) + 1), pin1, strlen(pin1) + 1); memcpy(msgb_put(nmsg, strlen(pin2) + 1), pin2, strlen(pin2) + 1); sim_job(ms, nmsg); + return 0; } /* Attach SIM reader, no SIM must be currently attached */ -int gsm_subscr_simcard(struct osmocom_ms *ms) +int gsm_subscr_insert_simcard(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; - if (subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot attach card, until current card " - "is detached.\n"); - return -EBUSY; - } - - /* reset subscriber */ - gsm_subscr_exit(ms); - gsm_subscr_init(ms); - subscr->sim_type = GSM_SIM_TYPE_L1PHY; sprintf(subscr->sim_name, "sim"); - subscr->sim_valid = 1; subscr->ustate = GSM_SIM_U2_NOT_UPDATED; /* start with first index */ @@ -776,7 +1269,7 @@ int gsm_subscr_simcard(struct osmocom_ms *ms) } /* update plmn not allowed list on SIM */ -static int subscr_write_plmn_na(struct osmocom_ms *ms) +static int subscr_write_plmn_na_simcard(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; @@ -784,16 +1277,11 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms) struct gsm_sub_plmn_na *na, *nas[4] = { NULL, NULL, NULL, NULL }; int count = 0, i; uint8_t *data; - uint8_t lai[5]; #ifdef TEST_EMPTY_FPLMN return 0; #endif - /* skip, if no real valid SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) - return 0; - /* get tail list from "PLMN not allowed" */ llist_for_each_entry(na, &subscr->plmn_na, entry) { if (count < 4) @@ -820,11 +1308,8 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms) nsh->file = 0x6f7b; for (i = 0; i < 4; i++) { if (nas[i]) { - gsm48_encode_lai_hex((struct gsm48_loc_area_id *)lai, - nas[i]->mcc, nas[i]->mnc, 0); - *data++ = lai[0]; - *data++ = lai[1]; - *data++ = lai[2]; + osmo_plmn_to_bcd(data, &nas[i]->plmn); + data += 3; } else { *data++ = 0xff; *data++ = 0xff; @@ -837,19 +1322,13 @@ static int subscr_write_plmn_na(struct osmocom_ms *ms) } /* update LOCI on SIM */ -int gsm_subscr_write_loci(struct osmocom_ms *ms) +static int gsm_subscr_write_loci_simcard(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; struct sim_hdr *nsh; struct gsm1111_ef_loci *loci; - /* skip, if no real valid SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type) || !subscr->sim_valid) - return 0; - - LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); - /* write to SIM */ nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, SIM_JOB_UPDATE_BINARY); @@ -865,7 +1344,7 @@ int gsm_subscr_write_loci(struct osmocom_ms *ms) loci->tmsi = htonl(subscr->tmsi); /* LAI */ - gsm48_encode_lai_hex(&loci->lai, subscr->mcc, subscr->mnc, subscr->lac); + gsm48_generate_lai2(&loci->lai, &subscr->lai); /* TMSI time */ loci->tmsi_time = 0xff; @@ -873,13 +1352,59 @@ int gsm_subscr_write_loci(struct osmocom_ms *ms) /* location update status */ switch (subscr->ustate) { case GSM_SIM_U1_UPDATED: - loci->lupd_status = 0x00; + loci->lupd_status = GSM1111_EF_LOCI_LUPD_ST_UPDATED; break; case GSM_SIM_U3_ROAMING_NA: - loci->lupd_status = 0x03; + loci->lupd_status = GSM1111_EF_LOCI_LUPD_ST_LA_NOT_ALLOWED; + break; + default: + loci->lupd_status = GSM1111_EF_LOCI_LUPD_ST_NOT_UPDATED; + } + + sim_job(ms, nmsg); + + return 0; +} + +/* update LOCIGPRS on SIM */ +int gsm_subscr_write_locigprs_simcard(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct sim_hdr *nsh; + struct gsm1111_ef_locigprs *locigprs; + + LOGP(DMM, LOGL_INFO, "Updating LOCI on SIM\n"); + + /* write to SIM */ + nmsg = gsm_sim_msgb_alloc(subscr->sim_handle_update, + SIM_JOB_UPDATE_BINARY); + if (!nmsg) + return -ENOMEM; + nsh = (struct sim_hdr *) nmsg->data; + nsh->path[0] = 0x7f20; + nsh->path[1] = 0; + nsh->file = 0x6f53; + locigprs = (struct gsm1111_ef_locigprs *)msgb_put(nmsg, sizeof(*locigprs)); + + /* P-TMSI, P-TMSI signature */ + locigprs->ptmsi = htonl(subscr->gprs.ptmsi); + locigprs->ptmsi_sig_hi = htonl(subscr->gprs.ptmsi) >> 8; + locigprs->ptmsi_sig_lo = htonl(subscr->gprs.ptmsi) & 0xff; + + /* RAI */ + gsm48_encode_ra(&locigprs->rai, &subscr->gprs.rai); + + /* location update status */ + switch (subscr->gprs.gu_state) { + case GSM_SIM_GU1_UPDATED: + locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_UPDATED; + break; + case GSM_SIM_GU3_ROAMING_NA: + locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_RA_NOT_ALLOWED; break; default: - loci->lupd_status = 0x01; + locigprs->rau_status = GSM1111_EF_LOCIGPRS_RAU_ST_NOT_UPDATED; } sim_job(ms, nmsg); @@ -900,63 +1425,13 @@ static void subscr_sim_update_cb(struct osmocom_ms *ms, struct msgb *msg) msgb_free(msg); } -int gsm_subscr_generate_kc(struct osmocom_ms *ms, uint8_t key_seq, - uint8_t *rand, uint8_t no_sim) +static int gsm_subscr_generate_kc_simcard(struct osmocom_ms *ms, uint8_t key_seq, + const uint8_t *rand, uint8_t no_sim) { struct gsm_subscriber *subscr = &ms->subscr; struct msgb *nmsg; struct sim_hdr *nsh; - /* not a SIM */ - if (!GSM_SIM_IS_READER(subscr->sim_type) - || !subscr->sim_valid || no_sim) { - struct gsm48_mm_event *nmme; - - LOGP(DMM, LOGL_INFO, "Sending dummy authentication response\n"); - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); - if (!nmsg) - return -ENOMEM; - nmme = (struct gsm48_mm_event *) nmsg->data; - nmme->sres[0] = 0x12; - nmme->sres[1] = 0x34; - nmme->sres[2] = 0x56; - nmme->sres[3] = 0x78; - gsm48_mmevent_msg(ms, nmsg); - - return 0; - } - - /* test SIM */ - if (subscr->sim_type == GSM_SIM_TYPE_TEST) { - struct gsm48_mm_event *nmme; - struct gsm_settings *set = &ms->settings; - static struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_GSM - }; - struct osmo_auth_vector _vec; - struct osmo_auth_vector *vec = &_vec; - - auth.algo = set->test_ki_type; - memcpy(auth.u.gsm.ki, set->test_ki, sizeof(auth.u.gsm.ki)); - int ret = osmo_auth_gen_vec(vec, &auth, rand); - if (ret < 0) - return ret; - - /* store sequence */ - subscr->key_seq = key_seq; - memcpy(subscr->key, vec->kc, 8); - - LOGP(DMM, LOGL_INFO, "Sending authentication response\n"); - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); - if (!nmsg) - return -ENOMEM; - nmme = (struct gsm48_mm_event *) nmsg->data; - memcpy(nmme->sres, vec->sres, 4); - gsm48_mmevent_msg(ms, nmsg); - - return 0; - } - LOGP(DMM, LOGL_INFO, "Generating KEY at SIM\n"); /* command to SIM */ @@ -986,8 +1461,8 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg) uint16_t payload_len = msg->len - sizeof(*sh); struct msgb *nmsg; struct sim_hdr *nsh; - struct gsm48_mm_event *nmme; uint8_t *data; + struct osmobb_l23_subscr_sim_auth_resp_sig_data sd; /* error handling */ if (sh->job_type == SIM_JOB_ERROR) { @@ -1023,278 +1498,38 @@ static void subscr_sim_key_cb(struct osmocom_ms *ms, struct msgb *msg) sim_job(ms, nmsg); /* return signed response */ - nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); - if (!nmsg) - return; - nmme = (struct gsm48_mm_event *) nmsg->data; - memcpy(nmme->sres, payload, 4); - gsm48_mmevent_msg(ms, nmsg); - + sd.ms = ms; + memcpy(sd.sres, payload, 4); + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_AUTH_RESP, &sd); msgb_free(msg); } -/* - * detach - */ - -/* Detach card */ -int gsm_subscr_remove(struct osmocom_ms *ms) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; - - if (!subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); - return -EINVAL; - } - - /* remove card */ - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); - - return 0; -} - -/* - * state and lists - */ - -static const char *subscr_ustate_names[] = { - "U0_NULL", - "U1_UPDATED", - "U2_NOT_UPDATED", - "U3_ROAMING_NA" -}; - -/* change to new U state */ -void new_sim_ustate(struct gsm_subscriber *subscr, int state) -{ - LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, - subscr_ustate_names[subscr->ustate], - subscr_ustate_names[state]); - - subscr->ustate = state; -} - -/* del forbidden PLMN. if MCC==0, flush complete list */ -int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc) -{ - struct gsm_sub_plmn_na *na, *na2; - int deleted = 0; - - llist_for_each_entry_safe(na, na2, &subscr->plmn_na, entry) { - if (!mcc || (na->mcc == mcc && na->mnc == mnc)) { - LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " - "PLMNs (mcc=%s, mnc=%s)\n", - gsm_print_mcc(mcc), gsm_print_mnc(mnc)); - llist_del(&na->entry); - talloc_free(na); - deleted = 1; - if (mcc) - break; - } - } - - if (deleted) { - /* update plmn not allowed list on SIM */ - subscr_write_plmn_na(subscr->ms); - } - - return -EINVAL; -} - -/* add forbidden PLMN */ -int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc, uint8_t cause) -{ - struct gsm_sub_plmn_na *na; - - /* if already in the list, remove and add to tail */ - gsm_subscr_del_forbidden_plmn(subscr, mcc, mnc); - - LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " - "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); - na = talloc_zero(subscr->ms, struct gsm_sub_plmn_na); - if (!na) - return -ENOMEM; - na->mcc = mcc; - na->mnc = mnc; - na->cause = cause ? : -1; /* cause 0 is not allowed */ - llist_add_tail(&na->entry, &subscr->plmn_na); - - /* don't add Home PLMN to SIM */ - if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi)) - return -EINVAL; - - /* update plmn not allowed list on SIM */ - subscr_write_plmn_na(subscr->ms); - - return 0; -} - -/* search forbidden PLMN */ -int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, - uint16_t mnc) -{ - struct gsm_sub_plmn_na *na; - - llist_for_each_entry(na, &subscr->plmn_na, entry) { - if (na->mcc == mcc && na->mnc == mnc) - return 1; - } - - return 0; -} - -int gsm_subscr_get_key_seq(struct osmocom_ms *ms, struct gsm_subscriber *subscr) -{ - if (ms->settings.force_rekey) - return 7; - else - return subscr->key_seq; -} - -int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, - void (*print)(void *, const char *, ...), void *priv) -{ - struct gsm_subscriber *subscr = &ms->subscr; - struct gsm_sub_plmn_na *temp; - - print(priv, "MCC |MNC |cause\n"); - print(priv, "-------+-------+-------\n"); - llist_for_each_entry(temp, &subscr->plmn_na, entry) - print(priv, "%s |%s%s |#%d\n", - gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), - ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause); - - return 0; -} - -/* dump subscriber */ -void gsm_subscr_dump(struct gsm_subscriber *subscr, - void (*print)(void *, const char *, ...), void *priv) -{ - int i; - struct gsm_sub_plmn_list *plmn_list; - struct gsm_sub_plmn_na *plmn_na; - - print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); - - if (!subscr->sim_valid) { - print(priv, " No SIM present.\n"); - return; - } - - print(priv, " IMSI: %s\n", subscr->imsi); - if (subscr->iccid[0]) - print(priv, " ICCID: %s\n", subscr->iccid); - if (subscr->sim_spn[0]) - print(priv, " Service Provider Name: %s\n", subscr->sim_spn); - if (subscr->msisdn[0]) - print(priv, " MSISDN: %s\n", subscr->msisdn); - if (subscr->sms_sca[0]) - print(priv, " SMS Service Center Address: %s\n", - subscr->sms_sca); - print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate], - (subscr->imsi_attached) ? "attached" : "detached"); - if (subscr->tmsi != 0xffffffff) - print(priv, " TMSI 0x%08x", subscr->tmsi); - if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { - print(priv, "\n"); - print(priv, " LAI: MCC %s MNC %s LAC 0x%04x " - "(%s, %s)\n", gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac, - gsm_get_mcc(subscr->mcc), - gsm_get_mnc(subscr->mcc, subscr->mnc)); - } else - print(priv, " LAI: invalid\n"); - if (subscr->key_seq != 7) { - print(priv, " Key: sequence %d ", subscr->key_seq); - for (i = 0; i < sizeof(subscr->key); i++) - print(priv, " %02x", subscr->key[i]); - print(priv, "\n"); - } - if (subscr->plmn_valid) - print(priv, " Registered PLMN: MCC %s MNC %s (%s, %s)\n", - gsm_print_mcc(subscr->plmn_mcc), - gsm_print_mnc(subscr->plmn_mnc), - gsm_get_mcc(subscr->plmn_mcc), - gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); - print(priv, " Access barred cells: %s\n", - (subscr->acc_barr) ? "yes" : "no"); - print(priv, " Access classes:"); - for (i = 0; i < 16; i++) - if ((subscr->acc_class & (1 << i))) - print(priv, " C%d", i); - print(priv, "\n"); - if (!llist_empty(&subscr->plmn_list)) { - print(priv, " List of preferred PLMNs:\n"); - print(priv, " MCC |MNC\n"); - print(priv, " -------+-------\n"); - llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) - print(priv, " %s |%s (%s, %s)\n", - gsm_print_mcc(plmn_list->mcc), - gsm_print_mnc(plmn_list->mnc), - gsm_get_mcc(plmn_list->mcc), - gsm_get_mnc(plmn_list->mcc, plmn_list->mnc)); - } - if (!llist_empty(&subscr->plmn_na)) { - print(priv, " List of forbidden PLMNs:\n"); - print(priv, " MCC |MNC |cause\n"); - print(priv, " -------+-------+-------\n"); - llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) - print(priv, " %s |%s%s |#%d " - "(%s, %s)\n", gsm_print_mcc(plmn_na->mcc), - gsm_print_mnc(plmn_na->mnc), - ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"", - plmn_na->cause, gsm_get_mcc(plmn_na->mcc), - gsm_get_mnc(plmn_na->mcc, plmn_na->mnc)); - } -} - -/* - * SAP interface integration - */ +/*********************************************** + * sapcard backend + * (SAP interface integration, reuses some parts of simcard backend) + ***********************************************/ /* Attach SIM card over SAP */ -int gsm_subscr_sapcard(struct osmocom_ms *ms) +int gsm_subscr_insert_sapcard(struct osmocom_ms *ms) { struct gsm_subscriber *subscr = &ms->subscr; - struct msgb *nmsg; int rc; - if (subscr->sim_valid) { - LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " - "is detached.\n"); - return -EBUSY; - } - - /* reset subscriber */ - gsm_subscr_exit(ms); - gsm_subscr_init(ms); - subscr->sim_type = GSM_SIM_TYPE_SAP; sprintf(subscr->sim_name, "sap"); - subscr->sim_valid = 1; /* Try to connect to the SAP interface */ - vty_notify(ms, NULL); - vty_notify(ms, "Connecting to the SAP interface...\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Connecting to the SAP interface...\n"); rc = sap_open(ms); if (rc < 0) { LOGP(DSAP, LOGL_ERROR, "Failed during sap_open(), no SAP based SIM reader\n"); - vty_notify(ms, "SAP connection error!\n"); + l23_vty_ms_notify(ms, "SAP connection error!\n"); ms->sap_wq.bfd.fd = -1; /* Detach SIM */ subscr->sim_valid = 0; - nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); - if (!nmsg) - return -ENOMEM; - gsm48_mmr_downmsg(ms, nmsg); + osmo_signal_dispatch(SS_L23_SUBSCR, S_L23_SUBSCR_SIM_DETACHED, ms); return rc; } @@ -1303,7 +1538,7 @@ int gsm_subscr_sapcard(struct osmocom_ms *ms) } /* Deattach sapcard */ -int gsm_subscr_remove_sapcard(struct osmocom_ms *ms) +static int gsm_subscr_remove_sapcard(struct osmocom_ms *ms) { return sap_close(ms); } diff --git a/src/host/layer23/src/mobile/support.c b/src/host/layer23/src/common/support.c index e9361a35..a31b456c 100644 --- a/src/host/layer23/src/mobile/support.c +++ b/src/host/layer23/src/common/support.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -24,6 +20,7 @@ #include <string.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> void gsm_support_init(struct osmocom_ms *ms) { @@ -37,9 +34,9 @@ void gsm_support_init(struct osmocom_ms *ms) /* revision level */ sup->rev_lev = 1; /* phase 2 mobile station */ /* support of VGCS */ - sup->vgcs = 0; /* no */ + sup->vgcs = true; /* yes */ /* support of VBS */ - sup->vbs = 0; /* no */ + sup->vbs = true; /* yes */ /* support of SMS */ sup->sms_ptp = 1; /* no */ /* screening indicator */ @@ -100,6 +97,14 @@ void gsm_support_init(struct osmocom_ms *ms) sup->full_v3 = 0; sup->half_v1 = 1; sup->half_v3 = 0; + + /* CSD modes */ + sup->csd_tch_f144 = 0; + sup->csd_tch_f96 = 1; + sup->csd_tch_f48 = 1; + sup->csd_tch_h48 = 1; + sup->csd_tch_f24 = 1; + sup->csd_tch_h24 = 1; } /* (3.2.1) maximum channels to scan within each band */ @@ -176,6 +181,14 @@ void gsm_support_dump(struct osmocom_ms *ms, print(priv, " Full-Rate V3 : %s\n", SUP_SET(full_v3)); print(priv, " Half-Rate V1 : %s\n", SUP_SET(half_v1)); print(priv, " Half-Rate V3 : %s\n", SUP_SET(half_v3)); + + print(priv, " CSD TCH/F14.4: %s\n", SUP_SET(csd_tch_f144)); + print(priv, " CSD TCH/F9.6 : %s\n", SUP_SET(csd_tch_f96)); + print(priv, " CSD TCH/F4.8 : %s\n", SUP_SET(csd_tch_f48)); + print(priv, " CSD TCH/H4.8 : %s\n", SUP_SET(csd_tch_h48)); + print(priv, " CSD TCH/F2.4 : %s\n", SUP_SET(csd_tch_f24)); + print(priv, " CSD TCH/H2.4 : %s\n", SUP_SET(csd_tch_h24)); + print(priv, " Min RXLEV : %d\n", set->min_rxlev_dbm); } diff --git a/src/host/layer23/src/common/sysinfo.c b/src/host/layer23/src/common/sysinfo.c index f927773f..efbc5198 100644 --- a/src/host/layer23/src/common/sysinfo.c +++ b/src/host/layer23/src/common/sysinfo.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -24,15 +20,18 @@ #include <string.h> #include <arpa/inet.h> +#include <osmocom/core/utils.h> #include <osmocom/core/bitvec.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/gsm48_rest_octets.h> + +#include <osmocom/gprs/rlcmac/csn1_defs.h> #include <osmocom/bb/common/osmocom_data.h> #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/sysinfo.h> -#define MIN(a, b) ((a < b) ? a : b) - /* * dumping */ @@ -51,28 +50,45 @@ char *gsm_print_arfcn(uint16_t arfcn) return text; } -/* check if the cell 'talks' about DCS (0) or PCS (1) */ -uint8_t gsm_refer_pcs(uint16_t arfcn, struct gsm48_sysinfo *s) +/* Check if the cell 'talks' about DCS (false) or PCS (true) */ +bool gsm_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s) { /* If ARFCN is PCS band, the cell refers to PCS */ - if ((arfcn & ARFCN_PCS)) - return 1; + if ((cell_arfcn & ARFCN_PCS)) + return true; /* If no SI1 is available, we assume DCS. Be sure to call this * function only if SI 1 is available. */ - if (!s->si1) + if (!cell_s->si1) return 0; /* If band indicator indicates PCS band, the cell refers to PCSThe */ - return s->band_ind; + return cell_s->band_ind; +} + +/* Change the given ARFCN to PCS ARFCN, if it is in the PCS channel range and the cell refers to PCS band. */ +uint16_t gsm_arfcn_refer_pcs(uint16_t cell_arfcn, const struct gsm48_sysinfo *cell_s, uint16_t arfcn) +{ + /* If ARFCN is not one of the overlapping channel of PCS and DCS. */ + if (arfcn < 512 || arfcn > 810) + return arfcn; + + /* If the 'cell' does not refer to PCS. */ + if (!gsm_refer_pcs(cell_arfcn, cell_s)) + return arfcn; + + /* The ARFCN is PCS, because the ARFCN is in the PCS range and the cell refers to it. */ + return arfcn | ARFCN_PCS; } -int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, - void (*print)(void *, const char *, ...), void *priv, uint8_t *freq_map) +int gsm48_sysinfo_dump(const struct gsm48_sysinfo *s, uint16_t arfcn, + void (*print)(void *, const char *, ...), + void *priv, uint8_t *freq_map) { - char buffer[81]; + char buffer[82]; int i, j, k, index; int refer_pcs = gsm_refer_pcs(arfcn, s); + int rc; /* available sysinfos */ print(priv, "ARFCN = %s channels 512+ refer to %s\n", @@ -175,7 +191,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, /* frequency map */ for (i = 0; i < 1024; i += 64) { - sprintf(buffer, " %3d ", i); + snprintf(buffer, sizeof(buffer), " %3d ", i); for (j = 0; j < 64; j++) { index = i+j; if (refer_pcs && index >= 512 && index <= 885) @@ -197,7 +213,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, } for (; j < 64; j++) buffer[j + 5] = ' '; - sprintf(buffer + 69, " %d", i + 63); + snprintf(buffer + 69, sizeof(buffer) - 69, " %d", i + 63); print(priv, "%s\n", buffer); } print(priv, " 'S' = serv. cell 'n' = SI2 (neigh.) 'r' = SI5 (rep.) " @@ -205,12 +221,10 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, /* serving cell */ print(priv, "Serving Cell:\n"); - print(priv, " BSIC = %d,%d MCC = %s MNC = %s LAC = 0x%04x Cell ID " - "= 0x%04x\n", s->bsic >> 3, s->bsic & 0x7, - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac, - s->cell_id); - print(priv, " Country = %s Network Name = %s\n", gsm_get_mcc(s->mcc), - gsm_get_mnc(s->mcc, s->mnc)); + print(priv, " BSIC = %d,%d LAI = %s Cell ID = 0x%04x\n", + s->bsic >> 3, s->bsic & 0x7, osmo_lai_name(&s->lai), s->cell_id); + print(priv, " Country = %s Network Name = %s\n", + gsm_get_mcc(s->lai.plmn.mcc), gsm_get_mnc(&s->lai.plmn)); print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n", s->max_retrans, s->tx_integer, (s->reest_denied) ? "denied" : "allowed"); @@ -256,7 +270,7 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, print(priv, "\n"); /* cell selection */ - print(priv, "MX_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d " + print(priv, "MS_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d " "NECI = %d ACS = %d\n", s->ms_txpwr_max_cch, s->cell_resel_hyst_db, s->rxlev_acc_min_db, s->neci, s->acs); @@ -285,6 +299,16 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, print(priv, " BS-PA-MFMS = %d Attachment = %s\n", s->pag_mf_periods, (s->att_allowed) ? "allowed" : "denied"); print(priv, "BS-AG_BLKS_RES = %d ", s->bs_ag_blks_res); + if (!s->nch) + print(priv, "NCH not available "); + else { + uint8_t num_blocks, first_block; + rc = osmo_gsm48_si1ro_nch_pos_decode(s->nch_position, &num_blocks, &first_block); + if (rc < 0) + print(priv, "NCH Position invalid "); + else + print(priv, "NCH Position %u / %u blocks ", first_block, num_blocks); + } if (s->t3212) print(priv, "T3212 = %d sec.\n", s->t3212); else @@ -302,12 +326,54 @@ int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, return 0; } +int gsm48_si10_dump(const struct gsm48_sysinfo *s, void (*print)(void *, const char *, ...), void *priv) +{ + const struct si10_cell_info *c; + int i; + + if (!s || !s->si10) { + print(priv, "No group channel neighbor information available.\n"); + return 0; + } + + if (!s->si10_cell_num) { + print(priv, "No group channel neighbors exist.\n"); + return 0; + } + + /* Group call neighbor cells. */ + print(priv, "Group channel neighbor cells (current or last call):\n"); + for (i = 0; i < s->si10_cell_num; i++) { + c = &s->si10_cell[i]; + print(priv, " index = %d", c->index); + if (c->arfcn >= 0) + print(priv, " ARFCN = %d", c->arfcn); + else + print(priv, " ARFCN = not in SI5*"); + print(priv, " BSIC = %d,%d", c->bsic >> 3, c->bsic & 0x7); + if (c->barred) { + print(priv, " barred"); + continue; + } + if (c->la_different) + print(priv, " CRH = %d", c->cell_resel_hyst_db); + print(priv, " MS_TXPWR_MAX_CCCH = %d\n", c->ms_txpwr_max_cch); + print(priv, " RXLEV_MIN = %d", c->rxlev_acc_min_db); + print(priv, " CRO = %d", c->cell_resel_offset); + print(priv, " TEMP_OFFSET = %d", c->temp_offset); + print(priv, " PENALTY_TIME = %d", c->penalty_time); + } + print(priv, "\n"); + + return 0; +} + /* * decoding */ -int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, - uint16_t *arfcn) +int gsm48_decode_chan_h0(const struct gsm48_chan_desc *cd, + uint8_t *tsc, uint16_t *arfcn) { *tsc = cd->h0.tsc; *arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8); @@ -315,8 +381,8 @@ int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, return 0; } -int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc, - uint8_t *maio, uint8_t *hsn) +int gsm48_decode_chan_h1(const struct gsm48_chan_desc *cd, + uint8_t *tsc, uint8_t *maio, uint8_t *hsn) { *tsc = cd->h1.tsc; *maio = cd->h1.maio_low | (cd->h1.maio_high << 2); @@ -326,8 +392,9 @@ int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc, } /* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ -static int decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, - uint8_t len, uint8_t mask, uint8_t frqt) +static int decode_freq_list(struct gsm_sysinfo_freq *f, + const uint8_t *cd, uint8_t len, + uint8_t mask, uint8_t frqt) { #if 0 /* only Bit map 0 format for P-GSM */ @@ -341,7 +408,7 @@ static int decode_freq_list(struct gsm_sysinfo_freq *f, uint8_t *cd, /* decode "Cell Selection Parameters" (10.5.2.4) */ static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s, - struct gsm48_cell_sel_par *cs) + const struct gsm48_cell_sel_par *cs) { s->ms_txpwr_max_cch = cs->ms_txpwr_max_ccch; s->cell_resel_hyst_db = cs->cell_resel_hyst * 2; @@ -354,7 +421,7 @@ static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s, /* decode "Cell Options (BCCH)" (10.5.2.3) */ static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s, - struct gsm48_cell_options *co) + const struct gsm48_cell_options *co) { s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4; s->bcch_dtx = co->dtx; @@ -365,7 +432,7 @@ static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s, /* decode "Cell Options (SACCH)" (10.5.2.3a) */ static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s, - struct gsm48_cell_options *co) + const struct gsm48_cell_options *co) { s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4; s->sacch_dtx = co->dtx; @@ -376,7 +443,7 @@ static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s, /* decode "Control Channel Description" (10.5.2.11) */ static int gsm48_decode_ccd(struct gsm48_sysinfo *s, - struct gsm48_control_channel_descr *cc) + const struct gsm48_control_channel_descr *cc) { s->ccch_conf = cc->ccch_conf; s->bs_ag_blks_res = cc->bs_ag_blks_res; @@ -389,7 +456,8 @@ static int gsm48_decode_ccd(struct gsm48_sysinfo *s, /* decode "Mobile Allocation" (10.5.2.21) */ int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq, - uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4) + const uint8_t *ma, uint8_t len, + uint16_t *hopping, uint8_t *hopp_len, int si4) { int i, j = 0; uint16_t f[len << 3]; @@ -442,16 +510,16 @@ int gsm48_decode_mobile_alloc(struct gsm_sysinfo_freq *freq, } /* Rach Control decode tables */ -static uint8_t gsm48_max_retrans[4] = { +static const uint8_t gsm48_max_retrans[4] = { 1, 2, 4, 7 }; -static uint8_t gsm48_tx_integer[16] = { +static const uint8_t gsm48_tx_integer[16] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50 }; /* decode "RACH Control Parameter" (10.5.2.29) */ static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s, - struct gsm48_rach_control *rc) + const struct gsm48_rach_control *rc) { s->reest_denied = rc->re; s->cell_barr = rc->cell_bar; @@ -462,7 +530,7 @@ static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s, return 0; } static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s, - struct gsm48_rach_control *rc) + const struct gsm48_rach_control *rc) { s->nb_reest_denied = rc->re; s->nb_cell_barr = rc->cell_bar; @@ -474,14 +542,13 @@ static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s, } /* decode "SI 1 Rest Octets" (10.5.2.32) */ -static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si, - uint8_t len) +static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, + const uint8_t *si, uint8_t len) { - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data_len = len; - bv.data = si; + struct bitvec bv = { + .data_len = len, + .data = (uint8_t *)si, + }; /* Optional Selection Parameters */ if (bitvec_get_bit_high(&bv) == H) { @@ -489,23 +556,19 @@ static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si, s->nch_position = bitvec_get_uint(&bv, 5); } else s->nch = 0; - if (bitvec_get_bit_high(&bv) == H) - s->band_ind = 1; - else - s->band_ind = 0; + s->band_ind = (bitvec_get_bit_high(&bv) == H); return 0; } /* decode "SI 3 Rest Octets" (10.5.2.34) */ -static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si, - uint8_t len) +static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, + const uint8_t *si, uint8_t len) { - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data_len = len; - bv.data = si; + struct bitvec bv = { + .data_len = len, + .data = (uint8_t *)si, + }; /* Optional Selection Parameters */ if (bitvec_get_bit_high(&bv) == H) { @@ -540,24 +603,23 @@ static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si, s->sched = 0; /* GPRS Indicator */ if (bitvec_get_bit_high(&bv) == H) { - s->gprs = 1; - s->gprs_ra_colour = bitvec_get_uint(&bv, 3); - s->gprs_si13_pos = bitvec_get_uint(&bv, 1); + s->gprs.supported = 1; + s->gprs.ra_colour = bitvec_get_uint(&bv, 3); + s->gprs.si13_pos = bitvec_get_uint(&bv, 1); } else - s->gprs = 0; + s->gprs.supported = 0; return 0; } /* decode "SI 4 Rest Octets" (10.5.2.35) */ -static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si, - uint8_t len) +static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, + const uint8_t *si, uint8_t len) { - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data_len = len; - bv.data = si; + struct bitvec bv = { + .data_len = len, + .data = (uint8_t *)si, + }; /* Optional Selection Parameters */ if (bitvec_get_bit_high(&bv) == H) { @@ -576,33 +638,215 @@ static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si, s->po = 0; /* GPRS Indicator */ if (bitvec_get_bit_high(&bv) == H) { - s->gprs = 1; - s->gprs_ra_colour = bitvec_get_uint(&bv, 3); - s->gprs_si13_pos = bitvec_get_uint(&bv, 1); + s->gprs.supported = 1; + s->gprs.ra_colour = bitvec_get_uint(&bv, 3); + s->gprs.si13_pos = bitvec_get_uint(&bv, 1); } else - s->gprs = 0; + s->gprs.supported = 0; // todo: more rest octet bits return 0; } -/* decode "SI 6 Rest Octets" (10.5.2.35a) */ -static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si, - uint8_t len) +/* TODO: decode "SI 6 Rest Octets" (10.5.2.35a) */ +static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, + const uint8_t *si, uint8_t len) { return 0; } +/* Decode "SI 10 Rest Octets" (10.5.2.44) */ +static int gsm48_decode_si10_rest_first(struct gsm48_sysinfo *s, struct bitvec *bv, + struct si10_cell_info *c) +{ + uint8_t ba_ind; + + /* <BA ind : bit(1)> */ + ba_ind = bitvec_get_uint(bv, 1); + if (ba_ind != s->nb_ba_ind_si5) { + LOGP(DRR, LOGL_NOTICE, "SI10: BA_IND %u != BA_IND %u of SI5!\n", ba_ind, s->nb_ba_ind_si5); + return EOF; + } + + /* { L <spare padding> | H <neighbour information> } */ + if (bitvec_get_bit_high(bv) != H) { + LOGP(DRR, LOGL_INFO, "SI10: No neighbor cell defined.\n"); + return EOF; + } + + /* <first frequency: bit(5)> */ + c->index = bitvec_get_uint(bv, 5); + + /* <bsic : bit(6)> */ + c->bsic = bitvec_get_uint(bv, 6); + + /* { H <cell parameters> | L } */ + if (bitvec_get_bit_high(bv) != H) { + LOGP(DRR, LOGL_NOTICE, "SI10: No cell parameters for first cell, cannot continue to decode!\n"); + return EOF; + } + + /* <cell barred (H)> | L <further cell info> */ + if (bitvec_get_bit_high(bv) == H) { + c->barred = true; + return 0; + } + + /* { H <cell reselect hysteresis : bit(3)> | L } */ + if (bitvec_get_bit_high(bv) == H) { + c->la_different = true; + c->cell_resel_hyst_db = bitvec_get_uint(bv, 3) * 2; + } + + /* <ms txpwr max cch : bit(5)> */ + c->ms_txpwr_max_cch = bitvec_get_uint(bv, 5); + /* <rxlev access min : bit(6)> */ + c->rxlev_acc_min_db = rxlev2dbm(bitvec_get_uint(bv, 6)); + /* <cell reselect offset : bit(6)> */ + c->cell_resel_offset = bitvec_get_uint(bv, 6); + /* <temporary offset : bit(3)> */ + c->temp_offset = bitvec_get_uint(bv, 3); + /* <penalty time : bit(5)> */ + c->penalty_time = bitvec_get_uint(bv, 5); + + return 0; +} + +static int gsm48_decode_si10_rest_other(struct gsm48_sysinfo *s, struct bitvec *bv, + struct si10_cell_info *c) +{ + int rc; + + /* { H <info field> }** L <spare padding> */ + if (bitvec_get_bit_high(bv) != H) + return EOF; + + c->index = (c->index + 1) & 0x1f; + /* <next frequency (H)>** L <differential cell info> */ + /* Increment frequency number for every <info field> and every <next frequency> occurrence. */ + while ((rc = bitvec_get_bit_high(bv)) == H) + c->index = (c->index + 1) & 0x1f; + if (rc < 0) + goto short_read; + + /* { H <BCC : bit(3)> | L <bsic : bit(6)> } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + rc = bitvec_get_uint(bv, 3); + if (rc < 0) + goto short_read; + c->bsic = (c->bsic & 0x07) | rc; + } else { + rc = bitvec_get_uint(bv, 6); + if (rc < 0) + goto short_read; + c->bsic = rc; + } + + /* { H <diff cell pars> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc != H) + return 0; + + /* <cell barred (H)> | L <further cell info> */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + c->barred = true; + return 0; + } + + /* { H <cell reselect hysteresis : bit(3)> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + c->la_different = true; + rc = bitvec_get_uint(bv, 3); + if (rc < 0) + goto short_read; + c->cell_resel_hyst_db = bitvec_get_uint(bv, 3) * 2; + } + + /* { H <ms txpwr max cch : bit(5)> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + rc = bitvec_get_uint(bv, 5); + if (rc < 0) + goto short_read; + c->ms_txpwr_max_cch = rc; + } + + /* { H <rxlev access min : bit(6)> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + rc = bitvec_get_uint(bv, 6); + if (rc < 0) + goto short_read; + c->rxlev_acc_min_db = rxlev2dbm(rc); + } else + c->rxlev_acc_min_db = -110; + + /* { H <cell reselect offset : bit(6)> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + rc = bitvec_get_uint(bv, 6); + if (rc < 0) + goto short_read; + c->cell_resel_offset = rc; + } + + /* { H <temporary offset : bit(3)> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + rc = bitvec_get_uint(bv, 3); + if (rc < 0) + goto short_read; + c->temp_offset = rc; + } + + /* { H <penalty time : bit(5)> | L } */ + rc = bitvec_get_bit_high(bv); + if (rc < 0) + goto short_read; + if (rc == H) { + rc = bitvec_get_uint(bv, 5); + if (rc < 0) + goto short_read; + c->penalty_time = rc; + } + + return 0; + +short_read: + LOGP(DRR, LOGL_NOTICE, "SI10: Short read of differential cell info.\n"); + return -EINVAL; +} + int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_1 *si, int len) + const struct gsm48_system_information_type_1 *si, int len) { int payload_len = len - sizeof(*si); - memcpy(s->si1_msg, si, MIN(len, sizeof(s->si1_msg))); + memcpy(s->si1_msg, si, OSMO_MIN(len, sizeof(s->si1_msg))); /* Cell Channel Description */ decode_freq_list(s->freq, si->cell_channel_description, - sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV); + sizeof(si->cell_channel_description), + 0xce, FREQ_TYPE_SERV); /* RACH Control Parameter */ gsm48_decode_rach_ctl_param(s, &si->rach_control); /* SI 1 Rest Octets */ @@ -612,26 +856,26 @@ int gsm48_decode_sysinfo1(struct gsm48_sysinfo *s, s->si1 = 1; if (s->si4) { - LOGP(DRR, LOGL_NOTICE, "Now updating previously received " - "SYSTEM INFORMATION 4\n"); - gsm48_decode_sysinfo4(s, - (struct gsm48_system_information_type_4 *) s->si4_msg, - sizeof(s->si4_msg)); + const struct gsm48_system_information_type_4 *si4 = (void *)s->si4_msg; + LOGP(DRR, LOGL_NOTICE, + "Now updating previously received SYSTEM INFORMATION 4\n"); + gsm48_decode_sysinfo4(s, si4, sizeof(s->si4_msg)); } return 0; } int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_2 *si, int len) + const struct gsm48_system_information_type_2 *si, int len) { - memcpy(s->si2_msg, si, MIN(len, sizeof(s->si2_msg))); + memcpy(s->si2_msg, si, OSMO_MIN(len, sizeof(s->si2_msg))); /* Neighbor Cell Description */ - s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1; - s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 4) & 1; decode_freq_list(s->freq, si->bcch_frequency_list, - sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2); + sizeof(si->bcch_frequency_list), + 0xce, FREQ_TYPE_NCELL_2); /* NCC Permitted */ s->nb_ncc_permitted_si2 = si->ncc_permitted; /* RACH Control Parameter */ @@ -643,13 +887,13 @@ int gsm48_decode_sysinfo2(struct gsm48_sysinfo *s, } int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_2bis *si, int len) + const struct gsm48_system_information_type_2bis *si, int len) { - memcpy(s->si2b_msg, si, MIN(len, sizeof(s->si2b_msg))); + memcpy(s->si2b_msg, si, OSMO_MIN(len, sizeof(s->si2b_msg))); /* Neighbor Cell Description */ - s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1; - s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 4) & 1; decode_freq_list(s->freq, si->bcch_frequency_list, sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2bis); /* RACH Control Parameter */ @@ -661,13 +905,13 @@ int gsm48_decode_sysinfo2bis(struct gsm48_sysinfo *s, } int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_2ter *si, int len) + const struct gsm48_system_information_type_2ter *si, int len) { - memcpy(s->si2t_msg, si, MIN(len, sizeof(s->si2t_msg))); + memcpy(s->si2t_msg, si, OSMO_MIN(len, sizeof(s->si2t_msg))); /* Neighbor Cell Description 2 */ - s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3; - s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 1; + s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 5) & 3; + s->nb_ba_ind_si2ter = (si->ext_bcch_frequency_list[0] >> 4) & 1; decode_freq_list(s->freq, si->ext_bcch_frequency_list, sizeof(si->ext_bcch_frequency_list), 0x8e, FREQ_TYPE_NCELL_2ter); @@ -678,16 +922,16 @@ int gsm48_decode_sysinfo2ter(struct gsm48_sysinfo *s, } int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_3 *si, int len) + const struct gsm48_system_information_type_3 *si, int len) { int payload_len = len - sizeof(*si); - memcpy(s->si3_msg, si, MIN(len, sizeof(s->si3_msg))); + memcpy(s->si3_msg, si, OSMO_MIN(len, sizeof(s->si3_msg))); /* Cell Identity */ s->cell_id = ntohs(si->cell_identity); /* LAI */ - gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac); + gsm48_decode_lai2(&si->lai, &s->lai); /* Control Channel Description */ gsm48_decode_ccd(s, &si->control_channel_desc); /* Cell Options (BCCH) */ @@ -700,9 +944,8 @@ int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s, if (payload_len >= 4) gsm48_decode_si3_rest(s, si->rest_octets, payload_len); - LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s " - "lac 0x%04x)\n", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); + LOGP(DRR, LOGL_INFO, + "New SYSTEM INFORMATION 3 (lai=%s)\n", osmo_lai_name(&s->lai)); s->si3 = 1; @@ -710,17 +953,17 @@ int gsm48_decode_sysinfo3(struct gsm48_sysinfo *s, } int gsm48_decode_sysinfo4(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_4 *si, int len) + const struct gsm48_system_information_type_4 *si, int len) { int payload_len = len - sizeof(*si); - uint8_t *data = si->data; - struct gsm48_chan_desc *cd; + const uint8_t *data = si->data; + const struct gsm48_chan_desc *cd; - memcpy(s->si4_msg, si, MIN(len, sizeof(s->si4_msg))); + memcpy(s->si4_msg, si, OSMO_MIN(len, sizeof(s->si4_msg))); /* LAI */ - gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac); + gsm48_decode_lai2(&si->lai, &s->lai); /* Cell Selection Parameters */ gsm48_decode_cell_sel_param(s, &si->cell_sel_par); /* RACH Control Parameter */ @@ -733,15 +976,13 @@ short_read: LOGP(DRR, LOGL_NOTICE, "Short read!\n"); return -EIO; } - cd = (struct gsm48_chan_desc *) (data + 1); + cd = (const struct gsm48_chan_desc *)(data + 1); s->chan_nr = cd->chan_nr; - if (cd->h0.h) { - s->h = 1; + s->h = cd->h0.h; + if (s->h) gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn); - } else { - s->h = 0; + else gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn); - } payload_len -= 4; data += 4; } @@ -751,11 +992,10 @@ short_read: goto short_read; if (!s->si1) { LOGP(DRR, LOGL_NOTICE, "Ignoring CBCH allocation of " - "SYSTEM INFORMATION 4 until SI 1 is " - "received.\n"); + "SYSTEM INFORMATION 4 until SI 1 is received.\n"); } else { gsm48_decode_mobile_alloc(s->freq, data + 2, data[1], - s->hopping, &s->hopp_len, 1); + s->hopping, &s->hopp_len, 1); } payload_len -= 2 + data[1]; data += 2 + data[1]; @@ -770,59 +1010,65 @@ short_read: } int gsm48_decode_sysinfo5(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_5 *si, int len) + const struct gsm48_system_information_type_5 *si, int len) { - memcpy(s->si5_msg, si, MIN(len, sizeof(s->si5_msg))); + memcpy(s->si5_msg, si, OSMO_MIN(len, sizeof(s->si5_msg))); /* Neighbor Cell Description */ - s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1; - s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 4) & 1; decode_freq_list(s->freq, si->bcch_frequency_list, - sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5); + sizeof(si->bcch_frequency_list), + 0xce, FREQ_TYPE_REP_5); s->si5 = 1; + s->si10 = false; return 0; } int gsm48_decode_sysinfo5bis(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_5bis *si, int len) + const struct gsm48_system_information_type_5bis *si, int len) { - memcpy(s->si5b_msg, si, MIN(len, sizeof(s->si5b_msg))); + memcpy(s->si5b_msg, si, OSMO_MIN(len, sizeof(s->si5b_msg))); /* Neighbor Cell Description */ - s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1; - s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 4) & 1; decode_freq_list(s->freq, si->bcch_frequency_list, - sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis); + sizeof(si->bcch_frequency_list), + 0xce, FREQ_TYPE_REP_5bis); s->si5bis = 1; + s->si10 = false; return 0; } int gsm48_decode_sysinfo5ter(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_5ter *si, int len) + const struct gsm48_system_information_type_5ter *si, int len) { - memcpy(s->si5t_msg, si, MIN(len, sizeof(s->si5t_msg))); + memcpy(s->si5t_msg, si, OSMO_MIN(len, sizeof(s->si5t_msg))); /* Neighbor Cell Description */ - s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 6) & 3; - s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 5) & 1; + s->nb_multi_rep_si5ter = (si->bcch_frequency_list[0] >> 5) & 3; + s->nb_ba_ind_si5ter = (si->bcch_frequency_list[0] >> 4) & 1; decode_freq_list(s->freq, si->bcch_frequency_list, - sizeof(si->bcch_frequency_list), 0x8e, FREQ_TYPE_REP_5ter); + sizeof(si->bcch_frequency_list), + 0x8e, FREQ_TYPE_REP_5ter); s->si5ter = 1; + s->si10 = false; return 0; } int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s, - struct gsm48_system_information_type_6 *si, int len) + const struct gsm48_system_information_type_6 *si, int len) { int payload_len = len - sizeof(*si); - memcpy(s->si6_msg, si, MIN(len, sizeof(s->si6_msg))); + memcpy(s->si6_msg, si, OSMO_MIN(len, sizeof(s->si6_msg))); /* Cell Identity */ if (s->si6 && s->cell_id != ntohs(si->cell_identity)) @@ -830,7 +1076,7 @@ int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s, "read.\n"); s->cell_id = ntohs(si->cell_identity); /* LAI */ - gsm48_decode_lai_hex(&si->lai, &s->mcc, &s->mnc, &s->lac); + gsm48_decode_lai2(&si->lai, &s->lai); /* Cell Options (SACCH) */ gsm48_decode_cellopt_sacch(s, &si->cell_options); /* NCC Permitted */ @@ -844,28 +1090,156 @@ int gsm48_decode_sysinfo6(struct gsm48_sysinfo *s, return 0; } -int gsm48_encode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t mcc, - uint16_t mnc, uint16_t lac) +/* Get ARFCN from BCCH allocation found in SI5/SI5bis an SI5ter. See TS 44.018 §10.5.2.20. */ +int16_t arfcn_from_freq_index(const struct gsm48_sysinfo *s, uint16_t index) { - lai->digits[0] = (mcc >> 8) | (mcc & 0xf0); - lai->digits[1] = (mcc & 0x0f) | (mnc << 4); - lai->digits[2] = (mnc >> 8) | (mnc & 0xf0); - lai->lac = htons(lac); + uint16_t arfcn, i = 0; + + /* Search for ARFCN found in SI5 or SI5bis. (first sub list) */ + for (arfcn = 1; arfcn <= 1024; arfcn++) { + if (!(s->freq[arfcn & 1023].mask & (FREQ_TYPE_REP_5 | FREQ_TYPE_REP_5bis))) + continue; + if (index == i++) + return arfcn & 1023; + } - return 0; + /* Search for ARFCN found in SI5ter. (second sub list) */ + for (arfcn = 1; arfcn <= 1024; arfcn++) { + if (!(s->freq[arfcn & 1023].mask & FREQ_TYPE_REP_5ter)) + continue; + if (index == i++) + return arfcn & 1023; + } + + /* If not found, return EOF (-1) as idicator. */ + return EOF; } - int gsm48_decode_lai_hex(struct gsm48_loc_area_id *lai, uint16_t *mcc, - uint16_t *mnc, uint16_t *lac) +int gsm48_decode_sysinfo10(struct gsm48_sysinfo *s, + const struct gsm48_system_information_type_10 *si, int len) { - *mcc = ((lai->digits[0] & 0x0f) << 8) - | (lai->digits[0] & 0xf0) - | (lai->digits[1] & 0x0f); - *mnc = ((lai->digits[2] & 0x0f) << 8) - | (lai->digits[2] & 0xf0) - | ((lai->digits[1] & 0xf0) >> 4); - *lac = ntohs(lai->lac); - + int payload_len = len - sizeof(*si); + struct bitvec bv; + int i; + int rc; + + bv = (struct bitvec) { + .data_len = payload_len, + .data = (uint8_t *)si->rest_octets, + }; + + memcpy(s->si10_msg, si, OSMO_MIN(len, sizeof(s->si10_msg))); + + /* Clear cell list. */ + s->si10_cell_num = 0; + memset(s->si10_cell, 0, sizeof(s->si10_cell)); + + /* SI 10 Rest Octets of first neighbor cell, if included. */ + rc = gsm48_decode_si10_rest_first(s, &bv, &s->si10_cell[0]); + if (rc == EOF) { + s->si10 = true; + return 0; + } + if (rc < 0) + return rc; + s->si10_cell[0].arfcn = arfcn_from_freq_index(s, s->si10_cell[0].index); + s->si10_cell_num++; + + for (i = 1; i < ARRAY_SIZE(s->si10_cell); i++) { + /* Clone last cell info and then store differential elements. */ + memcpy(&s->si10_cell[i], &s->si10_cell[i - 1], sizeof(s->si10_cell[i])); + /* SI 10 Rest Octets of other neighbor cell, if included. */ + rc = gsm48_decode_si10_rest_other(s, &bv, &s->si10_cell[i]); + if (rc == EOF) + break; + if (rc < 0) + return rc; + s->si10_cell[i].arfcn = arfcn_from_freq_index(s, s->si10_cell[i].index); + s->si10_cell_num++; + } + + s->si10 = true; return 0; } +int gsm48_decode_sysinfo13(struct gsm48_sysinfo *s, + const struct gsm48_system_information_type_13 *si, int len) +{ + SI13_RestOctets_t si13ro; + int rc; + + memcpy(s->si13_msg, si, OSMO_MIN(len, sizeof(s->si13_msg))); + + rc = osmo_gprs_rlcmac_decode_si13ro(&si13ro, si->rest_octets, sizeof(s->si13_msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to parse SI13 Rest Octets\n"); + return rc; + } + + if (si13ro.UnionType != 0) { + LOGP(DRR, LOGL_NOTICE, "PBCCH is deprecated and not supported\n"); + return -ENOTSUP; + } + + s->gprs.hopping = si13ro.Exist_MA; + if (s->gprs.hopping) { + const GPRS_Mobile_Allocation_t *gma = &si13ro.GPRS_Mobile_Allocation; + + s->gprs.hsn = gma->HSN; + s->gprs.rfl_num_len = gma->ElementsOf_RFL_NUMBER; + memcpy(&s->gprs.rfl_num[0], &gma->RFL_NUMBER[0], sizeof(gma->RFL_NUMBER)); + + if (gma->UnionType == 0) { /* MA Bitmap */ + const MobileAllocation_t *ma = &gma->u.MA; + s->gprs.ma_bitlen = ma->MA_BitLength; + memcpy(&s->gprs.ma_bitmap[0], &ma->MA_BITMAP[0], sizeof(ma->MA_BITMAP)); + } else { /* ARFCN Index List */ + const ARFCN_index_list_t *ai = &gma->u.ARFCN_index_list; + s->gprs.arfcn_idx_len = ai->ElementsOf_ARFCN_INDEX; + memcpy(&s->gprs.arfcn_idx[0], &ai->ARFCN_INDEX[0], sizeof(ai->ARFCN_INDEX)); + } + } + + const PBCCH_Not_present_t *np = &si13ro.u.PBCCH_Not_present; + + s->gprs.rac = np->RAC; + s->gprs.prio_acc_thresh = np->PRIORITY_ACCESS_THR; + s->gprs.nco = np->NETWORK_CONTROL_ORDER; + + const GPRS_Cell_Options_t *gco = &np->GPRS_Cell_Options; + + s->gprs.nmo = gco->NMO; + s->gprs.T3168 = gco->T3168; + s->gprs.T3192 = gco->T3192; + s->gprs.ab_type = gco->ACCESS_BURST_TYPE; + s->gprs.ctrl_ack_type_use_block = !gco->CONTROL_ACK_TYPE; /* inverted */ + s->gprs.bs_cv_max = gco->BS_CV_MAX; + + s->gprs.pan_params_present = gco->Exist_PAN; + if (s->gprs.pan_params_present) { + s->gprs.pan_dec = gco->PAN_DEC; + s->gprs.pan_inc = gco->PAN_INC; + s->gprs.pan_max = gco->PAN_MAX; + } + + s->gprs.egprs_supported = 0; + if (gco->Exist_Extension_Bits) { + /* CSN.1 codec is not powerful enough (yet?) to decode this part :( */ + unsigned int ext_len = gco->Extension_Bits.extension_length; + const uint8_t *ext = &gco->Extension_Bits.Extension_Info[0]; + + s->gprs.egprs_supported = (ext[0] >> 7); + if (s->gprs.egprs_supported) { + if (ext_len < 6) + return -EINVAL; + s->gprs.egprs_pkt_chan_req = ~ext[0] & (1 << 6); /* inverted */ + s->gprs.egprs_bep_period = (ext[0] >> 2) & 0x0f; + } + } + + /* TODO: GPRS_Power_Control_Parameters */ + + s->si13 = 1; + + return 0; +} diff --git a/src/host/layer23/src/common/utils.c b/src/host/layer23/src/common/utils.c index 4ecb134b..417519b0 100644 --- a/src/host/layer23/src/common/utils.c +++ b/src/host/layer23/src/common/utils.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/utils.h> diff --git a/src/host/layer23/src/common/vty.c b/src/host/layer23/src/common/vty.c new file mode 100644 index 00000000..dd80a14b --- /dev/null +++ b/src/host/layer23/src/common/vty.c @@ -0,0 +1,1499 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/types.h> + +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/gsmtap.h> +#include <osmocom/core/gsmtap_util.h> +#include <osmocom/crypt/auth.h> +#include <osmocom/vty/cpu_sched_vty.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/stats.h> +#include <osmocom/vty/misc.h> + +#include <osmocom/bb/common/vty.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/gps.h> +#include <osmocom/bb/common/l1l2_interface.h> + +extern struct llist_head active_connections; /* libosmocore */ + +bool l23_vty_reading = false; + +bool l23_vty_hide_default = false; + +static struct cmd_node ms_node = { + MS_NODE, + "%s(ms)# ", + 1 +}; + +static struct cmd_node gsmtap_node = { + GSMTAP_NODE, + "%s(gsmtap)# ", + 1 +}; + +struct cmd_node testsim_node = { + TESTSIM_NODE, + "%s(test-sim)# ", + 1 +}; + +static void l23_vty_restart_required_warn(struct vty *vty, struct osmocom_ms *ms) +{ + if (l23_vty_reading) + return; + if (ms->shutdown != MS_SHUTDOWN_NONE) + return; + vty_out(vty, "You must restart MS '%s' ('shutdown / no shutdown') for " + "change to take effect!%s", ms->name, VTY_NEWLINE); +} + +struct osmocom_ms *l23_vty_get_ms(const char *name, struct vty *vty) +{ + struct osmocom_ms *ms; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, name)) { + if (ms->shutdown != MS_SHUTDOWN_NONE) { + vty_out(vty, "MS '%s' is admin down.%s", name, + VTY_NEWLINE); + return NULL; + } + return ms; + } + } + vty_out(vty, "MS name '%s' does not exist.%s", name, VTY_NEWLINE); + + return NULL; +} + +void l23_vty_ms_notify(struct osmocom_ms *ms, const char *fmt, ...) +{ + struct telnet_connection *connection; + char buffer[1000]; + va_list args; + struct vty *vty; + + if (fmt) { + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (!buffer[0]) + return; + } + + llist_for_each_entry(connection, &active_connections, entry) { + vty = connection->vty; + if (!vty) + continue; + if (!fmt) { + vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name, + VTY_NEWLINE); + continue; + } + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE); + buffer[strlen(buffer)] = '\n'; + } else + vty_out(vty, "%% %s", buffer); + } +} + +void l23_vty_printf(void *priv, const char *fmt, ...) +{ + char buffer[1000]; + struct vty *vty = priv; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (buffer[0]) { + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%s%s", buffer, VTY_NEWLINE); + } else + vty_out(vty, "%s", buffer); + } +} + +/* placeholder for layer23 shared MS info to be dumped */ +void l23_ms_dump(struct osmocom_ms *ms, struct vty *vty) +{ + struct gsm_settings *set = &ms->settings; + char *service = ""; + + if (!ms->started) + service = ", radio is not started"; + else if (ms->mmlayer.state == GSM48_MM_ST_NULL) { + service = ", MM connection not yet set up"; + } else if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) { + /* current MM idle state */ + switch (ms->mmlayer.substate) { + case GSM48_MM_SST_NORMAL_SERVICE: + case GSM48_MM_SST_PLMN_SEARCH_NORMAL: + service = ", service is normal"; + break; + case GSM48_MM_SST_LOC_UPD_NEEDED: + case GSM48_MM_SST_ATTEMPT_UPDATE: + service = ", service is limited (pending)"; + break; + case GSM48_MM_SST_NO_CELL_AVAIL: + service = ", service is unavailable"; + break; + default: + if (ms->subscr.sim_valid) + service = ", service is limited"; + else + service = ", service is limited " + "(IMSI detached)"; + break; + } + } else + service = ", MM connection active"; + + vty_out(vty, "MS '%s' is %s%s%s%s", ms->name, + (ms->shutdown != MS_SHUTDOWN_NONE) ? "administratively " : "", + (ms->shutdown != MS_SHUTDOWN_NONE || !ms->started) ? "down" : "up", + (ms->shutdown == MS_SHUTDOWN_NONE) ? service : "", + VTY_NEWLINE); + + vty_out(vty, " IMEI: %s%s", set->imei, VTY_NEWLINE); + vty_out(vty, " IMEISV: %s%s", set->imeisv, VTY_NEWLINE); + if (set->imei_random) + vty_out(vty, " IMEI generation: random (%d trailing " + "digits)%s", set->imei_random, VTY_NEWLINE); + else + vty_out(vty, " IMEI generation: fixed%s", VTY_NEWLINE); +} + +/* CONFIG NODE: */ +DEFUN(cfg_hide_default, cfg_hide_default_cmd, "hide-default", + "Hide most default values in config to make it more compact") +{ + l23_vty_hide_default = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_hide_default, cfg_no_hide_default_cmd, "no hide-default", + NO_STR "Show default values in config") +{ + l23_vty_hide_default = 0; + + return CMD_SUCCESS; +} + +DEFUN(show_support, show_support_cmd, "show support [MS_NAME]", + SHOW_STR "Display information about MS support\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_support_dump(ms, l23_vty_printf, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + gsm_support_dump(ms, l23_vty_printf, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(show_subscr, show_subscr_cmd, "show subscriber [MS_NAME]", + SHOW_STR "Display information about subscriber\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_subscr_dump(&ms->subscr, l23_vty_printf, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + if (ms->shutdown == MS_SHUTDOWN_NONE) { + gsm_subscr_dump(&ms->subscr, l23_vty_printf, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + } + + return CMD_SUCCESS; +} + +/* "gsmtap" config */ +gDEFUN(l23_cfg_gsmtap, l23_cfg_gsmtap_cmd, "gsmtap", + "Configure GSMTAP\n") +{ + vty->node = GSMTAP_NODE; + return CMD_SUCCESS; +} + +static const struct value_string gsmtap_categ_gprs_names[] = { + { L23_GSMTAP_GPRS_C_DL_UNKNOWN, "dl-unknown" }, + { L23_GSMTAP_GPRS_C_DL_DUMMY, "dl-dummy" }, + { L23_GSMTAP_GPRS_C_DL_CTRL, "dl-ctrl" }, + { L23_GSMTAP_GPRS_C_DL_DATA_GPRS, "dl-data-gprs" }, + { L23_GSMTAP_GPRS_C_DL_DATA_EGPRS, "dl-data-egprs" }, + { L23_GSMTAP_GPRS_C_UL_UNKNOWN, "ul-unknown" }, + { L23_GSMTAP_GPRS_C_UL_DUMMY, "ul-dummy" }, + { L23_GSMTAP_GPRS_C_UL_CTRL, "ul-ctrl" }, + { L23_GSMTAP_GPRS_C_UL_DATA_GPRS, "ul-data-gprs" }, + { L23_GSMTAP_GPRS_C_UL_DATA_EGPRS, "ul-data-egprs" }, + { 0, NULL } +}; + +static const struct value_string gsmtap_categ_gprs_help[] = { + { L23_GSMTAP_GPRS_C_DL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" }, + { L23_GSMTAP_GPRS_C_DL_DUMMY, "Downlink Dummy Blocks" }, + { L23_GSMTAP_GPRS_C_DL_CTRL, "Downlink Control Blocks" }, + { L23_GSMTAP_GPRS_C_DL_DATA_GPRS, "Downlink Data Blocks (GPRS)" }, + { L23_GSMTAP_GPRS_C_DL_DATA_EGPRS, "Downlink Data Blocks (EGPRS)" }, + { L23_GSMTAP_GPRS_C_UL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" }, + { L23_GSMTAP_GPRS_C_UL_DUMMY, "Uplink Dummy Blocks" }, + { L23_GSMTAP_GPRS_C_UL_CTRL, "Uplink Control Blocks" }, + { L23_GSMTAP_GPRS_C_UL_DATA_GPRS, "Uplink Data Blocks (GPRS)" }, + { L23_GSMTAP_GPRS_C_UL_DATA_EGPRS, "Uplink Data Blocks (EGPRS)" }, + { 0, NULL } +}; + +DEFUN(cfg_gsmtap_gsmtap_remote_host, + cfg_gsmtap_gsmtap_remote_host_cmd, + "remote-host [HOSTNAME]", + "Enable GSMTAP Um logging\n" + "Remote IP address or hostname ('localhost' if omitted)\n") +{ + osmo_talloc_replace_string(l23_ctx, &l23_cfg.gsmtap.remote_host, + argc > 0 ? argv[0] : "localhost"); + + if (vty->type != VTY_FILE) + vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_no_gsmtap_remote_host, + cfg_gsmtap_no_gsmtap_remote_host_cmd, + "no remote-host", + NO_STR "Disable GSMTAP Um logging\n") +{ + TALLOC_FREE(l23_cfg.gsmtap.remote_host); + if (vty->type != VTY_FILE) + vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_gsmtap_local_host, + cfg_gsmtap_gsmtap_local_host_cmd, + "local-host " VTY_IPV46_CMD, + "Set source for GSMTAP Um logging\n" + "Local IPv4 address\n" "Local IPv6 address\n") +{ + osmo_talloc_replace_string(l23_ctx, &l23_cfg.gsmtap.local_host, argv[0]); + + if (vty->type != VTY_FILE) + vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_no_gsmtap_local_host, + cfg_gsmtap_no_gsmtap_local_host_cmd, + "no local-host", + NO_STR "Disable explicit source for GSMTAP Um logging\n") +{ + TALLOC_FREE(l23_cfg.gsmtap.local_host); + if (vty->type != VTY_FILE) + vty_out(vty, "%% This command requires restart%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_gsmtap_lchan_all, cfg_gsmtap_gsmtap_lchan_all_cmd, + "lchan (enable-all|disable-all)", + "Enable/disable sending of UL/DL messages over GSMTAP\n" + "Enable all kinds of messages (all LCHAN)\n" + "Disable all kinds of messages (all LCHAN)\n") +{ + if (argv[0][0] == 'e') { + l23_cfg.gsmtap.lchan_mask = UINT32_MAX; + l23_cfg.gsmtap.lchan_acch_mask = UINT32_MAX; + l23_cfg.gsmtap.lchan_acch = true; + } else { + l23_cfg.gsmtap.lchan_mask = 0x00; + l23_cfg.gsmtap.lchan_acch_mask = 0x00; + l23_cfg.gsmtap.lchan_acch = false; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_gsmtap_lchan, cfg_gsmtap_gsmtap_lchan_cmd, + "HIDDEN", "HIDDEN") +{ + unsigned int channel; + + if (osmo_str_startswith(argv[0], "sacch")) { + if (strcmp(argv[0], "sacch") == 0) { + l23_cfg.gsmtap.lchan_acch = true; + } else { + channel = get_string_value(gsmtap_gsm_channel_names, argv[0]); + channel &= ~GSMTAP_CHANNEL_ACCH; + l23_cfg.gsmtap.lchan_acch_mask |= (1 << channel); + } + } else { + channel = get_string_value(gsmtap_gsm_channel_names, argv[0]); + l23_cfg.gsmtap.lchan_mask |= (1 << channel); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_no_gsmtap_lchan, cfg_gsmtap_no_gsmtap_lchan_cmd, + "HIDDEN", "HIDDEN") +{ + unsigned int channel; + + if (osmo_str_startswith(argv[0], "sacch")) { + if (strcmp(argv[0], "sacch") == 0) { + l23_cfg.gsmtap.lchan_acch = false; + } else { + channel = get_string_value(gsmtap_gsm_channel_names, argv[0]); + channel &= ~GSMTAP_CHANNEL_ACCH; + l23_cfg.gsmtap.lchan_acch_mask &= ~(1 << channel); + } + } else { + channel = get_string_value(gsmtap_gsm_channel_names, argv[0]); + l23_cfg.gsmtap.lchan_mask &= ~(1 << channel); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_gsmtap_categ_gprs_all, cfg_gsmtap_gsmtap_categ_gprs_all_cmd, + "category gprs (enable-all|disable-all)", + "Enable/disable sending of UL/DL messages over GSMTAP\n" + "Enable all kinds of messages (all categories)\n" + "Disable all kinds of messages (all categories)\n") +{ + + if (strcmp(argv[0], "enable-all") == 0) + l23_cfg.gsmtap.categ_gprs_mask = UINT32_MAX; + else + l23_cfg.gsmtap.categ_gprs_mask = 0x00; + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_gsmtap_categ_gprs, cfg_gsmtap_gsmtap_categ_gprs_cmd, "HIDDEN", "HIDDEN") +{ + int categ; + + categ = get_string_value(gsmtap_categ_gprs_names, argv[0]); + if (categ < 0) + return CMD_WARNING; + + l23_cfg.gsmtap.categ_gprs_mask |= (1 << categ); + + return CMD_SUCCESS; +} + +DEFUN(cfg_gsmtap_no_gsmtap_categ_gprs, cfg_gsmtap_no_gsmtap_categ_gprs_cmd, "HIDDEN", "HIDDEN") +{ + int categ; + + categ = get_string_value(gsmtap_categ_gprs_names, argv[0]); + if (categ < 0) + return CMD_WARNING; + + l23_cfg.gsmtap.categ_gprs_mask &= ~(1 << categ); + + return CMD_SUCCESS; +} + + +gDEFUN(l23_show_ms, l23_show_ms_cmd, "show ms [MS_NAME]", + SHOW_STR "Display available MS entities\n") +{ + struct osmocom_ms *ms; + + if (argc) { + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + l23_ms_dump(ms, vty); + return CMD_SUCCESS; + } + } + vty_out(vty, "MS name '%s' does not exist.%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + llist_for_each_entry(ms, &ms_list, entity) { + l23_ms_dump(ms, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +static int _sim_testcard_cmd(struct vty *vty, int argc, const char *argv[], + int attached) +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + int rc; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (ms->subscr.sim_valid) { + vty_out(vty, "SIM already attached, remove first!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + set = &ms->settings; + set->sim_type = GSM_SIM_TYPE_TEST; + + if (argc == 2) { + vty_out(vty, "Give MNC together with MCC%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (argc >= 3) { + struct osmo_plmn_id plmn; + if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + memcpy(&set->test_sim.rplmn, &plmn, sizeof(plmn)); + set->test_sim.rplmn_valid = true; + } else { + set->test_sim.rplmn_valid = false; + } + + if (argc >= 4) + set->test_sim.lac = strtoul(argv[3], NULL, 16); + + if (argc >= 5) + set->test_sim.tmsi = strtoul(argv[4], NULL, 16); + + set->test_sim.imsi_attached = attached; + + rc = gsm_subscr_insert(ms); + if (rc < 0) { + vty_out(vty, "Attach test SIM card failed: %d%s", rc, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(sim_testcard, sim_testcard_cmd, + "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]", + "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n" + "Optionally set mobile Country Code of RPLMN\n" + "Optionally set mobile Network Code of RPLMN\n" + "Optionally set location area code of RPLMN\n" + "Optionally set current assigned TMSI") +{ + return _sim_testcard_cmd(vty, argc, argv, 0); +} + +DEFUN(sim_testcard_att, sim_testcard_att_cmd, + "sim testcard MS_NAME MCC MNC LAC TMSI attached", + "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n" + "Set mobile Country Code of RPLMN\nSet mobile Network Code of RPLMN\n" + "Set location area code\nSet current assigned TMSI\n" + "Indicate to MM that card is already attached") +{ + return _sim_testcard_cmd(vty, argc, argv, 1); +} + +DEFUN(sim_sap, sim_sap_cmd, "sim sap MS_NAME", + "SIM actions\nAttach SIM over SAP interface\n" + "Name of MS (see \"show ms\")\n") +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (ms->subscr.sim_valid) { + vty_out(vty, "SIM already attached, remove first!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + set = &ms->settings; + set->sim_type = GSM_SIM_TYPE_SAP; + if (gsm_subscr_insert(ms) != 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME", + "SIM actions\nAttach SIM from reader\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + struct gsm_settings *set; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (ms->subscr.sim_valid) { + vty_out(vty, "SIM already attached, remove first!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + set = &ms->settings; + set->sim_type = GSM_SIM_TYPE_L1PHY; + gsm_subscr_insert(ms); + + return CMD_SUCCESS; +} + +DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME", + "SIM actions\nDetach SIM card\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (!ms->subscr.sim_valid) { + vty_out(vty, "No SIM attached!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_remove(ms); + return CMD_SUCCESS; +} + +DEFUN(sim_pin, sim_pin_cmd, "sim pin MS_NAME PIN", + "SIM actions\nEnter PIN for SIM card\nName of MS (see \"show ms\")\n" + "PIN number") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!ms->subscr.sim_pin_required) { + vty_out(vty, "No PIN is required at this time!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], "", 0); + + return CMD_SUCCESS; +} + +DEFUN(sim_disable_pin, sim_disable_pin_cmd, "sim disable-pin MS_NAME PIN", + "SIM actions\nDisable PIN of SIM card\nName of MS (see \"show ms\")\n" + "PIN number") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], "", -1); + + return CMD_SUCCESS; +} + +DEFUN(sim_enable_pin, sim_enable_pin_cmd, "sim enable-pin MS_NAME PIN", + "SIM actions\nEnable PIN of SIM card\nName of MS (see \"show ms\")\n" + "PIN number") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], "", 1); + + return CMD_SUCCESS; +} + +DEFUN(sim_change_pin, sim_change_pin_cmd, "sim change-pin MS_NAME OLD NEW", + "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n" + "Old PIN number\nNew PIN number") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { + vty_out(vty, "Old PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) { + vty_out(vty, "New PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 2); + + return CMD_SUCCESS; +} + +DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW", + "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n" + "Personal Unblock Key\nNew PIN number") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (strlen(argv[1]) != 8) { + vty_out(vty, "PUC must be 8 digits!%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) { + vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99); + + return CMD_SUCCESS; +} + +DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC", + "SIM actions\nChange LAI of SIM card\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code\nLocation Area Code " + " (use 0000 to remove LAI)") +{ + struct osmocom_ms *ms; + struct osmo_plmn_id plmn; + uint16_t lac; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + lac = strtoul(argv[3], NULL, 0); + + memcpy(&ms->subscr.lai.plmn, &plmn, sizeof(plmn)); + ms->subscr.lai.lac = lac; + ms->subscr.tmsi = GSM_RESERVED_TMSI; + + gsm_subscr_write_loci(ms); + + return CMD_SUCCESS; +} + +/* per MS config */ +gDEFUN(l23_cfg_ms, l23_cfg_ms_cmd, "ms MS_NAME", + "Select a mobile station to configure\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, argv[0])) { + vty->index = ms; + vty->node = MS_NODE; + return CMD_SUCCESS; + } + } + + vty_out(vty, "MS name '%s' does not exits%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; +} + +DEFUN(cfg_ms_layer2, cfg_ms_layer2_cmd, "layer2-socket PATH", + "Define socket path to connect between layer 2 and layer 1\n" + "Unix socket, default '" L2_DEFAULT_SOCKET_PATH "'") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + OSMO_STRLCPY_ARRAY(set->layer2_socket_path, argv[0]); + + l23_vty_restart_required_warn(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]", + "Set IMEI (enter without control digit)\n15 Digits IMEI\n" + "Software version digit") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + char *error, *sv = "0"; + + if (argc >= 2) + sv = (char *)argv[1]; + + error = gsm_check_imei(argv[0], sv); + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + + OSMO_STRLCPY_ARRAY(set->imei, argv[0]); + OSMO_STRLCPY_ARRAY(set->imeisv, argv[0]); + osmo_strlcpy(set->imeisv + GSM23003_IMEI_NUM_DIGITS, sv, + sizeof(set->imeisv) - GSM23003_IMEI_NUM_DIGITS); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed", + "Use fixed IMEI on every power on") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->imei_random = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>", + "Use random IMEI on every power on\n" + "Number of trailing digits to randomize") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->imei_random = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test|sap)", + "Set SIM card to attach when powering on\nAttach no SIM\n" + "Attach SIM from reader\nAttach build in test SIM\n" + "Attach SIM over SAP interface") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + switch (argv[0][0]) { + case 'n': + set->sim_type = GSM_SIM_TYPE_NONE; + break; + case 'r': + set->sim_type = GSM_SIM_TYPE_L1PHY; + break; + case 't': + set->sim_type = GSM_SIM_TYPE_TEST; + break; + case 's': + set->sim_type = GSM_SIM_TYPE_SAP; + break; + default: + vty_out(vty, "unknown SIM type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown", + NO_STR "Activate and run MS") +{ + struct osmocom_ms *ms = vty->index; + + struct osmobb_l23_vty_sig_data data; + memset(&data, 0, sizeof(data)); + + data.vty = vty; + data.ms_start.ms = ms; + data.ms_start.rc = CMD_SUCCESS; + osmo_signal_dispatch(SS_L23_VTY, S_L23_VTY_MS_START, &data); + + return data.ms_start.rc; +} + +DEFUN(cfg_ms_shutdown, cfg_ms_shutdown_cmd, "shutdown", + "Shut down and deactivate MS") +{ + struct osmocom_ms *ms = vty->index; + + struct osmobb_l23_vty_sig_data data; + memset(&data, 0, sizeof(data)); + + data.vty = vty; + data.ms_stop.ms = ms; + data.ms_stop.force = false; + data.ms_stop.rc = CMD_SUCCESS; + osmo_signal_dispatch(SS_L23_VTY, S_L23_VTY_MS_STOP, &data); + + return data.ms_stop.rc; +} + +DEFUN(cfg_ms_shutdown_force, cfg_ms_shutdown_force_cmd, "shutdown force", + "Shut down and deactivate MS\nDo not perform IMSI detach") +{ + struct osmocom_ms *ms = vty->index; + + struct osmobb_l23_vty_sig_data data; + memset(&data, 0, sizeof(data)); + + data.vty = vty; + data.ms_stop.ms = ms; + data.ms_stop.force = true; + data.ms_stop.rc = CMD_SUCCESS; + osmo_signal_dispatch(SS_L23_VTY, S_L23_VTY_MS_STOP, &data); + + return data.ms_stop.rc; +} + +/* per testsim config */ +DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim", + "Configure test SIM emulation") +{ + vty->node = TESTSIM_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_imsi, cfg_testsim_imsi_cmd, "imsi IMSI", + "Set IMSI on test card\n15 digits IMSI") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + if (!osmo_imsi_str_valid(argv[0])) { + vty_out(vty, "Wrong IMSI format%s", VTY_NEWLINE); + return CMD_WARNING; + } + + OSMO_STRLCPY_ARRAY(set->test_sim.imsi, argv[0]); + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +#define HEX_STR "\nByte as two digits hexadecimal" +DEFUN(cfg_testsim_ki_xor, cfg_testsim_ki_xor_cmd, "ki xor HEX HEX HEX HEX HEX HEX " + "HEX HEX HEX HEX HEX HEX", + "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR + HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + uint8_t ki[12]; + const char *p; + int i; + + for (i = 0; i < 12; i++) { + p = argv[i]; + if (!strncmp(p, "0x", 2)) + p += 2; + if (strlen(p) != 2) { + vty_out(vty, "Expecting two digits hex value (with or " + "without 0x in front)%s", VTY_NEWLINE); + return CMD_WARNING; + } + ki[i] = strtoul(p, NULL, 16); + } + + set->test_sim.ki_type = OSMO_AUTH_ALG_XOR; + memcpy(set->test_sim.ki, ki, 12); + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_ki_comp128, cfg_testsim_ki_comp128_cmd, "ki comp128 HEX HEX HEX " + "HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX", + "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR + HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR + HEX_STR HEX_STR HEX_STR HEX_STR) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + uint8_t ki[16]; + const char *p; + int i; + + for (i = 0; i < 16; i++) { + p = argv[i]; + if (!strncmp(p, "0x", 2)) + p += 2; + if (strlen(p) != 2) { + vty_out(vty, "Expecting two digits hex value (with or " + "without 0x in front)%s", VTY_NEWLINE); + return CMD_WARNING; + } + ki[i] = strtoul(p, NULL, 16); + } + + set->test_sim.ki_type = OSMO_AUTH_ALG_COMP128v1; + memcpy(set->test_sim.ki, ki, 16); + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_barr, cfg_testsim_barr_cmd, "barred-access", + "Allow access to barred cells") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_sim.barr = true; + + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_no_barr, cfg_testsim_no_barr_cmd, "no barred-access", + NO_STR "Deny access to barred cells") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_sim.barr = false; + + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_no_rplmn, cfg_testsim_no_rplmn_cmd, "no rplmn", + NO_STR "Unset Registered PLMN") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_sim.rplmn_valid = false; + set->test_sim.rplmn.mcc = 1; + set->test_sim.rplmn.mnc = 1; + set->test_sim.rplmn.mnc_3_digits = false; + set->test_sim.lac = 0x0000; + set->test_sim.tmsi = GSM_RESERVED_TMSI; + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +static int _testsim_rplmn_cmd(struct vty *vty, int argc, const char *argv[], bool attached) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct osmo_plmn_id plmn; + + if (osmo_mcc_from_str(argv[0], &plmn.mcc) < 0) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (osmo_mnc_from_str(argv[1], &plmn.mnc, &plmn.mnc_3_digits) < 0) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + set->test_sim.rplmn_valid = true; + memcpy(&set->test_sim.rplmn, &plmn, sizeof(plmn)); + + if (argc >= 3) + set->test_sim.lac = strtoul(argv[2], NULL, 16); + else + set->test_sim.lac = 0xfffe; + + if (argc >= 4) + set->test_sim.tmsi = strtoul(argv[3], NULL, 16); + else + set->test_sim.tmsi = GSM_RESERVED_TMSI; + + set->test_sim.imsi_attached = attached; + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_rplmn, cfg_testsim_rplmn_cmd, + "rplmn MCC MNC [LAC] [TMSI]", + "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n" + "Optionally set location area code\n" + "Optionally set current assigned TMSI") +{ + return _testsim_rplmn_cmd(vty, argc, argv, false); +} + +DEFUN(cfg_testsim_rplmn_att, cfg_testsim_rplmn_att_cmd, + "rplmn MCC MNC LAC TMSI attached", + "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n" + "Set location area code\nSet current assigned TMSI\n" + "Indicate to MM that card is already attached") +{ + return _testsim_rplmn_cmd(vty, argc, argv, true); +} + +DEFUN(cfg_testsim_hplmn, cfg_testsim_hplmn_cmd, "hplmn-search (everywhere|foreign-country)", + "Set Home PLMN search mode\n" + "Search for HPLMN when on any other network\n" + "Search for HPLMN when in a different country") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + switch (argv[0][0]) { + case 'e': + set->test_sim.always_search_hplmn = true; + break; + case 'f': + set->test_sim.always_search_hplmn = false; + break; + } + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +static int _testsim_locigprs_cmd(struct vty *vty, int argc, const char *argv[], bool attached) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + struct osmo_plmn_id plmn; + + if (osmo_mcc_from_str(argv[0], &plmn.mcc) < 0) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (osmo_mnc_from_str(argv[1], &plmn.mnc, &plmn.mnc_3_digits) < 0) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + set->test_sim.locigprs.valid = true; + set->test_sim.locigprs.rai.mcc = plmn.mcc; + set->test_sim.locigprs.rai.mnc = plmn.mnc; + set->test_sim.locigprs.rai.mnc_3_digits = plmn.mnc_3_digits; + + if (argc >= 3) + set->test_sim.locigprs.rai.lac = strtoul(argv[2], NULL, 16); + else + set->test_sim.locigprs.rai.lac = 0xfffe; + + if (argc >= 4) + set->test_sim.locigprs.rai.rac = strtoul(argv[3], NULL, 16); + else + set->test_sim.locigprs.rai.rac = 0xff; + + if (argc >= 5) + set->test_sim.locigprs.ptmsi = strtoul(argv[4], NULL, 16); + else + set->test_sim.locigprs.ptmsi = GSM_RESERVED_TMSI; + + if (argc >= 6) + set->test_sim.locigprs.ptmsi_sig = strtoul(argv[5], NULL, 16); + else + set->test_sim.locigprs.ptmsi_sig = GSM_RESERVED_TMSI; + + set->test_sim.locigprs.imsi_attached = attached; + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_no_locigprs, cfg_testsim_no_locigprs_cmd, "no locigprs", + NO_STR "Unset EF LOCIgprs\n") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->test_sim.locigprs.valid = false; + set->test_sim.locigprs.ptmsi = GSM_RESERVED_TMSI; + set->test_sim.locigprs.ptmsi_sig = GSM_RESERVED_TMSI; + set->test_sim.locigprs.rai.mcc = 1; + set->test_sim.locigprs.rai.mnc = 1; + set->test_sim.locigprs.rai.mnc_3_digits = false; + set->test_sim.locigprs.rai.lac = 0x0000; + set->test_sim.locigprs.rai.rac = 0x0000; + + l23_vty_restart_required_warn(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_testsim_locigprs, cfg_testsim_locigprs_cmd, + "locigprs MCC MNC [LAC] [RAC] [PTMSI] [PTMSISIG]", + "Set EF LOCIgprs\nMobile Country Code\nMobile Network Code\n" + "Optionally set location area code\n" + "Optionally set routing area code\n" + "Optionally set current assigned P-TMSI\n" + "Optionally set current assigned P-TMSI signature\n") +{ + return _testsim_locigprs_cmd(vty, argc, argv, false); +} + +DEFUN(cfg_testsim_locigprs_att, cfg_testsim_locigprs_att_cmd, + "locigprs MCC MNC LAC RAC PTMSI PTMSISIG attached", + "Set EF LOCIgprs\nMobile Country Code\nMobile Network Code\n" + "Set location area code\n" + "Set routing area code\n" + "Set current assigned P-TMSI\n" + "Set current assigned P-TMSI signature\n" + "Indicate to MM that card is already attached\n") +{ + return _testsim_locigprs_cmd(vty, argc, argv, true); +} + +static int l23_vty_config_write_gsmtap_node(struct vty *vty) +{ + const char *chan_buf; + unsigned int i; + + vty_out(vty, "gsmtap%s", VTY_NEWLINE); + + if (l23_cfg.gsmtap.remote_host) + vty_out(vty, " remote-host %s%s", l23_cfg.gsmtap.remote_host, VTY_NEWLINE); + else + vty_out(vty, " no remote-host%s", VTY_NEWLINE); + + if (l23_cfg.gsmtap.local_host) + vty_out(vty, " local-host %s%s", l23_cfg.gsmtap.local_host, VTY_NEWLINE); + else + vty_out(vty, " no local-host%s", VTY_NEWLINE); + + if (l23_cfg.gsmtap.lchan_acch) + vty_out(vty, " lchan sacch%s", VTY_NEWLINE); + + for (i = 0; i < sizeof(uint32_t) * 8; i++) { + if (l23_cfg.gsmtap.lchan_acch_mask & ((uint32_t) 1 << i)) { + chan_buf = get_value_string_or_null(gsmtap_gsm_channel_names, GSMTAP_CHANNEL_ACCH | i); + if (chan_buf == NULL) + continue; + chan_buf = osmo_str_tolower(chan_buf); + vty_out(vty, " lchan %s%s", chan_buf, VTY_NEWLINE); + } + } + + for (i = 0; i < sizeof(uint32_t) * 8; i++) { + if (l23_cfg.gsmtap.lchan_mask & ((uint32_t) 1 << i)) { + chan_buf = get_value_string_or_null(gsmtap_gsm_channel_names, i); + if (chan_buf == NULL) + continue; + chan_buf = osmo_str_tolower(chan_buf); + vty_out(vty, " lchan %s%s", chan_buf, VTY_NEWLINE); + } + } + + for (i = 0; i < 32; i++) { + if (l23_cfg.gsmtap.categ_gprs_mask & ((uint32_t)1 << i)) { + const char *category_buf; + if (!(category_buf = get_value_string_or_null(gsmtap_categ_gprs_names, i))) + continue; + vty_out(vty, " category gprs %s%s", category_buf, VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +static int l23_vty_config_write_testsim_node(struct vty *vty, const struct osmocom_ms *ms, const char *prefix) +{ + const struct gsm_settings *set = &ms->settings; + vty_out(vty, "%stest-sim%s", prefix, VTY_NEWLINE); + vty_out(vty, "%s imsi %s%s", prefix, set->test_sim.imsi, VTY_NEWLINE); + switch (set->test_sim.ki_type) { + case OSMO_AUTH_ALG_XOR: + vty_out(vty, "%s ki xor %s%s", + prefix, osmo_hexdump(set->test_sim.ki, 12), VTY_NEWLINE); + break; + case OSMO_AUTH_ALG_COMP128v1: + vty_out(vty, "%s ki comp128 %s%s", + prefix, osmo_hexdump(set->test_sim.ki, 16), VTY_NEWLINE); + break; + } + if (!l23_vty_hide_default || set->test_sim.barr) + vty_out(vty, "%s %sbarred-access%s", prefix, + (set->test_sim.barr) ? "" : "no ", VTY_NEWLINE); + if (set->test_sim.rplmn_valid) { + vty_out(vty, "%s rplmn %s %s", prefix, + osmo_mcc_name(set->test_sim.rplmn.mcc), + osmo_mnc_name(set->test_sim.rplmn.mnc, set->test_sim.rplmn.mnc_3_digits)); + if (set->test_sim.lac > 0x0000 && set->test_sim.lac < 0xfffe) { + vty_out(vty, " 0x%04x", set->test_sim.lac); + if (set->test_sim.tmsi != GSM_RESERVED_TMSI) { + vty_out(vty, " 0x%08x", set->test_sim.tmsi); + if (set->test_sim.imsi_attached) + vty_out(vty, " attached"); + } + } + vty_out(vty, "%s", VTY_NEWLINE); + } else + if (!l23_vty_hide_default) + vty_out(vty, "%s no rplmn%s", prefix, VTY_NEWLINE); + + if (!l23_vty_hide_default || set->test_sim.always_search_hplmn) + vty_out(vty, "%s hplmn-search %s%s", prefix, + set->test_sim.always_search_hplmn ? "everywhere" : "foreign-country", + VTY_NEWLINE); + + if (set->test_sim.locigprs.valid) { + vty_out(vty, "%s locigprs %s %s", prefix, + osmo_mcc_name(set->test_sim.locigprs.rai.mcc), + osmo_mnc_name(set->test_sim.locigprs.rai.mnc, + set->test_sim.locigprs.rai.mnc_3_digits)); + if (set->test_sim.locigprs.rai.lac > 0x0000 && set->test_sim.locigprs.rai.lac < 0xfffe) { + vty_out(vty, " 0x%04x", set->test_sim.locigprs.rai.lac); + if (set->test_sim.locigprs.rai.rac < 0xff) { + vty_out(vty, " 0x%02x", set->test_sim.locigprs.rai.rac); + if (set->test_sim.locigprs.ptmsi != GSM_RESERVED_TMSI) { + vty_out(vty, " 0x%08x 0x%06x", + set->test_sim.locigprs.ptmsi, + set->test_sim.locigprs.ptmsi_sig); + if (set->test_sim.locigprs.imsi_attached) + vty_out(vty, " attached"); + } + } + } + vty_out(vty, "%s", VTY_NEWLINE); + } else + if (!l23_vty_hide_default) + vty_out(vty, "%s no locigprs%s", prefix, VTY_NEWLINE); + return CMD_SUCCESS; +} + +void l23_vty_config_write_ms_node(struct vty *vty, const struct osmocom_ms *ms, const char *prefix) +{ + size_t prefix_len = strlen(prefix); + char *prefix_content = alloca(prefix_len + 1 + 1); + + memcpy(prefix_content, prefix, prefix_len); + prefix_content[prefix_len] = ' '; + prefix_content[prefix_len + 1] = '\0'; + + vty_out(vty, "%sms %s%s", prefix, ms->name, VTY_NEWLINE); + l23_vty_config_write_ms_node_contents(vty, ms, prefix_content); + l23_vty_config_write_ms_node_contents_final(vty, ms, prefix_content); +} + +/* placeholder for shared VTY commands */ +void l23_vty_config_write_ms_node_contents(struct vty *vty, const struct osmocom_ms *ms, const char *prefix) +{ + const struct gsm_settings *set = &ms->settings; + + vty_out(vty, "%slayer2-socket %s%s", prefix, set->layer2_socket_path, + VTY_NEWLINE); + + vty_out(vty, "%simei %s %s%s", prefix, set->imei, + set->imeisv + strlen(set->imei), VTY_NEWLINE); + if (set->imei_random) + vty_out(vty, "%simei-random %d%s", prefix, set->imei_random, VTY_NEWLINE); + else if (!l23_vty_hide_default) + vty_out(vty, "%simei-fixed%s", prefix, VTY_NEWLINE); + + switch (set->sim_type) { + case GSM_SIM_TYPE_NONE: + vty_out(vty, "%ssim none%s", prefix, VTY_NEWLINE); + break; + case GSM_SIM_TYPE_L1PHY: + vty_out(vty, "%ssim reader%s", prefix, VTY_NEWLINE); + break; + case GSM_SIM_TYPE_TEST: + vty_out(vty, "%ssim test%s", prefix, VTY_NEWLINE); + break; + case GSM_SIM_TYPE_SAP: + vty_out(vty, "%ssim sap%s", prefix, VTY_NEWLINE); + break; + default: + OSMO_ASSERT(0); + } + + l23_vty_config_write_testsim_node(vty, ms, prefix); +} + +/* placeholder for shared VTY commands. Must be put at the end of the node: */ +void l23_vty_config_write_ms_node_contents_final(struct vty *vty, const struct osmocom_ms *ms, const char *prefix) +{ + /* no shutdown must be written to config, because shutdown is default */ + vty_out(vty, "%s%sshutdown%s", prefix, (ms->shutdown != MS_SHUTDOWN_NONE) ? "" : "no ", + VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); +} + +static void l23_vty_init_gsmtap(void) +{ + cfg_gsmtap_gsmtap_lchan_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names, + "lchan (sacch|", + "|", ")", VTY_DO_LOWER); + cfg_gsmtap_gsmtap_lchan_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names, + "Enable sending of UL/DL messages over GSMTAP\n" "SACCH\n", + "\n", "", 0); + + cfg_gsmtap_no_gsmtap_lchan_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names, + "no lchan (sacch|", + "|", ")", VTY_DO_LOWER); + cfg_gsmtap_no_gsmtap_lchan_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_gsm_channel_names, + NO_STR "Disable sending of UL/DL messages over GSMTAP\n" "SACCH\n", + "\n", "", 0); + + + cfg_gsmtap_gsmtap_categ_gprs_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_names, + "category gprs (", + "|", ")", VTY_DO_LOWER); + cfg_gsmtap_gsmtap_categ_gprs_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_help, + "GSMTAP Category\n" "GPRS\n", + "\n", "", 0); + cfg_gsmtap_no_gsmtap_categ_gprs_cmd.string = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_names, + "no category gprs (", + "|", ")", VTY_DO_LOWER); + cfg_gsmtap_no_gsmtap_categ_gprs_cmd.doc = vty_cmd_string_from_valstr(l23_ctx, gsmtap_categ_gprs_help, + NO_STR "GSMTAP Category\n" "GPRS\n", + "\n", "", 0); + + install_element(CONFIG_NODE, &l23_cfg_gsmtap_cmd); + + install_node(&gsmtap_node, l23_vty_config_write_gsmtap_node); + install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_remote_host_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_remote_host_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_local_host_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_local_host_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_lchan_all_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_lchan_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_lchan_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_categ_gprs_all_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_gsmtap_categ_gprs_cmd); + install_element(GSMTAP_NODE, &cfg_gsmtap_no_gsmtap_categ_gprs_cmd); +} + +int l23_vty_init(int (*config_write_ms_node_cb)(struct vty *), osmo_signal_cbfn *l23_vty_signal_cb) +{ + int rc = 0; + + if (l23_app_info.opt_supported & L23_OPT_TAP) + l23_vty_init_gsmtap(); + + if (l23_app_info.opt_supported & L23_OPT_VTY) + osmo_stats_vty_add_cmds(); + + install_element_ve(&show_subscr_cmd); + install_element_ve(&show_support_cmd); + + install_element(ENABLE_NODE, &sim_testcard_cmd); + install_element(ENABLE_NODE, &sim_testcard_att_cmd); + install_element(ENABLE_NODE, &sim_sap_cmd); + install_element(ENABLE_NODE, &sim_reader_cmd); + install_element(ENABLE_NODE, &sim_remove_cmd); + install_element(ENABLE_NODE, &sim_pin_cmd); + install_element(ENABLE_NODE, &sim_disable_pin_cmd); + install_element(ENABLE_NODE, &sim_enable_pin_cmd); + install_element(ENABLE_NODE, &sim_change_pin_cmd); + install_element(ENABLE_NODE, &sim_unblock_pin_cmd); + install_element(ENABLE_NODE, &sim_lai_cmd); + + install_element(CONFIG_NODE, &cfg_hide_default_cmd); + install_element(CONFIG_NODE, &cfg_no_hide_default_cmd); + + install_node(&ms_node, config_write_ms_node_cb); + install_element(MS_NODE, &cfg_ms_layer2_cmd); + install_element(MS_NODE, &cfg_ms_imei_cmd); + install_element(MS_NODE, &cfg_ms_imei_fixed_cmd); + install_element(MS_NODE, &cfg_ms_imei_random_cmd); + install_element(MS_NODE, &cfg_ms_sim_cmd); + install_element(MS_NODE, &cfg_ms_testsim_cmd); + install_node(&testsim_node, NULL); + install_element(TESTSIM_NODE, &cfg_testsim_imsi_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_ki_xor_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_ki_comp128_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_barr_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_no_barr_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_no_rplmn_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_rplmn_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_rplmn_att_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_hplmn_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_no_locigprs_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_locigprs_cmd); + install_element(TESTSIM_NODE, &cfg_testsim_locigprs_att_cmd); + install_element(MS_NODE, &cfg_ms_shutdown_cmd); + install_element(MS_NODE, &cfg_ms_shutdown_force_cmd); + install_element(MS_NODE, &cfg_ms_no_shutdown_cmd); + + /* Register the talloc context introspection command */ + osmo_talloc_vty_add_cmds(); + osmo_cpu_sched_vty_init(l23_ctx); + if (l23_vty_signal_cb) + rc = osmo_signal_register_handler(SS_L23_VTY, l23_vty_signal_cb, NULL); + return rc; +} + diff --git a/src/host/layer23/src/misc/Makefile.am b/src/host/layer23/src/misc/Makefile.am index 78ab962f..05995c3f 100644 --- a/src/host/layer23/src/misc/Makefile.am +++ b/src/host/layer23/src/misc/Makefile.am @@ -1,14 +1,78 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS) -LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBGPS_LIBS) +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) -bin_PROGRAMS = bcch_scan ccch_scan echo_test cell_log cbch_sniff +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOGPRSRLCMAC_CFLAGS) \ + $(LIBOSMOGPRSLLC_CFLAGS) \ + $(LIBOSMOGPRSSNDCP_CFLAGS) \ + $(LIBGPS_CFLAGS) \ + $(NULL) -noinst_HEADERS = bcch_scan.h geo.h +LDADD = \ + $(top_builddir)/src/common/liblayer23.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + $(LIBOSMOGPRSRLCMAC_LIBS) \ + $(LIBOSMOGPRSLLC_LIBS) \ + $(LIBOSMOGPRSSNDCP_LIBS) \ + $(LIBGPS_LIBS) \ + $(NULL) + +bin_PROGRAMS = \ + bcch_scan \ + ccch_scan \ + echo_test \ + cell_log \ + cbch_sniff \ + gsmmap \ + $(NULL) + +noinst_HEADERS = \ + bcch_scan.h \ + $(NULL) + +bcch_scan_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + app_bcch_scan.c \ + bcch_scan.c \ + $(NULL) + +ccch_scan_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + app_ccch_scan.c \ + rslms.c \ + $(NULL) + +echo_test_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + app_echo_test.c \ + $(NULL) -bcch_scan_SOURCES = ../common/main.c app_bcch_scan.c bcch_scan.c -ccch_scan_SOURCES = ../common/main.c app_ccch_scan.c rslms.c -echo_test_SOURCES = ../common/main.c app_echo_test.c cell_log_LDADD = $(LDADD) -lm -cell_log_SOURCES = ../common/main.c app_cell_log.c cell_log.c geo.c -cbch_sniff_SOURCES = ../common/main.c app_cbch_sniff.c +cell_log_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + app_cell_log.c \ + cell_log.c \ + geo.c \ + $(NULL) + +cbch_sniff_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + app_cbch_sniff.c \ + $(NULL) + +gsmmap_LDADD = $(LDADD) -lm +gsmmap_SOURCES = \ + gsmmap.c \ + geo.c \ + locate.c \ + log.c \ + $(NULL) diff --git a/src/host/layer23/src/misc/app_bcch_scan.c b/src/host/layer23/src/misc/app_bcch_scan.c index 59466991..768920d0 100644 --- a/src/host/layer23/src/misc/app_bcch_scan.c +++ b/src/host/layer23/src/misc/app_bcch_scan.c @@ -15,16 +15,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/misc/layer3.h> #include <osmocom/core/msgb.h> @@ -35,6 +33,8 @@ #include <l1ctl_proto.h> #include "bcch_scan.h" +static struct osmocom_ms *g_ms; + static int signal_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { @@ -51,20 +51,32 @@ static int signal_cb(unsigned int subsys, unsigned int signal, return 0; } -int l23_app_init(struct osmocom_ms *ms) +static int _bcch_scan_start(void) +{ + int rc; + + rc = layer2_open(g_ms, g_ms->settings.layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return rc; + } + + l1ctl_tx_reset_req(g_ms, L1CTL_RES_T_FULL); + return 0; +} + +int l23_app_init(void) { + g_ms = osmocom_ms_alloc(l23_ctx, "1"); + OSMO_ASSERT(g_ms); /* don't do layer3_init() as we don't want an actual L3 */ fps_init(); - l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + l23_app_start = _bcch_scan_start; return osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL); } -static struct l23_app_info info = { +const struct l23_app_info l23_app_info = { .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", .contribution = "Contributions by Holger Hans Peter Freyther\n", + .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_DBG, }; - -struct l23_app_info *l23_app_info() -{ - return &info; -} diff --git a/src/host/layer23/src/misc/app_cbch_sniff.c b/src/host/layer23/src/misc/app_cbch_sniff.c index ed85cefe..f83815ef 100644 --- a/src/host/layer23/src/misc/app_cbch_sniff.c +++ b/src/host/layer23/src/misc/app_cbch_sniff.c @@ -16,29 +16,29 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/misc/layer3.h> +#include <osmocom/bb/common/sysinfo.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/core/select.h> #include <osmocom/core/signal.h> #include <osmocom/gsm/rsl.h> - #include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/gsm/lapdm.h> #include <l1ctl_proto.h> -struct osmocom_ms *g_ms; +static struct osmocom_ms *g_ms; struct gsm48_sysinfo g_sysinfo = {}; static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s) @@ -68,12 +68,12 @@ static int try_cbch(struct osmocom_ms *ms, struct gsm48_sysinfo *s) return l1ctl_tx_dm_est_req_h1(ms, s->maio, s->hsn, s->hopping, s->hopp_len, chan_nr, s->tsc, - GSM48_CMODE_SIGN, 0); + GSM48_CMODE_SIGN, 0, 0); } else { LOGP(DRR, LOGL_INFO, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n", s->chan_nr, s->tsc, s->arfcn); return l1ctl_tx_dm_est_req_h0(ms, s->arfcn, - chan_nr, s->tsc, GSM48_CMODE_SIGN, 0); + chan_nr, s->tsc, GSM48_CMODE_SIGN, 0, 0); } } @@ -115,14 +115,24 @@ static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", rllh->chan_nr, rllh->link_id); - rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) { + LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return -EINVAL; + } + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); return -EIO; } msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); - rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rllh->chan_nr); + return -EINVAL; + } + switch (ch_type) { case RSL_CHAN_BCCH: return bcch(ms, msg); @@ -188,26 +198,37 @@ static int signal_cb(unsigned int subsys, unsigned int signal, return 0; } -int l23_app_init(struct osmocom_ms *ms) +static int _cbch_sniff_start(void) { - /* don't do layer3_init() as we don't want an actual L3 */ + int rc; - g_ms = ms; - lapdm_channel_set_l3(&ms->lapdm_channel, &rcv_rsl, ms); + rc = layer2_open(g_ms, g_ms->settings.layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return rc; + } - l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + l1ctl_tx_reset_req(g_ms, L1CTL_RES_T_FULL); /* FIXME: L1CTL_RES_T_FULL doesn't reset dedicated mode * (if previously set), so we release it here. */ - l1ctl_tx_dm_rel_req(ms); + l1ctl_tx_dm_rel_req(g_ms); + return 0; +} + +int l23_app_init(void) +{ + /* don't do layer3_init() as we don't want an actual L3 */ + l23_app_start = _cbch_sniff_start; + + g_ms = osmocom_ms_alloc(l23_ctx, "1"); + OSMO_ASSERT(g_ms); + + lapdm_channel_set_l3(&g_ms->lapdm_channel, &rcv_rsl, g_ms); return osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL); } -static struct l23_app_info info = { +const struct l23_app_info l23_app_info = { .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", .contribution = "Contributions by Holger Hans Peter Freyther\n", + .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_DBG, }; - -struct l23_app_info *l23_app_info() -{ - return &info; -} diff --git a/src/host/layer23/src/misc/app_ccch_scan.c b/src/host/layer23/src/misc/app_ccch_scan.c index e5a184f1..d2b6c242 100644 --- a/src/host/layer23/src/misc/app_ccch_scan.c +++ b/src/host/layer23/src/misc/app_ccch_scan.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -36,12 +32,16 @@ #include <osmocom/bb/misc/rslms.h> #include <osmocom/bb/misc/layer3.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/ms.h> #include <l1ctl_proto.h> static struct { + struct osmocom_ms *ms; int ccch_mode; } app_state; @@ -170,7 +170,12 @@ static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms) if (ia->page_mode & 0xf0) return 0; - rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, ia->chan_desc.chan_nr); + return -EINVAL; + } if (!ia->chan_desc.h0.h) { /* Non-hopping */ @@ -231,24 +236,6 @@ static char *chan_need(int need) } } -static char *mi_type_to_string(int type) -{ - switch (type) { - case GSM_MI_TYPE_NONE: - return "none"; - case GSM_MI_TYPE_IMSI: - return "imsi"; - case GSM_MI_TYPE_IMEI: - return "imei"; - case GSM_MI_TYPE_IMEISV: - return "imeisv"; - case GSM_MI_TYPE_TMSI: - return "tmsi"; - default: - return "invalid"; - } -} - /** * This can contain two MIs. The size checking is a bit of a mess. */ @@ -256,6 +243,7 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms) { struct gsm48_paging1 *pag; int len1, len2, mi_type, tag; + struct osmo_mobile_identity mi; char mi_string[GSM48_MI_SIZE]; /* is there enough room for the header + LV? */ @@ -274,11 +262,11 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms) } if (mi_type != GSM_MI_TYPE_NONE) { - gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[1], len1); - LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to %s M(%s) \n", + osmo_mobile_identity_decode(&mi, &pag->data[1], len1, false); + osmo_mobile_identity_to_str_buf(mi_string, sizeof(mi_string), &mi); + LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to M(%s)\n", pag_print_mode(pag->pag_mode), chan_need(pag->cneed1), - mi_type_to_string(mi_type), mi_string); } @@ -295,11 +283,11 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms) return -1; } - gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2 + len1 + 2], len2); - LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to %s M(%s) \n", + osmo_mobile_identity_decode(&mi, &pag->data[2 + len1 + 2], len2, false); + osmo_mobile_identity_to_str_buf(mi_string, sizeof(mi_string), &mi); + LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to M(%s)\n", pag_print_mode(pag->pag_mode), chan_need(pag->cneed2), - mi_type_to_string(mi_type), mi_string); } return 0; @@ -308,8 +296,9 @@ static int gsm48_rx_paging_p1(struct msgb *msg, struct osmocom_ms *ms) static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms) { struct gsm48_paging2 *pag; - int tag, len, mi_type; + struct osmo_mobile_identity mi; char mi_string[GSM48_MI_SIZE]; + int tag, len; if (msgb_l3len(msg) < sizeof(*pag)) { LOGP(DRR, LOGL_ERROR, "Paging2 message is too small.\n"); @@ -317,12 +306,14 @@ static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms) } pag = msgb_l3(msg); - LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n", - pag_print_mode(pag->pag_mode), - chan_need(pag->cneed1), pag->tmsi1); - LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n", - pag_print_mode(pag->pag_mode), - chan_need(pag->cneed2), pag->tmsi2); + LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to M(TMSI-0x%08x)\n", + pag_print_mode(pag->pag_mode), + chan_need(pag->cneed1), + osmo_load32be(&pag->tmsi1)); + LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to M(TMSI-0x%08x)\n", + pag_print_mode(pag->pag_mode), + chan_need(pag->cneed2), + osmo_load32be(&pag->tmsi2)); /* no optional element */ if (msgb_l3len(msg) < sizeof(*pag) + 3) @@ -330,7 +321,6 @@ static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms) tag = pag->data[0]; len = pag->data[1]; - mi_type = pag->data[2] & GSM_MI_TYPE_MASK; if (tag != GSM48_IE_MOBILE_ID) return 0; @@ -340,11 +330,10 @@ static int gsm48_rx_paging_p2(struct msgb *msg, struct osmocom_ms *ms) return -1; } - gsm48_mi_to_string(mi_string, sizeof(mi_string), &pag->data[2], len); - LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to %s M(%s) \n", + osmo_mobile_identity_decode(&mi, &pag->data[2], len, false); + osmo_mobile_identity_to_str_buf(mi_string, sizeof(mi_string), &mi); + LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan n/a to M(%s)\n", pag_print_mode(pag->pag_mode), - "n/a ", - mi_type_to_string(mi_type), mi_string); return 0; @@ -360,18 +349,20 @@ static int gsm48_rx_paging_p3(struct msgb *msg, struct osmocom_ms *ms) } pag = msgb_l3(msg); - LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to TMSI M(0x%x) \n", - pag_print_mode(pag->pag_mode), - chan_need(pag->cneed1), pag->tmsi1); - LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to TMSI M(0x%x) \n", - pag_print_mode(pag->pag_mode), - chan_need(pag->cneed2), pag->tmsi2); - LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan %s to TMSI M(0x%x) \n", - pag_print_mode(pag->pag_mode), - "n/a ", pag->tmsi3); - LOGP(DRR, LOGL_NOTICE, "Paging4: %s chan %s to TMSI M(0x%x) \n", - pag_print_mode(pag->pag_mode), - "n/a ", pag->tmsi4); + LOGP(DRR, LOGL_NOTICE, "Paging1: %s chan %s to M(TMSI-0x%08x)\n", + pag_print_mode(pag->pag_mode), + chan_need(pag->cneed1), + osmo_load32be(&pag->tmsi1)); + LOGP(DRR, LOGL_NOTICE, "Paging2: %s chan %s to M(TMSI-0x%08x)\n", + pag_print_mode(pag->pag_mode), + chan_need(pag->cneed2), + osmo_load32be(&pag->tmsi2)); + LOGP(DRR, LOGL_NOTICE, "Paging3: %s chan n/a to M(TMSI-0x%08x)\n", + pag_print_mode(pag->pag_mode), + osmo_load32be(&pag->tmsi3)); + LOGP(DRR, LOGL_NOTICE, "Paging4: %s chan n/a to M(TMSI-0x%08x)\n", + pag_print_mode(pag->pag_mode), + osmo_load32be(&pag->tmsi4)); return 0; } @@ -489,20 +480,33 @@ static int signal_cb(unsigned int subsys, unsigned int signal, return 0; } +static int _ccch_scan_start(void) +{ + int rc; + + rc = layer2_open(app_state.ms, app_state.ms->settings.layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return rc; + } + + l1ctl_tx_reset_req(app_state.ms, L1CTL_RES_T_FULL); + return 0; +} -int l23_app_init(struct osmocom_ms *ms) +int l23_app_init(void) { + l23_app_start = _ccch_scan_start; + + app_state.ms = osmocom_ms_alloc(l23_ctx, "1"); + OSMO_ASSERT(app_state.ms); + osmo_signal_register_handler(SS_L1CTL, &signal_cb, NULL); - l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); - return layer3_init(ms); + return layer3_init(app_state.ms); } -static struct l23_app_info info = { +const struct l23_app_info l23_app_info = { .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", .contribution = "Contributions by Holger Hans Peter Freyther\n", + .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_DBG, }; - -struct l23_app_info *l23_app_info() -{ - return &info; -} diff --git a/src/host/layer23/src/misc/app_cell_log.c b/src/host/layer23/src/misc/app_cell_log.c index 5b7f7b40..9c498006 100644 --- a/src/host/layer23/src/misc/app_cell_log.c +++ b/src/host/layer23/src/misc/app_cell_log.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <signal.h> @@ -30,28 +26,44 @@ #include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/gps.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/misc/cell_log.h> +#include <osmocom/core/application.h> #include <osmocom/core/talloc.h> #include <osmocom/core/utils.h> #include <l1ctl_proto.h> -extern struct log_target *stderr_target; -extern void *l23_ctx; - extern uint16_t basic_band_range[][2]; extern uint16_t (*band_range)[][2]; char *logname = "/dev/null"; int RACH_MAX = 2; +static struct osmocom_ms *g_ms; + -int _scan_work(struct osmocom_ms *ms) +int _scan_start(void) { + int rc; + + rc = layer2_open(g_ms, g_ms->settings.layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return rc; + } + + l1ctl_tx_reset_req(g_ms, L1CTL_RES_T_FULL); return 0; } -int _scan_exit(struct osmocom_ms *ms) +int _scan_work(void) +{ + return 0; +} + +int _scan_exit(void) { /* in case there is a lockup during exit */ signal(SIGINT, SIG_DFL); @@ -64,33 +76,31 @@ int _scan_exit(struct osmocom_ms *ms) return 0; } -int l23_app_init(struct osmocom_ms *ms) +int l23_app_init(void) { int rc; srand(time(NULL)); -// log_parse_category_mask(stderr_target, "DL1C:DRSL:DRR:DGPS:DSUM"); - log_parse_category_mask(stderr_target, "DSUM"); - log_set_log_level(stderr_target, LOGL_INFO); +// log_parse_category_mask(osmo_stderr_target, "DL1C:DRSL:DRR:DGPS:DSUM"); + log_parse_category_mask(osmo_stderr_target, "DSUM"); + log_set_log_level(osmo_stderr_target, LOGL_INFO); + l23_app_start = _scan_start; l23_app_work = _scan_work; l23_app_exit = _scan_exit; - rc = scan_init(ms); + g_ms = osmocom_ms_alloc(l23_ctx, "1"); + OSMO_ASSERT(g_ms); + + rc = scan_init(g_ms); if (rc) return rc; - l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); printf("Mobile initialized, please start phone now!\n"); return 0; } -static int l23_cfg_supported() -{ - return L23_OPT_TAP | L23_OPT_DBG; -} - static int l23_getopt_options(struct option **options) { static struct option opts [] = { @@ -151,7 +161,7 @@ static void parse_band_range(char* s) (*band_range)[i][1] = 0; } -static int l23_cfg_print_help() +static int l23_cfg_print_help(void) { printf("\nApplication specific\n"); printf(" -l --logfile LOGFILE Logfile for the cell log.\n"); @@ -232,16 +242,11 @@ cmd_line_error: exit(1); } -static struct l23_app_info info = { +const struct l23_app_info l23_app_info = { .copyright = "Copyright (C) 2010 Andreas Eversberg\n", .getopt_string = "g:p:l:r:nf:b:A:", - .cfg_supported = l23_cfg_supported, + .opt_supported = L23_OPT_TAP | L23_OPT_DBG, .cfg_getopt_opt = l23_getopt_options, .cfg_handle_opt = l23_cfg_handle, .cfg_print_help = l23_cfg_print_help, }; - -struct l23_app_info *l23_app_info() -{ - return &info; -} diff --git a/src/host/layer23/src/misc/app_echo_test.c b/src/host/layer23/src/misc/app_echo_test.c index 3a0ee0f4..f5b3136b 100644 --- a/src/host/layer23/src/misc/app_echo_test.c +++ b/src/host/layer23/src/misc/app_echo_test.c @@ -15,22 +15,19 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/misc/layer3.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> #include <osmocom/core/select.h> - +#include <osmocom/core/timer.h> static struct { struct osmo_timer_list timer; @@ -44,8 +41,11 @@ static void test_tmr_cb(void *data) osmo_timer_schedule(&test_data.timer, 1, 0); } -int l23_app_init(struct osmocom_ms *ms) +int l23_app_init(void) { + struct osmocom_ms *ms = osmocom_ms_alloc(l23_ctx, "1"); + OSMO_ASSERT(ms); + test_data.timer.cb = &test_tmr_cb; test_data.timer.data = ms; @@ -54,12 +54,7 @@ int l23_app_init(struct osmocom_ms *ms) return 0; } -static struct l23_app_info info = { +const struct l23_app_info l23_app_info = { .copyright = "Copyright (C) 2010 Harald Welte <laforge@gnumonks.org>\n", .contribution = "Contributions by Holger Hans Peter Freyther\n", }; - -struct l23_app_info *l23_app_info() -{ - return &info; -} diff --git a/src/host/layer23/src/misc/bcch_scan.c b/src/host/layer23/src/misc/bcch_scan.c index 5dc0bc3b..69df043d 100644 --- a/src/host/layer23/src/misc/bcch_scan.c +++ b/src/host/layer23/src/misc/bcch_scan.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/host/layer23/src/misc/cell_log.c b/src/host/layer23/src/misc/cell_log.c index 9edd742e..ed4d74d1 100644 --- a/src/host/layer23/src/misc/cell_log.c +++ b/src/host/layer23/src/misc/cell_log.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -36,14 +32,19 @@ #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/lapdm.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/gps.h> +#include <osmocom/bb/common/sysinfo.h> +#include <osmocom/bb/mobile/gsm48_rr.h> #include <osmocom/bb/misc/cell_log.h> -#include "../../../gsmmap/geo.h" +#include <osmocom/bb/misc/geo.h> #define READ_WAIT 2, 0 #define RACH_WAIT 0, 900000 @@ -97,7 +98,7 @@ static struct log_si { uint16_t flags; uint8_t bsic; int8_t rxlev_dbm; - uint16_t mcc, mnc, lac, cellid; + struct osmo_cell_global_id cgi; uint8_t ta; double latitude, longitude; } log_si; @@ -186,9 +187,10 @@ static void log_sysinfo(void) if (log_si.ta != 0xff) sprintf(ta_str, " TA=%d", log_si.ta); - LOGP(DSUM, LOGL_INFO, "Cell: ARFCN=%d MCC=%s MNC=%s (%s, %s)%s\n", - arfcn, gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), - gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc), ta_str); + LOGP(DSUM, LOGL_INFO, "Cell: ARFCN=%d MCC-MNC=%s (%s, %s)%s\n", + arfcn, osmo_plmn_name(&s->lai.plmn), + gsm_get_mcc(s->lai.plmn.mcc), + gsm_get_mnc(&s->lai.plmn), ta_str); LOGFILE("[sysinfo]\n"); LOGFILE("arfcn %d\n", s->arfcn); @@ -291,7 +293,7 @@ static void start_rach(void) ncch->data[2] = (s->ccch_conf == 1) << 7; ncch->data[3] = 0; ncch->data[4] = RSL_IE_ACCESS_DELAY; - ncch->data[5] = 0; /* no delay */ + ncch->data[5] = 0; /* no delay */ ncch->data[6] = RSL_IE_MS_POWER; ncch->data[7] = 0; /* full power */ @@ -669,11 +671,15 @@ static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); struct tlv_parsed tv; uint8_t ch_type, ch_subch, ch_ts; - + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", rllh->chan_nr, rllh->link_id); - rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) { + LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return -EINVAL; + } + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); return -EIO; @@ -684,7 +690,13 @@ static int unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rllh->chan_nr); + return -EINVAL; + } + switch (ch_type) { case RSL_CHAN_PCH_AGCH: return pch_agch(ms, msg); diff --git a/src/host/layer23/src/misc/geo.c b/src/host/layer23/src/misc/geo.c index 65633d2c..e02a2391 100644 --- a/src/host/layer23/src/misc/geo.c +++ b/src/host/layer23/src/misc/geo.c @@ -1,5 +1,6 @@ #include <math.h> -#include "geo.h" + +#include <osmocom/bb/misc/geo.h> void geo2space(double *x, double *y, double *z, double lon, double lat) { diff --git a/src/host/layer23/src/misc/geo.h b/src/host/layer23/src/misc/geo.h deleted file mode 100644 index 25e26cba..00000000 --- a/src/host/layer23/src/misc/geo.h +++ /dev/null @@ -1,12 +0,0 @@ -/* WGS 84 */ -#define EQUATOR_RADIUS 6378137.0 -#define POLE_RADIUS 6356752.314 - -#define PI 3.1415926536 - -void geo2space(double *x, double *y, double *z, double lat, double lon); -void space2geo(double *lat, double *lon, double x, double y, double z); -double distinspace(double x1, double y1, double z1, double x2, double y2, - double z2); -double distonplane(double x1, double y1, double x2, double y2); - diff --git a/src/host/gsmmap/gsmmap.c b/src/host/layer23/src/misc/gsmmap.c index 83f0d01c..378b358b 100644 --- a/src/host/gsmmap/gsmmap.c +++ b/src/host/layer23/src/misc/gsmmap.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #warning todo bsic #include <stdio.h> @@ -34,9 +30,9 @@ #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/logging.h> -#include "log.h" -#include "geo.h" -#include "locate.h" +#include <osmocom/bb/misc/log.h> +#include <osmocom/bb/misc/geo.h> +#include <osmocom/bb/misc/locate.h> /* * structure of power and cell infos @@ -56,7 +52,7 @@ static void nomem(void) exit(-ENOMEM); } -static void add_power() +static void add_power(void) { struct node_power *node_power; @@ -85,7 +81,7 @@ static void print_si(void *priv, const char *fmt, ...) fprintf(outfp, "%s", buffer); } -static void add_sysinfo() +static void add_sysinfo(void) { struct gsm48_sysinfo s; struct node_mcc *mcc; @@ -125,13 +121,13 @@ static void add_sysinfo() 23); printf("--------------------------------------------------------------------------\n"); gsm48_sysinfo_dump(&s, sysinfo.arfcn, print_si, stdout, NULL); - mcc = get_node_mcc(s.mcc); + mcc = get_node_mcc(s.lai.plmn.mcc); if (!mcc) nomem(); - mnc = get_node_mnc(mcc, s.mnc); + mnc = get_node_mnc(mcc, s.lai.plmn.mnc, s.lai.plmn.mnc_3_digits); if (!mnc) nomem(); - lac = get_node_lac(mnc, s.lac); + lac = get_node_lac(mnc, s.lai.lac); if (!lac) nomem(); cell = get_node_cell(lac, s.cell_id); @@ -300,8 +296,7 @@ void kml_footer(FILE *outfp) } -void kml_meas(FILE *outfp, struct node_meas *meas, int n, uint16_t mcc, - uint16_t mnc, uint16_t lac, uint16_t cellid) +static void kml_meas(FILE *outfp, struct node_meas *meas, int n, const struct osmo_cell_global_id *cgi) { struct tm *tm = localtime(&meas->gmt); @@ -309,8 +304,11 @@ void kml_meas(FILE *outfp, struct node_meas *meas, int n, uint16_t mcc, fprintf(outfp, "\t\t\t\t\t\t<name>%d: %d</name>\n", n, meas->rxlev); fprintf(outfp, "\t\t\t\t\t\t<description>\n"); fprintf(outfp, "MCC=%s MNC=%s\nLAC=%04x CELL-ID=%04x\n(%s %s)\n", - gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac, cellid, - gsm_get_mcc(mcc), gsm_get_mnc(mcc, mnc)); + osmo_mcc_name(cgi->lai.plmn.mcc), + osmo_mnc_name(cgi->lai.plmn.mnc, cgi->lai.plmn.mnc_3_digits), + cgi->lai.lac, cgi->cell_identity, + gsm_get_mcc(cgi->lai.plmn.mcc), + gsm_get_mnc(&cgi->lai.plmn)); fprintf(outfp, "\n%s", asctime(tm)); fprintf(outfp, "RX-LEV %d dBm\n", meas->rxlev); if (meas->ta_valid) @@ -427,11 +425,11 @@ void kml_cell(FILE *outfp, struct node_cell *cell) return; fprintf(outfp, "\t\t\t\t\t<Placemark>\n"); - fprintf(outfp, "\t\t\t\t\t\t<name>MCC=%s MNC=%s\nLAC=%04x " - "CELL-ID=%04x\n(%s %s)</name>\n", gsm_print_mcc(cell->s.mcc), - gsm_print_mnc(cell->s.mnc), cell->s.lac, cell->s.cell_id, - gsm_get_mcc(cell->s.mcc), - gsm_get_mnc(cell->s.mcc, cell->s.mnc)); + fprintf(outfp, "\t\t\t\t\t\t<name>LAI=%s " + "CELL-ID=%04x\n(%s %s)</name>\n", + osmo_lai_name(&cell->s.lai), cell->s.cell_id, + gsm_get_mcc(cell->s.lai.plmn.mcc), + gsm_get_mnc(&cell->s.lai.plmn)); fprintf(outfp, "\t\t\t\t\t\t<description>\n"); gsm48_sysinfo_dump(&cell->s, cell->sysinfo.arfcn, print_si, outfp, NULL); @@ -580,15 +578,20 @@ usage: /* folder open */ fprintf(outfp, "\t<Folder>\n"); fprintf(outfp, "\t\t<name>MCC %s (%s)</name>\n", - gsm_print_mcc(mcc->mcc), gsm_get_mcc(mcc->mcc)); + osmo_mcc_name(mcc->mcc), gsm_get_mcc(mcc->mcc)); fprintf(outfp, "\t\t<open>0</open>\n"); mnc = mcc->mnc; while (mnc) { + struct osmo_plmn_id plmn = { + .mcc = mcc->mcc, + .mnc = mnc->mnc, + .mnc_3_digits = mnc->mnc_3_digits, + }; printf(" MNC: %02x\n", mnc->mnc); /* folder open */ fprintf(outfp, "\t\t<Folder>\n"); fprintf(outfp, "\t\t\t<name>MNC %s (%s)</name>\n", - gsm_print_mnc(mnc->mnc), gsm_get_mnc(mcc->mcc, mnc->mnc)); + osmo_mnc_name(mnc->mnc, mnc->mnc_3_digits), gsm_get_mnc(&plmn)); fprintf(outfp, "\t\t\t<open>0</open>\n"); lac = mnc->lac; while (lac) { @@ -608,9 +611,20 @@ usage: while (meas) { if (meas->ta_valid) printf(" TA: %d\n", meas->ta); - if (meas->gps_valid) - kml_meas(outfp, meas, ++n, mcc->mcc, mnc->mnc, - lac->lac, cell->cellid); + if (meas->gps_valid) { + struct osmo_cell_global_id cgi = { + .lai = { + .plmn = { + .mcc = mcc->mcc, + .mnc = mnc->mnc, + .mnc_3_digits = mnc->mnc_3_digits, + }, + .lac = lac->lac, + }, + .cell_identity = cell->cellid, + }; + kml_meas(outfp, meas, ++n, &cgi); + } meas = meas->next; } kml_cell(outfp, cell); diff --git a/src/host/gsmmap/locate.c b/src/host/layer23/src/misc/locate.c index ed0ac931..eb37285e 100644 --- a/src/host/gsmmap/locate.c +++ b/src/host/layer23/src/misc/locate.c @@ -5,8 +5,8 @@ #include <errno.h> #include <math.h> -#include "geo.h" -#include "locate.h" +#include <osmocom/bb/misc/geo.h> +#include <osmocom/bb/misc/locate.h> #define CIRCLE_PROBE 30.0 #define FINETUNE_RADIUS 5.0 diff --git a/src/host/gsmmap/log.c b/src/host/layer23/src/misc/log.c index c2db801e..577e1268 100644 --- a/src/host/gsmmap/log.c +++ b/src/host/layer23/src/misc/log.c @@ -14,18 +14,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> #include <stdlib.h> #include <osmocom/bb/common/osmocom_data.h> - -#include "log.h" +#include <osmocom/bb/misc/log.h> extern struct power power; extern struct sysinfo sysinfo; @@ -60,18 +55,22 @@ struct node_mcc *get_node_mcc(uint16_t mcc) return node_mcc; } -struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc) +struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc, bool mnc_3_digits) { struct node_mnc *node_mnc; struct node_mnc **node_mnc_p = &mcc->mnc; while (*node_mnc_p) { /* found in list */ - if ((*node_mnc_p)->mnc == mnc) + if ((*node_mnc_p)->mnc == mnc && + (*node_mnc_p)->mnc_3_digits == mnc_3_digits) return *node_mnc_p; /* insert into list */ - if ((*node_mnc_p)->mnc > mnc) + if (((*node_mnc_p)->mnc > mnc) || + ((*node_mnc_p)->mnc == mnc && + (*node_mnc_p)->mnc_3_digits > mnc_3_digits)) { break; + } node_mnc_p = &((*node_mnc_p)->next); } @@ -80,6 +79,7 @@ struct node_mnc *get_node_mnc(struct node_mcc *mcc, uint16_t mnc) if (!node_mnc) return NULL; node_mnc->mnc = mnc; + node_mnc->mnc_3_digits = mnc_3_digits; node_mnc->next = *node_mnc_p; *node_mnc_p = node_mnc; return node_mnc; diff --git a/src/host/layer23/src/misc/rslms.c b/src/host/layer23/src/misc/rslms.c index 455e6631..c071113b 100644 --- a/src/host/layer23/src/misc/rslms.c +++ b/src/host/layer23/src/misc/rslms.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -34,6 +30,7 @@ #include <osmocom/bb/misc/rslms.h> #include <osmocom/bb/misc/layer3.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1ctl.h> /* Send a 'simple' RLL request to L2 */ @@ -61,11 +58,15 @@ static int rslms_rx_udata_ind(struct msgb *msg, struct osmocom_ms *ms) struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); struct tlv_parsed tv; int rc = 0; - + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", rllh->chan_nr, rllh->link_id); - rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) { + LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return -EINVAL; + } + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); return -EIO; diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am index 783ae162..d00e45ea 100644 --- a/src/host/layer23/src/mobile/Makefile.am +++ b/src/host/layer23/src/mobile/Makefile.am @@ -1,16 +1,62 @@ -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBGPS_CFLAGS) $(LIBLUA_CFLAGS) -LDADD = ../common/liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBGPS_LIBS) $(LIBLUA_LIBS) +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOISDN_CFLAGS) \ + $(LIBOSMOGPRSRLCMAC_CFLAGS) \ + $(LIBOSMOGPRSLLC_CFLAGS) \ + $(LIBOSMOGPRSSNDCP_CFLAGS) \ + $(LIBOSMOCODEC_CFLAGS) \ + $(LIBOSMOGAPK_CFLAGS) \ + $(LIBGPS_CFLAGS) \ + $(LIBLUA_CFLAGS) \ + $(NULL) noinst_LIBRARIES = libmobile.a -libmobile_a_SOURCES = gsm322.c gsm480_ss.c gsm411_sms.c gsm48_cc.c gsm48_mm.c \ - gsm48_rr.c gsm414.c mnccms.c settings.c subscriber.c support.c \ - transaction.c vty_interface.c voice.c mncc_sock.c primitives.c +libmobile_a_SOURCES = \ + gsm322.c \ + gsm480_ss.c \ + gsm411_sms.c \ + gsm48_cc.c \ + gsm44068_gcc_bcc.c \ + gsm48_mm.c \ + gsm48_rr.c \ + gsm414.c \ + mnccms.c \ + mncc_sock.c \ + primitives.c \ + tch.c \ + tch_data.c \ + tch_data_sock.c \ + tch_voice.c \ + transaction.c \ + vty_interface.c \ + $(NULL) bin_PROGRAMS = mobile mobile_SOURCES = main.c app_mobile.c -mobile_LDADD = libmobile.a $(LDADD) +mobile_LDADD = \ + libmobile.a \ + $(top_builddir)/src/common/liblayer23.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOISDN_LIBS) \ + $(LIBOSMOGPRSRLCMAC_LIBS) \ + $(LIBOSMOGPRSLLC_LIBS) \ + $(LIBOSMOGPRSSNDCP_LIBS) \ + $(LIBOSMOCODEC_LIBS) \ + $(LIBOSMOGAPK_LIBS) \ + $(LIBGPS_LIBS) \ + $(LIBLUA_LIBS) \ + $(NULL) # lua support if BUILD_LUA @@ -19,3 +65,9 @@ libmobile_a_SOURCES += script_lua.c else libmobile_a_SOURCES += script_nolua.c endif + +# GAPK I/O support +if BUILD_GAPK +AM_CPPFLAGS += -DWITH_GAPK_IO=1 +libmobile_a_SOURCES += gapk_io.c +endif diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c index a5feb796..477c4fa5 100644 --- a/src/host/layer23/src/mobile/app_mobile.c +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -16,32 +16,35 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <errno.h> #include <signal.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1l2_interface.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/gps.h> +#include <osmocom/bb/common/sap_interface.h> +#include <osmocom/bb/common/sim.h> +#include <osmocom/bb/common/l23_app.h> + #include <osmocom/bb/mobile/gsm48_rr.h> #include <osmocom/bb/mobile/gsm480_ss.h> +#include <osmocom/bb/mobile/gsm48_mm.h> +#include <osmocom/bb/mobile/gsm48_cc.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/bb/mobile/gsm411_sms.h> +#include <osmocom/bb/mobile/gsm322.h> #include <osmocom/bb/mobile/vty.h> #include <osmocom/bb/mobile/app_mobile.h> #include <osmocom/bb/mobile/mncc.h> -#include <osmocom/bb/mobile/voice.h> +#include <osmocom/bb/mobile/tch.h> #include <osmocom/bb/mobile/primitives.h> -#include <osmocom/bb/common/sap_interface.h> -#include <osmocom/vty/ports.h> -#include <osmocom/vty/logging.h> +#include <osmocom/vty/vty.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/core/msgb.h> @@ -51,14 +54,16 @@ #include <l1ctl_proto.h> +#include "config.h" + extern void *l23_ctx; extern struct llist_head ms_list; -extern int vty_reading; -int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg); +int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg); +int mncc_recv_external(struct osmocom_ms *ms, int msg_type, void *arg); int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg); -int (*mncc_recv_app)(struct osmocom_ms *ms, int, void *); -static int quit; +static int _quit; +extern int quit; /* l23 main */ /* handle ms instance */ int mobile_work(struct osmocom_ms *ms) @@ -82,12 +87,54 @@ int mobile_work(struct osmocom_ms *ms) return work; } -/* run ms instance, if layer1 is available */ -int mobile_signal_cb(unsigned int subsys, unsigned int signal, +/* SIM becomes ATTACHED/DETACHED, or answers a request */ +static int mobile_l23_subscr_signal_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) { + struct msgb *nmsg; + struct gsm48_mm_event *nmme; + struct osmocom_ms *ms; + struct osmobb_l23_subscr_sim_auth_resp_sig_data *sim_auth_resp; + + OSMO_ASSERT(subsys == SS_L23_SUBSCR); + + switch (signal) { + case S_L23_SUBSCR_SIM_ATTACHED: + ms = signal_data; + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + if (!nmsg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, nmsg); + break; + case S_L23_SUBSCR_SIM_DETACHED: + ms = signal_data; + nmsg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); + if (!nmsg) + return 0; + gsm48_mmr_downmsg(ms, nmsg); + break; + case S_L23_SUBSCR_SIM_AUTH_RESP: + sim_auth_resp = signal_data; + ms = sim_auth_resp->ms; + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_AUTH_RESPONSE); + if (!nmsg) + return 0; + nmme = (struct gsm48_mm_event *) nmsg->data; + memcpy(nmme->sres, sim_auth_resp->sres, 4); + gsm48_mmevent_msg(ms, nmsg); + break; + default: + OSMO_ASSERT(0); + } + + return 0; +} + +/* run ms instance, if layer1 is available */ +static int mobile_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ struct osmocom_ms *ms; - struct gsm_settings *set; struct msgb *nmsg; if (subsys != SS_L1CTL) @@ -96,7 +143,6 @@ int mobile_signal_cb(unsigned int subsys, unsigned int signal, switch (signal) { case S_L1CTL_RESET: ms = signal_data; - set = &ms->settings; /* waiting for reset after shutdown */ if (ms->shutdown == MS_SHUTDOWN_WAIT_RESET) { @@ -108,21 +154,10 @@ int mobile_signal_cb(unsigned int subsys, unsigned int signal, if (ms->started) break; - /* insert test card, if enabled */ - switch (set->sim_type) { - case GSM_SIM_TYPE_L1PHY: - /* trigger sim card reader process */ - gsm_subscr_simcard(ms); - break; - case GSM_SIM_TYPE_TEST: - gsm_subscr_testcard(ms, set->test_rplmn_mcc, - set->test_rplmn_mnc, set->test_lac, - set->test_tmsi, set->test_imsi_attached); - break; - case GSM_SIM_TYPE_SAP: - gsm_subscr_sapcard(ms); - break; - default: + if (ms->settings.sim_type != GSM_SIM_TYPE_NONE) { + /* insert sim card */ + gsm_subscr_insert(ms); + } else { /* no SIM, trigger PLMN selection process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON); if (!nmsg) @@ -167,6 +202,7 @@ int mobile_exit(struct osmocom_ms *ms, int force) gsm48_cc_exit(ms); gsm480_ss_exit(ms); gsm411_sms_exit(ms); + gsm44068_gcc_exit(ms); gsm_sim_exit(ms); lapdm_channel_exit(&ms->lapdm_channel); @@ -176,8 +212,8 @@ int mobile_exit(struct osmocom_ms *ms, int force) } else { mobile_set_shutdown(ms, MS_SHUTDOWN_COMPL); /* being down */ } - vty_notify(ms, NULL); - vty_notify(ms, "Power off!\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Power off!\n"); LOGP(DMOB, LOGL_NOTICE, "Power off! (MS %s)\n", ms->name); return 0; @@ -190,26 +226,27 @@ static int mobile_init(struct osmocom_ms *ms) gsm_settings_arfcn(ms); - lapdm_channel_init(&ms->lapdm_channel, LAPDM_MODE_MS); - ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI3].dl.t200_sec = - T200_DCCH_SHARED; - ms->lapdm_channel.lapdm_dcch.datalink[DL_SAPI3].dl.t200_usec = 0; - ms->lapdm_channel.lapdm_acch.datalink[DL_SAPI3].dl.t200_sec = - T200_ACCH; - ms->lapdm_channel.lapdm_acch.datalink[DL_SAPI3].dl.t200_usec = 0; + const int t200_ms_dcch[_NR_DL_SAPI] = { + [DL_SAPI0] = 1000, + [DL_SAPI3] = 1000 * T200_DCCH_SHARED + }; + const int t200_ms_acch[_NR_DL_SAPI] = { + [DL_SAPI0] = 2000, + [DL_SAPI3] = 1000 * T200_ACCH + }; + + lapdm_channel_init3(&ms->lapdm_channel, LAPDM_MODE_MS, + t200_ms_dcch, t200_ms_acch, + GSM_LCHAN_SDCCH, NULL); + lapdm_channel_set_flags(&ms->lapdm_channel, LAPDM_ENT_F_DROP_2ND_REJ); lapdm_channel_set_l1(&ms->lapdm_channel, l1ctl_ph_prim_cb, ms); - /* init SAP client before SIM card starts up */ - sap_init(ms); - - /* SAP response call-back */ - ms->sap_entity.sap_rsp_cb = &gsm_subscr_sap_rsp_cb; - gsm_sim_init(ms); gsm48_cc_init(ms); gsm480_ss_init(ms); gsm411_sms_init(ms); - gsm_voice_init(ms); + gsm44068_gcc_init(ms); + tch_init(ms); gsm_subscr_init(ms); gsm48_rr_init(ms); gsm48_mm_init(ms); @@ -236,6 +273,24 @@ static int mobile_init(struct osmocom_ms *ms) "default IMEI.\n***\n"); } + switch (ms->settings.mncc_handler) { + case MNCC_HANDLER_INTERNAL: + LOGP(DMOB, LOGL_INFO, "Using the built-in MNCC-handler for MS '%s'\n", ms->name); + ms->mncc_entity.mncc_recv = &mncc_recv_internal; + break; + case MNCC_HANDLER_EXTERNAL: + LOGP(DMOB, LOGL_INFO, "Using external MNCC-handler (socket '%s') for MS '%s'\n", + ms->settings.mncc_socket_path, ms->name); + ms->mncc_entity.mncc_recv = &mncc_recv_external; + ms->mncc_entity.sock_state = mncc_sock_init(ms, ms->settings.mncc_socket_path); + break; + case MNCC_HANDLER_DUMMY: + default: + LOGP(DMOB, LOGL_INFO, "Using dummy MNCC-handler (no call support) " + "for MS '%s'\n", ms->name); + ms->mncc_entity.mncc_recv = &mncc_recv_dummy; + } + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); LOGP(DMOB, LOGL_NOTICE, "Mobile '%s' initialized, please start phone now!\n", ms->name); return 0; @@ -255,7 +310,7 @@ int mobile_start(struct osmocom_ms *ms, char **other_name) if (!strcmp(ms->settings.layer2_socket_path, tmp->settings.layer2_socket_path)) { LOGP(DMOB, LOGL_ERROR, "Cannot start MS '%s', because MS '%s' " - "use the same layer2-socket.\nPlease shutdown " + "is using the same layer2-socket.\nPlease shutdown " "MS '%s' first.\n", ms->name, tmp->name, tmp->name); *other_name = tmp->name; return -1; @@ -263,11 +318,19 @@ int mobile_start(struct osmocom_ms *ms, char **other_name) if (!strcmp(ms->settings.sap_socket_path, tmp->settings.sap_socket_path)) { LOGP(DMOB, LOGL_ERROR, "Cannot start MS '%s', because MS '%s' " - "use the same sap-socket.\nPlease shutdown " + "is using the same sap-socket.\nPlease shutdown " "MS '%s' first.\n", ms->name, tmp->name, tmp->name); *other_name = tmp->name; return -2; } + if (!strcmp(ms->settings.mncc_socket_path, + tmp->settings.mncc_socket_path)) { + LOGP(DMOB, LOGL_ERROR, "Cannot start MS '%s', because MS '%s' " + "is using the same mncc-socket.\nPlease shutdown " + "MS '%s' first.\n", ms->name, tmp->name, tmp->name); + *other_name = tmp->name; + return -3; + } } rc = mobile_init(ms); @@ -290,40 +353,14 @@ int mobile_stop(struct osmocom_ms *ms, int force) struct osmocom_ms *mobile_new(char *name) { static struct osmocom_ms *ms; - char *mncc_name; - ms = talloc_zero(l23_ctx, struct osmocom_ms); + ms = osmocom_ms_alloc(l23_ctx, name); if (!ms) { LOGP(DMOB, LOGL_ERROR, "Failed to allocate MS: %s\n", name); return NULL; } - talloc_set_name(ms, "ms_%s", name); - ms->name = talloc_strdup(ms, name); - ms->l2_wq.bfd.fd = -1; - ms->sap_wq.bfd.fd = -1; - - /* Register a new MS */ - llist_add_tail(&ms->entity, &ms_list); - - gsm_support_init(ms); - gsm_settings_init(ms); - mobile_set_shutdown(ms, MS_SHUTDOWN_COMPL); - - if (mncc_recv_app) { - mncc_name = talloc_asprintf(ms, "/tmp/ms_mncc_%s", ms->name); - - ms->mncc_entity.mncc_recv = mncc_recv_app; - ms->mncc_entity.sock_state = mncc_sock_init(ms, mncc_name); - - talloc_free(mncc_name); - } else if (ms->settings.ch_cap == GSM_CAP_SDCCH) - ms->mncc_entity.mncc_recv = mncc_recv_dummy; - else - ms->mncc_entity.mncc_recv = mncc_recv_mobile; - - return ms; } @@ -334,7 +371,7 @@ int mobile_delete(struct osmocom_ms *ms, int force) ms->deleting = true; - if (mncc_recv_app) { + if (ms->settings.mncc_handler == MNCC_HANDLER_EXTERNAL) { mncc_sock_exit(ms->mncc_entity.sock_state); ms->mncc_entity.sock_state = NULL; } @@ -349,8 +386,8 @@ int mobile_delete(struct osmocom_ms *ms, int force) } /* handle global shutdown */ -int global_signal_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) +static int global_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) { struct osmocom_ms *ms, *ms2; @@ -361,20 +398,20 @@ int global_signal_cb(unsigned int subsys, unsigned int signal, case S_GLOBAL_SHUTDOWN: /* force to exit, if signalled */ if (signal_data && *((uint8_t *)signal_data)) - quit = 1; + _quit = 1; llist_for_each_entry_safe(ms, ms2, &ms_list, entity) - mobile_delete(ms, quit); + mobile_delete(ms, _quit); /* quit, after all MS processes are gone */ - quit = 1; + _quit = 1; break; } return 0; } /* global work handler */ -int l23_app_work(int *_quit) +static int _mobile_app_work(void) { struct osmocom_ms *ms, *ms2; int work = 0; @@ -387,14 +424,9 @@ int l23_app_work(int *_quit) layer2_close(ms); ms->l2_wq.bfd.fd = -1; } - - if (ms->sap_wq.bfd.fd > -1) { - sap_close(ms); - ms->sap_wq.bfd.fd = -1; - } - if (ms->deleting) { gsm_settings_exit(ms); + script_lua_close(ms); llist_del(&ms->entity); talloc_free(ms); work = 1; @@ -403,70 +435,27 @@ int l23_app_work(int *_quit) } /* return, if a shutdown was scheduled (quit = 1) */ - *_quit = quit; + quit = _quit; return work; } /* global exit */ -int l23_app_exit(void) +static int _mobile_app_exit(void) { + osmo_signal_unregister_handler(SS_L23_SUBSCR, &mobile_l23_subscr_signal_cb, NULL); osmo_signal_unregister_handler(SS_L1CTL, &gsm322_l1_signal, NULL); osmo_signal_unregister_handler(SS_L1CTL, &mobile_signal_cb, NULL); osmo_signal_unregister_handler(SS_GLOBAL, &global_signal_cb, NULL); osmo_gps_close(); - telnet_exit(); - return 0; } -static struct vty_app_info vty_info = { - .name = "OsmocomBB", - .version = PACKAGE_VERSION, - .go_parent_cb = ms_vty_go_parent, -}; -/* global init */ -int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *), - const char *config_file) +static int _mobile_app_start(void) { - struct telnet_connection dummy_conn; - int rc = 0; - - mncc_recv_app = mncc_recv; - - osmo_gps_init(); - - vty_info.tall_ctx = l23_ctx; - vty_init(&vty_info); - logging_vty_add_cmds(); - ms_vty_init(); - dummy_conn.priv = NULL; - vty_reading = 1; - if (config_file != NULL) { - rc = vty_read_config_file(config_file, &dummy_conn); - if (rc < 0) { - LOGP(DMOB, LOGL_FATAL, "Failed to parse the configuration " - "file '%s'\n", config_file); - LOGP(DMOB, LOGL_FATAL, "Please make sure the file " - "'%s' exists, or use an example from " - "'doc/examples/mobile/'\n", config_file); - return rc; - } - LOGP(DMOB, LOGL_INFO, "Using configuration from '%s'\n", config_file); - } - vty_reading = 0; - rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB); - if (rc < 0) { - LOGP(DMOB, LOGL_FATAL, "Cannot init VTY on %s port %u: %s\n", - vty_get_bind_addr(), OSMO_VTY_PORT_BB, strerror(errno)); - return rc; - } - - osmo_signal_register_handler(SS_GLOBAL, &global_signal_cb, NULL); - osmo_signal_register_handler(SS_L1CTL, &mobile_signal_cb, NULL); - osmo_signal_register_handler(SS_L1CTL, &gsm322_l1_signal, NULL); + int rc; if (llist_empty(&ms_list)) { struct osmocom_ms *ms; @@ -481,11 +470,45 @@ int l23_app_init(int (*mncc_recv)(struct osmocom_ms *ms, int, void *), return rc; } - quit = 0; + _quit = 0; + + return 0; +} + +/* global init */ +int l23_app_init(void) +{ + l23_app_start = _mobile_app_start; + l23_app_work = _mobile_app_work; + l23_app_exit = _mobile_app_exit; + osmo_gps_init(); + + osmo_signal_register_handler(SS_GLOBAL, &global_signal_cb, NULL); + osmo_signal_register_handler(SS_L1CTL, &mobile_signal_cb, NULL); + osmo_signal_register_handler(SS_L1CTL, &gsm322_l1_signal, NULL); + osmo_signal_register_handler(SS_L23_SUBSCR, &mobile_l23_subscr_signal_cb, NULL); return 0; } +static int _mobile_vty_init(void) +{ + return ms_vty_init(); +} + +static struct vty_app_info _mobile_vty_info = { + .name = "OsmocomBB(mobile)", + .version = PACKAGE_VERSION, +}; + +const struct l23_app_info l23_app_info = { + .copyright = "Copyright (C) 2010-2015 Andreas Eversberg, Sylvain Munaut, Holger Freyther, Harald Welte\n", + .contribution = "Contributions by Alex Badea, Pablo Neira, Steve Markgraf and others\n", + .opt_supported = L23_OPT_TAP | L23_OPT_VTY | L23_OPT_DBG, + .vty_info = &_mobile_vty_info, + .vty_init = _mobile_vty_init, +}; + void mobile_set_started(struct osmocom_ms *ms, bool state) { ms->started = state; diff --git a/src/host/layer23/src/mobile/gapk_io.c b/src/host/layer23/src/mobile/gapk_io.c new file mode 100644 index 00000000..cc756b06 --- /dev/null +++ b/src/host/layer23/src/mobile/gapk_io.c @@ -0,0 +1,547 @@ +/* + * GAPK (GSM Audio Pocket Knife) based audio I/O + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <string.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/gapk/procqueue.h> +#include <osmocom/gapk/formats.h> +#include <osmocom/gapk/codecs.h> +#include <osmocom/gapk/common.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/logging.h> + +#include <osmocom/bb/mobile/tch.h> +#include <osmocom/bb/mobile/gapk_io.h> + +/* The RAW PCM format is common for both audio source and sink */ +static const struct osmo_gapk_format_desc *rawpcm_fmt; + +static int pq_queue_tch_fb_recv(void *_state, uint8_t *out, + const uint8_t *in, unsigned int in_len) +{ + struct gapk_io_state *state = (struct gapk_io_state *)_state; + struct msgb *tch_msg; + size_t frame_len; + + /* Obtain one TCH frame from the DL buffer */ + tch_msg = msgb_dequeue_count(&state->tch_dl_fb, + &state->tch_dl_fb_len); + if (tch_msg == NULL) + return -EIO; + + /* Calculate received frame length */ + frame_len = msgb_l3len(tch_msg); + if (frame_len == 0) { + msgb_free(tch_msg); + return -EIO; + } + + /* Copy the frame bytes from message */ + memcpy(out, tch_msg->l3h, frame_len); + + /* Release memory */ + msgb_free(tch_msg); + + return frame_len; +} + +static int pq_queue_tch_fb_send(void *_state, uint8_t *out, + const uint8_t *in, unsigned int in_len) +{ + struct gapk_io_state *state = (struct gapk_io_state *)_state; + struct msgb *tch_msg; + + if (state->tch_ul_fb_len >= GAPK_ULDL_QUEUE_LIMIT) { + LOGP(DGAPK, LOGL_ERROR, "UL TCH frame buffer overflow, dropping msg\n"); + return -EOVERFLOW; + } + + /* Allocate a new message for the lower layers */ + tch_msg = msgb_alloc_headroom(in_len + 64, 64, "TCH frame"); + if (tch_msg == NULL) + return -ENOMEM; + + /* Copy the frame bytes to a new message */ + tch_msg->l2h = msgb_put(tch_msg, in_len); + memcpy(tch_msg->l2h, in, in_len); + + /* Put encoded TCH frame to the UL buffer */ + msgb_enqueue_count(&state->tch_ul_fb, tch_msg, + &state->tch_ul_fb_len); + + return 0; +} + +/** + * A custom TCH frame buffer block, which actually + * handles incoming frames from DL buffer and puts + * outgoing frames to UL buffer... + */ +static int pq_queue_tch_fb(struct osmo_gapk_pq *pq, + struct gapk_io_state *state, + bool is_src) +{ + struct osmo_gapk_pq_item *item; + unsigned int frame_len; + + LOGP(DGAPK, LOGL_DEBUG, "PQ '%s': Adding TCH frame buffer %s\n", + pq->name, is_src ? "input" : "output"); + + /* Allocate and add a new queue item */ + item = osmo_gapk_pq_add_item(pq); + if (item == NULL) + return -ENOMEM; + + /* General item type and description */ + item->type = is_src ? OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK; + item->cat_name = is_src ? "source" : "sink"; + item->sub_name = "tch_fb"; + + /* I/O length */ + frame_len = state->phy_fmt_desc->frame_len; + item->len_in = is_src ? 0 : frame_len; + item->len_out = is_src ? frame_len : 0; + + /* Handler and it's state */ + item->proc = is_src ? &pq_queue_tch_fb_recv : &pq_queue_tch_fb_send; + item->state = state; + + return 0; +} + +/** + * Auxiliary wrapper around format conversion block. + * Is used to perform either a conversion from the format, + * produced by encoder, to canonical, or a conversion + * from canonical format to the format expected by decoder. + */ +static int pq_queue_codec_fmt_conv(struct osmo_gapk_pq *pq, + const struct osmo_gapk_codec_desc *codec, + bool is_src) +{ + const struct osmo_gapk_format_desc *codec_fmt_desc; + + /* Get format description */ + codec_fmt_desc = osmo_gapk_fmt_get_from_type(is_src ? + codec->codec_enc_format_type : codec->codec_dec_format_type); + if (codec_fmt_desc == NULL) + return -ENOTSUP; + + /* Put format conversion block */ + return osmo_gapk_pq_queue_fmt_convert(pq, codec_fmt_desc, !is_src); +} + +/** + * Prepares the following queue (source is mic): + * + * source/alsa -> proc/codec -> proc/format -> + * -> proc/format -> sink/tch_fb + * + * The two format conversion blocks are aimed to + * convert an encoder specific format + * to a PHY specific format. + */ +static int prepare_audio_source(struct gapk_io_state *state, + const char *alsa_input_dev) +{ + struct osmo_gapk_pq *pq; + char *pq_desc; + int rc; + + LOGP(DGAPK, LOGL_DEBUG, "Prepare audio input (capture) chain\n"); + + /* Allocate a processing queue */ + pq = osmo_gapk_pq_create("pq_audio_source"); + if (pq == NULL) + return -ENOMEM; + + /* ALSA audio source */ + rc = osmo_gapk_pq_queue_alsa_input(pq, alsa_input_dev, rawpcm_fmt->frame_len); + if (rc) + goto error; + + /* Frame encoder */ + rc = osmo_gapk_pq_queue_codec(pq, state->codec_desc, 1); + if (rc) + goto error; + + /* Encoder specific format -> canonical */ + rc = pq_queue_codec_fmt_conv(pq, state->codec_desc, true); + if (rc) + goto error; + + /* Canonical -> PHY specific format */ + rc = osmo_gapk_pq_queue_fmt_convert(pq, state->phy_fmt_desc, 1); + if (rc) + goto error; + + /* TCH frame buffer sink */ + rc = pq_queue_tch_fb(pq, state, false); + if (rc) + goto error; + + /* Check composed queue in strict mode */ + rc = osmo_gapk_pq_check(pq, 1); + if (rc) + goto error; + + /* Prepare queue (allocate buffers, etc.) */ + rc = osmo_gapk_pq_prepare(pq); + if (rc) + goto error; + + /* Save pointer within MS GAPK state */ + state->pq_source = pq; + + /* Describe prepared chain */ + pq_desc = osmo_gapk_pq_describe(pq); + LOGP(DGAPK, LOGL_DEBUG, "PQ '%s': chain '%s' prepared\n", pq->name, pq_desc); + talloc_free(pq_desc); + + return 0; + +error: + talloc_free(pq); + return rc; +} + +/** + * Prepares the following queue (sink is speaker): + * + * src/tch_fb -> proc/format -> [proc/ecu] -> + * proc/format -> proc/codec -> sink/alsa + * + * The two format conversion blocks (proc/format) + * are aimed to convert a PHY specific format + * to an encoder specific format. + * + * A ECU (Error Concealment Unit) block is optionally + * added if implemented for a given codec. + */ +static int prepare_audio_sink(struct gapk_io_state *state, + const char *alsa_output_dev) +{ + struct osmo_gapk_pq *pq; + char *pq_desc; + int rc; + + LOGP(DGAPK, LOGL_DEBUG, "Prepare audio output (playback) chain\n"); + + /* Allocate a processing queue */ + pq = osmo_gapk_pq_create("pq_audio_sink"); + if (pq == NULL) + return -ENOMEM; + + /* TCH frame buffer source */ + rc = pq_queue_tch_fb(pq, state, true); + if (rc) + goto error; + + /* PHY specific format -> canonical */ + rc = osmo_gapk_pq_queue_fmt_convert(pq, state->phy_fmt_desc, 0); + if (rc) + goto error; + + /* Optional ECU (Error Concealment Unit) */ + osmo_gapk_pq_queue_ecu(pq, state->codec_desc); + + /* Canonical -> decoder specific format */ + rc = pq_queue_codec_fmt_conv(pq, state->codec_desc, false); + if (rc) + goto error; + + /* Frame decoder */ + rc = osmo_gapk_pq_queue_codec(pq, state->codec_desc, 0); + if (rc) + goto error; + + /* ALSA audio sink */ + rc = osmo_gapk_pq_queue_alsa_output(pq, alsa_output_dev, rawpcm_fmt->frame_len); + if (rc) + goto error; + + /* Check composed queue in strict mode */ + rc = osmo_gapk_pq_check(pq, 1); + if (rc) + goto error; + + /* Prepare queue (allocate buffers, etc.) */ + rc = osmo_gapk_pq_prepare(pq); + if (rc) + goto error; + + /* Save pointer within MS GAPK state */ + state->pq_sink = pq; + + /* Describe prepared chain */ + pq_desc = osmo_gapk_pq_describe(pq); + LOGP(DGAPK, LOGL_DEBUG, "PQ '%s': chain '%s' prepared\n", pq->name, pq_desc); + talloc_free(pq_desc); + + return 0; + +error: + talloc_free(pq); + return rc; +} + +/** + * Cleans up both TCH frame I/O buffers, destroys both + * processing queues (chains), and deallocates the memory. + * Should be called when a voice call is finished... + */ +void gapk_io_state_free(struct gapk_io_state *state) +{ + struct msgb *msg; + + if (state == NULL) + return; + + /* Flush TCH frame I/O buffers */ + while ((msg = msgb_dequeue(&state->tch_dl_fb))) + msgb_free(msg); + while ((msg = msgb_dequeue(&state->tch_ul_fb))) + msgb_free(msg); + + /* Destroy both audio I/O chains */ + if (state->pq_source != NULL) + osmo_gapk_pq_destroy(state->pq_source); + if (state->pq_sink != NULL) + osmo_gapk_pq_destroy(state->pq_sink); + + talloc_free(state); +} + +/** + * Picks the corresponding PHY's frame format for a given codec. + * To be used with PHYs that produce audio frames in RTP format, + * such as trxcon (GSM 05.03 libosmocoding API). + */ +static enum osmo_gapk_format_type phy_fmt_pick_rtp(enum osmo_gapk_codec_type codec) +{ + switch (codec) { + case CODEC_HR: + return FMT_RTP_HR_IETF; + case CODEC_FR: + return FMT_GSM; + case CODEC_EFR: + return FMT_RTP_EFR; + case CODEC_AMR: + return FMT_RTP_AMR; + default: + return FMT_INVALID; + } +} + +/** + * Picks the corresponding PHY's frame format for a given codec. + * To be used with PHYs that produce audio in TI Calypso format. + */ +static enum osmo_gapk_format_type phy_fmt_pick_ti(enum osmo_gapk_codec_type codec) +{ + switch (codec) { + case CODEC_HR: + return FMT_TI_HR; + case CODEC_FR: + return FMT_TI_FR; + case CODEC_EFR: + return FMT_TI_EFR; + case CODEC_AMR: /* not supported */ + default: + return FMT_INVALID; + } +} + +/** + * Allocates both TCH frame I/O buffers + * and prepares both processing queues (chains). + * Should be called when a voice call is initiated... + */ +struct gapk_io_state * +gapk_io_state_alloc(struct osmocom_ms *ms, + enum osmo_gapk_codec_type codec) +{ + const struct osmo_gapk_format_desc *phy_fmt_desc; + const struct osmo_gapk_codec_desc *codec_desc; + const struct gsm_settings *set = &ms->settings; + enum osmo_gapk_format_type phy_fmt; + struct gapk_io_state *state; + int rc = 0; + + LOGP(DGAPK, LOGL_NOTICE, "Initialize GAPK I/O\n"); + + /* Make sure that the chosen codec has description */ + codec_desc = osmo_gapk_codec_get_from_type(codec); + if (codec_desc == NULL) { + LOGP(DGAPK, LOGL_ERROR, "Invalid codec type 0x%02x\n", codec); + return NULL; + } + + /* Make sure that the chosen codec is supported */ + if (codec_desc->codec_encode == NULL || codec_desc->codec_decode == NULL) { + LOGP(DGAPK, LOGL_ERROR, + "Codec '%s' is not supported by GAPK\n", codec_desc->name); + return NULL; + } + + switch (set->tch_voice.io_format) { + case TCH_VOICE_IOF_RTP: + phy_fmt = phy_fmt_pick_rtp(codec); + break; + case TCH_VOICE_IOF_TI: + phy_fmt = phy_fmt_pick_ti(codec); + break; + default: + LOGP(DGAPK, LOGL_ERROR, "Unhandled I/O format %s\n", + tch_voice_io_format_name(set->tch_voice.io_format)); + return NULL; + } + + phy_fmt_desc = osmo_gapk_fmt_get_from_type(phy_fmt); + if (phy_fmt_desc == NULL) { + LOGP(DGAPK, LOGL_ERROR, "Failed to pick the PHY specific " + "frame format for codec '%s'\n", codec_desc->name); + return NULL; + } + + state = talloc_zero(ms, struct gapk_io_state); + if (state == NULL) { + LOGP(DGAPK, LOGL_ERROR, "Failed to allocate memory\n"); + return NULL; + } + + /* Init TCH frame I/O buffers */ + INIT_LLIST_HEAD(&state->tch_dl_fb); + INIT_LLIST_HEAD(&state->tch_ul_fb); + + /* Store the codec / format description */ + state->codec_desc = codec_desc; + state->phy_fmt_desc = phy_fmt_desc; + + /* Use gapk_io_state as talloc context for both chains */ + osmo_gapk_set_talloc_ctx(state); + + /* Prepare both source and sink chains */ + rc |= prepare_audio_source(state, set->tch_voice.alsa_input_dev); + rc |= prepare_audio_sink(state, set->tch_voice.alsa_output_dev); + + /* Fall back to ms instance */ + osmo_gapk_set_talloc_ctx(ms); + + /* If at lease one chain constructor failed */ + if (rc) { + /* Destroy both audio I/O chains */ + if (state->pq_source) + osmo_gapk_pq_destroy(state->pq_source); + if (state->pq_sink) + osmo_gapk_pq_destroy(state->pq_sink); + + /* Release the memory and return */ + talloc_free(state); + + LOGP(DGAPK, LOGL_ERROR, "Failed to initialize GAPK I/O\n"); + return NULL; + } + + LOGP(DGAPK, LOGL_NOTICE, + "GAPK I/O initialized for MS '%s', codec '%s'\n", + ms->name, codec_desc->name); + + return state; +} + +/* gapk_io_init_ms() wrapper, selecting a codec based on channel mode and rate */ +struct gapk_io_state * +gapk_io_state_alloc_mode_rate(struct osmocom_ms *ms, + enum gsm48_chan_mode ch_mode, + bool full_rate) +{ + enum osmo_gapk_codec_type codec; + + switch (ch_mode) { + case GSM48_CMODE_SPEECH_V1: /* FR or HR */ + codec = full_rate ? CODEC_FR : CODEC_HR; + break; + case GSM48_CMODE_SPEECH_EFR: + codec = CODEC_EFR; + break; + case GSM48_CMODE_SPEECH_AMR: + codec = CODEC_AMR; + break; + default: + LOGP(DGAPK, LOGL_ERROR, "Unhandled channel mode 0x%02x (%s)\n", + ch_mode, get_value_string(gsm48_chan_mode_names, ch_mode)); + return NULL; + } + + return gapk_io_state_alloc(ms, codec); +} + +/* Enqueue a Downlink TCH frame */ +void gapk_io_enqueue_dl(struct gapk_io_state *state, struct msgb *msg) +{ + if (state->tch_dl_fb_len >= GAPK_ULDL_QUEUE_LIMIT) { + LOGP(DGAPK, LOGL_ERROR, "DL TCH frame buffer overflow, dropping msg\n"); + msgb_free(msg); + return; + } + + msgb_enqueue_count(&state->tch_dl_fb, msg, + &state->tch_dl_fb_len); + + /* Decode and play a received DL TCH frame */ + osmo_gapk_pq_execute(state->pq_sink); +} + +/* Dequeue an Uplink TCH frame */ +void gapk_io_dequeue_ul(struct osmocom_ms *ms, struct gapk_io_state *state) +{ + struct msgb *msg; + + /* Record and encode an UL TCH frame */ + osmo_gapk_pq_execute(state->pq_source); + + /* Obtain one TCH frame from the UL buffer */ + msg = msgb_dequeue_count(&state->tch_ul_fb, &state->tch_ul_fb_len); + if (msg != NULL) + tch_send_msg(ms, msg); +} + +/** + * Performs basic initialization of GAPK library, + * setting the talloc root context and a logging category. + */ +static __attribute__((constructor)) void gapk_io_init(void) +{ + /* Init logging subsystem */ + osmo_gapk_log_init(DGAPK); + + /* Make RAWPCM format info easy to access */ + rawpcm_fmt = osmo_gapk_fmt_get_from_type(FMT_RAWPCM_S16LE); +} diff --git a/src/host/layer23/src/mobile/gsm322.c b/src/host/layer23/src/mobile/gsm322.c index cc4f0cd0..3d0cc77d 100644 --- a/src/host/layer23/src/mobile/gsm322.c +++ b/src/host/layer23/src/mobile/gsm322.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -36,18 +32,21 @@ #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/l1ctl.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/networks.h> +#include <osmocom/bb/common/utils.h> +#include <osmocom/bb/common/settings.h> #include <osmocom/bb/mobile/vty.h> #include <osmocom/bb/mobile/app_mobile.h> -#include <osmocom/bb/common/utils.h> +#include <osmocom/bb/mobile/gsm322.h> +#include <osmocom/bb/mobile/gsm48_mm.h> #include <l1ctl_proto.h> const char *ba_version = "osmocom BA V1\n"; static void gsm322_cs_timeout(void *arg); -static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc, - uint16_t mnc, int any); +static int gsm322_cs_select(struct osmocom_ms *ms, int index, const struct osmo_plmn_id *plmn, int any); static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg); static void gsm322_any_timeout(void *arg); static int gsm322_nb_scan(struct osmocom_ms *ms); @@ -513,7 +512,7 @@ static void gsm322_unselect_cell(struct gsm322_cellsel *cs) cs->si->si5 = 0; /* unset SI5* */ cs->si = NULL; memset(&cs->sel_si, 0, sizeof(cs->sel_si)); - cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0; + memset(&cs->sel_cgi, 0, sizeof(cs->sel_cgi)); } /* print to DCS logging */ @@ -535,17 +534,15 @@ static void print_dcs(void *priv, const char *fmt, ...) } /* del forbidden LA */ -int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc, uint16_t lac) +int gsm322_del_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai) { struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_la_list *la; llist_for_each_entry(la, &plmn->forbidden_la, entry) { - if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) { - LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " - "LAs (mcc=%s, mnc=%s, lac=%04x)\n", - gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac); + if (osmo_lai_cmp(&la->lai, lai) == 0) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden LAs (LAI=%s)\n", + osmo_lai_name(lai)); llist_del(&la->entry); talloc_free(la); return 0; @@ -556,21 +553,16 @@ int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, } /* add forbidden LA */ -int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc, uint16_t lac, uint8_t cause) +int gsm322_add_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai, uint8_t cause) { struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_la_list *la; - LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs " - "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc), - gsm_print_mnc(mnc), lac); + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs (LAI=%s)\n", osmo_lai_name(lai)); la = talloc_zero(ms, struct gsm322_la_list); if (!la) return -ENOMEM; - la->mcc = mcc; - la->mnc = mnc; - la->lac = lac; + la->lai = *lai; la->cause = cause; llist_add_tail(&la->entry, &plmn->forbidden_la); @@ -578,14 +570,13 @@ int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, } /* search forbidden LA */ -int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, - uint16_t lac) +int gsm322_is_forbidden_la(struct osmocom_ms *ms, const struct osmo_location_area_id *lai) { struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_la_list *la; llist_for_each_entry(la, &plmn->forbidden_la, entry) { - if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) + if (osmo_lai_cmp(&la->lai, lai) == 0) return 1; } @@ -593,15 +584,13 @@ int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, } /* search for PLMN in all BA lists */ -static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, - uint16_t mcc, uint16_t mnc) +static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn) { struct gsm322_ba_list *ba, *ba_found = NULL; /* search for BA list */ llist_for_each_entry(ba, &cs->ba_list, entry) { - if (ba->mcc == mcc - && ba->mnc == mnc) { + if (osmo_plmn_cmp(&ba->plmn, plmn) == 0) { ba_found = ba; break; } @@ -611,16 +600,14 @@ static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, } /* search available PLMN */ -int gsm322_is_plmn_avail_and_allow(struct gsm322_cellsel *cs, uint16_t mcc, - uint16_t mnc) +int gsm322_is_plmn_avail_and_allow(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn) { int i; for (i = 0; i <= 1023+299; i++) { if ((cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA) && cs->list[i].sysinfo - && cs->list[i].sysinfo->mcc == mcc - && cs->list[i].sysinfo->mnc == mnc) + && (osmo_plmn_cmp(&cs->list[i].sysinfo->lai.plmn, plmn) == 0)) return 1; } @@ -635,9 +622,12 @@ int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi) for (i = 0; i <= 1023+299; i++) { if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) && cs->list[i].sysinfo - && gsm_match_mnc(cs->list[i].sysinfo->mcc, - cs->list[i].sysinfo->mnc, imsi)) + && gsm_match_mnc(cs->list[i].sysinfo->lai.plmn.mcc, + cs->list[i].sysinfo->lai.plmn.mnc, + cs->list[i].sysinfo->lai.plmn.mnc_3_digits, + imsi)) return 1; + /* TODO: take into account mnc_3_digits, probably use osmo_mnc_cmp()*/ } return 0; @@ -899,8 +889,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) /* search if network has multiple cells */ found = NULL; llist_for_each_entry(temp, &temp_list, entry) { - if (temp->mcc == cs->list[i].sysinfo->mcc - && temp->mnc == cs->list[i].sysinfo->mnc) { + if (osmo_plmn_cmp(&temp->plmn, &cs->list[i].sysinfo->lai.plmn) == 0) { found = temp; break; } @@ -913,8 +902,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) temp = talloc_zero(ms, struct gsm322_plmn_list); if (!temp) return -ENOMEM; - temp->mcc = cs->list[i].sysinfo->mcc; - temp->mnc = cs->list[i].sysinfo->mnc; + memcpy(&temp->plmn, &cs->list[i].sysinfo->lai.plmn, sizeof(temp->plmn)); temp->rxlev = cs->list[i].rxlev; llist_add_tail(&temp->entry, &temp_list); } @@ -924,7 +912,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) if (subscr->sim_valid) { found = NULL; llist_for_each_entry(temp, &temp_list, entry) { - if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) { + if (gsm_match_mnc(temp->plmn.mcc, temp->plmn.mnc, temp->plmn.mnc_3_digits, subscr->imsi)) { found = temp; break; } @@ -940,8 +928,7 @@ static int gsm322_sort_list(struct osmocom_ms *ms) llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) { found = NULL; llist_for_each_entry(temp, &temp_list, entry) { - if (temp->mcc == sim_entry->mcc - && temp->mnc == sim_entry->mnc) { + if (osmo_plmn_cmp(&temp->plmn, &sim_entry->plmn) == 0) { found = temp; break; } @@ -995,16 +982,15 @@ static int gsm322_sort_list(struct osmocom_ms *ms) i = 0; llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { llist_for_each_entry(na_entry, &subscr->plmn_na, entry) { - if (temp->mcc == na_entry->mcc - && temp->mnc == na_entry->mnc) { + if (osmo_plmn_cmp(&temp->plmn, &na_entry->plmn) == 0) { temp->cause = na_entry->cause; break; } } LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. " - "(%02d: mcc %s mnc %s allowed %s rx-lev %s)\n", - i, gsm_print_mcc(temp->mcc), - gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes", + "(%02d: mcc-mnc %s allowed %s rx-lev %s)\n", + i, osmo_plmn_name(&temp->plmn), + (temp->cause) ? "no ":"yes", gsm_print_rxlev(temp->rxlev)); i++; } @@ -1027,9 +1013,9 @@ static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) new_a_state(plmn, GSM322_A2_ON_PLMN); /* start timer, if on VPLMN of home country OR special case */ - if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi) + if (!gsm_match_mnc(plmn->plmn.mcc, plmn->plmn.mnc, plmn->plmn.mnc_3_digits, subscr->imsi) && (subscr->always_search_hplmn - || gsm_match_mcc(plmn->mcc, subscr->imsi)) + || gsm_match_mcc(plmn->plmn.mcc, subscr->imsi)) && subscr->sim_valid && subscr->t6m_hplmn) start_plmn_timer(plmn, subscr->t6m_hplmn * 360); else @@ -1070,7 +1056,7 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) int found; /* any allowable PLMN available? */ - found = gsm322_cs_select(ms, -1, 0, 0, 0); + found = gsm322_cs_select(ms, -1, NULL, 0); /* if no PLMN in list: * this means that we are at a point where we camp on any cell or @@ -1079,32 +1065,28 @@ static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) if (subscr->plmn_valid) { LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. " "Do limited search with RPLMN.\n"); - plmn->mcc = subscr->plmn_mcc; - plmn->mnc = subscr->plmn_mnc; + memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id)); } else if (subscr->sim_valid) { LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. " "Do limited search with HPLMN.\n"); - plmn->mcc = subscr->mcc; - plmn->mnc = subscr->mnc; + memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id)); } else { LOGP(DPLMN, LOGL_INFO, "Not any PLMN allowable. " "Do limited search with no PLMN.\n"); - plmn->mcc = 0; - plmn->mnc = 0; + memset(&plmn->plmn, 0, sizeof(struct osmo_plmn_id)); } return gsm322_a_go_wait_for_plmns(ms, msg); } /* select first PLMN in list */ - plmn->mcc = cs->list[found].sysinfo->mcc; - plmn->mnc = cs->list[found].sysinfo->mnc; + memcpy(&plmn->plmn, &cs->list[found].sysinfo->lai.plmn, sizeof(struct osmo_plmn_id)); LOGP(DPLMN, LOGL_INFO, "PLMN available after searching PLMN list " - "(mcc=%s mnc=%s %s, %s)\n", - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), - gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + "(mcc=-mnc=%s %s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), gsm_get_mnc(&plmn->plmn)); /* indicate New PLMN */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); @@ -1133,10 +1115,9 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) i = 0; llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { /* if last selected PLMN was HPLMN, we skip that */ - if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc, - subscr->imsi) - && plmn_entry->mcc == plmn->mcc - && plmn_entry->mnc == plmn->mnc) { + if (gsm_match_mnc(plmn_entry->plmn.mcc, plmn_entry->plmn.mnc, + plmn_entry->plmn.mnc_3_digits, subscr->imsi) + && (osmo_plmn_cmp(&plmn_entry->plmn, &plmn->plmn) == 0)) { LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was " "previously selected.\n"); i++; @@ -1147,10 +1128,9 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) plmn_first = plmn_entry; break; } - LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), " + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc-mnc=%s), " "because it is not allowed (cause %d).\n", i, - gsm_print_mcc(plmn_entry->mcc), - gsm_print_mnc(plmn_entry->mnc), + osmo_plmn_name(&plmn_entry->plmn), plmn_entry->cause); i++; } @@ -1164,15 +1144,14 @@ static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) return 0; } - LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " - "mnc=%s %s, %s)\n", plmn->plmn_curr, - gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc), - gsm_get_mcc(plmn_first->mcc), - gsm_get_mnc(plmn_first->mcc, plmn_first->mnc)); + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc-mnc=%s %s, %s)\n", + plmn->plmn_curr, + osmo_plmn_name(&plmn_first->plmn), + gsm_get_mcc(plmn_first->plmn.mcc), + gsm_get_mnc(&plmn_first->plmn)); /* set current network */ - plmn->mcc = plmn_first->mcc; - plmn->mnc = plmn_first->mnc; + memcpy(&plmn->plmn, &plmn_first->plmn, sizeof(struct osmo_plmn_id)); new_a_state(plmn, GSM322_A3_TRYING_PLMN); @@ -1208,10 +1187,9 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) plmn_next = plmn_entry; break; } - LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%s, mnc=%s), " + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc-mnc=%s), " "because it is not allowed (cause %d).\n", i, - gsm_print_mcc(plmn_entry->mcc), - gsm_print_mnc(plmn_entry->mnc), + osmo_plmn_name(&plmn_entry->plmn), plmn_entry->cause); i++; } @@ -1225,13 +1203,13 @@ static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) } /* set next network */ - plmn->mcc = plmn_next->mcc; - plmn->mnc = plmn_next->mnc; + memcpy(&plmn->plmn, &plmn_next->plmn, sizeof(struct osmo_plmn_id)); - LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " - "mnc=%s %s, %s)\n", plmn->plmn_curr, - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), - gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc-mnc=%s %s, %s)\n", + plmn->plmn_curr, + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); new_a_state(plmn, GSM322_A3_TRYING_PLMN); @@ -1268,8 +1246,7 @@ static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg) /* search current PLMN in list */ llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { - if (plmn_entry->mcc == plmn->mcc - && plmn_entry->mnc == plmn->mnc) { + if (osmo_plmn_cmp(&plmn_entry->plmn, &plmn->plmn) == 0) { plmn_found = plmn_entry; break; } @@ -1305,8 +1282,8 @@ static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; - if (subscr->plmn_valid && plmn->mcc == gm->mcc - && plmn->mnc == gm->mnc) { + if (subscr->plmn_valid && + (osmo_plmn_cmp(&plmn->plmn, &gm->plmn) == 0)) { struct msgb *nmsg; new_m_state(plmn, GSM322_A1_TRYING_RPLMN); @@ -1337,17 +1314,14 @@ static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) int found; /* any allowable PLMN available */ - found = gsm322_cs_select(ms, -1, 0, 0, 0); + found = gsm322_cs_select(ms, -1, NULL, 0); /* if PLMN in list */ if (found >= 0) { - LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s " - "%s, %s)\n", gsm_print_mcc( - cs->list[found].sysinfo->mcc), - gsm_print_mnc(cs->list[found].sysinfo->mnc), - gsm_get_mcc(cs->list[found].sysinfo->mcc), - gsm_get_mnc(cs->list[found].sysinfo->mcc, - cs->list[found].sysinfo->mnc)); + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&cs->list[found].sysinfo->lai.plmn), + gsm_get_mcc(cs->list[found].sysinfo->lai.plmn.mcc), + gsm_get_mnc(&cs->list[found].sysinfo->lai.plmn)); return gsm322_a_sel_first_plmn(ms, msg); } @@ -1374,17 +1348,17 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) /* if there is a registered PLMN */ if (subscr->plmn_valid) { /* select the registered PLMN */ - plmn->mcc = subscr->plmn_mcc; - plmn->mnc = subscr->plmn_mnc; + memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id)); LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " - "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), - gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); - LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " - "%s, %s)\n", gsm_print_mcc(plmn->mcc), - gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); + "(mcc-%s %s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); new_a_state(plmn, GSM322_A1_TRYING_RPLMN); @@ -1397,7 +1371,7 @@ static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) return 0; } - plmn->mcc = plmn->mnc = 0; + memset(&plmn->plmn, 0, sizeof(plmn->plmn)); /* initiate search at cell selection */ LOGP(DSUM, LOGL_INFO, "Search for network\n"); @@ -1440,7 +1414,7 @@ static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg) gsm322_cs_sendmsg(ms, nmsg); /* flush list of PLMNs */ - gsm_subscr_del_forbidden_plmn(&ms->subscr, 0, 0); + gsm_subscr_del_forbidden_plmn(&ms->subscr, NULL); return gsm322_a_switch_on(ms, msg); } @@ -1515,41 +1489,37 @@ static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) /* generate list */ gsm322_sort_list(ms); - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); switch (msg_type) { case GSM322_EVENT_REG_FAILED: - vty_notify(ms, "Failed to register to network %s, %s " - "(%s, %s)\n", - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), - gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); + l23_vty_ms_notify(ms, "Failed to register to network %s (%s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); break; case GSM322_EVENT_NO_CELL_FOUND: - vty_notify(ms, "No cell found for network %s, %s " - "(%s, %s)\n", - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), - gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); + l23_vty_ms_notify(ms, "No cell found for network %s (%s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); break; case GSM322_EVENT_ROAMING_NA: - vty_notify(ms, "Roaming not allowed to network %s, %s " - "(%s, %s)\n", - gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), - gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); + l23_vty_ms_notify(ms, "Roaming not allowed to network %s (%s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); break; } if (llist_empty(&plmn->sorted_plmn)) - vty_notify(ms, "Search network!\n"); + l23_vty_ms_notify(ms, "Search network!\n"); else { - vty_notify(ms, "Search or select from network:\n"); + l23_vty_ms_notify(ms, "Search or select from network:\n"); llist_for_each_entry(temp, &plmn->sorted_plmn, entry) - vty_notify(ms, " Network %s, %s (%s, %s)\n", - gsm_print_mcc(temp->mcc), - gsm_print_mnc(temp->mnc), - gsm_get_mcc(temp->mcc), - gsm_get_mnc(temp->mcc, temp->mnc)); + l23_vty_ms_notify(ms, " Network mcc-mnc=%s (%s, %s)\n", + osmo_plmn_name(&temp->plmn), + gsm_get_mcc(temp->plmn.mcc), + gsm_get_mnc(&temp->plmn)); } /* go Not on PLMN state */ @@ -1582,7 +1552,7 @@ static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg) * selected by the user. this prevents from switching back to the * last selected PLMN and destroying the list of scanned networks. */ - plmn->mcc = plmn->mnc = 0; + memset(&plmn->plmn, 0, sizeof(plmn->plmn)); if (!subscr->sim_valid) { return 0; @@ -1634,17 +1604,16 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; /* select the registered PLMN */ - plmn->mcc = subscr->plmn_mcc; - plmn->mnc = subscr->plmn_mnc; + memcpy(&plmn->plmn, &subscr->plmn, sizeof(struct osmo_plmn_id)); - LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " - "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), - gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); - LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " - "%s, %s)\n", gsm_print_mcc(plmn->mcc), - gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); new_m_state(plmn, GSM322_M1_TRYING_RPLMN); @@ -1657,7 +1626,7 @@ static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) return 0; } - plmn->mcc = plmn->mnc = 0; + memset(&plmn->plmn, 0, sizeof(plmn->plmn)); /* initiate search at cell selection */ LOGP(DSUM, LOGL_INFO, "Search for network\n"); @@ -1704,7 +1673,7 @@ static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg) gsm322_cs_sendmsg(ms, nmsg); /* flush list of PLMNs */ - gsm_subscr_del_forbidden_plmn(&ms->subscr, 0, 0); + gsm_subscr_del_forbidden_plmn(&ms->subscr, NULL); return gsm322_m_switch_on(ms, msg); } @@ -1716,9 +1685,8 @@ static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) struct gsm_subscriber *subscr = &ms->subscr; /* set last registered PLMN */ - subscr->plmn_valid = 1; - subscr->plmn_mcc = plmn->mcc; - subscr->plmn_mnc = plmn->mnc; + subscr->plmn_valid = true; + memcpy(&subscr->plmn, &plmn->plmn, sizeof(struct osmo_plmn_id)); new_m_state(plmn, GSM322_M2_ON_PLMN); @@ -1753,15 +1721,15 @@ static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; /* use user selection */ - plmn->mcc = gm->mcc; - plmn->mnc = gm->mnc; + memcpy(&plmn->plmn, &gm->plmn, sizeof(struct osmo_plmn_id)); - LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s " - "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), - gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&plmn->plmn), + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); /* if selected PLMN is in list of forbidden PLMNs */ - gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc); + gsm_subscr_del_forbidden_plmn(subscr, &plmn->plmn); new_m_state(plmn, GSM322_M4_TRYING_PLMN); @@ -1817,8 +1785,7 @@ static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg) */ /* select a suitable and allowable cell */ -static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc, - uint16_t mnc, int any) +static int gsm322_cs_select(struct osmocom_ms *ms, int index, const struct osmo_plmn_id *plmn, int any) { struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_settings *set = &ms->settings; @@ -1906,58 +1873,51 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc, if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { if (!any) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is " - "in list of forbidden LAs. (mcc=%s " - "mnc=%s lai=%04x)\n", + "in list of forbidden LAs. (lai=%s)\n", gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); + osmo_lai_name(&s->lai)); continue; } LOGP(DCS, LOGL_INFO, "Accept ARFCN %s: Cell is in " "list of forbidden LAs, but we search for any " - "cell. (mcc=%s mnc=%s lai=%04x)\n", + "cell. (lai=%s)\n", gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); + osmo_lai_name(&s->lai)); cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; } /* if cell is in list of forbidden PLMNs */ - if (gsm_subscr_is_forbidden_plmn(subscr, s->mcc, s->mnc)) { + if (gsm_subscr_is_forbidden_plmn(subscr, &s->lai.plmn)) { if (!any) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is " - "in list of forbidden PLMNs. (mcc=%s " - "mnc=%s)\n", + "in list of forbidden PLMNs. (mcc-mnc=%s)\n", gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc)); + osmo_plmn_name(&s->lai.plmn)); continue; } LOGP(DCS, LOGL_INFO, "Accept ARFCN %s: Cell is in list " "of forbidden PLMNs, but we search for any " - "cell. (mcc=%s mnc=%s)\n", + "cell. (mcc-mnc=%s)\n", gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc)); + osmo_plmn_name(&s->lai.plmn)); cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; } /* if we search a specific PLMN, but it does not match */ - if (!any && mcc && (mcc != s->mcc - || mnc != s->mnc)) { + if (!any && plmn && (osmo_plmn_cmp(&s->lai.plmn, plmn) != 0)) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: PLMN of cell " - "does not match target PLMN. (mcc=%s " - "mnc=%s)\n", gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc)); + "does not match target PLMN. (mcc-mnc=%s)\n", + gsm_print_arfcn(index2arfcn(i)), + osmo_plmn_name(&s->lai.plmn)); continue; } LOGP(DCS, LOGL_INFO, "Cell ARFCN %s: Cell found, (rxlev=%s " - "mcc=%s mnc=%s lac=%04x %s, %s)\n", - gsm_print_arfcn(index2arfcn(i)), + "lai=%s %s, %s)\n", + gsm_print_arfcn(index2arfcn(i)), gsm_print_rxlev(cs->list[i].rxlev), - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac, - gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc)); + osmo_lai_name(&s->lai), + gsm_get_mcc(s->lai.plmn.mcc), gsm_get_mnc(&s->lai.plmn)); /* find highest power cell */ if (found < 0 || cs->list[i].rxlev > power) { @@ -1974,8 +1934,7 @@ static int gsm322_cs_select(struct osmocom_ms *ms, int index, uint16_t mcc, } /* re-select a suitable and allowable cell */ -static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc, - uint16_t mnc, int any) +static int gsm322_cs_reselect(struct osmocom_ms *ms, const struct osmo_plmn_id *plmn, int any) { struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_subscriber *subscr = &ms->subscr; @@ -2004,18 +1963,18 @@ static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc, /* if cell is in list of forbidden LAs */ if (!any && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in list of " - "forbidden LAs. (mcc=%s mnc=%s lai=%04x)\n", - gsm_print_arfcn(index2arfcn(i)), gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); + "forbidden LAs. (lai=%s)\n", + gsm_print_arfcn(index2arfcn(i)), + osmo_lai_name(&s->lai)); return -1; } /* if cell is in list of forbidden PLMNs */ - if (!any && gsm_subscr_is_forbidden_plmn(subscr, s->mcc, s->mnc)) { + if (!any && gsm_subscr_is_forbidden_plmn(subscr, &s->lai.plmn)) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: Cell is in " - "list of forbidden PLMNs. (mcc=%s mnc=%s)\n", + "list of forbidden PLMNs. (mcc-mnc=%s)\n", gsm_print_arfcn(index2arfcn(i)), - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc)); + osmo_plmn_name(&s->lai.plmn)); return -1; } @@ -2030,21 +1989,21 @@ static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc, } /* if we search a specific PLMN, but it does not match */ - if (!any && mcc && (mcc != s->mcc - || mnc != s->mnc)) { + if (!any && plmn && (osmo_plmn_cmp(plmn, &s->lai.plmn) != 0)) { LOGP(DCS, LOGL_INFO, "Skip ARFCN %s: PLMN of cell " - "does not match target PLMN. (mcc=%s mnc=%s)\n", - gsm_print_arfcn(index2arfcn(i)), gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc)); + "does not match target PLMN. (mcc-mnc=%s)\n", + gsm_print_arfcn(index2arfcn(i)), + osmo_plmn_name(&s->lai.plmn)); return -1; } LOGP(DCS, LOGL_INFO, "Cell ARFCN %s: Neighbour cell accepted, " - "(rxlev=%s mcc=%s mnc=%s lac=%04x %s, %s)\n", + "(rxlev=%s lai=%s %s, %s)\n", gsm_print_arfcn(index2arfcn(i)), gsm_print_rxlev(cs->list[i].rxlev), - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac, - gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc)); + osmo_lai_name(&s->lai), + gsm_get_mcc(s->lai.plmn.mcc), + gsm_get_mnc(&s->lai.plmn)); return i; } @@ -2053,12 +2012,13 @@ static int gsm322_cs_reselect(struct osmocom_ms *ms, uint16_t mcc, static int gsm322_search_end(struct osmocom_ms *ms) { struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_plmn *plmn322 = &ms->plmn; struct msgb *nmsg; struct gsm322_msg *ngm; int msg_type = -1; /* no message to be sent */ - int tune_back = 0, mcc = 0, mnc = 0; + int tune_back = 0; int found; + struct osmo_plmn_id plmn = {}; switch (cs->state) { case GSM322_ANY_SEARCH: @@ -2066,12 +2026,12 @@ static int gsm322_search_end(struct osmocom_ms *ms) LOGP(DCS, LOGL_INFO, "Any cell search finished.\n"); /* create AA flag */ - found = gsm322_cs_select(ms, -1, 0, 0, 0); + found = gsm322_cs_select(ms, -1, NULL, 0); /* if no cell is found, or if we don't wait for any available * and allowable PLMN to appear, we just continue to camp */ if (ms->settings.plmn_mode != PLMN_MODE_AUTO - || plmn->state != GSM322_A4_WAIT_FOR_PLMN + || plmn322->state != GSM322_A4_WAIT_FOR_PLMN || found < 0) { tune_back = 1; gsm322_c_camp_any_cell(ms, NULL); @@ -2080,10 +2040,9 @@ static int gsm322_search_end(struct osmocom_ms *ms) /* indicate available PLMN, include selected PLMN, if found */ msg_type = GSM322_EVENT_PLMN_AVAIL; - if (gsm322_is_plmn_avail_and_allow(cs, plmn->mcc, plmn->mnc)) { + if (gsm322_is_plmn_avail_and_allow(cs, &plmn322->plmn)) { /* set what PLMN becomes available */ - mcc = plmn->mcc; - mnc = plmn->mnc; + memcpy(&plmn, &plmn322->plmn, sizeof(struct osmo_plmn_id)); } new_c_state(cs, GSM322_C0_NULL); @@ -2096,7 +2055,7 @@ static int gsm322_search_end(struct osmocom_ms *ms) LOGP(DCS, LOGL_INFO, "PLMN search finished.\n"); /* create AA flag */ - gsm322_cs_select(ms, -1, 0, 0, 0); + gsm322_cs_select(ms, -1, NULL, 0); new_c_state(cs, GSM322_C0_NULL); @@ -2148,8 +2107,7 @@ static int gsm322_search_end(struct osmocom_ms *ms) if (!nmsg) return -ENOMEM; ngm = (struct gsm322_msg *) nmsg->data; - ngm->mcc = mcc; - ngm->mnc = mnc; + memcpy(&ngm->plmn, &plmn, sizeof(struct osmo_plmn_id)); gsm322_plmn_sendmsg(ms, nmsg); } @@ -2166,10 +2124,8 @@ static int gsm322_search_end(struct osmocom_ms *ms) memcpy(cs->list[cs->arfci].sysinfo, &cs->sel_si, sizeof(struct gsm48_sysinfo)); cs->si = cs->list[cs->arfci].sysinfo; - cs->sel_mcc = cs->si->mcc; - cs->sel_mnc = cs->si->mnc; - cs->sel_lac = cs->si->lac; - cs->sel_id = cs->si->cell_id; + cs->sel_cgi.lai = cs->si->lai; + cs->sel_cgi.cell_identity = cs->si->cell_id; LOGP(DCS, LOGL_INFO, "Tuning back to frequency %s after full " "search.\n", gsm_print_arfcn(cs->arfcn)); cs->sync_retries = SYNC_RETRIES; @@ -2317,17 +2273,17 @@ static int gsm322_cs_store(struct osmocom_ms *ms) cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_BARRED; /* store selected network */ - if (s->mcc) { - if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) + if (s->lai.plmn.mcc) { + if (gsm322_is_forbidden_la(ms, &s->lai)) cs->list[cs->arfci].flags |= GSM322_CS_FLAG_FORBIDD; else cs->list[cs->arfci].flags &= ~GSM322_CS_FLAG_FORBIDD; } - LOGP(DCS, LOGL_DEBUG, "Scan frequency %s: Cell found. (rxlev %s " - "mcc %s mnc %s lac %04x)\n", gsm_print_arfcn(cs->arfcn), + LOGP(DCS, LOGL_DEBUG, "Scan frequency %s: Cell found. (rxlev %s lai %s)\n", + gsm_print_arfcn(cs->arfcn), gsm_print_rxlev(cs->list[cs->arfci].rxlev), - gsm_print_mcc(s->mcc), gsm_print_mnc(s->mnc), s->lac); + osmo_lai_name(&s->lai)); /* selected PLMN (auto) becomes available during "any search" */ if (ms->settings.plmn_mode == PLMN_MODE_AUTO @@ -2336,10 +2292,10 @@ static int gsm322_cs_store(struct osmocom_ms *ms) || cs->state == GSM322_C8_ANY_CELL_RESEL || cs->state == GSM322_C9_CHOOSE_ANY_CELL) && plmn->state == GSM322_A4_WAIT_FOR_PLMN - && s->mcc == plmn->mcc && s->mnc == plmn->mnc) { + && (osmo_plmn_cmp(&s->lai.plmn, &plmn->plmn) == 0)) { LOGP(DCS, LOGL_INFO, "Candidate network to become available " "again\n"); - found = gsm322_cs_select(ms, cs->arfci, s->mcc, s->mnc, 0); + found = gsm322_cs_select(ms, cs->arfci, &s->lai.plmn, 0); if (found >= 0) { LOGP(DCS, LOGL_INFO, "Selected PLMN in \"A4_WAIT_F" "OR_PLMN\" state becomes available.\n"); @@ -2350,8 +2306,7 @@ indicate_plmn_avail: return -ENOMEM; /* set what PLMN becomes available */ ngm = (struct gsm322_msg *) nmsg->data; - ngm->mcc = plmn->mcc; - ngm->mnc = plmn->mcc; + memcpy(&ngm->plmn, &plmn->plmn, sizeof(struct osmo_plmn_id)); gsm322_plmn_sendmsg(ms, nmsg); new_c_state(cs, GSM322_C0_NULL); @@ -2367,10 +2322,10 @@ indicate_plmn_avail: || cs->state == GSM322_C8_ANY_CELL_RESEL || cs->state == GSM322_C9_CHOOSE_ANY_CELL) && plmn->state == GSM322_M3_NOT_ON_PLMN - && s->mcc == plmn->mcc && s->mnc == plmn->mnc) { + && (osmo_plmn_cmp(&s->lai.plmn, &plmn->plmn) == 0)) { LOGP(DCS, LOGL_INFO, "Candidate network to become available " "again\n"); - found = gsm322_cs_select(ms, cs->arfci, s->mcc, s->mnc, 0); + found = gsm322_cs_select(ms, cs->arfci, &s->lai.plmn, 0); if (found >= 0) { LOGP(DCS, LOGL_INFO, "Current selected PLMN in \"M3_N" "OT_ON_PLMN\" state becomes available.\n"); @@ -2410,9 +2365,9 @@ indicate_plmn_avail: if (cs->state == GSM322_C4_NORMAL_CELL_RESEL || cs->state == GSM322_C8_ANY_CELL_RESEL) - found = gsm322_cs_reselect(ms, cs->mcc, cs->mnc, any); + found = gsm322_cs_reselect(ms, &cs->plmn, any); else - found = gsm322_cs_select(ms, -1, cs->mcc, cs->mnc, any); + found = gsm322_cs_select(ms, -1, &cs->plmn, any); /* if not found */ if (found < 0) { @@ -2437,18 +2392,14 @@ indicate_plmn_avail: cs->selected = 1; cs->sel_arfcn = cs->arfcn; memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); - cs->sel_mcc = cs->si->mcc; - cs->sel_mnc = cs->si->mnc; - cs->sel_lac = cs->si->lac; - cs->sel_id = cs->si->cell_id; + cs->sel_cgi.lai = cs->si->lai; + cs->sel_cgi.cell_identity = cs->si->cell_id; if (ms->rrlayer.monitor) { - vty_notify(ms, "MON: %scell selected ARFCN=%s MCC=%s MNC=%s " - "LAC=0x%04x cellid=0x%04x (%s %s)\n", + l23_vty_ms_notify(ms, "MON: %scell selected ARFCN=%s CGI=%s (%s %s)\n", (any) ? "any " : "", gsm_print_arfcn(cs->sel_arfcn), - gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc), - cs->sel_lac, cs->sel_id, - gsm_get_mcc(cs->sel_mcc), - gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + osmo_cgi_name(&cs->sel_cgi), + gsm_get_mcc(cs->sel_cgi.lai.plmn.mcc), + gsm_get_mnc(&cs->sel_cgi.lai.plmn)); } /* tell CS process about available cell */ @@ -2468,7 +2419,8 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s; struct gsm322_ba_list *ba = NULL; - int i, refer_pcs; + int i; + bool refer_pcs; uint8_t freq[128+38]; if (!cs) { @@ -2484,13 +2436,12 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) /* collect system information received during dedicated mode */ if (s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) { /* find or create ba list */ - ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + ba = gsm322_find_ba_list(cs, &s->lai.plmn); if (!ba) { ba = talloc_zero(ms, struct gsm322_ba_list); if (!ba) return NULL; - ba->mcc = s->mcc; - ba->mnc = s->mnc; + memcpy(&ba->plmn, &s->lai.plmn, sizeof(struct osmo_plmn_id)); llist_add_tail(&ba->entry, &cs->ba_list); } /* update (add) ba list */ @@ -2506,10 +2457,10 @@ struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) } } if (!!memcmp(freq, ba->freq, sizeof(freq))) { - LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " - "%s, %s).\n", gsm_print_mcc(ba->mcc), - gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), - gsm_get_mnc(ba->mcc, ba->mnc)); + LOGP(DCS, LOGL_INFO, "New BA list (mcc-mnc=%s %s, %s).\n", + osmo_plmn_name(&ba->plmn), + gsm_get_mcc(ba->plmn.mcc), + gsm_get_mnc(&ba->plmn)); memcpy(ba->freq, freq, sizeof(freq)); } } @@ -2522,17 +2473,17 @@ static int gsm322_store_ba_list(struct gsm322_cellsel *cs, struct gsm48_sysinfo *s) { struct gsm322_ba_list *ba; - int i, refer_pcs; + int i; + bool refer_pcs; uint8_t freq[128+38]; /* find or create ba list */ - ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + ba = gsm322_find_ba_list(cs, &s->lai.plmn); if (!ba) { ba = talloc_zero(cs->ms, struct gsm322_ba_list); if (!ba) return -ENOMEM; - ba->mcc = s->mcc; - ba->mnc = s->mnc; + memcpy(&ba->plmn, &s->lai.plmn, sizeof(struct osmo_plmn_id)); llist_add_tail(&ba->entry, &cs->ba_list); } /* update ba list */ @@ -2549,10 +2500,10 @@ static int gsm322_store_ba_list(struct gsm322_cellsel *cs, } } if (!!memcmp(freq, ba->freq, sizeof(freq))) { - LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " - "%s, %s).\n", gsm_print_mcc(ba->mcc), - gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), - gsm_get_mnc(ba->mcc, ba->mnc)); + LOGP(DCS, LOGL_INFO, "New BA list (mcc-mnc=%s %s, %s).\n", + osmo_plmn_name(&ba->plmn), + gsm_get_mcc(ba->plmn.mcc), + gsm_get_mnc(&ba->plmn)); memcpy(ba->freq, freq, sizeof(freq)); } @@ -2586,8 +2537,8 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) * Depending on the extended bit in the channel description, * we require more or less system information about neighbor cells */ - if (s->mcc - && s->mnc + if (s->lai.plmn.mcc + && s->lai.plmn.mnc && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis @@ -2627,7 +2578,7 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) && cs->list[cs->arfci].sysinfo->sp_cbq)) { LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n"); if (ms->rrlayer.monitor) - vty_notify(ms, "MON: trigger cell re-selection" + l23_vty_ms_notify(ms, "MON: trigger cell re-selection" ": cell becomes barred\n"); trigger_resel: /* mark cell as unscanned */ @@ -2657,27 +2608,26 @@ static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) & (s->class_barr ^ 0xffff))) { LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n"); if (ms->rrlayer.monitor) - vty_notify(ms, "MON: trigger cell re-selection" + l23_vty_ms_notify(ms, "MON: trigger cell re-selection" ": access to cell becomes barred\n"); goto trigger_resel; } } /* check if MCC, MNC, LAC, cell ID changes */ - if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc - || cs->sel_lac != s->lac) { + if (osmo_lai_cmp(&cs->sel_cgi.lai, &s->lai) != 0) { LOGP(DCS, LOGL_NOTICE, "Cell changes location area. " "This is not good!\n"); if (ms->rrlayer.monitor) - vty_notify(ms, "MON: trigger cell re-selection: " + l23_vty_ms_notify(ms, "MON: trigger cell re-selection: " "cell changes LAI\n"); goto trigger_resel; } - if (cs->sel_id != s->cell_id) { + if (cs->sel_cgi.cell_identity != s->cell_id) { LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. " "This is not good!\n"); if (ms->rrlayer.monitor) - vty_notify(ms, "MON: trigger cell re-selection: " + l23_vty_ms_notify(ms, "MON: trigger cell re-selection: " "cell changes cell ID\n"); goto trigger_resel; } @@ -2702,8 +2652,8 @@ static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) * Depending on the extended bit in the channel description, * we require more or less system information about neighbor cells */ - if (s->mcc - && s->mnc + if (s->lai.plmn.mcc + && s->lai.plmn.mnc && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis @@ -3095,7 +3045,7 @@ static void gsm322_cs_loss(void *arg) LOGP(DCS, LOGL_INFO, "Loss of CCCH, Trigger " "re-selection.\n"); if (ms->rrlayer.monitor) - vty_notify(ms, "MON: trigger cell " + l23_vty_ms_notify(ms, "MON: trigger cell " "re-selection: loss of signal\n"); nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); @@ -3287,7 +3237,7 @@ static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) new_c_state(cs, GSM322_C6_ANY_CELL_SEL); - cs->mcc = cs->mnc = 0; + memset(&cs->plmn, 0, sizeof(cs->plmn)); /* unset selected cell */ gsm322_unselect_cell(cs); @@ -3389,11 +3339,10 @@ static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; - LOGP(DSUM, LOGL_INFO, "Camping normally on cell (ARFCN=%s mcc=%s " - "mnc=%s %s, %s)\n", gsm_print_arfcn(cs->sel_arfcn), - gsm_print_mcc(cs->sel_mcc), - gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), - gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + LOGP(DSUM, LOGL_INFO, "Camping normally on cell (ARFCN=%s mcc-mnc=%s %s, %s)\n", gsm_print_arfcn(cs->sel_arfcn), + osmo_plmn_name(&cs->sel_cgi.lai.plmn), + gsm_get_mcc(cs->sel_cgi.lai.plmn.mcc), + gsm_get_mnc(&cs->sel_cgi.lai.plmn)); /* if we did cell reselection, we have a valid last serving cell */ if (cs->state != GSM322_C4_NORMAL_CELL_RESEL) @@ -3416,11 +3365,11 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; - LOGP(DSUM, LOGL_INFO, "Camping on any cell (ARFCN=%s mcc=%s " - "mnc=%s %s, %s)\n", gsm_print_arfcn(cs->sel_arfcn), - gsm_print_mcc(cs->sel_mcc), - gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), - gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + LOGP(DSUM, LOGL_INFO, "Camping on any cell (ARFCN=%s mcc-mnc=%s %s, %s)\n", + gsm_print_arfcn(cs->sel_arfcn), + osmo_plmn_name(&cs->sel_cgi.lai.plmn), + gsm_get_mcc(cs->sel_cgi.lai.plmn.mcc), + gsm_get_mnc(&cs->sel_cgi.lai.plmn)); /* (re-)starting 'any cell selection' timer to look for coverage of * allowed PLMNs. @@ -3429,13 +3378,12 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) if (ms->subscr.sim_valid && (cs->state != GSM322_C8_ANY_CELL_RESEL || !osmo_timer_pending(&cs->any_timer))) { - struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_plmn *plmn322 = &ms->plmn; stop_any_timer(cs); if (ms->settings.plmn_mode == PLMN_MODE_MANUAL - && (!plmn->mcc - || gsm_subscr_is_forbidden_plmn(&ms->subscr, plmn->mcc, - plmn->mnc))) { + && (!plmn322->plmn.mcc + || gsm_subscr_is_forbidden_plmn(&ms->subscr, &plmn322->plmn))) { LOGP(DCS, LOGL_INFO, "Not starting 'any search' timer, " "because no selected PLMN or forbidden\n"); } else @@ -3462,8 +3410,7 @@ static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) } /* create temporary ba range with given frequency ranges */ -struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms, - uint32_t *range, uint8_t ranges, uint8_t refer_pcs) +static struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms, uint32_t *range, uint8_t ranges, bool refer_pcs) { static struct gsm322_ba_list ba; int lower, higher; @@ -3526,8 +3473,7 @@ static int gsm322_cs_choose(struct osmocom_ms *ms) if (!ba) { LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored " "BA list.\n"); - ba = gsm322_find_ba_list(cs, cs->sel_si.mcc, - cs->sel_si.mnc); + ba = gsm322_find_ba_list(cs, &cs->sel_si.lai.plmn); } } @@ -3628,23 +3574,22 @@ static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg) { struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; struct gsm322_cellsel *cs = &ms->cellsel; - struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_plmn *plmn322 = &ms->plmn; struct gsm322_ba_list *ba; - cs->mcc = plmn->mcc; - cs->mnc = plmn->mnc; + memcpy(&cs->plmn, &plmn322->plmn, sizeof(struct osmo_plmn_id)); if (gm->limited) { LOGP(DCS, LOGL_INFO, "Selected PLMN with limited service.\n"); return gsm322_c_any_cell_sel(ms, msg); } - LOGP(DSUM, LOGL_INFO, "Selecting PLMN (mcc=%s mnc=%s %s, %s)\n", - gsm_print_mcc(cs->mcc), gsm_print_mnc(cs->mnc), - gsm_get_mcc(cs->mcc), gsm_get_mnc(cs->mcc, cs->mnc)); + LOGP(DSUM, LOGL_INFO, "Selecting PLMN (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&cs->plmn), + gsm_get_mcc(cs->plmn.mcc), gsm_get_mnc(&cs->plmn)); /* search for BA list */ - ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc); + ba = gsm322_find_ba_list(cs, &plmn322->plmn); if (ba) { LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n"); @@ -3835,7 +3780,8 @@ static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg) && ((1 << plmn->state) & plmnastatelist[i].states)) break; if (i == PLMNASLLEN) { - LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n"); + LOGP(DPLMN, LOGL_NOTICE, "Event %s unhandled in state %s.\n", + get_event_name(msg_type), get_a_state_name(plmn->state)); return 0; } @@ -4154,13 +4100,13 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) } if (ms->rrlayer.monitor) { - vty_notify(ms, "MON: cell ARFCN LAC C1 C2 CRH RLA_C " + l23_vty_ms_notify(ms, "MON: cell ARFCN LAC C1 C2 CRH RLA_C " "bargraph\n"); snprintf(arfcn_text, 10, "%s ", gsm_print_arfcn(cs->sel_arfcn)); arfcn_text[9] = '\0'; - vty_notify(ms, "MON: serving %s 0x%04x %3d %3d %4d " - "%s\n", arfcn_text, cs->sel_lac, cs->c1, cs->c2, + l23_vty_ms_notify(ms, "MON: serving %s 0x%04x %3d %3d %4d " + "%s\n", arfcn_text, cs->sel_cgi.lai.lac, cs->c1, cs->c2, cs->rla_c_dbm, bargraph(cs->rla_c_dbm / 2, -55, -24)); } @@ -4181,7 +4127,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) snprintf(arfcn_text, 10, "%s ", gsm_print_arfcn(nb->arfcn)); arfcn_text[9] = '\0'; - vty_notify(ms, "MON: nb %2d %s ARFCN not " + l23_vty_ms_notify(ms, "MON: nb %2d %s ARFCN not " "supported\n", i + 1, arfcn_text); } goto cont; @@ -4194,7 +4140,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) snprintf(arfcn_text, 10, "%s ", gsm_print_arfcn(nb->arfcn)); arfcn_text[9] = '\0'; - vty_notify(ms, "MON: nb %2d %s " + l23_vty_ms_notify(ms, "MON: nb %2d %s " " %4d %s\n", i + 1, arfcn_text, nb->rla_c_dbm, bargraph(nb->rla_c_dbm / 2, -55, -24)); @@ -4207,7 +4153,10 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) nb->prio_low = 1; /* get C1 & C2 */ - gsm_arfcn2band_rc(nb->arfcn, &band); + if (gsm_arfcn2band_rc(nb->arfcn, &band) != 0) { + LOGP(DNB, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n"); + goto cont; + } class = class_of_band(ms, band); nb->c1 = calculate_c1(DNB, nb->rla_c_dbm, s->rxlev_acc_min_db, ms_pwr_dbm(band, s->ms_txpwr_max_cch), @@ -4220,8 +4169,7 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) nb->c12_valid = 1; /* calculate CRH depending on LAI */ - if (cs->sel_mcc == s->mcc && cs->sel_mnc == s->mnc - && cs->sel_lac == s->lac) { + if (osmo_lai_cmp(&cs->sel_cgi.lai, &s->lai) == 0) { LOGP(DNB, LOGL_INFO, "-> Cell of is in the same LA, " "so CRH = 0\n"); nb->crh = 0; @@ -4240,8 +4188,8 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) snprintf(arfcn_text, 10, "%s ", gsm_print_arfcn(nb->arfcn)); arfcn_text[9] = '\0'; - vty_notify(ms, "MON: nb %2d %s 0x%04x %3d %3d %2d" - " %4d %s\n", i + 1, arfcn_text, s->lac, + l23_vty_ms_notify(ms, "MON: nb %2d %s 0x%04x %3d %3d %2d" + " %4d %s\n", i + 1, arfcn_text, s->lai.lac, nb->c1, nb->c2, nb->crh, nb->rla_c_dbm, bargraph(nb->rla_c_dbm / 2, -55, -24)); } @@ -4262,18 +4210,17 @@ static int gsm322_nb_check(struct osmocom_ms *ms, int any) } /* check if LA is forbidden */ - if (any && gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) { + if (any && gsm322_is_forbidden_la(ms, &s->lai)) { LOGP(DNB, LOGL_INFO, "Skip cell: Cell has " "forbidden LA.\n"); goto cont; } /* check if we have same PLMN */ - if (!any && (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc)) { + if (!any && (osmo_plmn_cmp(&cs->sel_cgi.lai.plmn, &s->lai.plmn) != 0)) { LOGP(DNB, LOGL_INFO, "Skip cell: PLMN of cell " - "does not match target PLMN. (cell: mcc=%s " - "mnc=%s)\n", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc)); + "does not match target PLMN. (cell: mcc-mnc=%s)\n", + osmo_plmn_name(&s->lai.plmn)); goto cont; } @@ -4316,7 +4263,7 @@ cont: if (!i) { if (ms->rrlayer.monitor) - vty_notify(ms, "MON: no neighbour cells\n"); + l23_vty_ms_notify(ms, "MON: no neighbour cells\n"); } if (cs->resel_when + GSM58_RESEL_THRESHOLD >= now) { @@ -4457,7 +4404,8 @@ static int gsm322_nb_start(struct osmocom_ms *ms, int synced) uint8_t map[128]; uint16_t nc[32]; uint8_t changed = 0; - int refer_pcs, index; + bool refer_pcs; + int index; uint16_t arfcn; if (cs->ms->settings.no_neighbour) @@ -4625,7 +4573,7 @@ printf("%d time to sync again: %u\n", nb->arfcn, now + GSM58_READ_AGAIN - nb->wh "reselection.\n"); if (ms->rrlayer.monitor) - vty_notify(ms, "MON: trigger cell re-selection: " + l23_vty_ms_notify(ms, "MON: trigger cell re-selection: " "better cell\n"); cs->resel_when = now; @@ -4707,7 +4655,11 @@ static int gsm322_nb_new_rxlev(struct gsm322_cellsel *cs) enum gsm_band band; int class; - gsm_arfcn2band_rc(cs->arfcn, &band); + if (gsm_arfcn2band_rc(cs->arfcn, &band) != 0) { + LOGP(DNB, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n"); + return -EINVAL; + } + class = class_of_band(cs->ms, band); /* calculate the RAL_C of serving cell */ @@ -4853,9 +4805,9 @@ int gsm322_dump_sorted_plmn(struct osmocom_ms *ms) LOGP(DPLMN, LOGL_INFO, "MCC |MNC |allowed|rx-lev\n"); LOGP(DPLMN, LOGL_INFO, "-------+-------+-------+-------\n"); llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { - LOGP(DPLMN, LOGL_INFO, "%s |%s%s |%s |%s\n", - gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), - ((temp->mnc & 0x00f) == 0x00f) ? " ":"", + LOGP(DPLMN, LOGL_INFO, "%s |%-3s |%s |%s\n", + osmo_mcc_name(temp->plmn.mcc), + osmo_mnc_name(temp->plmn.mnc, temp->plmn.mnc_3_digits), (temp->cause) ? "no ":"yes", gsm_print_rxlev(temp->rxlev)); } @@ -4883,11 +4835,11 @@ int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, print(priv, "%4dDCS|", i); else print(priv, "%4d |", i); - if (s->mcc) { - print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), - ((s->mnc & 0x00f) == 0x00f) ? " ":""); - print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id); + if (s->lai.plmn.mcc) { + print(priv, "%s |%-3s |", + osmo_mcc_name(s->lai.plmn.mcc), + osmo_mnc_name(s->lai.plmn.mnc, s->lai.plmn.mnc_3_digits)); + print(priv, "0x%04x |0x%04x |", s->lai.lac, s->cell_id); } else print(priv, "n/a |n/a |n/a |n/a |"); if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) { @@ -4926,27 +4878,27 @@ int gsm322_dump_forbidden_la(struct osmocom_ms *ms, print(priv, "MCC |MNC |LAC |cause\n"); print(priv, "-------+-------+-------+-------\n"); llist_for_each_entry(temp, &plmn->forbidden_la, entry) - print(priv, "%s |%s%s |0x%04x |#%d\n", - gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), - ((temp->mnc & 0x00f) == 0x00f) ? " ":"", - temp->lac, temp->cause); + print(priv, "%s |%-3s |0x%04x |#%d\n", + osmo_mcc_name(temp->lai.plmn.mcc), + osmo_mnc_name(temp->lai.plmn.mnc, temp->lai.plmn.mnc_3_digits), + temp->lai.lac, temp->cause); return 0; } -int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, const struct osmo_plmn_id *plmn, void (*print)(void *, const char *, ...), void *priv) { struct gsm322_ba_list *ba; int i; llist_for_each_entry(ba, &cs->ba_list, entry) { - if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc)) + if (plmn && (osmo_plmn_cmp(&ba->plmn, plmn) != 0)) continue; - print(priv, "Band Allocation of network: MCC %s MNC %s " - "(%s, %s)\n", gsm_print_mcc(ba->mcc), - gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), - gsm_get_mnc(ba->mcc, ba->mnc)); + print(priv, "Band Allocation of network: MCC-MNC %s (%s, %s)\n", + osmo_plmn_name(&ba->plmn), + gsm_get_mcc(ba->plmn.mcc), + gsm_get_mnc(&ba->plmn)); for (i = 0; i <= 1023+299; i++) { if ((ba->freq[i >> 3] & (1 << (i & 7)))) print(priv, " %s", @@ -4976,7 +4928,7 @@ int gsm322_dump_nb_list(struct gsm322_cellsel *cs, print(priv, "C1=%d C2=%d ", cs->c1, cs->c1); else print(priv, "C1 - C2 - "); - print(priv, "LAC=0x%04x\n\n", (cs->selected) ? cs->sel_si.lac : 0); + print(priv, "LAC=0x%04x\n\n", (cs->selected) ? cs->sel_si.lai.lac : 0); print(priv, "Neighbour cells:\n\n"); llist_for_each_entry(nb, &cs->nb_list, entry) { @@ -5019,7 +4971,7 @@ int gsm322_dump_nb_list(struct gsm322_cellsel *cs, s = cs->list[arfcn2index(nb->arfcn)].sysinfo; if (nb->state == GSM322_NB_SYSINFO && s) { print(priv, "%s |0x%04x |0x%04x |", - (nb->prio_low) ? "low ":"normal", s->lac, + (nb->prio_low) ? "low ":"normal", s->lai.lac, s->cell_id); } else print(priv, "- |- |- |"); @@ -5090,6 +5042,7 @@ int gsm322_init(struct osmocom_ms *ms) "stored BA list becomes obsolete.\n"); } else while(!feof(fp)) { + uint16_t mcc_hex, mnc_hex; ba = talloc_zero(ms, struct gsm322_ba_list); if (!ba) { fclose(fp); @@ -5100,18 +5053,30 @@ int gsm322_init(struct osmocom_ms *ms) talloc_free(ba); break; } - ba->mcc = (buf[0] << 8) | buf[1]; - ba->mnc = (buf[2] << 8) | buf[3]; + mcc_hex = (buf[0] << 8) | buf[1]; + mnc_hex = (buf[2] << 8) | buf[3]; + ba->plmn.mcc = (((mcc_hex & 0x0f00) >> 8) * 100) + + (((mcc_hex & 0x00f0) >> 4) * 10) + + (mcc_hex & 0x000f); + ba->plmn.mnc_3_digits = ((mnc_hex & 0x00f) != 0x00f); + if (ba->plmn.mnc_3_digits) + ba->plmn.mnc = (((mnc_hex & 0x0f00) >> 8) * 100) + + (((mnc_hex & 0x00f0) >> 4) * 10) + + (mnc_hex & 0x000f); + else + ba->plmn.mnc = (((mnc_hex & 0x0f00) >> 8) * 10) + + (((mnc_hex & 0x00f0) >> 4)); + rc = fread(ba->freq, sizeof(ba->freq), 1, fp); if (!rc) { talloc_free(ba); break; } llist_add_tail(&ba->entry, &cs->ba_list); - LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s " - "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), - gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), - gsm_get_mnc(ba->mcc, ba->mnc)); + LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&ba->plmn), + gsm_get_mcc(ba->plmn.mcc), + gsm_get_mnc(&ba->plmn)); } fclose(fp); } else @@ -5120,17 +5085,63 @@ int gsm322_init(struct osmocom_ms *ms) return 0; } +static void gsm322_write_ba(struct osmocom_ms *ms) +{ + const struct gsm322_ba_list *ba; + char *ba_filename; + FILE *fp; + + ba_filename = talloc_asprintf(ms, "%s/%s.ba", config_dir, ms->name); + OSMO_ASSERT(ba_filename != NULL); + + LOGP(DCS, LOGL_INFO, "Writing stored BA list to '%s'\n", ba_filename); + + fp = fopen(ba_filename, "w"); + talloc_free(ba_filename); + if (fp == NULL) { + LOGP(DCS, LOGL_ERROR, + "Failed to open '%s' for writing: %s\n", + ba_filename, strerror(errno)); + return; + } + + fputs(ba_version, fp); + + llist_for_each_entry(ba, &ms->cellsel.ba_list, entry) { + size_t rc = 0; + uint16_t mcc_hex = gsm_mcc_to_hex(ba->plmn.mcc); + uint16_t mnc_hex = gsm_mnc_to_hex(ba->plmn.mnc, ba->plmn.mnc_3_digits); + uint8_t buf[] = { + mcc_hex >> 8, mcc_hex & 0xff, + mnc_hex >> 8, mnc_hex & 0xff, + }; + + LOGP(DCS, LOGL_INFO, + "Writing stored BA list entry (mcc-mnc=%s %s, %s)\n", + osmo_plmn_name(&ba->plmn), + gsm_get_mcc(ba->plmn.mcc), + gsm_get_mnc(&ba->plmn)); + + rc += fwrite(buf, sizeof(buf), 1, fp); + rc += fwrite(ba->freq, sizeof(ba->freq), 1, fp); + + /* fwrite() returns count of written items, should be 2 */ + if (rc != 2) { + LOGP(DCS, LOGL_ERROR, + "Writing stored BA list: fwrite() failed (rc=%zu)\n", rc); + break; + } + } + + fclose(fp); +} + int gsm322_exit(struct osmocom_ms *ms) { struct gsm322_plmn *plmn = &ms->plmn; struct gsm322_cellsel *cs = &ms->cellsel; struct llist_head *lh, *lh2; struct msgb *msg; - FILE *fp; - char *ba_filename; - struct gsm322_ba_list *ba; - uint8_t buf[4]; - int rc = 0; int i; LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n"); @@ -5158,32 +5169,7 @@ int gsm322_exit(struct osmocom_ms *ms) cs->si = NULL; /* store BA list */ - ba_filename = talloc_asprintf(ms, "%s/%s.ba", config_dir, ms->name); - if (ba_filename) { - fp = fopen(ba_filename, "w"); - talloc_free(ba_filename); - if (fp) { - fputs(ba_version, fp); - llist_for_each_entry(ba, &cs->ba_list, entry) { - buf[0] = ba->mcc >> 8; - buf[1] = ba->mcc & 0xff; - buf[2] = ba->mnc >> 8; - buf[3] = ba->mnc & 0xff; - - LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s " - "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), - gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), - gsm_get_mnc(ba->mcc, ba->mnc)); - - rc += fwrite(buf, 4, 1, fp); - rc += fwrite(ba->freq, sizeof(ba->freq), 1, fp); - } - fclose(fp); - } - } - - if (rc != 2) - LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n"); + gsm322_write_ba(ms); /* free lists */ while ((msg = msgb_dequeue(&plmn->event_queue))) diff --git a/src/host/layer23/src/mobile/gsm411_sms.c b/src/host/layer23/src/mobile/gsm411_sms.c index 593a2ad3..a21133b9 100644 --- a/src/host/layer23/src/mobile/gsm411_sms.c +++ b/src/host/layer23/src/mobile/gsm411_sms.c @@ -19,10 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -34,9 +30,11 @@ #include <osmocom/core/msgb.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> #include <osmocom/bb/mobile/gsm411_sms.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/gsm/gsm0411_utils.h> #include <osmocom/core/talloc.h> #include <osmocom/bb/mobile/vty.h> @@ -123,11 +121,11 @@ struct gsm_sms *sms_from_text(const char *receiver, int dcs, const char *text) static int gsm411_sms_report(struct osmocom_ms *ms, struct gsm_sms *sms, uint8_t cause) { - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); if (!cause) - vty_notify(ms, "SMS to %s successful\n", sms->address); + l23_vty_ms_notify(ms, "SMS to %s successful\n", sms->address); else - vty_notify(ms, "SMS to %s failed: %s\n", sms->address, + l23_vty_ms_notify(ms, "SMS to %s failed: %s\n", sms->address, get_value_string(gsm411_rp_cause_strs, cause)); mobile_prim_ntfy_sms_status(ms, sms, cause); @@ -193,8 +191,8 @@ static int sms_store(struct osmocom_ms *ms, struct msgb *msg, if (*p == '\n' || *p == '\r') *p = ' '; } - vty_notify(ms, NULL); - vty_notify(ms, "SMS from %s: '%s'\n", gsms->address, vty_text); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "SMS from %s: '%s'\n", gsms->address, vty_text); home = getenv("HOME"); if (!home) { @@ -654,6 +652,14 @@ int gsm411_tx_sms_submit(struct osmocom_ms *ms, const char *sms_sca, return -EIO; } + /* ASCI call does not allow other transactions */ + if (trans_find_ongoing_gcc_bcc(ms)) { + LOGP(DLSMS, LOGL_ERROR, "Phone is busy doing ASCI call\n"); + gsm411_sms_report(ms, sms, GSM411_RP_CAUSE_MO_TEMP_FAIL); + sms_free(sms); + return -EIO; + } + /* allocate transaction with dummy reference */ transaction_id = trans_assign_trans_id(ms, GSM48_PDISC_SMS, 0); if (transaction_id < 0) { @@ -917,7 +923,7 @@ int gsm411_rcv_sms(struct osmocom_ms *ms, struct msgb *msg) struct gsm_trans *trans; int rc = 0; - trans = trans_find_by_callref(ms, mmh->ref); + trans = trans_find_by_callref(ms, GSM48_PDISC_SMS, mmh->ref); if (!trans) { LOGP(DLSMS, LOGL_INFO, " -> (new transaction sapi=%d)\n", sapi); trans = trans_alloc(ms, GSM48_PDISC_SMS, mmh->transaction_id, diff --git a/src/host/layer23/src/mobile/gsm414.c b/src/host/layer23/src/mobile/gsm414.c index 2f630df3..90fc2dfd 100644 --- a/src/host/layer23/src/mobile/gsm414.c +++ b/src/host/layer23/src/mobile/gsm414.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -37,7 +33,9 @@ #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/logging.h> +#include <osmocom/bb/mobile/gsm48_rr.h> #include <l1ctl_proto.h> @@ -139,7 +137,7 @@ static int handle_close_tch_loop(struct osmocom_ms *ms, const struct msgb *msg) /* Instruct the L1 to enable received TCH loopback mode * FIXME: delay applying this mode, so we can send the ACK first */ - l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, rr->tch_loop_mode); + l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, rr->cd_now.tch_flags, rr->tch_loop_mode); /* Craft and send the ACKnowledgement */ nmsg = alloc_gsm414_msg(GSM414_MT_CLOSE_TCH_LOOP_ACK); @@ -180,7 +178,7 @@ static int handle_open_tch_loop(struct osmocom_ms *ms, const struct msgb *msg) rsl_chan_nr_str(rr->cd_now.chan_nr), loop_mode_name(rr->tch_loop_mode)); /* Instruct the L1 to disable the TCH loopback mode */ - l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, L1CTL_TCH_LOOP_OPEN); + l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, rr->audio_mode, rr->cd_now.tch_flags, L1CTL_TCH_LOOP_OPEN); /* Only the loop mode C needs to be ACKnowledged */ bool needs_ack = rr->tch_loop_mode == L1CTL_TCH_LOOP_C; diff --git a/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c b/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c new file mode 100644 index 00000000..3a4db3a6 --- /dev/null +++ b/src/host/layer23/src/mobile/gsm44068_gcc_bcc.c @@ -0,0 +1,1967 @@ +/* Handle VGCS/VBCS calls. (Voice Group/Broadcast Call Service). */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * SPDX-License-Identifier: AGPL-3.0+ + * + * Author: Andreas Eversberg + * + * 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 <http://www.gnu.org/licenses/>. + */ + +/* Notes on the state machine: + * + * The state machine is different from the diagram depicted in the specs. + * This is because there are some messages missing and some state transitions + * are different or not shown. + * + * A call that has no channel is answered without joining the group channel. + * If it comes available, the establishment is performed and the U4 is entered. + * + * Uplink control is not described in the diagram. Talking/listening is + * requested by user and can be rejected by MM layer, if talking is not + * allowed. + * + * We can be sure that there is no other MM connection while doing VGCS call + * establishment: MMxx-EST-REQ is only accpepted, if there is no MM connection. + * We block calls from user, if there is some other transaction, which is not + * in state U3. Also we accept incoming indications any time and create + * transactions that go to state U3. + * + * If the upper layer or lower layer requests another call/SMS/SS while VGCS + * call is ongoing, this may cause undefined behaviour. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_44_068.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/fsm.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> +#include <osmocom/bb/mobile/tch.h> +#include <osmocom/bb/mobile/vty.h> +#include <l1ctl_proto.h> + +#define S(x) (1 << (x)) + +#define LOG_GCC(trans, level, fmt, args...) \ + LOGP(((trans)->protocol == GSM48_PDISC_GROUP_CC) ? DGCC : DBCC, level, \ + ((trans)->protocol == GSM48_PDISC_GROUP_CC) ? ("VGCS callref %u: " fmt) : ("VBS callref %u: " fmt), \ + (trans)->callref, ##args) +#define LOG_GCC_PR(protocol, ref, level, fmt, args...) \ + LOGP((protocol == GSM48_PDISC_GROUP_CC) ? DGCC : DBCC, level, \ + (protocol == GSM48_PDISC_GROUP_CC) ? ("VGCS callref %u: " fmt) : ("VBS callref %u: " fmt), \ + ref, ##args) + +/* + * init + */ + +int gsm44068_gcc_init(struct osmocom_ms *ms) +{ + LOGP(DGCC, LOGL_INFO, "init GCC/BCC\n"); + + return 0; +} + +int gsm44068_gcc_exit(struct osmocom_ms *ms) +{ + struct gsm_trans *trans, *trans2; + + LOGP(DGCC, LOGL_INFO, "exit GCC/BCC processes for %s\n", ms->name); + + llist_for_each_entry_safe(trans, trans2, &ms->trans_list, entry) { + if (trans->protocol == GSM48_PDISC_GROUP_CC || trans->protocol == GSM48_PDISC_BCAST_CC) { + LOG_GCC(trans, LOGL_NOTICE, "Free pendig CC-transaction.\n"); + trans_free(trans); + } + } + + return 0; +} + + +/* + * messages + */ + +/* TS 44.068 Chapter 6.1.2.1 */ +enum vgcs_gcc_fsm_states { + VGCS_GCC_ST_U0_NULL = 0, + VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING, + VGCS_GCC_ST_U1_GROUP_CALL_INITIATED, + VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, /* sepeate link */ + VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, /* wait for receive mode */ + VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, /* receive mode / U6 @ BCC */ + VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE, /* wait for send and receive mode */ + VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE, /* send and receive mode */ + VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, /* no channel */ + VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, + VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, + VGCS_GCC_ST_U5_TERMINATION_REQUESTED, +}; + +/* TS 44.068 Figure 6.1 (additional events added) */ +enum vgcs_gcc_fsm_event { + VGCS_GCC_EV_SETUP_REQ, /* calling user initiates call */ + VGCS_GCC_EV_TERM_REQ, /* calling user requests termination */ + VGCS_GCC_EV_MM_EST_CNF, /* MM connection established */ + VGCS_GCC_EV_MM_EST_REJ, /* MM connection failed */ + VGCS_GCC_EV_DI_TERMINATION, /* network acknowledges termination */ + VGCS_GCC_EV_DI_TERM_REJECT, /* network rejects termination */ + VGCS_GCC_EV_DI_CONNECT, /* network indicates connect */ + VGCS_GCC_EV_TIMEOUT, /* several timeout events */ + VGCS_GCC_EV_SETUP_IND, /* notification of ongoing call received */ + VGCS_GCC_EV_REL_IND, /* notification of call being gone */ + VGCS_GCC_EV_JOIN_GC_REQ, /* user wants to join ongoing call */ + VGCS_GCC_EV_JOIN_GC_CNF, /* MM confirms joining ongoing call */ + VGCS_GCC_EV_ABORT_REQ, /* user rejects or leaves call */ + VGCS_GCC_EV_ABORT_IND, /* MM indicates channel released or failed */ + VGCS_GCC_EV_TALK_REQ, /* user wants to talk */ + VGCS_GCC_EV_TALK_CNF, /* MM confirms talk */ + VGCS_GCC_EV_TALK_REJ, /* MM rejects talk */ + VGCS_GCC_EV_LISTEN_REQ, /* user wants to listen */ + VGCS_GCC_EV_LISTEN_CNF, /* MM confirms listen */ + VGCS_GCC_EV_MM_IDLE, /* MM layer becomes ready for new channel */ + VGCS_GCC_EV_UPLINK_FREE, /* MM layer indicates free uplink in group receive mode */ + VGCS_GCC_EV_UPLINK_BUSY, /* MM layer indicates busy uplink in group receive mode */ +}; + +static const struct value_string vgcs_gcc_fsm_event_names[] = { + OSMO_VALUE_STRING(VGCS_GCC_EV_SETUP_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_TERM_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_MM_EST_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_MM_EST_REJ), + OSMO_VALUE_STRING(VGCS_GCC_EV_DI_TERMINATION), + OSMO_VALUE_STRING(VGCS_GCC_EV_DI_TERM_REJECT), + OSMO_VALUE_STRING(VGCS_GCC_EV_DI_CONNECT), + OSMO_VALUE_STRING(VGCS_GCC_EV_TIMEOUT), + OSMO_VALUE_STRING(VGCS_GCC_EV_SETUP_IND), + OSMO_VALUE_STRING(VGCS_GCC_EV_REL_IND), + OSMO_VALUE_STRING(VGCS_GCC_EV_JOIN_GC_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_JOIN_GC_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_ABORT_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_ABORT_IND), + OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_TALK_REJ), + OSMO_VALUE_STRING(VGCS_GCC_EV_LISTEN_REQ), + OSMO_VALUE_STRING(VGCS_GCC_EV_LISTEN_CNF), + OSMO_VALUE_STRING(VGCS_GCC_EV_MM_IDLE), + OSMO_VALUE_STRING(VGCS_GCC_EV_UPLINK_FREE), + OSMO_VALUE_STRING(VGCS_GCC_EV_UPLINK_BUSY), + { } +}; + +/*! return string representation of GCC/BCC Message Type */ +static const char *gsm44068_gcc_msg_name(uint8_t msg_type) +{ + return get_value_string(osmo_gsm44068_msg_type_names, msg_type); +} + +#define TFU(param) ((param < 0) ? "unchanged" : ((param) ? "T" : "F")) + +/* Set state attributes and check if they are consistent with the current state. */ +static int set_state_attributes(struct gsm_trans *trans, int d_att, int u_att, int comm, int orig, int call_state) +{ + bool orig_t = false, comm_t = false; + + LOG_GCC(trans, LOGL_DEBUG, "Setting state attributes: D-ATT = %s, U-ATT = %s, COMM = %s, ORIG = %s.\n", + TFU(d_att), TFU(u_att), TFU(comm), TFU(orig)); + + /* Control Speaker. */ + if (d_att >= 0 && trans->gcc.d_att != d_att) { + LOG_GCC(trans, LOGL_DEBUG, "Switching Speaker to %d\n", d_att); + gsm48_rr_audio_mode(trans->ms, AUDIO_TX_MICROPHONE | (d_att * AUDIO_RX_SPEAKER)); + } + + if (d_att >= 0) + trans->gcc.d_att = d_att; + if (u_att >= 0) + trans->gcc.u_att = u_att; + if (comm >= 0) + trans->gcc.comm = comm; + if (orig >= 0) + trans->gcc.orig = orig; + if (call_state >= 0) + trans->gcc.call_state = call_state; + + switch (trans->gcc.fi->state) { + case VGCS_GCC_ST_U3_GROUP_CALL_PRESENT: + case VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST: + orig_t = orig; + comm_t = comm; + break; + case VGCS_GCC_ST_U0_NULL: + case VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE: + case VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE: + comm_t = comm; + break; + } + + if (orig_t) + LOG_GCC(trans, LOGL_ERROR, "ORIG = T is inconsistent with states U3 and U4. Please fix!"); + + if (comm_t) + LOG_GCC(trans, LOGL_ERROR, + "COMM = T is inconsistent with states U0, U3, U4, U2nc and U2r. Please fix!"); + + return (orig_t || comm_t) ? -EINVAL : 0; +} + +static void vgcs_vty_notify(struct gsm_trans *trans, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +static void vgcs_vty_notify(struct gsm_trans *trans, const char *fmt, ...) +{ + struct osmocom_ms *ms = trans->ms; + char buffer[1000]; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "%s call %d: %s", (trans->protocol == GSM48_PDISC_GROUP_CC) ? "Group" : "Broadcast", + trans->callref, buffer); +} + +/* + * messages + */ + +/* Send MMxx-GROUP-REQ to MM. */ +static int vgcs_group_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_GROUP_REQ : GSM48_MMBCC_GROUP_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + OSMO_ASSERT(trans->gcc.ch_desc_present); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *) nmsg->data; + nmmh->ch_desc_present = trans->gcc.ch_desc_present; + memcpy(&nmmh->ch_desc, &trans->gcc.ch_desc, sizeof(nmmh->ch_desc)); + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Send MMxx-EST-REQ to MM. */ +static int vgcs_est_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_EST_REQ : GSM48_MMBCC_EST_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Push message and send MMxx-DATA-REQ to MM. */ +static int vgcs_data_req(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_DATA_REQ : GSM48_MMBCC_DATA_REQ; + + /* push RR header */ + msg->l3h = msg->data; + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = msg_type; + mmh->ref = trans->callref; + mmh->transaction_id = trans->transaction_id; + mmh->sapi = 0; + + /* send message to MM */ + return gsm48_mmxx_downmsg(trans->ms, msg); +} + +/* Send MMxx-REL-REQ to MM. */ +static int vgcs_rel_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_REL_REQ : GSM48_MMBCC_REL_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Send MMxx-UPLINK-REQ to MM. */ +static int vgcs_uplink_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_UPLINK_REQ : GSM48_MMBCC_UPLINK_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* Send MMxx-UPLINK-REL-REQ to MM. */ +static int vgcs_uplink_rel_req(struct gsm_trans *trans) +{ + struct msgb *nmsg; + uint16_t msg_type = (trans->protocol == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_UPLINK_REL_REQ + : GSM48_MMBCC_UPLINK_REL_REQ; + + LOG_GCC(trans, LOGL_INFO, "Sending %s.\n", get_mmxx_name(msg_type)); + + nmsg = gsm48_mmxx_msgb_alloc(msg_type, trans->callref, trans->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +static void _add_callref_ie(struct msgb *msg, uint32_t callref, bool with_prio, uint8_t prio) +{ + uint32_t ie; + + ie = callref << 5; + if (with_prio) + ie |= 0x10 | (prio << 1); + msgb_put_u32(msg, ie); +} + +static void _add_user_user_ie(struct msgb *msg, uint8_t user_pdisc, uint8_t *user, uint8_t user_len) +{ + uint8_t *ie; + + ie = msgb_put(msg, user_len + 2); + *ie++ = GSM48_IE_USER_USER; + *ie++ = user_len; + memcpy(ie, user, user_len); +} + +#define GSM44068_IE_CALL_STATE 0xA0 +#define GSM44068_IE_STATE_ATTRS 0xB0 +#define GSM44068_IE_TALKER_PRIO 0xc0 + +static void _add_call_state_ie(struct msgb *msg, uint8_t call_state) +{ + msgb_put_u8(msg, GSM44068_IE_CALL_STATE | call_state); +} + +static void _add_state_attrs_ie(struct msgb *msg, uint8_t da, uint8_t ua, uint8_t comm, uint8_t oi) +{ + msgb_put_u8(msg, GSM44068_IE_STATE_ATTRS | (da << 3) | (ua << 2) | (comm << 1) | oi); +} + +static void _add_talker_prio_ie(struct msgb *msg, uint8_t talker_prio) +{ + msgb_put_u8(msg, GSM44068_IE_TALKER_PRIO | talker_prio); +} + +static void _add_cause_ie(struct msgb *msg, uint8_t cause, uint8_t *diag, uint8_t diag_len) +{ + uint8_t *ie; + + ie = msgb_put(msg, diag_len + 2); + *ie++ = diag_len + 1; + *ie++ = 0x80 | cause; + if (diag_len && diag) + memcpy(ie, diag, diag_len); +} + +static int _msg_too_short(struct gsm_trans *trans) +{ + LOG_GCC(trans, LOGL_ERROR, "MSG too short.\n"); + return -EINVAL; +} + +static int _ie_invalid(struct gsm_trans *trans) +{ + LOG_GCC(trans, LOGL_ERROR, "IE invalid.\n"); + return -EINVAL; +} + +/* 3GPP TS 44.068 Clause 8.4 */ +static int gsm44068_rx_set_parameter(struct gsm_trans *trans, struct msgb *msg, + uint8_t *da, uint8_t *ua, uint8_t *comm, uint8_t *oi) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int remaining_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t *ie = gh->data; + + /* State attributes */ + if (remaining_len < 1) + return _msg_too_short(trans); + if (da) + *da = (ie[0] >> 3) & 0x1; + if (ua) + *ua = (ie[0] >> 2) & 0x1; + if (comm) + *comm = (ie[0] >> 1) & 0x1; + if (oi) + *oi = ie[0] & 0x1; + ie += 1; + + return 0; +} + +/* 3GPP TS 44.068 Clause 8.5 */ +static int gsm44068_tx_setup(struct gsm_trans *trans, uint32_t callref, bool with_prio, uint8_t prio, + uint8_t user_pdisc, uint8_t *user, uint8_t user_len, + bool with_talker_prio, uint8_t talker_prio) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX SETUP"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + LOG_GCC(trans, LOGL_INFO, "Sending SETUP.\n"); + + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + gh->msg_type = OSMO_GSM44068_MSGT_SETUP; + _add_callref_ie(msg, callref, with_prio, prio); + if (user_len && user) + _add_user_user_ie(msg, user_pdisc, user, user_len); + if (with_talker_prio) + _add_talker_prio_ie(msg, talker_prio); + + return vgcs_data_req(trans, msg); +} + +/* 3GPP TS 44.068 Clause 8.6 */ +static int gsm44068_tx_status(struct gsm_trans *trans, uint8_t cause, uint8_t *diag, uint8_t diag_len, + bool with_call_state, uint8_t call_state, bool with_state_attrs, + uint8_t da, uint8_t ua, uint8_t comm, uint8_t oi) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX STATUS"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + LOG_GCC(trans, LOGL_INFO, "Sending STATUS.\n"); + + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + gh->msg_type = OSMO_GSM44068_MSGT_STATUS; + _add_cause_ie(msg, cause, diag, diag_len); + if (with_call_state) + _add_call_state_ie(msg, call_state); + if (with_state_attrs) + _add_state_attrs_ie(msg, da, ua, comm, oi); + + return vgcs_data_req(trans, msg); +} + +/* 3GPP TS 44.068 Clause 8.7 and 8.8 */ +static int gsm44068_rx_termination(struct gsm_trans *trans, struct msgb *msg, uint8_t *cause, uint8_t *diag, uint8_t *diag_len) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int remaining_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t *ie = gh->data; + uint8_t ie_len; + + /* Cause */ + if (remaining_len < 2 || ie[0] < remaining_len - 2) + return _msg_too_short(trans); + ie_len = ie[0]; + if (remaining_len < ie_len + 1) + return _msg_too_short(trans); + if (ie_len < 1) + return _ie_invalid(trans); + if (cause) + *cause = ie[1] & 0x7f; + if (diag && diag_len) { + *diag_len = ie_len - 1; + if (*diag_len) + memcpy(diag, ie + 2, ie_len - 1); + } + remaining_len -= ie_len + 1; + ie += ie_len + 1; + + return 0; +} + +/* 3GPP TS 44.068 Clause 8.9 */ +static int gsm44068_tx_termination_request(struct gsm_trans *trans, uint32_t callref, bool with_prio, uint8_t prio, + bool with_talker_prio, uint8_t talker_prio) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX TERMINATION REQUEST"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + LOG_GCC(trans, LOGL_INFO, "Sending TERMINATION REQUEST.\n"); + + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + gh->msg_type = OSMO_GSM44068_MSGT_TERMINATION_REQUEST; + _add_callref_ie(msg, callref, with_prio, prio); + if (with_talker_prio) + _add_talker_prio_ie(msg, talker_prio); + + return vgcs_data_req(trans, msg); +} + +/* 3GPP TS 44.018 Clause 9.1.48 */ +static int gsm44068_tx_uplink_release(struct gsm_trans *trans, uint8_t cause) +{ + struct msgb *msg = gsm44068_msgb_alloc_name("GSM 44.068 TX UPLINK RELEASE"); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *) msgb_put(msg, sizeof(*ur)); + + LOG_GCC(trans, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE; + ur->rr_cause = cause; + + return vgcs_data_req(trans, msg); +} + +/* + * GCC/BCC state machine + * + * For reference see Figure 6.1 of TS 44.068. + * + * Note: There are some events that are not depicted in the state diagram: + * + * "L: ABORT-IND" indicates closing/failing of radio channel. + * "H: TALK-REQ" request talking on the channel. + * "L: TALK-CNF" confirms talker. + * "L: TALK-REJ" rejects talker. + * "H: LISTEN-REQ" request listening. + * "L: LISTEN-CNF" confirms listening. + * + */ + +/* Table 6.1 of TS 44.068 */ +#define T_NO_CHANNEL 3 +#define T_MM_EST 7 +#define T_TERM 10 +#define T_CONN_REQ 10 + +static void vgcs_gcc_fsm_u0_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U0); +} + +static void vgcs_gcc_fsm_u0_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_SETUP_REQ: + /* The calling user initiates a new call. */ + LOG_GCC(trans, LOGL_INFO, "Received call from user.\n"); + /* Change to MM CONNECTION PENDING state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING, 0, 0); + /* Send EST-REQ to MM layer. */ + vgcs_est_req(trans); + break; + case VGCS_GCC_EV_SETUP_IND: + /* New call notification. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify call at VTY. */ + vgcs_vty_notify(trans, "Incoming call\n"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u0p_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 1, OSMO_GSM44068_CSTATE_U0p); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_MM_EST, 0); +} + +static void vgcs_gcc_fsm_u0p_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + int rc; + + switch (event) { + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. MM confirmation/rejection is handled without transaction also. */ + trans_free(trans); + break; + case VGCS_GCC_EV_MM_EST_REJ: + /* The MM layer rejects the call. */ + LOG_GCC(trans, LOGL_INFO, "Call was rejected by MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify reject at VTY. */ + vgcs_vty_notify(trans, "Rejected (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_MM_EST_CNF: + /* The MM connection was confirmed. */ + LOG_GCC(trans, LOGL_INFO, "Call was confirmed by MM layer.\n"); + /* Change to GROUP CALL INITIATED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U1_GROUP_CALL_INITIATED, 0, 0); + /* Choose transaction ID. */ + rc = trans_assign_trans_id(trans->ms, trans->protocol, 0); + if (rc < 0) { + /* No free transaction ID. */ + trans_free(trans); + return; + } + trans->transaction_id = rc; + /* Send SETUP towards network. */ + gsm44068_tx_setup(trans, trans->callref, false, 0, false, NULL, 0, false, 0); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Establishment of MM layer timed out. */ + LOG_GCC(trans, LOGL_INFO, "MM layer timed out.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. MM confirmation/rejection is handled without transaction also. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u1_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 1, 1, OSMO_GSM44068_CSTATE_U1); +} + +static void vgcs_gcc_fsm_u1_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_CONNECT: + /* Received CONNECT from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was accepted by network.\n"); + /* Change to GROUP CALL ACTIVE (separate link) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, 0, 0); + /* Notify connect at VTY. */ + vgcs_vty_notify(trans, "Connect\n"); + break; + case VGCS_GCC_EV_DI_TERMINATION: + /* Received TERMINATION from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause); + /* Chane to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Release MM connection. */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to TERMINATION REQUESTED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0); + /* Send TERMINATION REQUEST towards network. */ + gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0); + break; + case VGCS_GCC_EV_ABORT_IND: + /* Radio link was released or failed. */ + LOG_GCC(trans, LOGL_INFO, "Got release from MM layer.\n"); + /* Chane to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Release MM connection. */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2sl_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 1, 1, OSMO_GSM44068_CSTATE_U2sl_U2); +} + +static void vgcs_gcc_fsm_u2sl_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_TERMINATION: + /* Received TERMINATION from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */ + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to TERMINATION REQUESTED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0); + /* Send TERMINATION REQUEST towards network. */ + gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0); + break; + case VGCS_GCC_EV_ABORT_IND: + /* Radio link was released or failed. */ + LOG_GCC(trans, LOGL_INFO, "Got release from MM layer.\n"); + /* Chane to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_LISTEN_REQ: + /* The user wants to release dedicated link and join the group channel as listener. */ + LOG_GCC(trans, LOGL_INFO, "User releases uplink on dedicated channel.\n"); + /* Change state to ACTIVE (wait receive). */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, 0, 0); + /* Set flag that we change to group receive mode after separate link. */ + trans->gcc.receive_after_sl = true; + /* Request release of the uplink. */ + gsm44068_tx_uplink_release(trans, GSM48_RR_CAUSE_LEAVE_GROUP_CA); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2wr_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 0, 1, -1, OSMO_GSM44068_CSTATE_U2wr_U6); +} + +static void vgcs_gcc_fsm_u2wr_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_LISTEN_CNF: + /* The MM layer confirms uplink release. */ + LOG_GCC(trans, LOGL_INFO, "Uplink is now released.\n"); + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Listening\n"); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Reset flag after we changed to group receive mode after separate link. */ + trans->gcc.receive_after_sl = false; + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + if (trans->gcc.receive_after_sl) { + LOG_GCC(trans, LOGL_INFO, "Ignoring release, because we released separate link.\n"); + break; + } + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_MM_IDLE: + if (!trans->gcc.receive_after_sl) + break; + /* If no channel is available (no notification received), enter the U2nc state. */ + if (!trans->gcc.ch_desc_present) { + /* Change state to ACTIVE (no channel). */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Listen (no channel yet)\n"); + break; + } + /* The MM layer indicates that the phone is ready to request group channel. */ + LOG_GCC(trans, LOGL_INFO, "MM is now idle, we can request group channel.\n"); + /* Send GROUP-REQ to MM layer. */ + vgcs_group_req(trans); + break; + case VGCS_GCC_EV_JOIN_GC_CNF: + /* Reset flag after we changed to group receive mode after separate link. */ + trans->gcc.receive_after_sl = false; + /* The MM layer confirms group channel. */ + LOG_GCC(trans, LOGL_INFO, "Joined group call after releasing separate link.\n"); + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Listening\n"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2r_u6_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 0, 0, -1, (trans->protocol == GSM48_PDISC_GROUP_CC) ? + OSMO_GSM44068_CSTATE_U2r : OSMO_GSM44068_CSTATE_U2wr_U6); + + /* There is a pending termination request, request uplink. */ + if (trans->gcc.termination) + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REQ, NULL); +} + +static void vgcs_gcc_fsm_u2r_u6_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + /* fall-thru */ + case VGCS_GCC_EV_TALK_REQ: + /* The user wants to talk. */ + LOG_GCC(trans, LOGL_INFO, "User wants to talk on the uplink.\n"); + /* Change to GROUP CALL ACTIVE (wait send) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE, 0, 0); + /* Request group transmit mode from MM layer. */ + vgcs_uplink_req(trans); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_UPLINK_FREE: + /* The MM layer indicates that the uplink is free. */ + LOG_GCC(trans, LOGL_INFO, "Uplink free indication received from MM layer.\n"); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Uplink free => You may talk\n"); + break; + case VGCS_GCC_EV_UPLINK_BUSY: + /* The MM layer indicates that the uplink is busy. */ + LOG_GCC(trans, LOGL_INFO, "Uplink busy indication received from MM layer.\n"); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Uplink busy => You cannot talk\n"); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2ws_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 0, -1, OSMO_GSM44068_CSTATE_U2ws); +} + +static void vgcs_gcc_fsm_u2ws_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_TALK_CNF: + /* Uplink was granted. */ + LOG_GCC(trans, LOGL_INFO, "Uplink established, user can talk now.\n"); + /* Change to GROUP CALL ACTIVE (send and receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Talking\n"); + break; + case VGCS_GCC_EV_TALK_REJ: + /* Uplink was rejected. */ + LOG_GCC(trans, LOGL_INFO, "Uplink rejected, user cannot talk.\n"); + /* Clear termination flag, if set. */ + trans->gcc.termination = false; + /* Change to GROUP CALL ACTIVE (receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Talking rejected (cause %d)\n", *cause); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2sr_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 1, -1, OSMO_GSM44068_CSTATE_U2sr); + + /* There is a pending termination request, request uplink. */ + if (trans->gcc.termination) + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TERM_REQ, NULL); +} + +static void vgcs_gcc_fsm_u2sr_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_TERMINATION: + /* Received TERMINATION from network. */ + LOG_GCC(trans, LOGL_INFO, "Call was terminated by network (cause %d).\n", *cause); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */ + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TALK_REJ: + /* Uplink was rejected by network. (Reject after granting access.) */ + LOG_GCC(trans, LOGL_INFO, "Uplink rejected, user cannot talk.\n"); + /* Change to GROUP CALL ACTIVE (receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Talking rejected (cause %d)\n", *cause); + break; + case VGCS_GCC_EV_LISTEN_REQ: + /* The user wants to release the uplink and become a listener. */ + LOG_GCC(trans, LOGL_INFO, "User wants to release the uplink.\n"); + /* Change to GROUP CALL ACTIVE (wait receive) state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE, 0, 0); + /* Request group receive mode from MM layer. */ + vgcs_uplink_rel_req(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The user terminates the call. */ + LOG_GCC(trans, LOGL_INFO, "User terminates the call.\n"); + /* Change to TERMINATION REQUESTED state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U5_TERMINATION_REQUESTED, 0, 0); + /* Send TERMINATION REQUEST towards network. */ + gsm44068_tx_termination_request(trans, trans->callref, false, 0, false, 0); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify leaving at VTY. */ + vgcs_vty_notify(trans, "Call left\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The MM layer released the group channel. */ + LOG_GCC(trans, LOGL_INFO, "Release/failure received from MM layer.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u2nc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 1, 1, 0, -1, OSMO_GSM44068_CSTATE_U2nc); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_NO_CHANNEL, 0); +} + +static void vgcs_gcc_fsm_u2nc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_SETUP_IND: + /* Channel becomes available, now join the group call. */ + LOG_GCC(trans, LOGL_INFO, "Radio channel becomes available.\n"); + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, 0, 0); + /* Send GROUP-REQ to MM layer. */ + vgcs_group_req(trans); + break; + case VGCS_GCC_EV_TERM_REQ: + /* The calling subscriber wants to terminate the call. */ + LOG_GCC(trans, LOGL_INFO, "User wants to terminate. Setting termination flag.\n"); + trans->gcc.termination = true; + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User leaves the group channel, that is not yet established.\n"); + /* Change to GROUP CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + break; + case VGCS_GCC_EV_REL_IND: + /* The MM layer indicates that group channel is gone. */ + LOG_GCC(trans, LOGL_INFO, "Group call notification is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released\n"); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Group channel did not become availblet. */ + LOG_GCC(trans, LOGL_INFO, "Timeout waiting for group channel.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Timeout\n"); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u3_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U3); +} + +static void vgcs_gcc_fsm_u3_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + + switch (event) { + case VGCS_GCC_EV_JOIN_GC_REQ: + /* Join (answer) incoming group call. */ + LOG_GCC(trans, LOGL_INFO, "Join call.\n"); + /* If no channel is available, enter the U2nc state. */ + if (!trans->gcc.ch_desc_present) { + /* Change state to ACTIVE (no channel). */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Answer (no channel yet)\n"); + break; + } + /* Change to CALL PRESENT state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST, 0, 0); + /* Send GROUP-REQ to MM layer. */ + vgcs_group_req(trans); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User rejects group call. */ + LOG_GCC(trans, LOGL_INFO, "User rejects group call.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_REL_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released\n"); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u4_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U4); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_CONN_REQ, 0); +} + +static void vgcs_gcc_fsm_u4_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_JOIN_GC_CNF: + /* The MM layer confirms the group receive mode. (We have a channel.) */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE, 0, 0); + /* Notify answer at VTY. */ + vgcs_vty_notify(trans, "Answer\n"); + break; + case VGCS_GCC_EV_ABORT_REQ: + /* User aborts group channel. */ + LOG_GCC(trans, LOGL_INFO, "User aborts group channel.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_REL_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released\n"); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Call rejected.\n"); + /* Change back to U3 state, so that call may be joined later. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U3_GROUP_CALL_PRESENT, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Group channel timed out. */ + LOG_GCC(trans, LOGL_INFO, "Timeout waiting for group channel.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Timeout\n"); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static void vgcs_gcc_fsm_u5_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_trans *trans = fi->priv; + + set_state_attributes(trans, -1, -1, 1, 1, OSMO_GSM44068_CSTATE_U5); + + /* Start timer */ + osmo_timer_schedule(&fi->timer, T_TERM, 0); +} + +static void vgcs_gcc_fsm_u5_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_trans *trans = fi->priv; + uint8_t *cause = data; + + switch (event) { + case VGCS_GCC_EV_DI_TERMINATION: + /* The network confirm the termination. */ + LOG_GCC(trans, LOGL_INFO, "Termination confirmend by network.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Terminated (cause %d)\n", *cause); + /* Don't release MM connection, this is done from network side using CHANNEL RELEASE. */ + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_DI_TERM_REJECT: + /* Termination was rejected. */ + LOG_GCC(trans, LOGL_INFO, "Termination rejected (cause %d), releasing MM connection.\n", *cause); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Termination rejected (cause %d), Call left\n", *cause); + /* Release MM connection. (Leave call.) + * We must release here, because we can have a dedicated MM connection or joined a group channel. + * We cannot go back, because we don't know what state we had before U5 state was entered. + * Do we really need to go back or just leave the call? */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_ABORT_IND: + /* The notified call is gone. */ + LOG_GCC(trans, LOGL_INFO, "Received call from network is gone.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Released (cause %d)\n", *cause); + /* Free transaction. */ + trans_free(trans); + break; + case VGCS_GCC_EV_TIMEOUT: + /* Termination timed out. */ + LOG_GCC(trans, LOGL_INFO, "Timeout waiting for termination.\n"); + /* Change to NULL state. */ + osmo_fsm_inst_state_chg(fi, VGCS_GCC_ST_U0_NULL, 0, 0); + /* Notify termination at VTY. */ + vgcs_vty_notify(trans, "Timeout\n"); + /* Release MM connection. (Leave call.) */ + vgcs_rel_req(trans); + /* Free transaction. */ + trans_free(trans); + break; + default: + OSMO_ASSERT(0); + } +} + +static int vgcs_gcc_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + return osmo_fsm_inst_dispatch(fi, VGCS_GCC_EV_TIMEOUT, NULL); +} + +static const struct osmo_fsm_state vgcs_gcc_fsm_states[] = { + [VGCS_GCC_ST_U0_NULL] = { + .name = "NULL (U0)", + .in_event_mask = S(VGCS_GCC_EV_SETUP_REQ) | + S(VGCS_GCC_EV_SETUP_IND), + .out_state_mask = S(VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING) | + S(VGCS_GCC_ST_U1_GROUP_CALL_INITIATED) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT), + .onenter = vgcs_gcc_fsm_u0_onenter, + .action = vgcs_gcc_fsm_u0_action, + }, + [VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING] = { + .name = "MM CONNECTION PENDING (U0.p)", + .in_event_mask = S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_MM_EST_REJ) | + S(VGCS_GCC_EV_MM_EST_CNF) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL) | + S(VGCS_GCC_ST_U1_GROUP_CALL_INITIATED), + .onenter = vgcs_gcc_fsm_u0p_onenter, + .action = vgcs_gcc_fsm_u0p_action, + }, + [VGCS_GCC_ST_U1_GROUP_CALL_INITIATED] = { + .name = "GROUP CALL INITIATED (U1)", + .in_event_mask = S(VGCS_GCC_EV_DI_CONNECT) | + S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U5_TERMINATION_REQUESTED) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u1_onenter, + .action = vgcs_gcc_fsm_u1_action, + }, + [VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE separate link (U2sl)", + .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_LISTEN_REQ), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL) | + S(VGCS_GCC_ST_U5_TERMINATION_REQUESTED) | + S(VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE), + .onenter = vgcs_gcc_fsm_u2sl_onenter, + .action = vgcs_gcc_fsm_u2sl_action, + }, + [VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE wait for receive mode (U2wr)", + .in_event_mask = S(VGCS_GCC_EV_LISTEN_CNF) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_MM_IDLE) | + S(VGCS_GCC_EV_JOIN_GC_CNF), + .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2wr_onenter, + .action = vgcs_gcc_fsm_u2wr_action, + }, + [VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE receive mode (U2r or U6 @ BCC)", + .in_event_mask = S(VGCS_GCC_EV_TALK_REQ) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_UPLINK_FREE) | + S(VGCS_GCC_EV_UPLINK_BUSY), + .out_state_mask = S(VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2r_u6_onenter, + .action = vgcs_gcc_fsm_u2r_u6_action, + }, + [VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE wait for send+receive mode (U2ws)", + .in_event_mask = S(VGCS_GCC_EV_TALK_CNF) | + S(VGCS_GCC_EV_TALK_REJ) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2ws_onenter, + .action = vgcs_gcc_fsm_u2ws_action, + }, + [VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE send+receive mode (U2sr)", + .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_LISTEN_REQ) | + S(VGCS_GCC_EV_TALK_REJ) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_ABORT_IND), + .out_state_mask = S(VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2sr_onenter, + .action = vgcs_gcc_fsm_u2sr_action, + }, + [VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE] = { + .name = "GROUP CALL ACTIVE no channel (U2nc)", + .in_event_mask = S(VGCS_GCC_EV_SETUP_IND) | + S(VGCS_GCC_EV_TERM_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_REL_IND) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u2nc_onenter, + .action = vgcs_gcc_fsm_u2nc_action, + }, + [VGCS_GCC_ST_U3_GROUP_CALL_PRESENT] = { + .name = "GROUP CALL PRESENT (U3)", + .in_event_mask = S(VGCS_GCC_EV_JOIN_GC_REQ) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_REL_IND), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL) | + S(VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST) | + S(VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE), + .onenter = vgcs_gcc_fsm_u3_onenter, + .action = vgcs_gcc_fsm_u3_action, + }, + [VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST] = { + .name = "GROUP CALL CONNECTION REQUEST (U4)", + .in_event_mask = S(VGCS_GCC_EV_JOIN_GC_CNF) | + S(VGCS_GCC_EV_ABORT_REQ) | + S(VGCS_GCC_EV_REL_IND) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) | + S(VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) | + S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u4_onenter, + .action = vgcs_gcc_fsm_u4_action, + }, + [VGCS_GCC_ST_U5_TERMINATION_REQUESTED] = { + .name = "TERMINATION REQUESTED (U5)", + .in_event_mask = S(VGCS_GCC_EV_DI_TERMINATION) | + S(VGCS_GCC_EV_DI_TERM_REJECT) | + S(VGCS_GCC_EV_ABORT_IND) | + S(VGCS_GCC_EV_TIMEOUT), + .out_state_mask = S(VGCS_GCC_ST_U0_NULL), + .onenter = vgcs_gcc_fsm_u5_onenter, + .action = vgcs_gcc_fsm_u5_action, + }, +}; + +static struct osmo_fsm vgcs_gcc_fsm = { + .name = "gcc", + .states = vgcs_gcc_fsm_states, + .num_states = ARRAY_SIZE(vgcs_gcc_fsm_states), + .log_subsys = DGCC, + .event_names = vgcs_gcc_fsm_event_names, + .timer_cb = vgcs_gcc_fsm_timer_cb, +}; + +static struct osmo_fsm vgcs_bcc_fsm = { + .name = "bcc", + .states = vgcs_gcc_fsm_states, + .num_states = ARRAY_SIZE(vgcs_gcc_fsm_states), + .log_subsys = DBCC, + .event_names = vgcs_gcc_fsm_event_names, + .timer_cb = vgcs_gcc_fsm_timer_cb, +}; + +static __attribute__((constructor)) void on_dso_load(void) +{ + OSMO_ASSERT(osmo_fsm_register(&vgcs_gcc_fsm) == 0); + OSMO_ASSERT(osmo_fsm_register(&vgcs_bcc_fsm) == 0); +} + +static const char *gsm44068_gcc_state_name(struct osmo_fsm_inst *fi) +{ + return vgcs_gcc_fsm_states[fi->state].name; +} + +/* + * transaction + */ + +/* Create transaction together with state machine and set initail states. */ +static struct gsm_trans *trans_alloc_vgcs(struct osmocom_ms *ms, uint8_t pdisc, uint8_t transaction_id, + uint32_t callref) +{ + struct gsm_trans *trans; + + trans = trans_alloc(ms, pdisc, 0xff, callref); + if (!trans) + return NULL; + trans->gcc.fi = osmo_fsm_inst_alloc((pdisc == GSM48_PDISC_GROUP_CC) ? &vgcs_gcc_fsm : &vgcs_bcc_fsm, + trans, trans, LOGL_DEBUG, NULL); + if (!trans->gcc.fi) { + trans_free(trans); + return NULL; + } + + LOG_GCC(trans, LOGL_DEBUG, "Created transaction for callref %d, pdisc %d, transaction_id 0x%x\n", + callref, pdisc, transaction_id); + + set_state_attributes(trans, 0, 0, 0, 0, OSMO_GSM44068_CSTATE_U0); + + return trans; +} + +/* Call Control Specific transaction release. + * gets called by trans_free, DO NOT CALL YOURSELF! + */ +void _gsm44068_gcc_bcc_trans_free(struct gsm_trans *trans) +{ + if (!trans->gcc.fi) + return; + + /* Free state machine. */ + osmo_fsm_inst_free(trans->gcc.fi); +} + +/* Find ongoing call. (The call must not be present.) */ +struct gsm_trans *trans_find_ongoing_gcc_bcc(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol != GSM48_PDISC_GROUP_CC && trans->protocol != GSM48_PDISC_BCAST_CC) + continue; + if (trans->gcc.fi->state != VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) + return trans; + } + + return NULL; +} + +/* + * messages from upper/lower layers + */ + +static int gsm44068_gcc_data_ind(struct gsm_trans *trans, struct msgb *msg, uint8_t transaction_id) +{ + struct osmocom_ms *ms = trans->ms; + struct gsm48_hdr *gh = msgb_l3(msg); + int msg_type = gh->msg_type & 0xbf; + int rc = 0; + uint8_t cause = 0; + uint8_t d_att, u_att, comm, orig; + + /* pull the MMCC header */ + msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); + + /* Use transaction ID from message. If we join a group call, we don't have it until now. */ + trans->transaction_id = transaction_id; + + LOG_GCC(trans, LOGL_INFO, "(ms %s) Received '%s' in state %s\n", ms->name, gsm44068_gcc_msg_name(msg_type), + gsm44068_gcc_state_name(trans->gcc.fi)); + + switch (msg_type) { + case OSMO_GSM44068_MSGT_CONNECT: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_CONNECT, NULL); + break; + case OSMO_GSM44068_MSGT_TERMINATION: + rc = gsm44068_rx_termination(trans, msg, &cause, NULL, NULL); + if (rc < 0) + break; + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_TERMINATION, &cause); + break; + case OSMO_GSM44068_MSGT_TERMINATION_REJECT: + rc = gsm44068_rx_termination(trans, msg, &cause, NULL, NULL); + if (rc < 0) + break; + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_DI_TERM_REJECT, &cause); + break; + case OSMO_GSM44068_MSGT_GET_STATUS: + /* Note: The message via UNIT DATA is not supported. */ + gsm44068_tx_status(trans, OSMO_GSM44068_CAUSE_RESPONSE_TO_GET_STATUS, NULL, 0, + true, trans->gcc.call_state, + true, trans->gcc.d_att, trans->gcc.u_att, trans->gcc.comm, trans->gcc.orig); + break; + case OSMO_GSM44068_MSGT_SET_PARAMETER: + rc = gsm44068_rx_set_parameter(trans, msg, &d_att, &u_att, &comm, &orig); + if (rc >= 0) + set_state_attributes(trans, d_att, u_att, comm, orig, -1); + break; + default: + LOG_GCC(trans, LOGL_NOTICE, "Message '%s' not supported.\n", gsm44068_gcc_msg_name(msg_type)); + rc = -EINVAL; + } + + return rc; +} + +/* receive message from MM layer */ +int gsm44068_rcv_gcc_bcc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_settings *set = &ms->settings; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + uint8_t pdisc; + struct gsm_trans *trans; + int rc = 0; + + /* Check for message class and get protocol type. */ + switch ((msg_type & GSM48_MMXX_MASK)) { + case GSM48_MMGCC_CLASS: + pdisc = GSM48_PDISC_GROUP_CC; + if (!set->vgcs) { + LOGP(DGCC, LOGL_ERROR, "Ignoring message '%s', because VGCS is not supported!\n", + get_mmxx_name(msg_type)); + return -ENOTSUP; + } + break; + case GSM48_MMBCC_CLASS: + pdisc = GSM48_PDISC_BCAST_CC; + if (!set->vbs) { + LOGP(DGCC, LOGL_ERROR, "Ignoring message '%s', because VBS is not supported!\n", + get_mmxx_name(msg_type)); + return -ENOTSUP; + } + break; + default: + LOGP(DGCC, LOGL_ERROR, "Message class not allowed, please fix!\n"); + return -EINVAL; + } + + trans = trans_find_by_callref(ms, pdisc, mmh->ref); + if (!trans) { + LOG_GCC_PR(pdisc, mmh->ref, LOGL_INFO, "(ms %s) Received '%s' without transaction.\n", + ms->name, get_mmxx_name(msg_type)); + + if (msg_type == GSM48_MMGCC_REL_IND || msg_type == GSM48_MMBCC_REL_IND) { + /* If we got the MMxx-EST-REJ after we aborted the call, we ignore. */ + return 0; + } + if (msg_type == GSM48_MMGCC_EST_CNF || msg_type == GSM48_MMBCC_EST_CNF || + msg_type == GSM48_MMGCC_GROUP_CNF || msg_type == GSM48_MMBCC_GROUP_CNF) { + struct msgb *nmsg; + LOG_GCC_PR(pdisc, mmh->ref, LOGL_ERROR, "Received confirm for GCC/BCC call, " + "but there is no transaction, releasing.\n"); + /* If we got the MMxx-EST-CNF after we aborted the call, we release. */ + msg_type = (pdisc == GSM48_PDISC_GROUP_CC) ? GSM48_MMGCC_REL_REQ : GSM48_MMBCC_REL_REQ; + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, 0); + if (!nmsg) + return -ENOMEM; + LOG_GCC_PR(pdisc, mmh->ref, LOGL_INFO, "Sending %s\n", get_mmxx_name(msg_type)); + gsm48_mmxx_downmsg(ms, nmsg); + return 0; + + } else if (msg_type == GSM48_MMGCC_NOTIF_IND || msg_type == GSM48_MMBCC_NOTIF_IND) { + /* Notification gone but no transaction. */ + if (mmh->notify != MMXX_NOTIFY_SETUP) + return 0; + /* Incoming notification, creation transaction. */ + trans = trans_alloc_vgcs(ms, pdisc, 0xff, mmh->ref); + if (!trans) + return -ENOMEM; + } else { + LOG_GCC_PR(pdisc, mmh->ref, LOGL_ERROR, "Received GCC/BCC message for unknown transaction.\n"); + return -ENOENT; + } + } + + LOG_GCC(trans, LOGL_INFO, "(ms %s) Received '%s' in state %s\n", ms->name, get_mmxx_name(msg_type), + gsm44068_gcc_state_name(trans->gcc.fi)); + + switch (msg_type) { + case GSM48_MMGCC_EST_CNF: + case GSM48_MMBCC_EST_CNF: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_EST_CNF, NULL); + break; + case GSM48_MMGCC_ERR_IND: + case GSM48_MMBCC_ERR_IND: + case GSM48_MMGCC_REL_IND: + case GSM48_MMBCC_REL_IND: + /* Ignore release confirm or release collision. */ + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) + break; + /* If MM fails or is rejected during U0.p state, this is a MM-EST-REJ. */ + if (trans->gcc.fi->state == VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING) + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_EST_REJ, &mmh->cause); + else + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_IND, &mmh->cause); + break; + case GSM48_MMGCC_DATA_IND: + case GSM48_MMBCC_DATA_IND: + rc = gsm44068_gcc_data_ind(trans, msg, mmh->transaction_id); + break; + case GSM48_MMGCC_NOTIF_IND: + case GSM48_MMBCC_NOTIF_IND: + switch (mmh->notify) { + case MMXX_NOTIFY_SETUP: + /* Store channel description. */ + trans->gcc.ch_desc_present = mmh->ch_desc_present; + memcpy(&trans->gcc.ch_desc, &mmh->ch_desc, sizeof(trans->gcc.ch_desc)); + if (trans->gcc.fi->state == VGCS_GCC_ST_U0_NULL) { + /* In null state this indication is a SETUP-IND. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_IND, NULL); + } else if (trans->gcc.fi->state == VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE && mmh->ch_desc_present) { + /* In U2nc state with a channel info, is a SETUP-IND (with updated mode). */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_IND, NULL); + } + break; + case MMXX_NOTIFY_RELEASE: + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT || + trans->gcc.fi->state == VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST || + trans->gcc.fi->state == VGCS_GCC_ST_U2nc_GROUP_CALL_ACTIVE) { + /* If notification is gone, release pending received call. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_REL_IND, NULL); + } + break; + } + break; + case GSM48_MMGCC_GROUP_CNF: + case GSM48_MMBCC_GROUP_CNF: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_JOIN_GC_CNF, NULL); + break; + case GSM48_MMGCC_UPLINK_CNF: + case GSM48_MMBCC_UPLINK_CNF: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_CNF, NULL); + break; + case GSM48_MMGCC_UPLINK_REL_IND: + case GSM48_MMBCC_UPLINK_REL_IND: + if (trans->gcc.fi->state == VGCS_GCC_ST_U2ws_GROUP_CALL_ACTIVE || + trans->gcc.fi->state == VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) { + /* We are waiting to send, so this is a reject. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REJ, &mmh->cause); + } else if (trans->gcc.fi->state == VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE) { + /* We are waiting to receive, so this is a confirm. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_LISTEN_CNF, &mmh->cause); + } + break; + case GSM48_MMGCC_UPLINK_FREE_IND: + case GSM48_MMBCC_UPLINK_FREE_IND: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_UPLINK_FREE, NULL); + break; + case GSM48_MMGCC_UPLINK_BUSY_IND: + case GSM48_MMBCC_UPLINK_BUSY_IND: + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_UPLINK_BUSY, NULL); + break; + default: + LOG_GCC(trans, LOGL_NOTICE, "Message '%s' unhandled.\n", get_mmxx_name(msg_type)); + rc = -ENOTSUP; + } + + return rc; +} + +/* Special function to receive IDLE state of MM layer. This is required to request a channel after dedicated mode. */ +int gsm44068_rcv_mm_idle(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) + return -ENOENT; + if (trans->gcc.fi->state == VGCS_GCC_ST_U2wr_GROUP_CALL_ACTIVE && trans->gcc.receive_after_sl) { + /* Finally the MM layer is IDLE. */ + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_MM_IDLE, NULL); + return 0; + } + return -EINVAL; +} + +/* Setup or join a VGC/VBC. */ +int gcc_bcc_call(struct osmocom_ms *ms, uint8_t protocol, const char *number) +{ + uint32_t callref = strtoul(number, NULL, 0); + struct gsm_trans *trans; + int i; + + if (strlen(number) > 8) { +inval: + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Invalid group '%s'\n", number); + return -EINVAL; + } + + for (i = 0; i < strlen(number); i++) { + if (number[i] < '0' || number[i] > '9') + goto inval; + } + + if (callref == 0) + goto inval; + + /* Reject if there is already an ongoing call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Cannot call/join, we are busy\n"); + return -EBUSY; + } + + /* Find call that matches the given protocol+cellref. */ + trans = trans_find_by_callref(ms, protocol, callref); + if (trans) { + /* Answer incoming call. */ + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) { + osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_JOIN_GC_REQ, NULL); + return 0; + } + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call already established\n"); + return -EEXIST; + } + + /* Create new transaction. ORIG will be set when entering U2sl state. */ + trans = trans_alloc_vgcs(ms, protocol, 0xff, callref); + if (!trans) + return -ENOMEM; + + /* Setup new call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_SETUP_REQ, NULL); +} + +/* Leave a VGC. (If we are the originator, we do not terminate.) */ +int gcc_leave(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->gcc.fi->state == VGCS_GCC_ST_U0p_MM_CONNECTION_PENDING || + trans->gcc.fi->state == VGCS_GCC_ST_U1_GROUP_CALL_INITIATED || + trans->gcc.fi->state == VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE || + trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot leave (abort), in this state.\n"); + if (trans->gcc.fi->state == VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE) + vgcs_vty_notify(trans, "Cannot leave while talking\n"); + return -EINVAL; + } + + /* Send ABORT-REQ to leave the call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_REQ, NULL); +} + +/* Hangup VGC/VBC. (If we are the originator, we terminate the call.) */ +int gcc_bcc_hangup(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->gcc.orig) { + if (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT || + trans->gcc.fi->state == VGCS_GCC_ST_U4_GROUP_CALL_CONN_REQUEST || + trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot hangup (terminate) in this state.\n"); + return -EINVAL; + } + /* Send TERM-REQ to leave the call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TERM_REQ, NULL); + } + + if (trans->gcc.fi->state == VGCS_GCC_ST_U5_TERMINATION_REQUESTED) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot hangup (abort) in this state.\n"); + return -EINVAL; + } + /* Send ABORT-REQ to leave the call. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_ABORT_REQ, NULL); +} + +int gcc_talk(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->protocol != GSM48_PDISC_GROUP_CC) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot talk, the ongoing call is not a group call.\n"); + vgcs_vty_notify(trans, "Not a group call\n"); + return -EIO; + } + + if (trans->gcc.fi->state != VGCS_GCC_ST_U2r_U6_GROUP_CALL_ACTIVE) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot talk, we are not listening.\n"); + return -EINVAL; + } + + /* Send TALK-REQ to become talker. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_TALK_REQ, NULL); +} + +int gcc_listen(struct osmocom_ms *ms) +{ + struct gsm_trans *trans; + + /* Reject if there is no call. */ + trans = trans_find_ongoing_gcc_bcc(ms); + if (!trans) { + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No Call\n"); + return -EINVAL; + } + + if (trans->protocol != GSM48_PDISC_GROUP_CC) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot become listener, the ongoing call is not a group call.\n"); + vgcs_vty_notify(trans, "Not a group call\n"); + return -EIO; + } + + if (trans->gcc.fi->state != VGCS_GCC_ST_U2sl_GROUP_CALL_ACTIVE && + trans->gcc.fi->state != VGCS_GCC_ST_U2sr_GROUP_CALL_ACTIVE) { + LOG_GCC(trans, LOGL_NOTICE, "Cannot listen, we are not talking.\n"); + return -EINVAL; + } + + /* Send LISTEN-REQ to become listener. */ + return osmo_fsm_inst_dispatch(trans->gcc.fi, VGCS_GCC_EV_LISTEN_REQ, NULL); +} + +int gsm44068_dump_calls(struct osmocom_ms *ms, void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol != GSM48_PDISC_GROUP_CC && trans->protocol != GSM48_PDISC_BCAST_CC) + continue; + print(priv, "Voice %s call '%d' is %s.\n", + (trans->protocol == GSM48_PDISC_GROUP_CC) ? "group" : "broadcast", trans->callref, + (trans->gcc.fi->state == VGCS_GCC_ST_U3_GROUP_CALL_PRESENT) ? "incoming" : "active"); + } + + return 0; +} diff --git a/src/host/layer23/src/mobile/gsm480_ss.c b/src/host/layer23/src/mobile/gsm480_ss.c index 4ad2d7cb..0acd18d7 100644 --- a/src/host/layer23/src/mobile/gsm480_ss.c +++ b/src/host/layer23/src/mobile/gsm480_ss.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -26,15 +22,20 @@ #include <stdlib.h> #include <osmocom/core/msgb.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/gsm/protocol/gsm_04_80.h> +#include <osmocom/gsm/gsm48.h> + #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> #include <osmocom/bb/mobile/gsm480_ss.h> -#include <osmocom/core/talloc.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/bb/mobile/vty.h> -#include <osmocom/gsm/protocol/gsm_04_80.h> -#include <osmocom/gsm/gsm48.h> static uint32_t new_callref = 0x80000001; @@ -196,7 +197,7 @@ static const struct value_string Bearerservice_vals[] = { static int gsm480_ss_result(struct osmocom_ms *ms, const char *response, uint8_t error) { - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); if (response) { char text[256], *t = text, *s; @@ -204,18 +205,18 @@ static int gsm480_ss_result(struct osmocom_ms *ms, const char *response, while ((s = strchr(text, '\r'))) *s = '\n'; while ((s = strsep(&t, "\n"))) { - vty_notify(ms, "Service response: %s\n", s); + l23_vty_ms_notify(ms, "Service response: %s\n", s); } } else if (error) - vty_notify(ms, "Service request failed: %s\n", + l23_vty_ms_notify(ms, "Service request failed: %s\n", get_value_string(gsm480_err_names, error)); else - vty_notify(ms, "Service request failed.\n"); + l23_vty_ms_notify(ms, "Service request failed.\n"); return 0; } -enum { +enum gsm480_ss_state { GSM480_SS_ST_IDLE = 0, GSM480_SS_ST_REGISTER, GSM480_SS_ST_ACTIVE, @@ -263,8 +264,8 @@ void _gsm480_ss_trans_free(struct gsm_trans *trans) msgb_free(trans->ss.msg); trans->ss.msg = NULL; } - vty_notify(trans->ms, NULL); - vty_notify(trans->ms, "Service connection terminated.\n"); + l23_vty_ms_notify(trans->ms, NULL); + l23_vty_ms_notify(trans->ms, "Service connection terminated.\n"); } /* release MM connection, free transaction */ @@ -286,6 +287,13 @@ static int gsm480_trans_free(struct gsm_trans *trans) return 0; } +static void gsm480_trans_state_chg(struct gsm_trans *trans, + enum gsm480_ss_state state) +{ + trans->ss.state = state; + osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_STATE_CHG, trans); +} + /* * encoding */ @@ -364,7 +372,7 @@ static const char *ss_code_by_char(const char *code, uint8_t *ss_code) static const char *decode_ss_code(uint8_t ss_code) { static char unknown[16]; - + switch (ss_code) { case 33: return "CFU"; @@ -602,6 +610,12 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans) return -EIO; } + /* ASCI call does not allow other transactions */ + if (trans_find_ongoing_gcc_bcc(ms)) { + gsm480_ss_result(ms, "<ongoing ASCI call>", 0); + return -EBUSY; + } + /* allocate transaction with dummy reference */ transaction_id = trans_assign_trans_id(ms, GSM48_PDISC_NC_SS, 0); @@ -620,8 +634,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans) return -ENOMEM; } - /* go register sent state */ - trans->ss.state = GSM480_SS_ST_REGISTER; + gsm480_trans_state_chg(trans, GSM480_SS_ST_REGISTER); /* FIXME: generate invoke ID */ trans->ss.invoke_id = 5; @@ -646,7 +659,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans) code++; to = ss_code_by_char(code + 1, &ss_code); - + /* register */ if (ss_code && to && to[0] == '*') { OSMO_STRLCPY_ARRAY(dest, to + 1); @@ -664,7 +677,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans) uint8_t ss_code = 0; ss_code_by_char(code + 2, &ss_code); - + if (ss_code) return gsm480_tx_cf(trans, GSM0480_MTYPE_REGISTER, GSM0480_OP_CODE_ERASE_SS, ss_code, NULL); @@ -674,7 +687,7 @@ int ss_send(struct osmocom_ms *ms, const char *code, int new_trans) uint8_t ss_code = 0; ss_code_by_char(code + 1, &ss_code); - + if (ss_code) return gsm480_tx_cf(trans, GSM0480_MTYPE_REGISTER, GSM0480_OP_CODE_DEACTIVATE_SS, ss_code, NULL); @@ -712,7 +725,7 @@ static int parse_tag_asn1(const uint8_t *data, int len, /* check for buffer overflow */ if (len < *tag_len) return -1; - + /* return length */ return len; } @@ -789,7 +802,7 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data, LOGP(DSS, LOGL_INFO, "call forwarding reply: len %d data %s\n", len, osmo_hexdump(data, len)); - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); /* forwarding feature list */ if (parse_tag_asn1(data, len, &tag_data, &tag_len) < 0) { @@ -798,9 +811,9 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data, } if (data[0] == 0x80) { if ((tag_data[0] & 0x01)) - vty_notify(ms, "Status: activated\n"); + l23_vty_ms_notify(ms, "Status: activated\n"); else - vty_notify(ms, "Status: deactivated\n"); + l23_vty_ms_notify(ms, "Status: deactivated\n"); return 0; } @@ -819,7 +832,7 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data, /* check for SS code */ if (data[0] != 0x04) break; - vty_notify(ms, "Reply for %s\n", decode_ss_code(tag_data[0])); + l23_vty_ms_notify(ms, "Reply for %s\n", decode_ss_code(tag_data[0])); len -= tag_data - data + tag_len; data = tag_data + tag_len; /* sequence tag */ @@ -835,10 +848,10 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data, data = tag_data; break; default: - vty_notify(ms, "Call Forwarding reply unsupported.\n"); + l23_vty_ms_notify(ms, "Call Forwarding reply unsupported.\n"); return 0; } - + while (len) { /* sequence tag */ if (parse_tag_asn1(data, len, &tag_data, &tag_len) < 0) { @@ -867,20 +880,20 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data, osmo_hexdump(tag_data, tag_len)); switch (data2[0]) { case 0x82: - vty_notify(ms, "Bearer Service: %s\n", + l23_vty_ms_notify(ms, "Bearer Service: %s\n", get_value_string(Bearerservice_vals, tag_data[0])); break; case 0x83: - vty_notify(ms, "Teleservice: %s\n", + l23_vty_ms_notify(ms, "Teleservice: %s\n", get_value_string(Teleservice_vals, tag_data[0])); break; case 0x84: if ((tag_data[0] & 0x01)) - vty_notify(ms, "Status: activated\n"); + l23_vty_ms_notify(ms, "Status: activated\n"); else - vty_notify(ms, "Status: deactivated\n"); + l23_vty_ms_notify(ms, "Status: deactivated\n"); break; case 0x85: if (((tag_data[0] & 0x70) >> 4) == 1) @@ -892,7 +905,7 @@ static int gsm480_rx_cf(struct gsm_trans *trans, const uint8_t *data, gsm48_decode_bcd_number2(number + strlen(number), sizeof(number) - strlen(number), tag_data - 1, tag_len + 1, 1); - vty_notify(ms, "Destination: %s\n", number); + l23_vty_ms_notify(ms, "Destination: %s\n", number); break; } len2 -= tag_data - data2 + tag_len; @@ -1058,7 +1071,12 @@ static int gsm480_rx_release_comp(struct gsm_trans *trans, struct msgb *msg) struct tlv_parsed tp; int rc = 0; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DSS, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + gsm480_trans_free(trans); + return -EINVAL; + } + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rc = gsm480_rx_fac_ie(trans, TLVP_VAL(&tp, GSM48_IE_FACILITY), *(TLVP_VAL(&tp, GSM48_IE_FACILITY)-1)); @@ -1091,11 +1109,15 @@ static int gsm480_rx_facility(struct gsm_trans *trans, struct msgb *msg) struct tlv_parsed tp; int rc = 0; - /* go register state */ - trans->ss.state = GSM480_SS_ST_ACTIVE; + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_FACILITY, 0) < 0) { + LOGP(DSS, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + /* XXX: indicate an error somehow */ + return -EINVAL; + } + + gsm480_trans_state_chg(trans, GSM480_SS_ST_ACTIVE); - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, - GSM48_IE_FACILITY, 0); if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rc = gsm480_rx_fac_ie(trans, TLVP_VAL(&tp, GSM48_IE_FACILITY), *(TLVP_VAL(&tp, GSM48_IE_FACILITY)-1)); @@ -1123,10 +1145,14 @@ static int gsm480_rx_register(struct gsm_trans *trans, struct msgb *msg) struct tlv_parsed tp; int rc = 0; - /* go register state */ - trans->ss.state = GSM480_SS_ST_ACTIVE; + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DSS, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + /* XXX: indicate an error somehow */ + return -EINVAL; + } + + gsm480_trans_state_chg(trans, GSM480_SS_ST_ACTIVE); - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { rc = gsm480_rx_fac_ie(trans, TLVP_VAL(&tp, GSM48_IE_FACILITY), *(TLVP_VAL(&tp, GSM48_IE_FACILITY)-1)); @@ -1243,7 +1269,7 @@ int gsm480_rcv_ss(struct osmocom_ms *ms, struct msgb *msg) struct gsm_trans *trans; int rc = 0; - trans = trans_find_by_callref(ms, mmh->ref); + trans = trans_find_by_callref(ms, GSM48_PDISC_NC_SS, mmh->ref); if (!trans) { LOGP(DSS, LOGL_INFO, " -> (new transaction)\n"); trans = trans_alloc(ms, GSM48_PDISC_NC_SS, mmh->transaction_id, diff --git a/src/host/layer23/src/mobile/gsm48_cc.c b/src/host/layer23/src/mobile/gsm48_cc.c index de0d0352..94b81cb0 100644 --- a/src/host/layer23/src/mobile/gsm48_cc.c +++ b/src/host/layer23/src/mobile/gsm48_cc.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -29,13 +25,16 @@ #include <osmocom/core/utils.h> #include <osmocom/gsm/gsm48.h> #include <osmocom/core/talloc.h> +#include <osmocom/core/signal.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> #include <osmocom/bb/mobile/gsm48_cc.h> -#include <osmocom/bb/mobile/voice.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> +#include <osmocom/bb/mobile/tch.h> #include <l1ctl_proto.h> static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); @@ -44,6 +43,8 @@ int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans, uint32_t callref, int location, int value); static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg); +static void gsm48_cc_trans_bcap_update(struct gsm_trans *trans, + const struct gsm_mncc *mncc); /* * init @@ -55,7 +56,7 @@ int gsm48_cc_init(struct osmocom_ms *ms) cc->ms = ms; - if (!cc->mncc_upqueue.next == 0) + if (cc->mncc_upqueue.next != NULL) return 0; LOGP(DCC, LOGL_INFO, "init Call Control\n"); @@ -183,11 +184,13 @@ static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans, /* enqueue message to application (MNCC-SAP) */ static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans, - int msg_type, struct gsm_mncc *mncc) + uint32_t msg_type, struct gsm_mncc *mncc) { struct gsm48_cclayer *cc = &ms->cclayer; struct msgb *msg; + gsm48_cc_trans_bcap_update(trans, mncc); + if (trans) LOGP(DCC, LOGL_INFO, "(ms %s ti %x) Sending '%s' to MNCC.\n", ms->name, trans->transaction_id, @@ -241,6 +244,8 @@ static void new_cc_state(struct gsm_trans *trans, int state) gsm48_cc_state_name(state)); trans->cc.state = state; + + osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_STATE_CHG, trans); } /* @@ -369,6 +374,9 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans) { gsm48_stop_cc_timer(trans); + talloc_free(trans->cc.bcap); + trans->cc.bcap = NULL; + /* disable audio distribution */ if (trans->ms->mncc_entity.ref == trans->callref) trans->ms->mncc_entity.ref = 0; @@ -384,6 +392,16 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans) new_cc_state(trans, GSM_CSTATE_NULL); } +static void gsm48_cc_trans_bcap_update(struct gsm_trans *trans, + const struct gsm_mncc *mncc) +{ + if (~mncc->fields & MNCC_F_BEARER_CAP) + return; + if (trans->cc.bcap == NULL) + trans->cc.bcap = talloc(trans, struct gsm_mncc_bearer_cap); + memcpy(trans->cc.bcap, &mncc->bearer_cap, sizeof(mncc->bearer_cap)); +} + /* release MM connection, go NULL state, free transaction */ static int gsm48_rel_null_free(struct gsm_trans *trans) { @@ -625,10 +643,14 @@ static int gsm48_cc_rx_progress(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received PROGRESS\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_PROGR_IND, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + memset(&progress, 0, sizeof(struct gsm_mncc)); progress.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, - GSM48_IE_PROGR_IND, 0); /* progress */ if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { progress.fields |= MNCC_F_PROGRESS; @@ -656,13 +678,17 @@ static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans, struct tlv_parsed tp; struct gsm_mncc call_proc; - LOGP(DCC, LOGL_INFO, "sending CALL PROCEEDING\n"); + LOGP(DCC, LOGL_INFO, "received CALL PROCEEDING\n"); + + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } gsm48_stop_cc_timer(trans); memset(&call_proc, 0, sizeof(struct gsm_mncc)); call_proc.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); #if 0 /* repeat */ if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) @@ -714,12 +740,16 @@ static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received ALERTING\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + gsm48_stop_cc_timer(trans); /* no T301 in MS call control */ memset(&alerting, 0, sizeof(struct gsm_mncc)); alerting.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { alerting.fields |= MNCC_F_FACILITY; @@ -755,11 +785,15 @@ static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received CONNECT\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + gsm48_stop_cc_timer(trans); memset(&connect, 0, sizeof(struct gsm_mncc)); connect.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* facility */ if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { connect.fields |= MNCC_F_FACILITY; @@ -825,10 +859,13 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received SETUP\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + memset(&setup, 0, sizeof(struct gsm_mncc)); setup.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { setup.fields |= MNCC_F_BEARER_CAP; @@ -1078,9 +1115,13 @@ static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received START DTMF ACKNOWLEDGE\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* keypad facility */ if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { dtmf.fields |= MNCC_F_KEYPAD; @@ -1141,9 +1182,13 @@ static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received STOP DTMF ACKNOWLEDGE\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + memset(&dtmf, 0, sizeof(struct gsm_mncc)); dtmf.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf); } @@ -1337,15 +1382,18 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received USERINFO\n"); - memset(&user, 0, sizeof(struct gsm_mncc)); - user.callref = trans->callref; if (payload_len < 1) { - LOGP(DCC, LOGL_NOTICE, "Short read of userinfo message " - "error.\n"); + LOGP(DCC, LOGL_NOTICE, "Short read of USERINFO message\n"); return -EINVAL; } - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, - GSM48_IE_USER_USER, 0); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_USER_USER, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; /* user-user */ gsm48_decode_useruser(&user.useruser, TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); @@ -1419,17 +1467,20 @@ static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received MODIFY REJECT\n"); + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of MODIFY REJECT message\n"); + return -EINVAL; + } + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + gsm48_stop_cc_timer(trans); memset(&modify, 0, sizeof(struct gsm_mncc)); modify.callref = trans->callref; - if (payload_len < 1) { - LOGP(DCC, LOGL_NOTICE, "Short read of modify reject message " - "error.\n"); - return -EINVAL; - } - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, - GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); /* bearer capability */ if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { modify.fields |= MNCC_F_BEARER_CAP; @@ -1677,14 +1728,18 @@ static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received DISCONNECT\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_CAUSE, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + gsm48_stop_cc_timer(trans); new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); memset(&disc, 0, sizeof(struct gsm_mncc)); disc.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, - GSM48_IE_CAUSE, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { disc.fields |= MNCC_F_CAUSE; @@ -1726,11 +1781,15 @@ static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received RELEASE\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + gsm48_stop_cc_timer(trans); memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; @@ -1795,11 +1854,15 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) LOGP(DCC, LOGL_INFO, "received RELEASE COMPLETE\n"); + if (tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DCC, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return -EINVAL; + } + gsm48_stop_cc_timer(trans); memset(&rel, 0, sizeof(struct gsm_mncc)); rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); /* cause */ if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { rel.fields |= MNCC_F_CAUSE; @@ -1937,10 +2000,16 @@ int mncc_tx_to_cc(void *inst, int msg_type, void *arg) return -EBUSY; } + /* ASCI call does not allow other transactions */ + if (trans_find_ongoing_gcc_bcc(ms)) { + LOGP(DCC, LOGL_NOTICE, "Phone is busy doing ASCI call\n"); + return -EBUSY; + } + data->msg_type = msg_type; /* Find callref */ - trans = trans_find_by_callref(ms, data->callref); + trans = trans_find_by_callref(ms, GSM48_PDISC_CC, data->callref); if (!trans) { /* check for SETUP message */ @@ -1968,9 +2037,15 @@ int mncc_tx_to_cc(void *inst, int msg_type, void *arg) } } + gsm48_cc_trans_bcap_update(trans, data); + switch (msg_type) { case GSM_TCHF_FRAME: - return gsm_send_voice(ms, arg); + case GSM_TCHF_FRAME_EFR: + case GSM_TCHH_FRAME: + case GSM_TCH_FRAME_AMR: + case GSM_BAD_FRAME: + return tch_send_mncc_frame(ms, arg); case MNCC_LCHAN_MODIFY: return 0; case MNCC_FRAME_RECV: @@ -2095,15 +2170,21 @@ static struct datastate { static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg) { struct osmocom_ms *ms = trans->ms; - struct gsm48_hdr *gh = msgb_l3(msg); - int msg_type = gh->msg_type & 0xbf; - uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; - /* flip */ + const struct gsm48_hdr *gh = msgb_l3(msg); int msg_supported = 0; /* determine, if message is supported at all */ + uint8_t msg_type; int i, rc; - /* set transaction ID, if not already */ - trans->transaction_id = transaction_id; + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DCC, LOGL_INFO, "%s(): short read of msgb: %s\n", + __func__, msgb_hexdump(msg)); + return -EINVAL; + } + + msg_type = gh->msg_type & 0xbf; + + /* set transaction ID (flip), if not already */ + trans->transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* pull the MMCC header */ msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); @@ -2146,7 +2227,7 @@ int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg) struct gsm_trans *trans; int rc = 0; - trans = trans_find_by_callref(ms, mmh->ref); + trans = trans_find_by_callref(ms, GSM48_PDISC_CC, mmh->ref); if (!trans) { trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id, mmh->ref); diff --git a/src/host/layer23/src/mobile/gsm48_mm.c b/src/host/layer23/src/mobile/gsm48_mm.c index 49cc2bc6..fad475e2 100644 --- a/src/host/layer23/src/mobile/gsm48_mm.c +++ b/src/host/layer23/src/mobile/gsm48_mm.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -30,18 +26,24 @@ #include <osmocom/core/utils.h> #include <osmocom/gsm/gsm48.h> #include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/utils.h> +#include <osmocom/bb/common/subscriber.h> #include <osmocom/bb/mobile/gsm48_cc.h> #include <osmocom/bb/mobile/gsm480_ss.h> #include <osmocom/bb/mobile/gsm411_sms.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/bb/mobile/app_mobile.h> #include <osmocom/bb/mobile/primitives.h> #include <osmocom/bb/mobile/vty.h> -#include <osmocom/bb/common/utils.h> +#include <osmocom/bb/mobile/gsm48_rr.h> +#include <osmocom/bb/mobile/gsm322.h> extern void *l23_ctx; @@ -59,6 +61,9 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate); static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg); static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg); static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_group_reject(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_uplink_reject(struct osmocom_ms *ms, struct msgb *msg); /* * notes @@ -143,6 +148,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * During LIMITED SERVICE state: (4.2.2.3) * - reject MM connection except for emergency calls * - perform location update, if new LAI is entered + * - indicate GCC/BCC calls with channel description only + * - reject joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description * * * The LOCATION UPDATE NEEDED state is entered if: @@ -175,6 +183,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * - accept MM connection for emergency calls * - trigger location update on any other MM connection * - respond to paging (with IMSI only, because in U2 TMSI is not valid) + * - indicate GCC/BCC calls with channel description only + * - reject joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description * * * The NORMAL SERVICE state is entered if: @@ -184,12 +195,52 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * - and SIM LAI == cell * * During NORMAL SERVICE state: (4.2.2.1) - * - on expirery of T3211 or T3213: Perform location updated - * - on expirery of T3212: Perform location updated + * - on expirery of T3211 or T3213: Perform location update + * - on expirery of T3212: Perform location update * - on change of LAI: Perform location update * - perform IMSI detach * - perform MM connections * - respond to paging + * - indicate GCC/BCC calls with and without channel description + * - accept joining to GCC/BCC calls without channel description + * -> The GCC/BCC layer waits for channel description before joining. + * - accept joining to GCC/BCC calls with channel description + * + * + * The RECEIVING GROUP CALL (NORMAL SERVICE) is entered if: + * - the upper layer requests to join to GCC/BCC call + * - and service state is NORMAL SERVICE + * + * During RECEIVING GROUP CALL (NORMAL SERVICE) state: (4.2.2.7) + * - reject all MM connections + * - indicate notifications and paging to GCC or BCC layer + * -> If supported by RR layer. + * The following events are not be supported here: + * - perform IMSI detach (This is delayed until the call has ended.) + * - on expirery of T3211 or T3213: Perform location update + * - on expirery of T3212: Perform location update + * - accept MM connections + * - on change of LAI: Perform location update + * - accept joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description + * + * + * The RECEIVING GROUP CALL (LIMITED SERVICE) is entered if: + * - the upper layer requests to join to GCC/BCC call + * - and service state is LIMITED SERVICE or ATTEMPTING TO UPDATE + * + * During RECEIVING GROUP CALL (LIMITED SERVICE) state: (4.2.2.8) + * - reject all MM connections + * - indicate notifications and paging to GCC or BCC layer + * -> If supported by RR layer. + * The following events are not be supported here: + * - reject MM connection except for emergency calls + * - on expirery of T3212: Perform location updated + * - reject joining to GCC/BCC calls without channel description + * - accept joining to GCC/BCC calls with channel description + * + * + * A group call is only accepted, if there is no other MM connection ongoing. * * * gsm48_mm_set_plmn_search() is used enter PLMN SEARCH or PLMN SEARCH NORMAL @@ -222,6 +273,33 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); * * gsm48_mm_cell_selected() is used to select the Service state. * + * + * New primitives are invented for group/broadcast calls. They are not + * specified in any recommendation. They are: + * + * - MMxCC_NOTIF_IND: The MM layer indicates new/updated/ceased calls. This is + * completely independent from the state of the MM layer. + * - MMxCC_GROUP_REQ: The GCC/BCC layer requests group channel in receive mode. + * This mode has no MM connection. Speical flags (mm->vgcs*) are used to + * define that mode and store the reference (callref + group|broadcast). This + * reference is used for messages towards GCC/BCC layer. The state is IDLE + * and the sub-state defines group receive mode at normal service or at + * limited service state. + * - MMxCC_GROUP_CNF: The MM layer confirms group channel. + * - MMxCC_UPLINK_REQ: The GCC/BCC layer requests uplink (group transmit mode). + * The MM layer changes to group transmit mode. + * - MMxCC_UPLINK_CNF: The MM layer confirms uplink. (Uplink was granted.) + * - MMxCC_UPLINK_REL_REQ: The GCC/BCC layer requests release of uplink. + * - MMxCC_UPLINK_REL_IND: The MM layer indicates/confirms release of uplink + * + * Existing primitives are used with group calls: + * + * MMxCC_REL_IND: Failed to establish group receive mode. + * MMxCC_ERR_IND: Abort received from RR layer in group receive or transmit mode. + * MMxCC_REL_REQ: Leave group call (receive mode or transmit mode). + * + * The group call is released at MM layer, if one of the primitives above are + * received or transmitted. */ /* @@ -271,46 +349,68 @@ static int decode_network_name(char *name, int name_len, return length; } -/* encode 'mobile identity' */ -int gsm48_encode_mi(uint8_t *buf, struct msgb *msg, struct osmocom_ms *ms, - uint8_t mi_type) +/* Encode and append 'mobile identity' of given type to message, based on current settings. */ +int gsm48_encode_mi_lv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi) { struct gsm_subscriber *subscr = &ms->subscr; struct gsm_settings *set = &ms->settings; - uint8_t *ie; + struct osmo_mobile_identity mi = { }; + int rc; + uint8_t *l; - switch(mi_type) { + /* Copy MI values according to their types. */ + switch (mi_type) { case GSM_MI_TYPE_TMSI: - gsm48_generate_mid_from_tmsi(buf, subscr->tmsi); + mi.tmsi = subscr->tmsi; break; case GSM_MI_TYPE_IMSI: - gsm48_generate_mid_from_imsi(buf, subscr->imsi); + if (emergency_imsi) + OSMO_STRLCPY_ARRAY(mi.imsi, set->emergency_imsi); + else + OSMO_STRLCPY_ARRAY(mi.imsi, subscr->imsi); break; case GSM_MI_TYPE_IMEI: - gsm48_generate_mid_from_imsi(buf, set->imei); + OSMO_STRLCPY_ARRAY(mi.imei, set->imei); break; case GSM_MI_TYPE_IMEISV: - gsm48_generate_mid_from_imsi(buf, set->imeisv); - break; - case GSM_MI_TYPE_NONE: - default: - buf[0] = GSM48_IE_MOBILE_ID; - buf[1] = 1; - buf[2] = 0xf0; + OSMO_STRLCPY_ARRAY(mi.imeisv, set->imeisv); break; } - /* alter MI type */ - buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | mi_type; - if (msg) { - /* MI as LV */ - ie = msgb_put(msg, 1 + buf[1]); - memcpy(ie, buf + 1, 1 + buf[1]); + /* Generate MI or 'NONE', if not available. */ + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + case GSM_MI_TYPE_IMSI: + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + mi.type = mi_type; + l = msgb_put(msg, 1); + rc = osmo_mobile_identity_encode_msgb(msg, &mi, true); + if (rc < 0) { + LOGP(DMM, LOGL_ERROR, "Failed to encode mobile identity type %d. Please fix!\n", mi_type); + *l = 1; + msgb_put_u8(msg, 0xf0 | GSM_MI_TYPE_NONE); + break; + } + *l = rc; + break; + case GSM_MI_TYPE_NONE: + default: + msgb_put_u8(msg, 1); + msgb_put_u8(msg, 0xf0 | mi_type); } return 0; } +int gsm48_encode_mi_tlv(struct osmocom_ms *ms, struct msgb *msg, uint8_t mi_type, bool emergency_imsi) +{ + /* Append IE type. */ + msgb_put_u8(msg, GSM48_IE_MOBILE_ID); + + return gsm48_encode_mi_lv(ms, msg, mi_type, emergency_imsi); +} + /* encode 'classmark 1' */ int gsm48_encode_classmark1(struct gsm48_classmark1 *cm, uint8_t rev_lev, uint8_t es_ind, uint8_t a5_1, uint8_t pwr_lev) @@ -554,6 +654,9 @@ static const struct value_string gsm48_mmevent_names[] = { { GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" }, { GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" }, { GSM48_MM_EVENT_LOST_COVERAGE, "MM_EVENT_LOST_COVERAGE" }, + { GSM48_MM_EVENT_NOTIFICATION, "MM_EVENT_NOTIFICATION" }, + { GSM48_MM_EVENT_UPLINK_FREE, "MM_EVENT_UPLINK_FREE" }, + { GSM48_MM_EVENT_UPLINK_BUSY, "MM_EVENT_UPLINK_BUSY" }, { 0, NULL } }; @@ -638,6 +741,46 @@ static const struct value_string gsm48_mmxx_msg_names[] = { { GSM48_MMSMS_ERR_IND, "MMSMS_ERR_IND" }, { GSM48_MMSMS_PROMPT_IND, "MMSMS_PROMPT_IND" }, { GSM48_MMSMS_PROMPT_REJ, "MMSMS_PROMPT_REJ" }, + { GSM48_MMGCC_EST_REQ, "MMGCC_EST_REQ" }, + { GSM48_MMGCC_EST_CNF, "MMGCC_EST_CNF" }, + { GSM48_MMGCC_REL_REQ, "MMGCC_REL_REQ" }, + { GSM48_MMGCC_REL_IND, "MMGCC_REL_IND" }, + { GSM48_MMGCC_DATA_REQ, "MMGCC_DATA_REQ" }, + { GSM48_MMGCC_DATA_IND, "MMGCC_DATA_IND" }, + { GSM48_MMGCC_UNIT_DATA_REQ, "MMGCC_UNIT_DATA_REQ" }, + { GSM48_MMGCC_UNIT_DATA_IND, "MMGCC_UNIT_DATA_IND" }, + { GSM48_MMGCC_REEST_REQ, "MMBCC_REEST_REQ" }, + { GSM48_MMGCC_REEST_CNF, "MMBCC_REEST_CNF" }, + { GSM48_MMGCC_ERR_IND, "MMGCC_ERR_IND" }, + { GSM48_MMGCC_NOTIF_IND, "MMGCC_NOTIF_IND" }, + { GSM48_MMGCC_GROUP_REQ, "MMGCC_GROUP_REQ" }, + { GSM48_MMGCC_GROUP_CNF, "MMGCC_GROUP_CNF" }, + { GSM48_MMGCC_UPLINK_REQ, "MMGCC_UPLINK_REQ" }, + { GSM48_MMGCC_UPLINK_CNF, "MMGCC_UPLINK_CNF" }, + { GSM48_MMGCC_UPLINK_REL_REQ, "MMGCC_UPLINK_REL_REQ" }, + { GSM48_MMGCC_UPLINK_REL_IND, "MMGCC_UPLINK_REL_CNF" }, + { GSM48_MMGCC_UPLINK_FREE_IND, "MMGCC_UPLINK_FREE_IND" }, + { GSM48_MMGCC_UPLINK_BUSY_IND, "MMGCC_UPLINK_BUSY_IND" }, + { GSM48_MMBCC_EST_REQ, "MMBCC_EST_REQ" }, + { GSM48_MMBCC_EST_CNF, "MMBCC_EST_CNF" }, + { GSM48_MMBCC_REL_REQ, "MMBCC_REL_REQ" }, + { GSM48_MMBCC_REL_IND, "MMBCC_REL_IND" }, + { GSM48_MMBCC_DATA_REQ, "MMBCC_DATA_REQ" }, + { GSM48_MMBCC_DATA_IND, "MMBCC_DATA_IND" }, + { GSM48_MMBCC_UNIT_DATA_REQ, "MMBCC_UNIT_DATA_REQ" }, + { GSM48_MMBCC_UNIT_DATA_IND, "MMBCC_UNIT_DATA_IND" }, + { GSM48_MMBCC_REEST_REQ, "MMBCC_REEST_REQ" }, + { GSM48_MMBCC_REEST_CNF, "MMBCC_REEST_CNF" }, + { GSM48_MMBCC_ERR_IND, "MMBCC_ERR_IND" }, + { GSM48_MMBCC_NOTIF_IND, "MMBCC_NOTIF_IND" }, + { GSM48_MMBCC_GROUP_REQ, "MMBCC_GROUP_REQ" }, + { GSM48_MMBCC_GROUP_CNF, "MMBCC_GROUP_CNF" }, + { GSM48_MMBCC_UPLINK_REQ, "MMBCC_UPLINK_REQ" }, + { GSM48_MMBCC_UPLINK_CNF, "MMBCC_UPLINK_CNF" }, + { GSM48_MMBCC_UPLINK_REL_REQ, "MMBCC_UPLINK_REL_REQ" }, + { GSM48_MMBCC_UPLINK_REL_IND, "MMBCC_UPLINK_REL_CNF" }, + { GSM48_MMBCC_UPLINK_FREE_IND, "MMBCC_UPLINK_FREE_IND" }, + { GSM48_MMBCC_UPLINK_BUSY_IND, "MMBCC_UPLINK_BUSY_IND" }, { 0, NULL } }; @@ -763,6 +906,10 @@ int gsm48_mmxx_dequeue(struct osmocom_ms *ms) case GSM48_MMSMS_CLASS: gsm411_rcv_sms(ms, msg); break; + case GSM48_MMGCC_CLASS: + case GSM48_MMBCC_CLASS: + gsm44068_rcv_gcc_bcc(ms, msg); + break; } msgb_free(msg); work = 1; /* work done */ @@ -864,8 +1011,8 @@ const char *gsm48_mm_state_names[] = { "wait for RR connection active", "MM idle", "wait for additional outgoing MM connection", - "MM_CONN_ACTIVE_VGCS", - "WAIT_RR_CONN_VGCS", + "MM connection active (group transmit mode)", + "wait for RR connection (group transmit mode)", "location updating pending", "IMSI detach pending", "RR connection release not allowed" @@ -881,8 +1028,8 @@ const char *gsm48_mm_substate_names[] = { "location updating needed", "PLMN search", "PLMN search (normal)", - "RX_VGCS_NORMAL", - "RX_VGCS_LIMITED" + "receiving group call (normal service)", + "receiving group call (limiteed service)" }; /* change state from LOCATION UPDATE NEEDED to ATTEMPTING TO UPDATE */ @@ -925,34 +1072,39 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) && (mm->state != GSM48_MM_ST_MM_IDLE || mm->substate != substate)) { switch (substate) { case GSM48_MM_SST_NORMAL_SERVICE: - vty_notify(ms, NULL); - vty_notify(ms, "On Network, normal service: %s, %s\n", - gsm_get_mcc(plmn->mcc), - gsm_get_mnc(plmn->mcc, plmn->mnc)); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "On Network, normal service: %s, %s\n", + gsm_get_mcc(plmn->plmn.mcc), + gsm_get_mnc(&plmn->plmn)); break; case GSM48_MM_SST_LIMITED_SERVICE: - vty_notify(ms, NULL); - vty_notify(ms, "Limited service, emergency calls are " + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Limited service, emergency calls are " "possible.\n"); break; case GSM48_MM_SST_PLMN_SEARCH_NORMAL: case GSM48_MM_SST_PLMN_SEARCH: - vty_notify(ms, NULL); - vty_notify(ms, "Searching network...\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Searching network...\n"); break; case GSM48_MM_SST_NO_IMSI: - vty_notify(ms, NULL); - vty_notify(ms, "No SIM, emergency calls are " + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No SIM, emergency calls are " "possible.\n"); break; case GSM48_MM_SST_NO_CELL_AVAIL: - vty_notify(ms, NULL); - vty_notify(ms, "No service.\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No service.\n"); break; case GSM48_MM_SST_ATTEMPT_UPDATE: - vty_notify(ms, NULL); - vty_notify(ms, "Trying to registering with " - "network...\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Trying to register with network %s, %s...\n", + gsm_get_mcc(plmn->plmn.mcc), gsm_get_mnc(&plmn->plmn)); + break; + case GSM48_MM_SST_RX_VGCS_NORMAL: + case GSM48_MM_SST_RX_VGCS_LIMITED: + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Listening to %s call.\n", (mm->vgcs.group_call) ? "group" : "broadcast"); break; } } @@ -975,6 +1127,8 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) if (!nmsg) return; gsm48_mmevent_msg(ms, nmsg); + /* Stop here and wait for the IMSI_DETACH event being handled. */ + return; } /* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */ @@ -1021,6 +1175,15 @@ static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) /* must exit, because this function can be called recursively */ return; } + + /* Tell the group call that we are ready for a new channel activation. */ + if (state == GSM48_MM_ST_MM_IDLE + && (substate == GSM48_MM_SST_NORMAL_SERVICE + || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) { + gsm44068_rcv_mm_idle(ms); + /* must exit, because this function can be called recursively */ + return; + } } /* return PLMN SEARCH or PLMN SEARCH NORMAL state */ @@ -1042,7 +1205,7 @@ static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms) "SIM not updated.\n"); return GSM48_MM_SST_PLMN_SEARCH; } - if (subscr->lac == 0x0000 || subscr->lac >= 0xfffe) { + if (subscr->lai.lac == 0x0000 || subscr->lai.lac >= 0xfffe) { LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " "LAI in SIM not valid.\n"); return GSM48_MM_SST_PLMN_SEARCH; @@ -1056,15 +1219,12 @@ static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms) } /* selected cell's LAI not equal to LAI stored on the sim */ - if (cs->sel_mcc != subscr->mcc - || cs->sel_mnc != subscr->mnc - || cs->sel_lac != subscr->lac) { + if (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) != 0) { LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " - "LAI of selected cell (MCC %s MNC %s LAC 0x%04x) " - "!= LAI in SIM (MCC %s MNC %s LAC 0x%04x).\n", - gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc), - cs->sel_lac, gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac); + "LAI of selected cell (MCC-MNC %s LAC 0x%04x) " + "!= LAI in SIM (MCC-MNC %s LAC 0x%04x).\n", + osmo_plmn_name(&cs->sel_cgi.lai.plmn), cs->sel_cgi.lai.lac, + osmo_plmn_name2(&subscr->lai.plmn), subscr->lai.lac); return GSM48_MM_SST_PLMN_SEARCH; } @@ -1081,6 +1241,14 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; + /* When we are in group transmit mode, we return to group receive mode. */ + if (mm->vgcs.enabled) { + LOGP(DMM, LOGL_INFO, "Return to group receive mode.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_RX_VGCS_LIMITED); + return 0; + } + if (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL) { LOGP(DMM, LOGL_INFO, "Not camping, wait for CS process to " @@ -1120,10 +1288,8 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) /* if we are attached and selected cell equals the registered LAI */ if (subscr->imsi_attached - && subscr->lac /* valid */ - && cs->sel_mcc == subscr->mcc - && cs->sel_mnc == subscr->mnc - && cs->sel_lac == subscr->lac) { + && subscr->lai.lac /* valid */ + && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0)) { LOGP(DMM, LOGL_INFO, "We are in registered LAI as returning " "to MM IDLE\n"); /* if SIM not updated (abnormal case as described in 4.4.4.9) */ @@ -1131,8 +1297,7 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_ATTEMPT_UPDATE); else - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE); return 0; } @@ -1140,27 +1305,22 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) if (cs->state == GSM322_C3_CAMPED_NORMALLY) { LOGP(DMM, LOGL_INFO, "We are camping normally as returning to " "MM IDLE\n"); - if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, - cs->sel_mnc)) { + if (gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)) { /* location update not allowed */ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE); } else - if (gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, - cs->sel_lac)) { + if (gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai)) { /* location update not allowed */ LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE); } else /* 4.4.4.9 if cell is barred, don't start */ if ((!subscr->acc_barr && s->cell_barr) || (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) & (s->class_barr ^ 0xffff)))) { LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE); } else { /* location update allowed */ LOGP(DMM, LOGL_INFO, "Loc. upd. allowed.\n"); @@ -1171,8 +1331,7 @@ static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) /* location update not allowed */ LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning " "to MM IDLE\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE); } return 0; @@ -1200,6 +1359,13 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_sysinfo *s = &cs->sel_si; struct gsm_settings *set = &ms->settings; + if (mm->vgcs.enabled) { + LOGP(DMM, LOGL_ERROR, "Cell selection in group receive mode, this should not happen.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_RX_VGCS_LIMITED); + return 0; + } + /* no SIM is inserted */ if (!subscr->sim_valid) { LOGP(DMM, LOGL_INFO, "SIM invalid as cell is selected.\n"); @@ -1210,17 +1376,14 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) /* SIM not updated in this LA */ if (subscr->ustate == GSM_SIM_U1_UPDATED - && subscr->lac /* valid */ - && cs->sel_mcc == subscr->mcc - && cs->sel_mnc == subscr->mnc - && cs->sel_lac == subscr->lac + && subscr->lai.lac /* valid */ + && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0) && !mm->lupd_periodic) { if (subscr->imsi_attached) { struct msgb *nmsg; LOGP(DMM, LOGL_INFO, "Valid in location area.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); @@ -1234,8 +1397,7 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; LOGP(DMM, LOGL_INFO, "Attachment not required.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); @@ -1251,14 +1413,12 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) /* PLMN mode auto and selected cell is forbidden */ if (set->plmn_mode == PLMN_MODE_AUTO && (!cs->selected - || gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc) - || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, - cs->sel_lac))) { + || gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn) + || gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai))) { struct msgb *nmsg; LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); @@ -1273,14 +1433,12 @@ static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) * in M3 state the PLMN is not selected for registration. */ if (set->plmn_mode == PLMN_MODE_MANUAL && (!cs->selected - || plmn->mcc != cs->sel_mcc - || plmn->mnc != cs->sel_mnc + || (osmo_plmn_cmp(&plmn->plmn, &cs->sel_cgi.lai.plmn) != 0) || plmn->state == GSM322_M3_NOT_ON_PLMN)) { struct msgb *nmsg; LOGP(DMM, LOGL_INFO, "Selected cell not found.\n"); - new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_LIMITED_SERVICE); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LIMITED_SERVICE); /* send message to PLMN search process */ nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); @@ -1388,9 +1546,8 @@ uint32_t mm_conn_new_ref = 0x80000001; /* new MM connection state */ static void new_conn_state(struct gsm48_mm_conn *conn, int state) { - LOGP(DMM, LOGL_INFO, "(ref %x) new state %s -> %s\n", conn->ref, - gsm48_mmxx_state_names[conn->state], - gsm48_mmxx_state_names[state]); + LOGP(DMM, LOGL_INFO, "(ref 0x%x proto %d) new state %s -> %s\n", conn->ref, conn->protocol, + gsm48_mmxx_state_names[conn->state], gsm48_mmxx_state_names[state]); conn->state = state; } @@ -1409,13 +1566,35 @@ struct gsm48_mm_conn *mm_conn_by_id(struct gsm48_mmlayer *mm, } /* find MM connection by reference */ -struct gsm48_mm_conn *mm_conn_by_ref(struct gsm48_mmlayer *mm, - uint32_t ref) +struct gsm48_mm_conn *mm_conn_by_ref_and_class(struct gsm48_mmlayer *mm, + uint32_t ref, uint16_t cls) { struct gsm48_mm_conn *conn; + uint8_t protocol; + + switch (cls) { + case GSM48_MMCC_CLASS: + protocol = GSM48_PDISC_CC; + break; + case GSM48_MMSS_CLASS: + protocol = GSM48_PDISC_NC_SS; + break; + case GSM48_MMSMS_CLASS: + protocol = GSM48_PDISC_SMS; + break; + case GSM48_MMGCC_CLASS: + protocol = GSM48_PDISC_GROUP_CC; + break; + case GSM48_MMBCC_CLASS: + protocol = GSM48_PDISC_BCAST_CC; + break; + default: + LOGP(DMM, LOGL_ERROR, "Invalid message class 0x%03x. Please fix!", cls); + return NULL; + } llist_for_each_entry(conn, &mm->mm_conn, list) { - if (conn->ref == ref) + if (conn->ref == ref && conn->protocol == protocol) return conn; } return NULL; @@ -1465,6 +1644,7 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any, struct gsm48_mm_conn *conn, *conn2; struct msgb *nmsg; struct gsm48_mmxx_hdr *nmmh; + int msg_type; /* Note: For SAPI 0 all connections are released */ @@ -1480,35 +1660,44 @@ static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any, /* abort any OR the pending connection */ if ((abort_any || conn->state == GSM48_MMXX_ST_CONN_PEND) && (sapi == conn->sapi || sapi == 0)) { - /* send MMxx-REL-IND */ - nmsg = NULL; + /* send MMXX-REL-IND or MMXX-ERR-IND */ switch(conn->protocol) { case GSM48_PDISC_CC: - nmsg = gsm48_mmxx_msgb_alloc( - error ? GSM48_MMCC_ERR_IND - : GSM48_MMCC_REL_IND, conn->ref, - conn->transaction_id, - conn->sapi); + msg_type = (error) ? GSM48_MMCC_ERR_IND + : GSM48_MMCC_REL_IND; break; case GSM48_PDISC_NC_SS: - nmsg = gsm48_mmxx_msgb_alloc( - error ? GSM48_MMSS_ERR_IND - : GSM48_MMSS_REL_IND, conn->ref, - conn->transaction_id, - conn->sapi); + msg_type = (error) ? GSM48_MMSS_ERR_IND + : GSM48_MMSS_REL_IND; break; case GSM48_PDISC_SMS: - nmsg = gsm48_mmxx_msgb_alloc( - error ? GSM48_MMSMS_ERR_IND - : GSM48_MMSMS_REL_IND, conn->ref, - conn->transaction_id, - conn->sapi); + msg_type = (error) ? GSM48_MMSMS_ERR_IND + : GSM48_MMSMS_REL_IND; + break; + case GSM48_PDISC_GROUP_CC: + msg_type = (error) ? GSM48_MMGCC_ERR_IND + : GSM48_MMGCC_REL_IND; break; + case GSM48_PDISC_BCAST_CC: + msg_type = (error) ? GSM48_MMBCC_ERR_IND + : GSM48_MMBCC_REL_IND; + break; + default: + msg_type = -1; } - if (!nmsg) { + if (msg_type == -1) { /* this should not happen */ + LOGP(DMM, LOGL_ERROR, "MM connection of " + "unsupported protocol? Please fix!\n"); + mm_conn_free(conn); + continue; + } + nmsg = gsm48_mmxx_msgb_alloc(msg_type, conn->ref, + conn->transaction_id, + conn->sapi); + if (!nmsg) { mm_conn_free(conn); - continue; /* skip if not of CC type */ + continue; } nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; nmmh->cause = cause; @@ -1584,7 +1773,7 @@ static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } /* LAI */ - gsm48_decode_lai_hex(lai, &subscr->mcc, &subscr->mnc, &subscr->lac); + gsm48_decode_lai2(lai, &subscr->lai); /* MI */ mi = gh->data + sizeof(struct gsm48_loc_area_id); mi_type = mi[1] & GSM_MI_TYPE_MASK; @@ -1600,12 +1789,12 @@ static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg) gsm48_mm_tx_tmsi_reall_cpl(ms); break; case GSM_MI_TYPE_IMSI: - subscr->tmsi = 0xffffffff; + subscr->tmsi = GSM_RESERVED_TMSI; LOGP(DMM, LOGL_INFO, "TMSI removed.\n"); gsm48_mm_tx_tmsi_reall_cpl(ms); break; default: - subscr->tmsi = 0xffffffff; + subscr->tmsi = GSM_RESERVED_TMSI; LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown MI " "type %d.\n", mi_type); gsm48_mm_tx_mm_status(ms, GSM48_REJECT_INCORRECT_MESSAGE); @@ -1697,8 +1886,8 @@ static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg) subscr->sim_valid = 0; /* TMSI and LAI invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; + subscr->tmsi = GSM_RESERVED_TMSI; + subscr->lai.lac = 0x0000; /* key is invalid */ subscr->key_seq = 7; @@ -1754,7 +1943,7 @@ static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg) return gsm48_mm_tx_mm_status(ms, GSM48_REJECT_MSG_NOT_COMPATIBLE); } - if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == 0xffffffff) { + if (mi_type == GSM_MI_TYPE_TMSI && subscr->tmsi == GSM_RESERVED_TMSI) { LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST of TMSI, but we have no " "TMSI\n"); return gsm48_mm_tx_mm_status(ms, @@ -1769,7 +1958,6 @@ static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type) { struct msgb *nmsg; struct gsm48_hdr *ngh; - uint8_t buf[11]; LOGP(DMM, LOGL_INFO, "IDENTITY RESPONSE\n"); @@ -1781,8 +1969,8 @@ static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type) ngh->proto_discr = GSM48_PDISC_MM; ngh->msg_type = GSM48_MT_MM_ID_RESP; - /* MI */ - gsm48_encode_mi(buf, nmsg, ms, mi_type); + /* MI (LV) */ + gsm48_encode_mi_lv(ms, nmsg, mi_type, false); /* push RR header and send down */ return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0, 0); @@ -1800,7 +1988,6 @@ static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim) struct msgb *nmsg; struct gsm48_hdr *ngh; uint8_t pwr_lev; - uint8_t buf[11]; struct gsm48_classmark1 cm; @@ -1821,13 +2008,13 @@ static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim) pwr_lev = gsm48_current_pwr_lev(set, rr->cd_now.arfcn); gsm48_encode_classmark1(&cm, sup->rev_lev, sup->es_ind, set->a5_1, pwr_lev); - msgb_v_put(nmsg, *((uint8_t *)&cm)); - /* MI */ - if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */ - gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_TMSI); + msgb_v_put(nmsg, *((uint8_t *)&cm)); + /* MI (LV) */ + if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */ + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi); } else { - gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_IMSI); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi); } @@ -1962,6 +2149,34 @@ static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, struct msgb *msg) return gsm48_mm_imsi_detach_sent(ms, msg); } +/* Detach during VGCS. Queue and return idle. */ +static int gsm48_mm_imsi_detach_vgcs(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + int msg_type; + + LOGP(DMM, LOGL_INFO, "IMSI detach delayed until group receive/transmit mode is left.\n"); + + /* remember to detach later */ + mm->delay_detach = 1; + + /* Release group call. */ + gsm48_mm_group_rel_req(ms, msg); + + /* Release message to GCC/BCC layer */ + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND; + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + /* ignore ongoing IMSI detach */ static int gsm48_mm_imsi_detach_ignore(struct osmocom_ms *ms, struct msgb *msg) { @@ -2016,8 +2231,8 @@ static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg) subscr->sim_valid = 0; /* TMSI and LAI invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; + subscr->tmsi = GSM_RESERVED_TMSI; + subscr->lai.lac = 0x0000; /* key is invalid */ subscr->key_seq = 7; @@ -2044,11 +2259,13 @@ static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg) struct tlv_parsed tp; if (payload_len < 0) { - LOGP(DMM, LOGL_NOTICE, "Short read of MM INFORMATION message " - "error.\n"); + LOGP(DMM, LOGL_ERROR, "Short read of MM INFORMATION message\n"); + return -EINVAL; + } + if (tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0) < 0) { + LOGP(DMM, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); return -EINVAL; } - tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0); /* long name */ if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) { @@ -2078,6 +2295,8 @@ static int gsm48_mm_sysinfo(struct osmocom_ms *ms, struct msgb *msg) if (mm->state == GSM48_MM_ST_MM_IDLE && (mm->substate == GSM48_MM_SST_NO_CELL_AVAIL || mm->substate == GSM48_MM_SST_LIMITED_SERVICE + || mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL + || mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED || mm->substate == GSM48_MM_SST_PLMN_SEARCH || mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL)) return 0; @@ -2169,20 +2388,18 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) "forbidden PLMN.\n"); LOGP(DSUM, LOGL_INFO, "Location updating is disabled by " "configuration\n"); - gsm_subscr_add_forbidden_plmn(subscr, cs->sel_mcc, - cs->sel_mnc, GSM48_REJECT_PLMN_NOT_ALLOWED); + gsm_subscr_add_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn, GSM48_REJECT_PLMN_NOT_ALLOWED); msg_type = GSM322_EVENT_REG_FAILED; goto _stop; } /* if LAI is forbidden, don't start */ - if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) { + if (gsm_subscr_is_forbidden_plmn(subscr, &cs->sel_cgi.lai.plmn)) { LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); msg_type = GSM322_EVENT_REG_FAILED; goto stop; } - if (gsm322_is_forbidden_la(ms, cs->sel_mcc, - cs->sel_mnc, cs->sel_lac)) { + if (gsm322_is_forbidden_la(ms, &cs->sel_cgi.lai)) { LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); msg_type = GSM322_EVENT_REG_FAILED; goto stop; @@ -2197,13 +2414,9 @@ static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) goto stop; } - mm->lupd_mcc = cs->sel_mcc; - mm->lupd_mnc = cs->sel_mnc; - mm->lupd_lac = cs->sel_lac; + memcpy(&mm->lupd_lai, &cs->sel_cgi.lai, sizeof(mm->lupd_lai)); - LOGP(DSUM, LOGL_INFO, "Perform location update (MCC %s, MNC %s " - "LAC 0x%04x)\n", gsm_print_mcc(mm->lupd_mcc), - gsm_print_mnc(mm->lupd_mnc), mm->lupd_lac); + LOGP(DSUM, LOGL_INFO, "Perform location update (LAI=%s)\n", osmo_lai_name(&mm->lupd_lai)); return gsm48_mm_tx_loc_upd_req(ms); } @@ -2215,6 +2428,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) struct gsm_subscriber *subscr = &ms->subscr; struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = &cs->sel_si; + bool vgcs = mm->vgcs.enabled; struct msgb *nmsg; /* in case we already have a location update going on */ @@ -2246,17 +2460,16 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) /* if location update is not required */ if (subscr->ustate == GSM_SIM_U1_UPDATED && cs->selected - && cs->sel_mcc == subscr->mcc - && cs->sel_mnc == subscr->mnc - && cs->sel_lac == subscr->lac + && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0) && (subscr->imsi_attached || !s->att_allowed)) { LOGP(DMM, LOGL_INFO, "Loc. upd. not required.\n"); - subscr->imsi_attached = 1; + subscr->imsi_attached = true; /* go straight to normal service state */ new_mm_state(mm, GSM48_MM_ST_MM_IDLE, - GSM48_MM_SST_NORMAL_SERVICE); + (vgcs) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_NORMAL_SERVICE); #if 0 /* don't send message, if we got not triggered by PLMN search */ @@ -2276,9 +2489,7 @@ static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) /* 4.4.3 is attachment required? */ if (subscr->ustate == GSM_SIM_U1_UPDATED && cs->selected - && cs->sel_mcc == subscr->mcc - && cs->sel_mnc == subscr->mnc - && cs->sel_lac == subscr->lac + && (osmo_lai_cmp(&cs->sel_cgi.lai, &subscr->lai) == 0) && !subscr->imsi_attached && s->att_allowed) { /* do location update for IMSI attach */ @@ -2335,7 +2546,6 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms) struct gsm48_hdr *ngh; struct gsm48_loc_upd_req *nlu; /* NOTE: mi_len is part of struct */ uint8_t pwr_lev; - uint8_t buf[11]; LOGP(DMM, LOGL_INFO, "LOCATION UPDATING REQUEST\n"); @@ -2343,7 +2553,8 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms) if (!nmsg) return -ENOMEM; ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); - nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu)); + /* Do not add mi_len to the message, this is done at gsm48_encode_mi_lv(). */ + nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu) - 1); ngh->proto_discr = GSM48_PDISC_MM; ngh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST; @@ -2356,24 +2567,20 @@ static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms) * * NOTE: The TMSI is only valid within a LAI! */ - gsm48_encode_lai_hex(&nlu->lai, subscr->mcc, subscr->mnc, subscr->lac); - LOGP(DMM, LOGL_INFO, " using LAI (mcc %s mnc %s " "lac 0x%04x)\n", - gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac); + gsm48_generate_lai2(&nlu->lai, &subscr->lai); + LOGP(DMM, LOGL_INFO, " using LAI=%s\n", osmo_lai_name(&subscr->lai)); /* classmark 1 */ pwr_lev = gsm48_current_pwr_lev(set, cs->sel_arfcn); gsm48_encode_classmark1(&nlu->classmark1, sup->rev_lev, sup->es_ind, set->a5_1, pwr_lev); - /* MI */ - if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */ - gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI); + /* MI (LV) */ + if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */ + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); LOGP(DMM, LOGL_INFO, " using TMSI 0x%08x\n", subscr->tmsi); } else { - gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); LOGP(DMM, LOGL_INFO, " using IMSI %s\n", subscr->imsi); } - msgb_put(nmsg, buf[1]); /* length is part of nlu */ - memcpy(&nlu->mi_len, buf + 1, 1 + buf[1]); new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_LUPD, 0); @@ -2406,15 +2613,17 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) struct tlv_parsed tp; struct msgb *nmsg; - if (payload_len < sizeof(struct gsm48_loc_area_id)) { - short_read: - LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING ACCEPT " - "message error.\n"); + if (payload_len < sizeof(*lai)) { +short_read: + LOGP(DMM, LOGL_ERROR, "Short read of LOCATION UPDATING ACCEPT message\n"); + return -EINVAL; + } + if (tlv_parse(&tp, &gsm48_mm_att_tlvdef, + gh->data + sizeof(*lai), + payload_len - sizeof(*lai), 0, 0) < 0) { + LOGP(DMM, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); return -EINVAL; } - tlv_parse(&tp, &gsm48_mm_att_tlvdef, - gh->data + sizeof(struct gsm48_loc_area_id), - payload_len - sizeof(struct gsm48_loc_area_id), 0, 0); /* update has finished */ mm->lupd_pending = 0; @@ -2426,7 +2635,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) stop_mm_t3212(mm); /* 4.4.2 */ /* LAI */ - gsm48_decode_lai_hex(lai, &subscr->mcc, &subscr->mnc, &subscr->lac); + gsm48_decode_lai2(lai, &subscr->lai); /* stop location update timer */ stop_mm_t3210(mm); @@ -2435,7 +2644,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) mm->lupd_attempt = 0; /* mark SIM as attached */ - subscr->imsi_attached = 1; + subscr->imsi_attached = true; /* set the status in the sim to updated */ new_sim_ustate(subscr, GSM_SIM_U1_UPDATED); @@ -2444,19 +2653,17 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) gsm_subscr_write_loci(ms); /* set last registered PLMN */ - if (subscr->lac > 0x0000 && subscr->lac < 0xfffe) { + if (subscr->lai.lac > 0x0000 && subscr->lai.lac < 0xfffe) { subscr->plmn_valid = 1; - subscr->plmn_mcc = subscr->mcc; - subscr->plmn_mnc = subscr->mnc; + memcpy(&subscr->plmn, &subscr->lai.plmn, sizeof(struct osmo_plmn_id)); } LOGP(DSUM, LOGL_INFO, "Location update accepted\n"); - LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (mcc %s mnc %s " - "lac 0x%04x)\n", gsm_print_mcc(subscr->mcc), - gsm_print_mnc(subscr->mnc), subscr->lac); + LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (lai=%s)\n", + osmo_lai_name(&subscr->lai)); /* remove LA from forbidden list */ - gsm322_del_forbidden_la(ms, subscr->mcc, subscr->mnc, subscr->lac); + gsm322_del_forbidden_la(ms, &subscr->lai); /* MI */ if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) { @@ -2486,7 +2693,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) break; case GSM_MI_TYPE_IMSI: LOGP(DMM, LOGL_INFO, "TMSI removed\n"); - subscr->tmsi = 0xffffffff; + subscr->tmsi = GSM_RESERVED_TMSI; /* store LOCI on sim */ gsm_subscr_write_loci(ms); @@ -2507,7 +2714,7 @@ static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) gsm322_plmn_sendmsg(ms, nmsg); /* follow on proceed */ - if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) + if (TLVP_PRESENT(&tp, GSM48_IE_FOLLOW_ON_PROC)) LOGP(DMM, LOGL_NOTICE, "follow-on proceed not supported.\n"); /* start RR release timer */ @@ -2581,8 +2788,8 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) case GSM48_REJECT_LOC_NOT_ALLOWED: case GSM48_REJECT_ROAMING_NOT_ALLOWED: /* TMSI and LAI invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; + subscr->tmsi = GSM_RESERVED_TMSI; + subscr->lai.lac = 0x0000; /* key is invalid */ subscr->key_seq = 7; @@ -2631,15 +2838,13 @@ static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal ME)\n"); break; case GSM48_REJECT_PLMN_NOT_ALLOWED: - gsm_subscr_add_forbidden_plmn(subscr, mm->lupd_mcc, - mm->lupd_mnc, mm->lupd_rej_cause); + gsm_subscr_add_forbidden_plmn(subscr, &mm->lupd_lai.plmn, mm->lupd_rej_cause); LOGP(DSUM, LOGL_INFO, "Location update failed (PLMN not " "allowed)\n"); break; case GSM48_REJECT_LOC_NOT_ALLOWED: case GSM48_REJECT_ROAMING_NOT_ALLOWED: - gsm322_add_forbidden_la(ms, mm->lupd_mcc, mm->lupd_mnc, - mm->lupd_lac, mm->lupd_rej_cause); + gsm322_add_forbidden_la(ms, &mm->lupd_lai, mm->lupd_rej_cause); LOGP(DSUM, LOGL_INFO, "Location update failed (LAI not " "allowed)\n"); break; @@ -2686,9 +2891,7 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg) stop_mm_t3210(mm); if (subscr->ustate == GSM_SIM_U1_UPDATED - && mm->lupd_mcc == subscr->mcc - && mm->lupd_mnc == subscr->mnc - && mm->lupd_lac == subscr->lac) { + && (osmo_lai_cmp(&mm->lupd_lai, &subscr->lai) == 0)) { if (mm->lupd_attempt < 4) { LOGP(DSUM, LOGL_INFO, "Try location update later\n"); LOGP(DMM, LOGL_INFO, "Loc. upd. failed, retry #%d\n", @@ -2704,8 +2907,8 @@ static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg) } /* TMSI and LAI invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; + subscr->tmsi = GSM_RESERVED_TMSI; + subscr->lai.lac = 0x0000; /* key is invalid */ subscr->key_seq = 7; @@ -2797,7 +3000,6 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim, struct gsm48_hdr *ngh; struct gsm48_service_request *nsr; /* NOTE: includes MI length */ uint8_t *cm2lv; - uint8_t buf[11]; LOGP(DMM, LOGL_INFO, "CM SERVICE REQUEST (cause %d)\n", mm->est_cause); @@ -2805,7 +3007,8 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim, if (!nmsg) return -ENOMEM; ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); - nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr)); + /* Do not add mi_len to the message, this is done at gsm48_encode_mi_lv(). */ + nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr) - 1); cm2lv = (uint8_t *)&nsr->classmark; ngh->proto_discr = GSM48_PDISC_MM; @@ -2823,23 +3026,21 @@ static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim, if (mm->est_cause == RR_EST_CAUSE_EMERGENCY && set->emergency_imsi[0]) { LOGP(DMM, LOGL_INFO, "-> Using IMSI %s for emergency\n", set->emergency_imsi); - gsm48_generate_mid_from_imsi(buf, set->emergency_imsi); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, true); } else if (!subscr->sim_valid) { /* have no SIM ? */ LOGP(DMM, LOGL_INFO, "-> Using IMEI %s\n", set->imei); - gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMEI); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMEI, false); } else - if (subscr->tmsi != 0xffffffff) { /* have TMSI ? */ - gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI); + if (subscr->tmsi != GSM_RESERVED_TMSI) { /* have TMSI ? */ + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); LOGP(DMM, LOGL_INFO, "-> Using TMSI\n"); } else { - gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); LOGP(DMM, LOGL_INFO, "-> Using IMSI %s\n", subscr->imsi); } - msgb_put(nmsg, buf[1]); /* length is part of nsr */ - memcpy(&nsr->mi_len, buf + 1, 1 + buf[1]); /* prio is optional for eMLPP */ /* push RR header and send down */ @@ -2912,8 +3113,8 @@ static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg) abort_any = 1; /* TMSI and LAI invalid */ - subscr->tmsi = 0xffffffff; - subscr->lac = 0x0000; + subscr->tmsi = GSM_RESERVED_TMSI; + subscr->lai.lac = 0x0000; /* key is invalid */ subscr->key_seq = 7; @@ -2989,21 +3190,10 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg, */ sapi = conn_found->sapi; reject: - nmsg = NULL; - switch(msg_type) { - case GSM48_MMCC_EST_REQ: - nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, - mmh->ref, mmh->transaction_id, sapi); - break; - case GSM48_MMSS_EST_REQ: - nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, - mmh->ref, mmh->transaction_id, sapi); - break; - case GSM48_MMSMS_EST_REQ: - nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, - mmh->ref, mmh->transaction_id, sapi); - break; - } + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMXX_REL_IND | + (msg_type & GSM48_MMXX_MASK), + mmh->ref, mmh->transaction_id, + sapi); if (!nmsg) return -ENOMEM; nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; @@ -3077,6 +3267,16 @@ static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg, cm_serv = GSM48_CMSERV_SMS; proto = GSM48_PDISC_SMS; break; + case GSM48_MMGCC_EST_REQ: + cause = RR_EST_CAUSE_OTHER_SDCCH; + cm_serv = GSM48_CMSERV_VGCS; + proto = GSM48_PDISC_GROUP_CC; + break; + case GSM48_MMBCC_EST_REQ: + cause = RR_EST_CAUSE_OTHER_SDCCH; + cm_serv = GSM48_CMSERV_VBS; + proto = GSM48_PDISC_BCAST_CC; + break; } /* create MM connection instance */ @@ -3212,21 +3412,10 @@ static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mmxx_hdr *nmmh; /* reject */ - nmsg = NULL; - switch(msg_type) { - case GSM48_MMCC_EST_REQ: - nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref, - mmh->transaction_id, sapi); - break; - case GSM48_MMSS_EST_REQ: - nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref, - mmh->transaction_id, sapi); - break; - case GSM48_MMSMS_EST_REQ: - nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref, - mmh->transaction_id, sapi); - break; - } + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMXX_REL_IND | + (msg_type & GSM48_MMXX_MASK), + mmh->ref, mmh->transaction_id, + sapi); if (!nmsg) return -ENOMEM; nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; @@ -3292,6 +3481,16 @@ static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms) conn_found->ref, conn_found->transaction_id, conn_found->sapi); break; + case GSM48_PDISC_GROUP_CC: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMGCC_EST_CNF, + conn_found->ref, conn_found->transaction_id, + conn_found->sapi); + break; + case GSM48_PDISC_BCAST_CC: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMBCC_EST_CNF, + conn_found->ref, conn_found->transaction_id, + conn_found->sapi); + break; } if (!nmsg) return -ENOMEM; @@ -3358,6 +3557,7 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_mmlayer *mm = &ms->mmlayer; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + uint32_t msg_type = rrh->msg_type; int cause; /* stop RR release timer */ @@ -3377,11 +3577,13 @@ static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg) cause = 47; } + LOGP(DMM, LOGL_INFO, "Aborting connection with cause %d\n", cause); + /* stop MM connection timer */ stop_mm_t3230(mm); /* release all connections */ - gsm48_mm_release_mm_conn(ms, 1, cause, 1, 0); + gsm48_mm_release_mm_conn(ms, 1, cause, (msg_type == GSM48_RR_ABORT_IND), 0); /* return to MM IDLE */ return gsm48_mm_return_idle(ms, NULL); @@ -3425,37 +3627,32 @@ static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; struct gsm48_mm_conn *conn; int msg_type = mmh->msg_type; + uint8_t sapi; - /* get connection, if not exist (anymore), release */ - conn = mm_conn_by_ref(mm, mmh->ref); - if (!conn) { - LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already " - "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref); - switch(msg_type & GSM48_MMXX_MASK) { - case GSM48_MMCC_CLASS: - mmh->msg_type = GSM48_MMCC_REL_IND; - break; - case GSM48_MMSS_CLASS: - mmh->msg_type = GSM48_MMSS_REL_IND; - break; - case GSM48_MMSMS_CLASS: - mmh->msg_type = GSM48_MMSMS_REL_IND; - break; - } - mmh->cause = 31; + if (mm->state == GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) { + /* Group transmit mode has no MM connection. */ + sapi = 0; + } else { + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); + if (!conn) { + LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already " + "released) ref=%x, sending MMXX_REL_IND\n", mmh->ref); + mmh->msg_type = GSM48_MMXX_REL_IND | (msg_type & GSM48_MMXX_MASK); + mmh->cause = 31; - /* mirror message with REL_IND + cause */ - return gsm48_mmxx_upmsg(ms, msg); + /* mirror message with REL_IND + cause */ + return gsm48_mmxx_upmsg(ms, msg); + } + /* set SAPI, if upper layer does not do it correctly */ + sapi = conn->sapi; } - /* set SAPI, if upper layer does not do it correctly */ - mmh->sapi = conn->sapi; - /* pull MM header */ msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); /* push RR header and send down */ - return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, conn->sapi, 0); + return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, sapi, 0); } /* release of MM connection (active state) */ @@ -3466,7 +3663,7 @@ static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mm_conn *conn; /* get connection, if not exist (anymore), release */ - conn = mm_conn_by_ref(mm, mmh->ref); + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); if (conn) mm_conn_free(conn); @@ -3490,7 +3687,7 @@ static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mm_conn *conn; /* get connection, if not exist (anymore), release */ - conn = mm_conn_by_ref(mm, mmh->ref); + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); if (conn) mm_conn_free(conn); @@ -3505,7 +3702,7 @@ static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mm_conn *conn; /* get connection, if not exist (anymore), release */ - conn = mm_conn_by_ref(mm, mmh->ref); + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); if (conn) mm_conn_free(conn); @@ -3534,7 +3731,7 @@ static int gsm48_mm_release_wait_rr(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_mm_conn *conn; /* get connection, if not exist (anymore), release */ - conn = mm_conn_by_ref(mm, mmh->ref); + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); if (conn) mm_conn_free(conn); @@ -3562,6 +3759,351 @@ static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg) return gsm48_mm_return_idle(ms, NULL); } +/* The RR indicates notification of ongoing VGCS/VBS calls. */ +static int gsm48_mm_notification(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mm_event *mme = (struct gsm48_mm_event *)msg->data; + uint32_t ref = osmo_load32be(mme->notification.gcr) >> 5; + uint16_t msg_type = (mme->notification.gcr[3] & 0x10) ? GSM48_MMGCC_NOTIF_IND : GSM48_MMBCC_NOTIF_IND; + struct gsm48_mmxx_hdr *mmh; + struct msgb *nmsg; + + /* Notification message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, ref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + mmh = (struct gsm48_mmxx_hdr *)nmsg->data; + mmh->notify = (mme->notification.gone) ? MMXX_NOTIFY_RELEASE : MMXX_NOTIFY_SETUP; + mmh->ch_desc_present = mme->notification.ch_desc_present; + memcpy(&mmh->ch_desc, &mme->notification.ch_desc, sizeof(mmh->ch_desc)); + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* The RR indicates uplink busy. */ +static int gsm48_mm_uplink_free(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_event *mme = (struct gsm48_mm_event *)msg->data; + struct msgb *nmsg; + uint16_t msg_type; + + if (mm->vgcs.group_call) + msg_type = (mme->msg_type == GSM48_MM_EVENT_UPLINK_BUSY) ? GSM48_MMGCC_UPLINK_BUSY_IND + : GSM48_MMGCC_UPLINK_FREE_IND; + else + msg_type = (mme->msg_type == GSM48_MM_EVENT_UPLINK_BUSY) ? GSM48_MMBCC_UPLINK_BUSY_IND + : GSM48_MMBCC_UPLINK_FREE_IND; + + LOGP(DMM, LOGL_INFO, "Update uplink free/busy state in group receive mode.\n"); + + /* Notification message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* Join VGCS/VBS call as listener. */ +static int gsm48_mm_group_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_settings *set = &ms->settings; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct msgb *nmsg; + + if (mm->substate == GSM48_MM_SST_NO_IMSI && !set->asci_allow_any) + return gsm48_mm_group_reject(ms, msg); + + LOGP(DMM, LOGL_INFO, "Request for joining a group call, trying to establish group receive mode.\n"); + + /* Store infos about group/broadcast call. */ + mm->vgcs.enabled = true; + mm->vgcs.group_call = (mmh->msg_type == GSM48_MMGCC_GROUP_REQ); + mm->vgcs.callref = mmh->ref; + mm->vgcs.normal_service = (mm->substate == GSM48_MM_SST_NORMAL_SERVICE || + mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL); + + /* Change to VGCS substate. */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) + ? GSM48_MM_SST_RX_VGCS_NORMAL : GSM48_MM_SST_RX_VGCS_LIMITED); + + /* Group recevie mode request to RR layer */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Add channel description. */ + memcpy(msgb_put(nmsg, sizeof(mmh->ch_desc)), &mmh->ch_desc, sizeof(mmh->ch_desc)); + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REQ, 0, 0); +} + +/* Joining VGCS/VBS call is not allowed in other states. */ +static int gsm48_mm_group_reject(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "Joining group call rejected in current state.\n"); + + msg_type = (mmh->msg_type == GSM48_MMGCC_GROUP_REQ) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND; + + /* Release message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, mmh->sapi); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer confirms group call. */ +static int gsm48_mm_group_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + uint16_t msg_type; + struct msgb *nmsg; + + LOGP(DMM, LOGL_NOTICE, "RR confirms group call.\n"); + + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_GROUP_CNF : GSM48_MMBCC_GROUP_CNF; + + /* Uplink confirm message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer releases group call channel. */ +static int gsm48_mm_group_rel_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "RR released or rejected group call channel.\n"); + + /* Disable group mode. */ + mm->vgcs.enabled = false; + + /* Change mode back to normal or limited service. */ + if (mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED) { + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (subscr->sim_valid) ? GSM48_MM_SST_LIMITED_SERVICE + : GSM48_MM_SST_NO_IMSI); + } + if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL) + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE); + + /* Return IDLE, if not already. Also select the sub-state to use. */ + gsm48_mm_return_idle(ms, NULL); + + /* Release message to GCC/BCC layer */ + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_REL_IND : GSM48_MMBCC_REL_IND; + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + switch (rrh->cause) { + case RR_REL_CAUSE_TRY_LATER: + /* Joining not yet possible */ + nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE; + break; + case RR_REL_CAUSE_LOST_SIGNAL: + /* Lower layer failed. */ + nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE; + break; + case RR_REL_CAUSE_NORMAL: + /* Channel was released by network. */ + nmmh->cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + break; + default: + nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + break; + } + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* Upper layer releases group call. */ +static int gsm48_mm_group_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Request to release group call in receive or transmit mode.\n"); + + /* Disable group mode. */ + mm->vgcs.enabled = false; + + /* Change mode back to normal or limited service. */ + if (mm->substate == GSM48_MM_SST_RX_VGCS_LIMITED) { + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (subscr->sim_valid) ? GSM48_MM_SST_LIMITED_SERVICE + : GSM48_MM_SST_NO_IMSI); + } + if (mm->substate == GSM48_MM_SST_RX_VGCS_NORMAL) + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NORMAL_SERVICE); + + /* We are already IDLE. Also select the sub-state to use. */ + gsm48_mm_return_idle(ms, NULL); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_GROUP_REL_REQ, 0, 0); +} + +/* Upper layer requests uplink. */ +static int gsm48_mm_uplink_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + + if (mm->substate != GSM48_MM_SST_RX_VGCS_NORMAL && !set->asci_allow_any) + return gsm48_mm_uplink_reject(ms, msg); + + LOGP(DMM, LOGL_INFO, "Request for uplink, trying to establish group transmit mode.\n"); + + /* Go into uplink pending state. */ + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_VGCS, 0); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_UPLINK_REQ, 0, 0); +} + +/* Uplink not allowed in this state. */ +static int gsm48_mm_uplink_reject(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "Request for uplink rejected in current state.\n"); + + msg_type = (mmh->msg_type == GSM48_MMGCC_UPLINK_REQ) ? GSM48_MMGCC_UPLINK_REL_IND : GSM48_MMBCC_UPLINK_REL_IND; + + /* Uplink release message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mmh->ref, mmh->transaction_id, mmh->sapi); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer confirms uplink. */ +static int gsm48_mm_uplink_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + uint16_t msg_type; + struct msgb *nmsg; + + LOGP(DMM, LOGL_NOTICE, "RR confirms uplink.\n"); + + /* Go into group transmit state. */ + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE_VGCS, 0); + + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_UPLINK_CNF : GSM48_MMBCC_UPLINK_CNF; + + /* Uplink confirm message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* RR layer releases/rejects uplink. */ +static int gsm48_mm_uplink_rel_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + uint16_t msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + LOGP(DMM, LOGL_NOTICE, "RR released or rejected uplink.\n"); + + /* Change to VGCS substate. */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, (mm->vgcs.normal_service) ? GSM48_MM_SST_RX_VGCS_NORMAL + : GSM48_MM_SST_RX_VGCS_LIMITED); + + msg_type = (mm->vgcs.group_call) ? GSM48_MMGCC_UPLINK_REL_IND : GSM48_MMBCC_UPLINK_REL_IND; + + /* Uplink reject message to GCC/BCC layer */ + nmsg = gsm48_mmxx_msgb_alloc(msg_type, mm->vgcs.callref, 0xff, 0); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + switch (rrh->cause) { + case RR_REL_CAUSE_UPLINK_REJECTED: + /* Access to uplink was rejected by network or when not in group receive mode. */ + nmmh->cause = GSM48_CC_CAUSE_CALL_REJECTED; + break; + case RR_REL_CAUSE_UPLINK_BUSY: + /* Uplink was busy and did not become free. */ + nmmh->cause = GSM48_CC_CAUSE_USER_BUSY; + break; + case RR_REL_CAUSE_LINK_FAILURE: + /* Access to uplink failed. */ + nmmh->cause = GSM48_CC_CAUSE_TEMP_FAILURE; + break; + case RR_REL_CAUSE_NORMAL: + /* Uplink was released by message. */ + nmmh->cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + break; + default: + nmmh->cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + break; + } + + gsm48_mmxx_upmsg(ms, nmsg); + return 0; +} + +/* Upper layer releases uplink. */ +static int gsm48_mm_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Request to release uplink, leaving group transmit mode.\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + + /* Push RR header and send to RR layer. */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_UPLINK_REL_REQ, 0, 0); +} + /* * other processes */ @@ -3640,19 +4182,51 @@ static struct downstate { {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.2 Attempt to update / Loc. Upd. needed */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, /* emergency only */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.3 Limited service */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.4 No IMSI */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.5 PLMN search, normal service */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, @@ -3663,10 +4237,54 @@ static struct downstate { {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + /* 4.2.2.6 PLMN search */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_req}, + + /* 4.2.2.7 Receiving group call, normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMGCC_UPLINK_REQ, gsm48_mm_uplink_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_req}, + + /* 4.2.2.8 Receiving group call, limited service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMGCC_UPLINK_REQ, gsm48_mm_uplink_req}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MMBCC_UPLINK_REQ, gsm48_mm_uplink_req}, + /* 4.5.1.1 MM Connection (EST) */ {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_first}, @@ -3677,6 +4295,12 @@ static struct downstate { {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_first}, + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_first}, + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_more}, @@ -3695,6 +4319,7 @@ static struct downstate { {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_wait}, + /* Reject call in other states. */ {ALL_STATES, ALL_STATES, GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_reject}, @@ -3704,6 +4329,18 @@ static struct downstate { {ALL_STATES, ALL_STATES, GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_reject}, + {ALL_STATES, ALL_STATES, + GSM48_MMGCC_EST_REQ, gsm48_mm_init_mm_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMBCC_EST_REQ, gsm48_mm_init_mm_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMGCC_GROUP_REQ, gsm48_mm_group_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMBCC_GROUP_REQ, gsm48_mm_group_reject}, + /* 4.5.2.1 MM Connection (DATA) */ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, @@ -3717,6 +4354,14 @@ static struct downstate { SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, GSM48_MMSMS_DATA_REQ, gsm48_mm_data}, + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMGCC_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMBCC_DATA_REQ, gsm48_mm_data}, + /* 4.5.2.1 MM Connection (REL) */ {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, GSM48_MMCC_REL_REQ, gsm48_mm_release_active}, @@ -3727,6 +4372,12 @@ static struct downstate { {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, GSM48_MMSMS_REL_REQ, gsm48_mm_release_active}, + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMGCC_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMBCC_REL_REQ, gsm48_mm_release_active}, + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_add}, @@ -3736,6 +4387,12 @@ static struct downstate { {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_add}, + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMGCC_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMBCC_REL_REQ, gsm48_mm_release_wait_add}, + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_active}, @@ -3753,6 +4410,35 @@ static struct downstate { {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_rr}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMGCC_REL_REQ, gsm48_mm_release_wait_rr}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMBCC_REL_REQ, gsm48_mm_release_wait_rr}, + + /* Group transmit mode */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMGCC_UPLINK_REL_REQ, gsm48_mm_uplink_rel_req}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMBCC_UPLINK_REL_REQ, gsm48_mm_uplink_rel_req}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMGCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMBCC_REL_REQ, gsm48_mm_group_rel_req}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMGCC_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), ALL_STATES, + GSM48_MMBCC_DATA_REQ, gsm48_mm_data}, }; #define DOWNSLLEN \ @@ -3767,7 +4453,7 @@ int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg) int i, rc; /* keep up to date with the transaction ID */ - conn = mm_conn_by_ref(mm, mmh->ref); + conn = mm_conn_by_ref_and_class(mm, mmh->ref, (mmh->msg_type & GSM48_MMXX_MASK)); if (conn) conn->transaction_id = mmh->transaction_id; @@ -3880,6 +4566,23 @@ static struct rrdatastate { SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* not supported */ GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con}, + /* Group call */ + {ALL_STATES, + GSM48_RR_GROUP_CNF, gsm48_mm_group_cnf}, + + {ALL_STATES, + GSM48_RR_UPLINK_CNF, gsm48_mm_uplink_cnf}, + + {ALL_STATES, + GSM48_RR_GROUP_REL_IND, gsm48_mm_group_rel_ind}, + + {ALL_STATES, + GSM48_RR_UPLINK_REL_IND, gsm48_mm_uplink_rel_ind}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS), + GSM48_RR_REL_IND, gsm48_mm_group_rel_ind}, + /* other (also wait for network command) */ {ALL_STATES, GSM48_RR_REL_IND, gsm48_mm_rel_other}, @@ -3965,20 +4668,84 @@ static struct mmdatastate { #define MMDATASLLEN \ (sizeof(mmdatastatelist) / sizeof(struct mmdatastate)) +static int create_conn_and_push_mm_hdr(struct gsm48_mmlayer *mm, struct msgb *msg, int rr_est, int rr_prim, + uint8_t sapi) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t transaction_id; + uint32_t callref; + struct gsm48_mm_conn *conn; + struct gsm48_mmxx_hdr *mmh; + + transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ + + if (mm->vgcs.enabled) { + /* Ongoing group call. */ + callref = mm->vgcs.callref; + } else { + /* find transaction, if any */ + conn = mm_conn_by_id(mm, pdisc, transaction_id); + + /* create MM connection instance */ + if (!conn) { + /* if MT calls are not supported with protocol */ + if (rr_est == -1) { + LOGP(DMM, LOGL_ERROR, "No MO connection for pdisc=%d, transaction_id=%d\n", + pdisc, transaction_id); + return -EINVAL; + } + conn = mm_conn_new(mm, pdisc, transaction_id, sapi, mm_conn_new_ref++); + rr_prim = rr_est; + } + if (!conn) + return -ENOMEM; + callref = conn->ref; + } + + /* push new header */ + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = rr_prim; + mmh->ref = callref; + mmh->transaction_id = transaction_id; + mmh->sapi = sapi; + + /* go MM CONN ACTIVE state */ + if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD || + mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) { + /* stop RR release timer */ + stop_mm_t3240(mm); + + /* stop "RR connection release not allowed" timer */ + stop_mm_t3241(mm); + + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + } + + return 0; +} + static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_mmlayer *mm = &ms->mmlayer; struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; uint8_t sapi = rrh->sapi; - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - uint8_t msg_type = gh->msg_type & 0xbf; - struct gsm48_mmxx_hdr *mmh; + const struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc, msg_type; int msg_supported = 0; /* determine, if message is supported at all */ - int rr_prim = -1, rr_est = -1; /* no prim set */ uint8_t skip_ind; int i, rc; + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DMM, LOGL_INFO, "%s(): short read of msgb: %s\n", + __func__, msgb_hexdump(msg)); + return -EINVAL; + } + + pdisc = gh->proto_discr & 0x0f; + msg_type = gh->msg_type & 0xbf; + /* 9.2.19 */ if (msg_type == GSM48_MT_MM_NULL) { msgb_free(msg); @@ -3994,61 +4761,6 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg) /* pull the RR header */ msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); - /* create transaction (if not exists) and push message */ - switch (pdisc) { - case GSM48_PDISC_CC: - rr_prim = GSM48_MMCC_DATA_IND; - rr_est = GSM48_MMCC_EST_IND; - break; - case GSM48_PDISC_NC_SS: - rr_prim = GSM48_MMSS_DATA_IND; - rr_est = GSM48_MMSS_EST_IND; - break; - case GSM48_PDISC_SMS: - rr_prim = GSM48_MMSMS_DATA_IND; - rr_est = GSM48_MMSMS_EST_IND; - break; - } - if (rr_prim != -1) { - uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; - /* flip */ - struct gsm48_mm_conn *conn; - - /* find transaction, if any */ - conn = mm_conn_by_id(mm, pdisc, transaction_id); - - /* create MM connection instance */ - if (!conn) { - conn = mm_conn_new(mm, pdisc, transaction_id, sapi, - mm_conn_new_ref++); - rr_prim = rr_est; - } - if (!conn) { - msgb_free(msg); - return -ENOMEM; - } - - /* push new header */ - msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); - mmh = (struct gsm48_mmxx_hdr *)msg->data; - mmh->msg_type = rr_prim; - mmh->ref = conn->ref; - mmh->transaction_id = conn->transaction_id; - mmh->sapi = conn->sapi; - - /* go MM CONN ACTIVE state */ - if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD - || mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) { - /* stop RR release timer */ - stop_mm_t3240(mm); - - /* stop "RR connection release not allowed" timer */ - stop_mm_t3241(mm); - - new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); - } - } - /* forward message */ switch (pdisc) { case GSM48_PDISC_MM: @@ -4062,23 +4774,42 @@ static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg) break; /* follow the selection procedure below */ case GSM48_PDISC_CC: - rc = gsm48_rcv_cc(ms, msg); + rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMCC_EST_IND, GSM48_MMCC_DATA_IND, sapi); + if (rc == 0) + rc = gsm48_rcv_cc(ms, msg); msgb_free(msg); return rc; case GSM48_PDISC_NC_SS: - rc = gsm480_rcv_ss(ms, msg); + rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMSS_EST_IND, GSM48_MMSS_DATA_IND, sapi); + if (rc == 0) + rc = gsm480_rcv_ss(ms, msg); msgb_free(msg); return rc; case GSM48_PDISC_SMS: - rc = gsm411_rcv_sms(ms, msg); + rc = create_conn_and_push_mm_hdr(mm, msg, GSM48_MMSMS_EST_IND, GSM48_MMSMS_DATA_IND, sapi); + if (rc == 0) + rc = gsm411_rcv_sms(ms, msg); + msgb_free(msg); + return rc; + + case GSM48_PDISC_GROUP_CC: + rc = create_conn_and_push_mm_hdr(mm, msg, -1, GSM48_MMGCC_DATA_IND, sapi); + if (rc == 0) + rc = gsm44068_rcv_gcc_bcc(ms, msg); + msgb_free(msg); + return rc; + case GSM48_PDISC_BCAST_CC: + rc = create_conn_and_push_mm_hdr(mm, msg, -1, GSM48_MMBCC_DATA_IND, sapi); + if (rc == 0) + rc = gsm44068_rcv_gcc_bcc(ms, msg); msgb_free(msg); return rc; default: LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n", - pdisc); + pdisc); msgb_free(msg); return gsm48_mm_tx_mm_status(ms, GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); @@ -4160,6 +4891,10 @@ static struct eventstate { {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start}, + /* 4.2.2.7 Receiving Group Call (Normal service) */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs}, + /* 4.2.2.2 Attempt to update / Loc. upd. needed */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), @@ -4195,6 +4930,10 @@ static struct eventstate { {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + /* 4.2.2.8 Receiving Group Call (Limited service) */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs}, + /* 4.2.2.4 No IMSI */ /* 4.2.2.5 PLMN search, normal service */ {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), @@ -4230,12 +4969,15 @@ static struct eventstate { {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, /* silently detach */ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_end}, + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) | + SBIT(GSM48_MM_ST_WAIT_RR_CONN_VGCS), ALL_STATES, /* uplink access */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_vgcs}, + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | SBIT(GSM48_MM_ST_PROCESS_CM_SERV_P) | SBIT(GSM48_MM_ST_WAIT_REEST) | SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON) | - SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) | SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, /* we can release */ GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_release}, @@ -4290,6 +5032,17 @@ static struct eventstate { {ALL_STATES, ALL_STATES, GSM48_MM_EVENT_CLASSMARK_CHG, gsm48_mm_classm_chg}, #endif + + /* Group call notification event */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_NOTIFICATION, gsm48_mm_notification}, + + /* Uplink free/busy while in group receive mode */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL) | SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MM_EVENT_UPLINK_BUSY, gsm48_mm_uplink_free}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_RX_VGCS_NORMAL) | SBIT(GSM48_MM_SST_RX_VGCS_LIMITED), + GSM48_MM_EVENT_UPLINK_FREE, gsm48_mm_uplink_free}, }; #define EVENTSLLEN \ @@ -4318,7 +5071,8 @@ static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg) && ((1 << mm->substate) & eventstatelist[i].substates)) break; if (i == EVENTSLLEN) { - LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + LOGP(DMM, LOGL_NOTICE, "Message %s unhandled in state %s.\n", + get_mmevent_name(msg_type), gsm48_mm_state_names[mm->state]); return 0; } diff --git a/src/host/layer23/src/mobile/gsm48_rr.c b/src/host/layer23/src/mobile/gsm48_rr.c index e115d159..83287c14 100644 --- a/src/host/layer23/src/mobile/gsm48_rr.c +++ b/src/host/layer23/src/mobile/gsm48_rr.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* Very short description of some of the procedures: @@ -38,6 +34,53 @@ * When the assignment or handover fails, the old channel is activate and the * link is established again. Also pending messages are sent. * + * Group Channel: + * + * A group receive/transmit mode is not documented at GSM 04.07. The group + * receive mode is similar to the IDLE mode, except that the MS is listening + * to a TCH. The group transmit mode is similar to the DEDICATED mode. Special + * (undocumented) commands are used to enter group receive and transmit mode. + * + * There is a substate that indicates group receive/transmit mode. If the + * substate is set to group receive, the IDLE mode becomes the group receive + * mode. If the substate is set to group transmit, the dedicated mode becomes + * the group transmit mode. The substate set to group receive allows to enter + * regular dedicated mode and return back to group receive mode afterwards. + * + * new_rr_state(rr, GSM48_RR_ST_IDLE) is used to return to IDLE mode or to + * group receive mode, depending on the substate. + * + * The Uplink access on group channel: + * + * If the uplink is busy, wait until it becomes free (Uplink investigation + * procedure). Abort, if the procedure times out, if the VGCS UPLINK GRANT + * message is recived for a different talker. + * + * Request uplink using access bursts on TCH until an VGCS UPLINK GRANT is + * received. Abort, if it the procedure times out, if the uplink becomes busy, + * if the VGCS UPLINK GRANT message references a different frame numer. + * + * Establish layer 2 with TALKER INDICATION. Abort, if content resolution + * mismatches (RR_REL_IND), if link fails (MDL_ERROR), if uplink becomes free. + * + * Release uplink and wait until uplink becomes free. Abort, if UPLINK RELEASE + * is received or if uplink fails. + * + * New primitives are invented for group/broadcast calls. They are not + * specified in any recommendation. They are: + * + * GSM48_MM_EVENT_NOTIFICATION: Notify MM layer about received/ceased call. + * GSM48_MM_EVENT_UPLINK_BUSY: Notify MM layer about uplink becoming busy. + * GSM48_MM_EVENT_UPLINK_FREE: Notify MM layer about uplink becoming free. + * + * RR_GROUP_REQ: The MM layer requests group channel in receive mode. + * RR_GROUP_CNF: The RR confirms group channel. + * RR_GROUP_REL_REQ: The MM layer releases group channel. + * RR_GROUP_REL_IND: The RR indicates/confirms release of group channel. + * RR_UPLINK_REQ: The MM layer requests uplink (group transmit mode). + * RR_UPLINK_CNF: The RR layer confirms uplink. (Uplink was granted.) + * RR_UPLINK_REL_REQ: The MM layer requests release of uplink. + * RR_UPLINK_REL_IND: The RR layer indicates/confirms release of uplink */ /* Testing delayed (immediate) assignment / handover @@ -71,36 +114,51 @@ #include <osmocom/gsm/rsl.h> #include <osmocom/gsm/gsm48.h> #include <osmocom/core/bitvec.h> -#include <osmocom/codec/codec.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/l1l2_interface.h> #include <osmocom/bb/common/l23_app.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/l1ctl.h> -#include <osmocom/bb/mobile/vty.h> #include <osmocom/bb/common/utils.h> +#include <osmocom/bb/common/settings.h> + +#include <osmocom/bb/mobile/vty.h> +#include <osmocom/bb/mobile/gsm48_rr.h> #include <l1ctl_proto.h> +/* Check response for the last 3 channel requests only. See TS 44.018 §3.3.1.1.3.1 and §3.3.1.1.3.2. */ +#define IMM_ASS_HISTORY 3 +/* Check response for up to 5 uplink requests. See TS 44.018 §3.3.1.2.1.2. */ +#define UL_GRANT_HISTROY 5 + +static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t *ma_len); +static int gsm48_rr_activate_channel(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, uint16_t *ma, uint8_t ma_len); static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro); static void stop_rr_t_starting(struct gsm48_rrlayer *rr); static void stop_rr_t3124(struct gsm48_rrlayer *rr); static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg); static int gsm48_rr_dl_est(struct osmocom_ms *ms); static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms); -static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, - uint8_t mode); +static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags); static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg); int gsm414_rcv_test(struct osmocom_ms *ms, const struct msgb *msg); +static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause); +static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause); +static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event); +static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num); +static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms); +static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic); +static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg); /* * support */ -#define MIN(a, b) ((a < b) ? a : b) - /* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */ static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, uint8_t *power_level, uint8_t *atc) @@ -251,8 +309,14 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, struct gsm_settings *set = &ms->settings; uint8_t ch_type, ch_subch, ch_ts; + if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, chan_nr); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + /* only complain if we use TCH/F or TCH/H */ - rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts); if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return 0; @@ -309,6 +373,56 @@ static uint8_t gsm48_rr_check_mode(struct osmocom_ms *ms, uint8_t chan_nr, LOGP(DRR, LOGL_INFO, "Mode: half-rate speech V3\n"); } break; + case GSM48_CMODE_DATA_14k5: + if (ch_type != RSL_CHAN_Bm_ACCHs) { + LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } else if (!set->csd_tch_f144) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F14.4 data (%s)\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/F14.4 data (%s)\n", + gsm48_chan_mode_name(mode)); + break; + case GSM48_CMODE_DATA_12k0: + if (ch_type != RSL_CHAN_Bm_ACCHs) { + LOGP(DRR, LOGL_ERROR, "TCH/F is expected for mode %s\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } else if (!set->csd_tch_f96) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/F9.6 data (%s)\n", + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/F9.6 data (%s)\n", + gsm48_chan_mode_name(mode)); + break; + case GSM48_CMODE_DATA_6k0: + if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f48) + || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h48)) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c4.8 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/%c4.8 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + break; + case GSM48_CMODE_DATA_3k6: + if ((ch_type == RSL_CHAN_Bm_ACCHs && !set->csd_tch_f24) + || (ch_type == RSL_CHAN_Lm_ACCHs && !set->csd_tch_h24)) { + LOGP(DRR, LOGL_ERROR, "Not supporting TCH/%c2.4 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + } + LOGP(DRR, LOGL_INFO, "Mode: TCH/%c2.4 data (%s)\n", + ch_type == RSL_CHAN_Bm_ACCHs ? 'F' : 'H', + gsm48_chan_mode_name(mode)); + break; default: LOGP(DRR, LOGL_ERROR, "Mode 0x%02x not supported!\n", mode); return GSM48_RR_CAUSE_CHAN_MODE_UNACCT; @@ -345,19 +459,21 @@ const char *gsm48_rr_state_names[] = { static void new_rr_state(struct gsm48_rrlayer *rr, int state) { + struct osmocom_ms *ms = rr->ms; + if (state < 0 || state >= (sizeof(gsm48_rr_state_names) / sizeof(char *))) return; - /* must check against equal state */ - if (rr->state == state) { + /* Check against equal state or IDLE state. */ + if (rr->state == state && state != GSM48_RR_ST_IDLE) { LOGP(DRR, LOGL_INFO, "equal state ? %s\n", gsm48_rr_state_names[rr->state]); return; } LOGP(DRR, LOGL_INFO, "new state %s -> %s\n", - gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]); + gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]); /* abort handover, in case of release of dedicated mode */ if (rr->state == GSM48_RR_ST_DEDICATED) { @@ -371,10 +487,15 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state) rr->state = state; - if (state == GSM48_RR_ST_IDLE) { + if (state != GSM48_RR_ST_IDLE) + return; + + /* Return from dedicated/group receive/transmit mode to idle mode. (Trigger cell reselection.) */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) { struct msgb *msg, *nmsg; struct gsm322_msg *em; + LOGP(DRR, LOGL_INFO, "Returning to IDLE mode.\n"); /* release dedicated mode, if any */ l1ctl_tx_dm_rel_req(rr->ms); rr->ms->meas.rl_fail = 0; @@ -413,6 +534,41 @@ static void new_rr_state(struct gsm48_rrlayer *rr, int state) msgb_free(nmsg); /* reset any BA range */ rr->ba_ranges = 0; + return; + } + + /* Return from dedicated mode to group receive mode. + * This is not used, because we never enter dedicated mode during group receive/transmit mode. + * It may be used later, if we support it in MM layer. */ + if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) { + uint16_t ma[64]; + uint8_t ma_len; + struct msgb *msg; + + LOGP(DRR, LOGL_INFO, "Returning to GROUP RECEIVE mode.\n"); + /* release dedicated mode, if any */ + l1ctl_tx_dm_rel_req(rr->ms); + rr->ms->meas.rl_fail = 0; + rr->dm_est = 0; + l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED); + /* free establish message, if any */ + rr->rr_est_req = 0; + if (rr->rr_est_msg) { + msgb_free(rr->rr_est_msg); + rr->rr_est_msg = NULL; + } + /* free all pending messages */ + while ((msg = msgb_dequeue(&rr->downqueue))) + msgb_free(msg); + /* reset ciphering */ + rr->cipher_on = 0; + /* copy channel description "group mode" */ + memcpy(&rr->cd_now, &rr->vgcs.cd_group, sizeof(rr->cd_now)); + /* render channel "group mode" */ + gsm48_rr_render_ma(ms, &rr->cd_now, ma, &ma_len); + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->cd_now, ma, ma_len); + return; } } @@ -453,6 +609,14 @@ static const struct value_string gsm48_rr_msg_names[] = { { GSM48_RR_ABORT_REQ, "RR_ABORT_REQ" }, { GSM48_RR_ABORT_IND, "RR_ABORT_IND" }, { GSM48_RR_ACT_REQ, "RR_ACT_REQ" }, + { GSM48_RR_GROUP_REQ, "RR_GROUP_REQ" }, + { GSM48_RR_GROUP_CNF, "RR_GROUP_CNF" }, + { GSM48_RR_GROUP_REL_REQ, "RR_GROUP_REL_REQ" }, + { GSM48_RR_GROUP_REL_IND, "RR_GROUP_REL_IND" }, + { GSM48_RR_UPLINK_REQ, "RR_UPLINK_REQ" }, + { GSM48_RR_UPLINK_CNF, "RR_UPLINK_CNF" }, + { GSM48_RR_UPLINK_REL_REQ, "RR_UPLINK_REL_REQ" }, + { GSM48_RR_UPLINK_REL_IND, "RR_UPLINK_REL_IND" }, { 0, NULL } }; @@ -634,39 +798,43 @@ static void timeout_rr_meas(void *arg) struct gsm_settings *set = &rr->ms->settings; int rxlev, berr, snr; uint8_t ch_type, ch_subch, ch_ts; + struct osmo_strbuf sb; char text[256]; + sb = (struct osmo_strbuf) { .buf = text, .len = sizeof(text) }; + /* don't monitor if no cell is selected or if we scan neighbour cells */ if (!cs->selected || cs->neighbour) { - sprintf(text, "MON: not camping on serving cell"); + OSMO_STRBUF_PRINTF(sb, "MON: not camping on serving cell"); goto restart; } else if (!meas->frames) { - sprintf(text, "MON: no cell info"); + OSMO_STRBUF_PRINTF(sb, "MON: no cell info"); } else { rxlev = (meas->rxlev + meas->frames / 2) / meas->frames; berr = (meas->berr + meas->frames / 2) / meas->frames; snr = (meas->snr + meas->frames / 2) / meas->frames; - sprintf(text, "MON: f=%d lev=%s snr=%2d ber=%3d " - "LAI=%s %s %04x ID=%04x", cs->sel_arfcn, - gsm_print_rxlev(rxlev), snr, berr, - gsm_print_mcc(cs->sel_mcc), - gsm_print_mnc(cs->sel_mnc), cs->sel_lac, cs->sel_id); + OSMO_STRBUF_PRINTF(sb, "MON: f=%d lev=%s snr=%2d ber=%3d " + "CGI=%s", cs->sel_arfcn, + gsm_print_rxlev(rxlev), snr, berr, + osmo_cgi_name(&cs->sel_cgi)); if (rr->state == GSM48_RR_ST_DEDICATED) { - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, - &ch_subch, &ch_ts); - sprintf(text + strlen(text), " TA=%d pwr=%d TS=%d", - rr->cd_now.ind_ta - set->alter_delay, - (set->alter_tx_power) ? set->alter_tx_power_value - : rr->cd_now.ind_tx_power, ch_ts); - if (ch_type == RSL_CHAN_SDCCH8_ACCH - || ch_type == RSL_CHAN_SDCCH4_ACCH) - sprintf(text + strlen(text), "/%d", ch_subch); + OSMO_STRBUF_PRINTF(sb, " TA=%d pwr=%d", + rr->cd_now.ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value + : rr->cd_now.ind_tx_power); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) == 0) { + OSMO_STRBUF_PRINTF(sb, " TS=%d", ch_ts); + if (ch_type == RSL_CHAN_SDCCH8_ACCH + || ch_type == RSL_CHAN_SDCCH4_ACCH + || ch_type == RSL_CHAN_Lm_ACCHs) + OSMO_STRBUF_PRINTF(sb, "/%d", ch_subch); + } } else gsm322_meas(rr->ms, rxlev); } LOGP(DRR, LOGL_INFO, "%s\n", text); if (rr->monitor) - vty_notify(rr->ms, "%s\n", text); + l23_vty_ms_notify(rr->ms, "%s\n", text); if (rr->dm_est) gsm48_rr_tx_meas_rep(rr->ms); @@ -767,6 +935,36 @@ static void timeout_rr_t3126(void *arg) new_rr_state(rr, GSM48_RR_ST_IDLE); } +static void timeout_rr_t3128(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + + LOGP(DRR, LOGL_INFO, "timer T3128 has fired\n"); + + LOGP(DRR, LOGL_NOTICE, "Failed to access uplink, uplink did not become free.\n"); + gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); +} + +static void timeout_rr_t3130(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + + LOGP(DRR, LOGL_INFO, "timer T3130 has fired\n"); + + gsm48_rr_uplink_access(ms, NULL); +} + +static void timeout_rr_t_ul_free(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + + LOGP(DRR, LOGL_INFO, "uplink free timer has fired\n"); + rr->vgcs.uplink_free = false; + gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY); +} + static void start_rr_t_meas(struct gsm48_rrlayer *rr, int sec, int micro) { rr->t_meas.cb = timeout_rr_meas; @@ -819,6 +1017,33 @@ static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro) osmo_timer_schedule(&rr->t3126, sec, micro); } +static void start_rr_t3128(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3128 with %d.%03d seconds\n", sec, + micro / 1000); + rr->vgcs.t3128.cb = timeout_rr_t3128; + rr->vgcs.t3128.data = rr; + osmo_timer_schedule(&rr->vgcs.t3128, sec, micro); +} + +static void start_rr_t3130(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3130 with %d.%03d seconds\n", sec, + micro / 1000); + rr->vgcs.t3130.cb = timeout_rr_t3130; + rr->vgcs.t3130.data = rr; + osmo_timer_schedule(&rr->vgcs.t3130, sec, micro); +} + +static void start_rr_t_ul_free(struct gsm48_rrlayer *rr) +{ + if (!osmo_timer_pending(&rr->vgcs.t_ul_free)) + LOGP(DRR, LOGL_INFO, "starting uplink free timer\n"); + rr->vgcs.t_ul_free.cb = timeout_rr_t_ul_free; + rr->vgcs.t_ul_free.data = rr; + osmo_timer_schedule(&rr->vgcs.t_ul_free, 0, 480000); +} + static void stop_rr_t_meas(struct gsm48_rrlayer *rr) { if (osmo_timer_pending(&rr->t_meas)) { @@ -875,6 +1100,30 @@ static void stop_rr_t3126(struct gsm48_rrlayer *rr) } } +static void stop_rr_t3128(struct gsm48_rrlayer *rr) +{ + if (osmo_timer_pending(&rr->vgcs.t3128)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3128\n"); + osmo_timer_del(&rr->vgcs.t3128); + } +} + +static void stop_rr_t3130(struct gsm48_rrlayer *rr) +{ + if (osmo_timer_pending(&rr->vgcs.t3130)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3130\n"); + osmo_timer_del(&rr->vgcs.t3130); + } +} + +static void stop_rr_t_ul_free(struct gsm48_rrlayer *rr) +{ + if (osmo_timer_pending(&rr->vgcs.t_ul_free)) { + LOGP(DRR, LOGL_INFO, "stopping pending uplink free timer\n"); + osmo_timer_del(&rr->vgcs.t_ul_free); + } +} + /* * status */ @@ -910,11 +1159,9 @@ int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) /* send chiperhing mode complete */ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) { - struct gsm_settings *set = &ms->settings; struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_rr_hdr *nrrh; - uint8_t buf[11], *tlv; LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr); @@ -927,13 +1174,8 @@ static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; /* MI */ - if (cr) { - gsm48_generate_mid_from_imsi(buf, set->imeisv); - /* alter MI type */ - buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | GSM_MI_TYPE_IMEISV; - tlv = msgb_put(nmsg, 2 + buf[1]); - memcpy(tlv, buf, 2 + buf[1]); - } + if (cr) + gsm48_encode_mi_tlv(ms, nmsg, GSM_MI_TYPE_IMEISV, false); gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); @@ -1280,6 +1522,648 @@ static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg) } /* + * ASCI notification + */ + +struct asci_notif { + struct llist_head entry; + struct gsm48_rrlayer *rr; + uint8_t gcr[5]; + bool ch_desc_present; + struct gsm48_chan_desc ch_desc; + struct osmo_timer_list timer; +}; + +/* When does a notification received from NCH expires. */ +#define NOTIFICATION_TIMEOUT 5 + +static void asci_notif_timeout(void *arg); + +/* Add new notification to list. */ +static struct asci_notif *asci_notif_alloc(struct gsm48_rrlayer *rr, const uint8_t *gcr) +{ + struct asci_notif *notif; + + notif = talloc_zero(rr->ms, struct asci_notif); + if (!notif) + return NULL; + notif->rr = rr; + memcpy(notif->gcr, gcr, sizeof(notif->gcr)); + llist_add_tail(¬if->entry, &rr->vgcs.notif_list); + + notif->timer.cb = asci_notif_timeout; + notif->timer.data = notif; + + return notif; +} + +/* Remove notification from list. */ +static void asci_notif_free(struct asci_notif *notif) +{ + osmo_timer_del(¬if->timer); + llist_del(¬if->entry); + talloc_free(notif); +} + +/* Remove all ASCI notifications from list. */ +static void asci_notif_list_free(struct gsm48_rrlayer *rr) +{ + struct asci_notif *notif, *notif2; + + llist_for_each_entry_safe(notif, notif2, &rr->vgcs.notif_list, entry) + asci_notif_free(notif); +} + +/* Notification timed out. */ +static void asci_notif_timeout(void *arg) +{ + struct asci_notif *notif = arg; + struct gsm48_rrlayer *rr = notif->rr; + struct msgb *nmsg; + struct gsm48_mm_event *mme; + + /* Send notification of ceased call to MM layer. */ + LOGP(DRR, LOGL_INFO, "Notify MM layer about ceased group call.\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION); + if (!nmsg) + return; + mme = (struct gsm48_mm_event *) nmsg->data; + memcpy(mme->notification.gcr, notif->gcr, sizeof(mme->notification.gcr)); + mme->notification.gone = true; + gsm48_mmevent_msg(rr->ms, nmsg); + + asci_notif_free(notif); +} + +/* Find notification in list. */ +static struct asci_notif *asci_notif_find(struct gsm48_rrlayer *rr, const uint8_t *gcr) +{ + struct asci_notif *notif; + + llist_for_each_entry(notif, &rr->vgcs.notif_list, entry) { + if (!memcmp(¬if->gcr, gcr, sizeof(notif->gcr))) + return notif; + } + + return NULL; +} + +static int gsm48_rr_rx_group_call(struct osmocom_ms *ms, const uint8_t *gcr, const uint8_t *ch_desc, bool nch) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct asci_notif *notif; + bool update_call = false; + struct msgb *nmsg; + struct gsm48_mm_event *mme; + + /* Find or create notification entry. Only create entries for notifications on NCH */ + notif = asci_notif_find(rr, gcr); + if (!notif) { + update_call = true; + if (nch) { + notif = asci_notif_alloc(rr, gcr); + if (!notif) + return -ENOMEM; + } + } + + /* Update channel description. */ + if (notif) { + if (ch_desc) { + if (!notif->ch_desc_present) + update_call = true; + notif->ch_desc_present = true; + memcpy(¬if->ch_desc, ch_desc, sizeof(notif->ch_desc)); + } else { + notif->ch_desc_present = false; + if (!notif->ch_desc_present) + update_call = true; + } + /* (Re-)Start timer. */ + osmo_timer_schedule(¬if->timer, NOTIFICATION_TIMEOUT, 0); + } else + update_call = true; + + /* Send notification of new or updated call to MM layer. */ + if (update_call) { + LOGP(DRR, LOGL_INFO, "Notify MM layer about new/updated group call.\n"); + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NOTIFICATION); + if (!nmsg) + return -ENOMEM; + mme = (struct gsm48_mm_event *) nmsg->data; + memcpy(mme->notification.gcr, gcr, sizeof(mme->notification.gcr)); + if (ch_desc) { + mme->notification.ch_desc_present = true; + memcpy(&mme->notification.ch_desc, ch_desc, sizeof(mme->notification.ch_desc)); + } + gsm48_mmevent_msg(ms, nmsg); + } + + return 0; +} + +/* Common function to decode Group Call Information */ +static int gsm48_rr_decode_group_call_info(struct osmocom_ms *ms, struct bitvec *bv, bool nch) +{ + struct bitvec *gcr_bv = NULL, *chd_bv = NULL; + int rc = 0; + int i; + + /* <Group Call Reference : bit(36)> */ + gcr_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(36), NULL); + OSMO_ASSERT(gcr_bv); + for (i = 0; i < 36; i++) + bitvec_set_bit(gcr_bv, bitvec_get_bit_pos(bv, bv->cur_bit++)); + /* Group Channel Description */ + if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) { + chd_bv = bitvec_alloc(OSMO_BYTES_FOR_BITS(24), NULL); + OSMO_ASSERT(chd_bv); + for (i = 0; i < 24; i++) + bitvec_set_bit(chd_bv, bitvec_get_bit_pos(bv, bv->cur_bit++)); + /* FIXME: hopping */ + if (bitvec_get_bit_pos(bv, bv->cur_bit++) == 1) { + LOGP(DRR, LOGL_ERROR, "Hopping not supported on VGCS/VBS channel, please fix!\n"); + rc = -ENOTSUP; + goto out; + } + } + + rc = gsm48_rr_rx_group_call(ms, gcr_bv->data, (chd_bv) ? chd_bv->data : NULL, nch); + +out: + bitvec_free(chd_bv); + bitvec_free(gcr_bv); + + return rc; +} + +/* Notification/FACCH (9.1.21a) */ +static int gsm48_rr_rx_notif_facch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_hdr_sh *sgh = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*sgh); + struct bitvec bv; + int rc; + + LOGP(DRR, LOGL_INFO, "NOTIFICATION/FACCH\n"); + + bv = (struct bitvec) { + .data = sgh->data, + .data_len = payload_len, + }; + + /* Group Call Information */ + if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 0) { + rc = gsm48_rr_decode_group_call_info(ms, &bv, false); + return rc; + } + + /* Note: Other information are not used. */ + return -ENOTSUP; +} + +/* Notification/NCH (9.1.21b) */ +static int gsm48_rr_rx_notif_nch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_notification_nch *nn = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*nn); + struct bitvec bv; + int rc; + + LOGP(DRR, LOGL_INFO, "NOTIFICATION/NCH\n"); + + bv = (struct bitvec) { + .data = nn->data, + .data_len = payload_len, + }; + + /* 0 | 1 <NLN(NCH) : bit (2) > */ + if (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) { + /* NLN not used + nln = bitvec_get_uint(&bv, 2); + nln_present = true; + */ + bv.cur_bit += 2; + } + + /* < list of Group Call NCH information > */ + while (bitvec_get_bit_pos(&bv, bv.cur_bit++) == 1) { + rc = gsm48_rr_decode_group_call_info(ms, &bv, true); + if (rc < 0) + break; + } + + return 0; +} + +/* + * VGCS uplink control + */ + +/* Send uplink status to upper layer. */ +static int gsm48_rr_uplink_status(struct osmocom_ms *ms, uint32_t event) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + + if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) + return -EINVAL; + + /* Send notification of uplink state to MM layer. */ + LOGP(DRR, LOGL_INFO, "Notify MM layer about uplink state.\n"); + nmsg = gsm48_mmevent_msgb_alloc(event); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(rr->ms, nmsg); + + return 0; +} + +/* UPLINK BUSY (9.1.46) */ +static int gsm48_rr_rx_uplink_busy(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DRR, LOGL_INFO, "UPLINK BUSY\n"); + + /* Only allow when in some group mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + /* Uplink is busy now. */ + if (rr->vgcs.uplink_free) { + rr->vgcs.uplink_free = false; + gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_BUSY); + } + stop_rr_t_ul_free(rr); + + /* Abort during uplink investigation or access procedure. */ + if (osmo_timer_pending(&rr->vgcs.t3128) || osmo_timer_pending(&rr->vgcs.t3130)) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due to busy uplink.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); + } + + return 0; +} + +/* UPLINK FREE (9.1.47) */ +static int gsm48_rr_rx_uplink_free(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct bitvec bv; + bool uplink_access = false; + uint8_t uic = 0xff; + uint8_t *mode; + + bv = (struct bitvec) { + .data_len = msgb_l3len(msg), + .data = msgb_l3(msg), + .cur_bit = 8, + }; + + /* Uplink Access */ + if (bitvec_get_bit_high(&bv) == H) + uplink_access = true; + + /* UIC */ + if (bitvec_get_bit_high(&bv) == H) + uic = bitvec_get_uint(&bv, 6); + + /* Note: Emergency Indicator not used. */ + + /* Do not flood the logging with UPLINK FREE messages. Log only on the fist received message. */ + if (!osmo_timer_pending(&rr->vgcs.t_ul_free)) + LOGP(DRR, LOGL_INFO, "UPLINK FREE (uplink access=%s, uic=0x%02x)\n", (uplink_access) ? "true" : "false", + uic); + + /* Uplink is free now. */ + if (!rr->vgcs.uplink_free) { + rr->vgcs.uplink_free = true; + gsm48_rr_uplink_status(ms, GSM48_MM_EVENT_UPLINK_FREE); + } + rr->vgcs.uic = uic; + rr->vgcs.uplink_access = uplink_access; + start_rr_t_ul_free(rr); + + /* We can be in group mode or in dedicated mode. When we are in dedicated mode and we receive UPLINK FREE, + * we know that we are actually on a group channel. This is the actual confirm to the UPLINK RELEASE message + * on a group channel. We must then release layer 2 locally and indicate channel release toward upper layer. + * + * When we are in group transmit mode, we return to group receive mode and also release layer 2 locally and + * indicate uplink release towards upper layer. + * + * When we are waiting for a free uplink (T3128 is running), we start uplink access. While accessing the + * uplink, we ignore further UPLINK FREE messages. + */ + if (rr->vgcs.group_state != GSM48_RR_GST_OFF) { + /* Start uplink access. */ + if (osmo_timer_pending(&rr->vgcs.t3128)) { + /* Stop timer, because uplink is now free. */ + stop_rr_t3128(rr); + rr->vgcs.uplink_tries = 3; + return gsm48_rr_uplink_access(ms, NULL); + } + + /* Ignore uplink free messages while accessing uplink. */ + if (osmo_timer_pending(&rr->vgcs.t3130)) + return 0; + } + + /* Any time in group transmit mode or dedicated mode, release on uplink free. */ + if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) { + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "Uplink becomes free, send (local) release to layer 2.\n"); + + /* Go into pending release state if not already. + * We are already in pending relese state when we release uplink normally. + * If we receive UPLINK FREE while we release normally, we abort layer 2. */ + if (rr->state != GSM48_RR_ST_REL_PEND) + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* release message */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = RSL_REL_LOCAL_END; + gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); + + return 0; + } + + return 0; +} + +/* UPLINK RELEASE (9.1.48) */ +static int gsm48_rr_rx_uplink_release(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_uplink_release *ur = (struct gsm48_uplink_release *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ur); + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short UPLINK RELEASE message.\n"); + return -EINVAL; + } + + LOGP(DRR, LOGL_INFO, "UPLINK RELEASE with cause 0x%02x\n", ur->rr_cause); + + /* Only allow when in some group mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL); + + return 0; +} + +/* VGCS UPLINK GRANT (9.1.49) */ +static int gsm48_rr_rx_vgcs_uplink_grant(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + struct gsm_settings *set = &ms->settings; + struct gsm0408_vgcs_ul_grant *ug = msgb_l3(msg); + int ug_len = msgb_l3len(msg) - sizeof(*ug); + + LOGP(DRR, LOGL_INFO, "VGCS UPLINK GRANT\n"); + + if (ug_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of VGCS UPLINK GRANT message.\n"); + return -EINVAL; + } + + /* Only allow when in some group mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + /* Uplink is busy now. */ + if (rr->vgcs.uplink_free) { + rr->vgcs.uplink_free = false; + gsm48_rr_uplink_status(rr->ms, GSM48_MM_EVENT_UPLINK_BUSY); + } + stop_rr_t_ul_free(rr); + + /* Abort during uplink investigation or access procedure. */ + if (osmo_timer_pending(&rr->vgcs.t3128)) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone accessing the uplink.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); + } + + /* We are not waiting for the uplink to be granted. */ + if (!osmo_timer_pending(&rr->vgcs.t3130)) + return 0; + + /* Stop timer. */ + stop_rr_t3130(rr); + + /* Check if message is for our request. */ + if (!gsm48_match_ra(ms, &ug->req_ref, 5)) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, other phone gets uplink access granted.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_UPLINK_BUSY); + } + + LOGP(DRR, LOGL_INFO, "Access to uplink has been granted.\n"); + + /* Set initial power and timing advance. */ + rr->cd_now.ind_tx_power = s->ms_txpwr_max_cch; + rr->cd_now.ind_ta = ug->ta; + LOGP(DRR, LOGL_INFO, "Applying initial ta and tx_power\n"); + l1ctl_tx_param_req(ms, rr->cd_now.ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value : s->ms_txpwr_max_cch); + + /* Turn on transmitter. */ + rr->cd_now.tch_flags &= ~(L1CTL_TCH_FLAG_RXONLY); + gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags); + + /* Complete group transmit mode. */ + new_rr_state(rr, GSM48_RR_ST_DEDICATED); + + /* Establish layer 2 connection. */ + return gsm48_rr_tx_talker_indication(ms); +} + +/* send rr uplink release */ +static int gsm48_rr_tx_uplink_release(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_uplink_release *ur; + + LOGP(DRR, LOGL_INFO, "UPLINK RELEASE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + ur = (struct gsm48_uplink_release *) msgb_put(nmsg, sizeof(*ur)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_UPLINK_RELEASE; + ur->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg, 0); +} + +/* Start uplink access procedure. (3.3.1.2.1.2) */ +static int gsm48_rr_uplink_access(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + /* store frame number */ + if (msg) { + struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + struct gsm48_req_ref *ref = + (struct gsm48_req_ref *) (ch->data + 1); + + if (msgb_l2len(msg) < sizeof(*ch) + sizeof(*ref)) { + LOGP(DRR, LOGL_ERROR, "CHAN_CNF too slort\n"); + return -EINVAL; + } + + /* Store to history buffer. */ + /* shift history and store */ + memcpy(&(rr->cr_hist[4]), &(rr->cr_hist[3]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[3]), &(rr->cr_hist[2]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]), + sizeof(struct gsm48_cr_hist)); + rr->cr_hist[0].valid = 1; + rr->cr_hist[0].ref.ra = rr->cr_ra; + rr->cr_hist[0].ref.t1 = ref->t1; + rr->cr_hist[0].ref.t2 = ref->t2; + rr->cr_hist[0].ref.t3_low = ref->t3_low; + rr->cr_hist[0].ref.t3_high = ref->t3_high; + } + + if (!osmo_timer_pending(&rr->vgcs.t3130)) { + uint8_t uplink_ref; + + /* Only try up to 3 times. */ + if (!rr->vgcs.uplink_tries) { + LOGP(DRR, LOGL_NOTICE, "Abort uplink access, due uplink access timeout.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE); + } + + LOGP(DRR, LOGL_INFO, "Trying to access uplink.\n"); + + rr->vgcs.uplink_tries--; + + /* See Table 9.1.45.1 */ + uplink_ref = layer23_random(); + uplink_ref &= 0x1f; + uplink_ref |= 0xc0; + + /* store value, mask and history */ + rr->cr_ra = uplink_ref; + rr->cr_hist[4].valid = 0; + rr->cr_hist[3].valid = 0; + rr->cr_hist[2].valid = 0; + rr->cr_hist[1].valid = 0; + rr->cr_hist[0].valid = 0; + + /* Reset counter. */ + rr->vgcs.uplink_counter = 0; + + /* Start T3130. */ + start_rr_t3130(rr, GSM_T3130_MS); + } + + /* Send random access bursts up to 5 times. */ + if (rr->vgcs.uplink_counter < 5) { + int delay_ms; + + /* The first UPLINK ACCESS message shall be delayed between 0..20ms. + * Subsequent UPLINK ACCESS messages shall be delayed 100ms + 0..20ms. */ + delay_ms = (layer23_random() & 0xffff) / 3277; + if (rr->vgcs.uplink_counter) + delay_ms += 100; + + gsm48_rr_tx_rand_acc_dedicated(ms, rr->cr_ra, delay_ms * 26 / 120, rr->vgcs.uic); + rr->vgcs.uplink_counter++; + } + + return 0; +} + +/* Whenever uplink access is released or failed for some reason. */ +static int gsm48_rr_uplink_access_abort(struct osmocom_ms *ms, uint8_t cause) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* Stop group transmit mode timers. */ + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Turn off transmitter. */ + rr->cd_now.tch_flags |= L1CTL_TCH_FLAG_RXONLY; + gsm48_rr_set_mode(ms, rr->cd_now.chan_nr, rr->cd_now.mode, rr->cd_now.tch_flags); + + /* Only return IDLE without changing channel, because we are still in group transmit mode. */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + + /* Set group state to receive mode. */ + rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; + + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* send talker indication */ +static int gsm48_rr_tx_talker_indication(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_talker_indication *ti; + + LOGP(DRR, LOGL_INFO, "TALKER INDICATION\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + ti = (struct gsm48_talker_indication *) msgb_put(nmsg, sizeof(*ti)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_TALKER_IND; + + /* classmark 2 */ + ti->cm2_len = sizeof(ti->cm2); + gsm48_rr_enc_cm2(ms, &ti->cm2, rr->cd_now.arfcn); + + /* mobile identity */ + if (ms->subscr.tmsi != GSM_RESERVED_TMSI && (osmo_lai_cmp(&subscr->lai, &cs->sel_cgi.lai) == 0)) { + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); + LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with TMSI.\n"); + } else if (subscr->imsi[0]) { + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); + LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION with IMSI.\n"); + } else { + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false); + LOGP(DRR, LOGL_INFO, "Sending TALKER INDICATION without TMSI/IMSI.\n"); + } + + /* start establishmnet */ + return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg, 0); +} + +/* * random access */ @@ -1304,7 +2188,7 @@ static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging, && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY && cs->state != GSM322_C7_CAMPED_ANY_CELL))) { LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n"); - return -EINVAL; + return -EINVAL; } /* ignore channel request while not camping on a cell */ @@ -1485,12 +2369,10 @@ rel_ind: return -EINVAL; } - /* store value, mask and history */ + /* store value, mask and clear history */ rr->chan_req_val = chan_req_val; rr->chan_req_mask = chan_req_mask; - rr->cr_hist[2].valid = 0; - rr->cr_hist[1].valid = 0; - rr->cr_hist[0].valid = 0; + memset(rr->cr_hist, 0, sizeof(rr->cr_hist)); /* store establishment cause, so 'choose cell' selects the last cell * after location updating */ @@ -1520,7 +2402,10 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) uint8_t tx_power; enum gsm_band band; - gsm_arfcn2band_rc(cs->arfcn, &band); + if (gsm_arfcn2band_rc(cs->arfcn, &band) != 0) { + LOGP(DRR, LOGL_ERROR, "gsm_arfcn2band_rc() failed\n"); + return -EINVAL; + } /* already assigned */ if (rr->wait_assign == 2) @@ -1538,10 +2423,7 @@ int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) } /* shift history and store */ - memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]), - sizeof(struct gsm48_cr_hist)); - memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]), - sizeof(struct gsm48_cr_hist)); + memmove(rr->cr_hist + 1, rr->cr_hist, sizeof(rr->cr_hist) - sizeof(rr->cr_hist[0])); rr->cr_hist[0].valid = 1; rr->cr_hist[0].ref.ra = rr->cr_ra; rr->cr_hist[0].ref.t1 = ref->t1; @@ -1709,32 +2591,31 @@ static int gsm48_new_sysinfo(struct osmocom_ms *ms, uint8_t type) && s->si5 && (!s->nb_ext_ind_si5 || s->si5bis)) { struct gsm48_rr_meas *rrmeas = &ms->rrlayer.meas; - int n = 0, i, refer_pcs; + int i; + bool refer_pcs; + int16_t arfcn; LOGP(DRR, LOGL_NOTICE, "Complete set of SI5* for BA(%d)\n", s->nb_ba_ind_si5); rrmeas->nc_num = 0; refer_pcs = gsm_refer_pcs(cs->arfcn, s); - /* collect channels from freq list (1..1023,0) */ - for (i = 1; i <= 1024; i++) { - if ((s->freq[i & 1023].mask & FREQ_TYPE_REP)) { - if (n == 32) { - LOGP(DRR, LOGL_NOTICE, "SI5* report " - "exceeds 32 BCCHs\n"); - break; - } - if (refer_pcs && i >= 512 && i <= 810) - rrmeas->nc_arfcn[n] = i | ARFCN_PCS; - else - rrmeas->nc_arfcn[n] = i & 1023; - rrmeas->nc_rxlev_dbm[n] = -128; - LOGP(DRR, LOGL_NOTICE, "SI5* report arfcn %s\n", - gsm_print_arfcn(rrmeas->nc_arfcn[n])); - n++; - } + /* Collect channels from freq list in correct order. */ + for (i = 0; i < 32; i++) { + arfcn = arfcn_from_freq_index(s, i); + if (arfcn < 0) + break; + if (refer_pcs && arfcn >= 512 && arfcn <= 810) + rrmeas->nc_arfcn[i] = arfcn | ARFCN_PCS; + else + rrmeas->nc_arfcn[i] = arfcn; + rrmeas->nc_rxlev_dbm[i] = -128; + LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis report arfcn %s (index %d)\n", + gsm_print_arfcn(rrmeas->nc_arfcn[i]), i); } - rrmeas->nc_num = n; + rrmeas->nc_num = i; + if (i == 32 && arfcn_from_freq_index(s, i) >= 0) + LOGP(DRR, LOGL_NOTICE, "SI5/SI5bis/SI5ter define more than 32 channels.\n"); } /* send sysinfo event to other layers */ @@ -1779,7 +2660,7 @@ static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg)))) + if (!memcmp(si, s->si1_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si1_msg)))) return 0; gsm48_decode_sysinfo1(s, si, msgb_l3len(msg)); @@ -1808,7 +2689,7 @@ static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg)))) + if (!memcmp(si, s->si2_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2_msg)))) return 0; gsm48_decode_sysinfo2(s, si, msgb_l3len(msg)); @@ -1837,7 +2718,7 @@ static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg), sizeof(s->si2b_msg)))) + if (!memcmp(si, s->si2b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2b_msg)))) return 0; gsm48_decode_sysinfo2bis(s, si, msgb_l3len(msg)); @@ -1866,7 +2747,7 @@ static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg), sizeof(s->si2t_msg)))) + if (!memcmp(si, s->si2t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si2t_msg)))) return 0; gsm48_decode_sysinfo2ter(s, si, msgb_l3len(msg)); @@ -1896,7 +2777,7 @@ static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg)))) + if (!memcmp(si, s->si3_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si3_msg)))) return 0; gsm48_decode_sysinfo3(s, si, msgb_l3len(msg)); @@ -1931,14 +2812,12 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg)))) + if (!memcmp(si, s->si4_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si4_msg)))) return 0; gsm48_decode_sysinfo4(s, si, msgb_l3len(msg)); - LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s " - "lac 0x%04x)\n", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac); + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (lai=%s)\n", osmo_lai_name(&s->lai)); return gsm48_new_sysinfo(ms, si->header.system_information); } @@ -1946,9 +2825,10 @@ static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5" message (9.1.37) */ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5 *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 " @@ -1962,7 +2842,7 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg)))) + if (!memcmp(si, s->si5_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5_msg)))) return 0; gsm48_decode_sysinfo5(s, si, msgb_l3len(msg)); @@ -1975,9 +2855,10 @@ static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5bis *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis" @@ -1991,7 +2872,7 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg), + if (!memcmp(si, s->si5b_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5b_msg)))) return 0; @@ -2005,9 +2886,10 @@ static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_5ter *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter" @@ -2021,7 +2903,7 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg), + if (!memcmp(si, s->si5t_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si5t_msg)))) return 0; @@ -2035,10 +2917,11 @@ static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) /* receive "SYSTEM INFORMATION 6" message (9.1.39) */ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_system_information_type_6 *si = msgb_l3(msg); + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1; struct gsm48_sysinfo *s = ms->cellsel.si; struct rx_meas_stat *meas = &ms->meas; - int payload_len = msgb_l3len(msg) - sizeof(*si); + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; if (!s) { LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 " @@ -2052,14 +2935,13 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) return -EINVAL; } - if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) + if (!memcmp(si, s->si6_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) return 0; gsm48_decode_sysinfo6(s, si, msgb_l3len(msg)); - LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s " - "lac 0x%04x SACCH-timeout %d)\n", gsm_print_mcc(s->mcc), - gsm_print_mnc(s->mnc), s->lac, s->sacch_radio_link_timeout); + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (lai=%s SACCH-timeout %d)\n", + osmo_lai_name(&s->lai), s->sacch_radio_link_timeout); meas->rl_fail = meas->s = s->sacch_radio_link_timeout; LOGP(DRR, LOGL_INFO, "using (new) SACCH timeout %d\n", meas->rl_fail); @@ -2067,6 +2949,72 @@ static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) return gsm48_new_sysinfo(ms, si->system_information); } +/* Receive "SYSTEM INFORMATION 10" message (9.1.50). */ +static int gsm48_rr_rx_sysinfo_10(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: Short L2 header is included in this structure */ + struct gsm48_system_information_type_10 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 10 ignored.\n"); + return -EINVAL; + } + + if (payload_len < 20) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 10 message.\n"); + return -EINVAL; + } + + /* No complete SI5, cannot decode yet. */ + if (!s->si5 || !(s->si5bis || !s->nb_ext_ind_si5)) + return 0; + + /* We decode when changed or when SI10 could not decoded, due to missing neighbor cell infos. */ + if (!memcmp(si, s->si10_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si10_msg))) && s->si10) + return 0; + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 10\n"); + + gsm48_decode_sysinfo10(s, si, msgb_l3len(msg)); + + /* We cannot call gsm48_new_sysinfo, because it requires regular message types. */ + return 0; +} + +/* receive "SYSTEM INFORMATION 13" message (9.1.43a) */ +static int gsm48_rr_rx_sysinfo13(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct gsm48_system_information_type_13 *si = msgb_l3(msg); + int rest_octets_len = msgb_l3len(msg) - sizeof(si->header); + struct gsm48_sysinfo *s = ms->cellsel.si; + + if (!s) { + LOGP(DRR, LOGL_INFO, + "No cell selected, SYSTEM INFORMATION 13 ignored\n"); + return -EINVAL; + } + + if (rest_octets_len < 0) { + LOGP(DRR, LOGL_NOTICE, + "Short read of SYSTEM INFORMATION 13 message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si13_msg, OSMO_MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) + return 0; + + gsm48_decode_sysinfo13(s, si, msgb_l3len(msg)); + + LOGP(DRR, LOGL_INFO, + "New SYSTEM INFORMATION 13 (%s, RAC 0x%02x, NCO %u, MNO %u)\n", + s->gprs.egprs_supported ? "EGPRS" : "GPRS only", + s->gprs.rac, s->gprs.nco, s->gprs.nmo); + + return gsm48_new_sysinfo(ms, si->header.system_information); +} + /* * paging */ @@ -2080,45 +3028,37 @@ static int gsm48_rr_chan2cause[4] = { }; /* given LV of mobile identity is checked against ms */ -static uint8_t gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi) +static uint8_t gsm_match_mi(struct osmocom_ms *ms, const uint8_t *mi_lv) { struct gsm322_cellsel *cs = &ms->cellsel; - char imsi[16]; - uint32_t tmsi; - uint8_t mi_type; + struct osmo_mobile_identity mi; + char buf[32]; + int rc; - if (mi[0] < 1) - return 0; - mi_type = mi[1] & GSM_MI_TYPE_MASK; - switch (mi_type) { + rc = osmo_mobile_identity_decode(&mi, mi_lv+1, mi_lv[0], false); + if (rc < 0) + return rc; + osmo_mobile_identity_to_str_buf(buf, sizeof(buf), &mi); + + switch (mi.type) { case GSM_MI_TYPE_TMSI: - if (mi[0] < 5) - return 0; - memcpy(&tmsi, mi+2, 4); - if (ms->subscr.tmsi == ntohl(tmsi) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { - LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", - ntohl(tmsi)); - - return mi_type; + if ((ms->subscr.tmsi == mi.tmsi) + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { + LOGP(DPAG, LOGL_INFO, " %s matches\n", buf); + return mi.type; } else - LOGP(DPAG, LOGL_INFO, " TMSI %08x (not for us)\n", - ntohl(tmsi)); + LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf); break; case GSM_MI_TYPE_IMSI: - gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]); - if (!strcmp(imsi, ms->subscr.imsi)) { - LOGP(DPAG, LOGL_INFO, " IMSI %s matches\n", imsi); - - return mi_type; + if (!strcmp(mi.imsi, ms->subscr.imsi)) { + LOGP(DPAG, LOGL_INFO, " %s matches\n", buf); + return mi.type; } else - LOGP(DPAG, LOGL_INFO, " IMSI %s (not for us)\n", imsi); + LOGP(DPAG, LOGL_INFO, " %s (not for us)\n", buf); break; default: LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n", - mi_type); + mi.type); } return 0; @@ -2217,9 +3157,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) chan_2 = pa->cneed2; /* first MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi1) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, GSM_MI_TYPE_TMSI); @@ -2228,9 +3166,7 @@ static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi1)); /* second MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi2) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, GSM_MI_TYPE_TMSI); @@ -2287,9 +3223,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) chan_4 = pa->cneed4; /* first MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi1) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi1)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1, GSM_MI_TYPE_TMSI); @@ -2298,9 +3232,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi1)); /* second MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi2) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi2)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1, GSM_MI_TYPE_TMSI); @@ -2309,9 +3241,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi2)); /* third MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi3) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi3)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1, GSM_MI_TYPE_TMSI); @@ -2320,9 +3250,7 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) ntohl(pa->tmsi3)); /* fourth MI */ if (ms->subscr.tmsi == ntohl(pa->tmsi4) - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac) { + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0)) { LOGP(DPAG, LOGL_INFO, " TMSI %08x matches\n", ntohl(pa->tmsi4)); return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1, GSM_MI_TYPE_TMSI); @@ -2338,23 +3266,22 @@ static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) */ /* match request reference against request history */ -static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref) +static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref, uint8_t hist_num) { struct gsm48_rrlayer *rr = &ms->rrlayer; int i; uint8_t ia_t1, ia_t2, ia_t3; uint8_t cr_t1, cr_t2, cr_t3; - for (i = 0; i < 3; i++) { + for (i = 0; i < hist_num; i++) { /* filter confirmed RACH requests only */ if (rr->cr_hist[i].valid && ref->ra == rr->cr_hist[i].ref.ra) { - ia_t1 = ref->t1; - ia_t2 = ref->t2; - ia_t3 = (ref->t3_high << 3) | ref->t3_low; - ref = &rr->cr_hist[i].ref; - cr_t1 = ref->t1; - cr_t2 = ref->t2; - cr_t3 = (ref->t3_high << 3) | ref->t3_low; + ia_t1 = ref->t1; + ia_t2 = ref->t2; + ia_t3 = (ref->t3_high << 3) | ref->t3_low; + cr_t1 = rr->cr_hist[i].ref.t1; + cr_t2 = rr->cr_hist[i].ref.t2; + cr_t3 = (rr->cr_hist[i].ref.t3_high << 3) | rr->cr_hist[i].ref.t3_low; if (ia_t1 == cr_t1 && ia_t2 == cr_t2 && ia_t3 == cr_t3) { LOGP(DRR, LOGL_INFO, "request %02x matches " @@ -2425,7 +3352,12 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n"); cd.chan_nr = ia->chan_desc.chan_nr; - rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd.chan_nr); + return -EINVAL; + } if (ia->chan_desc.h0.h) { cd.h = 1; gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio, @@ -2439,8 +3371,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) } else { cd.h = 0; gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd.arfcn |= ARFCN_PCS; + cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn); LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ia->timing_advance, @@ -2461,7 +3392,7 @@ static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) } /* request ref */ - if (gsm48_match_ra(ms, &ia->req_ref)) { + if (gsm48_match_ra(ms, &ia->req_ref, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); /* timing advance */ @@ -2539,7 +3470,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n"); cd1.chan_nr = ia->chan_desc1.chan_nr; - rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd1.chan_nr); + return -EINVAL; + } if (ia->chan_desc1.h0.h) { cd1.h = 1; gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio, @@ -2553,8 +3489,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } else { cd1.h = 0; gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd1.arfcn |= ARFCN_PCS; + cd1.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd1.arfcn); LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", ia->timing_advance1, @@ -2563,7 +3498,12 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) gsm_print_arfcn(cd1.arfcn), ch_ts, ch_subch, cd1.tsc); } cd2.chan_nr = ia->chan_desc2.chan_nr; - rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd2.chan_nr); + return -EINVAL; + } if (ia->chan_desc2.h0.h) { cd2.h = 1; gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio, @@ -2577,8 +3517,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } else { cd2.h = 0; gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd2.arfcn |= ARFCN_PCS; + cd2.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd2.arfcn); LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " "chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", ia->timing_advance2, @@ -2599,7 +3538,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) } /* request ref 1 */ - if (gsm48_match_ra(ms, &ia->req_ref1)) { + if (gsm48_match_ra(ms, &ia->req_ref1, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now)); /* timing advance */ @@ -2615,7 +3554,7 @@ static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_dl_est(ms); } /* request ref 2 */ - if (gsm48_match_ra(ms, &ia->req_ref2)) { + if (gsm48_match_ra(ms, &ia->req_ref2, IMM_ASS_HISTORY)) { /* channel description */ memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now)); /* timing advance */ @@ -2665,7 +3604,7 @@ static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) (((uint8_t *)&ia->req_ref1) + i * 4); LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT " "(ref 0x%02x)\n", req_ref->ra); - if (gsm48_match_ra(ms, req_ref)) { + if (gsm48_match_ra(ms, req_ref, IMM_ASS_HISTORY)) { /* wait indication */ t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4); if (t3122_value) @@ -2694,12 +3633,13 @@ static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg) struct tlv_parsed tp; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } - tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0); return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } @@ -2753,7 +3693,7 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) if (rep_valid) { int8_t strongest, current; uint8_t ncc; - int i, index; + int i, index, strongest_i; #if 0 /* FIXME: multi-band reporting, if not: 0 = normal reporting */ @@ -2767,13 +3707,20 @@ static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) current = -128; /* -infinite */ index = 0; for (i = 0; i < rrmeas->nc_num; i++) { + /* Skip stronger cells that have been added to measurement report so far. */ + if (rrmeas->nc_rxlev_dbm[i] > strongest) + continue; + /* Skip cells with equal strength that have been added so far. */ + if (rrmeas->nc_rxlev_dbm[i] == strongest && i <= strongest_i) + continue; /* only check if NCC is permitted */ ncc = rrmeas->nc_bsic[i] >> 3; if ((s->nb_ncc_permitted_si6 & (1 << ncc)) - && rrmeas->nc_rxlev_dbm[i] > current - && rrmeas->nc_rxlev_dbm[i] < strongest) { + && rrmeas->nc_rxlev_dbm[i] > current) { current = rrmeas->nc_rxlev_dbm[i]; + strongest = current; index = i; + strongest_i = i; } } if (current == -128) /* no more found */ @@ -2878,6 +3825,9 @@ int gsm48_rr_los(struct osmocom_ms *ms) LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n"); + if (rr->vgcs.group_state != GSM48_RR_GST_OFF) + return gsm48_rr_group_rel(ms, RR_REL_CAUSE_LOST_SIGNAL); + /* stop T3211 if running */ stop_rr_t3110(rr); @@ -2982,18 +3932,22 @@ static int gsm48_rr_activate_channel(struct osmocom_ms *ms, gsm48_rr_tx_meas_rep(ms); /* establish */ - LOGP(DRR, LOGL_INFO, "establishing channel in dedicated mode\n"); - rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); - LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, " - "audio-mode %d, cipher %d\n", ch_type, ch_subch, ch_ts, - cd->mode, rr->audio_mode, rr->cipher_type + 1); + LOGP(DRR, LOGL_INFO, "establishing channel in dedicated/group mode\n"); + + if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd->chan_nr); + return -EINVAL; + } + + LOGP(DRR, LOGL_INFO, " Channel type %d, subch %d, ts %d, mode %d, audio-mode %d, flags 0x%02x, cipher %d\n", + ch_type, ch_subch, ch_ts, cd->mode, rr->audio_mode, cd->tch_flags, rr->cipher_type + 1); if (cd->h) - l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, - ma, ma_len, cd->chan_nr, cd->tsc, cd->mode, - rr->audio_mode); + l1ctl_tx_dm_est_req_h1(ms, cd->maio, cd->hsn, ma, ma_len, cd->chan_nr, cd->tsc, cd->mode, + rr->audio_mode, cd->tch_flags); else - l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, - cd->mode, rr->audio_mode); + l1ctl_tx_dm_est_req_h0(ms, cd->arfcn, cd->chan_nr, cd->tsc, cd->mode, rr->audio_mode, cd->tch_flags); rr->dm_est = 1; /* old SI 5/6 are not valid on a new dedicated channel */ @@ -3023,7 +3977,7 @@ static int gsm48_rr_channel_after_time(struct osmocom_ms *ms, l1ctl_tx_crypto_req(ms, rr->cd_now.chan_nr, rr->cipher_type + 1, subscr->key, 8); - gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); + gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode, cd->tch_flags); return 0; } @@ -3035,8 +3989,8 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, struct gsm322_cellsel *cs = &ms->cellsel; struct gsm48_sysinfo *s = cs->si; struct gsm_settings *set = &ms->settings; - int i, pcs, index; - uint16_t arfcn; + int i, index; + uint16_t arfcn, pcs; pcs = gsm_refer_pcs(cs->arfcn, s) ? ARFCN_PCS : 0; @@ -3139,7 +4093,9 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, /* convert to band_arfcn and check for unsported frequency */ for (i = 0; i < *ma_len; i++) { - arfcn = ma[i] | pcs; + arfcn = ma[i]; + if (arfcn >= 512 && arfcn <= 810) + arfcn |= pcs; ma[i] = arfcn; index = arfcn2index(arfcn); if (!(set->freq_map[index >> 3] & (1 << (index & 7)))) { @@ -3156,12 +4112,11 @@ static int gsm48_rr_render_ma(struct osmocom_ms *ms, struct gsm48_rr_cd *cd, static int gsm48_rr_dl_est(struct osmocom_ms *ms) { struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm322_cellsel *cs = &ms->cellsel; struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; struct msgb *nmsg; struct gsm48_hdr *gh; struct gsm48_pag_rsp *pr; - uint8_t mi[11]; uint16_t ma[64]; uint8_t ma_len; @@ -3235,26 +4190,21 @@ static int gsm48_rr_dl_est(struct osmocom_ms *ms) pr->cm2_len = sizeof(pr->cm2); gsm48_rr_enc_cm2(ms, &pr->cm2, rr->cd_now.arfcn); /* mobile identity */ - if (ms->subscr.tmsi != 0xffffffff - && ms->subscr.mcc == cs->sel_mcc - && ms->subscr.mnc == cs->sel_mnc - && ms->subscr.lac == cs->sel_lac + if (ms->subscr.tmsi != GSM_RESERVED_TMSI + && (osmo_lai_cmp(&ms->subscr.lai, &cs->sel_cgi.lai) == 0) && rr->paging_mi_type == GSM_MI_TYPE_TMSI) { - gsm48_generate_mid_from_tmsi(mi, subscr->tmsi); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_TMSI, false); LOGP(DRR, LOGL_INFO, "sending paging response with " "TMSI\n"); } else if (subscr->imsi[0]) { - gsm48_generate_mid_from_imsi(mi, subscr->imsi); + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_IMSI, false); LOGP(DRR, LOGL_INFO, "sending paging response with " "IMSI\n"); } else { - mi[1] = 1; - mi[2] = 0xf0 | GSM_MI_TYPE_NONE; + gsm48_encode_mi_lv(ms, nmsg, GSM_MI_TYPE_NONE, false); LOGP(DRR, LOGL_INFO, "sending paging response without " "TMSI/IMSI\n"); } - msgb_put(nmsg, 1 + mi[1]); - memcpy(pr->data, mi + 1, 1 + mi[1]); } #ifdef TEST_FREQUENCY_MOD @@ -3328,6 +4278,12 @@ static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg) struct msgb *nmsg; struct gsm48_rr_hdr *nrrh; + /* Handle on group channel. */ + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release indication.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL); + } + /* switch back to old channel, if modify/ho failed */ switch (rr->modify_state) { case GSM48_RR_MOD_ASSIGN: @@ -3374,12 +4330,13 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) uint8_t *mode; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } - tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0); LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x\n", cr->rr_cause); @@ -3397,6 +4354,9 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) new_rr_state(rr, GSM48_RR_ST_REL_PEND); + /* When we receive a channel release, we are not just releasing the transmit mode. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + /* start T3110, so that two DISCs can be sent due to T200 timeout */ start_rr_t3110(rr, 1, 500000); @@ -3414,27 +4374,59 @@ static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) return 0; } +/* Release of channel on VGCS/VBS. */ +static int gsm48_rr_rx_chan_rel_ui(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr); + struct tlv_parsed tp; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + LOGP(DRR, LOGL_INFO, "CHANNEL RELESE via UI frame with cause 0x%02x\n", cr->rr_cause); + + /* Only allow when in group receive mode. */ + if (rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) + return 0; + + return gsm48_rr_group_rel(ms, RR_REL_CAUSE_NORMAL); +} + /* * frequency redefition, channel mode modify, assignment, and handover */ /* set channel mode in case of TCH */ -static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, - uint8_t mode) +static int gsm48_rr_set_mode(struct osmocom_ms *ms, uint8_t chan_nr, uint8_t mode, uint8_t tch_flags) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t ch_type, ch_subch, ch_ts; + if (rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, chan_nr); + return -EINVAL; + } + /* only apply mode to TCH/F or TCH/H */ - rsl_dec_chan_nr(chan_nr, &ch_type, &ch_subch, &ch_ts); if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return -ENOTSUP; - /* setting (new) timing advance */ - LOGP(DRR, LOGL_INFO, "setting TCH mode to %d, audio mode to %d\n", - mode, rr->audio_mode); - l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, rr->tch_loop_mode); + /* Apply indicated channel mode */ + LOGP(DRR, LOGL_INFO, "setting TCH mode to %s, audio mode to %d, tch flags to %d\n", + get_value_string(gsm48_chan_mode_names, mode), rr->audio_mode, tch_flags); + l1ctl_tx_tch_mode_req(ms, mode, rr->audio_mode, tch_flags, rr->tch_loop_mode); return 0; } @@ -3471,7 +4463,12 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ LOGP(DRR, LOGL_INFO, "FREQUENCY REDEFINITION:\n"); cd.chan_nr = fr->chan_desc.chan_nr; - rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd.chan_nr); + return -EINVAL; + } if (fr->chan_desc.h0.h) { cd.h = 1; gsm48_decode_chan_h1(&fr->chan_desc, &cd.tsc, &cd.maio, @@ -3481,8 +4478,7 @@ static int gsm48_rr_rx_frq_redef(struct osmocom_ms *ms, struct msgb *msg) } else { cd.h = 0; gsm48_decode_chan_h0(&fr->chan_desc, &cd.tsc, &cd.arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd.arfcn |= ARFCN_PCS; + cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn); LOGP(DRR, LOGL_INFO, " (ARFCN %s TS %u SS %u TSC %u)\n", gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc); } @@ -3561,7 +4557,7 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm); struct gsm48_rr_cd *cd = &rr->cd_now; uint8_t ch_type, ch_subch, ch_ts; - uint8_t cause; + int rc; LOGP(DRR, LOGL_INFO, "CHANNEL MODE MODIFY\n"); @@ -3575,7 +4571,12 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description */ cd->chan_nr = cm->chan_desc.chan_nr; - rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cd->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd->chan_nr); + return -EINVAL; + } if (cm->chan_desc.h0.h) { cd->h = 1; gsm48_decode_chan_h1(&cm->chan_desc, &cd->tsc, &cd->maio, @@ -3586,21 +4587,36 @@ static int gsm48_rr_rx_chan_modify(struct osmocom_ms *ms, struct msgb *msg) } else { cd->h = 0; gsm48_decode_chan_h0(&cm->chan_desc, &cd->tsc, &cd->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cd->arfcn |= ARFCN_PCS; + cd->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd->arfcn); LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u " "TSC %u mode %u)\n", cm->chan_desc.chan_nr, gsm_print_arfcn(cd->arfcn), ch_ts, ch_subch, cd->tsc, cm->mode); } - /* mode */ - cause = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode); - if (cause) - return gsm48_rr_tx_rr_status(ms, cause); + + /** + * According to 3GPP TS 04.08, section 3.4.6.1.3 + * "Abnormal cases" of "channel mode modify procedure", + * if the MS doesn't support the indicated channel mode, + * it shall retain the old mode and return the associated + * channel mode information in the ACKNOWLEDGE message. + */ + + /* Check if we support this channel mode */ + rc = gsm48_rr_check_mode(ms, cd->chan_nr, cm->mode); + if (rc) + goto ack; + + /* Attempt to apply this mode */ + rc = gsm48_rr_set_mode(ms, cd->chan_nr, cm->mode, cd->tch_flags); + if (rc) + goto ack; + + /* Finally set (a new) mode */ cd->mode = cm->mode; - gsm48_rr_set_mode(ms, cd->chan_nr, cd->mode); - return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cm->mode); +ack: + return gsm48_rr_tx_chan_modify_ack(ms, &cm->chan_desc, cd->mode); } /* 9.1.3 sending ASSIGNMENT COMPLETE */ @@ -3686,19 +4702,25 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) cdb->ind_tx_power = rr->cd_now.ind_tx_power; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } - tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0); /* decode channel description (before time) */ if (TLVP_PRESENT(&tp, GSM48_IE_CH_DESC_1_BEFORE)) { struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *) TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE); cdb->chan_nr = ccd->chan_nr; - rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cdb->chan_nr); + return -EINVAL; + } if (ccd->h0.h) { cdb->h = 1; gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio, @@ -3709,8 +4731,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cdb->arfcn |= ARFCN_PCS; + cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn); LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr, gsm_print_arfcn(cdb->arfcn), @@ -3721,7 +4742,12 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description (after time) */ cda->chan_nr = ac->chan_desc.chan_nr; - rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cda->chan_nr); + return -EINVAL; + } if (ac->chan_desc.h0.h) { cda->h = 1; gsm48_decode_chan_h1(&ac->chan_desc, &cda->tsc, &cda->maio, @@ -3732,8 +4758,7 @@ static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cda->h = 0; gsm48_decode_chan_h0(&ac->chan_desc, &cda->tsc, &cda->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cda->arfcn |= ARFCN_PCS; + cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn); LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u " "SS %u TSC %u)\n", ac->chan_desc.chan_nr, gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc); @@ -4059,10 +5084,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) cdb->ind_tx_power = rr->cd_now.ind_tx_power; if (payload_len < 0) { - LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND " - "message.\n"); - return gsm48_rr_tx_rr_status(ms, - GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + if (tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0) < 0) { + LOGP(DRR, LOGL_ERROR, "%s(): tlv_parse() failed\n", __func__); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); } /* cell description */ @@ -4072,8 +5099,6 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) rr->chan_req_val = ho->ho_ref; rr->chan_req_mask = 0x00; - tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0); - /* sync ind */ if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) { gsm48_decode_sync_ind(rr, (struct gsm48_sync_ind *) @@ -4087,7 +5112,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_chan_desc *ccd = (struct gsm48_chan_desc *) TLVP_VAL(&tp, GSM48_IE_CH_DESC_1_BEFORE); cdb->chan_nr = ccd->chan_nr; - rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cdb->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cdb->chan_nr); + return -EINVAL; + } if (ccd->h0.h) { cdb->h = 1; gsm48_decode_chan_h1(ccd, &cdb->tsc, &cdb->maio, @@ -4098,8 +5128,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cdb->h = 0; gsm48_decode_chan_h0(ccd, &cdb->tsc, &cdb->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cdb->arfcn |= ARFCN_PCS; + cdb->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cdb->arfcn); LOGP(DRR, LOGL_INFO, " before: (chan_nr 0x%02x " "ARFCN %s TS %u SS %u TSC %u)\n", ccd->chan_nr, gsm_print_arfcn(cdb->arfcn), @@ -4110,7 +5139,12 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) /* decode channel description (after time) */ cda->chan_nr = ho->chan_desc.chan_nr; - rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(cda->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cda->chan_nr); + return -EINVAL; + } if (ho->chan_desc.h0.h) { cda->h = 1; gsm48_decode_chan_h1(&ho->chan_desc, &cda->tsc, &cda->maio, @@ -4121,8 +5155,7 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) } else { cda->h = 0; gsm48_decode_chan_h0(&ho->chan_desc, &cda->tsc, &cda->arfcn); - if (gsm_refer_pcs(cs->arfcn, s)) - cda->arfcn |= ARFCN_PCS; + cda->arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cda->arfcn); LOGP(DRR, LOGL_INFO, " after: (chan_nr 0x%02x ARFCN %s TS %u " "SS %u TSC %u)\n", ho->chan_desc.chan_nr, gsm_print_arfcn(cda->arfcn), ch_ts, ch_subch, cda->tsc); @@ -4368,6 +5401,16 @@ static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) return 0; } +/* send HANDOVER/UPLINK ACCESS burst (9.1.14) */ +static int gsm48_rr_tx_rand_acc_dedicated(struct osmocom_ms *ms, uint8_t ref, uint16_t offset, int8_t uic) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DRR, LOGL_INFO, "send access burst on DCCH (ref 0x%02x)\n", ref); + + return l1ctl_tx_rach_req(ms, rr->cd_now.chan_nr, 0x00, ref, 0, offset, uic); +} + /* send all queued messages down to layer 2 */ static int gsm48_rr_dequeue_down(struct osmocom_ms *ms) { @@ -4390,17 +5433,27 @@ static int gsm48_rr_dequeue_down(struct osmocom_ms *ms) return 0; } -/* channel is resumed in dedicated mode */ +/* Channel is resumed in dedicated mode or uplink is estabished. */ static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; - LOGP(DRR, LOGL_INFO, "data link is resumed\n"); + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "data link established on uplink\n"); - /* transmit queued frames during ho / ass transition */ - gsm48_rr_dequeue_down(ms); + /* Confirm uplink access. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_CNF); + if (nmsg) + gsm48_rr_upmsg(ms, nmsg); + } else { + LOGP(DRR, LOGL_INFO, "data link is resumed\n"); - rr->modify_state = GSM48_RR_MOD_NONE; + /* transmit queued frames during ho / ass transition */ + gsm48_rr_dequeue_down(ms); + + rr->modify_state = GSM48_RR_MOD_NONE; + } return 0; } @@ -4490,6 +5543,13 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) struct gsm48_rr_hdr *nrrh; uint16_t acc_class; + /* Reject during group call. */ + if (rr->vgcs.group_state != GSM48_RR_GST_OFF) { + LOGP(DRR, LOGL_INFO, "We have a group call, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + /* 3.3.1.1.3.2 */ if (osmo_timer_pending(&rr->t3122)) { if (rrh->cause != RR_EST_CAUSE_EMERGENCY) { @@ -4577,10 +5637,317 @@ static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)), msgb_l3(msg), msgb_l3len(msg)); + if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) { + /* Release group receive mode. */ + l1ctl_tx_dm_rel_req(rr->ms); + rr->ms->meas.rl_fail = 0; + l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_SCHED); + } + /* request channel */ return gsm48_rr_chan_req(ms, rrh->cause, 0, 0); } +/* 3.3.3.2 Request for group receive mode. */ +static int gsm48_rr_group_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm48_chan_desc *ch_desc = (struct gsm48_chan_desc *)(msg->data + sizeof(struct gsm48_rr_hdr)); + uint8_t cause; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + struct gsm48_rr_cd cd; + uint8_t ch_type, ch_subch, ch_ts; + uint16_t ma[64]; + uint8_t ma_len; + int rc; + + /* if state is not idle */ + if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_OFF) { + LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; +reject: + LOGP(DSUM, LOGL_INFO, "Joining group call channel not possible\n"); + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); + } + + /* cell selected */ + if (!cs->selected) { + LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* check if camping */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL) { + LOGP(DRR, LOGL_INFO, "Not camping, rejecting! " + "(cs->state = %d)\n", cs->state); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* get channel description */ + memset(&cd, 0, sizeof(cd)); + cd.chan_nr = ch_desc->chan_nr; + if (rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRR, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, cd.chan_nr); + cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + goto reject; + } + if (ch_desc->h0.h) { + LOGP(DRR, LOGL_ERROR, "HOPPING NOT SUPPORTED, PLEASE FIX!\n"); + cd.h = 1; + gsm48_decode_chan_h1(ch_desc, &cd.tsc, &cd.maio, + &cd.hsn); + LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ch_desc->chan_nr, cd.maio, cd.hsn, ch_ts, ch_subch, cd.tsc); + } else { + cd.h = 0; + gsm48_decode_chan_h0(ch_desc, &cd.tsc, &cd.arfcn); + cd.arfcn = gsm_arfcn_refer_pcs(cs->arfcn, s, cd.arfcn); + LOGP(DRR, LOGL_INFO, " (chan_nr 0x%02x ARFCN %s TS %u SS %u TSC %u)\n", + ch_desc->chan_nr, gsm_print_arfcn(cd.arfcn), ch_ts, ch_subch, cd.tsc); + } + if (gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len)) { + cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + goto reject; + } + + /* Turn off transmitter. */ + cd.tch_flags |= L1CTL_TCH_FLAG_RXONLY; + + /* Set mode to Speech V1. FIXME: Add AMR support. */ + cd.mode = GSM48_CMODE_SPEECH_V1; + + /* Set current channel and also store as 'vgcs.cd_group', used when leaving dedicated mode. */ + memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); + memcpy(&rr->vgcs.cd_group, &cd, sizeof(rr->vgcs.cd_group)); + + /* tell cell selection process to leave idle mode + * NOTE: this must be sent unbuffered, because the state may not + * change until idle mode is left + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE); + if (!nmsg) + return -ENOMEM; + rc = gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + if (rc) { + LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n"); + cause = GSM48_RR_CAUSE_CHAN_MODE_UNACCT; + goto reject; + } + + /* Initial uplink state. */ + rr->vgcs.uplink_free = false; + + /* Set group state to receive mode. */ + rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; + + /* Wait until synced to the CCCH ... */ + return 0; +} +/* ... Continue after synced to the CCCH. */ +static int gsm48_rr_group_req_continue(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint16_t ma[64]; + uint8_t ma_len; + struct msgb *nmsg; + + /* get hopping sequence, if required */ + gsm48_rr_render_ma(ms, &rr->vgcs.cd_group, ma, &ma_len); + /* activate channel */ + gsm48_rr_activate_channel(ms, &rr->vgcs.cd_group, ma, ma_len); + + /* Confirm group call channel. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_CNF); + if (!nmsg) + return -ENOMEM; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* After "loss of signal"/release in group transmit or receive mode, return IDLE and release towards MM. */ +static int gsm48_rr_group_rel(struct osmocom_ms *ms, int cause) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint8_t *mode; + + /* Stop group receive and transmit timers. */ + stop_rr_t_ul_free(rr); + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + if (rr->state == GSM48_RR_ST_DEDICATED || rr->state == GSM48_RR_ST_REL_PEND) { + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "Channel lost, send (local) release to layer 2.\n"); + + /* Go into group receive mode, so that the channel gets released on LAPD release confirm. */ + rr->vgcs.group_state = GSM48_RR_GST_RECEIVE; + if (rr->state != GSM48_RR_ST_REL_PEND) + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* release message */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = RSL_REL_LOCAL_END; + gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); + + /* release SAPI 3 link, if exits */ + gsm48_release_sapi3_link(ms); + + /* Wait for LAPD to confirm. Then return idle. */ + return 0; + } + + /* Go back to IDLE mode. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + new_rr_state(rr, GSM48_RR_ST_IDLE); + + /* Indicate release to MM. */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* Release group channel, return to IDLE. */ +static int gsm48_rr_group_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + /* Only in group receive/transmit mode. */ + if (rr->vgcs.group_state == GSM48_RR_GST_OFF) + return 0; + + if (rr->state == GSM48_RR_ST_CONN_PEND || rr->state == GSM48_RR_ST_DEDICATED) { + LOGP(DRR, LOGL_INFO, "Connot release group channel yet, perform uplink release first.\n"); + gsm48_rr_uplink_rel_req(ms, msg); + if (rr->state == GSM48_RR_ST_REL_PEND) { + /* Leave the group call state, so that we change to IDLE when we released the uplink. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + return 0; + } + } + + /* Stop group receive and transmit timers. */ + stop_rr_t_ul_free(rr); + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Unset group state. Wait, if release is pending. */ + rr->vgcs.group_state = GSM48_RR_GST_OFF; + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DRR, LOGL_INFO, "Cannot release group channel yet, wait to return to IDLE state.\n"); + return 0; + } + + LOGP(DRR, LOGL_INFO, "Release Group channel.\n"); + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* Request uplink in group receive mode. */ +static int gsm48_rr_uplink_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* Only in group receive mode */ + if (rr->state != GSM48_RR_ST_IDLE || rr->vgcs.group_state != GSM48_RR_GST_RECEIVE) { + LOGP(DRR, LOGL_INFO, "We are not in group receive mode yet, rejecting!\n"); + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_UPLINK_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_TRY_LATER; + return gsm48_rr_upmsg(ms, nmsg); + } + + LOGP(DRR, LOGL_INFO, "Changing from group receive mode to group transmit mode.\n"); + + /* Enter uplink access procedure. */ + new_rr_state(rr, GSM48_RR_ST_CONN_PEND); + + /* Set group state to transmit mode. */ + rr->vgcs.group_state = GSM48_RR_GST_TRANSMIT; + + /* Uplink investigation procedure (3.3.1.2.1.1) */ + if (!rr->vgcs.uplink_free) { + start_rr_t3128(rr, GSM_T3128_MS); + return 0; + } + + rr->vgcs.uplink_tries = 3; + return gsm48_rr_uplink_access(ms, NULL); +} + +/* Leave uplink, also called when leaving group channel. */ +static int gsm48_rr_uplink_rel_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + uint8_t *mode; + + /* Only in group transmit mode */ + if (rr->vgcs.group_state != GSM48_RR_GST_TRANSMIT) + return -EINVAL; + + /* Stop group transmit mode timers. */ + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Continue if in dedicated mode, so we release and wait for uplink to become free. */ + if (rr->state != GSM48_RR_ST_DEDICATED) + return 0; + + LOGP(DRR, LOGL_INFO, "Returning from group transmit to group receive mode.\n"); + + /* Leave uplink, wait for uplink being free, channel release or channel/link failure. */ + gsm48_rr_tx_uplink_release(ms, GSM48_RR_CAUSE_LEAVE_GROUP_CA); + + /* Go into release pending mode. */ + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* Special setting where we release locally. This means we wait for free uplink an then release. */ + if (set->uplink_release_local) { + LOGP(DRR, LOGL_INFO, "Release L2 locally as specified via VTY. Wait for UPLINK FREE.\n"); + return 0; + } + + /* Release dedicated mode. */ + /* disconnect the main signalling link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = RSL_REL_NORMAL; + gsm48_send_rsl_nol3(ms, RSL_MT_REL_REQ, nmsg, 0); + + return 0; +} + /* 3.4.2 transfer data in dedicated mode */ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg) { @@ -4625,6 +5992,8 @@ static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg) /* 3.4.2 data from layer 2 to RR and upper layer*/ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) { + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t sapi = rllh->link_id & 7; struct gsm48_hdr *gh = msgb_l3(msg); struct gsm48_rr_hdr *rrh; uint8_t pdisc = gh->proto_discr & 0x0f; @@ -4664,6 +6033,9 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) case GSM48_MT_RR_CHAN_REL: rc = gsm48_rr_rx_chan_rel(ms, msg); break; + case GSM48_MT_RR_UPLINK_RELEASE: + rc = gsm48_rr_rx_uplink_release(ms, msg); + break; case GSM48_MT_RR_APP_INFO: LOGP(DRR, LOGL_NOTICE, "APP INFO not supported!\n"); break; @@ -4690,6 +6062,7 @@ static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) msgb_push(msg, sizeof(struct gsm48_rr_hdr)); rrh = (struct gsm48_rr_hdr *)msg->data; rrh->msg_type = GSM48_RR_DATA_IND; + rrh->sapi = sapi; return gsm48_rr_upmsg(ms, msg); } @@ -4712,6 +6085,8 @@ static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_rx_sysinfo3(ms, msg); case GSM48_MT_RR_SYSINFO_4: return gsm48_rr_rx_sysinfo4(ms, msg); + case GSM48_MT_RR_SYSINFO_13: + return gsm48_rr_rx_sysinfo13(ms, msg); default: #if 0 LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n", @@ -4726,6 +6101,11 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_system_information_type_header *sih = msgb_l3(msg); + if (msgb_l3len(msg) < sizeof(*sih)) { + LOGP(DRR, LOGL_NOTICE, "Short read of CCCH message.\n"); + return -EINVAL; + } + switch (sih->system_information) { case GSM48_MT_RR_PAG_REQ_1: return gsm48_rr_rx_pag_req_1(ms, msg); @@ -4740,6 +6120,10 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_rx_imm_ass_ext(ms, msg); case GSM48_MT_RR_IMM_ASS_REJ: return gsm48_rr_rx_imm_ass_rej(ms, msg); + + case GSM48_MT_RR_NOTIF_NCH: + return gsm48_rr_rx_notif_nch(ms, msg); + default: #if 0 LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n", @@ -4749,48 +6133,70 @@ static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) } } +#define N201_Bter_SACCH 21 +#define N201_Bter_SDCCH_FACCH 23 +#define N201_B4 19 + /* receive ACCH at RR layer */ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) { - struct gsm48_rrlayer *rr = &ms->rrlayer; - struct gsm_settings *set = &ms->settings; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - struct gsm48_hdr *sih = msgb_l3(msg); - uint8_t ind_ta, ind_tx_power; - - if (msgb_l2len(msg) < sizeof(*rllh) + 2 + 2) { - LOGP(DRR, LOGL_ERROR, "Missing TA and TX_POWER IEs\n"); - return -EINVAL; + const struct gsm48_system_information_type_header *sih; + const struct gsm48_hdr_sh *sgh; + const struct gsm48_hdr *gh; + + /* Bter frame (SACCH or SDCCH/FACCH) */ + if (msgb_l3len(msg) == N201_Bter_SACCH || msgb_l3len(msg) == N201_Bter_SDCCH_FACCH) { + sgh = msgb_l3(msg); + if (sgh->rr_short_pd != GSM48_PDISC_SH_RR) { + LOGP(DRR, LOGL_NOTICE, "Short header message is not an RR message.\n"); + return -EINVAL; + } + switch (sgh->msg_type) { + case GSM48_MT_RR_SH_UL_FREE: + return gsm48_rr_rx_uplink_free(ms, msg); + case GSM48_MT_RR_SH_FACCH: + return gsm48_rr_rx_notif_facch(ms, msg); + case GSM48_MT_RR_SH_SI10: + return gsm48_rr_rx_sysinfo_10(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "Short header message type 0x%02x unsupported.\n", sgh->msg_type); + return -EINVAL; + } } - ind_ta = rllh->data[1]; - ind_tx_power = rllh->data[3]; - LOGP(DRR, LOGL_INFO, "Indicated ta %d (actual ta %d)\n", - ind_ta, ind_ta - set->alter_delay); - LOGP(DRR, LOGL_INFO, "Indicated tx_power %d\n", - ind_tx_power); - if (ind_ta != rr->cd_now.ind_ta - || ind_tx_power != rr->cd_now.ind_tx_power) { - LOGP(DRR, LOGL_INFO, "setting new ta and tx_power\n"); - l1ctl_tx_param_req(ms, ind_ta - set->alter_delay, - (set->alter_tx_power) ? set->alter_tx_power_value - : ind_tx_power); - rr->cd_now.ind_ta = ind_ta; - rr->cd_now.ind_tx_power = ind_tx_power; + /* B4 frame (SACCH) */ + if ((msg->cb[0] & 0x40) && msgb_l3len(msg) == N201_B4) { + sih = msgb_l3(msg); + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_5: + return gsm48_rr_rx_sysinfo5(ms, msg); + case GSM48_MT_RR_SYSINFO_5bis: + return gsm48_rr_rx_sysinfo5bis(ms, msg); + case GSM48_MT_RR_SYSINFO_5ter: + return gsm48_rr_rx_sysinfo5ter(ms, msg); + case GSM48_MT_RR_SYSINFO_6: + return gsm48_rr_rx_sysinfo6(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", sih->system_information); + return -EINVAL; + } } - switch (sih->msg_type) { - case GSM48_MT_RR_SYSINFO_5: - return gsm48_rr_rx_sysinfo5(ms, msg); - case GSM48_MT_RR_SYSINFO_5bis: - return gsm48_rr_rx_sysinfo5bis(ms, msg); - case GSM48_MT_RR_SYSINFO_5ter: - return gsm48_rr_rx_sysinfo5ter(ms, msg); - case GSM48_MT_RR_SYSINFO_6: - return gsm48_rr_rx_sysinfo6(ms, msg); + /* B frame (UI frame on VGCS) */ + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DRR, LOGL_NOTICE, "ACCH message too short.\n"); + return -EINVAL; + } + gh = msgb_l3(msg); + switch (gh->msg_type) { + case GSM48_MT_RR_VGCS_UPL_GRANT: + return gsm48_rr_rx_vgcs_uplink_grant(ms, msg); + case GSM48_MT_RR_UPLINK_BUSY: + return gsm48_rr_rx_uplink_busy(ms, msg); + case GSM48_MT_RR_CHAN_REL: + return gsm48_rr_rx_chan_rel_ui(ms, msg); default: - LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", - sih->msg_type); + LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", gh->msg_type); return -EINVAL; } } @@ -4798,15 +6204,40 @@ static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) /* unit data from layer 2 to RR layer */ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) { + struct gsm48_rrlayer *rr = &ms->rrlayer; struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_settings *set = &ms->settings; struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); struct tlv_parsed tv; + int ind_ta = -1, ind_tx_power = -1; uint8_t ch_type, ch_subch, ch_ts; DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", rllh->chan_nr, rllh->link_id); - rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) { + LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return -EINVAL; + } + + /* Update TX power and timing advance, if included in message. */ + if (TLVP_PRES_LEN(&tv, RSL_IE_TIMING_ADVANCE, 1)) { + ind_ta = *TLVP_VAL(&tv, RSL_IE_TIMING_ADVANCE); + LOGP(DRR, LOGL_INFO, "DL SACCH indicates ta %d (actual ta %d)\n", ind_ta, ind_ta - set->alter_delay); + } + if (TLVP_PRES_LEN(&tv, RSL_IE_MS_POWER, 1)) { + ind_tx_power = *TLVP_VAL(&tv, RSL_IE_MS_POWER); + LOGP(DRR, LOGL_INFO, "DL SACCH indicates tx_power %d\n", ind_tx_power); + } + if ((ind_ta >= 0 && ind_ta != rr->cd_now.ind_ta) || + (ind_tx_power >= 0 && ind_tx_power != rr->cd_now.ind_tx_power)) { + LOGP(DRR, LOGL_INFO, "Applying new ta and tx_power\n"); + l1ctl_tx_param_req(ms, ind_ta - set->alter_delay, + (set->alter_tx_power) ? set->alter_tx_power_value : ind_tx_power); + rr->cd_now.ind_ta = ind_ta; + rr->cd_now.ind_tx_power = ind_tx_power; + } + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); return -EIO; @@ -4822,6 +6253,10 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) LOGP(DCS, LOGL_INFO, "Channel provides data.\n"); cs->ccch_state = GSM322_CCCH_ST_DATA; + /* in group receive mode */ + if (ms->rrlayer.vgcs.group_state == GSM48_RR_GST_RECEIVE) + return gsm48_rr_group_req_continue(ms); + /* in dedicated mode */ if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) return gsm48_rr_tx_rand_acc(ms, NULL); @@ -4840,7 +6275,13 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) // TODO: timer depends on BCCH config } - rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rllh->chan_nr); + return -EINVAL; + } + switch (ch_type) { case RSL_CHAN_PCH_AGCH: return gsm48_rr_rx_pch_agch(ms, msg); @@ -4850,6 +6291,7 @@ static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) case RSL_CHAN_Lm_ACCHs: case RSL_CHAN_SDCCH4_ACCH: case RSL_CHAN_SDCCH8_ACCH: + msg->cb[0] = rllh->link_id; return gsm48_rr_rx_acch(ms, msg); default: LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n", @@ -4908,6 +6350,12 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) uint16_t ma[64]; uint8_t ma_len; + /* Handle on group channel. */ + if (rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link release confirm.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_NORMAL); + } + /* switch back to old channel, if modify/ho failed */ switch (rr->modify_state) { case GSM48_RR_MOD_ASSIGN: @@ -4954,7 +6402,11 @@ static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) stop_rr_t3110(rr); /* send release indication */ - nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (rr->vgcs.group_state == GSM48_RR_GST_RECEIVE) { + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_GROUP_REL_IND); + rr->vgcs.group_state = GSM48_RR_GST_OFF; + } else + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); if (!nmsg) return -ENOMEM; nrrh = (struct gsm48_rr_hdr *)nmsg->data; @@ -5002,6 +6454,12 @@ static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg) if (rr->modify_state) return 0; + /* Handle on group channel. */ + if ((link_id & 7) == 0 && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + LOGP(DRR, LOGL_INFO, "Returning to group receive mode, due to link failure.\n"); + return gsm48_rr_uplink_access_abort(ms, RR_REL_CAUSE_LINK_FAILURE); + } + /* send abort ind to upper layer */ nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_IND); if (!nmsg) @@ -5164,7 +6622,13 @@ static int gsm48_rr_est_req_sapi3(struct osmocom_ms *ms, struct msgb *msg) return gsm48_rr_upmsg(ms, nmsg); } - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rr->cd_now.chan_nr); + return -EINVAL; + } + if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) { LOGP(DRR, LOGL_INFO, "Requesting DCCH link, because no TCH " @@ -5258,11 +6722,6 @@ static struct dldatastate { {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated}, -#if 0 - {SBIT(GSM48_RR_ST_DEDICATED), - RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated}, -#endif - {SBIT(GSM48_RR_ST_DEDICATED), RSL_MT_ERROR_IND, gsm48_rr_mdl_error_ind}, }; @@ -5359,11 +6818,24 @@ static int gsm48_rcv_cch(struct osmocom_ms *ms, struct msgb *msg) "%s\n", ms->name, rsl_msg_name(msg_type), gsm48_rr_state_names[rr->state]); - if (rr->state == GSM48_RR_ST_CONN_PEND - && msg_type == RSL_MT_CHAN_CONF) { - rc = gsm48_rr_tx_rand_acc(ms, msg); - msgb_free(msg); - return rc; + if (msg_type == RSL_MT_CHAN_CONF) { + /* Recevie confirm to get the FN when the access burst was transmitted on VGCS channel. */ + if (rr->state == GSM48_RR_ST_CONN_PEND && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + rc = gsm48_rr_uplink_access(ms, msg); + msgb_free(msg); + return rc; + } + /* Ignore subsequent access bursts in dedicated group transmit mode. */ + if (rr->state == GSM48_RR_ST_DEDICATED && rr->vgcs.group_state == GSM48_RR_GST_TRANSMIT) { + msgb_free(msg); + return 0; + } + /* Recevie confirm to get the FN when the access burst was transmitted on CCCH. */ + if (rr->state == GSM48_RR_ST_CONN_PEND) { + rc = gsm48_rr_tx_rand_acc(ms, msg); + msgb_free(msg); + return rc; + } } LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); @@ -5415,6 +6887,20 @@ static struct rrdownstate { {SBIT(GSM48_RR_ST_CONN_PEND) | SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */ GSM48_RR_ABORT_REQ, gsm48_rr_abort_req}, + + /* NOTE: If not IDLE, it is rejected there. */ + {ALL_STATES, /* 3.3.3.2 */ + GSM48_RR_GROUP_REQ, gsm48_rr_group_req}, + + {ALL_STATES, + GSM48_RR_GROUP_REL_REQ, gsm48_rr_group_rel_req}, + + /* NOTE: If not IDLE, it is rejected there. */ + {ALL_STATES, /* 3.3.1.2 */ + GSM48_RR_UPLINK_REQ, gsm48_rr_uplink_req}, + + {ALL_STATES, /* 3.4.13.4 */ + GSM48_RR_UPLINK_REL_REQ, gsm48_rr_uplink_rel_req}, }; #define RRDOWNSLLEN \ @@ -5509,8 +6995,11 @@ int gsm48_rr_init(struct osmocom_ms *ms) start_rr_t_meas(rr, 1, 0); - rr->audio_mode = AUDIO_TX_MICROPHONE | AUDIO_RX_SPEAKER; rr->tch_loop_mode = L1CTL_TCH_LOOP_OPEN; + rr->audio_mode = 0x00; /* set in tch_{voice,data}_state_init() */ + + /* List of notifications about ongoing ASCI calls */ + INIT_LLIST_HEAD(&rr->vgcs.notif_list); return 0; } @@ -5540,6 +7029,12 @@ int gsm48_rr_exit(struct osmocom_ms *ms) stop_rr_t3122(rr); stop_rr_t3124(rr); stop_rr_t3126(rr); + stop_rr_t_ul_free(rr); + stop_rr_t3128(rr); + stop_rr_t3130(rr); + + /* Free pending list entries. */ + asci_notif_list_free(rr); return 0; } @@ -5583,112 +7078,29 @@ static void start_rr_t3124(struct gsm48_rrlayer *rr, int sec, int micro) osmo_timer_schedule(&rr->t3124, sec, micro); } -/* send HANDOVER ACCESS burst (9.1.14) */ -static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms) -{ - nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS"); - if (!nmsg) - return -ENOMEM; - *msgb_put(nmsg, 1) = rr->hando_ref; - todo burst - return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg, 0); -} - -/* send next channel request in dedicated state */ -static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm48_rrlayer *rr = &ms->rrlayer; - struct msgb *nmsg; - int s; - - if (rr->modify_state != GSM48_RR_MOD_HANDO) { - LOGP(DRR, LOGL_NOTICE, "Random access confirm, but not in handover state.\n"); - return 0; - } - - /* send up to four handover access bursts */ - if (rr->hando_acc_left) { - rr->hando_acc_left--; - gsm48_rr_tx_hando_access(ms); - return; - } - - /* start timer for sending next HANDOVER ACCESS bursts afterwards */ - if (!osmo_timer_pending(&rr->t3124)) { - if (allocated channel is SDCCH) - start_rr_t3124(rr, GSM_T3124_675); - else - start_rr_t3124(rr, GSM_T3124_320); - } - if (!rr->n_chan_req) { - start_rr_t3126(rr, 5, 0); /* TODO improve! */ - return 0; - } - rr->n_chan_req--; - - /* wait for PHYSICAL INFORMATION message or T3124 timeout */ - return 0; - -} - #endif -#define LOG_FRAME_VERIFY(mode, level, fmt, args...) \ - LOGP(DRR, level, "Voice frame, mode=%s: " fmt, get_value_string(gsm48_chan_mode_names, mode), ## args) - -int voice_frame_verify(enum gsm48_chan_mode mode, uint8_t *frame, size_t frame_len) -{ - switch (mode) { - case GSM48_CMODE_SPEECH_V1: - /* FIXME: this is FR only, check for TCH/F (FR) and TCH/H (HR) */ - /* RFC 3551, section 4.5.8 GSM */ - if (frame_len != GSM_FR_BYTES) { - LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect length (%zu != %u)\n", frame_len, GSM_FR_BYTES); - return -2; - } - if ((frame[0] >> 4) != 0xd) { - LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect magic (%u != 0xd)\n", frame[0] >> 4); - return -3; - } - break; - case GSM48_CMODE_SPEECH_EFR: - /* RFC 3551, section 4.5.9 GSM-EFR */ - if (frame_len != GSM_EFR_BYTES) { - LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect length (%zu != %u)\n", frame_len, GSM_EFR_BYTES); - return -4; - } - if ((frame[0] >> 4) != 0xc) { - LOG_FRAME_VERIFY(mode, LOGL_ERROR, "incorrect magic (%u != 0xc)\n", frame[0] >> 4); - return -5; - } - break; - default: - LOG_FRAME_VERIFY(mode, LOGL_ERROR, "not implemented\n"); - return -1; - } - return 0; -} - -int gsm48_rr_tx_voice(struct osmocom_ms *ms, struct msgb *msg) +int gsm48_rr_tx_traffic(struct osmocom_ms *ms, struct msgb *msg) { struct gsm48_rrlayer *rr = &ms->rrlayer; uint8_t ch_type, ch_subch, ch_ts; - struct l1ctl_traffic_req *tr; if (!rr->dm_est) { LOGP(DRR, LOGL_INFO, "Current channel is not active\n"); goto error; } - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); - if (ch_type != RSL_CHAN_Bm_ACCHs) { - LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH/F\n"); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rr->cd_now.chan_nr); goto error; } - tr = (struct l1ctl_traffic_req *)msg->l2h; - if (voice_frame_verify(rr->cd_now.mode, tr->data, msgb_l2len(msg)) < 0) + if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) { + LOGP(DRR, LOGL_INFO, "Current channel is not (yet) TCH\n"); goto error; + } return l1ctl_tx_traffic_req(ms, msg, rr->cd_now.chan_nr, 0); error: @@ -5708,10 +7120,16 @@ int gsm48_rr_audio_mode(struct osmocom_ms *ms, uint8_t mode) if (!rr->dm_est) return 0; - rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts) != 0) { + LOGP(DRSL, LOGL_ERROR, + "%s(): rsl_dec_chan_nr(chan_nr=0x%02x) failed\n", + __func__, rr->cd_now.chan_nr); + return -EINVAL; + } + if (ch_type != RSL_CHAN_Bm_ACCHs && ch_type != RSL_CHAN_Lm_ACCHs) return 0; - return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode, rr->tch_loop_mode); + return l1ctl_tx_tch_mode_req(ms, rr->cd_now.mode, mode, rr->cd_now.tch_flags, rr->tch_loop_mode); } diff --git a/src/host/layer23/src/mobile/main.c b/src/host/layer23/src/mobile/main.c index b1e0940b..8720ea76 100644 --- a/src/host/layer23/src/mobile/main.c +++ b/src/host/layer23/src/mobile/main.c @@ -15,14 +15,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> #include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/vty.h> #include <osmocom/bb/mobile/app_mobile.h> #include <osmocom/core/talloc.h> @@ -31,6 +29,10 @@ #include <osmocom/core/gsmtap.h> #include <osmocom/core/signal.h> #include <osmocom/core/application.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/logging.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/ports.h> #include <arpa/inet.h> @@ -46,51 +48,43 @@ #include <time.h> #include <libgen.h> +#include "config.h" + void *l23_ctx = NULL; +struct l23_global_config l23_cfg; struct llist_head ms_list; -static char *gsmtap_ip = 0; static const char *custom_cfg_file = NULL; -struct gsmtap_inst *gsmtap_inst = NULL; +static const char *log_cat_mask = NULL; +static char *config_file = NULL; char *config_dir = NULL; -int use_mncc_sock = 0; int daemonize = 0; +int quit = 0; -int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg); +int (*l23_app_start)(void) = NULL; +int (*l23_app_work)(void) = NULL; +int (*l23_app_exit)(void) = NULL; int mobile_delete(struct osmocom_ms *ms, int force); -int mobile_signal_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data); int mobile_work(struct osmocom_ms *ms); int mobile_exit(struct osmocom_ms *ms, int force); const char *debug_default = - "DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DSAP:DGPS:DMOB:DPRIM:DLUA"; - -const char *openbsc_copyright = - "Copyright (C) 2010-2015 Andreas Eversberg, Sylvain Munaut, Holger Freyther, Harald Welte\n" - "Contributions by Alex Badea, Pablo Neira, Steve Markgraf and others\n\n" - "License GPLv2+: GNU GPL version 2 or later " - "<http://gnu.org/licenses/gpl.html>\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n"; + "DCS:DNB:DPLMN:DRR:DMM:DSIM:DCC:DMNCC:DSS:DLSMS:DPAG:DSUM:DSAP:DGPS:DMOB:DPRIM:DLUA:DGAPK"; static void print_usage(const char *app) { printf("Usage: %s\n", app); } -static void print_help() +static void print_help(void) { printf(" Some help...\n"); printf(" -h --help this text\n"); - printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n"); printf(" -d --debug Change debug flags. default: %s\n", debug_default); printf(" -D --daemonize Run as daemon\n"); printf(" -c --config-file filename The config file to use.\n"); - printf(" -m --mncc-sock Disable built-in MNCC handler and " - "offer socket\n"); } static int handle_options(int argc, char **argv) @@ -99,12 +93,12 @@ static int handle_options(int argc, char **argv) int option_index = 0, c; static struct option long_options[] = { {"help", 0, 0, 'h'}, - {"gsmtap-ip", 1, 0, 'i'}, {"debug", 1, 0, 'd'}, {"daemonize", 0, 0, 'D'}, {"config-file", 1, 0, 'c'}, - {"mncc-sock", 0, 0, 'm'}, /* DEPRECATED options, to be removed */ + {"gsmtap-ip", 1, 0, 'i'}, + {"mncc-sock", 0, 0, 'm'}, {"vty-ip", 1, 0, 'u'}, {"vty-port", 1, 0, 'v'}, {0, 0, 0, 0}, @@ -121,22 +115,26 @@ static int handle_options(int argc, char **argv) print_help(); exit(0); break; - case 'i': - gsmtap_ip = optarg; - break; case 'c': custom_cfg_file = optarg; break; case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); + log_cat_mask = optarg; break; case 'D': daemonize = 1; break; - case 'm': - use_mncc_sock = 1; - break; /* DEPRECATED options, to be removed */ + case 'i': + fprintf(stderr, "Option 'i' is deprecated! " + "Please use the configuration file " + "in order to set GSMTAP parameters.\n"); + return -EINVAL; + case 'm': + fprintf(stderr, "Option 'm' is deprecated! " + "Please use the configuration file " + "in order to change the MNCC handler.\n"); + return -EINVAL; case 'u': case 'v': fprintf(stderr, "Both 'u' and 'v' options are " @@ -205,13 +203,60 @@ void sighandler(int sigset) } } +static void print_copyright(void) +{ + printf("%s" + "%s\n" + "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n\n", + l23_app_info.copyright ? l23_app_info.copyright : "", + l23_app_info.contribution ? l23_app_info.contribution : ""); +} + +static int _vty_init(void) +{ + int rc; + + OSMO_ASSERT(l23_app_info.vty_info != NULL); + l23_app_info.vty_info->tall_ctx = l23_ctx; + + vty_init(l23_app_info.vty_info); + logging_vty_add_cmds(); + + if (l23_app_info.vty_init != NULL) + l23_app_info.vty_init(); + if (config_file) { + LOGP(DLGLOBAL, LOGL_INFO, "Using configuration from '%s'\n", config_file); + l23_vty_reading = true; + rc = vty_read_config_file(config_file, NULL); + l23_vty_reading = false; + if (rc < 0) { + LOGP(DLGLOBAL, LOGL_FATAL, + "Failed to parse the configuration file '%s'\n", config_file); + return rc; + } + } + rc = telnet_init_default(l23_ctx, NULL, OSMO_VTY_PORT_BB); + if (rc < 0) { + LOGP(DMOB, LOGL_FATAL, "Cannot init VTY on %s port %u: %s\n", + vty_get_bind_addr(), OSMO_VTY_PORT_BB, strerror(errno)); + return rc; + } + return rc; +} + int main(int argc, char **argv) { - char *config_file; - int quit = 0; int rc; - printf("%s\n", openbsc_copyright); + print_copyright(); + + rc = handle_options(argc, argv); + if (rc) { /* Abort in case of parsing errors */ + fprintf(stderr, "Error in command line options. Exiting.\n"); + return 1; + } srand(time(NULL)); @@ -224,19 +269,10 @@ int main(int argc, char **argv) /* Init default stderr logging */ osmo_init_logging2(l23_ctx, &log_info); - rc = handle_options(argc, argv); - if (rc) { /* Abort in case of parsing errors */ - fprintf(stderr, "Error in command line options. Exiting.\n"); - return 1; - } - - if (gsmtap_ip) { - gsmtap_inst = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1); - if (!gsmtap_inst) { - fprintf(stderr, "Failed during gsmtap_init()\n"); - exit(1); - } - gsmtap_source_add_sink(gsmtap_inst); + rc = l23_app_init(); + if (rc < 0) { + fprintf(stderr, "Failed during l23_app_init()\n"); + exit(1); } if (custom_cfg_file) { @@ -257,12 +293,32 @@ int main(int argc, char **argv) config_dir = talloc_strdup(l23_ctx, config_file); config_dir = dirname(config_dir); - if (use_mncc_sock) - rc = l23_app_init(mncc_recv_socket, config_file); - else - rc = l23_app_init(NULL, config_file); - if (rc) - exit(rc); + if (l23_app_info.opt_supported & L23_OPT_VTY) { + if (_vty_init() < 0) + exit(1); + } + + if (log_cat_mask != NULL) + log_parse_category_mask(osmo_stderr_target, log_cat_mask); + + if (l23_cfg.gsmtap.remote_host) { + l23_cfg.gsmtap.inst = gsmtap_source_init2(l23_cfg.gsmtap.local_host, 0, + l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT, 1); + if (!l23_cfg.gsmtap.inst) { + fprintf(stderr, "Failed during gsmtap_source_init2(%s -> %s:%u)\n", + l23_cfg.gsmtap.local_host, l23_cfg.gsmtap.remote_host, GSMTAP_UDP_PORT); + exit(1); + } + gsmtap_source_add_sink(l23_cfg.gsmtap.inst); + } + + if (l23_app_start) { + rc = l23_app_start(); + if (rc < 0) { + fprintf(stderr, "Failed during l23_app_start()\n"); + exit(1); + } + } signal(SIGINT, sighandler); signal(SIGTSTP, sighandler); @@ -281,13 +337,18 @@ int main(int argc, char **argv) } while (1) { - l23_app_work(&quit); + l23_app_work(); if (quit && llist_empty(&ms_list)) break; osmo_select_main(0); } - l23_app_exit(); + if (l23_app_exit) + rc = l23_app_exit(); + + if (l23_app_info.opt_supported & L23_OPT_VTY) + telnet_exit(); + log_fini(); talloc_free(config_file); diff --git a/src/host/layer23/src/mobile/mncc_sock.c b/src/host/layer23/src/mobile/mncc_sock.c index 5ea6feba..d31df291 100644 --- a/src/host/layer23/src/mobile/mncc_sock.c +++ b/src/host/layer23/src/mobile/mncc_sock.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -50,13 +46,19 @@ int mncc_sock_from_cc(struct mncc_sock_state *state, struct msgb *msg) /* Check if we currently have a MNCC handler connected */ if (state->conn_bfd.fd < 0) { + struct gsm_mncc mncc_out; LOGP(DMNCC, LOGL_ERROR, "mncc_sock receives %s for external CC app " "but socket is gone\n", get_mncc_name(msg_type)); - if (msg_type != GSM_TCHF_FRAME - && msg_type != GSM_TCHF_FRAME_EFR - && msg_type != MNCC_REL_IND) { + switch (msg_type) { + case MNCC_REL_IND: + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + case GSM_TCHH_FRAME: + case GSM_TCH_FRAME_AMR: + case GSM_BAD_FRAME: + break; + default: /* release the request */ - struct gsm_mncc mncc_out; memset(&mncc_out, 0, sizeof(mncc_out)); mncc_out.callref = mncc_in->callref; mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU, @@ -87,9 +89,9 @@ static void mncc_sock_close(struct mncc_sock_state *state) LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has closed connection\n"); + osmo_fd_unregister(bfd); close(bfd->fd); bfd->fd = -1; - osmo_fd_unregister(bfd); /* re-enable the generation of ACCEPT for new connections */ osmo_fd_read_enable(&state->listen_bfd); diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c index 9a37b973..67cf5a72 100644 --- a/src/host/layer23/src/mobile/mnccms.c +++ b/src/host/layer23/src/mobile/mnccms.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -26,9 +22,12 @@ #include <stdlib.h> #include <osmocom/core/talloc.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/settings.h> #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/mncc_ms.h> #include <osmocom/bb/mobile/vty.h> @@ -36,7 +35,15 @@ static uint32_t new_callref = 1; static LLIST_HEAD(call_list); -static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc); +static const char * const gsm_call_type_str[] = { + [GSM_CALL_T_UNKNOWN] = "unknown", + [GSM_CALL_T_VOICE] = "voice", + [GSM_CALL_T_DATA] = "data", + [GSM_CALL_T_DATA_FAX] = "fax", +}; + +static int dtmf_statemachine(struct gsm_call *call, + const struct gsm_mncc *mncc); static void timeout_dtmf(void *arg); /* @@ -82,72 +89,72 @@ struct gsm_call *get_call_ref(uint32_t callref) return NULL; } -static int8_t mncc_get_bearer(struct gsm_settings *set, uint8_t speech_ver) +static int8_t mncc_get_bearer(const struct gsm_settings *set, uint8_t speech_ver) { switch (speech_ver) { - case 4: + case GSM48_BCAP_SV_AMR_F: if (set->full_v3) LOGP(DMNCC, LOGL_INFO, " net suggests full rate v3\n"); else { LOGP(DMNCC, LOGL_INFO, " full rate v3 not supported\n"); - speech_ver = -1; + return -1; } break; - case 2: + case GSM48_BCAP_SV_EFR: if (set->full_v2) LOGP(DMNCC, LOGL_INFO, " net suggests full rate v2\n"); else { LOGP(DMNCC, LOGL_INFO, " full rate v2 not supported\n"); - speech_ver = -1; + return -1; } break; - case 0: /* mandatory */ + case GSM48_BCAP_SV_FR: /* mandatory */ if (set->full_v1) LOGP(DMNCC, LOGL_INFO, " net suggests full rate v1\n"); else { LOGP(DMNCC, LOGL_INFO, " full rate v1 not supported\n"); - speech_ver = -1; + return -1; } break; - case 5: + case GSM48_BCAP_SV_AMR_H: if (set->half_v3) LOGP(DMNCC, LOGL_INFO, " net suggests half rate v3\n"); else { LOGP(DMNCC, LOGL_INFO, " half rate v3 not supported\n"); - speech_ver = -1; + return -1; } break; - case 1: + case GSM48_BCAP_SV_HR: if (set->half_v1) LOGP(DMNCC, LOGL_INFO, " net suggests half rate v1\n"); else { LOGP(DMNCC, LOGL_INFO, " half rate v1 not supported\n"); - speech_ver = -1; + return -1; } break; default: LOGP(DMNCC, LOGL_INFO, " net suggests unknown speech version " "%d\n", speech_ver); - speech_ver = -1; + return -1; } return speech_ver; } -static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver, - struct gsm_mncc *mncc) +static void mncc_set_bcap_speech(struct gsm_mncc *mncc, + const struct gsm_settings *set, + int speech_ver) { - struct gsm_settings *set = &ms->settings; int i = 0; mncc->fields |= MNCC_F_BEARER_CAP; - mncc->bearer_cap.coding = 0; + mncc->bearer_cap.coding = GSM48_BCAP_CODING_GSM_STD; if (set->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH && (set->half_v1 || set->half_v3)) { - mncc->bearer_cap.radio = 3; + mncc->bearer_cap.radio = GSM48_BCAP_RRQ_DUAL_FR; LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n"); } else { - mncc->bearer_cap.radio = 1; + mncc->bearer_cap.radio = GSM48_BCAP_RRQ_FR_ONLY; LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n"); } mncc->bearer_cap.speech_ctm = 0; @@ -155,41 +162,379 @@ static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver, if (speech_ver < 0) { /* if half rate is supported and preferred */ if (set->half_v3 && set->half && set->half_prefer) { - mncc->bearer_cap.speech_ver[i++] = 5; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_AMR_H; LOGP(DMNCC, LOGL_INFO, " support half rate v3\n"); } if (set->half_v1 && set->half && set->half_prefer) { - mncc->bearer_cap.speech_ver[i++] = 1; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_HR; LOGP(DMNCC, LOGL_INFO, " support half rate v1\n"); } /* if full rate is supported */ if (set->full_v3) { - mncc->bearer_cap.speech_ver[i++] = 4; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_AMR_F; LOGP(DMNCC, LOGL_INFO, " support full rate v3\n"); } if (set->full_v2) { - mncc->bearer_cap.speech_ver[i++] = 2; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_EFR; LOGP(DMNCC, LOGL_INFO, " support full rate v2\n"); } if (set->full_v1) { /* mandatory, so it's always true */ - mncc->bearer_cap.speech_ver[i++] = 0; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_FR; LOGP(DMNCC, LOGL_INFO, " support full rate v1\n"); } /* if half rate is supported and not preferred */ if (set->half_v3 && set->half && !set->half_prefer) { - mncc->bearer_cap.speech_ver[i++] = 5; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_AMR_H; LOGP(DMNCC, LOGL_INFO, " support half rate v3\n"); } if (set->half_v1 && set->half && !set->half_prefer) { - mncc->bearer_cap.speech_ver[i++] = 1; + mncc->bearer_cap.speech_ver[i++] = GSM48_BCAP_SV_HR; LOGP(DMNCC, LOGL_INFO, " support half rate v1\n"); } /* if specific speech_ver is given (it must be supported) */ } else mncc->bearer_cap.speech_ver[i++] = speech_ver; mncc->bearer_cap.speech_ver[i] = -1; /* end of list */ - mncc->bearer_cap.transfer = 0; - mncc->bearer_cap.mode = 0; + mncc->bearer_cap.transfer = GSM48_BCAP_ITCAP_SPEECH; + mncc->bearer_cap.mode = GSM48_BCAP_TMOD_CIRCUIT; +} + +static const struct bcap_data_set { + enum gsm48_bcap_ra ra; + enum gsm48_bcap_interm_rate ir; + enum gsm48_bcap_user_rate ur; + enum gsm48_bcap_modem_type mt; +} bcap_data_set_map[] = { + [DATA_CALL_TR_V21_300] = { + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_300, + .mt = GSM48_BCAP_MT_V21, + }, + [DATA_CALL_TR_V22_1200] = { + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_1200, + .mt = GSM48_BCAP_MT_V22, + }, + [DATA_CALL_TR_V23_1200_75] = { + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_1200_75, + .mt = GSM48_BCAP_MT_V23, + }, + [DATA_CALL_TR_V22bis_2400] = { + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_2400, + .mt = GSM48_BCAP_MT_V22bis, + }, + [DATA_CALL_TR_V26ter_2400] = { + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_2400, + .mt = GSM48_BCAP_MT_V26ter, + }, + [DATA_CALL_TR_V32_4800] = { + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_4800, + .mt = GSM48_BCAP_MT_V32, + }, + [DATA_CALL_TR_V32_9600] = { + .ir = GSM48_BCAP_IR_16k, + .ur = GSM48_BCAP_UR_9600, + .mt = GSM48_BCAP_MT_V32, + }, +#if 0 + [DATA_CALL_TR_V34_9600] = { + .ir = GSM48_BCAP_IR_16k, + .ur = GSM48_BCAP_UR_9600, + .mt = GSM48_BCAP_MT_V32, + /* (Octet 6c) Modem type: According to ITU-T Rec. V.32 + * (Octet 6d) Other modem type: According to ITU-T Rec. V.34 (2) + * TODO: gsm48_encode_bearer_cap() does not support octet 6d */ + }, +#endif + [DATA_CALL_TR_V110_300] = { + .ra = GSM48_BCAP_RA_V110_X30, + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_300, + }, + [DATA_CALL_TR_V110_1200] = { + .ra = GSM48_BCAP_RA_V110_X30, + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_1200, + }, + [DATA_CALL_TR_V110_2400] = { + .ra = GSM48_BCAP_RA_V110_X30, + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_2400, + }, + [DATA_CALL_TR_V110_4800] = { + .ra = GSM48_BCAP_RA_V110_X30, + .ir = GSM48_BCAP_IR_8k, + .ur = GSM48_BCAP_UR_4800, + }, + [DATA_CALL_TR_V110_9600] = { + .ra = GSM48_BCAP_RA_V110_X30, + .ir = GSM48_BCAP_IR_16k, + .ur = GSM48_BCAP_UR_9600, + }, +}; + +static void mncc_set_bcap_data(struct gsm_mncc *mncc, + const struct gsm_settings *set, + enum gsm_call_type call_type) +{ + const struct data_call_params *cp = &set->call_params.data; + struct gsm_mncc_bearer_cap *bcap = &mncc->bearer_cap; + const struct bcap_data_set *ds; + + OSMO_ASSERT(cp->type_rate < ARRAY_SIZE(bcap_data_set_map)); + ds = &bcap_data_set_map[cp->type_rate]; + if (ds->ir == 0 && ds->ur == 0) { + LOGP(DMNCC, LOGL_ERROR, "AT+CBST=%d is not supported\n", cp->type_rate); + return; + } + + mncc->fields |= MNCC_F_BEARER_CAP; + + *bcap = (struct gsm_mncc_bearer_cap) { + /* .transfer is set below */ + .mode = GSM48_BCAP_TMOD_CIRCUIT, + .coding = GSM48_BCAP_CODING_GSM_STD, + /* .radio is set below */ + .data = { + .sig_access = GSM48_BCAP_SA_I440_I450, + .rate_adaption = ds->ra, + .interm_rate = ds->ir, + .user_rate = ds->ur, + .modem_type = ds->mt, + .transp = cp->transp, + + /* async call params */ + .async = cp->is_async, + .nr_data_bits = cp->nr_data_bits, + .nr_stop_bits = cp->nr_stop_bits, + .parity = cp->parity, + }, + }; + + /* Radio channel requirement (octet 3) */ + if (set->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH) { + if (set->half_prefer) + bcap->radio = GSM48_BCAP_RRQ_DUAL_HR; + else + bcap->radio = GSM48_BCAP_RRQ_DUAL_FR; + LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n"); + } else { + bcap->radio = GSM48_BCAP_RRQ_FR_ONLY; + LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n"); + } + + /* Information transfer capability (octet 3) */ + switch (call_type) { + case GSM_CALL_T_DATA: + if (ds->mt == GSM48_BCAP_MT_NONE) + bcap->transfer = GSM_MNCC_BCAP_UNR_DIG; + else /* analog modem */ + bcap->transfer = GSM_MNCC_BCAP_AUDIO; + break; + case GSM_CALL_T_DATA_FAX: + bcap->transfer = GSM_MNCC_BCAP_FAX_G3; + break; + case GSM_CALL_T_VOICE: + default: /* shall not happen */ + OSMO_ASSERT(0); + } + + /* FAX calls are special (see 3GPP TS 24.008, Annex D.3) */ + if (call_type == GSM_CALL_T_DATA_FAX) { + bcap->data.rate_adaption = GSM48_BCAP_RA_NONE; + bcap->data.async = 0; /* shall be sync */ + bcap->data.transp = GSM48_BCAP_TR_TRANSP; + bcap->data.modem_type = GSM48_BCAP_MT_NONE; + } +} + +/* Check the given Bearer Capability, select first supported speech codec version. + * The choice between half-rate and full-rate is made based on current settings. + * Return a selected codec or -1 if no speech codec was selected. */ +static int mncc_handle_bcap_speech(const struct gsm_mncc_bearer_cap *bcap, + const struct gsm_settings *set) +{ + int speech_ver_half = -1; + int speech_ver = -1; + + for (unsigned int i = 0; bcap->speech_ver[i] >= 0; i++) { + int temp = mncc_get_bearer(set, bcap->speech_ver[i]); + switch (temp) { + case GSM48_BCAP_SV_AMR_H: + case GSM48_BCAP_SV_HR: + /* only the first half rate */ + if (speech_ver_half < 0) + speech_ver_half = temp; + break; + default: + if (temp < 0) + continue; + /* only the first full rate */ + if (speech_ver < 0) + speech_ver = temp; + break; + } + } + + /* half and full given */ + if (speech_ver_half >= 0 && speech_ver >= 0) { + if (set->half_prefer) { + LOGP(DMNCC, LOGL_INFO, " both supported" + " codec rates are given, using " + "preferred half rate\n"); + speech_ver = speech_ver_half; + } else { + LOGP(DMNCC, LOGL_INFO, " both supported" + " codec rates are given, using " + "preferred full rate\n"); + } + } else if (speech_ver_half < 0 && speech_ver < 0) { + LOGP(DMNCC, LOGL_INFO, " no supported codec " + "rate is given\n"); + /* only half rate is given, use it */ + } else if (speech_ver_half >= 0) { + LOGP(DMNCC, LOGL_INFO, " only supported half " + "rate codec is given, using it\n"); + speech_ver = speech_ver_half; + /* only full rate is given, use it */ + } else { + LOGP(DMNCC, LOGL_INFO, " only supported full " + "rate codec is given, using it\n"); + } + + return speech_ver; +} + +/* Check the given Bearer Capability for a data call (CSD). + * Return 0 if the bearer is accepted, otherwise return -1. */ +static int mncc_handle_bcap_data(const struct gsm_mncc_bearer_cap *bcap, + const struct gsm_settings *set) +{ + switch (bcap->transfer) { + case GSM48_BCAP_ITCAP_UNR_DIG_INF: + if (bcap->data.rate_adaption != GSM48_BCAP_RA_V110_X30) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Rate adaption (octet 5) 0x%02x is not supported\n", + __func__, bcap->data.rate_adaption); + return -ENOTSUP; + } + break; + case GSM48_BCAP_ITCAP_3k1_AUDIO: + case GSM48_BCAP_ITCAP_FAX_G3: + if (bcap->data.rate_adaption != GSM48_BCAP_RA_NONE) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Rate adaption (octet 5) 0x%02x was expected to be NONE\n", + __func__, bcap->data.rate_adaption); + return -ENOTSUP; + } + break; + default: + break; + } + + if (bcap->data.sig_access != GSM48_BCAP_SA_I440_I450) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Signalling access protocol (octet 5) 0x%02x is not supported\n", + __func__, bcap->data.sig_access); + return -ENOTSUP; + } + +#define BCAP_RATE(interm_rate, user_rate) \ + ((interm_rate << 8) | (user_rate << 0)) + + switch (BCAP_RATE(bcap->data.interm_rate, bcap->data.user_rate)) { + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_300): + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_1200): + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_2400): + if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): wrong user-rate 0x%02x for a non-transparent call\n", + __func__, bcap->data.user_rate); + return -EINVAL; + } + /* fall-through */ + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_4800): + case BCAP_RATE(GSM48_BCAP_IR_16k, GSM48_BCAP_UR_9600): + if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): only transparent calls are supported so far\n", + __func__); + return -ENOTSUP; + } + break; + default: + LOGP(DMNCC, LOGL_ERROR, + "%s(): IR 0x%02x / UR 0x%02x combination is not supported\n", + __func__, bcap->data.interm_rate, bcap->data.user_rate); + return -ENOTSUP; + } + +#undef BCAP_RATE + + return 0; +} + +static int mncc_handle_bcap(struct gsm_mncc *mncc_out, /* CC Call Confirmed */ + const struct gsm_mncc *mncc_in, /* CC Setup */ + const struct gsm_settings *set) +{ + const struct gsm_mncc_bearer_cap *bcap = &mncc_in->bearer_cap; + + /* 3GPP TS 24.008, section 9.3.2.2 defines several cases in which the + * Bearer Capability 1 IE is to be included, provided that at least + * one of these conditions is met. */ + + /* if the Bearer Capability 1 IE is not present */ + if (~mncc_in->fields & MNCC_F_BEARER_CAP) { + /* ... include our own Bearer Capability, assuming a speech call */ + mncc_set_bcap_speech(mncc_out, set, -1); + return 0; + } + + if (bcap->mode != GSM48_BCAP_TMOD_CIRCUIT) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Transfer mode 0x%02x is not supported\n", + __func__, bcap->mode); + return -ENOTSUP; + } + if (bcap->coding != GSM48_BCAP_CODING_GSM_STD) { + LOGP(DMNCC, LOGL_ERROR, + "%s(): Coding standard 0x%02x is not supported\n", + __func__, bcap->coding); + return -ENOTSUP; + } + + switch (bcap->transfer) { + case GSM48_BCAP_ITCAP_SPEECH: + { + int speech_ver = mncc_handle_bcap_speech(bcap, set); + /* include bearer cap, if not given in setup (see above) + * or if multiple codecs are given + * or if not only full rate + * or if given codec is unimplemented + */ + if (speech_ver < 0) + mncc_set_bcap_speech(mncc_out, set, -1); + else if (bcap->speech_ver[1] >= 0 || speech_ver != 0) + mncc_set_bcap_speech(mncc_out, set, speech_ver); + break; + } + case GSM48_BCAP_ITCAP_UNR_DIG_INF: + case GSM48_BCAP_ITCAP_3k1_AUDIO: + case GSM48_BCAP_ITCAP_FAX_G3: + return mncc_handle_bcap_data(bcap, set); + default: + LOGP(DMNCC, LOGL_ERROR, + "%s(): Information transfer capability 0x%02x is not supported\n", + __func__, bcap->transfer); + return -ENOTSUP; + } + + return 0; } /* @@ -220,7 +565,7 @@ int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg) /* * MNCCms call application via socket */ -int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg) +int mncc_recv_external(struct osmocom_ms *ms, int msg_type, void *arg) { struct mncc_sock_state *state = ms->mncc_entity.sock_state; struct gsm_mncc *mncc = arg; @@ -257,14 +602,13 @@ int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg) * MNCCms basic call application */ -int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) +int mncc_recv_internal(struct osmocom_ms *ms, int msg_type, void *arg) { - struct gsm_settings *set = &ms->settings; - struct gsm_mncc *data = arg; + const struct gsm_settings *set = &ms->settings; + const struct gsm_mncc *data = arg; struct gsm_call *call = get_call_ref(data->callref); struct gsm_mncc mncc; uint8_t cause; - int8_t speech_ver = -1, speech_ver_half = -1, temp; int first_call = 0; /* call does not exist */ @@ -290,6 +634,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) return -ENOMEM; call->ms = ms; call->callref = data->callref; + call->type = GSM_CALL_T_UNKNOWN; llist_add_tail(&call->entry, &call_list); } @@ -298,62 +643,62 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) switch (msg_type) { case MNCC_DISC_IND: - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); switch (data->cause.value) { case GSM48_CC_CAUSE_UNASSIGNED_NR: - vty_notify(ms, "Call: Number not assigned\n"); + l23_vty_ms_notify(ms, "Call: Number not assigned\n"); break; case GSM48_CC_CAUSE_NO_ROUTE: - vty_notify(ms, "Call: Destination unreachable\n"); + l23_vty_ms_notify(ms, "Call: Destination unreachable\n"); break; case GSM48_CC_CAUSE_NORM_CALL_CLEAR: - vty_notify(ms, "Call: Remote hangs up\n"); + l23_vty_ms_notify(ms, "Call: Remote hangs up\n"); break; case GSM48_CC_CAUSE_USER_BUSY: - vty_notify(ms, "Call: Remote busy\n"); + l23_vty_ms_notify(ms, "Call: Remote busy\n"); break; case GSM48_CC_CAUSE_USER_NOTRESPOND: - vty_notify(ms, "Call: Remote not responding\n"); + l23_vty_ms_notify(ms, "Call: Remote not responding\n"); break; case GSM48_CC_CAUSE_USER_ALERTING_NA: - vty_notify(ms, "Call: Remote not answering\n"); + l23_vty_ms_notify(ms, "Call: Remote not answering\n"); break; case GSM48_CC_CAUSE_CALL_REJECTED: - vty_notify(ms, "Call has been rejected\n"); + l23_vty_ms_notify(ms, "Call has been rejected\n"); break; case GSM48_CC_CAUSE_NUMBER_CHANGED: - vty_notify(ms, "Call: Number changed\n"); + l23_vty_ms_notify(ms, "Call: Number changed\n"); break; case GSM48_CC_CAUSE_PRE_EMPTION: - vty_notify(ms, "Call: Cleared due to pre-emption\n"); + l23_vty_ms_notify(ms, "Call: Cleared due to pre-emption\n"); break; case GSM48_CC_CAUSE_DEST_OOO: - vty_notify(ms, "Call: Remote out of order\n"); + l23_vty_ms_notify(ms, "Call: Remote out of order\n"); break; case GSM48_CC_CAUSE_INV_NR_FORMAT: - vty_notify(ms, "Call: Number invalid or incomplete\n"); + l23_vty_ms_notify(ms, "Call: Number invalid or incomplete\n"); break; case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN: - vty_notify(ms, "Call: No channel available\n"); + l23_vty_ms_notify(ms, "Call: No channel available\n"); break; case GSM48_CC_CAUSE_NETWORK_OOO: - vty_notify(ms, "Call: Network out of order\n"); + l23_vty_ms_notify(ms, "Call: Network out of order\n"); break; case GSM48_CC_CAUSE_TEMP_FAILURE: - vty_notify(ms, "Call: Temporary failure\n"); + l23_vty_ms_notify(ms, "Call: Temporary failure\n"); break; case GSM48_CC_CAUSE_SWITCH_CONG: - vty_notify(ms, "Congestion\n"); + l23_vty_ms_notify(ms, "Congestion\n"); break; default: - vty_notify(ms, "Call has been disconnected " + l23_vty_ms_notify(ms, "Call has been disconnected " "(clear cause %d)\n", data->cause.value); } LOGP(DMNCC, LOGL_INFO, "Call has been disconnected " "(cause %d)\n", data->cause.value); if ((data->fields & MNCC_F_PROGRESS) && data->progress.descr == 8) { - vty_notify(ms, "Please hang up!\n"); + l23_vty_ms_notify(ms, "Please hang up!\n"); break; } free_call(call); @@ -361,18 +706,18 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) goto release; case MNCC_REL_IND: case MNCC_REL_CNF: - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED) - vty_notify(ms, "Call has been rejected\n"); + l23_vty_ms_notify(ms, "Call has been rejected\n"); else - vty_notify(ms, "Call has been released\n"); + l23_vty_ms_notify(ms, "Call has been released\n"); LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n", data->cause.value); free_call(call); break; case MNCC_CALL_PROC_IND: - vty_notify(ms, NULL); - vty_notify(ms, "Call is proceeding\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call is proceeding\n"); LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n"); if ((data->fields & MNCC_F_BEARER_CAP) && data->bearer_cap.speech_ver[0] >= 0) { @@ -380,94 +725,59 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) } break; case MNCC_ALERT_IND: - vty_notify(ms, NULL); - vty_notify(ms, "Call is alerting\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call is alerting\n"); LOGP(DMNCC, LOGL_INFO, "Call is alerting\n"); break; case MNCC_SETUP_CNF: - vty_notify(ms, NULL); - vty_notify(ms, "Call is answered\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call is answered\n"); LOGP(DMNCC, LOGL_INFO, "Call is answered\n"); break; case MNCC_SETUP_IND: - vty_notify(ms, NULL); + l23_vty_ms_notify(ms, NULL); if (!first_call && !ms->settings.cw) { - vty_notify(ms, "Incoming call rejected while busy\n"); + l23_vty_ms_notify(ms, "Incoming call rejected while busy\n"); LOGP(DMNCC, LOGL_INFO, "Incoming call but busy\n"); cause = GSM48_CC_CAUSE_USER_BUSY; goto release; } - /* select first supported speech_ver */ - if ((data->fields & MNCC_F_BEARER_CAP)) { - int i; - - for (i = 0; data->bearer_cap.speech_ver[i] >= 0; i++) { - - temp = mncc_get_bearer(set, - data->bearer_cap.speech_ver[i]); - if (temp < 0) - continue; - if (temp == 5 || temp == 1) { /* half */ - /* only the first half rate */ - if (speech_ver_half < 0) - speech_ver_half = temp; - } else { - /* only the first full rate */ - if (speech_ver < 0) - speech_ver = temp; - } - } - /* half and full given */ - if (speech_ver_half >= 0 && speech_ver >= 0) { - if (set->half_prefer) { - LOGP(DMNCC, LOGL_INFO, " both supported" - " codec rates are given, using " - "preferred half rate\n"); - speech_ver = speech_ver_half; - } else - LOGP(DMNCC, LOGL_INFO, " both supported" - " codec rates are given, using " - "preferred full rate\n"); - } else if (speech_ver_half < 0 && speech_ver < 0) { - LOGP(DMNCC, LOGL_INFO, " no supported codec " - "rate is given\n"); - /* only half rate is given, use it */ - } else if (speech_ver_half >= 0) { - LOGP(DMNCC, LOGL_INFO, " only supported half " - "rate codec is given, using it\n"); - speech_ver = speech_ver_half; - /* only full rate is given, use it */ - } else { - LOGP(DMNCC, LOGL_INFO, " only supported full " - "rate codec is given, using it\n"); - } - } /* presentation allowed if present == 0 */ if (data->calling.present || !data->calling.number[0]) - vty_notify(ms, "Incoming call (anonymous)\n"); + l23_vty_ms_notify(ms, "Incoming call (anonymous)\n"); else if (data->calling.type == 1) - vty_notify(ms, "Incoming call (from +%s)\n", + l23_vty_ms_notify(ms, "Incoming call (from +%s)\n", data->calling.number); else if (data->calling.type == 2) - vty_notify(ms, "Incoming call (from 0-%s)\n", + l23_vty_ms_notify(ms, "Incoming call (from 0-%s)\n", data->calling.number); else - vty_notify(ms, "Incoming call (from %s)\n", + l23_vty_ms_notify(ms, "Incoming call (from %s)\n", data->calling.number); LOGP(DMNCC, LOGL_INFO, "Incoming call (from %s callref %x)\n", data->calling.number, call->callref); memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; - /* only include bearer cap, if not given in setup - * or if multiple codecs are given - * or if not only full rate - * or if given codec is unimplemented - */ - if (!(data->fields & MNCC_F_BEARER_CAP) || speech_ver < 0) - mncc_set_bearer(ms, -1, &mncc); - else if (data->bearer_cap.speech_ver[1] >= 0 - || speech_ver != 0) - mncc_set_bearer(ms, speech_ver, &mncc); + /* Bearer capability (optional) */ + if (mncc_handle_bcap(&mncc, data, &ms->settings) != 0) { + cause = GSM48_CC_CAUSE_INCOMPAT_DEST; + goto release; + } + switch (mncc.bearer_cap.transfer) { + case GSM48_BCAP_ITCAP_SPEECH: + call->type = GSM_CALL_T_VOICE; + break; + case GSM48_BCAP_ITCAP_UNR_DIG_INF: + case GSM48_BCAP_ITCAP_3k1_AUDIO: + call->type = GSM_CALL_T_DATA; + break; + case GSM48_BCAP_ITCAP_FAX_G3: + call->type = GSM_CALL_T_DATA_FAX; + break; + default: + call->type = GSM_CALL_T_UNKNOWN; + break; + } /* CC capabilities (optional) */ if (ms->settings.cc_dtmf) { mncc.fields |= MNCC_F_CCCAP; @@ -490,30 +800,30 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) } break; case MNCC_SETUP_COMPL_IND: - vty_notify(ms, NULL); - vty_notify(ms, "Call is connected\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call is connected\n"); LOGP(DMNCC, LOGL_INFO, "Call is connected\n"); break; case MNCC_HOLD_CNF: - vty_notify(ms, NULL); - vty_notify(ms, "Call is on hold\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call is on hold\n"); LOGP(DMNCC, LOGL_INFO, "Call is on hold\n"); call->hold = true; break; case MNCC_HOLD_REJ: - vty_notify(ms, NULL); - vty_notify(ms, "Call hold was rejected\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call hold was rejected\n"); LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n"); break; case MNCC_RETRIEVE_CNF: - vty_notify(ms, NULL); - vty_notify(ms, "Call is retrieved\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call is retrieved\n"); LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n"); call->hold = false; break; case MNCC_RETRIEVE_REJ: - vty_notify(ms, NULL); - vty_notify(ms, "Call retrieve was rejected\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Call retrieve was rejected\n"); LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n"); break; case MNCC_FACILITY_IND: @@ -534,15 +844,16 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) return 0; } -int mncc_call(struct osmocom_ms *ms, char *number) +int mncc_call(struct osmocom_ms *ms, const char *number, + enum gsm_call_type call_type) { struct gsm_call *call; struct gsm_mncc setup; llist_for_each_entry(call, &call_list, entry) { if (!call->hold) { - vty_notify(ms, NULL); - vty_notify(ms, "Please put active call on hold " + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Please put active call on hold " "first!\n"); LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n"); return -EBUSY; @@ -554,6 +865,7 @@ int mncc_call(struct osmocom_ms *ms, char *number) return -ENOMEM; call->ms = ms; call->callref = new_callref++; + call->type = call_type; call->init = true; llist_add_tail(&call->entry, &call_list); @@ -565,7 +877,8 @@ int mncc_call(struct osmocom_ms *ms, char *number) /* emergency */ setup.emergency = 1; } else { - LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number); + LOGP(DMNCC, LOGL_INFO, "Make %s call to %s\n", + gsm_call_type_str[call_type], number); /* called number */ setup.fields |= MNCC_F_CALLED; if (number[0] == '+') { @@ -578,7 +891,10 @@ int mncc_call(struct osmocom_ms *ms, char *number) OSMO_STRLCPY_ARRAY(setup.called.number, number); /* bearer capability (mandatory) */ - mncc_set_bearer(ms, -1, &setup); + if (call_type == GSM_CALL_T_VOICE) + mncc_set_bcap_speech(&setup, &ms->settings, -1); + else + mncc_set_bcap_data(&setup, &ms->settings, call_type); /* CLIR */ if (ms->settings.clir) @@ -609,8 +925,8 @@ int mncc_hangup(struct osmocom_ms *ms) } if (!found) { LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n"); - vty_notify(ms, NULL); - vty_notify(ms, "No active call\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No active call\n"); return -EINVAL; } @@ -636,14 +952,14 @@ int mncc_answer(struct osmocom_ms *ms) } if (!alerting) { LOGP(DMNCC, LOGL_INFO, "No call alerting\n"); - vty_notify(ms, NULL); - vty_notify(ms, "No alerting call\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No alerting call\n"); return -EBUSY; } if (active) { LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n"); - vty_notify(ms, NULL); - vty_notify(ms, "Please put active call on hold first!\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Please put active call on hold first!\n"); return -EBUSY; } alerting->ring = false; @@ -667,8 +983,8 @@ int mncc_hold(struct osmocom_ms *ms) } if (!found) { LOGP(DMNCC, LOGL_INFO, "No active call to hold\n"); - vty_notify(ms, NULL); - vty_notify(ms, "No active call\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No active call\n"); return -EINVAL; } @@ -691,26 +1007,26 @@ int mncc_retrieve(struct osmocom_ms *ms, int number) } if (active) { LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n"); - vty_notify(ms, NULL); - vty_notify(ms, "Hold active call first!\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Hold active call first!\n"); return -EINVAL; } if (holdnum == 0) { - vty_notify(ms, NULL); - vty_notify(ms, "No call on hold!\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No call on hold!\n"); return -EINVAL; } if (holdnum > 1 && number <= 0) { - vty_notify(ms, NULL); - vty_notify(ms, "Select call 1..%d\n", holdnum); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Select call 1..%d\n", holdnum); return -EINVAL; } if (holdnum == 1 && number <= 0) number = 1; if (number > holdnum) { - vty_notify(ms, NULL); - vty_notify(ms, "Given number %d out of range!\n", number); - vty_notify(ms, "Select call 1..%d\n", holdnum); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "Given number %d out of range!\n", number); + l23_vty_ms_notify(ms, "Select call 1..%d\n", holdnum); return -EINVAL; } @@ -729,7 +1045,8 @@ int mncc_retrieve(struct osmocom_ms *ms, int number) * DTMF */ -static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc) +static int dtmf_statemachine(struct gsm_call *call, + const struct gsm_mncc *mncc) { struct osmocom_ms *ms = call->ms; struct gsm_mncc dtmf; @@ -795,8 +1112,8 @@ int mncc_dtmf(struct osmocom_ms *ms, char *dtmf) } if (!found) { LOGP(DMNCC, LOGL_INFO, "No active call to send DTMF\n"); - vty_notify(ms, NULL); - vty_notify(ms, "No active call\n"); + l23_vty_ms_notify(ms, NULL); + l23_vty_ms_notify(ms, "No active call\n"); return -EINVAL; } diff --git a/src/host/layer23/src/mobile/primitives.c b/src/host/layer23/src/mobile/primitives.c index f562466a..4a40b4f7 100644 --- a/src/host/layer23/src/mobile/primitives.c +++ b/src/host/layer23/src/mobile/primitives.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <inttypes.h> diff --git a/src/host/layer23/src/mobile/script_lua.c b/src/host/layer23/src/mobile/script_lua.c index 50315bdb..eb3f0085 100644 --- a/src/host/layer23/src/mobile/script_lua.c +++ b/src/host/layer23/src/mobile/script_lua.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <lua.h> @@ -23,6 +19,7 @@ #include <lauxlib.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/mobile/app_mobile.h> #include <osmocom/bb/common/logging.h> diff --git a/src/host/layer23/src/mobile/script_nolua.c b/src/host/layer23/src/mobile/script_nolua.c index 61466f77..5973a44c 100644 --- a/src/host/layer23/src/mobile/script_nolua.c +++ b/src/host/layer23/src/mobile/script_nolua.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/bb/common/osmocom_data.h> diff --git a/src/host/layer23/src/mobile/tch.c b/src/host/layer23/src/mobile/tch.c new file mode 100644 index 00000000..b78682f5 --- /dev/null +++ b/src/host/layer23/src/mobile/tch.c @@ -0,0 +1,234 @@ +/* + * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> + * (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <string.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/signal.h> +#include <osmocom/codec/codec.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/mncc_sock.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/tch.h> + +int tch_voice_state_init(struct gsm_trans *trans, + struct tch_voice_state *state); +int tch_data_state_init(struct gsm_trans *trans, + struct tch_data_state *state); + +void tch_voice_state_free(struct tch_voice_state *state); +void tch_data_state_free(struct tch_data_state *state); + +int tch_voice_recv(struct osmocom_ms *ms, struct msgb *msg); +int tch_data_recv(struct osmocom_ms *ms, struct msgb *msg); + +/* Receive a Downlink traffic (voice/data) frame from the lower layers */ +static int tch_recv_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + struct tch_state *state = ms->tch_state; + + if (state == NULL) { + msgb_free(msg); + return 0; + } + + if (state->is_voice) { + return tch_voice_recv(ms, msg); + } else { + /* Rx only mode makes no sense for data calls, so we discard + * received DL frames and thus do not trigger sending UL frames. */ + if (!state->rx_only) + return tch_data_recv(ms, msg); + msgb_free(msg); + return 0; + } +} + +/* Send an Uplink voice frame to the lower layers */ +int tch_send_msg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct tch_state *state = ms->tch_state; + + if (state == NULL || state->rx_only) { + /* TODO: fix callers and print a warning here */ + msgb_free(msg); + return -EIO; + } + + return gsm48_rr_tx_traffic(ms, msg); +} + +/* tch_send_msg() wrapper accepting an MNCC structure */ +int tch_send_mncc_frame(struct osmocom_ms *ms, const struct gsm_data_frame *frame) +{ + struct msgb *nmsg; + int len; + + switch (frame->msg_type) { + case GSM_TCHF_FRAME: + len = GSM_FR_BYTES; + break; + case GSM_TCHF_FRAME_EFR: + len = GSM_EFR_BYTES; + break; + case GSM_TCHH_FRAME: + len = GSM_HR_BYTES; + break; + /* TODO: case GSM_TCH_FRAME_AMR (variable length) */ + /* TODO: case GSM_BAD_FRAME (empty?) */ + default: + LOGP(DL1C, LOGL_ERROR, "%s(): msg_type=0x%02x: not implemented\n", + __func__, frame->msg_type); + return -EINVAL; + } + + nmsg = msgb_alloc_headroom(len + 64, 64, "TCH/F"); + if (!nmsg) + return -ENOMEM; + nmsg->l2h = msgb_put(nmsg, len); + memcpy(nmsg->l2h, frame->data, len); + + return tch_send_msg(ms, nmsg); +} + +static void tch_trans_cstate_active_cb(struct gsm_trans *trans, bool rx_only) +{ + struct osmocom_ms *ms = trans->ms; + const struct gsm48_rrlayer *rr = &ms->rrlayer; + const struct gsm48_rr_cd *cd = &rr->cd_now; + struct tch_state *state; + + if (ms->tch_state != NULL) { + ms->tch_state->rx_only = rx_only; + return; /* TODO: handle modify? */ + } + + state = talloc_zero(ms, struct tch_state); + OSMO_ASSERT(state != NULL); + ms->tch_state = state; + ms->tch_state->rx_only = rx_only; + + switch (cd->mode) { + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + state->is_voice = true; + state->voice.handler = ms->settings.tch_voice.io_handler; + if (tch_voice_state_init(trans, &state->voice) != 0) + goto exit_free; + break; + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + state->is_voice = false; + state->data.handler = ms->settings.tch_data.io_handler; + if (tch_data_state_init(trans, &state->data) != 0) + goto exit_free; + break; + case GSM48_CMODE_SIGN: + default: + LOGP(DL1C, LOGL_ERROR, "Unhandled channel mode %s\n", + get_value_string(gsm48_chan_mode_names, cd->mode)); +exit_free: + talloc_free(state); + ms->tch_state = NULL; + return; + } + + /* rr->audio_mode has been set by tch_{voice,data}_state_init(), apply it */ + gsm48_rr_audio_mode(ms, rr->audio_mode); +} + +static void tch_trans_free_cb(struct gsm_trans *trans) +{ + struct osmocom_ms *ms = trans->ms; + struct tch_state *state = ms->tch_state; + + if (state == NULL) + return; + if (state->is_voice) + tch_voice_state_free(&state->voice); + else + tch_data_state_free(&state->data); + ms->rrlayer.audio_mode = 0x00; + + talloc_free(state); + ms->tch_state = NULL; +} + +static void tch_trans_state_chg_cb(struct gsm_trans *trans) +{ + switch (trans->cc.state) { + case GSM_CSTATE_CALL_DELIVERED: /* MO call: Rx CC ALERTING */ + tch_trans_cstate_active_cb(trans, true); + break; + case GSM_CSTATE_ACTIVE: /* MO/MT call: Rx CC CONNECT */ + tch_trans_cstate_active_cb(trans, false); + break; + case GSM_CSTATE_NULL: + tch_trans_free_cb(trans); + break; + } +} + +/* a call-back for CC (Call Control) transaction related events */ +static int tch_trans_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_trans *trans = signal_data; + + OSMO_ASSERT(subsys == SS_L23_TRANS); + OSMO_ASSERT(trans != NULL); + + /* we only care about CC transactions here */ + if (trans->protocol != GSM48_PDISC_CC) + return 0; + + switch ((enum osmobb_l23_trans_sig)signal) { + case S_L23_CC_TRANS_ALLOC: + break; + case S_L23_CC_TRANS_FREE: + tch_trans_free_cb(trans); + break; + case S_L23_CC_TRANS_STATE_CHG: + tch_trans_state_chg_cb(trans); + break; + } + + return 0; +} + +/* Initialize the TCH router */ +int tch_init(struct osmocom_ms *ms) +{ + ms->l1_entity.l1_traffic_ind = &tch_recv_cb; + + osmo_signal_register_handler(SS_L23_TRANS, &tch_trans_signal_cb, ms); + + return 0; +} diff --git a/src/host/layer23/src/mobile/tch_data.c b/src/host/layer23/src/mobile/tch_data.c new file mode 100644 index 00000000..b6b3be6a --- /dev/null +++ b/src/host/layer23/src/mobile/tch_data.c @@ -0,0 +1,587 @@ +/* + * (C) 2023-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/soft_uart.h> + +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/gsm/gsm44021.h> + +#include <osmocom/isdn/v110.h> +#include <osmocom/isdn/v110_ta.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/tch.h> + +#include <l1ctl_proto.h> + +struct csd_v110_frame_desc { + uint16_t num_blocks; + uint16_t num_bits; +}; + +struct csd_v110_lchan_desc { + struct csd_v110_frame_desc fr; + struct csd_v110_frame_desc hr; +}; + +/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */ +const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = { +#if 0 + [GSM48_CMODE_DATA_14k5] = { + /* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */ + .fr = { .num_blocks = 1, .num_bits = 290 }, + }, +#endif + [GSM48_CMODE_DATA_12k0] = { + /* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */ + .fr = { .num_blocks = 4, .num_bits = 60 }, + }, + [GSM48_CMODE_DATA_6k0] = { + /* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */ + .fr = { .num_blocks = 2, .num_bits = 60 }, + /* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */ + .hr = { .num_blocks = 4, .num_bits = 60 }, + }, + [GSM48_CMODE_DATA_3k6] = { + /* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */ + .fr = { .num_blocks = 2, .num_bits = 36 }, + /* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */ + .hr = { .num_blocks = 4, .num_bits = 36 }, + }, +}; + +struct tch_csd_sock_state *tch_csd_sock_init(struct osmocom_ms *ms); +void tch_csd_sock_recv(struct tch_csd_sock_state *state, struct msgb *msg); +void tch_csd_sock_send(struct tch_csd_sock_state *state, struct msgb *msg); +void tch_csd_sock_exit(struct tch_csd_sock_state *state); + +static void tch_soft_uart_rx_cb(void *priv, struct msgb *msg, unsigned int flags) +{ + struct tch_data_state *state = (struct tch_data_state *)priv; + + LOGP(DCSD, LOGL_DEBUG, "%s(): [flags=0x%08x] %s\n", + __func__, flags, msgb_hexdump(msg)); + + if (state->sock != NULL && msgb_length(msg) > 0) + tch_csd_sock_send(state->sock, msg); + else + msgb_free(msg); +} + +static void tch_soft_uart_tx_cb(void *priv, struct msgb *msg) +{ + struct tch_data_state *state = (struct tch_data_state *)priv; + + tch_csd_sock_recv(state->sock, msg); + + LOGP(DCSD, LOGL_DEBUG, "%s(): [n_bytes=%u/%u] %s\n", + __func__, msg->len, msg->data_len, msgb_hexdump(msg)); +} + +struct osmo_soft_uart *tch_soft_uart_alloc(struct osmocom_ms *ms, + const struct gsm_mncc_bearer_cap *bcap) +{ + struct osmo_soft_uart *suart; + + struct osmo_soft_uart_cfg cfg = { + .num_data_bits = bcap->data.nr_data_bits, + .num_stop_bits = bcap->data.nr_stop_bits, + /* .parity_mode is set below */ + .rx_buf_size = 1024, /* TODO: align with the current TCH mode */ + .rx_timeout_ms = 100, /* TODO: align with TCH framing interval */ + .priv = (void *)&ms->tch_state->data, + .rx_cb = &tch_soft_uart_rx_cb, + .tx_cb = &tch_soft_uart_tx_cb, + }; + + switch (bcap->data.parity) { + case GSM48_BCAP_PAR_ODD: + cfg.parity_mode = OSMO_SUART_PARITY_ODD; + break; + case GSM48_BCAP_PAR_EVEN: + cfg.parity_mode = OSMO_SUART_PARITY_EVEN; + break; + case GSM48_BCAP_PAR_ZERO: + cfg.parity_mode = OSMO_SUART_PARITY_SPACE; + break; + case GSM48_BCAP_PAR_ONE: + cfg.parity_mode = OSMO_SUART_PARITY_MARK; + break; + case GSM48_BCAP_PAR_NONE: + default: + cfg.parity_mode = OSMO_SUART_PARITY_NONE; + break; + } + + suart = osmo_soft_uart_alloc(ms, "csd_soft_uart", &cfg); + if (suart == NULL) + return NULL; + + osmo_soft_uart_set_rx(suart, true); + osmo_soft_uart_set_tx(suart, true); + + return suart; +} + +/*************************************************************************************/ + +static void tch_v110_ta_rx_cb(void *priv, const ubit_t *buf, size_t buf_size) +{ + const struct tch_data_state *state = (struct tch_data_state *)priv; + + if (state->sock != NULL && buf_size > 0) { + struct msgb *msg = msgb_alloc(buf_size, __func__); + tch_csd_sock_send(state->sock, msg); + } +} + +static void tch_v110_ta_tx_cb(void *priv, ubit_t *buf, size_t buf_size) +{ + const struct tch_data_state *state = (struct tch_data_state *)priv; + + if (state->sock != NULL && buf_size > 0) { + struct msgb *msg = msgb_alloc(buf_size, __func__); + + tch_csd_sock_recv(state->sock, msg); + if (msgb_length(msg) < buf_size) { + LOGP(DCSD, LOGL_NOTICE, + "%s(): not enough bytes for sync Tx (%u < %zu)\n", + __func__, msgb_length(msg), buf_size); + } + } +} + +static void tch_v110_ta_async_rx_cb(void *priv, const ubit_t *buf, size_t buf_size) +{ + const struct tch_data_state *state = (struct tch_data_state *)priv; + + osmo_soft_uart_rx_ubits(state->suart, buf, buf_size); +} + +static void tch_v110_ta_async_tx_cb(void *priv, ubit_t *buf, size_t buf_size) +{ + const struct tch_data_state *state = (struct tch_data_state *)priv; + + osmo_soft_uart_tx_ubits(state->suart, buf, buf_size); +} + +static const struct { + enum osmo_v110_ta_circuit c; + enum osmo_soft_uart_status s; +} tch_v110_circuit_map[] = { + { OSMO_V110_TA_C_106, OSMO_SUART_STATUS_F_CTS }, + { OSMO_V110_TA_C_107, OSMO_SUART_STATUS_F_DSR }, + { OSMO_V110_TA_C_109, OSMO_SUART_STATUS_F_DCD }, +}; + +static void tch_v110_ta_status_update_cb(void *priv, unsigned int status) +{ + const struct tch_data_state *state = (struct tch_data_state *)priv; + + LOGP(DCSD, LOGL_DEBUG, "V.110 TA status mask=0x%08x\n", status); + + for (unsigned int i = 0; i < ARRAY_SIZE(tch_v110_circuit_map); i++) { + enum osmo_v110_ta_circuit c = tch_v110_circuit_map[i].c; + enum osmo_soft_uart_status s = tch_v110_circuit_map[i].s; + bool is_on = (status & (1 << c)) != 0; + + LOGP(DCSD, LOGL_DEBUG, "V.110 TA circuit %s (%s) is %s\n", + osmo_v110_ta_circuit_name(c), + osmo_v110_ta_circuit_desc(c), + is_on ? "ON" : "OFF"); + + /* update status lines of the soft-UART */ + if (state->suart != NULL) + osmo_soft_uart_set_status_line(state->suart, s, is_on); + } +} + +struct osmo_v110_ta *tch_v110_ta_alloc(struct osmocom_ms *ms, + const struct gsm_mncc_bearer_cap *bcap) +{ + struct tch_data_state *state = &ms->tch_state->data; + + struct osmo_v110_ta_cfg cfg = { + /* .rate is set below */ + .priv = (void *)state, + .rx_cb = &tch_v110_ta_rx_cb, + .tx_cb = &tch_v110_ta_tx_cb, + .status_update_cb = &tch_v110_ta_status_update_cb, + }; + + if (bcap->data.async) { + OSMO_ASSERT(state->suart != NULL); + cfg.rx_cb = &tch_v110_ta_async_rx_cb; + cfg.tx_cb = &tch_v110_ta_async_tx_cb; + } + +#define BCAP_RATE(interm_rate, user_rate) \ + ((interm_rate << 8) | (user_rate << 0)) + + switch (BCAP_RATE(bcap->data.interm_rate, bcap->data.user_rate)) { + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_1200): + cfg.rate = OSMO_V110_SYNC_RA1_1200; + break; + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_2400): + cfg.rate = OSMO_V110_SYNC_RA1_2400; + break; + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_4800): + cfg.rate = OSMO_V110_SYNC_RA1_4800; + break; + case BCAP_RATE(GSM48_BCAP_IR_16k, GSM48_BCAP_UR_9600): + cfg.rate = OSMO_V110_SYNC_RA1_9600; + break; + /* TODO: according to 3GPP TS 44.021, section 4.1, the 300 bit/s user data + * signalling rate shall be adapted to a synchronous 600 bit/s stream. */ + case BCAP_RATE(GSM48_BCAP_IR_8k, GSM48_BCAP_UR_300): + default: + LOGP(DCSD, LOGL_ERROR, + "%s(): IR 0x%02x / UR 0x%02x combination is not supported\n", + __func__, bcap->data.interm_rate, bcap->data.user_rate); + return NULL; + } + +#undef BCAP_RATE + + osmo_v110_e1e2e3_set(state->e1e2e3, cfg.rate); + + return osmo_v110_ta_alloc(ms, "csd_v110_ta", &cfg); +} + +/*************************************************************************************/ + +static void swap_words(uint8_t *data, size_t data_len) +{ + /* swap bytes in words */ + while (data_len >= 2) { + uint8_t tmp = data[0]; + data[0] = data[1]; + data[1] = tmp; + data_len -= 2; + data += 2; + } +} + +static int tch_csd_rx_from_l1(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct tch_data_state *state = &ms->tch_state->data; + const struct gsm48_rr_cd *cd = &ms->rrlayer.cd_now; + const struct csd_v110_frame_desc *desc; + ubit_t data[4 * 60]; + size_t data_len; + + if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs) + desc = &csd_v110_lchan_desc[cd->mode].fr; + else /* RSL_CHAN_Lm_ACCHs */ + desc = &csd_v110_lchan_desc[cd->mode].hr; + if (OSMO_UNLIKELY(desc->num_blocks == 0)) + return -ENOTSUP; + + data_len = desc->num_blocks * desc->num_bits; + OSMO_ASSERT(sizeof(data) >= data_len); + + switch (ms->settings.tch_data.io_format) { + case TCH_DATA_IOF_OSMO: + /* trxcon emits raw bits from the convolutional decoder */ + if (OSMO_UNLIKELY(msgb_l3len(msg) != data_len)) + return -EINVAL; + memcpy(&data[0], msgb_l3(msg), msgb_l3len(msg)); + break; + case TCH_DATA_IOF_TI: + /* the layer1 firmware emits packed bits (LE ordering) */ + if (OSMO_UNLIKELY(msgb_l3len(msg) < data_len / 8)) + return -EINVAL; + /* ... with swapped words (LE ordering) */ + swap_words(msgb_l3(msg), msgb_l3len(msg)); + osmo_pbit2ubit_ext(data, 0, msgb_l3(msg), 0, data_len, 1); + break; + default: + LOGP(DCSD, LOGL_FATAL, + "%s(): unhandled data I/O format\n", __func__); + OSMO_ASSERT(0); + } + + for (unsigned int i = 0; i < desc->num_blocks; i++) { + struct osmo_v110_decoded_frame df; + + if (desc->num_bits == 60) + osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60); + else /* desc->num_bits == 36 */ + osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36); + + /* E1/E2/E3 is out-of-band knowledge in GSM/CSD */ + memcpy(df.e_bits, state->e1e2e3, sizeof(state->e1e2e3)); + + osmo_v110_ta_frame_in(state->v110_ta, &df); + } + + if (state->suart != NULL) + osmo_soft_uart_flush_rx(state->suart); + + return 0; +} + +static int tch_csd_tx_to_l1(struct osmocom_ms *ms) +{ + struct tch_data_state *state = &ms->tch_state->data; + const struct gsm48_rr_cd *cd = &ms->rrlayer.cd_now; + const struct csd_v110_frame_desc *desc; + ubit_t data[60 * 4]; + struct msgb *nmsg; + size_t data_len; + + if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs) + desc = &csd_v110_lchan_desc[cd->mode].fr; + else /* RSL_CHAN_Lm_ACCHs */ + desc = &csd_v110_lchan_desc[cd->mode].hr; + if (OSMO_UNLIKELY(desc->num_blocks == 0)) + return -ENOTSUP; + + data_len = desc->num_blocks * desc->num_bits; + OSMO_ASSERT(sizeof(data) >= data_len); + + for (unsigned int i = 0; i < desc->num_blocks; i++) { + struct osmo_v110_decoded_frame df; + + if (osmo_v110_ta_frame_out(state->v110_ta, &df) != 0) + memset(&df, 0x01, sizeof(df)); + + /* If E1/E2/E3 bits indicate a meaningful user data rate (see Table 5/V.110), + * set E7 to binary 0 in every 4-th frame (as per 3GPP TS 44.021, subclause 10.2.1). + * ITU-T V.110 requires this only for 600 bps, but 3GPP TS 44.021 clearly states + * that "such a multiframe structure exists for all user data rates". */ + if ((df.e_bits[0] + df.e_bits[1] + df.e_bits[2]) == 2) + df.e_bits[6] = (state->num_tx != 0); + state->num_tx = (state->num_tx + 1) & 0x03; + + if (desc->num_bits == 60) + osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df); + else /* desc->num_bits == 36 */ + osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df); + } + + switch (ms->settings.tch_data.io_format) { + case TCH_DATA_IOF_OSMO: + /* trxcon operates on unpacked bits */ + nmsg = msgb_alloc_headroom(data_len + 64, 64, __func__); + if (nmsg == NULL) + return -ENOMEM; + memcpy(msgb_put(nmsg, data_len), &data[0], data_len); + break; + case TCH_DATA_IOF_TI: + /* XXX: the layer1 firmware expects TRAFFIC.req with len=33 bytes */ + nmsg = msgb_alloc_headroom(33 + 64, 64, __func__); + if (nmsg == NULL) + return -ENOMEM; + nmsg->l2h = msgb_put(nmsg, 33); + /* the layer1 firmware expects packed bits (LE ordering) */ + osmo_ubit2pbit_ext(msgb_l2(nmsg), 0, &data[0], 0, sizeof(data), 1); + /* ... with swapped words (LE ordering) */ + swap_words(msgb_l2(nmsg), msgb_l2len(nmsg)); + break; + default: + LOGP(DCSD, LOGL_FATAL, + "%s(): unhandled data I/O format\n", __func__); + OSMO_ASSERT(0); + } + + return tch_send_msg(ms, nmsg); +} + +static int tch_data_check_bcap(const struct gsm_mncc_bearer_cap *bcap) +{ + if (bcap == NULL) { + LOGP(DCSD, LOGL_ERROR, + "%s(): CC transaction without BCap\n", + __func__); + return -ENODEV; + } + + if (bcap->mode != GSM48_BCAP_TMOD_CIRCUIT) { + LOGP(DCSD, LOGL_ERROR, + "%s(): Transfer mode 0x%02x is not supported\n", + __func__, bcap->mode); + return -ENOTSUP; + } + if (bcap->coding != GSM48_BCAP_CODING_GSM_STD) { + LOGP(DCSD, LOGL_ERROR, + "%s(): Coding standard 0x%02x is not supported\n", + __func__, bcap->coding); + return -ENOTSUP; + } + + switch (bcap->transfer) { + case GSM48_BCAP_ITCAP_UNR_DIG_INF: + if (bcap->data.rate_adaption != GSM48_BCAP_RA_V110_X30) { + LOGP(DCSD, LOGL_ERROR, + "%s(): Rate adaption (octet 5) 0x%02x is not supported\n", + __func__, bcap->data.rate_adaption); + return -ENOTSUP; + } + break; + case GSM48_BCAP_ITCAP_3k1_AUDIO: + case GSM48_BCAP_ITCAP_FAX_G3: + if (bcap->data.rate_adaption != GSM48_BCAP_RA_NONE) { + LOGP(DCSD, LOGL_ERROR, + "%s(): Rate adaption (octet 5) 0x%02x was expected to be NONE\n", + __func__, bcap->data.rate_adaption); + return -ENOTSUP; + } + break; + default: + LOGP(DCSD, LOGL_ERROR, + "%s(): Information transfer capability 0x%02x is not supported\n", + __func__, bcap->transfer); + return -ENOTSUP; + } + + if (bcap->data.sig_access != GSM48_BCAP_SA_I440_I450) { + LOGP(DCSD, LOGL_ERROR, + "%s(): Signalling access protocol (octet 5) 0x%02x is not supported\n", + __func__, bcap->data.sig_access); + return -ENOTSUP; + } + if (bcap->data.transp != GSM48_BCAP_TR_TRANSP) { + LOGP(DCSD, LOGL_ERROR, + "%s(): only transparent calls are supported so far\n", + __func__); + return -ENOTSUP; + } + + return 0; +} + +/*************************************************************************************/ + +int tch_data_recv(struct osmocom_ms *ms, struct msgb *msg) +{ + struct tch_data_state *state = &ms->tch_state->data; + + switch (state->handler) { + case TCH_DATA_IOH_LOOPBACK: + /* Remove the DL info header */ + msgb_pull_to_l2(msg); + /* Send data frame back */ + return tch_send_msg(ms, msg); + case TCH_DATA_IOH_UNIX_SOCK: + tch_csd_rx_from_l1(ms, msg); + tch_csd_tx_to_l1(ms); + msgb_free(msg); + break; + case TCH_DATA_IOH_NONE: + /* Drop voice frame */ + msgb_free(msg); + break; + } + + return 0; +} + +int tch_data_state_init(struct gsm_trans *trans, + struct tch_data_state *state) +{ + struct osmocom_ms *ms = trans->ms; + struct gsm48_rrlayer *rr = &ms->rrlayer; + const struct gsm_mncc_bearer_cap *bcap = trans->cc.bcap; + int rc; + + if ((rc = tch_data_check_bcap(bcap)) != 0) + return rc; + + switch (state->handler) { + case TCH_DATA_IOH_UNIX_SOCK: + state->sock = tch_csd_sock_init(ms); + if (state->sock == NULL) + return -ENOMEM; + rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ; + break; + case TCH_DATA_IOH_LOOPBACK: + case TCH_DATA_IOH_NONE: + rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ; + /* we don't need V.110 TA / soft-UART */ + return 0; + } + + if (bcap->data.async) { + state->suart = tch_soft_uart_alloc(ms, bcap); + if (state->suart == NULL) + goto exit_free; + } + + state->v110_ta = tch_v110_ta_alloc(ms, bcap); + if (state->v110_ta == NULL) + goto exit_free; + + return 0; + +exit_free: + if (state->sock != NULL) + tch_csd_sock_exit(state->sock); + if (state->suart != NULL) + osmo_soft_uart_free(state->suart); + if (state->v110_ta != NULL) + osmo_v110_ta_free(state->v110_ta); + return -1; +} + +void tch_data_state_free(struct tch_data_state *state) +{ + switch (state->handler) { + case TCH_DATA_IOH_UNIX_SOCK: + if (state->sock != NULL) + tch_csd_sock_exit(state->sock); + break; + default: + break; + } + + if (state->suart != NULL) + osmo_soft_uart_free(state->suart); + if (state->v110_ta != NULL) + osmo_v110_ta_free(state->v110_ta); +} + +void tch_csd_sock_state_cb(struct osmocom_ms *ms, bool connected) +{ + struct tch_data_state *state = NULL; + + if (ms->tch_state == NULL || ms->tch_state->is_voice) { + LOGP(DCSD, LOGL_INFO, "No data call is ongoing, " + "ignoring [dis]connection event for CSD socket\n"); + return; + } + + state = &ms->tch_state->data; + osmo_v110_ta_set_circuit(state->v110_ta, OSMO_V110_TA_C_108, connected); + + /* GSM/CSD employs the modified 60-bit V.110 frame format, which is basically + * a stripped down version of the nurmal 80-bit V.110 frame without E1/E2/E3 + * bits and without the sync pattern. These 60-bit V.110 frames are perfectly + * aligned with the radio interface block boundaries, so we're always in sync. */ + if (connected) + osmo_v110_ta_sync_ind(state->v110_ta); +} diff --git a/src/host/layer23/src/mobile/tch_data_sock.c b/src/host/layer23/src/mobile/tch_data_sock.c new file mode 100644 index 00000000..7ce5a4f1 --- /dev/null +++ b/src/host/layer23/src/mobile/tch_data_sock.c @@ -0,0 +1,243 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/socket.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/tch.h> + +struct tch_csd_sock_state { + struct osmocom_ms *ms; /* the MS instance we belong to */ + struct osmo_fd listen_bfd; /* fd for listen socket */ + struct osmo_fd conn_bfd; /* fd for a client connection */ + struct llist_head rxqueue; + struct llist_head txqueue; +}; + +void tch_csd_sock_state_cb(struct osmocom_ms *ms, bool connected); + +static void tch_csd_sock_close(struct tch_csd_sock_state *state) +{ + struct osmo_fd *bfd = &state->conn_bfd; + + LOGP(DCSD, LOGL_NOTICE, "TCH CSD sock has closed connection\n"); + + tch_csd_sock_state_cb(state->ms, false); + + osmo_fd_unregister(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* re-enable the generation of ACCEPT for new connections */ + osmo_fd_read_enable(&state->listen_bfd); + + /* flush the queues */ + while (!llist_empty(&state->rxqueue)) + msgb_free(msgb_dequeue(&state->rxqueue)); + while (!llist_empty(&state->txqueue)) + msgb_free(msgb_dequeue(&state->txqueue)); +} + +static int tch_csd_sock_read(struct osmo_fd *bfd) +{ + struct tch_csd_sock_state *state = (struct tch_csd_sock_state *)bfd->data; + struct msgb *msg; + int rc; + + msg = msgb_alloc(256, "tch_csd_sock_rx"); + if (!msg) + return -ENOMEM; + + rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); + if (rc == 0) + goto close; + if (rc < 0) { + if (errno == EAGAIN) + return 0; + goto close; + } + + msgb_put(msg, rc); + msgb_enqueue(&state->rxqueue, msg); + return rc; + +close: + msgb_free(msg); + tch_csd_sock_close(state); + return -1; +} + +static int tch_csd_sock_write(struct osmo_fd *bfd) +{ + struct tch_csd_sock_state *state = bfd->data; + + while (!llist_empty(&state->txqueue)) { + struct msgb *msg; + int rc; + + /* dequeue a msgb */ + msg = msgb_dequeue(&state->txqueue); + + /* try to send it over the socket */ + rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); + if (rc < 0 && errno == EAGAIN) { + llist_add(&msg->list, &state->txqueue); + return 0; + } + msgb_free(msg); + if (rc <= 0) + goto close; + } + + osmo_fd_write_disable(bfd); + return 0; + +close: + tch_csd_sock_close(state); + return -1; +} + +static int tch_csd_sock_cb(struct osmo_fd *bfd, unsigned int flags) +{ + int rc = 0; + + if (flags & OSMO_FD_READ) { + rc = tch_csd_sock_read(bfd); + if (rc < 0) + return rc; + } + + if (flags & OSMO_FD_WRITE) + rc = tch_csd_sock_write(bfd); + + return rc; +} + +static int tch_csd_sock_accept(struct osmo_fd *bfd, unsigned int flags) +{ + struct tch_csd_sock_state *state = (struct tch_csd_sock_state *)bfd->data; + struct osmo_fd *conn_bfd = &state->conn_bfd; + struct sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(bfd->fd, (struct sockaddr *)&un_addr, &len); + if (rc < 0) { + LOGP(DCSD, LOGL_ERROR, "Failed to accept() a new connection\n"); + return -1; + } + + if (conn_bfd->fd >= 0) { + LOGP(DCSD, LOGL_NOTICE, "TCH CSD sock already has an active connection\n"); + close(rc); /* reject this connection request */ + return 0; + } + + osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, &tch_csd_sock_cb, state, 0); + if (osmo_fd_register(conn_bfd) != 0) { + LOGP(DCSD, LOGL_ERROR, "osmo_fd_register() failed\n"); + close(conn_bfd->fd); + conn_bfd->fd = -1; + return -1; + } + + LOGP(DCSD, LOGL_NOTICE, "TCH CSD sock got a connection\n"); + + tch_csd_sock_state_cb(state->ms, true); + + return 0; +} + +struct tch_csd_sock_state *tch_csd_sock_init(struct osmocom_ms *ms) +{ + const char *sock_path = ms->settings.tch_data.unix_socket_path; + struct tch_csd_sock_state *state; + struct osmo_fd *bfd; + int rc; + + state = talloc_zero(ms, struct tch_csd_sock_state); + if (state == NULL) + return NULL; + + INIT_LLIST_HEAD(&state->rxqueue); + INIT_LLIST_HEAD(&state->txqueue); + state->conn_bfd.fd = -1; + state->ms = ms; + + bfd = &state->listen_bfd; + + rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path, OSMO_SOCK_F_BIND); + if (rc < 0) { + LOGP(DCSD, LOGL_ERROR, "Could not create unix socket: %s\n", strerror(errno)); + talloc_free(state); + return NULL; + } + + bfd->cb = &tch_csd_sock_accept; + bfd->data = state; + + return state; +} + +void tch_csd_sock_exit(struct tch_csd_sock_state *state) +{ + if (state->conn_bfd.fd > -1) + tch_csd_sock_close(state); + osmo_fd_unregister(&state->listen_bfd); + close(state->listen_bfd.fd); + talloc_free(state); +} + +void tch_csd_sock_recv(struct tch_csd_sock_state *state, struct msgb *msg) +{ + while (msgb_tailroom(msg) > 0) { + struct msgb *rmsg = msgb_dequeue(&state->rxqueue); + if (rmsg == NULL) + break; + size_t len = OSMO_MIN(msgb_tailroom(msg), msgb_length(rmsg)); + memcpy(msgb_put(msg, len), msgb_data(rmsg), len); + msgb_pull(rmsg, len); + if (msgb_length(rmsg) > 0) + llist_add(&rmsg->list, &state->rxqueue); + else + msgb_free(rmsg); + } +} + +void tch_csd_sock_send(struct tch_csd_sock_state *state, struct msgb *msg) +{ + msgb_enqueue(&state->txqueue, msg); + osmo_fd_write_enable(&state->conn_bfd); +} diff --git a/src/host/layer23/src/mobile/tch_voice.c b/src/host/layer23/src/mobile/tch_voice.c new file mode 100644 index 00000000..ac793e84 --- /dev/null +++ b/src/host/layer23/src/mobile/tch_voice.c @@ -0,0 +1,155 @@ +/* + * (C) 2017-2018 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> +#include <errno.h> + +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/gapk_io.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/mncc_sock.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/tch.h> + +#include <l1ctl_proto.h> + +/* Forward a Downlink voice frame to the external MNCC handler */ +static int tch_forward_mncc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_data_frame *mncc; + + /* Drop the l1ctl_info_dl header */ + msgb_pull_to_l2(msg); + /* push mncc header in front of data */ + mncc = (struct gsm_data_frame *) + msgb_push(msg, sizeof(struct gsm_data_frame)); + mncc->callref = ms->mncc_entity.ref; + + switch (ms->rrlayer.cd_now.mode) { + case GSM48_CMODE_SPEECH_V1: + { + const uint8_t cbits = ms->rrlayer.cd_now.chan_nr >> 3; + if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs) + mncc->msg_type = GSM_TCHF_FRAME; + else + mncc->msg_type = GSM_TCHH_FRAME; + break; + } + case GSM48_CMODE_SPEECH_EFR: + mncc->msg_type = GSM_TCHF_FRAME_EFR; + break; + case GSM48_CMODE_SPEECH_AMR: /* TODO: no AMR support yet */ + default: + /* TODO: print error message here */ + goto exit_free; + } + + /* distribute and then free */ + if (ms->mncc_entity.sock_state && ms->mncc_entity.ref) + return mncc_sock_from_cc(ms->mncc_entity.sock_state, msg); + +exit_free: + msgb_free(msg); + return 0; +} + +int tch_voice_recv(struct osmocom_ms *ms, struct msgb *msg) +{ + struct tch_voice_state *state = &ms->tch_state->voice; + + switch (state->handler) { + case TCH_VOICE_IOH_LOOPBACK: + /* Remove the DL info header */ + msgb_pull_to_l2(msg); + /* Send voice frame back */ + return tch_send_msg(ms, msg); + case TCH_VOICE_IOH_MNCC_SOCK: + return tch_forward_mncc(ms, msg); + case TCH_VOICE_IOH_GAPK: +#ifdef WITH_GAPK_IO + if (state->gapk_io != NULL) { + gapk_io_enqueue_dl(state->gapk_io, msg); + gapk_io_dequeue_ul(ms, state->gapk_io); + } else { + msgb_free(msg); + } + break; +#endif + case TCH_VOICE_IOH_L1PHY: + case TCH_VOICE_IOH_NONE: + /* Drop voice frame */ + msgb_free(msg); + break; + } + + return 0; +} + +int tch_voice_state_init(struct gsm_trans *trans, struct tch_voice_state *state) +{ + struct osmocom_ms *ms = trans->ms; + struct gsm48_rrlayer *rr = &ms->rrlayer; + const struct gsm48_rr_cd *cd = &rr->cd_now; + + switch (state->handler) { + case TCH_VOICE_IOH_L1PHY: + rr->audio_mode = AUDIO_RX_SPEAKER | AUDIO_TX_MICROPHONE; + break; + case TCH_VOICE_IOH_MNCC_SOCK: + case TCH_VOICE_IOH_LOOPBACK: + rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ; + break; +#ifdef WITH_GAPK_IO + case TCH_VOICE_IOH_GAPK: + if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs) + state->gapk_io = gapk_io_state_alloc_mode_rate(ms, cd->mode, true); + else /* RSL_CHAN_Lm_ACCHs */ + state->gapk_io = gapk_io_state_alloc_mode_rate(ms, cd->mode, false); + if (state->gapk_io == NULL) + return -1; + rr->audio_mode = AUDIO_RX_TRAFFIC_IND | AUDIO_TX_TRAFFIC_REQ; + break; +#endif + case TCH_VOICE_IOH_NONE: + rr->audio_mode = 0x00; + break; + } + + return 0; +} + +void tch_voice_state_free(struct tch_voice_state *state) +{ + switch (state->handler) { +#ifdef WITH_GAPK_IO + case TCH_VOICE_IOH_GAPK: + gapk_io_state_free(state->gapk_io); + break; +#endif + default: + break; + } +} diff --git a/src/host/layer23/src/mobile/transaction.c b/src/host/layer23/src/mobile/transaction.c index 9824bd1b..570545ba 100644 --- a/src/host/layer23/src/mobile/transaction.c +++ b/src/host/layer23/src/mobile/transaction.c @@ -13,19 +13,17 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> +#include <osmocom/core/signal.h> #include <osmocom/core/talloc.h> #include <osmocom/core/timer.h> #include <osmocom/core/msgb.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/logging.h> #include <osmocom/bb/mobile/mncc.h> #include <osmocom/bb/mobile/transaction.h> @@ -33,6 +31,7 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans); void _gsm480_ss_trans_free(struct gsm_trans *trans); void _gsm411_sms_trans_free(struct gsm_trans *trans); +void _gsm44068_gcc_bcc_trans_free(struct gsm_trans *trans); struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, uint8_t proto, uint8_t trans_id) @@ -47,13 +46,13 @@ struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, return NULL; } -struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, uint8_t protocol, uint32_t callref) { struct gsm_trans *trans; llist_for_each_entry(trans, &ms->trans_list, entry) { - if (trans->callref == callref) + if (trans->protocol == protocol && trans->callref == callref) return trans; } return NULL; @@ -81,11 +80,15 @@ struct gsm_trans *trans_alloc(struct osmocom_ms *ms, llist_add_tail(&trans->entry, &ms->trans_list); + osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_ALLOC, trans); + return trans; } void trans_free(struct gsm_trans *trans) { + osmo_signal_dispatch(SS_L23_TRANS, S_L23_CC_TRANS_FREE, trans); + switch (trans->protocol) { case GSM48_PDISC_CC: _gsm48_cc_trans_free(trans); @@ -96,6 +99,10 @@ void trans_free(struct gsm_trans *trans) case GSM48_PDISC_SMS: _gsm411_sms_trans_free(trans); break; + case GSM48_PDISC_GROUP_CC: + case GSM48_PDISC_BCAST_CC: + _gsm44068_gcc_bcc_trans_free(trans); + break; } DEBUGP(DCC, "ms %s frees transaction (mem %p)\n", trans->ms->name, diff --git a/src/host/layer23/src/mobile/voice.c b/src/host/layer23/src/mobile/voice.c deleted file mode 100644 index c3792650..00000000 --- a/src/host/layer23/src/mobile/voice.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <stdlib.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/codec/codec.h> - -#include <osmocom/bb/common/logging.h> -#include <osmocom/bb/common/osmocom_data.h> -#include <osmocom/bb/mobile/mncc.h> -#include <osmocom/bb/mobile/voice.h> - - -/* - * receive voice - */ - -static int gsm_recv_voice(struct osmocom_ms *ms, struct msgb *msg) -{ - struct gsm_data_frame *mncc; - - /* Drop the l1ctl_info_dl header */ - msgb_pull_to_l2(msg); - /* push mncc header in front of data */ - mncc = (struct gsm_data_frame *) - msgb_push(msg, sizeof(struct gsm_data_frame)); - mncc->callref = ms->mncc_entity.ref; - - /* FIXME: FR, EFR only! */ - switch (ms->rrlayer.cd_now.mode) { - case GSM48_CMODE_SPEECH_V1: - mncc->msg_type = GSM_TCHF_FRAME; - break; - case GSM48_CMODE_SPEECH_EFR: - mncc->msg_type = GSM_TCHF_FRAME_EFR; - break; - default: - /* TODO: print error message here */ - goto exit_free; - } - - /* send voice frame back, if appropriate */ - if (ms->settings.audio.io_handler == AUDIO_IOH_LOOPBACK) - gsm_send_voice(ms, mncc); - - /* distribute and then free */ - if (ms->mncc_entity.mncc_recv && ms->mncc_entity.ref) { - ms->mncc_entity.mncc_recv(ms, mncc->msg_type, mncc); - } - -exit_free: - msgb_free(msg); - return 0; -} - -/* - * send voice - */ -int gsm_send_voice(struct osmocom_ms *ms, struct gsm_data_frame *data) -{ - struct msgb *nmsg; - int len; - - switch (ms->rrlayer.cd_now.mode) { - case GSM48_CMODE_SPEECH_V1: - /* FIXME: FR only, check for TCH/F (FR) and TCH/H (HR) */ - len = GSM_FR_BYTES; - break; - case GSM48_CMODE_SPEECH_EFR: - len = GSM_EFR_BYTES; - break; - default: - LOGP(DL1C, LOGL_ERROR, "gsm_send_voice, msg_type=0x%02x: not implemented\n", data->msg_type); - return -EINVAL; - } - - nmsg = msgb_alloc_headroom(len + 64, 64, "TCH/F"); - if (!nmsg) - return -ENOMEM; - nmsg->l2h = msgb_put(nmsg, len); - memcpy(nmsg->l2h, data->data, len); - - return gsm48_rr_tx_voice(ms, nmsg); -} - -/* - * init - */ - -int gsm_voice_init(struct osmocom_ms *ms) -{ - ms->l1_entity.l1_traffic_ind = gsm_recv_voice; - - return 0; -} diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c index 073303ca..eb582d35 100644 --- a/src/host/layer23/src/mobile/vty_interface.c +++ b/src/host/layer23/src/mobile/vty_interface.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <string.h> @@ -30,8 +26,10 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/signal.h> #include <osmocom/crypt/auth.h> +#include <osmocom/gsm/gsm23003.h> #include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> #include <osmocom/bb/common/networks.h> #include <osmocom/bb/common/gps.h> #include <osmocom/bb/mobile/mncc.h> @@ -41,55 +39,39 @@ #include <osmocom/bb/mobile/app_mobile.h> #include <osmocom/bb/mobile/gsm480_ss.h> #include <osmocom/bb/mobile/gsm411_sms.h> +#include <osmocom/bb/mobile/gsm44068_gcc_bcc.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/misc.h> -extern struct llist_head ms_list; -extern struct llist_head active_connections; - -struct cmd_node ms_node = { - MS_NODE, - "%s(ms)# ", +struct cmd_node support_node = { + SUPPORT_NODE, + "%s(support)# ", 1 }; -struct cmd_node testsim_node = { - TESTSIM_NODE, - "%s(test-sim)# ", +struct cmd_node tch_voice_node = { + TCH_VOICE_NODE, + "%s(tch-voice)# ", 1 }; -struct cmd_node support_node = { - SUPPORT_NODE, - "%s(support)# ", +struct cmd_node tch_data_node = { + TCH_DATA_NODE, + "%s(tch-data)# ", 1 }; -struct cmd_node audio_node = { - AUDIO_NODE, - "%s(audio)# ", +struct cmd_node vgcs_node = { + VGCS_NODE, + "%s(group-call)# ", 1 }; -static void print_vty(void *priv, const char *fmt, ...) -{ - char buffer[1000]; - struct vty *vty = priv; - va_list args; - - va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); - buffer[sizeof(buffer) - 1] = '\0'; - va_end(args); - - if (buffer[0]) { - if (buffer[strlen(buffer) - 1] == '\n') { - buffer[strlen(buffer) - 1] = '\0'; - vty_out(vty, "%s%s", buffer, VTY_NEWLINE); - } else - vty_out(vty, "%s", buffer); - } -} +struct cmd_node vbs_node = { + VBS_NODE, + "%s(broadcast-call)# ", + 1 +}; int vty_check_number(struct vty *vty, const char *number) { @@ -116,12 +98,35 @@ int vty_check_number(struct vty *vty, const char *number) return 0; } -int vty_reading = 0; -static int hide_default = 0; +int vty_check_callref(struct vty *vty, const char *number) +{ + int i, ii = strlen(number); + + /* First check digits, so that a false command result the following error message. */ + for (i = 0; i < ii; i++) { + if (!(number[i] >= '0' && number[i] <= '9')) { + vty_out(vty, "Invalid digit '%c' in callref!%s", + number[i], VTY_NEWLINE); + return -EINVAL; + } + } + + if (ii < 1) { + vty_out(vty, "Given callref has no digits!%s", VTY_NEWLINE); + return -EINVAL; + } + + if (ii > 8) { + vty_out(vty, "Given callref is too long!%s", VTY_NEWLINE); + return -EINVAL; + } + + return 0; +} static void vty_restart(struct vty *vty, struct osmocom_ms *ms) { - if (vty_reading) + if (l23_vty_reading) return; if (ms->shutdown != MS_SHUTDOWN_NONE) return; @@ -136,25 +141,6 @@ static void vty_restart_if_started(struct vty *vty, struct osmocom_ms *ms) vty_restart(vty, ms); } -static struct osmocom_ms *get_ms(const char *name, struct vty *vty) -{ - struct osmocom_ms *ms; - - llist_for_each_entry(ms, &ms_list, entity) { - if (!strcmp(ms->name, name)) { - if (ms->shutdown != MS_SHUTDOWN_NONE) { - vty_out(vty, "MS '%s' is admin down.%s", name, - VTY_NEWLINE); - return NULL; - } - return ms; - } - } - vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE); - - return NULL; -} - static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty) { struct gsm_settings *set = &ms->settings; @@ -210,23 +196,21 @@ static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty) else vty_out(vty, " manual network selection state : %s%s", get_m_state_name(ms->plmn.state), VTY_NEWLINE); - if (ms->plmn.mcc) + if (ms->plmn.plmn.mcc) vty_out(vty, " MCC=%s " - "MNC=%s (%s, %s)%s", gsm_print_mcc(ms->plmn.mcc), - gsm_print_mnc(ms->plmn.mnc), gsm_get_mcc(ms->plmn.mcc), - gsm_get_mnc(ms->plmn.mcc, ms->plmn.mnc), VTY_NEWLINE); + "MNC=%s (%s, %s)%s", osmo_mcc_name(ms->plmn.plmn.mcc), + osmo_mnc_name(ms->plmn.plmn.mnc, ms->plmn.plmn.mnc_3_digits), + gsm_get_mcc(ms->plmn.plmn.mcc), + gsm_get_mnc(&ms->plmn.plmn), VTY_NEWLINE); vty_out(vty, " cell selection state: %s%s", get_cs_state_name(ms->cellsel.state), VTY_NEWLINE); - if (ms->cellsel.sel_mcc) { - vty_out(vty, " ARFCN=%s MCC=%s MNC=%s " - "LAC=0x%04x CELLID=0x%04x%s", + if (ms->cellsel.sel_cgi.lai.plmn.mcc) { + vty_out(vty, " ARFCN=%s CGI=%s%s", gsm_print_arfcn(ms->cellsel.sel_arfcn), - gsm_print_mcc(ms->cellsel.sel_mcc), - gsm_print_mnc(ms->cellsel.sel_mnc), - ms->cellsel.sel_lac, ms->cellsel.sel_id, VTY_NEWLINE); + osmo_cgi_name(&ms->cellsel.sel_cgi), VTY_NEWLINE); vty_out(vty, " (%s, %s)%s", - gsm_get_mcc(ms->cellsel.sel_mcc), - gsm_get_mnc(ms->cellsel.sel_mcc, ms->cellsel.sel_mnc), + gsm_get_mcc(ms->cellsel.sel_cgi.lai.plmn.mcc), + gsm_get_mnc(&ms->cellsel.sel_cgi.lai.plmn), VTY_NEWLINE); } vty_out(vty, " radio resource layer state: %s%s", @@ -245,7 +229,8 @@ static void gsm_ms_dump(struct osmocom_ms *ms, struct vty *vty) DEFUN(show_ms, show_ms_cmd, "show ms [MS_NAME]", - SHOW_STR "Display available MS entities\n") + SHOW_STR "Display available MS entities\n" + "Display specific MS with given name") { struct osmocom_ms *ms; @@ -257,57 +242,13 @@ DEFUN(show_ms, show_ms_cmd, "show ms [MS_NAME]", } } vty_out(vty, "MS name '%s' does not exits.%s", argv[0], - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; - } else { - llist_for_each_entry(ms, &ms_list, entity) { - gsm_ms_dump(ms, vty); - vty_out(vty, "%s", VTY_NEWLINE); - } } - return CMD_SUCCESS; -} - -DEFUN(show_support, show_support_cmd, "show support [MS_NAME]", - SHOW_STR "Display information about MS support\n" - "Name of MS (see \"show ms\")") -{ - struct osmocom_ms *ms; - - if (argc) { - ms = get_ms(argv[0], vty); - if (!ms) - return CMD_WARNING; - gsm_support_dump(ms, print_vty, vty); - } else { - llist_for_each_entry(ms, &ms_list, entity) { - gsm_support_dump(ms, print_vty, vty); - vty_out(vty, "%s", VTY_NEWLINE); - } - } - - return CMD_SUCCESS; -} - -DEFUN(show_subscr, show_subscr_cmd, "show subscriber [MS_NAME]", - SHOW_STR "Display information about subscriber\n" - "Name of MS (see \"show ms\")") -{ - struct osmocom_ms *ms; - - if (argc) { - ms = get_ms(argv[0], vty); - if (!ms) - return CMD_WARNING; - gsm_subscr_dump(&ms->subscr, print_vty, vty); - } else { - llist_for_each_entry(ms, &ms_list, entity) { - if (ms->shutdown == MS_SHUTDOWN_NONE) { - gsm_subscr_dump(&ms->subscr, print_vty, vty); - vty_out(vty, "%s", VTY_NEWLINE); - } - } + llist_for_each_entry(ms, &ms_list, entity) { + gsm_ms_dump(ms, vty); + vty_out(vty, "%s", VTY_NEWLINE); } return CMD_SUCCESS; @@ -319,11 +260,11 @@ DEFUN(show_cell, show_cell_cmd, "show cell MS_NAME", { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; - gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SUPPORT, print_vty, + gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SUPPORT, l23_vty_printf, vty); return CMD_SUCCESS; @@ -338,7 +279,7 @@ DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023> [pcs]", struct gsm48_sysinfo *s; uint16_t arfcn = atoi(argv[1]); - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; @@ -358,7 +299,7 @@ DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023> [pcs]", return CMD_SUCCESS; } - gsm48_sysinfo_dump(s, arfcn, print_vty, vty, ms->settings.freq_map); + gsm48_sysinfo_dump(s, arfcn, l23_vty_printf, vty, ms->settings.freq_map); return CMD_SUCCESS; } @@ -369,11 +310,11 @@ DEFUN(show_nbcells, show_nbcells_cmd, "show neighbour-cells MS_NAME", { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; - gsm322_dump_nb_list(&ms->cellsel, print_vty, vty); + gsm322_dump_nb_list(&ms->cellsel, l23_vty_printf, vty); return CMD_SUCCESS; } @@ -384,26 +325,26 @@ DEFUN(show_ba, show_ba_cmd, "show ba MS_NAME [MCC] [MNC]", "Mobile Network Code") { struct osmocom_ms *ms; - uint16_t mcc = 0, mnc = 0; + struct osmo_plmn_id plmn; + struct osmo_plmn_id *plmn_ptr = NULL; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; if (argc >= 3) { - mcc = gsm_input_mcc((char *)argv[1]); - mnc = gsm_input_mnc((char *)argv[2]); - if (mcc == GSM_INPUT_INVALID) { + if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) { vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); return CMD_WARNING; } - if (mnc == GSM_INPUT_INVALID) { + if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) { vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); return CMD_WARNING; } + plmn_ptr = &plmn; } - gsm322_dump_ba_list(&ms->cellsel, mcc, mnc, print_vty, vty); + gsm322_dump_ba_list(&ms->cellsel, plmn_ptr, l23_vty_printf, vty); return CMD_SUCCESS; } @@ -414,11 +355,11 @@ DEFUN(show_forb_plmn, show_forb_plmn_cmd, "show forbidden plmn MS_NAME", { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; - gsm_subscr_dump_forbidden_plmn(ms, print_vty, vty); + gsm_subscr_dump_forbidden_plmn(ms, l23_vty_printf, vty); return CMD_SUCCESS; } @@ -429,11 +370,41 @@ DEFUN(show_forb_la, show_forb_la_cmd, "show forbidden location-area MS_NAME", { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; - gsm322_dump_forbidden_la(ms, print_vty, vty); + gsm322_dump_forbidden_la(ms, l23_vty_printf, vty); + + return CMD_SUCCESS; +} + +#define SHOW_ASCI_STR SHOW_STR "Display information about ASCI items\nName of MS (see \"show ms\")\n" + +DEFUN(show_asci_calls, show_asci_calls_cmd, "show asci MS_NAME calls", + SHOW_ASCI_STR "Display ongoing ASCI calls") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm44068_dump_calls(ms, l23_vty_printf, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_asci_neighbors, show_asci_neighbors_cmd, "show asci MS_NAME neighbors", + SHOW_ASCI_STR "Display neigbor cells of ongoing or last ASCI call") +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm48_si10_dump(ms->cellsel.si, l23_vty_printf, vty); return CMD_SUCCESS; } @@ -443,7 +414,7 @@ DEFUN(monitor_network, monitor_network_cmd, "monitor network MS_NAME", { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; @@ -458,7 +429,7 @@ DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME", { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; @@ -467,440 +438,482 @@ DEFUN(no_monitor_network, no_monitor_network_cmd, "no monitor network MS_NAME", return CMD_SUCCESS; } -static int _sim_test_cmd(struct vty *vty, int argc, const char *argv[], - int attached) +DEFUN(network_select, network_select_cmd, + "network select MS_NAME MCC MNC [force]", + "Select ...\nSelect Network\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code\n" + "Force selecting a network that is not in the list") { struct osmocom_ms *ms; - struct gsm_settings *set; - - /* Initial testcard settings */ - uint16_t mcc = 0x001, mnc = 0x01f, lac = 0x0000; - uint32_t tmsi = 0xffffffff; + struct gsm322_plmn *plmn322; + struct msgb *nmsg; + struct gsm322_msg *ngm; + struct gsm322_plmn_list *temp; + struct osmo_plmn_id plmn; + int found = 0; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; + plmn322 = &ms->plmn; - if (ms->subscr.sim_valid) { - vty_out(vty, "SIM already attached, remove first!%s", + if (ms->settings.plmn_mode != PLMN_MODE_MANUAL) { + vty_out(vty, "Not in manual network selection mode%s", VTY_NEWLINE); return CMD_WARNING; } - set = &ms->settings; - if (set->test_rplmn_valid) { - mcc = set->test_rplmn_mcc; - mnc = set->test_rplmn_mnc; - - if (set->test_lac > 0x0000 && set->test_lac < 0xfffe) - lac = set->test_lac; - - if (set->test_tmsi != 0xffffffff) - tmsi = set->test_tmsi; + if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; } - - if (argc == 2) { - vty_out(vty, "Give MNC together with MCC%s", VTY_NEWLINE); + if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); return CMD_WARNING; } - if (argc >= 3) { - mcc = gsm_input_mcc((char *)argv[1]); - mnc = gsm_input_mnc((char *)argv[2]); - if (mcc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (mnc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + + if (argc < 4) { + llist_for_each_entry(temp, &plmn322->sorted_plmn, entry) + if (osmo_plmn_cmp(&temp->plmn, &plmn) == 0) + found = 1; + if (!found) { + vty_out(vty, "Network not in list!%s", VTY_NEWLINE); + vty_out(vty, "To force selecting this network, use " + "'force' keyword%s", VTY_NEWLINE); return CMD_WARNING; } } - if (argc >= 4) - lac = strtoul(argv[3], NULL, 16); - - if (argc >= 5) - tmsi = strtoul(argv[4], NULL, 16); - - gsm_subscr_testcard(ms, mcc, mnc, lac, tmsi, attached); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOOSE_PLMN); + if (!nmsg) + return CMD_WARNING; + ngm = (struct gsm322_msg *) nmsg->data; + memcpy(&ngm->plmn, &plmn, sizeof(struct osmo_plmn_id)); + gsm322_plmn_sendmsg(ms, nmsg); return CMD_SUCCESS; } -DEFUN(sim_test, sim_test_cmd, - "sim testcard MS_NAME [MCC] [MNC] [LAC] [TMSI]", - "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n" - "Optionally set mobile Country Code of RPLMN\n" - "Optionally set mobile Network Code of RPLMN\n" - "Optionally set location area code of RPLMN\n" - "Optionally set current assigned TMSI") -{ - return _sim_test_cmd(vty, argc, argv, 0); -} +#define CALL_CMD "call MS_NAME" +#define CALL_CMD_DESC \ + "Call related commands\n" \ + "Name of MS (see \"show ms\")\n" -DEFUN(sim_test_att, sim_test_att_cmd, - "sim testcard MS_NAME MCC MNC LAC TMSI attached", - "SIM actions\nAttach built in test SIM\nName of MS (see \"show ms\")\n" - "Set mobile Country Code of RPLMN\nSet mobile Network Code of RPLMN\n" - "Set location area code\nSet current assigned TMSI\n" - "Indicate to MM that card is already attached") -{ - return _sim_test_cmd(vty, argc, argv, 1); -} - -DEFUN(sim_sap, sim_sap_cmd, "sim sap MS_NAME", - "SIM actions\nAttach SIM over SAP interface\n" - "Name of MS (see \"show ms\")\n") +DEFUN(call_num, call_num_cmd, + CALL_CMD " NUMBER [(voice|data|fax)]", + CALL_CMD_DESC + "Phone number to call " + "(Use digits '0123456789*#abc', and '+' to dial international)\n" + "Initiate a regular voice call (default)\n" + "Initiate a data call (UDI or 3.1 kHz audio)\n" + "Initiate a data call (Facsimile group 3)\n") { struct osmocom_ms *ms; + struct gsm_settings *set; + struct gsm_settings_abbrev *abbrev; + enum gsm_call_type call_type; + const char *number; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; + set = &ms->settings; - if (ms->subscr.sim_valid) { - vty_out(vty, "SIM already attached, remove first!%s", - VTY_NEWLINE); + if (set->ch_cap == GSM_CAP_SDCCH) { + vty_out(vty, "Basic call is not supported for SDCCH only " + "mobile%s", VTY_NEWLINE); return CMD_WARNING; } - if (gsm_subscr_sapcard(ms) != 0) { - return CMD_WARNING; + number = argv[1]; + llist_for_each_entry(abbrev, &set->abbrev, list) { + if (!strcmp(number, abbrev->abbrev)) { + number = abbrev->number; + vty_out(vty, "Dialing number '%s'%s", number, + VTY_NEWLINE); + break; + } } - return CMD_SUCCESS; -} - -DEFUN(sim_reader, sim_reader_cmd, "sim reader MS_NAME", - "SIM actions\nAttach SIM from reader\nName of MS (see \"show ms\")") -{ - struct osmocom_ms *ms; - - ms = get_ms(argv[0], vty); - if (!ms) + if (vty_check_number(vty, number)) return CMD_WARNING; - if (ms->subscr.sim_valid) { - vty_out(vty, "SIM already attached, remove first!%s", - VTY_NEWLINE); + if (argc < 3 || !strcmp(argv[2], "voice")) + call_type = GSM_CALL_T_VOICE; /* implicit default */ + else if (!strcmp(argv[2], "data")) + call_type = GSM_CALL_T_DATA; + else if (!strcmp(argv[2], "fax")) + call_type = GSM_CALL_T_DATA_FAX; + else return CMD_WARNING; - } - gsm_subscr_simcard(ms); + mncc_call(ms, number, call_type); return CMD_SUCCESS; } -DEFUN(sim_remove, sim_remove_cmd, "sim remove MS_NAME", - "SIM actions\nDetach SIM card\nName of MS (see \"show ms\")") +DEFUN(call, call_cmd, + CALL_CMD " (emergency|answer|hangup|hold)", + CALL_CMD_DESC + "Make an emergency call\n" + "Answer an incoming call\n" + "Hangup a call\n" + "Hold current active call\n") { struct osmocom_ms *ms; + struct gsm_settings *set; + const char *number; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; + set = &ms->settings; - if (!ms->subscr.sim_valid) { - vty_out(vty, "No SIM attached!%s", VTY_NEWLINE); + if (set->ch_cap == GSM_CAP_SDCCH) { + vty_out(vty, "Basic call is not supported for SDCCH only " + "mobile%s", VTY_NEWLINE); return CMD_WARNING; } - if (ms->subscr.sim_type == GSM_SIM_TYPE_SAP) { - gsm_subscr_remove_sapcard(ms); - } + number = argv[1]; + if (!strcmp(number, "emergency")) + mncc_call(ms, number, GSM_CALL_T_VOICE); + else if (!strcmp(number, "answer")) + mncc_answer(ms); + else if (!strcmp(number, "hangup")) + mncc_hangup(ms); + else if (!strcmp(number, "hold")) + mncc_hold(ms); + else /* shall not happen */ + OSMO_ASSERT(0); - gsm_subscr_remove(ms); return CMD_SUCCESS; } -DEFUN(sim_pin, sim_pin_cmd, "sim pin MS_NAME PIN", - "SIM actions\nEnter PIN for SIM card\nName of MS (see \"show ms\")\n" - "PIN number") +DEFUN(call_retr, call_retr_cmd, + CALL_CMD " retrieve [NUMBER]", + CALL_CMD_DESC + "Retrieve call on hold\n" + "Number of call to retrieve\n") { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; - if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { - vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!ms->subscr.sim_pin_required) { - vty_out(vty, "No PIN is required at this time!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - gsm_subscr_sim_pin(ms, (char *)argv[1], "", 0); + mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0); return CMD_SUCCESS; } -DEFUN(sim_disable_pin, sim_disable_pin_cmd, "sim disable-pin MS_NAME PIN", - "SIM actions\nDisable PIN of SIM card\nName of MS (see \"show ms\")\n" - "PIN number") +DEFUN(call_dtmf, call_dtmf_cmd, + CALL_CMD " dtmf DIGITS", + CALL_CMD_DESC + "Send DTMF (Dual-Tone Multi-Frequency) tones\n" + "One or more DTMF digits to transmit\n") { struct osmocom_ms *ms; + struct gsm_settings *set; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; + set = &ms->settings; - if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { - vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); + if (!set->cc_dtmf) { + vty_out(vty, "DTMF not supported, please enable!%s", + VTY_NEWLINE); return CMD_WARNING; } - gsm_subscr_sim_pin(ms, (char *)argv[1], "", -1); + mncc_dtmf(ms, (char *)argv[1]); return CMD_SUCCESS; } -DEFUN(sim_enable_pin, sim_enable_pin_cmd, "sim enable-pin MS_NAME PIN", - "SIM actions\nEnable PIN of SIM card\nName of MS (see \"show ms\")\n" - "PIN number") +#define CALL_PARAMS_CMD \ + CALL_CMD " params" +#define CALL_PARAMS_CMD_DESC \ + CALL_CMD_DESC \ + "Call related parameters\n" + +#define CALL_PARAMS_DATA_CMD \ + CALL_PARAMS_CMD " data" +#define CALL_PARAMS_DATA_CMD_DESC \ + CALL_PARAMS_CMD_DESC \ + "Parameters for data calls\n" + +#define CFG_TCH_DATA_CALL_PARAMS_CMD \ + "call-params" +#define CFG_TCH_DATA_CALL_PARAMS_CMD_DESC \ + "Parameters for data calls\n" + +/* only supported rate/type ('<speed>' in AT+CBST) values are listed here */ +static const struct value_string data_type_rate_descs[] = { +#if 0 + /* TODO: rates below 2400 bps are not supported */ + { DATA_CALL_TR_V21_300, "300 bps (V.21)" }, + { DATA_CALL_TR_V22_1200, "1200 bps (V.22)" }, + { DATA_CALL_TR_V23_1200_75, "1200/75 bps (V.23)" }, +#endif + { DATA_CALL_TR_V22bis_2400, "2400 bps (V.22bis)" }, + { DATA_CALL_TR_V26ter_2400, "2400 bps (V.26ter)" }, + { DATA_CALL_TR_V32_4800, "4800 bps (V.32)" }, + { DATA_CALL_TR_V32_9600, "9600 bps (V.32)" }, +#if 0 + /* TODO: V.34 is not supported, see notes in bcap_data_set[] */ + { DATA_CALL_TR_V34_9600, "9600 bps (V.34)" }, + /* TODO: rates below 2400 bps are not supported */ + { DATA_CALL_TR_V110_300, "300 bps (V.110)" }, + { DATA_CALL_TR_V110_1200, "1200 bps (V.110)" }, +#endif + { DATA_CALL_TR_V110_2400, "2400 bps (V.110 or X.31 flag stuffing)" }, + { DATA_CALL_TR_V110_4800, "4800 bps (V.110 or X.31 flag stuffing)" }, + { DATA_CALL_TR_V110_9600, "9600 bps (V.110 or X.31 flag stuffing)" }, +#if 0 + /* TODO: 14400 bps is not supported */ + { DATA_CALL_TR_V110_14400, "14400 bps (V.110 or X.31 flag stuffing)" }, +#endif + { 0, NULL } +}; + +static void _data_type_rate_cmd_string(void *ctx, struct cmd_element *cmd) { - struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); - if (!ms) - return CMD_WARNING; + const struct value_string *vs; + char *string; - if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { - vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); - return CMD_WARNING; - } + string = talloc_asprintf(ctx, "%s type-rate (", cmd->string); + for (vs = &data_type_rate_descs[0]; vs->value || vs->str; vs++) + string = talloc_asprintf_append(string, "%u|", vs->value); + string[strlen(string) - 1] = ')'; + cmd->string = string; +} - gsm_subscr_sim_pin(ms, (char *)argv[1], "", 1); +DEFUN(cfg_ms_tch_data_cp_type_rate, + cfg_ms_tch_data_cp_type_rate_cmd, + CFG_TCH_DATA_CALL_PARAMS_CMD /* generated */, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC /* generated */) +{ + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct data_call_params *cp = &ms->settings.call_params.data; + int val; + + val = atoi(argv[0]); + OSMO_ASSERT(get_value_string_or_null(data_type_rate_descs, val) != NULL); + cp->type_rate = (enum data_call_type_rate)val; return CMD_SUCCESS; } -DEFUN(sim_change_pin, sim_change_pin_cmd, "sim change-pin MS_NAME OLD NEW", - "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n" - "Old PIN number\nNew PIN number") +DEFUN(call_params_data_type_rate, + call_params_data_type_rate_cmd, + CALL_PARAMS_DATA_CMD /* generated */, + CALL_PARAMS_DATA_CMD_DESC /* generated */) { - struct osmocom_ms *ms; - - ms = get_ms(argv[0], vty); - if (!ms) + vty->index = l23_vty_get_ms(argv[0], vty); + if (vty->index == NULL) return CMD_WARNING; - if (strlen(argv[1]) < 4 || strlen(argv[1]) > 8) { - vty_out(vty, "Old PIN must be in range 4..8!%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) { - vty_out(vty, "New PIN must be in range 4..8!%s", VTY_NEWLINE); + return cfg_ms_tch_data_cp_type_rate(self, vty, argc - 1, argv + 1); +} + +#define CALL_PARAMS_CE_CMD \ + "ce (transparent|non-transparent) [prefer]" +#define CALL_PARAMS_CE_CMD_DESC \ + "Connection element (does not apply to FAX calls)\n" \ + "Transparent connection\n" \ + "Non-transparent connection (RLP)\n" \ + "Prefer the selected mode, but also accept other(s)\n" + +DEFUN(cfg_ms_tch_data_cp_ce, + cfg_ms_tch_data_cp_ce_cmd, + CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_CE_CMD, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_CE_CMD_DESC) +{ + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct data_call_params *cp = &ms->settings.call_params.data; + + if (!strcmp(argv[0], "transparent")) { + if (argc > 1) + cp->transp = GSM48_BCAP_TR_TR_PREF; + else + cp->transp = GSM48_BCAP_TR_TRANSP; + } else if (!strcmp(argv[0], "non-transparent")) { + if (argc > 1) + cp->transp = GSM48_BCAP_TR_RLP_PREF; + else + cp->transp = GSM48_BCAP_TR_RLP; + } else { /* should not happen */ return CMD_WARNING; } - gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 2); - return CMD_SUCCESS; } -DEFUN(sim_unblock_pin, sim_unblock_pin_cmd, "sim unblock-pin MS_NAME PUC NEW", - "SIM actions\nChange PIN of SIM card\nName of MS (see \"show ms\")\n" - "Personal Unblock Key\nNew PIN number") +DEFUN(call_params_data_ce, + call_params_data_ce_cmd, + CALL_PARAMS_DATA_CMD " " CALL_PARAMS_CE_CMD, + CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_CE_CMD_DESC) { - struct osmocom_ms *ms; - - ms = get_ms(argv[0], vty); - if (!ms) + vty->index = l23_vty_get_ms(argv[0], vty); + if (vty->index == NULL) return CMD_WARNING; - if (strlen(argv[1]) != 8) { - vty_out(vty, "PUC must be 8 digits!%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (strlen(argv[2]) < 4 || strlen(argv[2]) > 8) { - vty_out(vty, "PIN must be in range 4..8!%s", VTY_NEWLINE); - return CMD_WARNING; - } + return cfg_ms_tch_data_cp_ce(self, vty, argc - 1, argv + 1); +} + +#define CALL_PARAMS_SYNC_ASYNC_CMD "(sync|async)" +#define CALL_PARAMS_SYNC_ASYNC_CMD_DESC \ + "Synchronous connection (always used for FAX calls)\n" \ + "Asynchronous connection (does not apply to FAX calls)\n" + +DEFUN(cfg_ms_tch_data_cp_sync_async, + cfg_ms_tch_data_cp_sync_async_cmd, + CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_SYNC_ASYNC_CMD, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_SYNC_ASYNC_CMD_DESC) +{ + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct data_call_params *cp = &ms->settings.call_params.data; - gsm_subscr_sim_pin(ms, (char *)argv[1], (char *)argv[2], 99); + cp->is_async = (argv[0][0] == 'a'); return CMD_SUCCESS; } -DEFUN(sim_lai, sim_lai_cmd, "sim lai MS_NAME MCC MNC LAC", - "SIM actions\nChange LAI of SIM card\nName of MS (see \"show ms\")\n" - "Mobile Country Code\nMobile Network Code\nLocation Area Code " - " (use 0000 to remove LAI)") +DEFUN(call_params_data_sync_async, + call_params_data_sync_async_cmd, + CALL_PARAMS_DATA_CMD " " CALL_PARAMS_SYNC_ASYNC_CMD, + CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_SYNC_ASYNC_CMD_DESC) { - struct osmocom_ms *ms; - uint16_t mcc = gsm_input_mcc((char *)argv[1]), - mnc = gsm_input_mnc((char *)argv[2]), - lac = strtoul(argv[3], NULL, 16); - - ms = get_ms(argv[0], vty); - if (!ms) + vty->index = l23_vty_get_ms(argv[0], vty); + if (vty->index == NULL) return CMD_WARNING; - if (mcc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (mnc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } + return cfg_ms_tch_data_cp_sync_async(self, vty, argc - 1, argv + 1); +} - ms->subscr.mcc = mcc; - ms->subscr.mnc = mnc; - ms->subscr.lac = lac; - ms->subscr.tmsi = 0xffffffff; +#define CALL_PARAMS_ASYNC_CMD "async" +#define CALL_PARAMS_ASYNC_CMD_DESC \ + "Asynchronous connection params (does not apply to FAX calls)\n" - gsm_subscr_write_loci(ms); +#define CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD \ + CALL_PARAMS_ASYNC_CMD " nr-stop-bits <1-2>" +#define CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD_DESC \ + CALL_PARAMS_ASYNC_CMD_DESC \ + "Number of stop bits (soft-UART config)\n" \ + "Number of stop bits (default: 1)\n" + +DEFUN(cfg_ms_tch_data_cp_async_nr_stop_bits, + cfg_ms_tch_data_cp_async_nr_stop_bits_cmd, + CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD_DESC) +{ + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct data_call_params *cp = &ms->settings.call_params.data; + + cp->nr_stop_bits = atoi(argv[0]); return CMD_SUCCESS; } -DEFUN(network_select, network_select_cmd, - "network select MS_NAME MCC MNC [force]", - "Select ...\nSelect Network\nName of MS (see \"show ms\")\n" - "Mobile Country Code\nMobile Network Code\n" - "Force selecting a network that is not in the list") +DEFUN(call_params_data_async_nr_stop_bits, + call_params_data_async_nr_stop_bits_cmd, + CALL_PARAMS_DATA_CMD " " CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD, + CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_ASYNC_NR_STOP_BITS_CMD_DESC) { - struct osmocom_ms *ms; - struct gsm322_plmn *plmn; - struct msgb *nmsg; - struct gsm322_msg *ngm; - struct gsm322_plmn_list *temp; - uint16_t mcc = gsm_input_mcc((char *)argv[1]), - mnc = gsm_input_mnc((char *)argv[2]); - int found = 0; - - ms = get_ms(argv[0], vty); - if (!ms) + vty->index = l23_vty_get_ms(argv[0], vty); + if (vty->index == NULL) return CMD_WARNING; - plmn = &ms->plmn; - if (ms->settings.plmn_mode != PLMN_MODE_MANUAL) { - vty_out(vty, "Not in manual network selection mode%s", - VTY_NEWLINE); - return CMD_WARNING; - } + return cfg_ms_tch_data_cp_async_nr_stop_bits(self, vty, argc - 1, argv + 1); +} - if (mcc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (mnc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } +#define CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD \ + CALL_PARAMS_ASYNC_CMD " nr-data-bits <7-8>" +#define CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD_DESC \ + CALL_PARAMS_ASYNC_CMD_DESC \ + "Number of data bits (soft-UART config)\n" \ + "Number of data bits (default: 8)\n" - if (argc < 4) { - llist_for_each_entry(temp, &plmn->sorted_plmn, entry) - if (temp->mcc == mcc && temp->mnc == mnc) - found = 1; - if (!found) { - vty_out(vty, "Network not in list!%s", VTY_NEWLINE); - vty_out(vty, "To force selecting this network, use " - "'force' keyword%s", VTY_NEWLINE); - return CMD_WARNING; - } - } +DEFUN(cfg_ms_tch_data_cp_async_nr_data_bits, + cfg_ms_tch_data_cp_async_nr_data_bits_cmd, + CFG_TCH_DATA_CALL_PARAMS_CMD " " CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD_DESC) +{ + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct data_call_params *cp = &ms->settings.call_params.data; - nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOOSE_PLMN); - if (!nmsg) - return CMD_WARNING; - ngm = (struct gsm322_msg *) nmsg->data; - ngm->mcc = mcc; - ngm->mnc = mnc; - gsm322_plmn_sendmsg(ms, nmsg); + cp->nr_data_bits = atoi(argv[0]); return CMD_SUCCESS; } -DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)", - "Make a call\nName of MS (see \"show ms\")\nPhone number to call " - "(Use digits '0123456789*#abc', and '+' to dial international)\n" - "Make an emergency call\nAnswer an incoming call\nHangup a call\n" - "Hold current active call\n") +DEFUN(call_params_data_async_nr_data_bits, + call_params_data_async_nr_data_bits_cmd, + CALL_PARAMS_DATA_CMD " " CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD, + CALL_PARAMS_DATA_CMD_DESC CALL_PARAMS_ASYNC_NR_DATA_BITS_CMD_DESC) { - struct osmocom_ms *ms; - struct gsm_settings *set; - struct gsm_settings_abbrev *abbrev; - char *number; - - ms = get_ms(argv[0], vty); - if (!ms) + vty->index = l23_vty_get_ms(argv[0], vty); + if (vty->index == NULL) return CMD_WARNING; - set = &ms->settings; - if (set->ch_cap == GSM_CAP_SDCCH) { - vty_out(vty, "Basic call is not supported for SDCCH only " - "mobile%s", VTY_NEWLINE); - return CMD_WARNING; - } + return cfg_ms_tch_data_cp_async_nr_data_bits(self, vty, argc - 1, argv + 1); +} - number = (char *)argv[1]; - if (!strcmp(number, "emergency")) - mncc_call(ms, number); - else if (!strcmp(number, "answer")) - mncc_answer(ms); - else if (!strcmp(number, "hangup")) - mncc_hangup(ms); - else if (!strcmp(number, "hold")) - mncc_hold(ms); - else { - llist_for_each_entry(abbrev, &set->abbrev, list) { - if (!strcmp(number, abbrev->abbrev)) { - number = abbrev->number; - vty_out(vty, "Dialing number '%s'%s", number, - VTY_NEWLINE); - break; - } - } - if (vty_check_number(vty, number)) - return CMD_WARNING; - mncc_call(ms, number); - } +static const struct value_string async_parity_names[] = { + { GSM48_BCAP_PAR_NONE, "none" }, + { GSM48_BCAP_PAR_EVEN, "even" }, + { GSM48_BCAP_PAR_ODD, "odd" }, + { GSM48_BCAP_PAR_ONE, "mark" }, + { GSM48_BCAP_PAR_ZERO, "space" }, + { 0, NULL } +}; - return CMD_SUCCESS; -} +static const struct value_string async_parity_descs[] = { + { GSM48_BCAP_PAR_NONE, "No parity bit (default)" }, + { GSM48_BCAP_PAR_EVEN, "Even parity" }, + { GSM48_BCAP_PAR_ODD, "Odd parity" }, + { GSM48_BCAP_PAR_ONE, "Always 1" }, + { GSM48_BCAP_PAR_ZERO, "Always 0" }, + { 0, NULL } +}; -DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [NUMBER]", - "Make a call\nName of MS (see \"show ms\")\n" - "Retrieve call on hold\nNumber of call to retrieve") -{ - struct osmocom_ms *ms; +#define CALL_PARAMS_ASYNC_PARITY_CMD \ + CALL_PARAMS_ASYNC_CMD " parity" +#define CALL_PARAMS_ASYNC_PARITY_CMD_DESC \ + CALL_PARAMS_ASYNC_CMD_DESC \ + "Parity mode (soft-UART config)\n" - ms = get_ms(argv[0], vty); - if (!ms) - return CMD_WARNING; +DEFUN(cfg_ms_tch_data_cp_async_parity, + cfg_ms_tch_data_cp_async_parity_cmd, + CFG_TCH_DATA_CALL_PARAMS_CMD /* generated */, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC + CALL_PARAMS_ASYNC_PARITY_CMD_DESC /* generated */) +{ + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct data_call_params *cp = &ms->settings.call_params.data; + int val; - mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0); + val = get_string_value(async_parity_names, argv[0]); + OSMO_ASSERT(val >= 0); /* should not happen */ + cp->parity = (enum gsm48_bcap_parity)val; return CMD_SUCCESS; } -DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS", - "Make a call\nName of MS (see \"show ms\")\n" - "One or more DTMF digits to transmit") +DEFUN(call_params_data_async_parity, + call_params_data_async_parity_cmd, + CALL_PARAMS_DATA_CMD /* generated */, + CALL_PARAMS_DATA_CMD_DESC + CALL_PARAMS_ASYNC_PARITY_CMD_DESC /* generated */) { - struct osmocom_ms *ms; - struct gsm_settings *set; - - ms = get_ms(argv[0], vty); - if (!ms) + vty->index = l23_vty_get_ms(argv[0], vty); + if (vty->index == NULL) return CMD_WARNING; - set = &ms->settings; - - if (!set->cc_dtmf) { - vty_out(vty, "DTMF not supported, please enable!%s", - VTY_NEWLINE); - return CMD_WARNING; - } - mncc_dtmf(ms, (char *)argv[1]); - - return CMD_SUCCESS; + return cfg_ms_tch_data_cp_async_parity(self, vty, argc - 1, argv + 1); } DEFUN(sms, sms_cmd, "sms MS_NAME NUMBER .LINE", @@ -913,7 +926,7 @@ DEFUN(sms, sms_cmd, "sms MS_NAME NUMBER .LINE", struct gsm_settings_abbrev *abbrev; char *number, *sms_sca = NULL; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; set = &ms->settings; @@ -972,7 +985,7 @@ DEFUN(service, service_cmd, "service MS_NAME (*#06#|*#21#|*#67#|*#61#|*#62#" { struct osmocom_ms *ms; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; @@ -981,14 +994,157 @@ DEFUN(service, service_cmd, "service MS_NAME (*#06#|*#21#|*#67#|*#61#|*#62#" return CMD_SUCCESS; } +#define VGCS_STR "Make a voice group call\nName of MS (see \"show ms\")\n" +#define VGCS_CMDS "(CALLREF|hangup|leave|talk|listen)" +#define VGCS_CMDS_TXT \ + "Voice group to call or join\nHangup voice group call\nLeave voice group call\nBecome talker\nBecome listener" + +/* This command enters VGCS call node with given MS. */ +DEFUN(vgcs_enter, vgcs_enter_cmd, "group-call MS_NAME", + VGCS_STR) +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + vty->index = ms; + vty->node = VGCS_NODE; + + return CMD_SUCCESS; +} + +/* These commands perform VGCS on VGCS node. */ +DEFUN(vgcs, vgcs_cmd, VGCS_CMDS, + VGCS_CMDS_TXT) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set; + const char *command; + + set = &ms->settings; + + if (!set->vgcs) { + vty_out(vty, "VGCS not supported by this mobile, please enable VGCS support%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (set->ch_cap == GSM_CAP_SDCCH) { + vty_out(vty, "ASCI call is not supported for SDCCH only mobile%s", VTY_NEWLINE); + return CMD_WARNING; + } + + command = (char *)argv[0]; + if (!strcmp(command, "hangup")) + gcc_bcc_hangup(ms); + else if (!strcmp(command, "leave")) + gcc_leave(ms); + else if (!strcmp(command, "talk")) + gcc_talk(ms); + else if (!strcmp(command, "listen")) + gcc_listen(ms); + else { + if (vty_check_callref(vty, command)) + return CMD_WARNING; + gcc_bcc_call(ms, GSM48_PDISC_GROUP_CC, command); + } + + return CMD_SUCCESS; +} + +/* These commands perform VGCS on given MS without entering the VGCS node. */ +DEFUN(vgcs_direct, vgcs_direct_cmd, "group-call MS_NAME " VGCS_CMDS, + VGCS_STR VGCS_CMDS_TXT) +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + vty->index = ms; + return vgcs(self, vty, argc - 1, argv + 1); +} + +#define VBS_STR "Make a voice broadcast call\nName of MS (see \"show ms\")\n" +#define VBS_CMDS "(CALLREF|hangup)" +#define VBS_CMDS_TXT \ + "Voice broadcast to call or join\nHangup voice broadcast call" + +/* This command enters VBS call node with given MS. */ +DEFUN(vbs_enter, vbs_enter_cmd, "broadcast-call MS_NAME", + VBS_STR) +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + vty->index = ms; + vty->node = VBS_NODE; + + return CMD_SUCCESS; +} + +/* These commands perform VBS on VBS node. */ +DEFUN(vbs, vbs_cmd, VBS_CMDS, + VBS_CMDS_TXT) +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set; + const char *command; + + set = &ms->settings; + + if (!set->vbs) { + vty_out(vty, "VBS not supported by this mobile, please enable VBS support%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (set->ch_cap == GSM_CAP_SDCCH) { + vty_out(vty, "ASCI call is not supported for SDCCH only mobile%s", VTY_NEWLINE); + return CMD_WARNING; + } + + command = (char *)argv[0]; + if (!strcmp(command, "hangup")) + gcc_bcc_hangup(ms); + else { + if (vty_check_callref(vty, command)) + return CMD_WARNING; + gcc_bcc_call(ms, GSM48_PDISC_BCAST_CC, command); + } + + return CMD_SUCCESS; +} + +/* These commands perform VBS on given MS without entering the VBS node. */ +DEFUN(vbs_direct, vbs_direct_cmd, "broadcast-call MS_NAME " VBS_CMDS, + VBS_STR VBS_CMDS_TXT) +{ + struct osmocom_ms *ms; + + ms = l23_vty_get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + vty->index = ms; + return vbs(self, vty, argc - 1, argv + 1); +} + +#define TEST_CMD_DESC "Special commands for testing\n" + DEFUN(test_reselection, test_reselection_cmd, "test re-selection NAME", - "Manually trigger cell re-selection\nName of MS (see \"show ms\")") + TEST_CMD_DESC "Manually trigger cell re-selection\n" + "Name of MS (see \"show ms\")") { struct osmocom_ms *ms; struct gsm_settings *set; struct msgb *nmsg; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; set = &ms->settings; @@ -1014,23 +1170,22 @@ DEFUN(delete_forbidden_plmn, delete_forbidden_plmn_cmd, "Mobile Country Code\nMobile Network Code") { struct osmocom_ms *ms; - uint16_t mcc = gsm_input_mcc((char *)argv[1]), - mnc = gsm_input_mnc((char *)argv[2]); + struct osmo_plmn_id plmn; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; - if (mcc == GSM_INPUT_INVALID) { + if (osmo_mcc_from_str(argv[1], &plmn.mcc) < 0) { vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); return CMD_WARNING; } - if (mnc == GSM_INPUT_INVALID) { + if (osmo_mnc_from_str(argv[2], &plmn.mnc, &plmn.mnc_3_digits) < 0) { vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); return CMD_WARNING; } - gsm_subscr_del_forbidden_plmn(&ms->subscr, mcc, mnc); + gsm_subscr_del_forbidden_plmn(&ms->subscr, &plmn); return CMD_SUCCESS; } @@ -1044,7 +1199,7 @@ DEFUN(network_show, network_show_cmd, "network show MS_NAME", struct gsm322_plmn *plmn; struct gsm322_plmn_list *temp; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; set = &ms->settings; @@ -1058,9 +1213,10 @@ DEFUN(network_show, network_show_cmd, "network show MS_NAME", llist_for_each_entry(temp, &plmn->sorted_plmn, entry) vty_out(vty, " Network %s, %s (%s, %s)%s", - gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), - gsm_get_mcc(temp->mcc), - gsm_get_mnc(temp->mcc, temp->mnc), VTY_NEWLINE); + osmo_mcc_name(temp->plmn.mcc), + osmo_mnc_name(temp->plmn.mnc, temp->plmn.mnc_3_digits), + gsm_get_mcc(temp->plmn.mcc), + gsm_get_mnc(&temp->plmn), VTY_NEWLINE); return CMD_SUCCESS; } @@ -1071,7 +1227,7 @@ DEFUN(network_search, network_search_cmd, "network search MS_NAME", struct osmocom_ms *ms; struct msgb *nmsg; - ms = get_ms(argv[0], vty); + ms = l23_vty_get_ms(argv[0], vty); if (!ms) return CMD_WARNING; @@ -1177,22 +1333,6 @@ DEFUN(cfg_gps_baud, cfg_gps_baud_cmd, "gps baudrate " return CMD_SUCCESS; } -DEFUN(cfg_hide_default, cfg_hide_default_cmd, "hide-default", - "Hide most default values in config to make it more compact") -{ - hide_default = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_hide_default, cfg_no_hide_default_cmd, "no hide-default", - NO_STR "Show default values in config") -{ - hide_default = 0; - - return CMD_SUCCESS; -} - /* per MS config */ DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME", "Select a mobile station to configure\nName of MS (see \"show ms\")") @@ -1208,7 +1348,7 @@ DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME", } if (!found) { - if (!vty_reading) { + if (!l23_vty_reading) { vty_out(vty, "MS name '%s' does not exits, try " "'ms %s create'%s", argv[0], argv[0], VTY_NEWLINE); @@ -1311,7 +1451,7 @@ DEFUN(cfg_no_ms, cfg_no_ms_cmd, "no ms MS_NAME", #define SUP_WRITE(item, cmd) \ if (sup->item) \ - if (!hide_default || !set->item) \ + if (!l23_vty_hide_default || !set->item) \ vty_out(vty, " %s%s%s", (set->item) ? "" : "no ", \ cmd, VTY_NEWLINE); @@ -1322,58 +1462,48 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) struct gsm_settings_abbrev *abbrev; vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE); - vty_out(vty, " layer2-socket %s%s", set->layer2_socket_path, - VTY_NEWLINE); + + l23_vty_config_write_ms_node_contents(vty, ms, " "); + vty_out(vty, " sap-socket %s%s", set->sap_socket_path, VTY_NEWLINE); - switch(set->sim_type) { - case GSM_SIM_TYPE_NONE: - vty_out(vty, " sim none%s", VTY_NEWLINE); - break; - case GSM_SIM_TYPE_L1PHY: - vty_out(vty, " sim reader%s", VTY_NEWLINE); - break; - case GSM_SIM_TYPE_TEST: - vty_out(vty, " sim test%s", VTY_NEWLINE); + vty_out(vty, " mncc-socket %s%s", set->mncc_socket_path, VTY_NEWLINE); + switch (set->mncc_handler) { + case MNCC_HANDLER_INTERNAL: + vty_out(vty, " mncc-handler internal%s", VTY_NEWLINE); break; - case GSM_SIM_TYPE_SAP: - vty_out(vty, " sim sap%s", VTY_NEWLINE); + case MNCC_HANDLER_EXTERNAL: + vty_out(vty, " mncc-handler external%s", VTY_NEWLINE); break; + case MNCC_HANDLER_DUMMY: + vty_out(vty, " mncc-handler dummy%s", VTY_NEWLINE); } vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode == PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE); - vty_out(vty, " imei %s %s%s", set->imei, - set->imeisv + strlen(set->imei), VTY_NEWLINE); - if (set->imei_random) - vty_out(vty, " imei-random %d%s", set->imei_random, - VTY_NEWLINE); - else - if (!hide_default) - vty_out(vty, " imei-fixed%s", VTY_NEWLINE); if (set->emergency_imsi[0]) vty_out(vty, " emergency-imsi %s%s", set->emergency_imsi, VTY_NEWLINE); else - if (!hide_default) + if (!l23_vty_hide_default) vty_out(vty, " no emergency-imsi%s", VTY_NEWLINE); if (set->sms_sca[0]) vty_out(vty, " sms-service-center %s%s", set->sms_sca, VTY_NEWLINE); else - if (!hide_default) + if (!l23_vty_hide_default) vty_out(vty, " no sms-service-center%s", VTY_NEWLINE); - if (!hide_default || set->cw) + if (!l23_vty_hide_default || set->cw) vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ", VTY_NEWLINE); - if (!hide_default || set->auto_answer) + if (!l23_vty_hide_default || set->auto_answer) vty_out(vty, " %sauto-answer%s", (set->auto_answer) ? "" : "no ", VTY_NEWLINE); - if (!hide_default || set->force_rekey) + if (!l23_vty_hide_default || set->force_rekey) vty_out(vty, " %sforce-rekey%s", (set->force_rekey) ? "" : "no ", VTY_NEWLINE); - if (!hide_default || set->clip) + if (!l23_vty_hide_default || set->clip) vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ", VTY_NEWLINE); - if (!hide_default || set->clir) + if (!l23_vty_hide_default || set->clir) vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ", VTY_NEWLINE); if (set->alter_tx_power) @@ -1383,25 +1513,25 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) else vty_out(vty, " tx-power full%s", VTY_NEWLINE); else - if (!hide_default) + if (!l23_vty_hide_default) vty_out(vty, " tx-power auto%s", VTY_NEWLINE); if (set->alter_delay) vty_out(vty, " simulated-delay %d%s", set->alter_delay, VTY_NEWLINE); else - if (!hide_default) + if (!l23_vty_hide_default) vty_out(vty, " no simulated-delay%s", VTY_NEWLINE); if (set->stick) vty_out(vty, " stick %d%s%s", set->stick_arfcn & 1023, (set->stick_arfcn & ARFCN_PCS) ? " pcs" : "", VTY_NEWLINE); else - if (!hide_default) + if (!l23_vty_hide_default) vty_out(vty, " no stick%s", VTY_NEWLINE); - if (!hide_default || set->no_lupd) + if (!l23_vty_hide_default || set->no_lupd) vty_out(vty, " %slocation-updating%s", (set->no_lupd) ? "no " : "", VTY_NEWLINE); - if (!hide_default || set->no_neighbour) + if (!l23_vty_hide_default || set->no_neighbour) vty_out(vty, " %sneighbour-measurement%s", (set->no_neighbour) ? "no " : "", VTY_NEWLINE); if (set->full_v1 || set->full_v2 || set->full_v3) { @@ -1419,7 +1549,7 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) vty_out(vty, " no codec half-speed%s", VTY_NEWLINE); } if (llist_empty(&set->abbrev)) { - if (!hide_default) + if (!l23_vty_hide_default) vty_out(vty, " no abbrev%s", VTY_NEWLINE); } else { llist_for_each_entry(abbrev, &set->abbrev, list) @@ -1439,32 +1569,32 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) SUP_WRITE(p_gsm, "p-gsm"); SUP_WRITE(e_gsm, "e-gsm"); SUP_WRITE(r_gsm, "r-gsm"); - SUP_WRITE(pcs, "gsm-850"); + SUP_WRITE(gsm_850, "gsm-850"); SUP_WRITE(gsm_480, "gsm-480"); SUP_WRITE(gsm_450, "gsm-450"); SUP_WRITE(dcs, "dcs"); SUP_WRITE(pcs, "pcs"); if (sup->r_gsm || sup->e_gsm || sup->p_gsm) - if (!hide_default || sup->class_900 != set->class_900) + if (!l23_vty_hide_default || sup->class_900 != set->class_900) vty_out(vty, " class-900 %d%s", set->class_900, VTY_NEWLINE); if (sup->gsm_850) - if (!hide_default || sup->class_850 != set->class_850) + if (!l23_vty_hide_default || sup->class_850 != set->class_850) vty_out(vty, " class-850 %d%s", set->class_850, VTY_NEWLINE); if (sup->gsm_480 || sup->gsm_450) - if (!hide_default || sup->class_400 != set->class_400) + if (!l23_vty_hide_default || sup->class_400 != set->class_400) vty_out(vty, " class-400 %d%s", set->class_400, VTY_NEWLINE); if (sup->dcs) - if (!hide_default || sup->class_dcs != set->class_dcs) + if (!l23_vty_hide_default || sup->class_dcs != set->class_dcs) vty_out(vty, " class-dcs %d%s", set->class_dcs, VTY_NEWLINE); if (sup->pcs) - if (!hide_default || sup->class_pcs != set->class_pcs) + if (!l23_vty_hide_default || sup->class_pcs != set->class_pcs) vty_out(vty, " class-pcs %d%s", set->class_pcs, VTY_NEWLINE); - if (!hide_default || sup->ch_cap != set->ch_cap) { + if (!l23_vty_hide_default || sup->ch_cap != set->ch_cap) { switch (set->ch_cap) { case GSM_CAP_SDCCH: vty_out(vty, " channel-capability sdcch%s", @@ -1485,63 +1615,84 @@ static void config_write_ms(struct vty *vty, struct osmocom_ms *ms) SUP_WRITE(full_v3, "full-speech-v3"); SUP_WRITE(half_v1, "half-speech-v1"); SUP_WRITE(half_v3, "half-speech-v3"); - if (!hide_default || sup->min_rxlev_dbm != set->min_rxlev_dbm) + SUP_WRITE(csd_tch_f144, "full-data-14400"); + SUP_WRITE(csd_tch_f96, "full-data-9600"); + SUP_WRITE(csd_tch_f48, "full-data-4800"); + SUP_WRITE(csd_tch_h48, "half-data-4800"); + SUP_WRITE(csd_tch_f24, "full-data-2400"); + SUP_WRITE(csd_tch_h24, "half-data-2400"); + if (!l23_vty_hide_default || sup->min_rxlev_dbm != set->min_rxlev_dbm) vty_out(vty, " min-rxlev %d%s", set->min_rxlev_dbm, VTY_NEWLINE); - if (!hide_default || sup->dsc_max != set->dsc_max) + if (!l23_vty_hide_default || sup->dsc_max != set->dsc_max) vty_out(vty, " dsc-max %d%s", set->dsc_max, VTY_NEWLINE); - if (!hide_default || set->skip_max_per_band) + if (!l23_vty_hide_default || set->skip_max_per_band) vty_out(vty, " %sskip-max-per-band%s", (set->skip_max_per_band) ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " test-sim%s", VTY_NEWLINE); - vty_out(vty, " imsi %s%s", set->test_imsi, VTY_NEWLINE); - switch (set->test_ki_type) { - case OSMO_AUTH_ALG_XOR: - vty_out(vty, " ki xor %s%s", - osmo_hexdump(set->test_ki, 12), VTY_NEWLINE); + SUP_WRITE(vgcs, "vgcs"); + SUP_WRITE(vbs, "vbs"); + if (!l23_vty_hide_default || set->any_timeout != MOB_C7_DEFLT_ANY_TIMEOUT) + vty_out(vty, " c7-any-timeout %d%s", + set->any_timeout, VTY_NEWLINE); + if (!l23_vty_hide_default || !set->uplink_release_local) + vty_out(vty, " %suplink-release-local%s", + (!set->uplink_release_local) ? "no " : "", VTY_NEWLINE); + if (!l23_vty_hide_default || set->asci_allow_any) + vty_out(vty, " %sasci-allow-any%s", + (set->asci_allow_any) ? "" : "no ", VTY_NEWLINE); + + vty_out(vty, " tch-voice%s", VTY_NEWLINE); + vty_out(vty, " io-handler %s%s", + tch_voice_io_handler_name(set->tch_voice.io_handler), VTY_NEWLINE); + if (set->tch_voice.io_handler == TCH_VOICE_IOH_GAPK) { + vty_out(vty, " io-tch-format %s%s", + tch_voice_io_format_name(set->tch_voice.io_format), VTY_NEWLINE); + vty_out(vty, " alsa-output-dev %s%s", + &set->tch_voice.alsa_output_dev[0], VTY_NEWLINE); + vty_out(vty, " alsa-input-dev %s%s", + &set->tch_voice.alsa_input_dev[0], VTY_NEWLINE); + } + + vty_out(vty, " tch-data%s", VTY_NEWLINE); + vty_out(vty, " io-handler %s%s", + tch_data_io_handler_name(set->tch_data.io_handler), VTY_NEWLINE); + vty_out(vty, " io-tch-format %s%s", + tch_data_io_format_name(set->tch_data.io_format), VTY_NEWLINE); + if (set->tch_data.io_handler == TCH_DATA_IOH_UNIX_SOCK) { + vty_out(vty, " unix-socket %s%s", + set->tch_data.unix_socket_path, VTY_NEWLINE); + } + + vty_out(vty, " call-params type-rate %d%s", + (int)set->call_params.data.type_rate, VTY_NEWLINE); + switch (set->call_params.data.transp) { + case GSM48_BCAP_TR_TR_PREF: + vty_out(vty, " call-params ce transparent prefer%s", VTY_NEWLINE); + break; + case GSM48_BCAP_TR_TRANSP: + vty_out(vty, " call-params ce transparent%s", VTY_NEWLINE); + break; + case GSM48_BCAP_TR_RLP_PREF: + vty_out(vty, " call-params ce non-transparent prefer%s", VTY_NEWLINE); break; - case OSMO_AUTH_ALG_COMP128v1: - vty_out(vty, " ki comp128 %s%s", - osmo_hexdump(set->test_ki, 16), VTY_NEWLINE); + case GSM48_BCAP_TR_RLP: + vty_out(vty, " call-params ce non-transparent%s", VTY_NEWLINE); break; } - if (!hide_default || set->test_barr) - vty_out(vty, " %sbarred-access%s", - (set->test_barr) ? "" : "no ", VTY_NEWLINE); - if (set->test_rplmn_valid) { - vty_out(vty, " rplmn %s %s", - gsm_print_mcc(set->test_rplmn_mcc), - gsm_print_mnc(set->test_rplmn_mnc)); - if (set->test_lac > 0x0000 && set->test_lac < 0xfffe) { - vty_out(vty, " 0x%04x", set->test_lac); - if (set->test_tmsi != 0xffffffff) { - vty_out(vty, " 0x%08x", set->test_tmsi); - if (set->test_imsi_attached) - vty_out(vty, " attached"); - } - } - vty_out(vty, "%s", VTY_NEWLINE); - } else - if (!hide_default) - vty_out(vty, " no rplmn%s", VTY_NEWLINE); - if (!hide_default || set->test_always) - vty_out(vty, " hplmn-search %s%s", - (set->test_always) ? "everywhere" : "foreign-country", - VTY_NEWLINE); - if (!hide_default || set->any_timeout != MOB_C7_DEFLT_ANY_TIMEOUT) - vty_out(vty, " c7-any-timeout %d%s", - set->any_timeout, VTY_NEWLINE); - - vty_out(vty, " audio%s", VTY_NEWLINE); - if (!hide_default || set->audio.io_handler != AUDIO_IOH_NONE) - vty_out(vty, " io-handler %s%s", audio_io_handler_name(set->audio.io_handler), VTY_NEWLINE); - - /* no shutdown must be written to config, because shutdown is default */ - vty_out(vty, " %sshutdown%s", (ms->shutdown != MS_SHUTDOWN_NONE) ? "" : "no ", + vty_out(vty, " call-params %s%s", + set->call_params.data.is_async ? "async" : "sync", VTY_NEWLINE); + vty_out(vty, " call-params async nr-stop-bits %u%s", + set->call_params.data.nr_stop_bits, VTY_NEWLINE); + vty_out(vty, " call-params async nr-data-bits %u%s", + set->call_params.data.nr_data_bits, VTY_NEWLINE); + vty_out(vty, " call-params async parity %s%s", + get_value_string(async_parity_names, set->call_params.data.parity), VTY_NEWLINE); + if (ms->lua_script) vty_out(vty, " lua-script %s%s", ms->lua_script, VTY_NEWLINE); - vty_out(vty, "!%s", VTY_NEWLINE); + + l23_vty_config_write_ms_node_contents_final(vty, ms, " "); } static int config_write(struct vty *vty) @@ -1559,7 +1710,7 @@ static int config_write(struct vty *vty) vty_out(vty, "%sgps enable%s", (g.enable) ? "" : "no ", VTY_NEWLINE); vty_out(vty, "!%s", VTY_NEWLINE); - vty_out(vty, "%shide-default%s", (hide_default) ? "": "no ", + vty_out(vty, "%shide-default%s", (l23_vty_hide_default) ? "" : "no ", VTY_NEWLINE); vty_out(vty, "!%s", VTY_NEWLINE); @@ -1579,60 +1730,75 @@ DEFUN(cfg_ms_show_this, cfg_ms_show_this_cmd, "show this", return CMD_SUCCESS; } -DEFUN(cfg_ms_layer2, cfg_ms_layer2_cmd, "layer2-socket PATH", - "Define socket path to connect between layer 2 and layer 1\n" - "Unix socket, default '/tmp/osmocom_l2'") +DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH", + "Define socket path to connect to SIM reader\n" + "Unix socket, default '/tmp/osmocom_sap'") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; - OSMO_STRLCPY_ARRAY(set->layer2_socket_path, argv[0]); + OSMO_STRLCPY_ARRAY(set->sap_socket_path, argv[0]); vty_restart(vty, ms); return CMD_SUCCESS; } -DEFUN(cfg_ms_sap, cfg_ms_sap_cmd, "sap-socket PATH", - "Define socket path to connect to SIM reader\n" - "Unix socket, default '/tmp/osmocom_sap'") +DEFUN(cfg_ms_mncc_sock, cfg_ms_mncc_sock_cmd, "mncc-socket PATH", + "Define socket path for MNCC interface\n" + "UNIX socket path (default '/tmp/ms_mncc_' + MS_NAME)") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; - OSMO_STRLCPY_ARRAY(set->sap_socket_path, argv[0]); + OSMO_STRLCPY_ARRAY(set->mncc_socket_path, argv[0]); vty_restart(vty, ms); return CMD_SUCCESS; } -DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|reader|test|sap)", - "Set SIM card to attach when powering on\nAttach no SIM\n" - "Attach SIM from reader\nAttach build in test SIM\n" - "Attach SIM over SAP interface") +DEFUN(cfg_ms_mncc_handler, cfg_ms_mncc_handler_cmd, + "mncc-handler (internal|external|dummy)", + "Set MNCC (Call Control) handler\n" + "Built-in MNCC handler (default)\n" + "External MNCC application via UNIX-socket (e.g. LCR)\n" + "Dummy MNCC handler (no Call Control)\n") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; switch (argv[0][0]) { - case 'n': - set->sim_type = GSM_SIM_TYPE_NONE; - break; - case 'r': - set->sim_type = GSM_SIM_TYPE_L1PHY; + case 'i': + if (set->ch_cap == GSM_CAP_SDCCH) { /* SDCCH only */ + vty_out(vty, "TCH support is disabled, " + "check 'channel-capability' param%s", VTY_NEWLINE); + return CMD_WARNING; + } + set->mncc_handler = MNCC_HANDLER_INTERNAL; break; - case 't': - set->sim_type = GSM_SIM_TYPE_TEST; + case 'e': + set->mncc_handler = MNCC_HANDLER_EXTERNAL; break; - case 's': - set->sim_type = GSM_SIM_TYPE_SAP; + case 'd': + set->mncc_handler = MNCC_HANDLER_DUMMY; break; default: - vty_out(vty, "unknown SIM type%s", VTY_NEWLINE); - return CMD_WARNING; + /* Shall not happen */ + OSMO_ASSERT(0); } vty_restart_if_started(vty, ms); + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_mncc_handler, cfg_ms_no_mncc_handler_cmd, + "no mncc-handler", NO_STR "Disable Call Control") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->mncc_handler = MNCC_HANDLER_DUMMY; + vty_restart_if_started(vty, ms); return CMD_SUCCESS; } @@ -1662,63 +1828,14 @@ DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)", return CMD_SUCCESS; } -DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]", - "Set IMEI (enter without control digit)\n15 Digits IMEI\n" - "Software version digit") -{ - struct osmocom_ms *ms = vty->index; - struct gsm_settings *set = &ms->settings; - char *error, *sv = "0"; - - if (argc >= 2) - sv = (char *)argv[1]; - - error = gsm_check_imei(argv[0], sv); - if (error) { - vty_out(vty, "%s%s", error, VTY_NEWLINE); - return CMD_WARNING; - } - - strcpy(set->imei, argv[0]); - strcpy(set->imeisv, argv[0]); - strcpy(set->imeisv + 15, sv); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed", - "Use fixed IMEI on every power on") -{ - struct osmocom_ms *ms = vty->index; - struct gsm_settings *set = &ms->settings; - - set->imei_random = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>", - "Use random IMEI on every power on\n" - "Number of trailing digits to randomize") -{ - struct osmocom_ms *ms = vty->index; - struct gsm_settings *set = &ms->settings; - - set->imei_random = atoi(argv[0]); - - return CMD_SUCCESS; -} - DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi IMSI", "Use special IMSI for emergency calls\n15 digits IMSI") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; - char *error; - error = gsm_check_imsi(argv[0]); - if (error) { - vty_out(vty, "%s%s", error, VTY_NEWLINE); + if (!osmo_imsi_str_valid(argv[0])) { + vty_out(vty, "Wrong IMSI format%s", VTY_NEWLINE); return CMD_WARNING; } strcpy(set->emergency_imsi, argv[0]); @@ -2209,6 +2326,48 @@ DEFUN(cfg_ms_any_timeout, cfg_ms_any_timeout_cmd, "c7-any-timeout <0-255>", return CMD_SUCCESS; } +DEFUN(cfg_ms_no_uplink_release_local, cfg_ms_no_uplink_release_local_cmd, "no uplink-release-local", + NO_STR "Release L2 on uplink of VGCS channel normally. Release locally when UPLINK FREE is received.") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->uplink_release_local = false; + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_uplink_release_local, cfg_ms_uplink_release_local_cmd, "uplink-release-local", + "Release L2 on uplink of VGCS channel locally after receiving UPLINK FREE.") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->uplink_release_local = true; + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_asci_allow_any, cfg_ms_asci_allow_any_cmd, "asci-allow-any", + "Allow any ASCI related call feature, even if service is limited or SIM invalid.") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->asci_allow_any = true; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_asci_allow_any, cfg_ms_no_asci_allow_any_cmd, "no asci-allow-any", + NO_STR "Do not allow any ASCI related call feature, if service is not normal.") +{ + struct osmocom_ms *ms = vty->index; + struct gsm_settings *set = &ms->settings; + + set->asci_allow_any = false; + + return CMD_SUCCESS; +} + static int config_write_dummy(struct vty *vty) { return CMD_SUCCESS; @@ -2223,15 +2382,17 @@ DEFUN(cfg_ms_support, cfg_ms_support_cmd, "support", return CMD_SUCCESS; } -#define SUP_EN(cfg, cfg_cmd, item, cmd, desc, restart) \ -DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \ +#define SUP_EN(item, cmd, desc, restart) \ +DEFUN(cfg_ms_sup_en_##item, \ + cfg_ms_sup_en_##item##_cmd, \ + cmd, "Enable " desc "support") \ { \ struct osmocom_ms *ms = vty->index; \ struct gsm_settings *set = &ms->settings; \ struct gsm_support *sup = &ms->support; \ if (!sup->item) { \ vty_out(vty, desc " not supported%s", VTY_NEWLINE); \ - if (vty_reading) \ + if (l23_vty_reading) \ return CMD_SUCCESS; \ return CMD_WARNING; \ } \ @@ -2241,15 +2402,17 @@ DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \ return CMD_SUCCESS; \ } -#define SUP_DI(cfg, cfg_cmd, item, cmd, desc, restart) \ -DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \ +#define SUP_DI(item, cmd, desc, restart) \ +DEFUN(cfg_ms_sup_di_##item, \ + cfg_ms_sup_di_##item##_cmd, \ + "no " cmd, NO_STR "Disable " desc " support") \ { \ struct osmocom_ms *ms = vty->index; \ struct gsm_settings *set = &ms->settings; \ struct gsm_support *sup = &ms->support; \ if (!sup->item) { \ vty_out(vty, desc " not supported%s", VTY_NEWLINE); \ - if (vty_reading) \ + if (l23_vty_reading) \ return CMD_SUCCESS; \ return CMD_WARNING; \ } \ @@ -2259,8 +2422,15 @@ DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \ return CMD_SUCCESS; \ } -#define SET_EN(cfg, cfg_cmd, item, cmd, desc, restart) \ -DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \ +#define SUP_EN_DI(item, cmd, desc, restart) \ + SUP_EN(item, cmd, desc, restart); \ + SUP_DI(item, cmd, desc, restart) + + +#define SET_EN(item, cmd, desc, restart) \ +DEFUN(cfg_ms_set_en_##item, \ + cfg_ms_set_en_##item##_cmd, \ + cmd, "Enable " desc "support") \ { \ struct osmocom_ms *ms = vty->index; \ struct gsm_settings *set = &ms->settings; \ @@ -2270,8 +2440,10 @@ DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \ return CMD_SUCCESS; \ } -#define SET_DI(cfg, cfg_cmd, item, cmd, desc, restart) \ -DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \ +#define SET_DI(item, cmd, desc, restart) \ +DEFUN(cfg_ms_set_di_##item, \ + cfg_ms_set_di_##item##_cmd, \ + "no " cmd, NO_STR "Disable " desc " support") \ { \ struct osmocom_ms *ms = vty->index; \ struct gsm_settings *set = &ms->settings; \ @@ -2281,52 +2453,28 @@ DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \ return CMD_SUCCESS; \ } -SET_EN(cfg_ms_sup_dtmf, cfg_ms_sup_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0); -SET_DI(cfg_ms_sup_no_dtmf, cfg_ms_sup_no_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0); -SUP_EN(cfg_ms_sup_sms, cfg_ms_sup_sms_cmd, sms_ptp, "sms", "SMS", 0); -SUP_DI(cfg_ms_sup_no_sms, cfg_ms_sup_no_sms_cmd, sms_ptp, "sms", "SMS", 0); -SUP_EN(cfg_ms_sup_a5_1, cfg_ms_sup_a5_1_cmd, a5_1, "a5/1", "A5/1", 0); -SUP_DI(cfg_ms_sup_no_a5_1, cfg_ms_sup_no_a5_1_cmd, a5_1, "a5/1", "A5/1", 0); -SUP_EN(cfg_ms_sup_a5_2, cfg_ms_sup_a5_2_cmd, a5_2, "a5/2", "A5/2", 0); -SUP_DI(cfg_ms_sup_no_a5_2, cfg_ms_sup_no_a5_2_cmd, a5_2, "a5/2", "A5/2", 0); -SUP_EN(cfg_ms_sup_a5_3, cfg_ms_sup_a5_3_cmd, a5_3, "a5/3", "A5/3", 0); -SUP_DI(cfg_ms_sup_no_a5_3, cfg_ms_sup_no_a5_3_cmd, a5_3, "a5/3", "A5/3", 0); -SUP_EN(cfg_ms_sup_a5_4, cfg_ms_sup_a5_4_cmd, a5_4, "a5/4", "A5/4", 0); -SUP_DI(cfg_ms_sup_no_a5_4, cfg_ms_sup_no_a5_4_cmd, a5_4, "a5/4", "A5/4", 0); -SUP_EN(cfg_ms_sup_a5_5, cfg_ms_sup_a5_5_cmd, a5_5, "a5/5", "A5/5", 0); -SUP_DI(cfg_ms_sup_no_a5_5, cfg_ms_sup_no_a5_5_cmd, a5_5, "a5/5", "A5/5", 0); -SUP_EN(cfg_ms_sup_a5_6, cfg_ms_sup_a5_6_cmd, a5_6, "a5/6", "A5/6", 0); -SUP_DI(cfg_ms_sup_no_a5_6, cfg_ms_sup_no_a5_6_cmd, a5_6, "a5/6", "A5/6", 0); -SUP_EN(cfg_ms_sup_a5_7, cfg_ms_sup_a5_7_cmd, a5_7, "a5/7", "A5/7", 0); -SUP_DI(cfg_ms_sup_no_a5_7, cfg_ms_sup_no_a5_7_cmd, a5_7, "a5/7", "A5/7", 0); -SUP_EN(cfg_ms_sup_p_gsm, cfg_ms_sup_p_gsm_cmd, p_gsm, "p-gsm", "P-GSM (900)", - 1); -SUP_DI(cfg_ms_sup_no_p_gsm, cfg_ms_sup_no_p_gsm_cmd, p_gsm, "p-gsm", - "P-GSM (900)", 1); -SUP_EN(cfg_ms_sup_e_gsm, cfg_ms_sup_e_gsm_cmd, e_gsm, "e-gsm", "E-GSM (850)", - 1); -SUP_DI(cfg_ms_sup_no_e_gsm, cfg_ms_sup_no_e_gsm_cmd, e_gsm, "e-gsm", - "E-GSM (850)", 1); -SUP_EN(cfg_ms_sup_r_gsm, cfg_ms_sup_r_gsm_cmd, r_gsm, "r-gsm", "R-GSM (850)", - 1); -SUP_DI(cfg_ms_sup_no_r_gsm, cfg_ms_sup_no_r_gsm_cmd, r_gsm, "r-gsm", - "R-GSM (850)", 1); -SUP_EN(cfg_ms_sup_dcs, cfg_ms_sup_dcs_cmd, dcs, "dcs", "DCS (1800)", 1); -SUP_DI(cfg_ms_sup_no_dcs, cfg_ms_sup_no_dcs_cmd, dcs, "dcs", "DCS (1800)", 1); -SUP_EN(cfg_ms_sup_gsm_850, cfg_ms_sup_gsm_850_cmd, gsm_850, "gsm-850", - "GSM 850", 1); -SUP_DI(cfg_ms_sup_no_gsm_850, cfg_ms_sup_no_gsm_850_cmd, gsm_850, "gsm-850", - "GSM 850", 1); -SUP_EN(cfg_ms_sup_pcs, cfg_ms_sup_pcs_cmd, pcs, "pcs", "PCS (1900)", 1); -SUP_DI(cfg_ms_sup_no_pcs, cfg_ms_sup_no_pcs_cmd, pcs, "pcs", "PCS (1900)", 1); -SUP_EN(cfg_ms_sup_gsm_480, cfg_ms_sup_gsm_480_cmd, gsm_480, "gsm-480", - "GSM 480", 1); -SUP_DI(cfg_ms_sup_no_gsm_480, cfg_ms_sup_no_gsm_480_cmd, gsm_480, "gsm-480", - "GSM 480", 1); -SUP_EN(cfg_ms_sup_gsm_450, cfg_ms_sup_gsm_450_cmd, gsm_450, "gsm-450", - "GSM 450", 1); -SUP_DI(cfg_ms_sup_no_gsm_450, cfg_ms_sup_no_gsm_450_cmd, gsm_450, "gsm-450", - "GSM 450", 1); +#define SET_EN_DI(item, cmd, desc, restart) \ + SET_EN(item, cmd, desc, restart); \ + SET_DI(item, cmd, desc, restart) + + +SET_EN_DI(cc_dtmf, "dtmf", "DTMF", 0); +SUP_EN_DI(sms_ptp, "sms", "SMS", 0); +SUP_EN_DI(a5_1, "a5/1", "A5/1", 0); +SUP_EN_DI(a5_2, "a5/2", "A5/2", 0); +SUP_EN_DI(a5_3, "a5/3", "A5/3", 0); +SUP_EN_DI(a5_4, "a5/4", "A5/4", 0); +SUP_EN_DI(a5_5, "a5/5", "A5/5", 0); +SUP_EN_DI(a5_6, "a5/6", "A5/6", 0); +SUP_EN_DI(a5_7, "a5/7", "A5/7", 0); +SUP_EN_DI(p_gsm, "p-gsm", "P-GSM (900)", 1); +SUP_EN_DI(e_gsm, "e-gsm", "E-GSM (850)", 1); +SUP_EN_DI(r_gsm, "r-gsm", "R-GSM (850)", 1); +SUP_EN_DI(dcs, "dcs", "DCS (1800)", 1); +SUP_EN_DI(gsm_850, "gsm-850", "GSM 850", 1); +SUP_EN_DI(pcs, "pcs", "PCS (1900)", 1); +SUP_EN_DI(gsm_480, "gsm-480", "GSM 480", 1); +SUP_EN_DI(gsm_450, "gsm-450", "GSM 450", 1); DEFUN(cfg_ms_sup_class_900, cfg_ms_sup_class_900_cmd, "class-900 (1|2|3|4|5)", "Select power class for GSM 900\n" @@ -2342,7 +2490,7 @@ DEFUN(cfg_ms_sup_class_900, cfg_ms_sup_class_900_cmd, "class-900 (1|2|3|4|5)", set->class_900 = atoi(argv[0]); - if (set->class_900 < sup->class_900 && !vty_reading) + if (set->class_900 < sup->class_900 && !l23_vty_reading) vty_out(vty, "Note: You selected a higher class than supported " " by hardware!%s", VTY_NEWLINE); @@ -2363,7 +2511,7 @@ DEFUN(cfg_ms_sup_class_850, cfg_ms_sup_class_850_cmd, "class-850 (1|2|3|4|5)", set->class_850 = atoi(argv[0]); - if (set->class_850 < sup->class_850 && !vty_reading) + if (set->class_850 < sup->class_850 && !l23_vty_reading) vty_out(vty, "Note: You selected a higher class than supported " " by hardware!%s", VTY_NEWLINE); @@ -2384,7 +2532,7 @@ DEFUN(cfg_ms_sup_class_400, cfg_ms_sup_class_400_cmd, "class-400 (1|2|3|4|5)", set->class_400 = atoi(argv[0]); - if (set->class_400 < sup->class_400 && !vty_reading) + if (set->class_400 < sup->class_400 && !l23_vty_reading) vty_out(vty, "Note: You selected a higher class than supported " " by hardware!%s", VTY_NEWLINE); @@ -2404,7 +2552,7 @@ DEFUN(cfg_ms_sup_class_dcs, cfg_ms_sup_class_dcs_cmd, "class-dcs (1|2|3)", set->class_dcs = atoi(argv[0]); if (((set->class_dcs + 1) & 3) < ((sup->class_dcs + 1) & 3) - && !vty_reading) + && !l23_vty_reading) vty_out(vty, "Note: You selected a higher class than supported " " by hardware!%s", VTY_NEWLINE); @@ -2424,7 +2572,7 @@ DEFUN(cfg_ms_sup_class_pcs, cfg_ms_sup_class_pcs_cmd, "class-pcs (1|2|3)", set->class_pcs = atoi(argv[0]); if (((set->class_pcs + 1) & 3) < ((sup->class_pcs + 1) & 3) - && !vty_reading) + && !l23_vty_reading) vty_out(vty, "Note: You selected a higher class than supported " " by hardware!%s", VTY_NEWLINE); @@ -2450,7 +2598,7 @@ DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd, else ch_cap = GSM_CAP_SDCCH; - if (ch_cap > sup->ch_cap && !vty_reading) { + if (ch_cap > sup->ch_cap && !l23_vty_reading) { vty_out(vty, "You selected an higher capability than supported " " by hardware!%s", VTY_NEWLINE); return CMD_WARNING; @@ -2465,26 +2613,18 @@ DEFUN(cfg_ms_sup_ch_cap, cfg_ms_sup_ch_cap_cmd, return CMD_SUCCESS; } -SUP_EN(cfg_ms_sup_full_v1, cfg_ms_sup_full_v1_cmd, full_v1, "full-speech-v1", - "Full rate speech V1", 0); -SUP_DI(cfg_ms_sup_no_full_v1, cfg_ms_sup_no_full_v1_cmd, full_v1, - "full-speech-v1", "Full rate speech V1", 0); -SUP_EN(cfg_ms_sup_full_v2, cfg_ms_sup_full_v2_cmd, full_v2, "full-speech-v2", - "Full rate speech V2 (EFR)", 0); -SUP_DI(cfg_ms_sup_no_full_v2, cfg_ms_sup_no_full_v2_cmd, full_v2, - "full-speech-v2", "Full rate speech V2 (EFR)", 0); -SUP_EN(cfg_ms_sup_full_v3, cfg_ms_sup_full_v3_cmd, full_v3, "full-speech-v3", - "Full rate speech V3 (AMR)", 0); -SUP_DI(cfg_ms_sup_no_full_v3, cfg_ms_sup_no_full_v3_cmd, full_v3, - "full-speech-v3", "Full rate speech V3 (AMR)", 0); -SUP_EN(cfg_ms_sup_half_v1, cfg_ms_sup_half_v1_cmd, half_v1, "half-speech-v1", - "Half rate speech V1", 0); -SUP_DI(cfg_ms_sup_no_half_v1, cfg_ms_sup_no_half_v1_cmd, half_v1, - "half-speech-v1", "Half rate speech V1", 0); -SUP_EN(cfg_ms_sup_half_v3, cfg_ms_sup_half_v3_cmd, half_v3, "half-speech-v3", - "Half rate speech V3 (AMR)", 0); -SUP_DI(cfg_ms_sup_no_half_v3, cfg_ms_sup_no_half_v3_cmd, half_v3, - "half-speech-v3", "Half rate speech V3 (AMR)", 0); +SUP_EN_DI(full_v1, "full-speech-v1", "Full rate speech V1", 0); +SUP_EN_DI(full_v2, "full-speech-v2", "Full rate speech V2 (EFR)", 0); +SUP_EN_DI(full_v3, "full-speech-v3", "Full rate speech V3 (AMR)", 0); +SUP_EN_DI(half_v1, "half-speech-v1", "Half rate speech V1", 0); +SUP_EN_DI(half_v3, "half-speech-v3", "Half rate speech V3 (AMR)", 0); + +SUP_EN_DI(csd_tch_f144, "full-data-14400", "CSD TCH/F14.4", 0); +SUP_EN_DI(csd_tch_f96, "full-data-9600", "CSD TCH/F9.6", 0); +SUP_EN_DI(csd_tch_f48, "full-data-4800", "CSD TCH/F4.8", 0); +SUP_EN_DI(csd_tch_h48, "half-data-4800", "CSD TCH/H4.8", 0); +SUP_EN_DI(csd_tch_f24, "full-data-2400", "CSD TCH/F2.4", 0); +SUP_EN_DI(csd_tch_h24, "half-data-2400", "CSD TCH/H2.4", 0); DEFUN(cfg_ms_sup_min_rxlev, cfg_ms_sup_min_rxlev_cmd, "min-rxlev <-110--47>", "Set the minimum receive level to select a cell\n" @@ -2536,291 +2676,184 @@ DEFUN(cfg_ms_sup_no_skip_max_per_band, cfg_ms_sup_no_skip_max_per_band_cmd, return CMD_SUCCESS; } -/* per testsim config */ -DEFUN(cfg_ms_testsim, cfg_ms_testsim_cmd, "test-sim", - "Configure test SIM emulation") -{ - vty->node = TESTSIM_NODE; +SUP_EN_DI(vgcs, "vgcs", "Voice Group Call Service (VGCS)", 0); +SUP_EN_DI(vbs, "vbs", "Voice Broadcast Service (VBS)", 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI", - "Set IMSI on test card\n15 digits IMSI") +/* TCH config */ +DEFUN(cfg_ms_tch_voice, + cfg_ms_tch_voice_cmd, + "tch-voice", "Configure TCH (Traffic CHannel) params for voice calls\n") { - struct osmocom_ms *ms = vty->index; - struct gsm_settings *set = &ms->settings; - char *error = gsm_check_imsi(argv[0]); - - if (error) { - vty_out(vty, "%s%s", error, VTY_NEWLINE); - return CMD_WARNING; - } - - strcpy(set->test_imsi, argv[0]); - - vty_restart_if_started(vty, ms); - + vty->node = TCH_VOICE_NODE; return CMD_SUCCESS; } -#define HEX_STR "\nByte as two digits hexadecimal" -DEFUN(cfg_test_ki_xor, cfg_test_ki_xor_cmd, "ki xor HEX HEX HEX HEX HEX HEX " - "HEX HEX HEX HEX HEX HEX", - "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR - HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR) -{ - struct osmocom_ms *ms = vty->index; - struct gsm_settings *set = &ms->settings; - uint8_t ki[12]; - const char *p; - int i; - - for (i = 0; i < 12; i++) { - p = argv[i]; - if (!strncmp(p, "0x", 2)) - p += 2; - if (strlen(p) != 2) { - vty_out(vty, "Expecting two digits hex value (with or " - "without 0x in front)%s", VTY_NEWLINE); - return CMD_WARNING; - } - ki[i] = strtoul(p, NULL, 16); - } +ALIAS_DEPRECATED(cfg_ms_tch_voice, /* alias to 'tch-voice' */ + cfg_ms_audio_cmd, + "audio", "(deprecated alias for 'tch-voice')\n"); - set->test_ki_type = OSMO_AUTH_ALG_XOR; - memcpy(set->test_ki, ki, 12); - return CMD_SUCCESS; -} - -DEFUN(cfg_test_ki_comp128, cfg_test_ki_comp128_cmd, "ki comp128 HEX HEX HEX " - "HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX HEX", - "Set Key (Ki) on test card\nUse XOR algorithm" HEX_STR HEX_STR HEX_STR - HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR HEX_STR - HEX_STR HEX_STR HEX_STR HEX_STR) +DEFUN(cfg_ms_tch_voice_io_handler, cfg_ms_tch_voice_io_handler_cmd, + "io-handler (none|gapk|l1phy|mncc-sock|loopback)", + "Set TCH frame I/O handler for voice calls\n" + "No handler, drop TCH frames (default)\n" + "libosmo-gapk based I/O handler (requires ALSA)\n" + "L1 PHY (e.g. Calypso DSP in Motorola C1xx phones)\n" + "External MNCC application (e.g. LCR) via MNCC socket\n" + "Return TCH frame payload back to sender\n") { - struct osmocom_ms *ms = vty->index; + int val = get_string_value(tch_voice_io_handler_names, argv[0]); + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; struct gsm_settings *set = &ms->settings; - uint8_t ki[16]; - const char *p; - int i; - for (i = 0; i < 16; i++) { - p = argv[i]; - if (!strncmp(p, "0x", 2)) - p += 2; - if (strlen(p) != 2) { - vty_out(vty, "Expecting two digits hex value (with or " - "without 0x in front)%s", VTY_NEWLINE); + OSMO_ASSERT(val >= 0); + + if (val == TCH_VOICE_IOH_MNCC_SOCK) { + if (ms->settings.mncc_handler != MNCC_HANDLER_INTERNAL) { + vty_out(vty, "TCH voice I/O handler 'mncc-sock' can only be used " + "with MNCC handler 'external'%s", VTY_NEWLINE); return CMD_WARNING; } - ki[i] = strtoul(p, NULL, 16); } - set->test_ki_type = OSMO_AUTH_ALG_COMP128v1; - memcpy(set->test_ki, ki, 16); - return CMD_SUCCESS; -} - -DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access", - "Allow access to barred cells") -{ - struct osmocom_ms *ms = vty->index; - struct gsm_settings *set = &ms->settings; +#ifndef WITH_GAPK_IO + if (val == TCH_VOICE_IOH_GAPK) { + vty_out(vty, "GAPK I/O is not compiled in (--with-gapk-io)%s", VTY_NEWLINE); + return CMD_WARNING; + } +#endif - set->test_barr = 1; + set->tch_voice.io_handler = (enum tch_voice_io_handler)val; return CMD_SUCCESS; } -DEFUN(cfg_test_no_barr, cfg_test_no_barr_cmd, "no barred-access", - NO_STR "Deny access to barred cells") +DEFUN(cfg_ms_tch_voice_no_io_handler, cfg_ms_tch_voice_no_io_handler_cmd, + "no io-handler", NO_STR "Disable TCH frame handling for voice calls\n") { - struct osmocom_ms *ms = vty->index; + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; struct gsm_settings *set = &ms->settings; - set->test_barr = 0; + set->tch_voice.io_handler = TCH_VOICE_IOH_NONE; return CMD_SUCCESS; } -DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn", - NO_STR "Unset Registered PLMN") +DEFUN(cfg_ms_tch_voice_io_tch_format, cfg_ms_tch_voice_io_tch_format_cmd, + "io-tch-format (rtp|ti)", + "Set TCH I/O frame format used by the L1 PHY (for GAPK only)\n" + "RTP format (RFC3551 for FR/EFR, RFC5993 for HR, RFC4867 for AMR)\n" + "Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx)\n") { - struct osmocom_ms *ms = vty->index; + int val = get_string_value(tch_voice_io_format_names, argv[0]); + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; struct gsm_settings *set = &ms->settings; - set->test_rplmn_valid = 0; + OSMO_ASSERT(val >= 0); - vty_restart_if_started(vty, ms); + if (set->tch_voice.io_handler != TCH_VOICE_IOH_GAPK) { + vty_out(vty, "This parameter is only valid for GAPK%s", VTY_NEWLINE); + return CMD_WARNING; + } + + set->tch_voice.io_format = val; return CMD_SUCCESS; } -static int _test_rplmn_cmd(struct vty *vty, int argc, const char *argv[], - int attached) +DEFUN(cfg_ms_tch_voice_alsa_out_dev, cfg_ms_tch_voice_alsa_out_dev_cmd, + "alsa-output-dev (default|NAME)", + "Set ALSA output (playback) device name (for GAPK only)\n" + "Default system playback device (default)\n" + "Name of a custom playback device") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; - uint16_t mcc = gsm_input_mcc((char *)argv[0]), - mnc = gsm_input_mnc((char *)argv[1]); - - if (mcc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } - if (mnc == GSM_INPUT_INVALID) { - vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); - return CMD_WARNING; - } - set->test_rplmn_valid = 1; - set->test_rplmn_mcc = mcc; - set->test_rplmn_mnc = mnc; - - if (argc >= 3) - set->test_lac = strtoul(argv[2], NULL, 16); - else - set->test_lac = 0xfffe; - - if (argc >= 4) - set->test_tmsi = strtoul(argv[3], NULL, 16); - else - set->test_tmsi = 0xffffffff; - if (attached) - set->test_imsi_attached = 1; - else - set->test_imsi_attached = 0; - - vty_restart_if_started(vty, ms); + OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_output_dev, argv[0]); return CMD_SUCCESS; } -DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, - "rplmn MCC MNC [LAC] [TMSI]", - "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n" - "Optionally set location area code\n" - "Optionally set current assigned TMSI") -{ - return _test_rplmn_cmd(vty, argc, argv, 0); -} - -DEFUN(cfg_test_rplmn_att, cfg_test_rplmn_att_cmd, - "rplmn MCC MNC LAC TMSI attached", - "Set Registered PLMN\nMobile Country Code\nMobile Network Code\n" - "Set location area code\nSet current assigned TMSI\n" - "Indicate to MM that card is already attached") -{ - return _test_rplmn_cmd(vty, argc, argv, 1); -} - -DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-country)", - "Set Home PLMN search mode\n" - "Search for HPLMN when on any other network\n" - "Search for HPLMN when in a different country") +DEFUN(cfg_ms_tch_voice_alsa_in_dev, cfg_ms_tch_voice_alsa_in_dev_cmd, + "alsa-input-dev (default|NAME)", + "Set ALSA input (capture) device name (for GAPK only)\n" + "Default system recording device (default)\n" + "Name of a custom recording device") { struct osmocom_ms *ms = vty->index; struct gsm_settings *set = &ms->settings; - switch (argv[0][0]) { - case 'e': - set->test_always = 1; - break; - case 'f': - set->test_always = 0; - break; - } - - vty_restart_if_started(vty, ms); + OSMO_STRLCPY_ARRAY(set->tch_voice.alsa_input_dev, argv[0]); return CMD_SUCCESS; } -/* per audio config */ -DEFUN(cfg_ms_audio, cfg_ms_audio_cmd, "audio", - "Configure audio settings") +DEFUN(cfg_ms_tch_data, + cfg_ms_tch_data_cmd, + "tch-data", "Configure TCH (Traffic CHannel) params for data calls\n") { - vty->node = AUDIO_NODE; + vty->node = TCH_DATA_NODE; return CMD_SUCCESS; } -static int set_audio_io_handler(struct vty *vty, enum audio_io_handler val) +DEFUN(cfg_ms_tch_data_io_handler, + cfg_ms_tch_data_io_handler_cmd, + "io-handler (none|unix-sock|loopback)", + "Set TCH frame I/O handler for data calls\n" + "No handler, drop TCH frames (default)\n" + "UNIX socket (path set by 'data-unix-socket')\n" + "Return TCH frame payload back to sender\n") { - struct osmocom_ms *ms = (struct osmocom_ms *) vty->index; + int val = get_string_value(tch_data_io_handler_names, argv[0]); + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; struct gsm_settings *set = &ms->settings; - /* Don't restart on unchanged value */ - if (val == set->audio.io_handler) - return CMD_SUCCESS; - set->audio.io_handler = val; - - /* Restart required */ - vty_restart_if_started(vty, ms); + OSMO_ASSERT(val >= 0); + set->tch_data.io_handler = (enum tch_data_io_handler)val; return CMD_SUCCESS; } -DEFUN(cfg_ms_audio_io_handler, cfg_ms_audio_io_handler_cmd, - "io-handler (loopback|none)", - "Set TCH frame I/O handler\n" - "Return TCH frame payload back to sender\n" - "No handler, drop TCH frames (default)") +DEFUN(cfg_ms_tch_data_no_io_handler, + cfg_ms_tch_data_no_io_handler_cmd, + "no io-handler", NO_STR "Disable TCH frame handling for data calls\n") { - int val = get_string_value(audio_io_handler_names, argv[0]); - return set_audio_io_handler(vty, val); -} + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct gsm_settings *set = &ms->settings; -DEFUN(cfg_ms_audio_no_io_handler, cfg_ms_audio_no_io_handler_cmd, - "no io-handler", NO_STR "Disable TCH frame processing") -{ - return set_audio_io_handler(vty, AUDIO_IOH_NONE); + set->tch_data.io_handler = TCH_DATA_IOH_NONE; + + return CMD_SUCCESS; } -DEFUN(cfg_no_shutdown, cfg_ms_no_shutdown_cmd, "no shutdown", - NO_STR "Activate and run MS") +DEFUN(cfg_ms_tch_data_io_tch_format, + cfg_ms_tch_data_io_tch_format_cmd, + "io-tch-format (osmo|ti)", + "Set TCH I/O frame format used by the L1 PHY\n" + "Osmocom format used by both trxcon and viryphy (default)\n" + "Texas Instruments format, used by Calypso based phones (e.g. Motorola C1xx)\n") { - struct osmocom_ms *ms = vty->index; - char *other_name = NULL; - int rc; + int val = get_string_value(tch_data_io_format_names, argv[0]); + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct gsm_settings *set = &ms->settings; - rc = mobile_start(ms, &other_name); - switch (rc) { - case -1: - vty_out(vty, "Cannot start MS '%s', because MS '%s' " - "use the same layer2-socket.%sPlease shutdown " - "MS '%s' first.%s", ms->name, other_name, - VTY_NEWLINE, other_name, VTY_NEWLINE); - return CMD_WARNING; - case -2: - vty_out(vty, "Cannot start MS '%s', because MS '%s' " - "use the same sap-socket.%sPlease shutdown " - "MS '%s' first.%s", ms->name, other_name, - VTY_NEWLINE, other_name, VTY_NEWLINE); - return CMD_WARNING; - case -3: - vty_out(vty, "Connection to layer 1 failed!%s", - VTY_NEWLINE); - return CMD_WARNING; - } + OSMO_ASSERT(val >= 0); + set->tch_data.io_format = val; return CMD_SUCCESS; } -DEFUN(cfg_shutdown, cfg_ms_shutdown_cmd, "shutdown", - "Shut down and deactivate MS") +DEFUN(cfg_ms_tch_data_unix_sock, + cfg_ms_tch_data_unix_sock_cmd, + "unix-socket PATH", + "Define UNIX socket path (for 'io-handler unix-sock')\n" + "UNIX socket path (default '/tmp/ms_data_' + MS_NAME)\n") { - struct osmocom_ms *ms = vty->index; - mobile_stop(ms, 0); - return CMD_SUCCESS; -} + struct osmocom_ms *ms = (struct osmocom_ms *)vty->index; + struct gsm_settings *set = &ms->settings; -DEFUN(cfg_shutdown_force, cfg_ms_shutdown_force_cmd, "shutdown force", - "Shut down and deactivate MS\nDo not perform IMSI detach") -{ - struct osmocom_ms *ms = vty->index; + OSMO_STRLCPY_ARRAY(set->tch_data.unix_socket_path, argv[0]); - mobile_stop(ms, 1); return CMD_SUCCESS; } @@ -2848,25 +2881,6 @@ DEFUN(cfg_ms_no_script_load_run, cfg_ms_no_script_load_run_cmd, "no lua-script", return CMD_SUCCESS; } -int ms_vty_go_parent(struct vty *vty) -{ - switch (vty->node) { - case MS_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case TESTSIM_NODE: - case SUPPORT_NODE: - case AUDIO_NODE: - vty->node = MS_NODE; - break; - default: - vty->node = CONFIG_NODE; - } - - return vty->node; -} - DEFUN(off, off_cmd, "off", "Turn mobiles off (shutdown) and exit") { @@ -2875,43 +2889,138 @@ DEFUN(off, off_cmd, "off", return CMD_SUCCESS; } +/* run ms instance, if layer1 is available */ +static int l23_vty_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmobb_l23_vty_sig_data *d = signal_data; + struct vty *vty = d->vty; + char *other_name = NULL; + int rc; + + if (subsys != SS_L23_VTY) + return 0; + + switch (signal) { + case S_L23_VTY_MS_START: + rc = mobile_start(d->ms_start.ms, &other_name); + switch (rc) { + case -1: + vty_out(vty, "Cannot start MS '%s', because MS '%s' " + "use the same layer2-socket.%sPlease shutdown " + "MS '%s' first.%s", d->ms_start.ms->name, other_name, + VTY_NEWLINE, other_name, VTY_NEWLINE); + break; + case -2: + vty_out(vty, "Cannot start MS '%s', because MS '%s' " + "use the same sap-socket.%sPlease shutdown " + "MS '%s' first.%s", d->ms_start.ms->name, other_name, + VTY_NEWLINE, other_name, VTY_NEWLINE); + break; + case -3: + vty_out(vty, "Connection to layer 1 failed!%s", + VTY_NEWLINE); + break; + } + d->ms_start.rc = (rc == 0) ? CMD_SUCCESS : CMD_WARNING; + break; + case S_L23_VTY_MS_STOP: + mobile_stop(d->ms_stop.ms, d->ms_stop.force); + d->ms_start.rc = CMD_SUCCESS; + break; + } + return 0; +} + + #define SUP_NODE(item) \ install_element(SUPPORT_NODE, &cfg_ms_sup_item_cmd); int ms_vty_init(void) { + int rc; + + _data_type_rate_cmd_string(NULL, &cfg_ms_tch_data_cp_type_rate_cmd); + _data_type_rate_cmd_string(NULL, &call_params_data_type_rate_cmd); + + cfg_ms_tch_data_cp_type_rate_cmd.doc = + vty_cmd_string_from_valstr(NULL, + data_type_rate_descs, + CFG_TCH_DATA_CALL_PARAMS_CMD_DESC + "Type and rate (values like in AT+CBST; " + "see 3GPP TS 27.007, section 6.7)\n", + "\n", "", 0); + call_params_data_type_rate_cmd.doc = + vty_cmd_string_from_valstr(NULL, + data_type_rate_descs, + CALL_PARAMS_DATA_CMD_DESC + "Type and rate (values like in AT+CBST; " + "see 3GPP TS 27.007, section 6.7)\n", + "\n", "", 0); + + cfg_ms_tch_data_cp_async_parity_cmd.string = + vty_cmd_string_from_valstr(NULL, + async_parity_names, + CFG_TCH_DATA_CALL_PARAMS_CMD " " + CALL_PARAMS_ASYNC_PARITY_CMD " (", + "|", ")", 0); + call_params_data_async_parity_cmd.string = + vty_cmd_string_from_valstr(NULL, + async_parity_names, + CALL_PARAMS_DATA_CMD " " + CALL_PARAMS_ASYNC_PARITY_CMD " (", + "|", ")", 0); + + cfg_ms_tch_data_cp_async_parity_cmd.doc = + vty_cmd_string_from_valstr(NULL, + async_parity_descs, + cfg_ms_tch_data_cp_async_parity_cmd.doc, + "\n", "", 0); + call_params_data_async_parity_cmd.doc = + vty_cmd_string_from_valstr(NULL, + async_parity_descs, + call_params_data_async_parity_cmd.doc, + "\n", "", 0); + + if ((rc = l23_vty_init(config_write, l23_vty_signal_cb)) < 0) + return rc; + install_element_ve(&show_ms_cmd); - install_element_ve(&show_subscr_cmd); - install_element_ve(&show_support_cmd); install_element_ve(&show_cell_cmd); install_element_ve(&show_cell_si_cmd); install_element_ve(&show_nbcells_cmd); install_element_ve(&show_ba_cmd); install_element_ve(&show_forb_la_cmd); install_element_ve(&show_forb_plmn_cmd); + install_element_ve(&show_asci_calls_cmd); + install_element_ve(&show_asci_neighbors_cmd); install_element_ve(&monitor_network_cmd); install_element_ve(&no_monitor_network_cmd); install_element(ENABLE_NODE, &off_cmd); - install_element(ENABLE_NODE, &sim_test_cmd); - install_element(ENABLE_NODE, &sim_test_att_cmd); - install_element(ENABLE_NODE, &sim_sap_cmd); - install_element(ENABLE_NODE, &sim_reader_cmd); - install_element(ENABLE_NODE, &sim_remove_cmd); - install_element(ENABLE_NODE, &sim_pin_cmd); - install_element(ENABLE_NODE, &sim_disable_pin_cmd); - install_element(ENABLE_NODE, &sim_enable_pin_cmd); - install_element(ENABLE_NODE, &sim_change_pin_cmd); - install_element(ENABLE_NODE, &sim_unblock_pin_cmd); - install_element(ENABLE_NODE, &sim_lai_cmd); install_element(ENABLE_NODE, &network_search_cmd); install_element(ENABLE_NODE, &network_show_cmd); install_element(ENABLE_NODE, &network_select_cmd); + install_element(ENABLE_NODE, &call_num_cmd); install_element(ENABLE_NODE, &call_cmd); install_element(ENABLE_NODE, &call_retr_cmd); install_element(ENABLE_NODE, &call_dtmf_cmd); + install_element(ENABLE_NODE, &call_params_data_type_rate_cmd); + install_element(ENABLE_NODE, &call_params_data_ce_cmd); + install_element(ENABLE_NODE, &call_params_data_sync_async_cmd); + install_element(ENABLE_NODE, &call_params_data_async_nr_stop_bits_cmd); + install_element(ENABLE_NODE, &call_params_data_async_nr_data_bits_cmd); + install_element(ENABLE_NODE, &call_params_data_async_parity_cmd); install_element(ENABLE_NODE, &sms_cmd); install_element(ENABLE_NODE, &service_cmd); + install_element(ENABLE_NODE, &vgcs_enter_cmd); + install_element(ENABLE_NODE, &vgcs_direct_cmd); + install_node(&vgcs_node, config_write_dummy); + install_element(VGCS_NODE, &vgcs_cmd); + install_element(ENABLE_NODE, &vbs_enter_cmd); + install_element(ENABLE_NODE, &vbs_direct_cmd); + install_node(&vbs_node, config_write_dummy); + install_element(VBS_NODE, &vbs_cmd); install_element(ENABLE_NODE, &test_reselection_cmd); install_element(ENABLE_NODE, &delete_forbidden_plmn_cmd); @@ -2923,22 +3032,18 @@ int ms_vty_init(void) install_element(CONFIG_NODE, &cfg_gps_enable_cmd); install_element(CONFIG_NODE, &cfg_no_gps_enable_cmd); - install_element(CONFIG_NODE, &cfg_hide_default_cmd); - install_element(CONFIG_NODE, &cfg_no_hide_default_cmd); - install_element(CONFIG_NODE, &cfg_ms_cmd); install_element(CONFIG_NODE, &cfg_ms_create_cmd); install_element(CONFIG_NODE, &cfg_ms_rename_cmd); install_element(CONFIG_NODE, &cfg_no_ms_cmd); - install_node(&ms_node, config_write); + + /* MS_NODE is installed by l23_vty_init(). App specific commands below: */ install_element(MS_NODE, &cfg_ms_show_this_cmd); - install_element(MS_NODE, &cfg_ms_layer2_cmd); install_element(MS_NODE, &cfg_ms_sap_cmd); - install_element(MS_NODE, &cfg_ms_sim_cmd); + install_element(MS_NODE, &cfg_ms_mncc_sock_cmd); + install_element(MS_NODE, &cfg_ms_mncc_handler_cmd); + install_element(MS_NODE, &cfg_ms_no_mncc_handler_cmd); install_element(MS_NODE, &cfg_ms_mode_cmd); - install_element(MS_NODE, &cfg_ms_imei_cmd); - install_element(MS_NODE, &cfg_ms_imei_fixed_cmd); - install_element(MS_NODE, &cfg_ms_imei_random_cmd); install_element(MS_NODE, &cfg_ms_no_emerg_imsi_cmd); install_element(MS_NODE, &cfg_ms_emerg_imsi_cmd); install_element(MS_NODE, &cfg_ms_no_sms_sca_cmd); @@ -2968,127 +3073,112 @@ int ms_vty_init(void) install_element(MS_NODE, &cfg_ms_no_codec_half_cmd); install_element(MS_NODE, &cfg_ms_abbrev_cmd); install_element(MS_NODE, &cfg_ms_no_abbrev_cmd); - install_element(MS_NODE, &cfg_ms_testsim_cmd); + install_element(MS_NODE, &cfg_ms_tch_voice_cmd); install_element(MS_NODE, &cfg_ms_audio_cmd); + install_element(MS_NODE, &cfg_ms_tch_data_cmd); install_element(MS_NODE, &cfg_ms_neighbour_cmd); install_element(MS_NODE, &cfg_ms_no_neighbour_cmd); install_element(MS_NODE, &cfg_ms_any_timeout_cmd); install_element(MS_NODE, &cfg_ms_sms_store_cmd); install_element(MS_NODE, &cfg_ms_no_sms_store_cmd); + install_element(MS_NODE, &cfg_ms_uplink_release_local_cmd); + install_element(MS_NODE, &cfg_ms_no_uplink_release_local_cmd); + install_element(MS_NODE, &cfg_ms_asci_allow_any_cmd); + install_element(MS_NODE, &cfg_ms_no_asci_allow_any_cmd); install_element(MS_NODE, &cfg_ms_support_cmd); install_node(&support_node, config_write_dummy); - install_element(SUPPORT_NODE, &cfg_ms_sup_dtmf_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_dtmf_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_sms_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_sms_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_1_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_1_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_2_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_2_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_3_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_3_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_4_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_4_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_5_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_5_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_6_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_6_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_a5_7_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_a5_7_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_p_gsm_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_p_gsm_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_e_gsm_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_e_gsm_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_r_gsm_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_r_gsm_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_dcs_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_dcs_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_850_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_850_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_pcs_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_pcs_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_480_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_480_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_gsm_450_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_gsm_450_cmd); + install_element(SUPPORT_NODE, &cfg_ms_set_en_cc_dtmf_cmd); + install_element(SUPPORT_NODE, &cfg_ms_set_di_cc_dtmf_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_sms_ptp_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_sms_ptp_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_4_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_4_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_5_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_5_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_6_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_6_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_a5_7_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_a5_7_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_p_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_p_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_e_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_e_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_r_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_r_gsm_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_dcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_dcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_gsm_850_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_gsm_850_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_pcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_pcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_gsm_480_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_gsm_480_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_gsm_450_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_gsm_450_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_class_900_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_class_dcs_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_class_850_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_class_pcs_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_class_400_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_ch_cap_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_full_v1_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v1_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_full_v2_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v2_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_full_v3_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_full_v3_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_half_v1_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v1_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_half_v3_cmd); - install_element(SUPPORT_NODE, &cfg_ms_sup_no_half_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_full_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_full_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_full_v2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_full_v2_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_full_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_full_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_half_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_half_v1_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_half_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_half_v3_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f144_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f144_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f96_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f96_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f48_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f48_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_h48_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_h48_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_f24_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_f24_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_csd_tch_h24_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_csd_tch_h24_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_min_rxlev_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_dsc_max_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_skip_max_per_band_cmd); install_element(SUPPORT_NODE, &cfg_ms_sup_no_skip_max_per_band_cmd); - install_node(&testsim_node, config_write_dummy); - install_element(TESTSIM_NODE, &cfg_test_imsi_cmd); - install_element(TESTSIM_NODE, &cfg_test_ki_xor_cmd); - install_element(TESTSIM_NODE, &cfg_test_ki_comp128_cmd); - install_element(TESTSIM_NODE, &cfg_test_barr_cmd); - install_element(TESTSIM_NODE, &cfg_test_no_barr_cmd); - install_element(TESTSIM_NODE, &cfg_test_no_rplmn_cmd); - install_element(TESTSIM_NODE, &cfg_test_rplmn_cmd); - install_element(TESTSIM_NODE, &cfg_test_rplmn_att_cmd); - install_element(TESTSIM_NODE, &cfg_test_hplmn_cmd); - install_element(MS_NODE, &cfg_ms_shutdown_cmd); - install_element(MS_NODE, &cfg_ms_shutdown_force_cmd); - install_element(MS_NODE, &cfg_ms_no_shutdown_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_vgcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_vgcs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_en_vbs_cmd); + install_element(SUPPORT_NODE, &cfg_ms_sup_di_vbs_cmd); install_element(MS_NODE, &cfg_ms_script_load_run_cmd); install_element(MS_NODE, &cfg_ms_no_script_load_run_cmd); - install_node(&audio_node, config_write_dummy); - install_element(AUDIO_NODE, &cfg_ms_audio_io_handler_cmd); - install_element(AUDIO_NODE, &cfg_ms_audio_no_io_handler_cmd); - - /* Register the talloc context introspection command */ - osmo_talloc_vty_add_cmds(); + install_node(&tch_voice_node, config_write_dummy); + install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_io_handler_cmd); + install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_no_io_handler_cmd); + install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_io_tch_format_cmd); + install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_alsa_out_dev_cmd); + install_element(TCH_VOICE_NODE, &cfg_ms_tch_voice_alsa_in_dev_cmd); + + install_node(&tch_data_node, config_write_dummy); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_io_handler_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_no_io_handler_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_io_tch_format_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_unix_sock_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_type_rate_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_ce_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_sync_async_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_async_nr_stop_bits_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_async_nr_data_bits_cmd); + install_element(TCH_DATA_NODE, &cfg_ms_tch_data_cp_async_parity_cmd); return 0; } -void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) -{ - struct telnet_connection *connection; - char buffer[1000]; - va_list args; - struct vty *vty; - - if (fmt) { - va_start(args, fmt); - vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); - buffer[sizeof(buffer) - 1] = '\0'; - va_end(args); - - if (!buffer[0]) - return; - } - - llist_for_each_entry(connection, &active_connections, entry) { - vty = connection->vty; - if (!vty) - continue; - if (!fmt) { - vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name, - VTY_NEWLINE); - continue; - } - if (buffer[strlen(buffer) - 1] == '\n') { - buffer[strlen(buffer) - 1] = '\0'; - vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE); - buffer[strlen(buffer)] = '\n'; - } else - vty_out(vty, "%% %s", buffer); - } -} - diff --git a/src/host/layer23/src/modem/Makefile.am b/src/host/layer23/src/modem/Makefile.am new file mode 100644 index 00000000..935722b1 --- /dev/null +++ b/src/host/layer23/src/modem/Makefile.am @@ -0,0 +1,41 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOGPRSRLCMAC_CFLAGS) \ + $(LIBOSMOGPRSLLC_CFLAGS) \ + $(LIBOSMOGPRSSNDCP_CFLAGS) \ + $(LIBOSMOGPRSGMM_CFLAGS) \ + $(LIBOSMOGPRSSM_CFLAGS) \ + $(NULL) + +bin_PROGRAMS = modem + +modem_SOURCES = \ + $(top_srcdir)/src/common/main.c \ + app_modem.c \ + gmm.c \ + grr.c \ + llc.c \ + rlcmac.c \ + sm.c \ + sndcp.c \ + vty.c \ + $(NULL) +modem_LDADD = \ + $(top_builddir)/src/common/liblayer23.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOGPRSRLCMAC_LIBS) \ + $(LIBOSMOGPRSLLC_LIBS) \ + $(LIBOSMOGPRSSNDCP_LIBS) \ + $(LIBOSMOGPRSGMM_LIBS) \ + $(LIBOSMOGPRSSM_LIBS) \ + $(NULL) diff --git a/src/host/layer23/src/modem/app_modem.c b/src/host/layer23/src/modem/app_modem.c new file mode 100644 index 00000000..2787ec70 --- /dev/null +++ b/src/host/layer23/src/modem/app_modem.c @@ -0,0 +1,348 @@ +/* modem app (gprs) */ + +/* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/signal.h> +#include <osmocom/core/application.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/tun.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/lapdm.h> +#include <osmocom/vty/vty.h> + +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/l23_app.h> +#include <osmocom/bb/common/l1l2_interface.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/modem/rlcmac.h> +#include <osmocom/bb/modem/llc.h> +#include <osmocom/bb/modem/sndcp.h> +#include <osmocom/bb/modem/gmm.h> +#include <osmocom/bb/modem/sm.h> +#include <osmocom/bb/modem/vty.h> +#include <osmocom/bb/modem/grr.h> +#include <osmocom/bb/modem/modem.h> + +#include <l1ctl_proto.h> + +#include "config.h" + +struct modem_app app_data; + +int modem_gprs_attach_if_needed(struct osmocom_ms *ms) +{ + int rc; + + if (app_data.modem_state != MODEM_ST_IDLE) + return 0; + + if (ms->grr_fi->state == GRR_ST_PACKET_NOT_READY) + return 0; + + if (!ms->subscr.sim_valid) + return 0; + + app_data.modem_state = MODEM_ST_ATTACHING; + rc = modem_gmm_gmmreg_attach_req(ms); + if (rc < 0) + app_data.modem_state = MODEM_ST_IDLE; + return rc; +} + +/* Local network-originated IP packet, needs to be sent via SNDCP/LLC (GPRS) towards GSM network */ +static int modem_tun_data_ind_cb(struct osmo_tundev *tun, struct msgb *msg) +{ + struct osmobb_apn *apn = (struct osmobb_apn *)osmo_tundev_get_priv_data(tun); + struct osmo_sockaddr dst; + struct iphdr *iph = (struct iphdr *)msgb_data(msg); + struct ip6_hdr *ip6h = (struct ip6_hdr *)msgb_data(msg); + size_t pkt_len = msgb_length(msg); + uint8_t pref_offset; + char addrstr[INET6_ADDRSTRLEN]; + int rc = 0; + + switch (iph->version) { + case 4: + if (pkt_len < sizeof(*iph) || pkt_len < 4*iph->ihl) + return -1; + dst.u.sin.sin_family = AF_INET; + dst.u.sin.sin_addr.s_addr = iph->daddr; + break; + case 6: + /* Due to the fact that 3GPP requires an allocation of a + * /64 prefix to each MS, we must instruct + * ippool_getip() below to match only the leading /64 + * prefix, i.e. the first 8 bytes of the address. If the ll addr + * is used, then the match should be done on the trailing 64 + * bits. */ + dst.u.sin6.sin6_family = AF_INET6; + pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0; + memcpy(&dst.u.sin6.sin6_addr, ((uint8_t *)&ip6h->ip6_dst) + pref_offset, 8); + break; + default: + LOGTUN(LOGL_NOTICE, tun, "non-IPv%u packet received\n", iph->version); + rc = -1; + goto free_ret; + } + + LOGPAPN(LOGL_DEBUG, apn, "system wants to transmit IPv%c pkt to %s (%zu bytes)\n", + iph->version == 4 ? '4' : '6', osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len); + + switch (apn->pdp.pdp_addr_ietf_type) { + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4: + if (iph->version != 4) { + LOGPAPN(LOGL_NOTICE, apn, + "system wants to transmit IPv%u pkt to %s (%zu bytes) on IPv4-only PDP Ctx, discarding!\n", + iph->version, osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len); + goto free_ret; + } + break; + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6: + if (iph->version != 6) { + LOGPAPN(LOGL_NOTICE, apn, + "system wants to transmit IPv%u pkt to %s (%zu bytes) on IPv6-only PDP Ctx, discarding!\n", + iph->version, osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len); + goto free_ret; + } + break; + default: /* OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6 */ + /* Allow any */ + break; + } + + rc = modem_sndcp_sn_unitdata_req(apn, msgb_data(msg), pkt_len); + +free_ret: + msgb_free(msg); + return rc; +} + +void layer3_app_reset(void) +{ + memset(&app_data, 0x00, sizeof(app_data)); +} + +/* SIM becomes ATTACHED/DETACHED, or answers a request */ +static int modem_l23_subscr_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + struct osmobb_l23_subscr_sim_auth_resp_sig_data *sim_auth_resp; + + OSMO_ASSERT(subsys == SS_L23_SUBSCR); + + switch (signal) { + case S_L23_SUBSCR_SIM_ATTACHED: + ms = signal_data; + modem_gprs_attach_if_needed(ms); + break; + case S_L23_SUBSCR_SIM_DETACHED: + ms = signal_data; + modem_gmm_gmmreg_detach_req(ms); + break; + case S_L23_SUBSCR_SIM_AUTH_RESP: + sim_auth_resp = signal_data; + ms = sim_auth_resp->ms; + modem_gmm_gmmreg_sim_auth_rsp(ms, sim_auth_resp->sres, + ms->subscr.key, + sizeof(ms->subscr.key)); + break; + default: + OSMO_ASSERT(0); + } + + return 0; +} + +int modem_sync_to_cell(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + if (cs->sync_pending) { + LOGP(DCS, LOGL_INFO, "Sync to ARFCN=%s, but there is a sync " + "already pending\n", gsm_print_arfcn(cs->arfcn)); + return 0; + } + + cs->sync_pending = true; + l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + return l1ctl_tx_fbsb_req(ms, cs->arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + cs->ccch_mode, dbm2rxlev(-85)); +} + +static int global_signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + struct gsm322_cellsel *cs; + struct osmobb_fbsb_res *fr; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + LOGP(DCS, LOGL_NOTICE, "S_L1CTL_RESET\n"); + ms = signal_data; + ms->cellsel.arfcn = ms->test_arfcn; + if (ms->started) + break; + layer3_app_reset(); + app_data.ms = ms; + + /* insert test card, if enabled */ + if (ms->settings.sim_type != GSM_SIM_TYPE_NONE) { + /* insert sim card */ + gsm_subscr_insert(ms); + } else { + /* No SIM, trigger PLMN selection process. + * FIXME: not implemented. Code in mobile needs to be + * moved to common/ and reuse it here. + */ + } + + ms->started = true; + return l1ctl_tx_fbsb_req(ms, ms->test_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + CCCH_MODE_NONE, dbm2rxlev(-85)); + case S_L1CTL_FBSB_RESP: + LOGP(DCS, LOGL_NOTICE, "S_L1CTL_FBSB_RESP\n"); + fr = signal_data; + ms = fr->ms; + cs = &ms->cellsel; + cs->sync_pending = false; + break; + case S_L1CTL_FBSB_ERR: + LOGP(DCS, LOGL_NOTICE, "S_L1CTL_FBSB_ERR\n"); + fr = signal_data; + ms = fr->ms; + cs = &ms->cellsel; + cs->sync_pending = false; + /* Retry: */ + modem_sync_to_cell(ms); + break; + } + + return 0; +} + +static int _modem_start(void) +{ + int rc; + + rc = layer2_open(app_data.ms, app_data.ms->settings.layer2_socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + return rc; + } + + l1ctl_tx_reset_req(app_data.ms, L1CTL_RES_T_FULL); + return 0; +} + +/* global exit */ +static int _modem_exit(void) +{ + osmo_signal_unregister_handler(SS_L23_SUBSCR, &modem_l23_subscr_signal_cb, NULL); + osmo_signal_unregister_handler(SS_GLOBAL, &global_signal_cb, NULL); + return 0; +} + +int l23_app_init(void) +{ + int rc; + + l23_app_start = _modem_start; + l23_app_exit = _modem_exit; + + log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG); + log_set_category_filter(osmo_stderr_target, DLCSN1, 1, LOGL_DEBUG); + log_set_category_filter(osmo_stderr_target, DRR, 1, LOGL_INFO); + + app_data.ms = osmocom_ms_alloc(l23_ctx, "1"); + OSMO_ASSERT(app_data.ms); + + if ((rc = modem_rlcmac_init(app_data.ms))) { + LOGP(DRLCMAC, LOGL_FATAL, "Failed initializing RLC/MAC layer\n"); + return rc; + } + + if ((rc = modem_llc_init(app_data.ms, NULL))) { + LOGP(DLLC, LOGL_FATAL, "Failed initializing LLC layer\n"); + return rc; + } + + if ((rc = modem_sndcp_init(app_data.ms))) { + LOGP(DSNDCP, LOGL_FATAL, "Failed initializing SNDCP layer\n"); + return rc; + } + + if ((rc = modem_gmm_init(app_data.ms))) { + LOGP(DGMM, LOGL_FATAL, "Failed initializing GMM layer\n"); + return rc; + } + + if ((rc = modem_sm_init(app_data.ms))) { + LOGP(DSM, LOGL_FATAL, "Failed initializing SM layer\n"); + return rc; + } + + /* TODO: move to a separate function */ + app_data.ms->grr_fi = osmo_fsm_inst_alloc(&grr_fsm_def, NULL, + app_data.ms, LOGL_DEBUG, + app_data.ms->name); + OSMO_ASSERT(app_data.ms->grr_fi != NULL); + + osmo_signal_register_handler(SS_L1CTL, &global_signal_cb, NULL); + osmo_signal_register_handler(SS_L23_SUBSCR, &modem_l23_subscr_signal_cb, NULL); + lapdm_channel_set_l3(&app_data.ms->lapdm_channel, &modem_grr_rslms_cb, app_data.ms); + return 0; +} + +static struct vty_app_info _modem_vty_info = { + .name = "OsmocomBB(modem)", + .version = PACKAGE_VERSION, + .go_parent_cb = modem_vty_go_parent, +}; + +const struct l23_app_info l23_app_info = { + .copyright = "Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\n", + .opt_supported = L23_OPT_ARFCN | L23_OPT_TAP | L23_OPT_VTY | L23_OPT_DBG, + .vty_info = &_modem_vty_info, + .vty_init = modem_vty_init, + .tun_data_ind_cb = modem_tun_data_ind_cb, +}; diff --git a/src/host/layer23/src/modem/gmm.c b/src/host/layer23/src/modem/gmm.c new file mode 100644 index 00000000..c0e69368 --- /dev/null +++ b/src/host/layer23/src/modem/gmm.c @@ -0,0 +1,271 @@ +/* GPRS GMM interfaces as per 3GPP TS 24.008, TS 24.007 */ +/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/tun.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/gprs/llc/llc.h> +#include <osmocom/gprs/llc/llc_prim.h> +#include <osmocom/gprs/gmm/gmm_prim.h> +#include <osmocom/gprs/gmm/gmm.h> +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> +#include <osmocom/gprs/sm/sm_prim.h> + +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/vty.h> +#include <osmocom/bb/modem/gmm.h> +#include <osmocom/bb/modem/sm.h> +#include <osmocom/bb/modem/modem.h> + +static int modem_gmm_prim_up_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_gmm_prim_name(gmm_prim); + struct osmocom_ms *ms = user_data; + struct osmobb_apn *apn; + int rc = 0; + + switch (gmm_prim->oph.sap) { + case OSMO_GPRS_GMM_SAP_GMMREG: + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_ATTACH, PRIM_OP_CONFIRM): + if (gmm_prim->gmmreg.attach_cnf.accepted) { + LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s: Attach success P-TMSI=0x%08x TLLI=0x%08x\n", + __func__, pdu_name, gmm_prim->gmmreg.attach_cnf.acc.allocated_ptmsi, + gmm_prim->gmmreg.attach_cnf.acc.allocated_tlli); + ms->subscr.gprs.ptmsi = gmm_prim->gmmreg.attach_cnf.acc.allocated_ptmsi; + ms->gmmlayer.tlli = gmm_prim->gmmreg.attach_cnf.acc.allocated_tlli; + app_data.modem_state = MODEM_ST_ATTACHED; + /* Activate APN if not yet already: */ + llist_for_each_entry(apn, &ms->gprs.apn_list, list) { + if (apn->fsm.fi->state != APN_ST_INACTIVE) + continue; + osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GMM_ATTACHED, NULL); + modem_sm_smreg_pdp_act_req(ms, apn); + } + } else { + uint8_t cause = gmm_prim->gmmreg.attach_cnf.rej.cause; + LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s: Attach rejected, cause=%u (%s)\n", + __func__, pdu_name, cause, get_value_string(gsm48_gmm_cause_names, cause)); + app_data.modem_state = MODEM_ST_IDLE; + modem_gprs_attach_if_needed(ms); + } + break; + case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_SIM_AUTH, PRIM_OP_INDICATION): + LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s ac_ref_nr=%u key_seq=%u rand=%s\n", + __func__, pdu_name, + gmm_prim->gmmreg.sim_auth_ind.ac_ref_nr, + gmm_prim->gmmreg.sim_auth_ind.key_seq, + osmo_hexdump(gmm_prim->gmmreg.sim_auth_ind.rand, + sizeof(gmm_prim->gmmreg.sim_auth_ind.rand))); + /* Cache request information, it'll be needed during response time: */ + ms->gmmlayer.ac_ref_nr = gmm_prim->gmmreg.sim_auth_ind.ac_ref_nr; + ms->gmmlayer.key_seq = gmm_prim->gmmreg.sim_auth_ind.key_seq; + memcpy(ms->gmmlayer.rand, gmm_prim->gmmreg.sim_auth_ind.rand, + sizeof(ms->gmmlayer.rand)); + /* Request SIM to authenticate. Wait for signal S_L23_SUBSCR_SIM_AUTH_RESP. */ + rc = gsm_subscr_generate_kc(ms, gmm_prim->gmmreg.sim_auth_ind.key_seq, + gmm_prim->gmmreg.sim_auth_ind.rand, false); + break; + case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_GMM_GMMREG_DETACH, PRIM_OP_INDICATION): + LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s\n", __func__, pdu_name); + ms_dispatch_all_apn(ms, APN_EV_GMM_DETACHED, NULL); + break; + default: + LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name); + break; + }; + break; + case OSMO_GPRS_GMM_SAP_GMMSM: + switch (OSMO_PRIM_HDR(&gmm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_ESTABLISH, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_RELEASE, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_UNITDATA, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_GMM_GMMSM_MODIFY, PRIM_OP_INDICATION): + osmo_gprs_sm_prim_gmm_lower_up(gmm_prim); + rc = 1; /* Tell RLCMAC that we take ownership of the prim. */ + break; + default: + LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name); + break; + }; + break; + default: + LOGP(DGMM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + return rc; +} + +static int modem_gmm_prim_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_gmm_prim_name(gmm_prim); + int rc = 0; + uint32_t old_tlli, new_tlli; + struct osmocom_ms *ms, *ms_found = NULL; + + osmo_static_assert(sizeof(struct osmo_gprs_gmm_gmmrr_prim) == sizeof(struct osmo_gprs_rlcmac_gmmrr_prim), + _gmmrr_prim_size); + + switch (gmm_prim->oph.sap) { + case OSMO_GPRS_GMM_SAP_GMMRR: + OSMO_ASSERT(gmm_prim->oph.primitive == OSMO_GPRS_GMM_GMMRR_ASSIGN); + /* Update app TLLI reference. This usually happens as a result of a RAU ACCEPT */ + old_tlli = gmm_prim->gmmrr.tlli; + new_tlli = gmm_prim->gmmrr.assign_req.new_tlli; + llist_for_each_entry(ms, &ms_list, entity) { + if (old_tlli != ms->gmmlayer.tlli) + continue; + ms_found = ms; + break; + } + if (ms_found) { + if (new_tlli != OSMO_GPRS_GMM_TLLI_UNASSIGNED) { + LOGP(DGMM, LOGL_INFO, "%s(): Rx %s Update TLLI 0x%08x -> 0x%08x\n", + __func__, pdu_name, old_tlli, new_tlli); + ms_found->gmmlayer.tlli = new_tlli; + } else { + LOGP(DGMM, LOGL_ERROR, "%s(): Rx %s with TLLI=0x%08x is being released, GMM should be restarted?\n", + __func__, pdu_name, old_tlli); + } + } else { + if (old_tlli != OSMO_GPRS_GMM_TLLI_UNASSIGNED) + LOGP(DGMM, LOGL_NOTICE, "%s(): Rx %s with unknown TLLI=0x%08x, probably the MS is still attaching\n", + __func__, pdu_name, old_tlli); + } + + /* Forward it to lower layers, pass ownership over to RLCMAC: */ + /* Optimization: GMM-GMMRR-ASSIGN-REQ is 1-to-1 ABI compatible with + RLCMAC-GMMRR-ASSIGN-REQ, we just need to adapt the header. + See osmo_static_assert(_gmmrr_prim_size) above. + */ + gmm_prim->oph.sap = OSMO_GPRS_RLCMAC_SAP_GMMRR; + gmm_prim->oph.primitive = OSMO_GPRS_RLCMAC_GMMRR_ASSIGN; + osmo_gprs_rlcmac_prim_upper_down((struct osmo_gprs_rlcmac_prim *)gmm_prim); + rc = 1; /* Tell GMM that we take ownership of the prim. */ + break; + case OSMO_GPRS_GMM_SAP_GMMREG: + default: + LOGP(DGMM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + return rc; +} + +static int modem_gmm_prim_llc_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data) +{ + int rc; + + rc = osmo_gprs_llc_prim_upper_down(llc_prim); + + /* LLC took ownership of the message, tell GMM layer to not free it: */ + rc = 1; + return rc; +} + +int modem_gmm_init(struct osmocom_ms *ms) +{ + int rc; + rc = osmo_gprs_gmm_init(OSMO_GPRS_GMM_LOCATION_MS); + if (rc != 0) + return rc; + + osmo_gprs_gmm_set_log_cat(OSMO_GPRS_GMM_LOGC_GMM, DGMM); + + osmo_gprs_gmm_prim_set_up_cb(modem_gmm_prim_up_cb, ms); + osmo_gprs_gmm_prim_set_down_cb(modem_gmm_prim_down_cb, ms); + osmo_gprs_gmm_prim_set_llc_down_cb(modem_gmm_prim_llc_down_cb, ms); + + osmo_gprs_gmm_enable_gprs(true); + return rc; +} + +int modem_gmm_gmmreg_attach_req(const struct osmocom_ms *ms) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + const struct gsm_subscriber *subscr = &ms->subscr; + int rc; + + gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_attach_req(); + gmm_prim->gmmreg.attach_req.attach_type = OSMO_GPRS_GMM_ATTACH_TYPE_GPRS; + gmm_prim->gmmreg.attach_req.ptmsi = subscr->gprs.ptmsi; + gmm_prim->gmmreg.attach_req.ptmsi_sig = subscr->gprs.ptmsi_sig; + gmm_prim->gmmreg.attach_req.attach_with_imsi = (subscr->gprs.ptmsi == GSM_RESERVED_TMSI); + memcpy(gmm_prim->gmmreg.attach_req.imsi, subscr->imsi, ARRAY_SIZE(subscr->imsi)); + memcpy(gmm_prim->gmmreg.attach_req.imei, ms->settings.imei, ARRAY_SIZE(ms->settings.imei)); + memcpy(gmm_prim->gmmreg.attach_req.imeisv, ms->settings.imeisv, ARRAY_SIZE(ms->settings.imeisv)); + memcpy(&gmm_prim->gmmreg.attach_req.old_rai, &subscr->gprs.rai, sizeof(subscr->gprs.rai)); + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + if (rc < 0) + LOGP(DMM, LOGL_ERROR, "Failed submitting GMMREG-ATTACH.req\n"); + return rc; +} + +int modem_gmm_gmmreg_detach_req(const struct osmocom_ms *ms) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + const struct gsm_subscriber *subscr = &ms->subscr; + int rc; + + gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_detach_req(); + gmm_prim->gmmreg.detach_req.ptmsi = subscr->gprs.ptmsi; + gmm_prim->gmmreg.detach_req.detach_type = OSMO_GPRS_GMM_DETACH_MS_TYPE_GPRS; + gmm_prim->gmmreg.detach_req.poweroff_type = OSMO_GPRS_GMM_DETACH_POWEROFF_TYPE_NORMAL; + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + if (rc < 0) + LOGP(DMM, LOGL_ERROR, "Failed submitting GMMREG-DETACH.req\n"); + return rc; +} + +int modem_gmm_gmmreg_sim_auth_rsp(const struct osmocom_ms *ms, uint8_t *sres, uint8_t *kc, uint8_t kc_len) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + int rc; + + gmm_prim = osmo_gprs_gmm_prim_alloc_gmmreg_sim_auth_rsp(); + gmm_prim->gmmreg.sim_auth_rsp.ac_ref_nr = ms->gmmlayer.ac_ref_nr; + gmm_prim->gmmreg.sim_auth_rsp.key_seq = ms->gmmlayer.key_seq; + memcpy(gmm_prim->gmmreg.sim_auth_rsp.rand, ms->gmmlayer.rand, + sizeof(gmm_prim->gmmreg.sim_auth_rsp.rand)); + memcpy(gmm_prim->gmmreg.sim_auth_rsp.sres, sres, + sizeof(gmm_prim->gmmreg.sim_auth_rsp.sres)); + memcpy(gmm_prim->gmmreg.sim_auth_rsp.kc, kc, + kc_len); + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + if (rc < 0) + LOGP(DMM, LOGL_ERROR, "Failed submitting GMMREG-SIM_AUTH.rsp\n"); + return rc; +} diff --git a/src/host/layer23/src/modem/grr.c b/src/host/layer23/src/modem/grr.c new file mode 100644 index 00000000..cd325c4c --- /dev/null +++ b/src/host/layer23/src/modem/grr.c @@ -0,0 +1,875 @@ +/* + * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> + +#include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/gsm/lapdm.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/gsm48_ie.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/sysinfo.h> +#include <osmocom/bb/common/l1ctl.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/modem/modem.h> +#include <osmocom/bb/modem/grr.h> + +#include <osmocom/bb/mobile/gsm322.h> +#include <osmocom/bb/mobile/gsm48_rr.h> + +#include <l1ctl_proto.h> + +static uint32_t _gsm48_req_ref2fn(const struct gsm48_req_ref *ref) +{ + const struct gsm_time time = { + .t3 = ref->t3_high << 3 | ref->t3_low, + .t2 = ref->t2, + .t1 = ref->t1, + }; + return gsm_gsmtime2fn(&time); +} + +/* Generate an 8-bit CHANNEL REQUEST message as per 3GPP TS 44.018, 9.1.8 */ +static uint8_t grr_gen_chan_req(bool single_block) +{ + uint8_t rnd = (uint8_t)rand(); + + if (single_block) /* 01110xxx */ + return 0x70 | (rnd & 0x07); + + /* 011110xx or 01111x0x or 01111xx0 */ + if ((rnd & 0x07) == 0x07) + return 0x78; + return 0x78 | (rnd & 0x07); +} + +static bool grr_match_req_ref(struct osmocom_ms *ms, + const struct gsm48_req_ref *ref) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + for (unsigned int i = 0; i < ARRAY_SIZE(rr->cr_hist); i++) { + const struct gsm48_cr_hist *hist = &rr->cr_hist[i]; + if (!hist->valid) + continue; + if (memcmp(&hist->ref, ref, sizeof(*ref)) == 0) + return true; + } + + return false; +} + +static int forward_to_rlcmac(struct osmocom_ms *ms, struct msgb *msg) +{ + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + const uint32_t fn = *(uint32_t *)(&msg->cb[0]); + + /* Forward a CCCH/BCCH block to the RLC/MAC layer */ + rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(fn, msgb_l3(msg)); + return osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); +} + +static int grr_handle_si1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&cs->sel_si.si1_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo1(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode SI1 message\n"); + return rc; + } + + return 0; +} + +static int grr_handle_si3(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&cs->sel_si.si3_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo3(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode SI3 message\n"); + return rc; + } + + if (cs->ccch_mode == CCCH_MODE_NONE) { + if (cs->sel_si.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) + cs->ccch_mode = CCCH_MODE_COMBINED; + else + cs->ccch_mode = CCCH_MODE_NON_COMBINED; + l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode); + } + + if (!cs->sel_si.gprs.supported) { + LOGP(DRR, LOGL_NOTICE, "SI3 Rest Octets IE contains no GPRS Indicator\n"); + return 0; + } + + LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n", + cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm"); + + return 0; +} + +static int grr_handle_si4(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&cs->sel_si.si4_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo4(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) { + LOGP(DRR, LOGL_ERROR, "Failed to decode SI4 message\n"); + return rc; + } + + if (!cs->sel_si.gprs.supported) { + LOGP(DRR, LOGL_NOTICE, "SI4 Rest Octets IE contains no GPRS Indicator\n"); + return 0; + } + + LOGP(DRR, LOGL_NOTICE, "Found GPRS Indicator (RA Colour %u, SI13 on BCCH %s)\n", + cs->sel_si.gprs.ra_colour, cs->sel_si.gprs.si13_pos ? "Ext" : "Norm"); + + return 0; +} + +static int grr_handle_si13(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int rc; + + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) + return -EINVAL; + if (!memcmp(&cs->sel_si.si13_msg[0], msgb_l3(msg), msgb_l3len(msg))) + return 0; /* this message is already handled */ + + rc = gsm48_decode_sysinfo13(&cs->sel_si, msgb_l3(msg), msgb_l3len(msg)); + if (rc != 0) + return rc; + + /* Forward SI13 to RLC/MAC layer */ + return forward_to_rlcmac(ms, msg); +} + +static int grr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct gsm48_system_information_type_header *si_hdr = msgb_l3(msg); + const uint8_t si_type = si_hdr->system_information; + const uint32_t fn = *(uint32_t *)(&msg->cb[0]); + + LOGP(DRR, LOGL_INFO, "BCCH message (type=0x%02x, fn=%u): %s\n", + si_type, fn, gsm48_rr_msg_name(si_type)); + + switch (si_type) { + case GSM48_MT_RR_SYSINFO_1: + return grr_handle_si1(ms, msg); + case GSM48_MT_RR_SYSINFO_3: + return grr_handle_si3(ms, msg); + case GSM48_MT_RR_SYSINFO_4: + return grr_handle_si4(ms, msg); + case GSM48_MT_RR_SYSINFO_13: + return grr_handle_si13(ms, msg); + default: + return 0; + }; +} + +static int grr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct gsm48_imm_ass *ia = msgb_l3(msg); + struct gsm48_rrlayer *rr = &ms->rrlayer; + + /* 3GPP TS 44.018, section 10.5.2.25b "Dedicated mode or TBF". + * As per table 9.1.18.1, only the value part (4 bits) is present in the + * IMMEDIATE ASSIGNMENT message. In struct gsm48_imm_ass it's combined + * with the Page Mode IE, perhaps due to historical reasons. */ + const uint8_t dm_or_tbf = ia->page_mode >> 4; + + /* T/D flag: discard dedicated channel assignment */ + if ((dm_or_tbf & (1 << 0)) == 0) { + LOGP(DRR, LOGL_INFO, + "%s(): Discarding IMM ASS: dedicated channel assignment\n", + __func__); + return 0; + } + /* NRA flag: discard No Resource Allocated */ + if ((dm_or_tbf & (1 << 3)) != 0) { + LOGP(DRR, LOGL_INFO, + "%s(): Discarding IMM ASS: NRA flag is set\n", + __func__); + return 0; + } + + /* If this is an Uplink TBF assignment, check the Request Reference IE. + * Checking this IE in Downlink TBF assignment makes no sense because + * no CHANNEL REQUEST was sent by the MS prior to it. */ + if ((dm_or_tbf & (1 << 1)) == 0) { + if (rr->state != GSM48_RR_ST_CONN_PEND) { + LOGP(DRR, LOGL_INFO, + "%s(): rr_state != GSM48_RR_ST_CONN_PEND\n", __func__); + return 0; + } + if (!grr_match_req_ref(ms, &ia->req_ref)) { + LOGP(DRR, LOGL_INFO, + "%s(): req_ref mismatch (RA=0x%02x, T1=%u, T3=%u, T2=%u, FN=%u)\n", + __func__, ia->req_ref.ra, ia->req_ref.t1, + ia->req_ref.t3_high << 3 | ia->req_ref.t3_low, ia->req_ref.t2, + _gsm48_req_ref2fn(&ia->req_ref)); + return 0; + } + } + + return forward_to_rlcmac(ms, msg); +} + +/* TS 44.018 9.1.22 "Paging request type 1" */ +static int grr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DRR, LOGL_INFO, "Rx Paging Request Type 1\n"); + + return forward_to_rlcmac(ms, msg); +} + +/* TS 44.018 9.1.23 "Paging request type 2" */ +static int grr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DRR, LOGL_INFO, "Rx Paging Request Type 2\n"); + + return forward_to_rlcmac(ms, msg); +} + +/* 9.1.24 Paging request type 3 */ +static int grr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DRR, LOGL_INFO, "Rx Paging Request Type 3\n"); + + /* Paging Request Type 3 contains 4 TMSI/P-TMSI, but P3 Rest Octets + contain no "Packet Page Indication" IE, hence it cannot be used to page + for GPRS. Simply ignore it. */ + return 0; +} + +/* Dummy Paging Request 1 with "no identity" */ +static const uint8_t paging_fill[] = { + 0x15, 0x06, 0x21, 0x00, 0x01, 0xf0, 0x2b, + /* The rest part may be randomized */ +}; + +/* LAPDm func=UI fill frame (for the BTS side) */ +static const uint8_t lapdm_fill[] = { + 0x03, 0x03, 0x01, 0x2b, + /* The rest part may be randomized */ +}; + +/* TODO: share / generalize this code */ +static bool is_fill_frame(const struct msgb *msg) +{ + const uint8_t *l2 = msgb_l3(msg); + + if (!memcmp(l2, paging_fill, sizeof(paging_fill))) + return true; + if (!memcmp(l2, lapdm_fill, sizeof(lapdm_fill))) + return true; + + return false; +} + +static int grr_rx_ccch(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + /* Skip frames with wrong length */ + if (msgb_l3len(msg) != GSM_MACBLOCK_LEN) { + LOGP(DRR, LOGL_ERROR, "Rx CCCH message with odd length=%u: %s\n", + msgb_l3len(msg), msgb_hexdump_l3(msg)); + return -EINVAL; + } + + /* Skip dummy (fill) frames */ + if (is_fill_frame(msg)) + return 0; + + if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) { + LOGP(DRR, LOGL_ERROR, "PCH pdisc (%s) != RR\n", + gsm48_pdisc_name(sih->rr_protocol_discriminator)); + } + + switch (sih->system_information) { + case GSM48_MT_RR_IMM_ASS: + return grr_rx_imm_ass(ms, msg); + case GSM48_MT_RR_PAG_REQ_1: + return grr_rx_pag_req_1(ms, msg); + case GSM48_MT_RR_PAG_REQ_2: + return grr_rx_pag_req_2(ms, msg); + case GSM48_MT_RR_PAG_REQ_3: + return grr_rx_pag_req_3(ms, msg); + default: + return 0; + } +} + +static int grr_rx_rslms_rll_ud(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + if (rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg) - sizeof(*rllh)) < 0) { + LOGP(DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return -EINVAL; + } + + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + LOGP(DRSL, LOGL_ERROR, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EINVAL; + } + + msg->l3h = (uint8_t *)TLVP_VAL(&tv, RSL_IE_L3_INFO); + + switch (rllh->chan_nr) { + case RSL_CHAN_PCH_AGCH: + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PCH_AGCH_BLOCK_IND, msg); + case RSL_CHAN_BCCH: + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_BCCH_BLOCK_IND, msg); + default: + return 0; + } +} + +static int grr_rx_rslms_rll(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + + switch (rllh->c.msg_type) { + case RSL_MT_UNIT_DATA_IND: + return grr_rx_rslms_rll_ud(ms, msg); + default: + LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms RLL message " + "(msg_type 0x%02x)\n", rllh->c.msg_type); + return -EINVAL; + } +} + +static int grr_rx_rslms_cchan(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct abis_rsl_cchan_hdr *ch = msgb_l2(msg); + + switch (ch->c.msg_type) { + case RSL_MT_CHAN_CONF: /* RACH.conf */ + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_CHAN_ACCESS_CNF, + (void *)&ch->data[1]); + default: + LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms CCHAN message " + "(msg_type 0x%02x)\n", ch->c.msg_type); + return -EINVAL; + } +} + +int modem_grr_rslms_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx) +{ + const struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc; + + /* Obtain FN from message context: */ + *(uint32_t *)(&msg->cb[0]) = le->datalink[DL_SAPI0].mctx.fn; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = grr_rx_rslms_rll((struct osmocom_ms *)ctx, msg); + break; + case ABIS_RSL_MDISC_COM_CHAN: + rc = grr_rx_rslms_cchan((struct osmocom_ms *)ctx, msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unhandled RSLms message " + "(msg_discr 0x%02x)\n", rslh->msg_discr); + rc = -EINVAL; + break; + } + + msgb_free(msg); + return rc; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +#define S(x) (1 << (x)) + +#include <osmocom/gsm/gsm0502.h> // XXX + +/* RACH re-transmission delay value (in ms) */ +#define GRR_PACKET_ACCESS_DELAY_MS 300 +/* RACH max number of transmissions */ +#define GRR_PACKET_ACCESS_MAX_CHAN_REQ 3 + +static void handle_chan_access_req(struct osmo_fsm_inst *fi, + const struct osmo_gprs_rlcmac_l1ctl_prim *lp) +{ + struct osmocom_ms *ms = fi->priv; + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (lp->rach_req.is_11bit) { /* TODO: implement 11-bit RACH */ + LOGPFSML(fi, LOGL_ERROR, "11-bit RACH is not supported\n"); + return; + } + + memset(&rr->cr_hist[0], 0x00, sizeof(rr->cr_hist)); + rr->chan_req_val = lp->rach_req.ra & ~0x07; + rr->n_chan_req = GRR_PACKET_ACCESS_MAX_CHAN_REQ; + rr->state = GSM48_RR_ST_CONN_PEND; + + osmo_fsm_inst_state_chg_ms(fi, GRR_ST_PACKET_ACCESS, + GRR_PACKET_ACCESS_DELAY_MS, 0); +} + +static void handle_pdch_establish_req(struct osmo_fsm_inst *fi, + const struct osmo_gprs_rlcmac_l1ctl_prim *lp) +{ + struct osmocom_ms *ms = fi->priv; + + if (!lp->pdch_est_req.fh) { + LOGPFSML(fi, LOGL_INFO, + "PDCH Establish.Req: TSC=%u, H0, ARFCN=%u\n", + lp->pdch_est_req.tsc, lp->pdch_est_req.arfcn); + l1ctl_tx_dm_est_req_h0(ms, lp->pdch_est_req.arfcn, + RSL_CHAN_OSMO_PDCH | lp->pdch_est_req.ts_nr, + lp->pdch_est_req.tsc, GSM48_CMODE_SIGN, 0, 0); + } else { + /* Hopping */ + uint8_t ma_len = 0; + uint16_t ma[64]; + + LOGPFSML(fi, LOGL_INFO, + "PDCH Establish.Req: TSC=%u, H1, HSN=%u, MAIO=%u\n", + lp->pdch_est_req.tsc, + lp->pdch_est_req.fhp.hsn, + lp->pdch_est_req.fhp.maio); + + for (unsigned int i = 1, j = 0; i <= 1024; i++) { + unsigned int arfcn = i & 1023; + unsigned int k; + + if (~ms->cellsel.sel_si.freq[arfcn].mask & 0x01) + continue; + + k = lp->pdch_est_req.fhp.ma_len - (j >> 3) - 1; + if (lp->pdch_est_req.fhp.ma[k] & (1 << (j & 7))) + ma[ma_len++] = arfcn; + j++; + } + + l1ctl_tx_dm_est_req_h1(ms, + lp->pdch_est_req.fhp.maio, + lp->pdch_est_req.fhp.hsn, + &ma[0], ma_len, + RSL_CHAN_OSMO_PDCH | lp->pdch_est_req.ts_nr, + lp->pdch_est_req.tsc, GSM48_CMODE_SIGN, 0, 0); + } + + osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_TRANSFER, 0, 0); +} + +static void handle_pdch_block_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_ul_block_cnf *cnf = (void *)msg->l1h; + const uint32_t fn = osmo_load32be(&cnf->fn); + struct osmo_gprs_rlcmac_prim *prim; + + prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_cnf(cnf->tn, fn, + msgb_l2(msg), + msgb_l2len(msg)); + osmo_gprs_rlcmac_prim_lower_up(prim); +} + +static void handle_pdch_block_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_dl_block_ind *ind = (void *)msg->l1h; + const uint32_t fn = osmo_load32be(&ind->hdr.fn); + struct osmo_gprs_rlcmac_prim *prim; + + /* FIXME: sadly, rlcmac_prim_l1ctl_alloc() is not exposed */ + prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(0, 0, 0, 0, 0, NULL, 0); + prim->l1ctl = (struct osmo_gprs_rlcmac_l1ctl_prim) { + .pdch_data_ind = { + .fn = fn, + .ts_nr = ind->hdr.tn, + .rx_lev = ind->meas.rx_lev, + .ber10k = osmo_load16be(&ind->meas.ber10k), + .ci_cb = osmo_load16be(&ind->meas.ci_cb), + .data_len = msgb_l2len(msg), + .data = msgb_l2(msg), + } + }; + osmo_gprs_rlcmac_prim_lower_up(prim); +} + +static void handle_pdch_rts_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + const struct l1ctl_gprs_rts_ind *ind = (void *)msg->l1h; + const uint32_t fn = osmo_load32be(&ind->fn); + struct osmo_gprs_rlcmac_prim *prim; + + prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ind->tn, fn, ind->usf); + osmo_gprs_rlcmac_prim_lower_up(prim); +} + +static bool grr_cell_is_usable(const struct osmocom_ms *ms) +{ + const struct gsm322_cellsel *cs = &ms->cellsel; + const struct gsm48_sysinfo *si = &cs->sel_si; + + if (cs->sync_pending) /* FBSB in process */ + return false; + + if (!si->si1 || !si->si3 || !si->si4 || !si->si13) + return false; + if (!si->gprs.supported) + return false; + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static void grr_st_packet_not_ready_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct osmocom_ms *ms = fi->priv; + + switch (event) { + case GRR_EV_BCCH_BLOCK_IND: + grr_rx_bcch(ms, (struct msgb *)data); + if (grr_cell_is_usable(ms)) { + LOGPFSML(fi, LOGL_NOTICE, "Cell is usable, GRR becomes ready\n"); + osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_IDLE, 0, 0); + } + break; + case GRR_EV_PCH_AGCH_BLOCK_IND: + grr_rx_ccch(ms, (struct msgb *)data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void grr_st_packet_idle_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct osmocom_ms *ms = fi->priv; + struct osmo_gprs_rlcmac_prim *prim; + + prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_ready_ind(); + osmo_gprs_rlcmac_prim_lower_up(prim); + + modem_gprs_attach_if_needed(ms); +} + +static void grr_st_packet_idle_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct osmocom_ms *ms = fi->priv; + + switch (event) { + case GRR_EV_BCCH_BLOCK_IND: + grr_rx_bcch(ms, (struct msgb *)data); + if (!grr_cell_is_usable(ms)) { + LOGPFSML(fi, LOGL_NOTICE, "Cell is not usable, GRR becomes not ready\n"); + osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0); + } + break; + case GRR_EV_PCH_AGCH_BLOCK_IND: + grr_rx_ccch(ms, (struct msgb *)data); + break; + case GRR_EV_CHAN_ACCESS_REQ: + handle_chan_access_req(fi, data); + break; + case GRR_EV_PDCH_ESTABLISH_REQ: + handle_pdch_establish_req(fi, data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void grr_st_packet_access_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct osmocom_ms *ms = fi->priv; + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (rr->n_chan_req == 0) { + LOGPFSML(fi, LOGL_ERROR, "Packet access failed (no more attempts left)\n"); + osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0); + return; + } + + /* (re-)generate the RA value */ + if ((rr->chan_req_val >> 3) == 0x0e) /* 01110xxx */ + rr->cr_ra = grr_gen_chan_req(true); + else /* 011110xx or 01111x0x or 01111xx0 */ + rr->cr_ra = grr_gen_chan_req(false); + rr->n_chan_req--; + + LOGPFSML(fi, LOGL_INFO, + "Sending CHANNEL REQUEST (ra=0x%02x, num_attempts=%u)\n", + rr->cr_ra, rr->n_chan_req); + + l1ctl_tx_rach_req(ms, RSL_CHAN_RACH, 0x00, rr->cr_ra, 0, + ms->cellsel.ccch_mode == CCCH_MODE_COMBINED, 0xff); +} + +static void grr_st_packet_access_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct osmocom_ms *ms = fi->priv; + + switch (event) { + case GRR_EV_BCCH_BLOCK_IND: + grr_rx_bcch(ms, (struct msgb *)data); + if (!grr_cell_is_usable(ms)) { + LOGPFSML(fi, LOGL_NOTICE, "Cell is not usable, GRR becomes not ready\n"); + osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0); + } + break; + case GRR_EV_PCH_AGCH_BLOCK_IND: + grr_rx_ccch(ms, (struct msgb *)data); + break; + case GRR_EV_CHAN_ACCESS_CNF: + { + struct gsm48_rrlayer *rr = &ms->rrlayer; + const struct gsm48_req_ref *ref = data; + + LOGPFSML(fi, LOGL_NOTICE, + "Rx RACH.conf (RA=0x%02x, T1=%u, T3=%u, T2=%u, FN=%u)\n", + rr->cr_ra, ref->t1, ref->t3_high << 3 | ref->t3_low, ref->t2, + _gsm48_req_ref2fn(ref)); + + if (ms->rrlayer.state != GSM48_RR_ST_CONN_PEND) { + LOGPFSML(fi, LOGL_ERROR, "Rx unexpected RACH.conf\n"); + return; + } + + /* shift the CHANNEL REQUEST history buffer */ + memmove(&rr->cr_hist[1], &rr->cr_hist[0], + sizeof(rr->cr_hist) - sizeof(rr->cr_hist[0])); + /* store the new entry */ + rr->cr_hist[0].ref = *ref; + rr->cr_hist[0].ref.ra = rr->cr_ra; + rr->cr_hist[0].valid = 1; + break; + } + case GRR_EV_PDCH_ESTABLISH_REQ: + handle_pdch_establish_req(fi, data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void grr_st_packet_transfer_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct osmocom_ms *ms = fi->priv; + + ms->rrlayer.state = GSM48_RR_ST_DEDICATED; +} + +static void grr_st_packet_transfer_onleave(struct osmo_fsm_inst *fi, uint32_t next_state) +{ + struct osmocom_ms *ms = fi->priv; + + ms->rrlayer.state = GSM48_RR_ST_IDLE; +} + +static void grr_st_packet_transfer_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct osmocom_ms *ms = fi->priv; + + switch (event) { + case GRR_EV_PDCH_UL_TBF_CFG_REQ: + { + const struct osmo_gprs_rlcmac_l1ctl_prim *lp = data; + l1ctl_tx_gprs_ul_tbf_cfg_req(ms, + lp->cfg_ul_tbf_req.ul_tbf_nr, + lp->cfg_ul_tbf_req.ul_slotmask, + lp->cfg_ul_tbf_req.start_fn); + break; + } + case GRR_EV_PDCH_DL_TBF_CFG_REQ: + { + const struct osmo_gprs_rlcmac_l1ctl_prim *lp = data; + l1ctl_tx_gprs_dl_tbf_cfg_req(ms, + lp->cfg_dl_tbf_req.dl_tbf_nr, + lp->cfg_dl_tbf_req.dl_slotmask, + lp->cfg_ul_tbf_req.start_fn, + lp->cfg_dl_tbf_req.dl_tfi); + break; + } + case GRR_EV_PDCH_BLOCK_REQ: + { + const struct osmo_gprs_rlcmac_l1ctl_prim *lp = data; + l1ctl_tx_gprs_ul_block_req(ms, + lp->pdch_data_req.fn, + lp->pdch_data_req.ts_nr, + lp->pdch_data_req.data, + lp->pdch_data_req.data_len); + break; + } + case GRR_EV_PDCH_BLOCK_CNF: + handle_pdch_block_cnf(ms, (struct msgb *)data); + break; + case GRR_EV_PDCH_BLOCK_IND: + handle_pdch_block_ind(ms, (struct msgb *)data); + break; + case GRR_EV_PDCH_RTS_IND: + handle_pdch_rts_ind(ms, (struct msgb *)data); + break; + case GRR_EV_PDCH_RELEASE_REQ: + modem_sync_to_cell(ms); + osmo_fsm_inst_state_chg(fi, GRR_ST_PACKET_NOT_READY, 0, 0); + break; + default: + OSMO_ASSERT(0); + } +} + +static int grr_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + switch (fi->state) { + case GRR_ST_PACKET_ACCESS: + /* perform a loop transaction, restarting the timer */ + osmo_fsm_inst_state_chg_ms(fi, GRR_ST_PACKET_ACCESS, + GRR_PACKET_ACCESS_DELAY_MS, 0); + return 0; + default: + OSMO_ASSERT(0); + } +} + +static const struct osmo_fsm_state grr_fsm_states[] = { + [GRR_ST_PACKET_NOT_READY] = { + .name = "PACKET_NOT_READY", + .out_state_mask = S(GRR_ST_PACKET_IDLE), + .in_event_mask = S(GRR_EV_BCCH_BLOCK_IND) + | S(GRR_EV_PCH_AGCH_BLOCK_IND), + .action = &grr_st_packet_not_ready_action, + }, + [GRR_ST_PACKET_IDLE] = { + .name = "PACKET_IDLE", + .out_state_mask = S(GRR_ST_PACKET_NOT_READY) + | S(GRR_ST_PACKET_ACCESS) + | S(GRR_ST_PACKET_TRANSFER), + .in_event_mask = S(GRR_EV_BCCH_BLOCK_IND) + | S(GRR_EV_PCH_AGCH_BLOCK_IND) + | S(GRR_EV_CHAN_ACCESS_REQ) + | S(GRR_EV_PDCH_ESTABLISH_REQ), /* DL TBF ASS */ + .action = &grr_st_packet_idle_action, + .onenter = &grr_st_packet_idle_onenter, + }, + [GRR_ST_PACKET_ACCESS] = { + .name = "PACKET_ACCESS", + .out_state_mask = S(GRR_ST_PACKET_NOT_READY) + | S(GRR_ST_PACKET_ACCESS) + | S(GRR_ST_PACKET_TRANSFER), + .in_event_mask = S(GRR_EV_BCCH_BLOCK_IND) + | S(GRR_EV_PCH_AGCH_BLOCK_IND) + | S(GRR_EV_CHAN_ACCESS_CNF) + | S(GRR_EV_PDCH_ESTABLISH_REQ), /* UL TBF ASS */ + .onenter = &grr_st_packet_access_onenter, + .action = &grr_st_packet_access_action, + }, + [GRR_ST_PACKET_TRANSFER] = { + .name = "PACKET_TRANSFER", + .out_state_mask = S(GRR_ST_PACKET_NOT_READY), + .in_event_mask = S(GRR_EV_PDCH_UL_TBF_CFG_REQ) + | S(GRR_EV_PDCH_DL_TBF_CFG_REQ) + | S(GRR_EV_PDCH_BLOCK_REQ) + | S(GRR_EV_PDCH_BLOCK_CNF) + | S(GRR_EV_PDCH_BLOCK_IND) + | S(GRR_EV_PDCH_RTS_IND) + | S(GRR_EV_PDCH_RELEASE_REQ), + .action = &grr_st_packet_transfer_action, + .onenter = &grr_st_packet_transfer_onenter, + .onleave = &grr_st_packet_transfer_onleave, + }, +}; + +static const struct value_string grr_fsm_event_names[] = { + OSMO_VALUE_STRING(GRR_EV_BCCH_BLOCK_IND), + OSMO_VALUE_STRING(GRR_EV_PCH_AGCH_BLOCK_IND), + OSMO_VALUE_STRING(GRR_EV_CHAN_ACCESS_REQ), + OSMO_VALUE_STRING(GRR_EV_CHAN_ACCESS_CNF), + OSMO_VALUE_STRING(GRR_EV_PDCH_ESTABLISH_REQ), + OSMO_VALUE_STRING(GRR_EV_PDCH_RELEASE_REQ), + OSMO_VALUE_STRING(GRR_EV_PDCH_UL_TBF_CFG_REQ), + OSMO_VALUE_STRING(GRR_EV_PDCH_DL_TBF_CFG_REQ), + OSMO_VALUE_STRING(GRR_EV_PDCH_BLOCK_REQ), + OSMO_VALUE_STRING(GRR_EV_PDCH_BLOCK_CNF), + OSMO_VALUE_STRING(GRR_EV_PDCH_BLOCK_IND), + OSMO_VALUE_STRING(GRR_EV_PDCH_RTS_IND), + { 0, NULL } +}; + +struct osmo_fsm grr_fsm_def = { + .name = "GPRS-RR", + .log_subsys = DRR, + .states = grr_fsm_states, + .num_states = ARRAY_SIZE(grr_fsm_states), + .event_names = grr_fsm_event_names, + .timer_cb = &grr_fsm_timer_cb, +}; + +static __attribute__((constructor)) void on_dso_load(void) +{ + OSMO_ASSERT(osmo_fsm_register(&grr_fsm_def) == 0); +} diff --git a/src/host/layer23/src/modem/llc.c b/src/host/layer23/src/modem/llc.c new file mode 100644 index 00000000..fe920ee7 --- /dev/null +++ b/src/host/layer23/src/modem/llc.c @@ -0,0 +1,181 @@ +/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ +/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> + +#include <osmocom/core/prim.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> + +#include <osmocom/gprs/gprs_msgb.h> +#include <osmocom/gprs/llc/llc_prim.h> +#include <osmocom/gprs/llc/llc.h> +#include <osmocom/gprs/gmm/gmm_prim.h> +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> +#include <osmocom/gprs/sndcp/sndcp_prim.h> + +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/modem/llc.h> + +static int modem_llc_handle_ll_gmm(struct osmo_gprs_llc_prim *llc_prim) +{ + int rc; + + switch (llc_prim->oph.primitive) { + case OSMO_GPRS_LLC_LL_UNITDATA: + break; + case OSMO_GPRS_LLC_LL_RESET: + case OSMO_GPRS_LLC_LL_ESTABLISH: + case OSMO_GPRS_LLC_LL_XID: + case OSMO_GPRS_LLC_LL_DATA: + case OSMO_GPRS_LLC_LL_STATUS: + default: + LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx LL prim %u\n", + __func__, llc_prim->oph.primitive); + return -EINVAL; + } + + /* GMM took ownership of the message, tell LLC layer to not free it: */ + rc = osmo_gprs_gmm_prim_llc_lower_up(llc_prim); + rc = 1; + return rc; +} + +static int modem_llc_handle_ll_sndcp(struct osmo_gprs_llc_prim *llc_prim) +{ + int rc; + switch (llc_prim->oph.primitive) { + case OSMO_GPRS_LLC_LL_RESET: + case OSMO_GPRS_LLC_LL_ESTABLISH: + case OSMO_GPRS_LLC_LL_XID: + case OSMO_GPRS_LLC_LL_DATA: + case OSMO_GPRS_LLC_LL_UNITDATA: + case OSMO_GPRS_LLC_LL_STATUS: + case OSMO_GPRS_LLC_LL_ASSIGN: + /* Forward it to upper layers, pass owneserip over to SNDCP: */ + osmo_gprs_sndcp_prim_lower_up(llc_prim); + rc = 1; /* Tell LLC that we take ownership of the prim. */ + break; + default: + LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx LL prim %u\n", + __func__, llc_prim->oph.primitive); + rc = -EINVAL; + } + return rc; +} + +int modem_llc_prim_up_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim); + int rc = 0; + + switch (llc_prim->oph.sap) { + case OSMO_GPRS_LLC_SAP_LLGMM: + LOGP(DLLC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x\n", + __func__, pdu_name, llc_prim->llgmm.tlli); + break; + case OSMO_GPRS_LLC_SAP_LL: + LOGP(DLLC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x SAPI=%s l3=[%s]\n", + __func__, pdu_name, llc_prim->ll.tlli, + osmo_gprs_llc_sapi_name(llc_prim->ll.sapi), + osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len)); + + switch (llc_prim->ll.sapi) { + case OSMO_GPRS_LLC_SAPI_GMM: + rc = modem_llc_handle_ll_gmm(llc_prim); + break; + case OSMO_GPRS_LLC_SAPI_SNDCP3: + case OSMO_GPRS_LLC_SAPI_SNDCP5: + case OSMO_GPRS_LLC_SAPI_SNDCP9: + case OSMO_GPRS_LLC_SAPI_SNDCP11: + rc = modem_llc_handle_ll_sndcp(llc_prim); + break; + case OSMO_GPRS_LLC_SAPI_TOM2: + case OSMO_GPRS_LLC_SAPI_SMS: + case OSMO_GPRS_LLC_SAPI_TOM8: + LOGP(DLLC, LOGL_NOTICE, "%s(): Unimplemented Rx llc_sapi %s\n", __func__, pdu_name); + rc = -EINVAL; + break; + default: + LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx llc_sapi %s\n", __func__, pdu_name); + rc = -EINVAL; + break; + } + break; + default: + LOGP(DLLC, LOGL_NOTICE, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return rc; +} + +int modem_llc_prim_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim); + int rc = 0; + + osmo_static_assert(sizeof(struct osmo_gprs_llc_grr_prim) == sizeof(struct osmo_gprs_rlcmac_grr_prim), + _grr_prim_size); + + switch (llc_prim->oph.sap) { + case OSMO_GPRS_LLC_SAP_GRR: + LOGP(DLLC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x SAPI=%s RadioPrio=%u ll=[%s]\n", + __func__, pdu_name, llc_prim->grr.tlli, + /* data_req.sapi in same union pos: */ + osmo_gprs_llc_sapi_name(llc_prim->grr.unitdata_req.sapi), + /* data_req.radio_prio in same union pos: */ + llc_prim->grr.unitdata_req.radio_prio, + osmo_hexdump(llc_prim->grr.ll_pdu, llc_prim->grr.ll_pdu_len)); + /* Forward it to lower layers, pass ownership over to RLCMAC: */ + /* Optimization: LLC-GRR-UNITDATA-IND is 1-to-1 ABI compatible with + RLCMAC-GRR-UNITDATA-IND, we just need to adapt the header. + See osmo_static_assert(_grr_prim_size) above. + */ + llc_prim->oph.sap = OSMO_GPRS_RLCMAC_SAP_GRR; + llc_prim->oph.primitive = OSMO_GPRS_RLCMAC_GRR_UNITDATA; + osmo_gprs_rlcmac_prim_upper_down((struct osmo_gprs_rlcmac_prim *)llc_prim); + rc = 1; /* Tell RLCMAC that we take ownership of the prim. */ + break; + default: + LOGP(DLLC, LOGL_DEBUG, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return rc; +} + +int modem_llc_init(struct osmocom_ms *ms, const char *cipher_plugin_path) +{ + int rc; + rc = osmo_gprs_llc_init(OSMO_GPRS_LLC_LOCATION_MS, cipher_plugin_path); + if (rc != 0) + return rc; + + osmo_gprs_llc_set_log_cat(OSMO_GPRS_LLC_LOGC_LLC, DLLC); + + osmo_gprs_llc_prim_set_up_cb(modem_llc_prim_up_cb, ms); + osmo_gprs_llc_prim_set_down_cb(modem_llc_prim_down_cb, ms); + return rc; +} diff --git a/src/host/layer23/src/modem/rlcmac.c b/src/host/layer23/src/modem/rlcmac.c new file mode 100644 index 00000000..7f7b5661 --- /dev/null +++ b/src/host/layer23/src/modem/rlcmac.c @@ -0,0 +1,199 @@ +/* GPRS RLC/MAC protocol implementation as per 3GPP TS 44.060 */ +/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <errno.h> +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/bits.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> + +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsm0502.h> + +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> +#include <osmocom/gprs/rlcmac/rlcmac.h> +#include <osmocom/gprs/llc/llc_prim.h> +#include <osmocom/gprs/gmm/gmm_prim.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/modem/rlcmac.h> +#include <osmocom/bb/modem/grr.h> + +static int modem_rlcmac_handle_grr(struct osmo_gprs_rlcmac_prim *rlcmac_prim) +{ + int rc; + + osmo_static_assert(sizeof(struct osmo_gprs_rlcmac_grr_prim) == sizeof(struct osmo_gprs_llc_grr_prim), + _grr_prim_size); + + switch (rlcmac_prim->oph.primitive) { + case OSMO_GPRS_RLCMAC_GRR_UNITDATA: + /* Forward it to upper layers, pass ownership over to LLC: */ + /* Optimization: RLCMAC-GRR-UNITDATA-IND is 1-to-1 ABI compatible with + LLC-GRR-UNITDATA-IND, we just need to adapt the header. + See osmo_static_assert(_grr_prim_size) above. + */ + rlcmac_prim->oph.sap = OSMO_GPRS_LLC_SAP_GRR; + rlcmac_prim->oph.primitive = OSMO_GPRS_LLC_GRR_UNITDATA; + osmo_gprs_llc_prim_lower_up((struct osmo_gprs_llc_prim *)rlcmac_prim); + rc = 1; /* Tell RLCMAC that we take ownership of the prim. */ + break; + case OSMO_GPRS_RLCMAC_GRR_DATA: + default: + LOGP(DRLCMAC, LOGL_NOTICE, "%s(): Unexpected Rx RLCMAC GRR prim %u\n", + __func__, rlcmac_prim->oph.primitive); + rc = -EINVAL; + break; + } + return rc; +} + +static int modem_rlcmac_handle_gmmrr(struct osmo_gprs_rlcmac_prim *rlcmac_prim) +{ + struct osmo_gprs_gmm_prim *gmm_prim; + int rc; + + osmo_static_assert(sizeof(struct osmo_gprs_rlcmac_gmmrr_prim) == sizeof(struct osmo_gprs_gmm_gmmrr_prim), + _gmmrr_prim_size); + + switch (rlcmac_prim->oph.primitive) { + case OSMO_GPRS_RLCMAC_GMMRR_PAGE: + /* Forward it to upper layers, pass ownership over to GMM: */ + /* Optimization: RLCMAC-GMMRR-PAGE-IND is 1-to-1 ABI compatible with + GMM-GMMRR-PAGE-IND, we just need to adapt the header. + See osmo_static_assert(_gmmrr_prim_size) above. + */ + gmm_prim = (struct osmo_gprs_gmm_prim *)rlcmac_prim; + gmm_prim->oph.sap = OSMO_GPRS_GMM_SAP_GMMRR; + gmm_prim->oph.primitive = OSMO_GPRS_RLCMAC_GMMRR_PAGE; + osmo_gprs_gmm_prim_lower_up(gmm_prim); + rc = 1; /* Tell RLCMAC that we take ownership of the prim. */ + break; + case OSMO_GPRS_RLCMAC_GMMRR_LLC_TRANSMITTED: + /* Forward it to upper layers, pass ownership over to GMM: */ + /* Optimization: RLCMAC-GMMRR-LLC-TRANSMITTED-IND is 1-to-1 ABI compatible with + GMM-GMMRR-LLC-TRANSMITTED-IND, we just need to adapt the header. + See osmo_static_assert(_gmmrr_prim_size) above. + */ + gmm_prim = (struct osmo_gprs_gmm_prim *)rlcmac_prim; + gmm_prim->oph.sap = OSMO_GPRS_GMM_SAP_GMMRR; + gmm_prim->oph.primitive = OSMO_GPRS_GMM_GMMRR_LLC_TRANSMITTED; + osmo_gprs_gmm_prim_lower_up(gmm_prim); + rc = 1; /* Tell RLCMAC that we take ownership of the prim. */ + break; + default: + LOGP(DRLCMAC, LOGL_NOTICE, "%s(): Unexpected Rx RLCMAC GMMRR prim %u\n", + __func__, rlcmac_prim->oph.primitive); + rc = -EINVAL; + } + return rc; +} + +static int modem_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim); + int rc = 0; + + switch (rlcmac_prim->oph.sap) { + case OSMO_GPRS_RLCMAC_SAP_GRR: + LOGP(DRLCMAC, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x ll=[%s]\n", + __func__, pdu_name, rlcmac_prim->grr.tlli, + osmo_hexdump(rlcmac_prim->grr.ll_pdu, rlcmac_prim->grr.ll_pdu_len)); + rc = modem_rlcmac_handle_grr(rlcmac_prim); + break; + case OSMO_GPRS_RLCMAC_SAP_GMMRR: + LOGP(DRLCMAC, LOGL_DEBUG, "%s(): Rx %s\n", + __func__, pdu_name); + rc = modem_rlcmac_handle_gmmrr(rlcmac_prim); + break; + default: + LOGP(DRLCMAC, LOGL_NOTICE, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return rc; +} + +static int modem_rlcmac_prim_down_cb(struct osmo_gprs_rlcmac_prim *prim, void *user_data) +{ + struct osmo_gprs_rlcmac_l1ctl_prim *lp = &prim->l1ctl; + const char *pdu_name = osmo_gprs_rlcmac_prim_name(prim); + struct osmocom_ms *ms = user_data; + + switch (OSMO_PRIM_HDR(&prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_RACH, PRIM_OP_REQUEST): + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_CHAN_ACCESS_REQ, lp); + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_DATA, PRIM_OP_REQUEST): + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_BLOCK_REQ, lp); + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_UL_TBF, PRIM_OP_REQUEST): + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_UL_TBF_CFG_REQ, lp); + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST): + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_DL_TBF_CFG_REQ, lp); + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_ESTABLISH, PRIM_OP_REQUEST): + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_ESTABLISH_REQ, lp); + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_RELEASE, PRIM_OP_REQUEST): + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_RELEASE_REQ, lp); + default: + LOGP(DRLCMAC, LOGL_DEBUG, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } +} + +static int l1ctl_ul_block_cnf_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_BLOCK_CNF, msg); +} + +static int l1ctl_dl_block_ind_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_BLOCK_IND, msg); +} + +static int l1ctl_rts_ind_cb(struct osmocom_ms *ms, struct msgb *msg) +{ + return osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_PDCH_RTS_IND, msg); +} + +int modem_rlcmac_init(struct osmocom_ms *ms) +{ + int rc; + rc = osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS); + if (rc != 0) + return rc; + + osmo_gprs_rlcmac_set_log_cat(OSMO_GPRS_RLCMAC_LOGC_RLCMAC, DRLCMAC); + osmo_gprs_rlcmac_set_log_cat(OSMO_GPRS_RLCMAC_LOGC_TBFUL, DRLCMAC); + osmo_gprs_rlcmac_set_log_cat(OSMO_GPRS_RLCMAC_LOGC_TBFDL, DRLCMAC); + + osmo_gprs_rlcmac_prim_set_up_cb(modem_rlcmac_prim_up_cb, ms); + osmo_gprs_rlcmac_prim_set_down_cb(modem_rlcmac_prim_down_cb, ms); + + ms->l1_entity.l1_gprs_ul_block_cnf = &l1ctl_ul_block_cnf_cb; + ms->l1_entity.l1_gprs_dl_block_ind = &l1ctl_dl_block_ind_cb; + ms->l1_entity.l1_gprs_rts_ind = &l1ctl_rts_ind_cb; + + return rc; +} diff --git a/src/host/layer23/src/modem/sm.c b/src/host/layer23/src/modem/sm.c new file mode 100644 index 00000000..1977cd6c --- /dev/null +++ b/src/host/layer23/src/modem/sm.c @@ -0,0 +1,277 @@ +/* GPRS SM interfaces as per 3GPP TS 24.008, TS 24.007 */ +/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/tun.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/gprs/gmm/gmm.h> +#include <osmocom/gprs/gmm/gmm_prim.h> +#include <osmocom/gprs/sndcp/sndcp.h> +#include <osmocom/gprs/sndcp/sndcp_prim.h> +#include <osmocom/gprs/sm/sm_prim.h> +#include <osmocom/gprs/sm/sm.h> +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> + +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/modem/sm.h> + + +static int modem_sm_handle_pdp_act_cnf(struct osmocom_ms *ms, struct osmo_gprs_sm_prim *sm_prim) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + struct osmobb_apn *apn = NULL, *apn_it; + struct osmo_netdev *netdev; + char buf_addr[INET6_ADDRSTRLEN]; + char buf_addr2[INET6_ADDRSTRLEN]; + int rc; + + llist_for_each_entry(apn_it, &ms->gprs.apn_list, list) { + if (apn_it->fsm.fi->state == APN_ST_ACTIVATING) { + apn = apn_it; + break; + } + } + + if (!apn) { + LOGP(DSM, LOGL_ERROR, "Rx %s but have no APN!\n", pdu_name); + return -ENOENT; + } + + if (!sm_prim->smreg.pdp_act_cnf.accepted) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Activate PDP failed! cause '%s'\n", pdu_name, + get_value_string(gsm48_gsm_cause_names, sm_prim->smreg.pdp_act_cnf.rej.cause)); + osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_REJ, NULL); + /* TODO: maybe retry ? */ + return 0; + } + + ms->subscr.gprs.ptmsi = sm_prim->smreg.pdp_act_cnf.acc.gmm.allocated_ptmsi; + ms->gmmlayer.tlli = sm_prim->smreg.pdp_act_cnf.acc.gmm.allocated_tlli; + + apn->pdp.pdp_addr_ietf_type = sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_ietf_type; + + netdev = osmo_tundev_get_netdev(apn->tun); + switch (sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_ietf_type) { + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4: + LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv4=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr)); + memcpy(&apn->pdp.pdp_addr_v4, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, sizeof(struct osmo_sockaddr)); + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, 30); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv4=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr)); + return rc; + } + break; + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6: + LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv6=%s [FIXME: IPv6 not yet supported!]\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr)); + memcpy(&apn->pdp.pdp_addr_v6, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, sizeof(struct osmo_sockaddr)); + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, 64); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv6=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr)); + return rc; + } + break; + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6: + LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv4=%s IPv6=%s [FIXME: IPv6 not yet supported!]\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr), + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr2)); + memcpy(&apn->pdp.pdp_addr_v4, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, sizeof(struct osmo_sockaddr)); + memcpy(&apn->pdp.pdp_addr_v6, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, sizeof(struct osmo_sockaddr)); + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, 30); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv4=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr)); + return rc; + } + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, 64); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv6=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr)); + return rc; + } + break; + default: + OSMO_ASSERT(0); + } + + /* TODO: Handle PCO */ + /* TODO: Handle QoS */ + + osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_ACC, NULL); + return rc; +} + +static int modem_sm_prim_up_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + struct osmocom_ms *ms = user_data; + int rc = 0; + + switch (sm_prim->oph.sap) { + case OSMO_GPRS_SM_SAP_SMREG: + switch (OSMO_PRIM_HDR(&sm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_CONFIRM): + modem_sm_handle_pdp_act_cnf(ms, sm_prim); + break; + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_MODIFY, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_MODIFY, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE_SEC, PRIM_OP_CONFIRM): + default: + LOGP(DSM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name); + break; + }; + break; + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + return rc; +} + +int modem_sm_prim_sndcp_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim); + int rc; + + switch (sndcp_prim->oph.sap) { + case OSMO_GPRS_SNDCP_SAP_SNSM: + switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_ACTIVATE, PRIM_OP_INDICATION): + LOGP(DSM, LOGL_INFO, "%s(): Rx %s\n", __func__, pdu_name); + rc = osmo_gprs_sndcp_prim_dispatch_snsm(sndcp_prim); + rc = 1; /* Tell SM that we take ownership of the prim. */ + break; + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + break; + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return rc; +} + +static int modem_sm_prim_down_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + int rc = 0; + + switch (sm_prim->oph.sap) { + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + return rc; +} + +static int modem_sm_prim_gmm_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) +{ + int rc; + + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + + /* GMM took ownership of the message, tell SM layer to not free it: */ + rc = 1; + return rc; +} + +int modem_sm_init(struct osmocom_ms *ms) +{ + int rc; + rc = osmo_gprs_sm_init(OSMO_GPRS_SM_LOCATION_MS); + if (rc != 0) + return rc; + + osmo_gprs_sm_set_log_cat(OSMO_GPRS_SM_LOGC_SM, DSM); + + osmo_gprs_sm_prim_set_up_cb(modem_sm_prim_up_cb, ms); + osmo_gprs_sm_prim_set_sndcp_up_cb(modem_sm_prim_sndcp_up_cb, ms); + osmo_gprs_sm_prim_set_down_cb(modem_sm_prim_down_cb, ms); + osmo_gprs_sm_prim_set_gmm_down_cb(modem_sm_prim_gmm_down_cb, ms); + + return rc; +} + +int modem_sm_smreg_pdp_act_req(const struct osmocom_ms *ms, const struct osmobb_apn *apn) +{ + struct osmo_gprs_sm_prim *sm_prim; + const struct gsm_subscriber *subscr = &ms->subscr; + enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type; + struct osmo_sockaddr pdp_addr_any = {0}; + int rc; + + if (apn->cfg.apn_type_mask & APN_TYPE_IPv4v6) { + pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6; + } else if (apn->cfg.apn_type_mask & APN_TYPE_IPv4) { + pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4; + } else if (apn->cfg.apn_type_mask & APN_TYPE_IPv6) { + pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6; + } else { + LOGP(DSM, LOGL_ERROR, "APN '%s' has no PDP address type set\n", apn->cfg.name); + return -EINVAL; + } + + sm_prim = osmo_gprs_sm_prim_alloc_smreg_pdp_act_req(); + sm_prim->smreg.pdp_act_req.nsapi = apn->pdp.nsapi; + sm_prim->smreg.pdp_act_req.llc_sapi = apn->pdp.llc_sapi; + sm_prim->smreg.pdp_act_req.pdp_addr_ietf_type = pdp_addr_ietf_type; + sm_prim->smreg.pdp_act_req.pdp_addr_v4 = pdp_addr_any; + sm_prim->smreg.pdp_act_req.pdp_addr_v6 = pdp_addr_any; + memcpy(sm_prim->smreg.pdp_act_req.qos, apn->pdp.qos, apn->pdp.qos_len); + sm_prim->smreg.pdp_act_req.qos_len = apn->pdp.qos_len; + memcpy(sm_prim->smreg.pdp_act_req.pco, apn->pdp.pco, apn->pdp.pco_len); + sm_prim->smreg.pdp_act_req.pco_len = apn->pdp.pco_len; + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.apn, apn->cfg.name); + sm_prim->smreg.pdp_act_req.gmm.ptmsi = subscr->gprs.ptmsi; + sm_prim->smreg.pdp_act_req.gmm.ptmsi_sig = subscr->gprs.ptmsi; + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imsi, subscr->imsi); + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imei, ms->settings.imei); + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imeisv, ms->settings.imeisv); + memcpy(&sm_prim->smreg.pdp_act_req.gmm.old_rai, &subscr->gprs.rai, sizeof(subscr->gprs.rai)); + + rc = osmo_gprs_sm_prim_upper_down(sm_prim); + if (rc < 0) + LOGP(DSM, LOGL_ERROR, "Failed submitting SMREG-PDP_ACT_REQ.req\n"); + return rc; +} diff --git a/src/host/layer23/src/modem/sndcp.c b/src/host/layer23/src/modem/sndcp.c new file mode 100644 index 00000000..d5b38213 --- /dev/null +++ b/src/host/layer23/src/modem/sndcp.c @@ -0,0 +1,217 @@ +/* GPRS SNDCP User/SN/SNSM interfaces as per 3GPP TS 04.65 */ +/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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 <http://www.gnu.org/lienses/>. + * + */ + +#include <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/tun.h> + +#include <osmocom/gprs/llc/llc.h> +#include <osmocom/gprs/llc/llc_prim.h> +#include <osmocom/gprs/sm/sm_prim.h> +#include <osmocom/gprs/sm/sm.h> +#include <osmocom/gprs/sndcp/sndcp_prim.h> +#include <osmocom/gprs/sndcp/sndcp.h> + +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/modem/sndcp.h> + +/* Received SN-XID.cnf from SNDCP layer: */ +static int modem_sndcp_handle_sn_xid_cnf(struct osmobb_apn *apn, struct osmo_gprs_sndcp_prim *sndcp_prim) +{ + LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx SN-XID.cnf: TODO IMPLEMENT!\n", __func__); + return 0; +} + +/* Received SN-UNITDTA.ind from SNDCP layer: */ +static int modem_sndcp_handle_sn_unitdata_ind(struct osmobb_apn *apn, struct osmo_gprs_sndcp_prim *sndcp_prim) +{ + const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim); + struct msgb *msg; + int rc; + + LOGP(DSNDCP, LOGL_DEBUG, "Rx %s TLLI=0x%08x SAPI=%s NSAPI=%u NPDU=[%s]\n", + npdu_name, + sndcp_prim->sn.tlli, osmo_gprs_llc_sapi_name(sndcp_prim->sn.sapi), + sndcp_prim->sn.data_req.nsapi, + osmo_hexdump(sndcp_prim->sn.data_ind.npdu, sndcp_prim->sn.data_ind.npdu_len)); + + msg = msgb_alloc(sndcp_prim->sn.data_ind.npdu_len, "tx_tun"); + memcpy(msgb_put(msg, sndcp_prim->sn.data_ind.npdu_len), + sndcp_prim->sn.data_ind.npdu, + sndcp_prim->sn.data_ind.npdu_len); + rc = osmo_tundev_send(apn->tun, msg); + return rc; +} + +static int modem_sndcp_prim_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data) +{ + struct osmocom_ms *ms = user_data; + struct osmobb_apn *apn = NULL, *apn_it; + const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim); + int rc = 0; + + if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SN) { + LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, npdu_name); + OSMO_ASSERT(0); + } + + if (ms->gmmlayer.tlli != sndcp_prim->sn.tlli) { + LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx %s: MS has no TLLI=0x%08x\n", __func__, npdu_name, sndcp_prim->sn.tlli); + return -ENOENT; + } + + llist_for_each_entry(apn_it, &ms->gprs.apn_list, list) { + if (apn_it->pdp.nsapi != sndcp_prim->sn.unitdata_ind.nsapi) + continue; + apn = apn_it; + break; + } + if (!apn) { + LOGP(DSNDCP, LOGL_NOTICE, "Unable to find destination APN: Rx %s\n", npdu_name); + return -ENODEV; + } + + switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_UNITDATA, PRIM_OP_INDICATION): + rc = modem_sndcp_handle_sn_unitdata_ind(apn, sndcp_prim); + break; + case OSMO_PRIM(OSMO_GPRS_SNDCP_SN_XID, PRIM_OP_CONFIRM): + rc = modem_sndcp_handle_sn_xid_cnf(apn, sndcp_prim); + break; + default: + LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, npdu_name); + break; + }; + return rc; +} + +static int modem_sndcp_prim_down_cb(struct osmo_gprs_llc_prim *llc_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_llc_prim_name(llc_prim); + int rc = 0; + + if (llc_prim->oph.sap != OSMO_GPRS_LLC_SAP_LL) { + LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + switch (OSMO_PRIM_HDR(&llc_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_LLC_LL_UNITDATA, PRIM_OP_REQUEST): + case OSMO_PRIM(OSMO_GPRS_LLC_LL_XID, PRIM_OP_REQUEST): + LOGP(DSNDCP, LOGL_DEBUG, "%s(): Rx %s TLLI=0x%08x SAPI=%s L3=[%s]\n", + __func__, pdu_name, + llc_prim->ll.tlli, osmo_gprs_llc_sapi_name(llc_prim->ll.sapi), + osmo_hexdump(llc_prim->ll.l3_pdu, llc_prim->ll.l3_pdu_len)); + rc = osmo_gprs_llc_prim_upper_down(llc_prim); + rc = 1; /* Tell SNDCP layer we took msgb ownsership and transfer it to LLC */ + break; + default: + LOGP(DSNDCP, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name); + break; + }; + return rc; +} + +static int modem_sndcp_prim_snsm_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data) +{ + const char *npdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim); + int rc = 0; + + if (sndcp_prim->oph.sap != OSMO_GPRS_SNDCP_SAP_SNSM) { + LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, npdu_name); + OSMO_ASSERT(0); + } + + switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_RESPONSE): + LOGP(DSNDCP, LOGL_INFO, "%s(): Rx %s\n", __func__, npdu_name); + rc = osmo_gprs_sm_prim_sndcp_upper_down(sndcp_prim); + rc = 1; /* Tell SNDCP layer we took msgb ownership and transfer it to SM */ + break; + default: + LOGP(DSNDCP, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, npdu_name); + OSMO_ASSERT(0); + } + return rc; +} + + +int modem_sndcp_init(struct osmocom_ms *ms) +{ + int rc; + rc = osmo_gprs_sndcp_init(OSMO_GPRS_SNDCP_LOCATION_MS); + if (rc != 0) + return rc; + + osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SNDCP, DSNDCP); + osmo_gprs_sndcp_set_log_cat(OSMO_GPRS_SNDCP_LOGC_SLHC, DSNDCP); + + osmo_gprs_sndcp_prim_set_up_cb(modem_sndcp_prim_up_cb, ms); + osmo_gprs_sndcp_prim_set_down_cb(modem_sndcp_prim_down_cb, ms); + osmo_gprs_sndcp_prim_set_snsm_cb(modem_sndcp_prim_snsm_cb, ms); + return rc; +} + +int modem_sndcp_sn_xid_req(struct osmobb_apn *apn) +{ + struct osmo_gprs_sndcp_prim *sndcp_prim; + int rc; + struct osmocom_ms *ms = apn->ms; + struct gprs_settings *set = &ms->gprs; + + sndcp_prim = osmo_gprs_sndcp_prim_alloc_sn_xid_req(ms->gmmlayer.tlli, + apn->pdp.llc_sapi, + apn->pdp.nsapi); + OSMO_ASSERT(sndcp_prim); + sndcp_prim->sn.xid_req.pcomp_rfc1144.active = set->pcomp_rfc1144.active; + sndcp_prim->sn.xid_req.pcomp_rfc1144.s01 = set->pcomp_rfc1144.s01; + sndcp_prim->sn.xid_req.dcomp_v42bis.active = set->dcomp_v42bis.active; + sndcp_prim->sn.xid_req.dcomp_v42bis.p0 = set->dcomp_v42bis.p0; + sndcp_prim->sn.xid_req.dcomp_v42bis.p1 = set->dcomp_v42bis.p1; + sndcp_prim->sn.xid_req.dcomp_v42bis.p2 = set->dcomp_v42bis.p2; + rc = osmo_gprs_sndcp_prim_upper_down(sndcp_prim); + return rc; +} + +int modem_sndcp_sn_unitdata_req(struct osmobb_apn *apn, uint8_t *npdu, size_t npdu_len) +{ + struct osmo_gprs_sndcp_prim *sndcp_prim; + int rc; + + sndcp_prim = osmo_gprs_sndcp_prim_alloc_sn_unitdata_req(apn->ms->gmmlayer.tlli, + apn->pdp.llc_sapi, + apn->pdp.nsapi, + npdu, npdu_len); + OSMO_ASSERT(sndcp_prim); + rc = osmo_gprs_sndcp_prim_upper_down(sndcp_prim); + return rc; +} diff --git a/src/host/layer23/src/modem/vty.c b/src/host/layer23/src/modem/vty.c new file mode 100644 index 00000000..f42aa669 --- /dev/null +++ b/src/host/layer23/src/modem/vty.c @@ -0,0 +1,450 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/linuxlist.h> + +#include <osmocom/gprs/llc/llc.h> +#include <osmocom/gprs/llc/llc_prim.h> +#include <osmocom/gprs/gmm/gmm_prim.h> +#include <osmocom/gprs/sm/sm_prim.h> +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> + + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> + +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/vty.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/modem/gmm.h> +#include <osmocom/bb/modem/grr.h> +#include <osmocom/bb/modem/sm.h> +#include <osmocom/bb/modem/vty.h> + +static struct cmd_node apn_node = { + APN_NODE, + "%s(apn)# ", + 1 +}; + +int modem_vty_go_parent(struct vty *vty) +{ + struct osmobb_apn *apn; + + switch (vty->node) { + case APN_NODE: + apn = vty->index; + vty->index = apn->ms; + vty->node = MS_NODE; + break; + } + return vty->node; +} + +#define MS_NAME_DESC "Name of MS (see \"show ms\")\n" +#define TEST_CMD_DESC "Testing commands for developers\n" +#define GRR_CMDG_DESC "GPRS RR specific commands\n" +#define LLC_CMDG_DESC "GPRS LLC specific commands\n" +#define GMM_CMDG_DESC "GPRS GMM specific commands\n" +#define SM_CMDG_DESC "GPRS SM specific commands\n" + +/* testing commands */ +DEFUN_HIDDEN(test_grr_tx_chan_req, + test_grr_tx_chan_req_cmd, + "test MS_NAME grr start-chan-access (1phase|2phase)", + TEST_CMD_DESC MS_NAME_DESC GRR_CMDG_DESC + "Send a CHANNEL REQUEST (RACH) to the network\n" + "One-phase packet access (011110xx or 01111x0x or 01111xx0)\n" + "Two-phase (single block) packet access (01110xxx)\n") +{ + struct osmocom_ms *ms; + + if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) + return CMD_WARNING; + + const struct osmo_gprs_rlcmac_l1ctl_prim lp = { + .rach_req = { + .is_11bit = false, + /* the 3 LSBs are randomized during (re)transmission */ + .ra = argv[1][0] == '1' ? 0x78 : 0x70, + } + }; + + if (osmo_fsm_inst_dispatch(ms->grr_fi, GRR_EV_CHAN_ACCESS_REQ, (void *)&lp)) { + vty_out(vty, "Failed to send a CHANNEL REQUEST%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(test_llc_unitdata_req_hexpdu, + test_llc_unitdata_req_hexpdu_cmd, + "test MS_NAME llc unitdata-req <0x00-0xffffffff> SAPI HEXSTRING", + TEST_CMD_DESC MS_NAME_DESC LLC_CMDG_DESC + "Enqueue an LLC UNITDATA.req for transmission\n" + "TLLI (Temporary Logical Link Identifier) value to be used\n" + "SAPI value to be used (for example, GMM, SMS, SNDCP3)\n" + "LLC PDU as a hexstring (up to 512 octets)\n") +{ + struct osmo_gprs_llc_prim *llc_prim; + struct osmocom_ms *ms; + uint8_t buf[512]; + int tlli, sapi, pdu_len; + + if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) + return CMD_WARNING; + + if (osmo_str_to_int(&tlli, argv[1], 0, 0, 0xffffff) < 0) + return CMD_WARNING; + sapi = get_string_value(osmo_gprs_llc_sapi_names, argv[2]); + if (sapi < 0) + return CMD_WARNING; + pdu_len = osmo_hexparse(argv[3], &buf[0], sizeof(buf)); + if (pdu_len < 0) + return CMD_WARNING; + + llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(tlli, sapi, &buf[0], pdu_len); + if (osmo_gprs_llc_prim_upper_down(llc_prim) != 0) { + vty_out(vty, "Failed to enqueue an LLC PDU%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static uint8_t pdu_gmmm_attach_req[] = { + 0x08, 0x01, 0x02, 0xe5, 0xe0, 0x01, 0x0a, 0x00, 0x05, 0xf4, 0xf4, 0x3c, 0xec, 0x71, 0x32, 0xf4, + 0x07, 0x00, 0x05, 0x00, 0x17, 0x19, 0x33, 0x43, 0x2b, 0x37, 0x15, 0x9e, 0xf9, 0x88, 0x79, 0xcb, + 0xa2, 0x8c, 0x66, 0x21, 0xe7, 0x26, 0x88, 0xb1, 0x98, 0x87, 0x9c, 0x00, 0x17, 0x05, +}; + +/* TODO: remove this command once we have the GMM layer implemented */ +DEFUN_HIDDEN(test_llc_unitdata_req_gmm_attch, + test_llc_unitdata_req_gmm_attch_cmd, + "test MS_NAME llc unitdata-req gmm-attach-req", + TEST_CMD_DESC MS_NAME_DESC LLC_CMDG_DESC + "Enqueue an LLC UNITDATA.req for transmission\n" + "Hard-coded GMM Attach Request (SAPI=GMM, TLLI=0xe1c5d364)\n") +{ + struct osmo_gprs_llc_prim *llc_prim; + const uint32_t tlli = 0xe1c5d364; + struct osmocom_ms *ms; + + if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) + return CMD_WARNING; + + llc_prim = osmo_gprs_llc_prim_alloc_ll_unitdata_req(tlli, OSMO_GPRS_LLC_SAPI_GMM, + &pdu_gmmm_attach_req[0], + sizeof(pdu_gmmm_attach_req)); + if (osmo_gprs_llc_prim_upper_down(llc_prim) != 0) { + vty_out(vty, "Failed to enqueue an LLC PDU%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(test_gmm_reg_attach, + test_gmm_reg_attach_cmd, + "test MS_NAME gmm attach", + TEST_CMD_DESC MS_NAME_DESC GMM_CMDG_DESC + "Enqueue a GMM GMMREG-ATTACH.req for transmission\n") +{ + struct osmocom_ms *ms; + + if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) { + vty_out(vty, "Failed to find ms '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (modem_gmm_gmmreg_attach_req(ms) < 0) { + vty_out(vty, "Failed to enqueue a GMM PDU%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(test_gmm_reg_detach, + test_gmm_reg_detach_cmd, + "test MS_NAME gmm detach", + TEST_CMD_DESC MS_NAME_DESC GMM_CMDG_DESC + "Enqueue a GMM GMMREG-DETACH.req for transmission\n") +{ + struct osmocom_ms *ms; + + if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) { + vty_out(vty, "Failed to find ms '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + if (modem_gmm_gmmreg_detach_req(ms) < 0) { + vty_out(vty, "Failed to enqueue a GMM PDU%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(test_sm_act_pdp_ctx, + test_sm_act_pdp_ctx_cmd, + "test MS_NAME sm act-pdp-ctx APN", + TEST_CMD_DESC MS_NAME_DESC SM_CMDG_DESC + "Enqueue a SM SMREG-ACTIVATE.req for transmission\n" + "APN to activate\n") +{ + struct osmocom_ms *ms; + struct osmobb_apn *apn; + + if ((ms = l23_vty_get_ms(argv[0], vty)) == NULL) { + vty_out(vty, "Unable to find MS '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + apn = ms_find_apn_by_name(ms, argv[1]); + if (!apn) { + vty_out(vty, "Unable to find APN '%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (modem_sm_smreg_pdp_act_req(ms, apn) < 0) { + vty_out(vty, "Failed submitting SM PDP Act Req%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* per APN config */ +DEFUN(cfg_ms_apn, cfg_ms_apn_cmd, "apn APN_NAME", + "Configure an APN\n" + "Name of APN\n") +{ + struct osmocom_ms *ms = vty->index; + struct osmobb_apn *apn; + + apn = ms_find_apn_by_name(ms, argv[0]); + if (!apn) + apn = apn_alloc(ms, argv[0]); + if (!apn) { + vty_out(vty, "Unable to create APN '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + vty->index = apn; + vty->node = APN_NODE; + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_no_apn, cfg_ms_no_apn_cmd, "no apn APN_NAME", + NO_STR "Configure an APN\n" + "Name of APN\n") +{ + struct osmocom_ms *ms = vty->index; + struct osmobb_apn *apn; + bool on = false; + + apn = ms_find_apn_by_name(ms, argv[0]); + if (!apn) { + vty_out(vty, "Unable to find APN '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Disable APN before getting rid of it. */ + osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on); + + apn_free(apn); + + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd, + "tun-device NAME", + "Configure tun device name\n" + "TUN device name") +{ + struct osmobb_apn *apn = (struct osmobb_apn *) vty->index; + osmo_talloc_replace_string(apn, &apn->cfg.dev_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_tun_netns_name, cfg_apn_tun_netns_name_cmd, + "tun-netns NAME", + "Configure tun device network namespace name\n" + "TUN device network namespace name") +{ + struct osmobb_apn *apn = (struct osmobb_apn *) vty->index; + osmo_talloc_replace_string(apn, &apn->cfg.dev_netns_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_no_tun_netns_name, cfg_apn_no_tun_netns_name_cmd, + "no tun-netns", + "Configure tun device to use default network namespace name\n") +{ + struct osmobb_apn *apn = (struct osmobb_apn *) vty->index; + TALLOC_FREE(apn->cfg.dev_netns_name); + return CMD_SUCCESS; +} + +static const struct value_string pdp_type_names[] = { + { APN_TYPE_IPv4, "v4" }, + { APN_TYPE_IPv6, "v6" }, + { APN_TYPE_IPv4v6, "v4v6" }, + { 0, NULL } +}; + +#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \ + "IPv6(-only) PDP Type\n" \ + "IPv4v6 (dual-stack) PDP Type\n" + +DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd, + "type-support (v4|v6|v4v6)", + "Enable support for PDP Type\n" + V4V6V46_STRING) +{ + struct osmobb_apn *apn = (struct osmobb_apn *) vty->index; + uint32_t type = get_string_value(pdp_type_names, argv[0]); + + apn->cfg.apn_type_mask |= type; + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd, + "shutdown", + "Put the APN in administrative shut-down\n") +{ + struct osmobb_apn *apn = (struct osmobb_apn *) vty->index; + bool on = false; + int rc; + + if (!apn->cfg.shutdown) { + rc = osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on); + if (rc < 0) { + vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE); + return CMD_WARNING; + } + apn->cfg.shutdown = true; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd, + "no shutdown", + NO_STR "Remove the APN from administrative shut-down\n") +{ + struct osmobb_apn *apn = (struct osmobb_apn *) vty->index; + bool on = true; + int rc; + + if (apn->cfg.shutdown) { + if (!apn->cfg.dev_name) { + vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + rc = osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_GPRS_ALLOWED, &on); + if (rc < 0) { + vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE); + return CMD_WARNING; + } + apn->cfg.shutdown = false; + } + + return CMD_SUCCESS; +} + +static void config_write_apn(struct vty *vty, const struct osmobb_apn *apn) +{ + unsigned int i; + + vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE); + + if (apn->cfg.dev_name) + vty_out(vty, " tun-device %s%s", apn->cfg.dev_name, VTY_NEWLINE); + if (apn->cfg.dev_netns_name) + vty_out(vty, " tun-netns %s%s", apn->cfg.dev_netns_name, VTY_NEWLINE); + + for (i = 0; i < 32; i++) { + if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i))) + continue; + vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)), + VTY_NEWLINE); + } + + /* must be last */ + vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE); +} + +static void config_write_ms(struct vty *vty, const struct osmocom_ms *ms) +{ + struct osmobb_apn *apn; + + vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE); + + l23_vty_config_write_ms_node_contents(vty, ms, " "); + + llist_for_each_entry(apn, &ms->gprs.apn_list, list) + config_write_apn(vty, apn); + + l23_vty_config_write_ms_node_contents_final(vty, ms, " "); +} + +static int config_write(struct vty *vty) +{ + struct osmocom_ms *ms; + llist_for_each_entry(ms, &ms_list, entity) + config_write_ms(vty, ms); + return CMD_SUCCESS; +} + +int modem_vty_init(void) +{ + int rc; + + if ((rc = l23_vty_init(config_write, NULL)) < 0) + return rc; + install_element_ve(&l23_show_ms_cmd); + install_element_ve(&test_grr_tx_chan_req_cmd); + install_element_ve(&test_llc_unitdata_req_hexpdu_cmd); + install_element_ve(&test_llc_unitdata_req_gmm_attch_cmd); + install_element_ve(&test_gmm_reg_attach_cmd); + install_element_ve(&test_gmm_reg_detach_cmd); + install_element_ve(&test_sm_act_pdp_ctx_cmd); + install_element(CONFIG_NODE, &l23_cfg_ms_cmd); + + install_element(MS_NODE, &cfg_ms_apn_cmd); + install_element(MS_NODE, &cfg_ms_no_apn_cmd); + install_node(&apn_node, NULL); + install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd); + install_element(APN_NODE, &cfg_apn_tun_netns_name_cmd); + install_element(APN_NODE, &cfg_apn_no_tun_netns_name_cmd); + install_element(APN_NODE, &cfg_apn_type_support_cmd); + install_element(APN_NODE, &cfg_apn_shutdown_cmd); + install_element(APN_NODE, &cfg_apn_no_shutdown_cmd); + + return 0; +} diff --git a/src/host/osmocon/configure.ac b/src/host/osmocon/configure.ac index a42f4874..4195ecd7 100644 --- a/src/host/osmocon/configure.ac +++ b/src/host/osmocon/configure.ac @@ -5,6 +5,8 @@ AC_INIT([osmocon], AM_INIT_AUTOMAKE([dist-bzip2]) +CFLAGS="$CFLAGS -std=gnu11" + dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) @@ -14,7 +16,7 @@ AC_PROG_CC AC_PROG_INSTALL dnl checks for libraries -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0) dnl checks for header files AC_HEADER_STDC @@ -42,6 +44,7 @@ AC_ARG_ENABLE(werror, if test x"$werror" = x"yes" then WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition" WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" CFLAGS="$CFLAGS $WERROR_FLAGS" diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c index d49c3fdf..547f49fe 100644 --- a/src/host/osmocon/osmocon.c +++ b/src/host/osmocon/osmocon.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <ctype.h> diff --git a/src/host/osmocon/osmoload.c b/src/host/osmocon/osmoload.c index d320b298..b2f14bbd 100644 --- a/src/host/osmocon/osmoload.c +++ b/src/host/osmocon/osmoload.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <errno.h> diff --git a/src/host/osmocon/tpu_debug.c b/src/host/osmocon/tpu_debug.c index 9160b014..82c8d30f 100644 --- a/src/host/osmocon/tpu_debug.c +++ b/src/host/osmocon/tpu_debug.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/host/trxcon/.gitignore b/src/host/trxcon/.gitignore index fe90e43c..b5c3a99d 100644 --- a/src/host/trxcon/.gitignore +++ b/src/host/trxcon/.gitignore @@ -9,6 +9,11 @@ install-sh missing compile +# libtool by-products +ltmain.sh +libtool +m4/*.m4 + # configure by-products .deps/ Makefile @@ -18,9 +23,11 @@ version.h # build by-products *.o +*.lo *.a +*.la -trxcon +/src/trxcon # various .version diff --git a/src/host/trxcon/Makefile.am b/src/host/trxcon/Makefile.am index b51db02f..5b1002c8 100644 --- a/src/host/trxcon/Makefile.am +++ b/src/host/trxcon/Makefile.am @@ -1,52 +1,21 @@ AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 -# versioning magic -BUILT_SOURCES = $(top_srcdir)/.version -$(top_srcdir)/.version: - echo $(VERSION) > $@-t && mv $@-t $@ -dist-hook: - echo $(VERSION) > $(distdir)/.tarball-version - -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOCODING_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ +SUBDIRS = \ + include \ + src \ $(NULL) -bin_PROGRAMS = trxcon +ACLOCAL_AMFLAGS = -I m4 -trxcon_SOURCES = \ - l1ctl_link.c \ - l1ctl.c \ - trx_if.c \ - logging.c \ - trxcon.c \ +BUILT_SOURCES = \ + $(top_srcdir)/.version \ $(NULL) - -# Scheduler -trxcon_SOURCES += \ - sched_lchan_common.c \ - sched_lchan_pdtch.c \ - sched_lchan_desc.c \ - sched_lchan_xcch.c \ - sched_lchan_tchf.c \ - sched_lchan_tchh.c \ - sched_lchan_rach.c \ - sched_lchan_sch.c \ - sched_mframe.c \ - sched_clck.c \ - sched_prim.c \ - sched_trx.c \ +EXTRA_DIST = \ + .version \ $(NULL) -trxcon_LDADD = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOCODING_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(NULL) +# versioning magic +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version diff --git a/src/host/trxcon/configure.ac b/src/host/trxcon/configure.ac index 1f24260d..6508689b 100644 --- a/src/host/trxcon/configure.ac +++ b/src/host/trxcon/configure.ac @@ -2,6 +2,8 @@ dnl Process this file with autoconf to produce a configure script AC_INIT([trxcon], [0.0.0]) AM_INIT_AUTOMAKE +CFLAGS="$CFLAGS -std=gnu11" + dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) @@ -18,6 +20,9 @@ PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) dnl checks for header files AC_HEADER_STDC +dnl init libtool +LT_INIT + AC_ARG_ENABLE(sanitize, [AS_HELP_STRING( [--enable-sanitize], @@ -29,7 +34,36 @@ then CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" fi +AC_ARG_ENABLE(werror, + [AS_HELP_STRING( + [--enable-werror], + [Turn all compiler warnings into errors, with exceptions: + a) deprecation (allow upstream to mark deprecation without breaking builds); + b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) + ] + )], + [werror=$enableval], [werror="no"]) +if test x"$werror" = x"yes" +then + WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition" + WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" + WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" + CFLAGS="$CFLAGS $WERROR_FLAGS" + CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" +fi + +AC_MSG_RESULT([CFLAGS="$CFLAGS"]) +AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) + dnl Checks for typedefs, structures and compiler characteristics -AC_OUTPUT( - Makefile) +AC_CONFIG_MACRO_DIRS([m4]) +AC_CONFIG_FILES([include/Makefile + include/osmocom/Makefile + include/osmocom/bb/Makefile + include/osmocom/bb/l1sched/Makefile + include/osmocom/bb/trxcon/Makefile + src/Makefile + Makefile]) +AC_OUTPUT diff --git a/src/host/trxcon/include/Makefile.am b/src/host/trxcon/include/Makefile.am new file mode 100644 index 00000000..9d963a02 --- /dev/null +++ b/src/host/trxcon/include/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + osmocom \ + $(NULL) diff --git a/src/host/trxcon/include/osmocom/Makefile.am b/src/host/trxcon/include/osmocom/Makefile.am new file mode 100644 index 00000000..83c6385c --- /dev/null +++ b/src/host/trxcon/include/osmocom/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + bb \ + $(NULL) diff --git a/src/host/trxcon/include/osmocom/bb/Makefile.am b/src/host/trxcon/include/osmocom/bb/Makefile.am new file mode 100644 index 00000000..4a575ffb --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = \ + l1sched \ + trxcon \ + $(NULL) + +noinst_HEADERS = \ + l1ctl_proto.h \ + l1gprs.h \ + $(NULL) diff --git a/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h b/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h new file mode 120000 index 00000000..ee19b80e --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1ctl_proto.h @@ -0,0 +1 @@ +../../../../../../include/l1ctl_proto.h
\ No newline at end of file diff --git a/src/host/trxcon/include/osmocom/bb/l1gprs.h b/src/host/trxcon/include/osmocom/bb/l1gprs.h new file mode 120000 index 00000000..3bf85176 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1gprs.h @@ -0,0 +1 @@ +../../../../../../include/l1gprs.h
\ No newline at end of file diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am new file mode 100644 index 00000000..39c32ba0 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1sched/Makefile.am @@ -0,0 +1,5 @@ +noinst_HEADERS = \ + l1sched.h \ + logging.h \ + prim.h \ + $(NULL) diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h new file mode 100644 index 00000000..6c5a31e8 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h @@ -0,0 +1,417 @@ +#pragma once + +#include <time.h> +#include <stdint.h> +#include <stdbool.h> + +#include <arpa/inet.h> + +#include <osmocom/core/bits.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsm0502.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/timer.h> + +#include <osmocom/bb/l1sched/prim.h> + +#define GPRS_L2_MAX_LEN 54 +#define EDGE_L2_MAX_LEN 155 + +#define L1SCHED_CH_LID_DEDIC 0x00 +#define L1SCHED_CH_LID_SACCH 0x40 + +/* Osmocom-specific extension for PTCCH (see 3GPP TS 45.002, section 3.3.4.2). + * Shall be used to distinguish PTCCH and PDTCH channels on a PDCH time-slot. */ +#define L1SCHED_CH_LID_PTCCH 0x80 + +/* Is a channel related to PDCH (GPRS) */ +#define L1SCHED_CH_FLAG_PDCH (1 << 0) +/* Should a channel be activated automatically */ +#define L1SCHED_CH_FLAG_AUTO (1 << 1) + +#define MAX_A5_KEY_LEN (128 / 8) +#define TRX_TS_COUNT 8 + +struct l1sched_lchan_state; +struct l1sched_meas_set; +struct l1sched_state; +struct l1sched_ts; + +enum l1sched_burst_type { + L1SCHED_BURST_GMSK, + L1SCHED_BURST_8PSK, +}; + +/** + * These types define the different channels on a multiframe. + * Each channel has queues and can be activated individually. + */ +enum l1sched_lchan_type { + L1SCHED_IDLE = 0, + L1SCHED_FCCH, + L1SCHED_SCH, + L1SCHED_BCCH, + L1SCHED_RACH, + L1SCHED_CCCH, + L1SCHED_TCHF, + L1SCHED_TCHH_0, + L1SCHED_TCHH_1, + L1SCHED_SDCCH4_0, + L1SCHED_SDCCH4_1, + L1SCHED_SDCCH4_2, + L1SCHED_SDCCH4_3, + L1SCHED_SDCCH8_0, + L1SCHED_SDCCH8_1, + L1SCHED_SDCCH8_2, + L1SCHED_SDCCH8_3, + L1SCHED_SDCCH8_4, + L1SCHED_SDCCH8_5, + L1SCHED_SDCCH8_6, + L1SCHED_SDCCH8_7, + L1SCHED_SACCHTF, + L1SCHED_SACCHTH_0, + L1SCHED_SACCHTH_1, + L1SCHED_SACCH4_0, + L1SCHED_SACCH4_1, + L1SCHED_SACCH4_2, + L1SCHED_SACCH4_3, + L1SCHED_SACCH8_0, + L1SCHED_SACCH8_1, + L1SCHED_SACCH8_2, + L1SCHED_SACCH8_3, + L1SCHED_SACCH8_4, + L1SCHED_SACCH8_5, + L1SCHED_SACCH8_6, + L1SCHED_SACCH8_7, + L1SCHED_PDTCH, + L1SCHED_PTCCH, + L1SCHED_SDCCH4_CBCH, + L1SCHED_SDCCH8_CBCH, + _L1SCHED_CHAN_MAX +}; + +/* Represents a burst to be transmitted */ +struct l1sched_burst_req { + uint32_t fn; + uint8_t tn; + uint8_t pwr; + + /* Internally used by the scheduler */ + uint8_t bid; + + ubit_t burst[GSM_NBITS_NB_8PSK_BURST]; + size_t burst_len; +}; + +/* Represents a received burst */ +struct l1sched_burst_ind { + uint32_t fn; + uint8_t tn; + + /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */ + int16_t toa256; + /*! RSSI (Received Signal Strength Indication) */ + int8_t rssi; + + /* Internally used by the scheduler */ + uint8_t bid; + + sbit_t burst[GSM_NBITS_NB_8PSK_BURST]; + size_t burst_len; +}; + +/* Probed lchan is active */ +#define L1SCHED_PROBE_F_ACTIVE (1 << 0) + +/* RTR (Ready-to-Receive) probe */ +struct l1sched_probe { + uint32_t flags; /* see L1SCHED_PROBE_F_* above */ + uint32_t fn; + uint8_t tn; +}; + +typedef int l1sched_lchan_rx_func(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); + +typedef int l1sched_lchan_tx_func(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); + +struct l1sched_lchan_desc { + /*! Human-readable name */ + const char *name; + /*! Human-readable description */ + const char *desc; + + /*! Channel Number (like in RSL) */ + uint8_t chan_nr; + /*! Link ID (like in RSL) */ + uint8_t link_id; + + /*! How much memory do we need to store bursts */ + size_t burst_buf_size; + /*! Channel specific flags */ + uint8_t flags; + + /*! Function to call when burst received from PHY */ + l1sched_lchan_rx_func *rx_fn; + /*! Function to call when data received from L2 */ + l1sched_lchan_tx_func *tx_fn; +}; + +struct l1sched_tdma_frame { + /*! Downlink channel (slot) type */ + enum l1sched_lchan_type dl_chan; + /*! Downlink block ID */ + uint8_t dl_bid; + /*! Uplink channel (slot) type */ + enum l1sched_lchan_type ul_chan; + /*! Uplink block ID */ + uint8_t ul_bid; +}; + +struct l1sched_tdma_multiframe { + /*! Channel combination */ + enum gsm_phys_chan_config chan_config; + /*! Human-readable name */ + const char *name; + /*! Repeats how many frames */ + uint8_t period; + /*! Applies to which timeslots */ + uint8_t slotmask; + /*! Contains which lchans */ + uint64_t lchan_mask; + /*! Pointer to scheduling structure */ + const struct l1sched_tdma_frame *frames; +}; + +struct l1sched_meas_set { + /*! TDMA frame number of the first burst this set belongs to */ + uint32_t fn; + /*! ToA256 (Timing of Arrival, 1/256 of a symbol) */ + int16_t toa256; + /*! RSSI (Received Signal Strength Indication) */ + int8_t rssi; +}; + +/* Simple ring buffer (up to 24 unique measurements) */ +struct l1sched_lchan_meas_hist { + struct l1sched_meas_set buf[24]; + struct l1sched_meas_set *head; +}; + +/* States each channel on a multiframe */ +struct l1sched_lchan_state { + /*! Channel type */ + enum l1sched_lchan_type type; + /*! Channel status */ + uint8_t active; + /*! Link to a list of channels */ + struct llist_head list; + + /*! Burst type: GMSK or 8PSK */ + enum l1sched_burst_type burst_type; + /*! Mask of received bursts */ + uint32_t rx_burst_mask; + /*! Mask of transmitted bursts */ + uint32_t tx_burst_mask; + /*! Burst buffer for RX */ + sbit_t *rx_bursts; + /*! Burst buffer for TX */ + ubit_t *tx_bursts; + + /*! Queue of Tx primitives */ + struct llist_head tx_prims; + /*! Tx primitive being sent */ + struct msgb *prim; + + /*! Mode for TCH channels (see GSM48_CMODE_*) */ + uint8_t tch_mode; + /*! Training Sequence Code */ + uint8_t tsc; + + /*! FACCH/H on downlink */ + bool dl_ongoing_facch; + /*! pending FACCH/H blocks on Uplink */ + uint8_t ul_facch_blocks; + + /*! Downlink measurements history */ + struct l1sched_lchan_meas_hist meas_hist; + /*! AVG measurements of the last received block */ + struct l1sched_meas_set meas_avg; + + /*! TDMA loss detection state */ + struct { + /*! Last processed TDMA frame number */ + uint32_t last_proc; + /*! Number of processed TDMA frames */ + unsigned long num_proc; + /*! Number of lost TDMA frames */ + unsigned long num_lost; + } tdma; + + /*! SACCH state */ + struct { + /*! Cached measurement report (last received) */ + uint8_t mr_cache[GSM_MACBLOCK_LEN]; + /*! Cache usage counter */ + uint8_t mr_cache_usage; + /*! Was a MR transmitted last time? */ + bool mr_tx_last; + } sacch; + + /* AMR specific */ + struct { + /*! 4 possible codecs for AMR */ + uint8_t codec[4]; + /*! Number of possible codecs */ + uint8_t codecs; + /*! Current uplink FT index */ + uint8_t ul_ft; + /*! Current downlink FT index */ + uint8_t dl_ft; + /*! Current uplink CMR index */ + uint8_t ul_cmr; + /*! Current downlink CMR index */ + uint8_t dl_cmr; + /*! If AMR loop is enabled */ + uint8_t amr_loop; + /*! Number of bit error rates */ + uint8_t ber_num; + /*! Sum of bit error rates */ + float ber_sum; + /* last received dtx frame type */ + uint8_t last_dtx; + } amr; + + /*! A5/X encryption state */ + struct { + uint8_t key[MAX_A5_KEY_LEN]; + uint8_t key_len; + uint8_t algo; + } a5; + + /* TS that this lchan belongs to */ + struct l1sched_ts *ts; +}; + +struct l1sched_ts { + /*! Timeslot index within a frame (0..7) */ + uint8_t index; + + /*! Pointer to multiframe layout */ + const struct l1sched_tdma_multiframe *mf_layout; + /*! Channel states for logical channels */ + struct llist_head lchans; + /*! Backpointer to the scheduler */ + struct l1sched_state *sched; +}; + +/*! Scheduler configuration */ +struct l1sched_cfg { + /*! Logging context (used as prefix for messages) */ + const char *log_prefix; +}; + +/*! One scheduler instance */ +struct l1sched_state { + /*! List of timeslots maintained by this scheduler */ + struct l1sched_ts *ts[TRX_TS_COUNT]; + /*! SACCH cache (common for all lchans) */ + uint8_t sacch_cache[GSM_MACBLOCK_LEN]; + /*! BSIC value learned from SCH bursts */ + uint8_t bsic; + /*! Logging context (used as prefix for messages) */ + const char *log_prefix; + /*! Some private data */ + void *priv; +}; + +extern const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX]; +const struct l1sched_tdma_multiframe * +l1sched_mframe_layout(enum gsm_phys_chan_config config, uint8_t tn); + +/* Scheduler management functions */ +struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv); +void l1sched_reset(struct l1sched_state *sched, bool reset_clock); +void l1sched_free(struct l1sched_state *sched); + +/* Timeslot management functions */ +struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn); +void l1sched_del_ts(struct l1sched_state *sched, int tn); +int l1sched_reset_ts(struct l1sched_state *sched, int tn); +int l1sched_configure_ts(struct l1sched_state *sched, int tn, + enum gsm_phys_chan_config config); +int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo, + const uint8_t *key, uint8_t key_len); + +/* Logical channel management functions */ +enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr); + +void l1sched_deactivate_all_lchans(struct l1sched_ts *ts); +int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr, + int active, uint8_t tch_mode, uint8_t tsc); +int l1sched_lchan_set_amr_cfg(struct l1sched_lchan_state *lchan, + uint8_t codecs_bitmask, uint8_t start_codec); +int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan); +int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan); +struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts, + enum l1sched_lchan_type type); +struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched, + uint8_t chan_nr, uint8_t link_id); + +#define L1SCHED_TCH_MODE_IS_SPEECH(mode) \ + (mode == GSM48_CMODE_SPEECH_V1 \ + || mode == GSM48_CMODE_SPEECH_EFR \ + || mode == GSM48_CMODE_SPEECH_AMR) + +#define L1SCHED_TCH_MODE_IS_DATA(mode) \ + (mode == GSM48_CMODE_DATA_14k5 \ + || mode == GSM48_CMODE_DATA_12k0 \ + || mode == GSM48_CMODE_DATA_6k0 \ + || mode == GSM48_CMODE_DATA_3k6) + +#define L1SCHED_CHAN_IS_TCH(chan) \ + (chan == L1SCHED_TCHF || chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1) + +#define L1SCHED_CHAN_IS_SACCH(chan) \ + (l1sched_lchan_desc[chan].link_id & L1SCHED_CH_LID_SACCH) + +int l1sched_handle_rx_burst(struct l1sched_state *sched, + struct l1sched_burst_ind *bi); +int l1sched_handle_rx_probe(struct l1sched_state *sched, + struct l1sched_probe *probe); +int l1sched_handle_burst_req(struct l1sched_state *sched, + const struct l1sched_burst_req *br); + +/* Shared declarations for lchan handlers */ +extern const uint8_t l1sched_nb_training_bits[8][26]; + +const char *l1sched_burst_mask2str(const uint32_t *mask, int bits); + +/* Interleaved TCH/H block TDMA frame mapping */ +bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan, + uint32_t fn, bool ul, bool facch, bool start); + +#define l1sched_tchh_traffic_start(chan, fn, ul) \ + l1sched_tchh_block_map_fn(chan, fn, ul, 0, 1) +#define l1sched_tchh_traffic_end(chan, fn, ul) \ + l1sched_tchh_block_map_fn(chan, fn, ul, 0, 0) + +#define l1sched_tchh_facch_start(chan, fn, ul) \ + l1sched_tchh_block_map_fn(chan, fn, ul, 1, 1) +#define l1sched_tchh_facch_end(chan, fn, ul) \ + l1sched_tchh_block_map_fn(chan, fn, ul, 1, 0) + +/* Measurement history */ +void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); +void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n); + +/* Clock and Downlink scheduling trigger */ +int l1sched_clck_handle(struct l1sched_state *sched, uint32_t fn); +void l1sched_clck_reset(struct l1sched_state *sched); + +void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br); +void l1sched_pull_send_frame(struct l1sched_state *sched); diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/logging.h b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h new file mode 100644 index 00000000..21be9955 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h @@ -0,0 +1,37 @@ +#pragma once + +extern int l1sched_log_cat_common; +extern int l1sched_log_cat_data; + +void l1sched_logging_init(int log_cat_common, int log_cat_data); + +/* Messages using l1sched_state as the context */ +#define LOGP_SCHED_CAT(sched, cat, level, fmt, args...) \ + LOGP(l1sched_log_cat_##cat, level, "%s" fmt, \ + (sched)->log_prefix, ## args) + +/* Common messages using l1sched_state as the context */ +#define LOGP_SCHEDC(sched, level, fmt, args...) \ + LOGP_SCHED_CAT(sched, common, level, fmt, ## args) + +/* Data messages using l1sched_state as the context */ +#define LOGP_SCHEDD(sched, level, fmt, args...) \ + LOGP_SCHED_CAT(sched, data, level, fmt, ## args) + + +#define LOGP_LCHAN_NAME_FMT "TS%u-%s" +#define LOGP_LCHAN_NAME_ARGS(lchan) \ + (lchan)->ts->index, l1sched_lchan_desc[(lchan)->type].name + +/* Messages using l1sched_lchan_state as the context */ +#define LOGP_LCHAN_CAT(lchan, cat, level, fmt, args...) \ + LOGP_SCHED_CAT((lchan)->ts->sched, cat, level, LOGP_LCHAN_NAME_FMT " " fmt, \ + LOGP_LCHAN_NAME_ARGS(lchan), ## args) + +/* Common messages using l1sched_lchan_state as the context */ +#define LOGP_LCHANC(lchan, level, fmt, args...) \ + LOGP_LCHAN_CAT(lchan, common, level, fmt, ## args) + +/* Data messages using l1sched_lchan_state as the context */ +#define LOGP_LCHAND(lchan, level, fmt, args...) \ + LOGP_LCHAN_CAT(lchan, data, level, fmt, ## args) diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/prim.h b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h new file mode 100644 index 00000000..a9187c2a --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/l1sched/prim.h @@ -0,0 +1,132 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> + +#define l1sched_prim_from_msgb(msg) \ + ((struct l1sched_prim *)(msg)->l1h) + +#define l1sched_prim_data_from_msgb(msg) \ + ((uint8_t *)msgb_l2(msg)) + +#define l1sched_prim_type_from_msgb(msg) \ + l1sched_prim_from_msgb(msg)->oph.primitive + +#define L1SCHED_PRIM_STR_FMT "%s.%s" +#define L1SCHED_PRIM_STR_ARGS(prim) \ + l1sched_prim_type_name((prim)->oph.primitive), \ + osmo_prim_operation_name((prim)->oph.operation) + +enum l1sched_prim_type { + L1SCHED_PRIM_T_DATA, /* Req | Ind | Cnf */ + L1SCHED_PRIM_T_RACH, /* Req | Cnf */ + L1SCHED_PRIM_T_SCH, /* Ind */ + L1SCHED_PRIM_T_PCHAN_COMB, /* Ind */ +}; + +extern const struct value_string l1sched_prim_type_names[]; +static inline const char *l1sched_prim_type_name(enum l1sched_prim_type val) +{ + return get_value_string(l1sched_prim_type_names, val); +} + +/*! Common header for L1SCHED_PRIM_T_{DATA,RACH} */ +struct l1sched_prim_chdr { + /*! TDMA Frame Number */ + uint32_t frame_nr; + /*! RSL Channel Number */ + uint8_t chan_nr; + /*! RSL Link Identifier */ + uint8_t link_id; + /*! Traffic or signalling */ + bool traffic; +}; + +/*! Payload of L1SCHED_PRIM_T_DATA | Ind */ +struct l1sched_prim_data_ind { + /*! Common sub-header */ + struct l1sched_prim_chdr chdr; + int16_t toa256; + int8_t rssi; + int n_errors; + int n_bits_total; +}; + +/*! Payload of L1SCHED_PRIM_T_RACH | {Req,Cnf} */ +struct l1sched_prim_rach { + /*! Common sub-header */ + struct l1sched_prim_chdr chdr; + /*! Training Sequence (only for 11-bit RA) */ + uint8_t synch_seq; + /*! Transmission offset (how many frames to skip) */ + uint8_t offset; + /*! RA value is 11 bit */ + bool is_11bit; + /*! RA value */ + uint16_t ra; +}; + +struct l1sched_prim { + /*! Primitive header */ + struct osmo_prim_hdr oph; + /*! Type specific header */ + union { + /*! L1SCHED_PRIM_T_DATA | Req */ + struct l1sched_prim_chdr data_req; + /*! L1SCHED_PRIM_T_DATA | Cnf */ + struct l1sched_prim_chdr data_cnf; + /*! L1SCHED_PRIM_T_DATA | Ind */ + struct l1sched_prim_data_ind data_ind; + + /*! L1SCHED_PRIM_T_RACH | Req */ + struct l1sched_prim_rach rach_req; + /*! L1SCHED_PRIM_T_RACH | Cnf */ + struct l1sched_prim_rach rach_cnf; + + /*! L1SCHED_PRIM_T_SCH | Ind */ + struct { + /*! TDMA frame number */ + uint32_t frame_nr; + /*! BSIC */ + uint8_t bsic; + } sch_ind; + + /*! L1SCHED_PRIM_T_PCHAN_COMB | Ind */ + struct { + /*! Timeslot number */ + uint8_t tn; + /*! Channel combination for a timeslot */ + enum gsm_phys_chan_config pchan; + } pchan_comb_ind; + }; +}; + + +struct l1sched_state; +struct l1sched_lchan_state; + +void l1sched_prim_init(struct msgb *msg, + enum l1sched_prim_type type, + enum osmo_prim_operation op); + +struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type, + enum osmo_prim_operation op); + +bool l1sched_lchan_amr_prim_is_valid(struct l1sched_lchan_state *lchan, + struct msgb *msg, bool is_cmr); +struct msgb *l1sched_lchan_prim_dequeue_sacch(struct l1sched_lchan_state *lchan); +struct msgb *l1sched_lchan_prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch); +struct msgb *l1sched_lchan_prim_dummy_lapdm(const struct l1sched_lchan_state *lchan); + +int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan, + const uint8_t *data, size_t data_len, + int n_errors, int n_bits_total, bool traffic); +int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan, + struct msgb *msg, uint32_t fn); + +int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg); +int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am b/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am new file mode 100644 index 00000000..ad1e89a0 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/Makefile.am @@ -0,0 +1,9 @@ +noinst_HEADERS = \ + l1ctl_server.h \ + l1ctl.h \ + phyif.h \ + trx_if.h \ + logging.h \ + trxcon.h \ + trxcon_fsm.h \ + $(NULL) diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h new file mode 100644 index 00000000..7e2fa6a5 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl.h @@ -0,0 +1,22 @@ +#pragma once + +#include <stdint.h> + +struct msgb; +struct trxcon_param_rx_data_ind; +struct trxcon_param_tx_data_cnf; +struct trxcon_param_tx_access_burst_cnf; + +int l1ctl_tx_fbsb_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, uint8_t bsic); +int l1ctl_tx_fbsb_fail(struct trxcon_inst *trxcon, uint16_t band_arfcn); +int l1ctl_tx_ccch_mode_conf(struct trxcon_inst *trxcon, uint8_t mode); +int l1ctl_tx_pm_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, int dbm, int last); +int l1ctl_tx_reset_conf(struct trxcon_inst *trxcon, uint8_t type); +int l1ctl_tx_reset_ind(struct trxcon_inst *trxcon, uint8_t type); + +int l1ctl_tx_dt_ind(struct trxcon_inst *trxcon, + const struct trxcon_param_rx_data_ind *ind); +int l1ctl_tx_dt_conf(struct trxcon_inst *trxcon, + const struct trxcon_param_tx_data_cnf *cnf); +int l1ctl_tx_rach_conf(struct trxcon_inst *trxcon, + const struct trxcon_param_tx_access_burst_cnf *cnf); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h new file mode 100644 index 00000000..83c61f02 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h @@ -0,0 +1,67 @@ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/write_queue.h> +#include <osmocom/core/select.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/msgb.h> + +#define L1CTL_LENGTH 512 +#define L1CTL_HEADROOM 32 + +/** + * Each L1CTL message gets its own length pushed + * as two bytes in front before sending. + */ +#define L1CTL_MSG_LEN_FIELD 2 + +struct l1ctl_client; + +typedef int l1ctl_conn_data_func(struct l1ctl_client *, struct msgb *); +typedef void l1ctl_conn_state_func(struct l1ctl_client *); + +struct l1ctl_server_cfg { + /* UNIX socket path to listen on */ + const char *sock_path; + /* maximum number of connected clients */ + unsigned int num_clients_max; + /* functions to be called on various events */ + l1ctl_conn_data_func *conn_read_cb; /* mandatory */ + l1ctl_conn_state_func *conn_accept_cb; /* optional */ + l1ctl_conn_state_func *conn_close_cb; /* optional */ +}; + +struct l1ctl_server { + /* list of connected clients */ + struct llist_head clients; + /* number of connected clients */ + unsigned int num_clients; + /* used for client ID generation */ + unsigned int next_client_id; + /* socket on which we listen for connections */ + struct osmo_fd ofd; + /* server configuration */ + const struct l1ctl_server_cfg *cfg; +}; + +struct l1ctl_client { + /* list head in l1ctl_server.clients */ + struct llist_head list; + /* struct l1ctl_server we belong to */ + struct l1ctl_server *server; + /* client's write queue */ + struct osmo_wqueue wq; + /* logging context (used as prefix for messages) */ + const char *log_prefix; + /* unique client ID */ + unsigned int id; + /* some private data */ + void *priv; +}; + +struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg); +void l1ctl_server_free(struct l1ctl_server *server); + +int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg); +void l1ctl_client_conn_close(struct l1ctl_client *client); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/logging.h b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h new file mode 100644 index 00000000..ce149926 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/logging.h @@ -0,0 +1,16 @@ +#pragma once + +#include <osmocom/core/logging.h> + +enum { + DAPP, + DL1C, + DL1D, + DTRXC, + DTRXD, + DSCH, + DSCHD, + DGPRS, +}; + +int trxcon_logging_init(void *tall_ctx, const char *category_mask); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h b/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h new file mode 100644 index 00000000..2ad7a678 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/phyif.h @@ -0,0 +1,120 @@ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/bits.h> + +/* PHYIF command type */ +enum trxcon_phyif_cmd_type { + TRXCON_PHYIF_CMDT_RESET, + TRXCON_PHYIF_CMDT_POWERON, + TRXCON_PHYIF_CMDT_POWEROFF, + TRXCON_PHYIF_CMDT_MEASURE, + TRXCON_PHYIF_CMDT_SETFREQ_H0, + TRXCON_PHYIF_CMDT_SETFREQ_H1, + TRXCON_PHYIF_CMDT_SETSLOT, + TRXCON_PHYIF_CMDT_SETTA, +}; + +/* param of TRXCON_PHYIF_CMDT_SETFREQ_H0 */ +struct trxcon_phyif_cmdp_setfreq_h0 { + uint16_t band_arfcn; +}; + +/* param of TRXCON_PHYIF_CMDT_SETFREQ_H1 */ +struct trxcon_phyif_cmdp_setfreq_h1 { + uint8_t hsn; + uint8_t maio; + const uint16_t *ma; + unsigned int ma_len; +}; + +/* param of TRXCON_PHYIF_CMDT_SETSLOT */ +struct trxcon_phyif_cmdp_setslot { + uint8_t tn; + uint8_t pchan; /* enum gsm_phys_chan_config */ +}; + +/* param of TRXCON_PHYIF_CMDT_SETTA */ +struct trxcon_phyif_cmdp_setta { + int8_t ta; /* intentionally signed */ +}; + +/* param of TRXCON_PHYIF_CMDT_MEASURE (command) */ +struct trxcon_phyif_cmdp_measure { + uint16_t band_arfcn; +}; + +/* param of TRXCON_PHYIF_CMDT_MEASURE (response) */ +struct trxcon_phyif_rspp_measure { + uint16_t band_arfcn; + int dbm; +}; + +struct trxcon_phyif_cmd { + enum trxcon_phyif_cmd_type type; + union { + struct trxcon_phyif_cmdp_setfreq_h0 setfreq_h0; + struct trxcon_phyif_cmdp_setfreq_h1 setfreq_h1; + struct trxcon_phyif_cmdp_setslot setslot; + struct trxcon_phyif_cmdp_setta setta; + struct trxcon_phyif_cmdp_measure measure; + } param; +}; + +struct trxcon_phyif_rsp { + enum trxcon_phyif_cmd_type type; + union { + struct trxcon_phyif_rspp_measure measure; + } param; +}; + +/* RTS.ind - Ready-to-Send indication */ +struct trxcon_phyif_rts_ind { + uint32_t fn; + uint8_t tn; +}; + +/* RTR.ind - Ready-to-Receive indicaton */ +struct trxcon_phyif_rtr_ind { + uint32_t fn; + uint8_t tn; +}; + +/* The probed lchan is active */ +#define TRXCON_PHYIF_RTR_F_ACTIVE (1 << 0) + +/* RTR.rsp - Ready-to-Receive response */ +struct trxcon_phyif_rtr_rsp { + uint32_t flags; /* see TRXCON_PHYIF_RTR_F_* above */ +}; + +/* BURST.req - a burst to be transmitted */ +struct trxcon_phyif_burst_req { + uint32_t fn; + uint8_t tn; + uint8_t pwr; + const ubit_t *burst; + unsigned int burst_len; +}; + +/* BURST.ind - a received burst */ +struct trxcon_phyif_burst_ind { + uint32_t fn; + uint8_t tn; + int16_t toa256; + int8_t rssi; + const sbit_t *burst; + unsigned int burst_len; +}; + +int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br); +int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *bi); + +int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts); +int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind, + struct trxcon_phyif_rtr_rsp *rsp); + +int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd); +int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp); +void trxcon_phyif_close(void *phyif); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h b/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h new file mode 100644 index 00000000..e564fd8e --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/trx_if.h @@ -0,0 +1,61 @@ +#pragma once + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/select.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/fsm.h> + +#include <osmocom/bb/trxcon/phyif.h> + +#define TRXC_BUF_SIZE 1024 +#define TRXD_BUF_SIZE 512 + +enum trx_fsm_states { + TRX_STATE_OFFLINE = 0, + TRX_STATE_IDLE, + TRX_STATE_ACTIVE, + TRX_STATE_RSP_WAIT, +}; + +struct trx_instance { + struct osmo_fd trx_ofd_ctrl; + struct osmo_fd trx_ofd_data; + + struct osmo_timer_list trx_ctrl_timer; + struct llist_head trx_ctrl_list; + struct osmo_fsm_inst *fi; + uint32_t fn_advance; + + /* HACK: we need proper state machines */ + uint32_t prev_state; + bool powered_up; + + /* Some private data */ + void *priv; +}; + +struct trx_ctrl_msg { + struct llist_head list; + char cmd[TRXC_BUF_SIZE]; + int retry_cnt; + int critical; + int cmd_len; +}; + +struct trx_if_params { + const char *local_host; + const char *remote_host; + uint16_t base_port; + uint32_t fn_advance; + uint8_t instance; + + struct osmo_fsm_inst *parent_fi; + uint32_t parent_term_event; + void *priv; +}; + +struct trx_instance *trx_if_open(const struct trx_if_params *params); +void trx_if_close(struct trx_instance *trx); + +int trx_if_handle_phyif_burst_req(struct trx_instance *trx, const struct trxcon_phyif_burst_req *br); +int trx_if_handle_phyif_cmd(struct trx_instance *trx, const struct trxcon_phyif_cmd *cmd); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h new file mode 100644 index 00000000..ff54e785 --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h @@ -0,0 +1,64 @@ +#pragma once + +#include <stdint.h> + +struct osmo_fsm_inst; +struct l1sched_state; +struct l1gprs_state; +struct msgb; + +struct trxcon_inst { + struct osmo_fsm_inst *fi; + unsigned int id; + + /* Logging context for sched and l1c */ + const char *log_prefix; + + /* GSMTAP instance (optional) */ + struct gsmtap_inst *gsmtap; + + /* The L1 scheduler */ + struct l1sched_state *sched; + /* GPRS state (MAC layer) */ + struct l1gprs_state *gprs; + + /* PHY interface (e.g. TRXC/TRXD) */ + void *phyif; + /* L2 interface (e.g. L1CTL) */ + void *l2if; + + /* State specific data of trxcon_fsm */ + void *fi_data; + + /* L1 parameters */ + struct { + uint16_t band_arfcn; + uint8_t tx_power; + uint8_t tsc; /* only valid for DCCH/PDCH */ + int8_t ta; + } l1p; + + /* PHY specific quirks */ + struct { + /* FBSB timeout extension (in TDMA FNs) */ + unsigned int fbsb_extend_fns; + } phy_quirks; +}; + +enum trxcon_log_cat { + TRXCON_LOGC_FSM, /* trxcon_fsm */ + TRXCON_LOGC_L1C, /* L1CTL control */ + TRXCON_LOGC_L1D, /* L1CTL data */ + TRXCON_LOGC_SCHC, /* l1sched control */ + TRXCON_LOGC_SCHD, /* l1sched data */ + TRXCON_LOGC_GPRS, /* l1gprs logging */ +}; + +void trxcon_set_log_cfg(const int *logc, unsigned int logc_num); + +struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id); +void trxcon_inst_free(struct trxcon_inst *trxcon); + +int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg); +int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg); +void trxcon_l1ctl_close(struct trxcon_inst *trxcon); diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h new file mode 100644 index 00000000..9eba4fde --- /dev/null +++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon_fsm.h @@ -0,0 +1,169 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/fsm.h> + +extern struct osmo_fsm trxcon_fsm_def; + +enum trxcon_fsm_states { + TRXCON_ST_RESET, + TRXCON_ST_FULL_POWER_SCAN, + TRXCON_ST_FBSB_SEARCH, + TRXCON_ST_BCCH_CCCH, + TRXCON_ST_DEDICATED, + TRXCON_ST_PACKET_DATA, +}; + +enum trxcon_fsm_events { + TRXCON_EV_PHYIF_FAILURE, + TRXCON_EV_L2IF_FAILURE, + TRXCON_EV_RESET_FULL_REQ, + TRXCON_EV_RESET_SCHED_REQ, + TRXCON_EV_FULL_POWER_SCAN_REQ, + TRXCON_EV_FULL_POWER_SCAN_RES, + TRXCON_EV_FBSB_SEARCH_REQ, + TRXCON_EV_FBSB_SEARCH_RES, + TRXCON_EV_SET_CCCH_MODE_REQ, + TRXCON_EV_SET_TCH_MODE_REQ, + TRXCON_EV_SET_PHY_CONFIG_REQ, + TRXCON_EV_TX_ACCESS_BURST_REQ, + TRXCON_EV_TX_ACCESS_BURST_CNF, + TRXCON_EV_UPDATE_SACCH_CACHE_REQ, + TRXCON_EV_DCH_EST_REQ, + TRXCON_EV_DCH_REL_REQ, + TRXCON_EV_TX_DATA_REQ, + TRXCON_EV_TX_DATA_CNF, + TRXCON_EV_RX_DATA_IND, + TRXCON_EV_CRYPTO_REQ, + TRXCON_EV_GPRS_UL_TBF_CFG_REQ, /* param: L1CTL msgb */ + TRXCON_EV_GPRS_DL_TBF_CFG_REQ, /* param: L1CTL msgb */ + TRXCON_EV_GPRS_UL_BLOCK_REQ, /* param: L1CTL msgb */ +}; + +/* param of TRXCON_EV_FULL_POWER_SCAN_REQ */ +struct trxcon_param_full_power_scan_req { + uint16_t band_arfcn_start; + uint16_t band_arfcn_stop; +}; + +/* param of TRXCON_EV_FULL_POWER_SCAN_RES */ +struct trxcon_param_full_power_scan_res { + uint16_t band_arfcn; + int dbm; +}; + +/* param of TRXCON_EV_FBSB_SEARCH_REQ */ +struct trxcon_param_fbsb_search_req { + uint16_t band_arfcn; + uint16_t timeout_fns; /* in TDMA Fn periods */ + uint8_t pchan_config; +}; + +/* param of TRXCON_EV_SET_{CCCH,TCH}_MODE_REQ */ +struct trxcon_param_set_ccch_tch_mode_req { + uint8_t mode; + struct { + uint8_t start_codec; + uint8_t codecs_bitmask; + } amr; + bool applied; +}; + +/* param of TRXCON_EV_SET_PHY_CONFIG_REQ */ +struct trxcon_param_set_phy_config_req { + enum { + TRXCON_PHY_CFGT_PCHAN_COMB, + TRXCON_PHY_CFGT_TX_PARAMS, + } type; + union { + struct { + uint8_t tn; + uint8_t pchan; + } pchan_comb; + struct { + uint8_t timing_advance; + uint8_t tx_power; + } tx_params; + }; +}; + +/* param of TRXCON_EV_TX_DATA_REQ */ +struct trxcon_param_tx_data_req { + bool traffic; + uint8_t chan_nr; + uint8_t link_id; + size_t data_len; + const uint8_t *data; +}; + +/* param of TRXCON_EV_TX_DATA_CNF */ +struct trxcon_param_tx_data_cnf { + bool traffic; + uint8_t chan_nr; + uint8_t link_id; + uint16_t band_arfcn; + uint32_t frame_nr; + size_t data_len; + const uint8_t *data; +}; + +/* param of TRXCON_EV_RX_DATA_IND */ +struct trxcon_param_rx_data_ind { + bool traffic; + uint8_t chan_nr; + uint8_t link_id; + uint16_t band_arfcn; + uint32_t frame_nr; + int16_t toa256; + int8_t rssi; + int n_errors; + int n_bits_total; + size_t data_len; + const uint8_t *data; +}; + +/* param of TRXCON_EV_TX_ACCESS_BURST_REQ */ +struct trxcon_param_tx_access_burst_req { + uint8_t chan_nr; + uint8_t link_id; + uint8_t offset; + uint8_t synch_seq; + uint16_t ra; + bool is_11bit; +}; + +/* param of TRXCON_EV_TX_ACCESS_BURST_CNF */ +struct trxcon_param_tx_access_burst_cnf { + uint16_t band_arfcn; + uint32_t frame_nr; +}; + +/* param of TRXCON_EV_DCH_EST_REQ */ +struct trxcon_param_dch_est_req { + uint8_t chan_nr; + uint8_t tch_mode; + uint8_t tsc; + + bool hopping; + union { + struct { /* hopping=false */ + uint16_t band_arfcn; + } h0; + struct { /* hopping=true */ + uint8_t hsn; + uint8_t maio; + uint8_t n; + uint16_t ma[64]; + } h1; + }; +}; + +/* param of TRXCON_EV_CRYPTO_REQ */ +struct trxcon_param_crypto_req { + uint8_t chan_nr; + uint8_t a5_algo; /* 0 is A5/0 */ + uint8_t key_len; + const uint8_t *key; +}; diff --git a/src/host/trxcon/l1ctl.c b/src/host/trxcon/l1ctl.c deleted file mode 100644 index 8083595f..00000000 --- a/src/host/trxcon/l1ctl.c +++ /dev/null @@ -1,916 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * GSM L1 control interface handlers - * - * (C) 2014 by Sylvain Munaut <tnt@246tNt.com> - * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <stdio.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <assert.h> - -#include <arpa/inet.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/protocol/gsm_08_58.h> - -#include "logging.h" -#include "l1ctl_link.h" -#include "l1ctl_proto.h" - -#include "trx_if.h" -#include "sched_trx.h" - -static const char *arfcn2band_name(uint16_t arfcn) -{ - enum gsm_band band; - - if (gsm_arfcn2band_rc(arfcn, &band) < 0) - return "(invalid)"; - - return gsm_band_name(band); -} - -static struct msgb *l1ctl_alloc_msg(uint8_t msg_type) -{ - struct l1ctl_hdr *l1h; - struct msgb *msg; - - /** - * Each L1CTL message gets its own length pushed in front - * before sending. This is why we need this small headroom. - */ - msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_MSG_LEN_FIELD, - L1CTL_MSG_LEN_FIELD, "l1ctl_tx_msg"); - if (!msg) { - LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); - return NULL; - } - - msg->l1h = msgb_put(msg, sizeof(*l1h)); - l1h = (struct l1ctl_hdr *) msg->l1h; - l1h->msg_type = msg_type; - - return msg; -} - -int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, - int dbm, int last) -{ - struct l1ctl_pm_conf *pmc; - struct msgb *msg; - - msg = l1ctl_alloc_msg(L1CTL_PM_CONF); - if (!msg) - return -ENOMEM; - - LOGP(DL1C, LOGL_DEBUG, "Send PM Conf (%s %d = %d dBm)\n", - arfcn2band_name(band_arfcn), - band_arfcn &~ ARFCN_FLAG_MASK, dbm); - - pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc)); - pmc->band_arfcn = htons(band_arfcn); - pmc->pm[0] = dbm2rxlev(dbm); - pmc->pm[1] = 0; - - if (last) { - struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h; - l1h->flags |= L1CTL_F_DONE; - } - - return l1ctl_link_send(l1l, msg); -} - -int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type) -{ - struct msgb *msg; - struct l1ctl_reset *res; - - msg = l1ctl_alloc_msg(L1CTL_RESET_IND); - if (!msg) - return -ENOMEM; - - LOGP(DL1C, LOGL_DEBUG, "Send Reset Ind (%u)\n", type); - - res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); - res->type = type; - - return l1ctl_link_send(l1l, msg); -} - -int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type) -{ - struct msgb *msg; - struct l1ctl_reset *res; - - msg = l1ctl_alloc_msg(L1CTL_RESET_CONF); - if (!msg) - return -ENOMEM; - - LOGP(DL1C, LOGL_DEBUG, "Send Reset Conf (%u)\n", type); - res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); - res->type = type; - - return l1ctl_link_send(l1l, msg); -} - -static struct l1ctl_info_dl *put_dl_info_hdr(struct msgb *msg, struct l1ctl_info_dl *dl_info) -{ - size_t len = sizeof(struct l1ctl_info_dl); - struct l1ctl_info_dl *dl = (struct l1ctl_info_dl *) msgb_put(msg, len); - - if (dl_info) /* Copy DL info provided by handler */ - memcpy(dl, dl_info, len); - else /* Init DL info header */ - memset(dl, 0x00, len); - - return dl; -} - -/* Fill in FBSB payload: BSIC and sync result */ -static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result, uint8_t bsic) -{ - struct l1ctl_fbsb_conf *conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf)); - - LOGP(DL1C, LOGL_DEBUG, "Send FBSB Conf (result=%u, bsic=%u)\n", result, bsic); - - conf->result = result; - conf->bsic = bsic; - - return conf; -} - -int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, - struct l1ctl_info_dl *dl_info, uint8_t bsic) -{ - struct l1ctl_fbsb_conf *conf; - struct msgb *msg; - - msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); - if (msg == NULL) - return -ENOMEM; - - put_dl_info_hdr(msg, dl_info); - talloc_free(dl_info); - - conf = fbsb_conf_make(msg, result, bsic); - - /* FIXME: set proper value */ - conf->initial_freq_err = 0; - - /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */ - l1l->fbsb_conf_sent = true; - - /* Abort FBSB expire timer */ - if (osmo_timer_pending(&l1l->fbsb_timer)) - osmo_timer_del(&l1l->fbsb_timer); - - return l1ctl_link_send(l1l, msg); -} - -int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode) -{ - struct l1ctl_ccch_mode_conf *conf; - struct msgb *msg; - - msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF); - if (msg == NULL) - return -ENOMEM; - - conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf)); - conf->ccch_mode = mode; - - return l1ctl_link_send(l1l, msg); -} - -/** - * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND. - */ -int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, - uint8_t *l2, size_t l2_len, bool traffic) -{ - struct msgb *msg; - uint8_t *msg_l2; - - msg = l1ctl_alloc_msg(traffic ? - L1CTL_TRAFFIC_IND : L1CTL_DATA_IND); - if (msg == NULL) - return -ENOMEM; - - put_dl_info_hdr(msg, data); - - /* Copy the L2 payload if preset */ - if (l2 && l2_len > 0) { - msg_l2 = (uint8_t *) msgb_put(msg, l2_len); - memcpy(msg_l2, l2, l2_len); - } - - /* Put message to upper layers */ - return l1ctl_link_send(l1l, msg); -} - -int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, - uint16_t band_arfcn, uint32_t fn) -{ - struct l1ctl_info_dl *dl; - struct msgb *msg; - - msg = l1ctl_alloc_msg(L1CTL_RACH_CONF); - if (msg == NULL) - return -ENOMEM; - - dl = put_dl_info_hdr(msg, NULL); - memset(dl, 0x00, sizeof(*dl)); - - dl->band_arfcn = htons(band_arfcn); - dl->frame_nr = htonl(fn); - - return l1ctl_link_send(l1l, msg); -} - - -/** - * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF. - */ -int l1ctl_tx_dt_conf(struct l1ctl_link *l1l, - struct l1ctl_info_dl *data, bool traffic) -{ - struct msgb *msg; - - msg = l1ctl_alloc_msg(traffic ? - L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF); - if (msg == NULL) - return -ENOMEM; - - /* Copy DL frame header from source message */ - put_dl_info_hdr(msg, data); - - return l1ctl_link_send(l1l, msg); -} - -static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mode) -{ - switch (mode) { - /* TODO: distinguish extended BCCH */ - case CCCH_MODE_NON_COMBINED: - case CCCH_MODE_NONE: - return GSM_PCHAN_CCCH; - - case CCCH_MODE_COMBINED: - return GSM_PCHAN_CCCH_SDCCH4; - case CCCH_MODE_COMBINED_CBCH: - return GSM_PCHAN_CCCH_SDCCH4_CBCH; - - default: - LOGP(DL1C, LOGL_NOTICE, "Undandled CCCH mode (%u), " - "assuming non-combined configuration\n", mode); - return GSM_PCHAN_CCCH; - } -} - -/* FBSB expire timer */ -static void fbsb_timer_cb(void *data) -{ - struct l1ctl_link *l1l = (struct l1ctl_link *) data; - struct l1ctl_info_dl *dl; - struct msgb *msg; - - msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); - if (msg == NULL) - return; - - LOGP(DL1C, LOGL_NOTICE, "FBSB timer fired for ARFCN %u\n", l1l->trx->band_arfcn &~ ARFCN_FLAG_MASK); - - dl = put_dl_info_hdr(msg, NULL); - - /* Fill in current ARFCN */ - dl->band_arfcn = htons(l1l->trx->band_arfcn); - - fbsb_conf_make(msg, 255, 0); - - /* Ask SCH handler not to send L1CTL_FBSB_CONF anymore */ - l1l->fbsb_conf_sent = true; - - l1ctl_link_send(l1l, msg); -} - -static int l1ctl_rx_fbsb_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - enum gsm_phys_chan_config ch_config; - struct l1ctl_fbsb_req *fbsb; - uint16_t band_arfcn; - uint16_t timeout; - int rc = 0; - - fbsb = (struct l1ctl_fbsb_req *) msg->l1h; - if (msgb_l1len(msg) < sizeof(*fbsb)) { - LOGP(DL1C, LOGL_ERROR, "MSG too short FBSB Req: %u\n", - msgb_l1len(msg)); - rc = -EINVAL; - goto exit; - } - - ch_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode); - band_arfcn = ntohs(fbsb->band_arfcn); - timeout = ntohs(fbsb->timeout); - - LOGP(DL1C, LOGL_NOTICE, "Received FBSB request (%s %d)\n", - arfcn2band_name(band_arfcn), - band_arfcn &~ ARFCN_FLAG_MASK); - - /* Reset scheduler and clock counter */ - sched_trx_reset(l1l->trx, true); - - /* Configure a single timeslot */ - sched_trx_configure_ts(l1l->trx, 0, ch_config); - - /* Ask SCH handler to send L1CTL_FBSB_CONF */ - l1l->fbsb_conf_sent = false; - - /* Only if current ARFCN differs */ - if (l1l->trx->band_arfcn != band_arfcn) { - /* Update current ARFCN */ - l1l->trx->band_arfcn = band_arfcn; - - /* Tune transceiver to required ARFCN */ - trx_if_cmd_rxtune(l1l->trx, band_arfcn); - trx_if_cmd_txtune(l1l->trx, band_arfcn); - } - - /* Transceiver might have been powered on before, e.g. - * in case of sending L1CTL_FBSB_REQ due to signal loss. */ - if (!l1l->trx->powered_up) - trx_if_cmd_poweron(l1l->trx); - - /* Start FBSB expire timer */ - l1l->fbsb_timer.data = l1l; - l1l->fbsb_timer.cb = fbsb_timer_cb; - LOGP(DL1C, LOGL_INFO, "Starting FBSB timer %u ms\n", timeout * GSM_TDMA_FN_DURATION_uS / 1000); - osmo_timer_schedule(&l1l->fbsb_timer, 0, - timeout * GSM_TDMA_FN_DURATION_uS); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_pm_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - uint16_t band_arfcn_start, band_arfcn_stop; - struct l1ctl_pm_req *pmr; - int rc = 0; - - pmr = (struct l1ctl_pm_req *) msg->l1h; - if (msgb_l1len(msg) < sizeof(*pmr)) { - LOGP(DL1C, LOGL_ERROR, "MSG too short PM Req: %u\n", - msgb_l1len(msg)); - rc = -EINVAL; - goto exit; - } - - band_arfcn_start = ntohs(pmr->range.band_arfcn_from); - band_arfcn_stop = ntohs(pmr->range.band_arfcn_to); - - LOGP(DL1C, LOGL_NOTICE, "Received power measurement " - "request (%s: %d -> %d)\n", - arfcn2band_name(band_arfcn_start), - band_arfcn_start &~ ARFCN_FLAG_MASK, - band_arfcn_stop &~ ARFCN_FLAG_MASK); - - /* Send measurement request to transceiver */ - rc = trx_if_cmd_measure(l1l->trx, band_arfcn_start, band_arfcn_stop); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_reset_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_reset *res; - int rc = 0; - - res = (struct l1ctl_reset *) msg->l1h; - if (msgb_l1len(msg) < sizeof(*res)) { - LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n", - msgb_l1len(msg)); - rc = -EINVAL; - goto exit; - } - - LOGP(DL1C, LOGL_NOTICE, "Received reset request (%u)\n", - res->type); - - switch (res->type) { - case L1CTL_RES_T_FULL: - /* TODO: implement trx_if_reset() */ - trx_if_cmd_poweroff(l1l->trx); - trx_if_cmd_echo(l1l->trx); - - /* Fall through */ - case L1CTL_RES_T_SCHED: - sched_trx_reset(l1l->trx, true); - break; - default: - LOGP(DL1C, LOGL_ERROR, "Unknown L1CTL_RESET_REQ type\n"); - goto exit; - } - - /* Confirm */ - rc = l1ctl_tx_reset_conf(l1l, res->type); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_echo_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_hdr *l1h; - - LOGP(DL1C, LOGL_NOTICE, "Recv Echo Req\n"); - LOGP(DL1C, LOGL_NOTICE, "Send Echo Conf\n"); - - /* Nothing to do, just send it back */ - l1h = (struct l1ctl_hdr *) msg->l1h; - l1h->msg_type = L1CTL_ECHO_CONF; - msg->data = msg->l1h; - - return l1ctl_link_send(l1l, msg); -} - -static int l1ctl_rx_ccch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - enum gsm_phys_chan_config ch_config; - struct l1ctl_ccch_mode_req *req; - struct trx_ts *ts; - int rc = 0; - - req = (struct l1ctl_ccch_mode_req *) msg->l1h; - if (msgb_l1len(msg) < sizeof(*req)) { - LOGP(DL1C, LOGL_ERROR, "MSG too short Reset Req: %u\n", - msgb_l1len(msg)); - rc = -EINVAL; - goto exit; - } - - LOGP(DL1C, LOGL_NOTICE, "Received CCCH mode request (%u)\n", - req->ccch_mode); /* TODO: add value-string for ccch_mode */ - - /* Make sure that TS0 is allocated and configured */ - ts = l1l->trx->ts_list[0]; - if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DL1C, LOGL_ERROR, "TS0 is not configured"); - rc = -EINVAL; - goto exit; - } - - /* Choose corresponding channel combination */ - ch_config = l1ctl_ccch_mode2pchan_config(req->ccch_mode); - - /* Do nothing if the current mode matches required */ - if (ts->mf_layout->chan_config != ch_config) - rc = sched_trx_configure_ts(l1l->trx, 0, ch_config); - - /* Confirm reconfiguration */ - if (!rc) - rc = l1ctl_tx_ccch_mode_conf(l1l, req->ccch_mode); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_rach_req(struct l1ctl_link *l1l, struct msgb *msg, bool ext) -{ - struct l1ctl_ext_rach_req *ext_req; - struct l1ctl_rach_req *req; - struct l1ctl_info_ul *ul; - struct trx_ts_prim *prim; - size_t len; - int rc; - - ul = (struct l1ctl_info_ul *) msg->l1h; - - /* Is it extended (11-bit) RACH or not? */ - if (ext) { - ext_req = (struct l1ctl_ext_rach_req *) ul->payload; - ext_req->offset = ntohs(ext_req->offset); - ext_req->ra11 = ntohs(ext_req->ra11); - len = sizeof(*ext_req); - - LOGP(DL1C, LOGL_NOTICE, "Received extended (11-bit) RACH request " - "(offset=%u, synch_seq=%u, ra11=0x%02hx)\n", - ext_req->offset, ext_req->synch_seq, ext_req->ra11); - } else { - req = (struct l1ctl_rach_req *) ul->payload; - req->offset = ntohs(req->offset); - len = sizeof(*req); - - LOGP(DL1C, LOGL_NOTICE, "Received regular (8-bit) RACH request " - "(offset=%u, ra=0x%02x)\n", req->offset, req->ra); - } - - /* The controlling L1CTL side always does include the UL info header, - * but may leave it empty. We assume RACH is on TS0 in this case. */ - if (ul->chan_nr == 0x00) { - LOGP(DL1C, LOGL_NOTICE, "The UL info header is empty, " - "assuming RACH is on TS0\n"); - ul->chan_nr = RSL_CHAN_RACH; - } - - /* Init a new primitive */ - rc = sched_prim_init(l1l->trx, &prim, len, ul->chan_nr, ul->link_id); - if (rc) - goto exit; - - /** - * Push this primitive to the transmit queue. - * Indicated timeslot needs to be configured. - */ - rc = sched_prim_push(l1l->trx, prim, ul->chan_nr); - if (rc) { - talloc_free(prim); - goto exit; - } - - /* Fill in the payload */ - memcpy(prim->payload, ul->payload, len); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_proc_est_req_h0(struct trx_instance *trx, struct l1ctl_h0 *h) -{ - uint16_t band_arfcn; - int rc = 0; - - band_arfcn = ntohs(h->band_arfcn); - - LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a single " - "ARFCN=%u channel\n", band_arfcn &~ ARFCN_FLAG_MASK); - - /* Do we need to retune? */ - if (trx->band_arfcn == band_arfcn) - return 0; - - /* Tune transceiver to required ARFCN */ - rc |= trx_if_cmd_rxtune(trx, band_arfcn); - rc |= trx_if_cmd_txtune(trx, band_arfcn); - if (rc) - return rc; - - /* Update current ARFCN */ - trx->band_arfcn = band_arfcn; - - return 0; -} - -static int l1ctl_proc_est_req_h1(struct trx_instance *trx, struct l1ctl_h1 *h) -{ - uint16_t ma[64]; - int i, rc; - - LOGP(DL1C, LOGL_NOTICE, "L1CTL_DM_EST_REQ indicates a Frequency " - "Hopping (hsn=%u, maio=%u, chans=%u) channel\n", - h->hsn, h->maio, h->n); - - /* No channels?!? */ - if (!h->n) { - LOGP(DL1C, LOGL_ERROR, "No channels in mobile allocation?!?\n"); - return -EINVAL; - } else if (h->n > ARRAY_SIZE(ma)) { - LOGP(DL1C, LOGL_ERROR, "More than 64 channels in mobile allocation?!?\n"); - return -EINVAL; - } - - /* Convert from network to host byte order */ - for (i = 0; i < h->n; i++) - ma[i] = ntohs(h->ma[i]); - - /* Forward hopping parameters to TRX */ - rc = trx_if_cmd_setfh(trx, h->hsn, h->maio, ma, h->n); - if (rc) - return rc; - - /** - * TODO: update the state of trx_instance somehow - * in order to indicate that it is in hopping mode... - */ - return 0; -} - -static int l1ctl_rx_dm_est_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - enum gsm_phys_chan_config config; - struct l1ctl_dm_est_req *est_req; - struct l1ctl_info_ul *ul; - struct trx_ts *ts; - uint8_t chan_nr, tn; - int rc; - - ul = (struct l1ctl_info_ul *) msg->l1h; - est_req = (struct l1ctl_dm_est_req *) ul->payload; - - chan_nr = ul->chan_nr; - tn = chan_nr & 0x07; - - LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_EST_REQ " - "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=0x%02x)\n", - tn, chan_nr, est_req->tsc, est_req->tch_mode); - - /* Determine channel config */ - config = sched_trx_chan_nr2pchan_config(chan_nr); - if (config == GSM_PCHAN_NONE) { - LOGP(DL1C, LOGL_ERROR, "Couldn't determine channel config\n"); - rc = -EINVAL; - goto exit; - } - - /* Frequency hopping? */ - if (est_req->h) - rc = l1ctl_proc_est_req_h1(l1l->trx, &est_req->h1); - else /* Single ARFCN */ - rc = l1ctl_proc_est_req_h0(l1l->trx, &est_req->h0); - if (rc) - goto exit; - - /* Update TSC (Training Sequence Code) */ - l1l->trx->tsc = est_req->tsc; - - /* Configure requested TS */ - rc = sched_trx_configure_ts(l1l->trx, tn, config); - ts = l1l->trx->ts_list[tn]; - if (rc) { - rc = -EINVAL; - goto exit; - } - - /* Deactivate all lchans */ - sched_trx_deactivate_all_lchans(ts); - - /* Activate only requested lchans */ - rc = sched_trx_set_lchans(ts, chan_nr, 1, est_req->tch_mode); - if (rc) { - LOGP(DL1C, LOGL_ERROR, "Couldn't activate requested lchans\n"); - rc = -EINVAL; - goto exit; - } - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_dm_rel_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ, " - "switching back to CCCH\n"); - - /* Reset scheduler */ - sched_trx_reset(l1l->trx, false); - - msgb_free(msg); - return 0; -} - -/** - * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ. - */ -static int l1ctl_rx_dt_req(struct l1ctl_link *l1l, - struct msgb *msg, bool traffic) -{ - struct l1ctl_info_ul *ul; - struct trx_ts_prim *prim; - uint8_t chan_nr, link_id; - size_t payload_len; - int rc; - - /* Extract UL frame header */ - ul = (struct l1ctl_info_ul *) msg->l1h; - - /* Calculate the payload len */ - msg->l2h = ul->payload; - payload_len = msgb_l2len(msg); - - /* Obtain channel description */ - chan_nr = ul->chan_nr; - link_id = ul->link_id & 0x40; - - LOGP(DL1D, LOGL_DEBUG, "Recv %s Req (chan_nr=0x%02x, " - "link_id=0x%02x, len=%zu)\n", traffic ? "TRAFFIC" : "DATA", - chan_nr, link_id, payload_len); - - /* Init a new primitive */ - rc = sched_prim_init(l1l->trx, &prim, payload_len, - chan_nr, link_id); - if (rc) - goto exit; - - /* Push this primitive to transmit queue */ - rc = sched_prim_push(l1l->trx, prim, chan_nr); - if (rc) { - talloc_free(prim); - goto exit; - } - - /* Fill in the payload */ - memcpy(prim->payload, ul->payload, payload_len); - -exit: - msgb_free(msg); - return rc; -} - -static int l1ctl_rx_param_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_par_req *par_req; - struct l1ctl_info_ul *ul; - - ul = (struct l1ctl_info_ul *) msg->l1h; - par_req = (struct l1ctl_par_req *) ul->payload; - - LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_PARAM_REQ " - "(ta=%d, tx_power=%u)\n", par_req->ta, par_req->tx_power); - - /* Instruct TRX to use new TA value */ - if (l1l->trx->ta != par_req->ta) { - trx_if_cmd_setta(l1l->trx, par_req->ta); - l1l->trx->ta = par_req->ta; - } - - l1l->trx->tx_power = par_req->tx_power; - - msgb_free(msg); - return 0; -} - -static int l1ctl_rx_tch_mode_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_tch_mode_req *req; - struct trx_lchan_state *lchan; - struct trx_ts *ts; - int i; - - req = (struct l1ctl_tch_mode_req *) msg->l1h; - - LOGP(DL1C, LOGL_NOTICE, "Received L1CTL_TCH_MODE_REQ " - "(tch_mode=%u, audio_mode=%u)\n", req->tch_mode, req->audio_mode); - - /* Iterate over timeslot list */ - for (i = 0; i < TRX_TS_COUNT; i++) { - /* Timeslot is not allocated */ - ts = l1l->trx->ts_list[i]; - if (ts == NULL) - continue; - - /* Timeslot is not configured */ - if (ts->mf_layout == NULL) - continue; - - /* Iterate over all allocated lchans */ - llist_for_each_entry(lchan, &ts->lchans, list) { - /* Omit inactive channels */ - if (!lchan->active) - continue; - - /* Set TCH mode */ - lchan->tch_mode = req->tch_mode; - } - } - - /* TODO: do we need to care about audio_mode? */ - - /* Re-use the original message as confirmation */ - struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; - l1h->msg_type = L1CTL_TCH_MODE_CONF; - - return l1ctl_link_send(l1l, msg); -} - -static int l1ctl_rx_crypto_req(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_crypto_req *req; - struct l1ctl_info_ul *ul; - struct trx_ts *ts; - uint8_t tn; - int rc = 0; - - ul = (struct l1ctl_info_ul *) msg->l1h; - req = (struct l1ctl_crypto_req *) ul->payload; - - LOGP(DL1C, LOGL_NOTICE, "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n", - req->algo, req->key_len); - - /* Determine TS index */ - tn = ul->chan_nr & 0x7; - - /* Make sure that required TS is allocated and configured */ - ts = l1l->trx->ts_list[tn]; - if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DL1C, LOGL_ERROR, "TS %u is not configured\n", tn); - rc = -EINVAL; - goto exit; - } - - /* Poke scheduler */ - rc = sched_trx_start_ciphering(ts, req->algo, req->key, req->key_len); - if (rc) { - LOGP(DL1C, LOGL_ERROR, "Couldn't configure ciphering\n"); - rc = -EINVAL; - goto exit; - } - -exit: - msgb_free(msg); - return rc; -} - -int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg) -{ - struct l1ctl_hdr *l1h; - - l1h = (struct l1ctl_hdr *) msg->l1h; - msg->l1h = l1h->data; - - switch (l1h->msg_type) { - case L1CTL_FBSB_REQ: - return l1ctl_rx_fbsb_req(l1l, msg); - case L1CTL_PM_REQ: - return l1ctl_rx_pm_req(l1l, msg); - case L1CTL_RESET_REQ: - return l1ctl_rx_reset_req(l1l, msg); - case L1CTL_ECHO_REQ: - return l1ctl_rx_echo_req(l1l, msg); - case L1CTL_CCCH_MODE_REQ: - return l1ctl_rx_ccch_mode_req(l1l, msg); - case L1CTL_RACH_REQ: - return l1ctl_rx_rach_req(l1l, msg, false); - case L1CTL_EXT_RACH_REQ: - return l1ctl_rx_rach_req(l1l, msg, true); - case L1CTL_DM_EST_REQ: - return l1ctl_rx_dm_est_req(l1l, msg); - case L1CTL_DM_REL_REQ: - return l1ctl_rx_dm_rel_req(l1l, msg); - case L1CTL_DATA_REQ: - return l1ctl_rx_dt_req(l1l, msg, false); - case L1CTL_TRAFFIC_REQ: - return l1ctl_rx_dt_req(l1l, msg, true); - case L1CTL_PARAM_REQ: - return l1ctl_rx_param_req(l1l, msg); - case L1CTL_TCH_MODE_REQ: - return l1ctl_rx_tch_mode_req(l1l, msg); - case L1CTL_CRYPTO_REQ: - return l1ctl_rx_crypto_req(l1l, msg); - - /* Not (yet) handled messages */ - case L1CTL_NEIGH_PM_REQ: - case L1CTL_DATA_TBF_REQ: - case L1CTL_TBF_CFG_REQ: - case L1CTL_DM_FREQ_REQ: - case L1CTL_SIM_REQ: - LOGP(DL1C, LOGL_NOTICE, "Ignoring unsupported message " - "(type=%u)\n", l1h->msg_type); - msgb_free(msg); - return -ENOTSUP; - default: - LOGP(DL1C, LOGL_ERROR, "Unknown MSG type %u: %s\n", l1h->msg_type, - osmo_hexdump(msgb_data(msg), msgb_length(msg))); - msgb_free(msg); - return -EINVAL; - } -} - -void l1ctl_shutdown_cb(struct l1ctl_link *l1l) -{ - /* Abort FBSB expire timer */ - if (osmo_timer_pending(&l1l->fbsb_timer)) - osmo_timer_del(&l1l->fbsb_timer); -} diff --git a/src/host/trxcon/l1ctl.h b/src/host/trxcon/l1ctl.h deleted file mode 100644 index 48bbe097..00000000 --- a/src/host/trxcon/l1ctl.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <osmocom/core/msgb.h> - -#include "l1ctl_link.h" -#include "l1ctl_proto.h" - -/* Event handlers */ -int l1ctl_rx_cb(struct l1ctl_link *l1l, struct msgb *msg); -void l1ctl_shutdown_cb(struct l1ctl_link *l1l); - -int l1ctl_tx_fbsb_conf(struct l1ctl_link *l1l, uint8_t result, - struct l1ctl_info_dl *dl_info, uint8_t bsic); -int l1ctl_tx_ccch_mode_conf(struct l1ctl_link *l1l, uint8_t mode); -int l1ctl_tx_pm_conf(struct l1ctl_link *l1l, uint16_t band_arfcn, - int dbm, int last); -int l1ctl_tx_reset_conf(struct l1ctl_link *l1l, uint8_t type); -int l1ctl_tx_reset_ind(struct l1ctl_link *l1l, uint8_t type); - -int l1ctl_tx_dt_ind(struct l1ctl_link *l1l, struct l1ctl_info_dl *data, - uint8_t *l2, size_t l2_len, bool traffic); -int l1ctl_tx_dt_conf(struct l1ctl_link *l1l, - struct l1ctl_info_dl *data, bool traffic); -int l1ctl_tx_rach_conf(struct l1ctl_link *l1l, - uint16_t band_arfcn, uint32_t fn); diff --git a/src/host/trxcon/l1ctl_link.c b/src/host/trxcon/l1ctl_link.c deleted file mode 100644 index 4c406d6c..00000000 --- a/src/host/trxcon/l1ctl_link.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * GSM L1 control socket (/tmp/osmocom_l2) handlers - * - * (C) 2013 by Sylvain Munaut <tnt@246tNt.com> - * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <stdio.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include <sys/un.h> -#include <arpa/inet.h> -#include <sys/socket.h> - -#include <osmocom/core/fsm.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/write_queue.h> - -#include "trxcon.h" -#include "logging.h" -#include "l1ctl_link.h" -#include "l1ctl.h" - -static struct value_string l1ctl_evt_names[] = { - { 0, NULL } /* no events? */ -}; - -static struct osmo_fsm_state l1ctl_fsm_states[] = { - [L1CTL_STATE_IDLE] = { - .out_state_mask = GEN_MASK(L1CTL_STATE_CONNECTED), - .name = "IDLE", - }, - [L1CTL_STATE_CONNECTED] = { - .out_state_mask = GEN_MASK(L1CTL_STATE_IDLE), - .name = "CONNECTED", - }, -}; - -static struct osmo_fsm l1ctl_fsm = { - .name = "l1ctl_link_fsm", - .states = l1ctl_fsm_states, - .num_states = ARRAY_SIZE(l1ctl_fsm_states), - .log_subsys = DL1C, - .event_names = l1ctl_evt_names, -}; - -static int l1ctl_link_read_cb(struct osmo_fd *bfd) -{ - struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data; - struct msgb *msg; - uint16_t len; - int rc; - - /* Attempt to read from socket */ - rc = read(bfd->fd, &len, L1CTL_MSG_LEN_FIELD); - if (rc < L1CTL_MSG_LEN_FIELD) { - LOGP(DL1D, LOGL_NOTICE, "L1CTL has lost connection\n"); - if (rc >= 0) - rc = -EIO; - l1ctl_link_close_conn(l1l); - return rc; - } - - /* Check message length */ - len = ntohs(len); - if (len > L1CTL_LENGTH) { - LOGP(DL1D, LOGL_ERROR, "Length is too big: %u\n", len); - return -EINVAL; - } - - /* Allocate a new msg */ - msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, - L1CTL_HEADROOM, "l1ctl_rx_msg"); - if (!msg) { - LOGP(DL1D, LOGL_ERROR, "Failed to allocate msg\n"); - return -ENOMEM; - } - - msg->l1h = msgb_put(msg, len); - rc = read(bfd->fd, msg->l1h, msgb_l1len(msg)); - if (rc != len) { - LOGP(DL1D, LOGL_ERROR, "Can not read data: len=%d < rc=%d: " - "%s\n", len, rc, strerror(errno)); - msgb_free(msg); - return rc; - } - - /* Debug print */ - LOGP(DL1D, LOGL_DEBUG, "RX: '%s'\n", - osmo_hexdump(msg->data, msg->len)); - - /* Call L1CTL handler */ - l1ctl_rx_cb(l1l, msg); - - return 0; -} - -static int l1ctl_link_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int len; - - if (bfd->fd <= 0) - return -EINVAL; - - len = write(bfd->fd, msg->data, msg->len); - if (len != msg->len) { - LOGP(DL1D, LOGL_ERROR, "Failed to write data: " - "written (%d) < msg_len (%d)\n", len, msg->len); - return -1; - } - - return 0; -} - -/* Connection handler */ -static int l1ctl_link_accept(struct osmo_fd *bfd, unsigned int flags) -{ - struct l1ctl_link *l1l = (struct l1ctl_link *) bfd->data; - struct osmo_fd *conn_bfd = &l1l->wq.bfd; - struct sockaddr_un un_addr; - socklen_t len; - int cfd; - - len = sizeof(un_addr); - cfd = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); - if (cfd < 0) { - LOGP(DL1C, LOGL_ERROR, "Failed to accept a new connection\n"); - return -1; - } - - /* Check if we already have an active connection */ - if (conn_bfd->fd != -1) { - LOGP(DL1C, LOGL_NOTICE, "A new connection rejected: " - "we already have another active\n"); - close(cfd); - return 0; - } - - osmo_wqueue_init(&l1l->wq, 100); - INIT_LLIST_HEAD(&conn_bfd->list); - - l1l->wq.write_cb = l1ctl_link_write_cb; - l1l->wq.read_cb = l1ctl_link_read_cb; - osmo_fd_setup(conn_bfd, cfd, OSMO_FD_READ, osmo_wqueue_bfd_cb, l1l, 0); - - if (osmo_fd_register(conn_bfd) != 0) { - LOGP(DL1C, LOGL_ERROR, "Failed to register new connection fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - return -1; - } - - osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_CONNECT, l1l); - osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_CONNECTED, 0, 0); - - LOGP(DL1C, LOGL_NOTICE, "L1CTL has a new connection\n"); - - return 0; -} - -int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg) -{ - uint8_t *len; - - /* Debug print */ - LOGP(DL1D, LOGL_DEBUG, "TX: '%s'\n", - osmo_hexdump(msg->data, msg->len)); - - if (msg->l1h != msg->data) - LOGP(DL1D, LOGL_INFO, "Message L1 header != Message Data\n"); - - /* Prepend 16-bit length before sending */ - len = msgb_push(msg, L1CTL_MSG_LEN_FIELD); - osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len); - - if (osmo_wqueue_enqueue(&l1l->wq, msg) != 0) { - LOGP(DL1D, LOGL_ERROR, "Failed to enqueue msg!\n"); - msgb_free(msg); - return -EIO; - } - - return 0; -} - -int l1ctl_link_close_conn(struct l1ctl_link *l1l) -{ - struct osmo_fd *conn_bfd = &l1l->wq.bfd; - - if (conn_bfd->fd <= 0) - return -EINVAL; - - /* Close connection socket */ - osmo_fd_unregister(conn_bfd); - close(conn_bfd->fd); - conn_bfd->fd = -1; - - /* Clear pending messages */ - osmo_wqueue_clear(&l1l->wq); - - osmo_fsm_inst_dispatch(trxcon_fsm, L1CTL_EVENT_DISCONNECT, l1l); - osmo_fsm_inst_state_chg(l1l->fsm, L1CTL_STATE_IDLE, 0, 0); - - return 0; -} - -struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path) -{ - struct l1ctl_link *l1l; - struct osmo_fd *bfd; - int rc; - - LOGP(DL1C, LOGL_NOTICE, "Init L1CTL link (%s)\n", sock_path); - - l1l = talloc_zero(tall_ctx, struct l1ctl_link); - if (!l1l) { - LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory\n"); - return NULL; - } - - /* Allocate a new dedicated state machine */ - l1l->fsm = osmo_fsm_inst_alloc(&l1ctl_fsm, l1l, - NULL, LOGL_DEBUG, "l1ctl_link"); - if (l1l->fsm == NULL) { - LOGP(DTRX, LOGL_ERROR, "Failed to allocate an instance " - "of FSM '%s'\n", l1ctl_fsm.name); - talloc_free(l1l); - return NULL; - } - - /* Create a socket and bind handlers */ - bfd = &l1l->listen_bfd; - - /* Bind connection handler */ - osmo_fd_setup(bfd, -1, OSMO_FD_READ, l1ctl_link_accept, l1l, 0); - - rc = osmo_sock_unix_init_ofd(bfd, SOCK_STREAM, 0, sock_path, - OSMO_SOCK_F_BIND); - if (rc < 0) { - LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n", - strerror(errno)); - osmo_fsm_inst_free(l1l->fsm); - talloc_free(l1l); - return NULL; - } - - /* Bind shutdown handler */ - l1l->shutdown_cb = l1ctl_shutdown_cb; - - /** - * To be able to accept first connection and - * drop others, it should be set to -1 - */ - l1l->wq.bfd.fd = -1; - - return l1l; -} - -void l1ctl_link_shutdown(struct l1ctl_link *l1l) -{ - struct osmo_fd *listen_bfd; - - /* May be unallocated due to init error */ - if (!l1l) - return; - - LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL link\n"); - - /* Call shutdown callback */ - if (l1l->shutdown_cb != NULL) - l1l->shutdown_cb(l1l); - - listen_bfd = &l1l->listen_bfd; - - /* Check if we have an established connection */ - if (l1l->wq.bfd.fd != -1) - l1ctl_link_close_conn(l1l); - - /* Unbind listening socket */ - if (listen_bfd->fd != -1) { - osmo_fd_unregister(listen_bfd); - close(listen_bfd->fd); - listen_bfd->fd = -1; - } - - osmo_fsm_inst_free(l1l->fsm); - talloc_free(l1l); -} - -static __attribute__((constructor)) void on_dso_load(void) -{ - OSMO_ASSERT(osmo_fsm_register(&l1ctl_fsm) == 0); -} diff --git a/src/host/trxcon/l1ctl_link.h b/src/host/trxcon/l1ctl_link.h deleted file mode 100644 index a333e407..00000000 --- a/src/host/trxcon/l1ctl_link.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include <stdint.h> - -#include <osmocom/core/write_queue.h> -#include <osmocom/core/select.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/fsm.h> - -#define L1CTL_LENGTH 256 -#define L1CTL_HEADROOM 32 - -/** - * Each L1CTL message gets its own length pushed - * as two bytes in front before sending. - */ -#define L1CTL_MSG_LEN_FIELD 2 - -/* Forward declaration to avoid mutual include */ -struct trx_instance; - -enum l1ctl_fsm_states { - L1CTL_STATE_IDLE = 0, - L1CTL_STATE_CONNECTED, -}; - -struct l1ctl_link { - struct osmo_fsm_inst *fsm; - struct osmo_fd listen_bfd; - struct osmo_wqueue wq; - - /* Bind TRX instance */ - struct trx_instance *trx; - - /* L1CTL handlers specific */ - struct osmo_timer_list fbsb_timer; - bool fbsb_conf_sent; - - /* Shutdown callback */ - void (*shutdown_cb)(struct l1ctl_link *l1l); -}; - -struct l1ctl_link *l1ctl_link_init(void *tall_ctx, const char *sock_path); -void l1ctl_link_shutdown(struct l1ctl_link *l1l); - -int l1ctl_link_send(struct l1ctl_link *l1l, struct msgb *msg); -int l1ctl_link_close_conn(struct l1ctl_link *l1l); diff --git a/src/host/trxcon/l1ctl_proto.h b/src/host/trxcon/l1ctl_proto.h deleted file mode 120000 index 75862bae..00000000 --- a/src/host/trxcon/l1ctl_proto.h +++ /dev/null @@ -1 +0,0 @@ -../../../include/l1ctl_proto.h
\ No newline at end of file diff --git a/src/host/trxcon/logging.h b/src/host/trxcon/logging.h deleted file mode 100644 index 152c3467..00000000 --- a/src/host/trxcon/logging.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include <osmocom/core/logging.h> - -#define DEBUG_DEFAULT "DAPP:DL1C:DL1D:DTRX:DTRXD:DSCH:DSCHD" - -enum { - DAPP, - DL1C, - DL1D, - DTRX, - DTRXD, - DSCH, - DSCHD, -}; - -int trx_log_init(void *tall_ctx, const char *category_mask); diff --git a/src/host/trxcon/m4/.gitkeep b/src/host/trxcon/m4/.gitkeep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/host/trxcon/m4/.gitkeep diff --git a/src/host/trxcon/sched_clck.c b/src/host/trxcon/sched_clck.c deleted file mode 100644 index 9476ccd7..00000000 --- a/src/host/trxcon/sched_clck.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: clock synchronization - * - * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu> - * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co> - * (C) 2015 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include <stdint.h> -#include <inttypes.h> -#include <string.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/bits.h> -#include <osmocom/core/fsm.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/timer_compat.h> -#include <osmocom/gsm/a5.h> - -#include "scheduler.h" -#include "logging.h" -#include "trx_if.h" - -#define MAX_FN_SKEW 50 -#define TRX_LOSS_FRAMES 400 - -static void sched_clck_tick(void *data) -{ - struct trx_sched *sched = (struct trx_sched *) data; - struct timespec tv_now, *tv_clock, elapsed; - int64_t elapsed_us; - const struct timespec frame_duration = { .tv_sec = 0, .tv_nsec = GSM_TDMA_FN_DURATION_nS }; - - /* Check if transceiver is still alive */ - if (sched->fn_counter_lost++ == TRX_LOSS_FRAMES) { - LOGP(DSCH, LOGL_DEBUG, "No more clock from transceiver\n"); - sched->state = SCH_CLCK_STATE_WAIT; - - return; - } - - /* Get actual / previous frame time */ - osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now); - tv_clock = &sched->clock; - - timespecsub(&tv_now, tv_clock, &elapsed); - elapsed_us = (elapsed.tv_sec * 1000000) + (elapsed.tv_nsec / 1000); - - /* If someone played with clock, or if the process stalled */ - if (elapsed_us > GSM_TDMA_FN_DURATION_uS * MAX_FN_SKEW || elapsed_us < 0) { - LOGP(DSCH, LOGL_NOTICE, "PC clock skew: " - "elapsed uS %" PRId64 "\n", elapsed_us); - - sched->state = SCH_CLCK_STATE_WAIT; - - return; - } - - /* Schedule next FN clock */ - while (elapsed_us > GSM_TDMA_FN_DURATION_uS / 2) { - timespecadd(tv_clock, &frame_duration, tv_clock); - elapsed_us -= GSM_TDMA_FN_DURATION_uS; - - GSM_TDMA_FN_INC(sched->fn_counter_proc); - - /* Call frame callback */ - if (sched->clock_cb) - sched->clock_cb(sched); - } - - osmo_timer_schedule(&sched->clock_timer, 0, - GSM_TDMA_FN_DURATION_uS - elapsed_us); -} - -static void sched_clck_correct(struct trx_sched *sched, - struct timespec *tv_now, uint32_t fn) -{ - sched->fn_counter_proc = fn; - - /* Call frame callback */ - if (sched->clock_cb) - sched->clock_cb(sched); - - /* Schedule first FN clock */ - sched->clock = *tv_now; - memset(&sched->clock_timer, 0, sizeof(sched->clock_timer)); - - sched->clock_timer.cb = sched_clck_tick; - sched->clock_timer.data = sched; - osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS); -} - -int sched_clck_handle(struct trx_sched *sched, uint32_t fn) -{ - struct timespec tv_now, *tv_clock, elapsed; - int64_t elapsed_us, elapsed_fn; - - /* Reset lost counter */ - sched->fn_counter_lost = 0; - - /* Get actual / previous frame time */ - osmo_clock_gettime(CLOCK_MONOTONIC, &tv_now); - tv_clock = &sched->clock; - - /* If this is the first CLCK IND */ - if (sched->state == SCH_CLCK_STATE_WAIT) { - sched_clck_correct(sched, &tv_now, fn); - - LOGP(DSCH, LOGL_DEBUG, "Initial clock received: fn=%u\n", fn); - sched->state = SCH_CLCK_STATE_OK; - - return 0; - } - - LOGP(DSCH, LOGL_NOTICE, "Clock indication: fn=%u\n", fn); - - osmo_timer_del(&sched->clock_timer); - - /* Calculate elapsed time / frames since last processed fn */ - timespecsub(&tv_now, tv_clock, &elapsed); - elapsed_us = (elapsed.tv_sec * 1000000) + (elapsed.tv_nsec / 1000); - elapsed_fn = GSM_TDMA_FN_SUB(fn, sched->fn_counter_proc); - - if (elapsed_fn >= 135774) - elapsed_fn -= GSM_TDMA_HYPERFRAME; - - /* Check for max clock skew */ - if (elapsed_fn > MAX_FN_SKEW || elapsed_fn < -MAX_FN_SKEW) { - LOGP(DSCH, LOGL_NOTICE, "GSM clock skew: old fn=%u, " - "new fn=%u\n", sched->fn_counter_proc, fn); - - sched_clck_correct(sched, &tv_now, fn); - return 0; - } - - LOGP(DSCH, LOGL_INFO, "GSM clock jitter: %" PRId64 "\n", - elapsed_fn * GSM_TDMA_FN_DURATION_uS - elapsed_us); - - /* Too many frames have been processed already */ - if (elapsed_fn < 0) { - struct timespec duration; - /** - * Set clock to the time or last FN should - * have been transmitted - */ - duration.tv_nsec = (0 - elapsed_fn) * GSM_TDMA_FN_DURATION_nS; - duration.tv_sec = duration.tv_nsec / 1000000000; - duration.tv_nsec = duration.tv_nsec % 1000000000; - timespecadd(&tv_now, &duration, tv_clock); - - /* Set time to the time our next FN has to be transmitted */ - osmo_timer_schedule(&sched->clock_timer, 0, - GSM_TDMA_FN_DURATION_uS * (1 - elapsed_fn)); - - return 0; - } - - /* Transmit what we still need to transmit */ - while (fn != sched->fn_counter_proc) { - GSM_TDMA_FN_INC(sched->fn_counter_proc); - - /* Call frame callback */ - if (sched->clock_cb) - sched->clock_cb(sched); - } - - /* Schedule next FN to be transmitted */ - *tv_clock = tv_now; - osmo_timer_schedule(&sched->clock_timer, 0, GSM_TDMA_FN_DURATION_uS); - - return 0; -} - -void sched_clck_reset(struct trx_sched *sched) -{ - /* Reset internal state */ - sched->state = SCH_CLCK_STATE_WAIT; - - /* Stop clock timer */ - osmo_timer_del(&sched->clock_timer); - - /* Flush counters */ - sched->fn_counter_proc = 0; - sched->fn_counter_lost = 0; -} diff --git a/src/host/trxcon/sched_lchan_common.c b/src/host/trxcon/sched_lchan_common.c deleted file mode 100644 index ae43ca94..00000000 --- a/src/host/trxcon/sched_lchan_common.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: common routines for lchan handlers - * - * (C) 2017-2020 by Vadim Yanitskiy <axilirator@gmail.com> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <string.h> -#include <talloc.h> -#include <stdint.h> -#include <stdbool.h> - -#include <arpa/inet.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/bits.h> -#include <osmocom/core/gsmtap_util.h> -#include <osmocom/core/gsmtap.h> - -#include <osmocom/codec/codec.h> - -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/protocol/gsm_08_58.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trxcon.h" -#include "trx_if.h" -#include "l1ctl.h" - -/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ -const uint8_t sched_nb_training_bits[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, - }, -}; - -/* Get a string representation of the burst buffer's completeness. - * Examples: " ****.." (incomplete, 4/6 bursts) - * " ****" (complete, all 4 bursts) - * "**.***.." (incomplete, 5/8 bursts) */ -const char *burst_mask2str(const uint8_t *mask, int bits) -{ - /* TODO: CSD is interleaved over 22 bursts, so the mask needs to be extended */ - static char buf[8 + 1]; - char *ptr = buf; - - OSMO_ASSERT(bits <= 8 && bits > 0); - - while (--bits >= 0) - *(ptr++) = (*mask & (1 << bits)) ? '*' : '.'; - *ptr = '\0'; - - return buf; -} - -int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn, - uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr, - const uint8_t *data, size_t data_len) -{ - const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[lchan_type]; - - /* GSMTAP logging may not be enabled */ - if (gsmtap == NULL) - return 0; - - /* Omit frames with unknown channel type */ - if (lchan_desc->gsmtap_chan_type == GSMTAP_CHANNEL_UNKNOWN) - return 0; - - /* TODO: distinguish GSMTAP_CHANNEL_PCH and GSMTAP_CHANNEL_AGCH */ - return gsmtap_send(gsmtap, band_arfcn, tn, lchan_desc->gsmtap_chan_type, - lchan_desc->ss_nr, fn, signal_dbm, snr, data, data_len); -} - -int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len, - int bit_error_count, bool dec_failed, bool traffic) -{ - const struct trx_meas_set *meas = &lchan->meas_avg; - const struct trx_lchan_desc *lchan_desc; - struct l1ctl_info_dl dl_hdr; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - - /* Fill in known downlink info */ - dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index; - dl_hdr.link_id = lchan_desc->link_id; - dl_hdr.band_arfcn = htons(trx->band_arfcn); - dl_hdr.num_biterr = bit_error_count; - - /* sched_trx_meas_avg() gives us TDMA frame number of the first burst */ - dl_hdr.frame_nr = htonl(meas->fn); - - /* RX level: 0 .. 63 in typical GSM notation (dBm + 110) */ - dl_hdr.rx_level = dbm2rxlev(meas->rssi); - - /* FIXME: set proper values */ - dl_hdr.snr = 0; - - /* Mark frame as broken if so */ - dl_hdr.fire_crc = dec_failed ? 2 : 0; - - /* Put a packet to higher layers */ - l1ctl_tx_dt_ind(trx->l1l, &dl_hdr, l2, l2_len, traffic); - - /* Optional GSMTAP logging */ - if (l2_len > 0 && (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH)) { - sched_gsmtap_send(lchan->type, meas->fn, ts->index, - trx->band_arfcn, meas->rssi, 0, l2, l2_len); - } - - return 0; -} - -int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, bool traffic) -{ - const struct trx_lchan_desc *lchan_desc; - struct l1ctl_info_dl dl_hdr; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - - /* Zero-initialize DL header, because we don't set all fields */ - memset(&dl_hdr, 0x00, sizeof(struct l1ctl_info_dl)); - - /* Fill in known downlink info */ - dl_hdr.chan_nr = lchan_desc->chan_nr | ts->index; - dl_hdr.link_id = lchan_desc->link_id; - dl_hdr.band_arfcn = htons(trx->band_arfcn); - dl_hdr.frame_nr = htonl(fn); - - l1ctl_tx_dt_conf(trx->l1l, &dl_hdr, traffic); - - /* Optional GSMTAP logging */ - if (!traffic || lchan_desc->chan_nr == RSL_CHAN_OSMO_PDCH) { - sched_gsmtap_send(lchan->type, fn, ts->index, - trx->band_arfcn | ARFCN_UPLINK, - 0, 0, lchan->prim->payload, - lchan->prim->payload_len); - } - - return 0; -} - -/** - * Composes a bad frame indication message - * according to the current tch_mode. - * - * @param l2 Caller-allocated byte array - * @param lchan Logical channel to generate BFI for - * @return How much bytes were written - */ -size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan) -{ - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - if (lchan->type == TRXC_TCHF) { /* Full Rate */ - memset(l2, 0x00, GSM_FR_BYTES); - l2[0] = 0xd0; - return GSM_FR_BYTES; - } else { /* Half Rate */ - memset(l2 + 1, 0x00, GSM_HR_BYTES); - l2[0] = 0x70; /* F = 0, FT = 111 */ - return GSM_HR_BYTES + 1; - } - case GSM48_CMODE_SPEECH_EFR: /* Enhanced Full Rate */ - memset(l2, 0x00, GSM_EFR_BYTES); - l2[0] = 0xc0; - return GSM_EFR_BYTES; - case GSM48_CMODE_SPEECH_AMR: /* Adaptive Multi Rate */ - /* FIXME: AMR is not implemented yet */ - return 0; - case GSM48_CMODE_SIGN: - LOGP(DSCH, LOGL_ERROR, "BFI is not allowed in signalling mode\n"); - return 0; - default: - LOGP(DSCH, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode); - return 0; - } -} diff --git a/src/host/trxcon/sched_lchan_pdtch.c b/src/host/trxcon/sched_lchan_pdtch.c deleted file mode 100644 index abbd480c..00000000 --- a/src/host/trxcon/sched_lchan_pdtch.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2018-2021 by Vadim Yanitskiy <axilirator@gmail.com> - * Contributions by sysmocom - s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <string.h> -#include <stdint.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/bits.h> - -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/coding/gsm0503_coding.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - const struct trx_lchan_desc *lchan_desc; - uint8_t l2[GPRS_L2_MAX_LEN], *mask; - int n_errors, n_bits_total, rc; - sbit_t *buffer, *offset; - size_t l2_len; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->rx_burst_mask; - buffer = lchan->rx_bursts; - - LOGP(DSCHD, LOGL_DEBUG, "Packet data received on %s: " - "fn=%u ts=%u bid=%u\n", lchan_desc->name, fn, ts->index, bid); - - /* Align to the first burst of a block */ - if (*mask == 0x00 && bid != 0) - return 0; - - /* Update mask */ - *mask |= (1 << bid); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* Copy burst to buffer of 4 bursts */ - offset = buffer + bid * 116; - memcpy(offset, bits + 3, 58); - memcpy(offset + 58, bits + 87, 58); - - /* Wait until complete set of bursts */ - if (bid != 3) - return 0; - - /* Calculate AVG of the measurements */ - sched_trx_meas_avg(lchan, 4); - - /* Check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at " - "fn=%u (%u/%u) for %s\n", - burst_mask2str(mask, 4), lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */ - } - - /* Keep the mask updated */ - *mask = *mask << 4; - - /* Attempt to decode */ - rc = gsm0503_pdtch_decode(l2, buffer, - NULL, &n_errors, &n_bits_total); - if (rc < 0) { - LOGP(DSCHD, LOGL_ERROR, "Received bad packet data frame " - "at fn=%u (%u/%u) for %s\n", lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - } - - /* Determine L2 length */ - l2_len = rc > 0 ? rc : 0; - - /* Send a L2 frame to the higher layers */ - sched_send_dt_ind(trx, ts, lchan, - l2, l2_len, n_errors, rc < 0, true); - - return 0; -} - - -int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br) -{ - const struct trx_lchan_desc *lchan_desc; - ubit_t *buffer, *offset; - const uint8_t *tsc; - uint8_t *mask; - int rc; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->tx_burst_mask; - buffer = lchan->tx_bursts; - - if (br->bid > 0) { - /* If we have encoded bursts */ - if (*mask) - goto send_burst; - else - return 0; - } - - /* Encode payload */ - rc = gsm0503_pdtch_encode(buffer, lchan->prim->payload, - lchan->prim->payload_len); - if (rc < 0) { - LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n", - lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, - lchan->prim->payload_len)); - - /* Forget this primitive */ - sched_prim_drop(lchan); - - return -EINVAL; - } - -send_burst: - /* Determine which burst should be sent */ - offset = buffer + br->bid * 116; - - /* Update mask */ - *mask |= (1 << br->bid); - - /* Choose proper TSC */ - tsc = sched_nb_training_bits[trx->tsc]; - - /* Compose a new burst */ - memset(br->burst, 0, 3); /* TB */ - memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */ - memcpy(br->burst + 61, tsc, 26); /* TSC */ - memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ - memset(br->burst + 145, 0, 3); /* TB */ - br->burst_len = GSM_BURST_LEN; - - LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n", - lchan_desc->name, br->fn, ts->index, br->bid); - - /* If we have sent the last (4/4) burst */ - if ((*mask & 0x0f) == 0x0f) { - /* Confirm data / traffic sending */ - sched_send_dt_conf(trx, ts, lchan, br->fn, true); - - /* Forget processed primitive */ - sched_prim_drop(lchan); - - /* Reset mask */ - *mask = 0x00; - } - - return 0; -} diff --git a/src/host/trxcon/sched_lchan_rach.c b/src/host/trxcon/sched_lchan_rach.c deleted file mode 100644 index 25e1b440..00000000 --- a/src/host/trxcon/sched_lchan_rach.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2017-2021 by Vadim Yanitskiy <axilirator@gmail.com> - * Contributions by sysmocom - s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <string.h> -#include <stdint.h> -#include <stdbool.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/bits.h> - -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/coding/gsm0503_coding.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */ -#define RACH_EXT_TAIL_BITS_LEN 8 -#define RACH_SYNCH_SEQ_LEN 41 -#define RACH_PAYLOAD_LEN 36 - -/* Extended tail bits (BN0..BN7) */ -static const ubit_t rach_ext_tail_bits[] = { - 0, 0, 1, 1, 1, 0, 1, 0, -}; - -/* Synchronization (training) sequence types */ -enum rach_synch_seq_t { - RACH_SYNCH_SEQ_UNKNOWN = -1, - RACH_SYNCH_SEQ_TS0, /* GSM, GMSK (default) */ - RACH_SYNCH_SEQ_TS1, /* EGPRS, 8-PSK */ - RACH_SYNCH_SEQ_TS2, /* EGPRS, GMSK */ - RACH_SYNCH_SEQ_NUM -}; - -/* Synchronization (training) sequence bits */ -static const char rach_synch_seq_bits[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = { - [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000", - [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101", - [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111", -}; - -/* Synchronization (training) sequence names */ -static struct value_string rach_synch_seq_names[] = { - { RACH_SYNCH_SEQ_UNKNOWN, "UNKNOWN" }, - { RACH_SYNCH_SEQ_TS0, "TS0: GSM, GMSK" }, - { RACH_SYNCH_SEQ_TS1, "TS1: EGPRS, 8-PSK" }, - { RACH_SYNCH_SEQ_TS2, "TS2: EGPRS, GMSK" }, - { 0, NULL }, -}; - -/* Obtain a to-be-transmitted RACH burst */ -int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br) -{ - struct l1ctl_ext_rach_req *ext_req = NULL; - struct l1ctl_rach_req *req = NULL; - enum rach_synch_seq_t synch_seq; - uint8_t *burst_ptr = br->burst; - uint8_t payload[36]; - int i, rc; - - /* Is it extended (11-bit) RACH or not? */ - if (PRIM_IS_RACH11(lchan->prim)) { - ext_req = (struct l1ctl_ext_rach_req *) lchan->prim->payload; - synch_seq = ext_req->synch_seq; - - /* Check requested synch. sequence */ - if (synch_seq >= RACH_SYNCH_SEQ_NUM) { - LOGP(DSCHD, LOGL_ERROR, "Unknown RACH synch. sequence=0x%02x\n", synch_seq); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return -ENOTSUP; - } - - /* Delay sending according to offset value */ - if (ext_req->offset-- > 0) - return 0; - - /* Encode extended (11-bit) payload */ - rc = gsm0503_rach_ext_encode(payload, ext_req->ra11, trx->bsic, true); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not encode extended RACH burst " - "(ra=%u bsic=%u)\n", ext_req->ra11, trx->bsic); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return rc; - } - } else if (PRIM_IS_RACH8(lchan->prim)) { - req = (struct l1ctl_rach_req *) lchan->prim->payload; - synch_seq = RACH_SYNCH_SEQ_TS0; - - /* Delay sending according to offset value */ - if (req->offset-- > 0) - return 0; - - /* Encode regular (8-bit) payload */ - rc = gsm0503_rach_ext_encode(payload, req->ra, trx->bsic, false); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Could not encode RACH burst " - "(ra=%u bsic=%u)\n", req->ra, trx->bsic); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return rc; - } - } else { - LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %zu or %zu), " - "so dropping...\n", lchan->prim->payload_len, - sizeof(*req), sizeof(*ext_req)); - sched_prim_drop(lchan); - return -EINVAL; - } - - - /* BN0-7: extended tail bits */ - memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN); - burst_ptr += RACH_EXT_TAIL_BITS_LEN; - - /* BN8-48: chosen synch. (training) sequence */ - for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++) - *(burst_ptr++) = rach_synch_seq_bits[synch_seq][i] == '1'; - - /* BN49-84: encrypted bits (the payload) */ - memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN); - burst_ptr += RACH_PAYLOAD_LEN; - - /* BN85-156: tail bits & extended guard period */ - memset(burst_ptr, 0, br->burst + GSM_BURST_LEN - burst_ptr); - br->burst_len = GSM_BURST_LEN; - - LOGP(DSCHD, LOGL_NOTICE, "Scheduled %s RACH (%s) on fn=%u, tn=%u, lchan=%s\n", - PRIM_IS_RACH11(lchan->prim) ? "extended (11-bit)" : "regular (8-bit)", - get_value_string(rach_synch_seq_names, synch_seq), br->fn, - ts->index, trx_lchan_desc[lchan->type].name); - - /* Confirm RACH request */ - l1ctl_tx_rach_conf(trx->l1l, trx->band_arfcn, br->fn); - - /* Optional GSMTAP logging */ - sched_gsmtap_send(lchan->type, br->fn, ts->index, - trx->band_arfcn | ARFCN_UPLINK, 0, 0, - PRIM_IS_RACH11(lchan->prim) ? (uint8_t *) &ext_req->ra11 : &req->ra, - PRIM_IS_RACH11(lchan->prim) ? 2 : 1); - - /* Forget processed primitive */ - sched_prim_drop(lchan); - - return 0; -} diff --git a/src/host/trxcon/sched_lchan_tchf.c b/src/host/trxcon/sched_lchan_tchf.c deleted file mode 100644 index 1e38e963..00000000 --- a/src/host/trxcon/sched_lchan_tchf.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2017-2021 by Vadim Yanitskiy <axilirator@gmail.com> - * Contributions by sysmocom - s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <string.h> -#include <stdint.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/bits.h> - -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/gsm_utils.h> - -#include <osmocom/coding/gsm0503_coding.h> -#include <osmocom/codec/codec.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - const struct trx_lchan_desc *lchan_desc; - int n_errors = -1, n_bits_total, rc; - sbit_t *buffer, *offset; - uint8_t l2[128], *mask; - size_t l2_len; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->rx_burst_mask; - buffer = lchan->rx_bursts; - - LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n", - lchan_desc->name, fn, ts->index, bid); - - /* Align to the first burst of a block */ - if (*mask == 0x00 && bid != 0) - return 0; - - /* Update mask */ - *mask |= (1 << bid); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* Copy burst to end of buffer of 8 bursts */ - offset = buffer + bid * 116 + 464; - memcpy(offset, bits + 3, 58); - memcpy(offset + 58, bits + 87, 58); - - /* Wait until complete set of bursts */ - if (bid != 3) - return 0; - - /* Calculate AVG of the measurements */ - sched_trx_meas_avg(lchan, 8); - - /* Check for complete set of bursts */ - if ((*mask & 0xff) != 0xff) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) traffic frame at " - "fn=%u (%u/%u) for %s\n", - burst_mask2str(mask, 8), lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */ - - } - - /* Keep the mask updated */ - *mask = *mask << 4; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - case GSM48_CMODE_SPEECH_V1: /* FR */ - rc = gsm0503_tch_fr_decode(l2, buffer, - 1, 0, &n_errors, &n_bits_total); - break; - case GSM48_CMODE_SPEECH_EFR: /* EFR */ - rc = gsm0503_tch_fr_decode(l2, buffer, - 1, 1, &n_errors, &n_bits_total); - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - /** - * TODO: AMR requires a dedicated loop, - * which will be implemented later... - */ - LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); - return -ENOTSUP; - default: - LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode); - return -EINVAL; - } - - /* Shift buffer by 4 bursts for interleaving */ - memcpy(buffer, buffer + 464, 464); - - /* Check decoding result */ - if (rc < 4) { - LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame ending at " - "fn=%u for %s\n", fn, lchan_desc->name); - - /* Send BFI */ - goto bfi; - } else if (rc == GSM_MACBLOCK_LEN) { - /* FACCH received, forward it to the higher layers */ - sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, - n_errors, false, false); - - /* Send BFI substituting a stolen TCH frame */ - n_errors = -1; /* ensure fake measurements */ - goto bfi; - } else { - /* A good TCH frame received */ - l2_len = rc; - } - - /* Send a traffic frame to the higher layers */ - return sched_send_dt_ind(trx, ts, lchan, l2, l2_len, - n_errors, false, true); - -bfi: - /* Didn't try to decode, fake measurements */ - if (n_errors < 0) { - lchan->meas_avg = (struct trx_meas_set) { - .fn = lchan->meas_avg.fn, - .toa256 = 0, - .rssi = -110, - }; - - /* No bursts => no errors */ - n_errors = 0; - } - - /* BFI is not applicable in signalling mode */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) - return sched_send_dt_ind(trx, ts, lchan, NULL, 0, - n_errors, true, false); - - /* Bad frame indication */ - l2_len = sched_bad_frame_ind(l2, lchan); - - /* Send a BFI frame to the higher layers */ - return sched_send_dt_ind(trx, ts, lchan, l2, l2_len, - n_errors, true, true); -} - -int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br) -{ - const struct trx_lchan_desc *lchan_desc; - ubit_t *buffer, *offset; - const uint8_t *tsc; - uint8_t *mask; - size_t l2_len; - int rc; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->tx_burst_mask; - buffer = lchan->tx_bursts; - - /* If we have encoded bursts */ - if (*mask) - goto send_burst; - - /* Wait until a first burst in period */ - if (br->bid > 0) - return 0; - - /* Check the current TCH mode */ - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - case GSM48_CMODE_SPEECH_V1: /* FR */ - l2_len = GSM_FR_BYTES; - break; - case GSM48_CMODE_SPEECH_EFR: /* EFR */ - l2_len = GSM_EFR_BYTES; - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - /** - * TODO: AMR requires a dedicated loop, - * which will be implemented later... - */ - LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, " - "dropping frame...\n"); - - /* Forget this primitive */ - sched_prim_drop(lchan); - - return -ENOTSUP; - default: - LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, " - "dropping frame...\n", lchan->tch_mode); - - /* Forget this primitive */ - sched_prim_drop(lchan); - - return -EINVAL; - } - - /* Determine and check the payload length */ - if (lchan->prim->payload_len == GSM_MACBLOCK_LEN) { - l2_len = GSM_MACBLOCK_LEN; /* FACCH */ - } else if (lchan->prim->payload_len != l2_len) { - LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu " - "(expected %zu for TCH or %u for FACCH), so dropping...\n", - lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN); - - sched_prim_drop(lchan); - return -EINVAL; - } - - /* Shift buffer by 4 bursts back for interleaving */ - memcpy(buffer, buffer + 464, 464); - - /* Encode payload */ - rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n", - lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, - lchan->prim->payload_len)); - - /* Forget this primitive */ - sched_prim_drop(lchan); - - return -EINVAL; - } - -send_burst: - /* Determine which burst should be sent */ - offset = buffer + br->bid * 116; - - /* Update mask */ - *mask |= (1 << br->bid); - - /* Choose proper TSC */ - tsc = sched_nb_training_bits[trx->tsc]; - - /* Compose a new burst */ - memset(br->burst, 0, 3); /* TB */ - memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */ - memcpy(br->burst + 61, tsc, 26); /* TSC */ - memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ - memset(br->burst + 145, 0, 3); /* TB */ - br->burst_len = GSM_BURST_LEN; - - LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n", - lchan_desc->name, br->fn, ts->index, br->bid); - - /* If we have sent the last (4/4) burst */ - if (*mask == 0x0f) { - /* Confirm data / traffic sending */ - sched_send_dt_conf(trx, ts, lchan, br->fn, PRIM_IS_TCH(lchan->prim)); - - /* Forget processed primitive */ - sched_prim_drop(lchan); - - /* Reset mask */ - *mask = 0x00; - } - - return 0; -} diff --git a/src/host/trxcon/sched_lchan_tchh.c b/src/host/trxcon/sched_lchan_tchh.c deleted file mode 100644 index 6a5c4714..00000000 --- a/src/host/trxcon/sched_lchan_tchh.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2018-2021 by Vadim Yanitskiy <axilirator@gmail.com> - * (C) 2018 by Harald Welte <laforge@gnumonks.org> - * Contributions by sysmocom - s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <string.h> -#include <stdint.h> -#include <stdbool.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/bits.h> - -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/gsm_utils.h> - -#include <osmocom/coding/gsm0503_coding.h> -#include <osmocom/codec/codec.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -static const uint8_t tch_h0_traffic_block_map[3][4] = { - /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */ - { 0, 2, 4, 6 }, - { 4, 6, 8, 10 }, - { 8, 10, 0, 2 }, -}; - -static const uint8_t tch_h1_traffic_block_map[3][4] = { - /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */ - { 1, 3, 5, 7 }, - { 5, 7, 9, 11 }, - { 9, 11, 1, 3 }, -}; - -static const uint8_t tch_h0_dl_facch_block_map[3][6] = { - /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */ - { 4, 6, 8, 10, 13, 15 }, - { 13, 15, 17, 19, 21, 23 }, - { 21, 23, 0, 2, 4, 6 }, -}; - -static const uint8_t tch_h0_ul_facch_block_map[3][6] = { - /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */ - { 0, 2, 4, 6, 8, 10 }, - { 8, 10, 13, 15, 17, 19 }, - { 17, 19, 21, 23, 0, 2 }, -}; - -static const uint8_t tch_h1_dl_facch_block_map[3][6] = { - /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */ - { 5, 7, 9, 11, 14, 16 }, - { 14, 16, 18, 20, 22, 24 }, - { 22, 24, 1, 3, 5, 7 }, -}; - -const uint8_t tch_h1_ul_facch_block_map[3][6] = { - /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */ - { 1, 3, 5, 7, 9, 11 }, - { 9, 11, 14, 16, 18, 20 }, - { 18, 20, 22, 24, 1, 3 }, -}; - -/** - * Can a TCH/H block transmission be initiated / finished - * on a given frame number and a given channel type? - * - * See GSM 05.02, clause 7, table 1 - * - * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1) - * @param fn the current frame number - * @param ul Uplink or Downlink? - * @param facch FACCH/H or traffic? - * @param start init or end of transmission? - * @return true (yes) or false (no) - */ -bool sched_tchh_block_map_fn(enum trx_lchan_type chan, - uint32_t fn, bool ul, bool facch, bool start) -{ - uint8_t fn_mf; - int i = 0; - - /* Just to be sure */ - OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1); - - /* Calculate a modulo */ - fn_mf = facch ? (fn % 26) : (fn % 13); - -#define MAP_GET_POS(map) \ - (start ? 0 : ARRAY_SIZE(map[i]) - 1) - -#define BLOCK_MAP_FN(map) \ - do { \ - if (map[i][MAP_GET_POS(map)] == fn_mf) \ - return true; \ - } while (++i < ARRAY_SIZE(map)) - - /* Choose a proper block map */ - if (facch) { - if (ul) { - if (chan == TRXC_TCHH_0) - BLOCK_MAP_FN(tch_h0_ul_facch_block_map); - else - BLOCK_MAP_FN(tch_h1_ul_facch_block_map); - } else { - if (chan == TRXC_TCHH_0) - BLOCK_MAP_FN(tch_h0_dl_facch_block_map); - else - BLOCK_MAP_FN(tch_h1_dl_facch_block_map); - } - } else { - if (chan == TRXC_TCHH_0) - BLOCK_MAP_FN(tch_h0_traffic_block_map); - else - BLOCK_MAP_FN(tch_h1_traffic_block_map); - } - - return false; -} - -/** - * Calculates a frame number of the first burst - * using given frame number of the last burst. - * - * See GSM 05.02, clause 7, table 1 - * - * @param chan channel type (TRXC_TCHH_0 or TRXC_TCHH_1) - * @param last_fn frame number of the last burst - * @param facch FACCH/H or traffic? - * @return either frame number of the first burst, - * or fn=last_fn if calculation failed - */ -uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan, - uint32_t last_fn, bool facch) -{ - uint8_t fn_mf, fn_diff; - int i = 0; - - /* Just to be sure */ - OSMO_ASSERT(chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1); - - /* Calculate a modulo */ - fn_mf = facch ? (last_fn % 26) : (last_fn % 13); - -#define BLOCK_FIRST_FN(map) \ - do { \ - if (map[i][ARRAY_SIZE(map[i]) - 1] == fn_mf) { \ - fn_diff = GSM_TDMA_FN_DIFF(fn_mf, map[i][0]); \ - return GSM_TDMA_FN_SUB(last_fn, fn_diff); \ - } \ - } while (++i < ARRAY_SIZE(map)) - - /* Choose a proper block map */ - if (facch) { - if (chan == TRXC_TCHH_0) - BLOCK_FIRST_FN(tch_h0_dl_facch_block_map); - else - BLOCK_FIRST_FN(tch_h1_dl_facch_block_map); - } else { - if (chan == TRXC_TCHH_0) - BLOCK_FIRST_FN(tch_h0_traffic_block_map); - else - BLOCK_FIRST_FN(tch_h1_traffic_block_map); - } - - LOGP(DSCHD, LOGL_ERROR, "Failed to calculate TDMA " - "frame number of the first burst of %s block, " - "using the current fn=%u\n", facch ? - "FACCH/H" : "TCH/H", last_fn); - - /* Couldn't calculate the first fn, return the last */ - return last_fn; -} - -int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - const struct trx_lchan_desc *lchan_desc; - int n_errors = -1, n_bits_total, rc; - sbit_t *buffer, *offset; - uint8_t l2[128], *mask; - size_t l2_len; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->rx_burst_mask; - buffer = lchan->rx_bursts; - - LOGP(DSCHD, LOGL_DEBUG, "Traffic received on %s: fn=%u ts=%u bid=%u\n", - lchan_desc->name, fn, ts->index, bid); - - if (*mask == 0x00) { - /* Align to the first burst */ - if (bid > 0) - return 0; - - /* Align reception of the first FACCH/H frame */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) { - if (!sched_tchh_facch_start(lchan->type, fn, 0)) - return 0; - } else { /* or TCH/H traffic frame */ - if (!sched_tchh_traffic_start(lchan->type, fn, 0)) - return 0; - } - } - - /* Update mask */ - *mask |= (1 << bid); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* Copy burst to the end of buffer of 6 bursts */ - offset = buffer + bid * 116 + 464; - memcpy(offset, bits + 3, 58); - memcpy(offset + 58, bits + 87, 58); - - /* Wait until the second burst */ - if (bid != 1) - return 0; - - /* Wait for complete set of bursts */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) { - /* FACCH/H is interleaved over 6 bursts */ - if ((*mask & 0x3f) != 0x3f) - goto bfi_shift; - } else { - /* Traffic is interleaved over 4 bursts */ - if ((*mask & 0x0f) != 0x0f) - goto bfi_shift; - } - - /* Skip decoding attempt in case of FACCH/H */ - if (lchan->dl_ongoing_facch) { - lchan->dl_ongoing_facch = false; - goto bfi_shift; /* 2/2 BFI */ - } - - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - case GSM48_CMODE_SPEECH_V1: /* HR */ - rc = gsm0503_tch_hr_decode(l2, buffer, - !sched_tchh_facch_end(lchan->type, fn, 0), - &n_errors, &n_bits_total); - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - /** - * TODO: AMR requires a dedicated loop, - * which will be implemented later... - */ - LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet\n"); - return -ENOTSUP; - default: - LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode); - return -EINVAL; - } - - /* Shift buffer by 4 bursts for interleaving */ - memcpy(buffer, buffer + 232, 232); - memcpy(buffer + 232, buffer + 464, 232); - - /* Shift burst mask */ - *mask = *mask << 2; - - /* Check decoding result */ - if (rc < 4) { - /* Calculate AVG of the measurements (assuming 4 bursts) */ - sched_trx_meas_avg(lchan, 4); - - LOGP(DSCHD, LOGL_ERROR, "Received bad TCH frame (%s) " - "at fn=%u on %s (rc=%d)\n", burst_mask2str(mask, 6), - lchan->meas_avg.fn, lchan_desc->name, rc); - - /* Send BFI */ - goto bfi; - } else if (rc == GSM_MACBLOCK_LEN) { - /* Skip decoding of the next 2 stolen bursts */ - lchan->dl_ongoing_facch = true; - - /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */ - sched_trx_meas_avg(lchan, 6); - - /* FACCH/H received, forward to the higher layers */ - sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, - n_errors, false, false); - - /* Send BFI substituting 1/2 stolen TCH frames */ - n_errors = -1; /* ensure fake measurements */ - goto bfi; - } else { - /* A good TCH frame received */ - l2_len = rc; - - /* Calculate AVG of the measurements (traffic takes 4 bursts) */ - sched_trx_meas_avg(lchan, 4); - } - - /* Send a traffic frame to the higher layers */ - return sched_send_dt_ind(trx, ts, lchan, l2, l2_len, - n_errors, false, true); - -bfi_shift: - /* Shift buffer */ - memcpy(buffer, buffer + 232, 232); - memcpy(buffer + 232, buffer + 464, 232); - - /* Shift burst mask */ - *mask = *mask << 2; - -bfi: - /* Didn't try to decode, fake measurements */ - if (n_errors < 0) { - lchan->meas_avg = (struct trx_meas_set) { - .fn = sched_tchh_block_dl_first_fn(lchan->type, fn, false), - .toa256 = 0, - .rssi = -110, - }; - - /* No bursts => no errors */ - n_errors = 0; - } - - /* BFI is not applicable in signalling mode */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) - return sched_send_dt_ind(trx, ts, lchan, NULL, 0, - n_errors, true, false); - - /* Bad frame indication */ - l2_len = sched_bad_frame_ind(l2, lchan); - - /* Send a BFI frame to the higher layers */ - return sched_send_dt_ind(trx, ts, lchan, l2, l2_len, - n_errors, true, true); -} - -int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br) -{ - const struct trx_lchan_desc *lchan_desc; - ubit_t *buffer, *offset; - const uint8_t *tsc; - uint8_t *mask; - size_t l2_len; - int rc; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->tx_burst_mask; - buffer = lchan->tx_bursts; - - if (br->bid > 0) { - /* Align to the first burst */ - if (*mask == 0x00) - return 0; - goto send_burst; - } - - if (*mask == 0x00) { - /* Align transmission of the first FACCH/H frame */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) - if (!sched_tchh_facch_start(lchan->type, br->fn, 1)) - return 0; - } - - /* Shift buffer by 2 bursts back for interleaving */ - memcpy(buffer, buffer + 232, 232); - - /* Also shift TX burst mask */ - *mask = *mask << 2; - - /* If FACCH/H blocks are still pending */ - if (lchan->ul_facch_blocks > 2) { - memcpy(buffer + 232, buffer + 464, 232); - goto send_burst; - } - - /* Check the current TCH mode */ - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - case GSM48_CMODE_SPEECH_V1: /* HR */ - l2_len = GSM_HR_BYTES + 1; - break; - case GSM48_CMODE_SPEECH_AMR: /* AMR */ - /** - * TODO: AMR requires a dedicated loop, - * which will be implemented later... - */ - LOGP(DSCHD, LOGL_ERROR, "AMR isn't supported yet, " - "dropping frame...\n"); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return -ENOTSUP; - default: - LOGP(DSCHD, LOGL_ERROR, "Invalid TCH mode: %u, " - "dropping frame...\n", lchan->tch_mode); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return -EINVAL; - } - - /* Determine payload length */ - if (PRIM_IS_FACCH(lchan->prim)) { - l2_len = GSM_MACBLOCK_LEN; /* FACCH */ - } else if (lchan->prim->payload_len != l2_len) { - LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu " - "(expected %zu for TCH or %u for FACCH), so dropping...\n", - lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return -EINVAL; - } - - /* Encode the payload */ - rc = gsm0503_tch_hr_encode(buffer, lchan->prim->payload, l2_len); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n", - lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, - lchan->prim->payload_len)); - - /* Forget this primitive */ - sched_prim_drop(lchan); - return -EINVAL; - } - - /* A FACCH/H frame occupies 6 bursts */ - if (PRIM_IS_FACCH(lchan->prim)) - lchan->ul_facch_blocks = 6; - -send_burst: - /* Determine which burst should be sent */ - offset = buffer + br->bid * 116; - - /* Update mask */ - *mask |= (1 << br->bid); - - /* Choose proper TSC */ - tsc = sched_nb_training_bits[trx->tsc]; - - /* Compose a new burst */ - memset(br->burst, 0, 3); /* TB */ - memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */ - memcpy(br->burst + 61, tsc, 26); /* TSC */ - memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ - memset(br->burst + 145, 0, 3); /* TB */ - br->burst_len = GSM_BURST_LEN; - - LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n", - lchan_desc->name, br->fn, ts->index, br->bid); - - /* In case of a FACCH/H frame, one block less */ - if (lchan->ul_facch_blocks) - lchan->ul_facch_blocks--; - - if ((*mask & 0x0f) == 0x0f) { - /** - * If no more FACCH/H blocks pending, - * confirm data / traffic sending - */ - if (!lchan->ul_facch_blocks) - sched_send_dt_conf(trx, ts, lchan, br->fn, - PRIM_IS_TCH(lchan->prim)); - - /* Forget processed primitive */ - sched_prim_drop(lchan); - } - - return 0; -} diff --git a/src/host/trxcon/sched_lchan_xcch.c b/src/host/trxcon/sched_lchan_xcch.c deleted file mode 100644 index 41677ec1..00000000 --- a/src/host/trxcon/sched_lchan_xcch.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: handlers for DL / UL bursts on logical channels - * - * (C) 2017-2021 by Vadim Yanitskiy <axilirator@gmail.com> - * Contributions by sysmocom - s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <string.h> -#include <stdint.h> - -#include <osmocom/core/logging.h> -#include <osmocom/core/bits.h> - -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/coding/gsm0503_coding.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" - -int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) -{ - const struct trx_lchan_desc *lchan_desc; - uint8_t l2[GSM_MACBLOCK_LEN], *mask; - int n_errors, n_bits_total, rc; - sbit_t *buffer, *offset; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->rx_burst_mask; - buffer = lchan->rx_bursts; - - LOGP(DSCHD, LOGL_DEBUG, "Data received on %s: fn=%u ts=%u bid=%u\n", - lchan_desc->name, fn, ts->index, bid); - - /* Align to the first burst of a block */ - if (*mask == 0x00 && bid != 0) - return 0; - - /* Update mask */ - *mask |= (1 << bid); - - /* Store the measurements */ - sched_trx_meas_push(lchan, meas); - - /* Copy burst to buffer of 4 bursts */ - offset = buffer + bid * 116; - memcpy(offset, bits + 3, 58); - memcpy(offset + 58, bits + 87, 58); - - /* Wait until complete set of bursts */ - if (bid != 3) - return 0; - - /* Calculate AVG of the measurements */ - sched_trx_meas_avg(lchan, 4); - - /* Check for complete set of bursts */ - if ((*mask & 0xf) != 0xf) { - LOGP(DSCHD, LOGL_ERROR, "Received incomplete (%s) data frame at " - "fn=%u (%u/%u) for %s\n", - burst_mask2str(mask, 4), lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - /* NOTE: xCCH has an insane amount of redundancy for error - * correction, so even just 2 valid bursts might be enough - * to reconstruct some L2 frames. This is why we do not - * abort here. */ - } - - /* Keep the mask updated */ - *mask = *mask << 4; - - /* Attempt to decode */ - rc = gsm0503_xcch_decode(l2, buffer, &n_errors, &n_bits_total); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Received bad data frame at fn=%u " - "(%u/%u) for %s\n", lchan->meas_avg.fn, - lchan->meas_avg.fn % ts->mf_layout->period, - ts->mf_layout->period, - lchan_desc->name); - - /** - * We should anyway send dummy frame for - * proper measurement reporting... - */ - return sched_send_dt_ind(trx, ts, lchan, NULL, 0, - n_errors, true, false); - } - - /* Send a L2 frame to the higher layers */ - return sched_send_dt_ind(trx, ts, lchan, l2, GSM_MACBLOCK_LEN, - n_errors, false, false); -} - -int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br) -{ - const struct trx_lchan_desc *lchan_desc; - ubit_t *buffer, *offset; - const uint8_t *tsc; - uint8_t *mask; - int rc; - - /* Set up pointers */ - lchan_desc = &trx_lchan_desc[lchan->type]; - mask = &lchan->tx_burst_mask; - buffer = lchan->tx_bursts; - - if (br->bid > 0) { - /* If we have encoded bursts */ - if (*mask) - goto send_burst; - else - return 0; - } - - /* Check the prim payload length */ - if (lchan->prim->payload_len != GSM_MACBLOCK_LEN) { - LOGP(DSCHD, LOGL_ERROR, "Primitive has odd length %zu (expected %u), " - "so dropping...\n", lchan->prim->payload_len, GSM_MACBLOCK_LEN); - - sched_prim_drop(lchan); - return -EINVAL; - } - - /* Encode payload */ - rc = gsm0503_xcch_encode(buffer, lchan->prim->payload); - if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Failed to encode L2 payload (len=%zu): %s\n", - lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload, - lchan->prim->payload_len)); - - /* Forget this primitive */ - sched_prim_drop(lchan); - - return -EINVAL; - } - -send_burst: - /* Determine which burst should be sent */ - offset = buffer + br->bid * 116; - - /* Update mask */ - *mask |= (1 << br->bid); - - /* Choose proper TSC */ - tsc = sched_nb_training_bits[trx->tsc]; - - /* Compose a new burst */ - memset(br->burst, 0, 3); /* TB */ - memcpy(br->burst + 3, offset, 58); /* Payload 1/2 */ - memcpy(br->burst + 61, tsc, 26); /* TSC */ - memcpy(br->burst + 87, offset + 58, 58); /* Payload 2/2 */ - memset(br->burst + 145, 0, 3); /* TB */ - br->burst_len = GSM_BURST_LEN; - - LOGP(DSCHD, LOGL_DEBUG, "Scheduled %s fn=%u ts=%u burst=%u\n", - lchan_desc->name, br->fn, ts->index, br->bid); - - /* If we have sent the last (4/4) burst */ - if ((*mask & 0x0f) == 0x0f) { - /* Confirm data sending */ - sched_send_dt_conf(trx, ts, lchan, br->fn, false); - - /* Forget processed primitive */ - sched_prim_drop(lchan); - - /* Reset mask */ - *mask = 0x00; - } - - return 0; -} diff --git a/src/host/trxcon/sched_mframe.c b/src/host/trxcon/sched_mframe.c deleted file mode 100644 index 9b759af3..00000000 --- a/src/host/trxcon/sched_mframe.c +++ /dev/null @@ -1,2101 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: channel combinations, burst mapping - * - * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu> - * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co> - * (C) 2015 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/gsm/gsm_utils.h> - -#include "sched_trx.h" - -/* Non-combined CCCH */ -static const struct trx_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 }, -}; - -/* Combined CCCH+SDCCH4 */ -static const struct trx_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 const struct trx_frame frame_bcch_sdcch4_cbch[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_IDLE, 0 }, - { TRXC_CCCH, 1, TRXC_IDLE, 1 }, - { TRXC_CCCH, 2, TRXC_IDLE, 2 }, - { TRXC_CCCH, 3, TRXC_IDLE, 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_CBCH, 0, TRXC_RACH, 0 }, - { TRXC_SDCCH4_CBCH, 1, TRXC_RACH, 0 }, - { TRXC_SDCCH4_CBCH, 2, TRXC_RACH, 0 }, - { TRXC_SDCCH4_CBCH, 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_IDLE, 0 }, - { TRXC_SACCH4_1, 2, TRXC_IDLE, 1 }, - { TRXC_SACCH4_1, 3, TRXC_IDLE, 2 }, - { TRXC_IDLE, 0, TRXC_IDLE, 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_CBCH, 0, TRXC_RACH, 0 }, - { TRXC_SDCCH4_CBCH, 1, TRXC_RACH, 0 }, - { TRXC_SDCCH4_CBCH, 2, TRXC_RACH, 0 }, - { TRXC_SDCCH4_CBCH, 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_IDLE, 0, TRXC_SDCCH4_1, 1 }, - { TRXC_IDLE, 1, TRXC_SDCCH4_1, 2 }, - { TRXC_IDLE, 2, TRXC_SDCCH4_1, 3 }, - { TRXC_IDLE, 3, TRXC_RACH, 0 }, - { TRXC_SACCH4_3, 0, TRXC_RACH, 0 }, - { TRXC_SACCH4_3, 1, TRXC_IDLE, 0 }, - { TRXC_SACCH4_3, 2, TRXC_IDLE, 1 }, - { TRXC_SACCH4_3, 3, TRXC_IDLE, 2 }, - { TRXC_IDLE, 0, TRXC_IDLE, 3 }, -}; - -static const struct trx_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 const struct trx_frame frame_sdcch8_cbch[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_CBCH, 0, TRXC_SACCH8_7, 0 }, - { TRXC_SDCCH8_CBCH, 1, TRXC_SACCH8_7, 1 }, - { TRXC_SDCCH8_CBCH, 2, TRXC_SACCH8_7, 2 }, - { TRXC_SDCCH8_CBCH, 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_IDLE, 0 }, - { TRXC_SDCCH8_6, 0, TRXC_IDLE, 1 }, - { TRXC_SDCCH8_6, 1, TRXC_IDLE, 2 }, - { TRXC_SDCCH8_6, 2, TRXC_IDLE, 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_IDLE, 0, TRXC_SDCCH8_6, 1 }, - { TRXC_IDLE, 1, TRXC_SDCCH8_6, 2 }, - { TRXC_IDLE, 2, TRXC_SDCCH8_6, 3 }, - { TRXC_IDLE, 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_IDLE, 0 }, - { TRXC_SDCCH8_1, 1, TRXC_IDLE, 1 }, - { TRXC_SDCCH8_1, 2, TRXC_IDLE, 2 }, - { TRXC_SDCCH8_1, 3, TRXC_IDLE, 3 }, - { TRXC_SDCCH8_CBCH, 0, TRXC_SACCH8_3, 0 }, - { TRXC_SDCCH8_CBCH, 1, TRXC_SACCH8_3, 1 }, - { TRXC_SDCCH8_CBCH, 2, TRXC_SACCH8_3, 2 }, - { TRXC_SDCCH8_CBCH, 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_IDLE, 0 }, - { TRXC_SDCCH8_6, 0, TRXC_IDLE, 1 }, - { TRXC_SDCCH8_6, 1, TRXC_IDLE, 2 }, - { TRXC_SDCCH8_6, 2, TRXC_IDLE, 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 const struct trx_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_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 const struct trx_frame frame_tchf_ts1[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, 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 const struct trx_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 const struct trx_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 const struct trx_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 const struct trx_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 const struct trx_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 const struct trx_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 const struct trx_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 const struct trx_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 const struct trx_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_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 }, -}; - -static const struct trx_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_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 }, - { 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 const struct trx_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 }, -}; - -/* Logical channel mask for a single channel */ -#define M64(x) \ - ((uint64_t) 0x01 << x) - -/* Logical channel mask for BCCH+CCCH */ -#define M64_BCCH_CCCH \ - (uint64_t) 0x00 \ - | M64(TRXC_FCCH) \ - | M64(TRXC_SCH) \ - | M64(TRXC_BCCH) \ - | M64(TRXC_RACH) \ - | M64(TRXC_CCCH) - -/* Logical channel mask for SDCCH4 (with SACCH, all sub-channels) */ -#define M64_SDCCH4 \ - (uint64_t) 0x00 \ - | M64(TRXC_SDCCH4_0) | M64(TRXC_SACCH4_0) \ - | M64(TRXC_SDCCH4_1) | M64(TRXC_SACCH4_1) \ - | M64(TRXC_SDCCH4_2) | M64(TRXC_SACCH4_2) \ - | M64(TRXC_SDCCH4_3) | M64(TRXC_SACCH4_3) - -/* Logical channel mask for SDCCH8 (with SACCH, all sub-channels) */ -#define M64_SDCCH8 \ - (uint64_t) 0x00 \ - | M64(TRXC_SDCCH8_0) | M64(TRXC_SACCH8_0) \ - | M64(TRXC_SDCCH8_1) | M64(TRXC_SACCH8_1) \ - | M64(TRXC_SDCCH8_2) | M64(TRXC_SACCH8_2) \ - | M64(TRXC_SDCCH8_3) | M64(TRXC_SACCH8_3) \ - | M64(TRXC_SDCCH8_4) | M64(TRXC_SACCH8_4) \ - | M64(TRXC_SDCCH8_5) | M64(TRXC_SACCH8_5) \ - | M64(TRXC_SDCCH8_6) | M64(TRXC_SACCH8_6) \ - | M64(TRXC_SDCCH8_7) | M64(TRXC_SACCH8_7) - -/* Logical channel mask for TCH/F (with SACCH) */ -#define M64_TCHF \ - (uint64_t) 0x00 \ - | M64(TRXC_TCHF) | M64(TRXC_SACCHTF) - -/* Logical channel mask for TCH/H (with SACCH, all sub-channels) */ -#define M64_TCHH \ - (uint64_t) 0x00 \ - | M64(TRXC_TCHH_0) | M64(TRXC_SACCHTH_0) \ - | M64(TRXC_TCHH_1) | M64(TRXC_SACCHTH_1) - -/** - * A few notes about frame count: - * - * 26 frame multiframe - traffic multiframe - * 51 frame multiframe - control multiframe - * - * 102 = 2 x 51 frame multiframe - * 104 = 4 x 26 frame multiframe - */ -static const struct trx_multiframe layouts[] = { - { - GSM_PCHAN_NONE, "NONE", - 0, 0xff, - 0x00, - NULL - }, - { - GSM_PCHAN_CCCH, "BCCH+CCCH", - 51, 0xff, - M64_BCCH_CCCH, - frame_bcch - }, - { - GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4", - 102, 0xff, - M64_BCCH_CCCH | M64_SDCCH4, - frame_bcch_sdcch4 - }, - { - GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH", - 102, 0xff, - M64_BCCH_CCCH | M64_SDCCH4 | M64(TRXC_SDCCH4_CBCH), - frame_bcch_sdcch4_cbch - }, - { - GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8", - 102, 0xff, - M64_SDCCH8, - frame_sdcch8 - }, - { - GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH", - 102, 0xff, - M64_SDCCH8 | M64(TRXC_SDCCH8_CBCH), - frame_sdcch8_cbch - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x01, - M64_TCHF, - frame_tchf_ts0 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x02, - M64_TCHF, - frame_tchf_ts1 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x04, - M64_TCHF, - frame_tchf_ts2 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x08, - M64_TCHF, - frame_tchf_ts3 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x10, - M64_TCHF, - frame_tchf_ts4 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x20, - M64_TCHF, - frame_tchf_ts5 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x40, - M64_TCHF, - frame_tchf_ts6 - }, - { - GSM_PCHAN_TCH_F, "TCH/F+SACCH", - 104, 0x80, - M64_TCHF, - frame_tchf_ts7 - }, - { - GSM_PCHAN_TCH_H, "TCH/H+SACCH", - 104, 0x03, - M64_TCHH, - frame_tchh_ts01 - }, - { - GSM_PCHAN_TCH_H, "TCH/H+SACCH", - 104, 0x0c, - M64_TCHH, - frame_tchh_ts23 - }, - { - GSM_PCHAN_TCH_H, "TCH/H+SACCH", - 104, 0x30, - M64_TCHH, - frame_tchh_ts45 - }, - { - GSM_PCHAN_TCH_H, "TCH/H+SACCH", - 104, 0xc0, - M64_TCHH, - frame_tchh_ts67 - }, - { - GSM_PCHAN_PDCH, "PDCH", - 104, 0xff, - M64(TRXC_PDTCH) | M64(TRXC_PTCCH), - frame_pdch - }, -}; - -const struct trx_multiframe *sched_mframe_layout( - enum gsm_phys_chan_config config, int tn) -{ - int i, ts_allowed; - - for (i = 0; i < ARRAY_SIZE(layouts); i++) { - ts_allowed = layouts[i].slotmask & (0x01 << tn); - if (layouts[i].chan_config == config && ts_allowed) - return &layouts[i]; - } - - return NULL; -} diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c deleted file mode 100644 index 94733203..00000000 --- a/src/host/trxcon/sched_prim.c +++ /dev/null @@ -1,617 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: primitive management - * - * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <talloc.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/linuxlist.h> - -#include <osmocom/gsm/protocol/gsm_04_08.h> - -#include "scheduler.h" -#include "sched_trx.h" -#include "trx_if.h" -#include "logging.h" - -/** - * Initializes a new primitive by allocating memory - * and filling some meta-information (e.g. lchan type). - * - * @param ctx parent talloc context - * @param prim external prim pointer (will point to the allocated prim) - * @param pl_len prim payload length - * @param chan_nr RSL channel description (used to set a proper chan) - * @param link_id RSL link description (used to set a proper chan) - * @return zero in case of success, otherwise a error number - */ -int sched_prim_init(void *ctx, struct trx_ts_prim **prim, - size_t pl_len, uint8_t chan_nr, uint8_t link_id) -{ - enum trx_lchan_type lchan_type; - struct trx_ts_prim *new_prim; - uint8_t len; - - /* Determine lchan type */ - lchan_type = sched_trx_chan_nr2lchan_type(chan_nr, link_id); - if (!lchan_type) { - LOGP(DSCH, LOGL_ERROR, "Couldn't determine lchan type " - "for chan_nr=%02x and link_id=%02x\n", chan_nr, link_id); - return -EINVAL; - } - - /* How much memory do we need? */ - len = sizeof(struct trx_ts_prim); /* Primitive header */ - len += pl_len; /* Requested payload size */ - - /* Allocate a new primitive */ - new_prim = talloc_zero_size(ctx, len); - if (new_prim == NULL) { - LOGP(DSCH, LOGL_ERROR, "Failed to allocate memory\n"); - return -ENOMEM; - } - - /* Init primitive header */ - new_prim->payload_len = pl_len; - new_prim->chan = lchan_type; - - /* Set external pointer */ - *prim = new_prim; - - return 0; -} - -/** - * Adds a primitive to the end of transmit queue of a particular - * timeslot, whose index is parsed from chan_nr. - * - * @param trx TRX instance - * @param prim to be enqueued primitive - * @param chan_nr RSL channel description - * @return zero in case of success, otherwise a error number - */ -int sched_prim_push(struct trx_instance *trx, - struct trx_ts_prim *prim, uint8_t chan_nr) -{ - struct trx_ts *ts; - uint8_t tn; - - /* Determine TS index */ - tn = chan_nr & 0x7; - - /* Check whether required timeslot is allocated and configured */ - ts = trx->ts_list[tn]; - if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DSCH, LOGL_ERROR, "Timeslot %u isn't configured\n", tn); - return -EINVAL; - } - - /** - * Change talloc context of primitive - * from trx to the parent ts - */ - talloc_steal(ts, prim); - - /* Add primitive to TS transmit queue */ - llist_add_tail(&prim->list, &ts->tx_prims); - - return 0; -} - -/** - * Composes a new primitive using either cached (if populated), - * or "dummy" Measurement Report message. - * - * @param lchan lchan to assign a primitive - * @return SACCH primitive to be transmitted - */ -static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan) -{ - struct trx_ts_prim *prim; - uint8_t *mr_src_ptr; - bool cached; - int rc; - - /* "Dummy" Measurement Report */ - static const uint8_t meas_rep_dummy[] = { - /* L1 SACCH pseudo-header */ - 0x0f, 0x00, - - /* LAPDm header */ - 0x01, 0x03, 0x49, - - /* RR Management messages, Measurement Report */ - 0x06, 0x15, - - /* Measurement results (see 3GPP TS 44.018, section 10.5.2.20): - * 0... .... = BA-USED: 0 - * .0.. .... = DTX-USED: DTX was not used - * ..11 0110 = RXLEV-FULL-SERVING-CELL: -57 <= x < -56 dBm (54) - * 0... .... = 3G-BA-USED: 0 - * .1.. .... = MEAS-VALID: The measurement results are not valid - * ..11 0110 = RXLEV-SUB-SERVING-CELL: -57 <= x < -56 dBm (54) - * 0... .... = SI23_BA_USED: 0 - * .000 .... = RXQUAL-FULL-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0) - * .... 000. = RXQUAL-SUB-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0) - * .... ...1 11.. .... = NO-NCELL-M: Neighbour cell information not available */ - 0x36, 0x76, 0x01, 0xc0, - - /* 0** -- Padding with zeroes */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }; - - /* Allocate a new primitive */ - rc = sched_prim_init(lchan, &prim, GSM_MACBLOCK_LEN, - trx_lchan_desc[lchan->type].chan_nr, TRX_CH_LID_SACCH); - OSMO_ASSERT(rc == 0); - - /* Check if the MR cache is populated (verify LAPDm header) */ - cached = (lchan->sacch.mr_cache[2] != 0x00 - && lchan->sacch.mr_cache[3] != 0x00 - && lchan->sacch.mr_cache[4] != 0x00); - if (cached) { /* Use the cached one */ - mr_src_ptr = lchan->sacch.mr_cache; - lchan->sacch.mr_cache_usage++; - } else { /* Use "dummy" one */ - mr_src_ptr = (uint8_t *) meas_rep_dummy; - } - - /* Compose a new Measurement Report primitive */ - memcpy(prim->payload, mr_src_ptr, GSM_MACBLOCK_LEN); - - /** - * Update the L1 SACCH pseudo-header (only for cached MRs) - * - * TODO: filling of the actual values into cached Measurement - * Reports would break the distance spoofing feature. If it - * were known whether the spoofing is enabled or not, we could - * decide whether to update the cached L1 SACCH header here. - */ - if (!cached) { - prim->payload[0] = lchan->ts->trx->tx_power; - prim->payload[1] = lchan->ts->trx->ta; - } - - /* Inform about the cache usage count */ - if (cached && lchan->sacch.mr_cache_usage > 5) { - LOGP(DSCHD, LOGL_NOTICE, "SACCH MR cache usage count=%u > 5 " - "on lchan=%s => ancient measurements, please fix!\n", - lchan->sacch.mr_cache_usage, - trx_lchan_desc[lchan->type].name); - } - - LOGP(DSCHD, LOGL_NOTICE, "Using a %s Measurement Report " - "on lchan=%s\n", (cached ? "cached" : "dummy"), - trx_lchan_desc[lchan->type].name); - - return prim; -} - -/** - * Dequeues a SACCH primitive from transmit queue, if present. - * Otherwise dequeues a cached Measurement Report (the last - * received one). Finally, if the cache is empty, a "dummy" - * measurement report is used. - * - * According to 3GPP TS 04.08, section 3.4.1, SACCH channel - * accompanies either a traffic or a signaling channel. It - * has the particularity that continuous transmission must - * occur in both directions, so on the Uplink direction - * measurement result messages are sent at each possible - * occasion when nothing else has to be sent. The LAPDm - * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not - * applicable on SACCH channels! - * - * Unfortunately, 3GPP TS 04.08 doesn't clearly state - * which "else messages" besides Measurement Reports - * can be send by the MS on SACCH channels. However, - * in sub-clause 3.4.1 it's stated that the interval - * between two successive measurement result messages - * shall not exceed one L2 frame. - * - * @param queue transmit queue to take a prim from - * @param lchan lchan to assign a primitive - * @return SACCH primitive to be transmitted - */ -static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue, - struct trx_lchan_state *lchan) -{ - struct trx_ts_prim *prim_nmr = NULL; - struct trx_ts_prim *prim_mr = NULL; - struct trx_ts_prim *prim; - bool mr_now; - - /* Shall we transmit MR now? */ - mr_now = !lchan->sacch.mr_tx_last; - -#define PRIM_IS_MR(prim) \ - (prim->payload[5] == GSM48_PDISC_RR \ - && prim->payload[6] == GSM48_MT_RR_MEAS_REP) - - /* Iterate over all primitives in the queue */ - llist_for_each_entry(prim, queue, list) { - /* We are looking for particular channel */ - if (prim->chan != lchan->type) - continue; - - /* Look for a Measurement Report */ - if (!prim_mr && PRIM_IS_MR(prim)) - prim_mr = prim; - - /* Look for anything else */ - if (!prim_nmr && !PRIM_IS_MR(prim)) - prim_nmr = prim; - - /* Should we look further? */ - if (mr_now && prim_mr) - break; /* MR was found */ - else if (!mr_now && prim_nmr) - break; /* something else was found */ - } - - LOGP(DSCHD, LOGL_DEBUG, "SACCH MR selection on lchan=%s: " - "mr_tx_last=%d prim_mr=%p prim_nmr=%p\n", - trx_lchan_desc[lchan->type].name, - lchan->sacch.mr_tx_last, - prim_mr, prim_nmr); - - /* Prioritize non-MR prim if possible */ - if (mr_now && prim_mr) - prim = prim_mr; - else if (!mr_now && prim_nmr) - prim = prim_nmr; - else if (!mr_now && prim_mr) - prim = prim_mr; - else /* Nothing was found */ - prim = NULL; - - /* Have we found what we were looking for? */ - if (prim) /* Dequeue if so */ - llist_del(&prim->list); - else /* Otherwise compose a new MR */ - prim = prim_compose_mr(lchan); - - /* Update the cached report */ - if (prim == prim_mr) { - memcpy(lchan->sacch.mr_cache, - prim->payload, GSM_MACBLOCK_LEN); - lchan->sacch.mr_cache_usage = 0; - - LOGP(DSCHD, LOGL_DEBUG, "SACCH MR cache has been updated " - "for lchan=%s\n", trx_lchan_desc[lchan->type].name); - } - - /* Update the MR transmission state */ - lchan->sacch.mr_tx_last = PRIM_IS_MR(prim); - - LOGP(DSCHD, LOGL_DEBUG, "SACCH decision on lchan=%s: %s\n", - trx_lchan_desc[lchan->type].name, PRIM_IS_MR(prim) ? - "Measurement Report" : "data frame"); - - return prim; -} - -/* Dequeues a primitive of a given channel type */ -static struct trx_ts_prim *prim_dequeue_one(struct llist_head *queue, - enum trx_lchan_type lchan_type) -{ - struct trx_ts_prim *prim; - - /** - * There is no need to use the 'safe' list iteration here - * as an item removal is immediately followed by return. - */ - llist_for_each_entry(prim, queue, list) { - if (prim->chan == lchan_type) { - llist_del(&prim->list); - return prim; - } - } - - return NULL; -} - -/** - * Dequeues either a FACCH, or a speech TCH primitive - * of a given channel type (Lm or Bm). - * - * Note: we could avoid 'lchan_type' parameter and just - * check the prim's channel type using CHAN_IS_TCH(), - * but the current approach is a bit more flexible, - * and allows one to have both sub-slots of TCH/H - * enabled on same timeslot e.g. for testing... - * - * @param queue transmit queue to take a prim from - * @param lchan_type required channel type of a primitive, - * e.g. TRXC_TCHF, TRXC_TCHH_0, or TRXC_TCHH_1 - * @param facch FACCH (true) or speech (false) prim? - * @return either a FACCH, or a TCH primitive if found, - * otherwise NULL - */ -static struct trx_ts_prim *prim_dequeue_tch(struct llist_head *queue, - enum trx_lchan_type lchan_type, bool facch) -{ - struct trx_ts_prim *prim; - - /** - * There is no need to use the 'safe' list iteration here - * as an item removal is immediately followed by return. - */ - llist_for_each_entry(prim, queue, list) { - if (prim->chan != lchan_type) - continue; - - /* Either FACCH, or not FACCH */ - if (PRIM_IS_FACCH(prim) != facch) - continue; - - llist_del(&prim->list); - return prim; - } - - return NULL; -} - -/** - * Dequeues either a TCH/F, or a FACCH/F prim (preferred). - * If a FACCH/F prim is found, one TCH/F prim is being - * dropped (i.e. replaced). - * - * @param queue a transmit queue to take a prim from - * @return either a FACCH/F, or a TCH/F primitive, - * otherwise NULL - */ -static struct trx_ts_prim *prim_dequeue_tch_f(struct llist_head *queue) -{ - struct trx_ts_prim *facch; - struct trx_ts_prim *tch; - - /* Attempt to find a pair of both FACCH/F and TCH/F frames */ - facch = prim_dequeue_tch(queue, TRXC_TCHF, true); - tch = prim_dequeue_tch(queue, TRXC_TCHF, false); - - /* Prioritize FACCH/F, if found */ - if (facch) { - /* One TCH/F prim is replaced */ - if (tch) - talloc_free(tch); - return facch; - } else if (tch) { - /* Only TCH/F prim was found */ - return tch; - } else { - /* Nothing was found, e.g. when only SACCH frames are in queue */ - return NULL; - } -} - -/** - * Dequeues either a TCH/H, or a FACCH/H prim (preferred). - * If a FACCH/H prim is found, two TCH/H prims are being - * dropped (i.e. replaced). - * - * According to GSM 05.02, the following blocks can be used - * to carry FACCH/H data (see clause 7, table 1 of 9): - * - * UL FACCH/H0: - * B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) - * - * UL FACCH/H1: - * B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) - * - * where the numbers within brackets are fn % 26. - * - * @param queue transmit queue to take a prim from - * @param fn the current frame number - * @param lchan_type required channel type of a primitive, - * @return either a FACCH/H, or a TCH/H primitive, - * otherwise NULL - */ -static struct trx_ts_prim *prim_dequeue_tch_h(struct llist_head *queue, - uint32_t fn, enum trx_lchan_type lchan_type) -{ - struct trx_ts_prim *facch; - struct trx_ts_prim *tch; - bool facch_now; - - /* May we initiate an UL FACCH/H frame transmission now? */ - facch_now = sched_tchh_facch_start(lchan_type, fn, true); - if (!facch_now) /* Just dequeue a TCH/H prim */ - goto no_facch; - - /* If there are no FACCH/H prims in the queue */ - facch = prim_dequeue_tch(queue, lchan_type, true); - if (!facch) /* Just dequeue a TCH/H prim */ - goto no_facch; - - /* FACCH/H prim replaces two TCH/F prims */ - tch = prim_dequeue_tch(queue, lchan_type, false); - if (tch) { - /* At least one TCH/H prim is dropped */ - talloc_free(tch); - - /* Attempt to find another */ - tch = prim_dequeue_tch(queue, lchan_type, false); - if (tch) /* Drop the second TCH/H prim */ - talloc_free(tch); - } - - return facch; - -no_facch: - return prim_dequeue_tch(queue, lchan_type, false); -} - -/** - * Dequeues a single primitive of required type - * from a specified transmit queue. - * - * @param queue a transmit queue to take a prim from - * @param fn the current frame number (used for FACCH/H) - * @param lchan logical channel state - * @return a primitive or NULL if not found - */ -struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, - uint32_t fn, struct trx_lchan_state *lchan) -{ - /* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */ - if (CHAN_IS_SACCH(lchan->type)) - return prim_dequeue_sacch(queue, lchan); - - /* There is nothing to dequeue */ - if (llist_empty(queue)) - return NULL; - - switch (lchan->type) { - /* TCH/F requires FACCH/F prioritization */ - case TRXC_TCHF: - return prim_dequeue_tch_f(queue); - - /* FACCH/H prioritization is a bit more complex */ - case TRXC_TCHH_0: - case TRXC_TCHH_1: - return prim_dequeue_tch_h(queue, fn, lchan->type); - - /* Other kinds of logical channels */ - default: - return prim_dequeue_one(queue, lchan->type); - } -} - -/** - * Drops the current primitive of specified logical channel - * - * @param lchan a logical channel to drop prim from - */ -void sched_prim_drop(struct trx_lchan_state *lchan) -{ - /* Forget this primitive */ - talloc_free(lchan->prim); - lchan->prim = NULL; -} - -/** - * Assigns a dummy primitive to a lchan depending on its type. - * Could be used when there is nothing to transmit, but - * CBTX (Continuous Burst Transmission) is assumed. - * - * @param lchan lchan to assign a primitive - * @return zero in case of success, otherwise a error code - */ -int sched_prim_dummy(struct trx_lchan_state *lchan) -{ - enum trx_lchan_type chan = lchan->type; - uint8_t tch_mode = lchan->tch_mode; - struct trx_ts_prim *prim; - uint8_t prim_buffer[40]; - size_t prim_len = 0; - int i; - - /** - * TS 144.006, section 8.4.2.3 "Fill frames" - * A fill frame is a UI command frame for SAPI 0, P=0 - * and with an information field of 0 octet length. - */ - static const uint8_t lapdm_fill_frame[] = { - 0x01, 0x03, 0x01, 0x2b, - /* Pending part is to be randomized */ - }; - - /* Make sure that there is no existing primitive */ - OSMO_ASSERT(lchan->prim == NULL); - /* Not applicable for SACCH! */ - OSMO_ASSERT(!CHAN_IS_SACCH(lchan->type)); - - /** - * Determine what actually should be generated: - * TCH in GSM48_CMODE_SIGN: LAPDm fill frame; - * TCH in other modes: silence frame; - * other channels: LAPDm fill frame. - */ - if (CHAN_IS_TCH(chan) && TCH_MODE_IS_SPEECH(tch_mode)) { - /* Bad frame indication */ - prim_len = sched_bad_frame_ind(prim_buffer, lchan); - } else if (CHAN_IS_TCH(chan) && TCH_MODE_IS_DATA(tch_mode)) { - /* FIXME: should we do anything for CSD? */ - return 0; - } else { - /* Copy LAPDm fill frame's header */ - memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame)); - - /** - * TS 144.006, section 5.2 "Frame delimitation and fill bits" - * Except for the first octet containing fill bits which shall - * be set to the binary value "00101011", each fill bit should - * be set to a random value when sent by the network. - */ - for (i = sizeof(lapdm_fill_frame); i < GSM_MACBLOCK_LEN; i++) - prim_buffer[i] = (uint8_t) rand(); - - /* Define a prim length */ - prim_len = GSM_MACBLOCK_LEN; - } - - /* Nothing to allocate / assign */ - if (!prim_len) - return 0; - - /* Allocate a new primitive */ - prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len); - if (prim == NULL) - return -ENOMEM; - - /* Init primitive header */ - prim->payload_len = prim_len; - prim->chan = lchan->type; - - /* Fill in the payload */ - memcpy(prim->payload, prim_buffer, prim_len); - - /* Assign the current prim */ - lchan->prim = prim; - - LOGP(DSCHD, LOGL_DEBUG, "Transmitting a dummy / silence frame " - "on lchan=%s\n", trx_lchan_desc[chan].name); - - return 0; -} - -/** - * Flushes a queue of primitives - * - * @param list list of prims going to be flushed - */ -void sched_prim_flush_queue(struct llist_head *list) -{ - struct trx_ts_prim *prim, *prim_next; - - llist_for_each_entry_safe(prim, prim_next, list, list) { - llist_del(&prim->list); - talloc_free(prim); - } -} diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c deleted file mode 100644 index adcf198b..00000000 --- a/src/host/trxcon/sched_trx.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * OsmocomBB <-> SDR connection bridge - * TDMA scheduler: GSM PHY routines - * - * (C) 2017-2019 by Vadim Yanitskiy <axilirator@gmail.com> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <error.h> -#include <errno.h> -#include <string.h> -#include <talloc.h> -#include <stdbool.h> - -#include <osmocom/gsm/a5.h> -#include <osmocom/gsm/protocol/gsm_08_58.h> -#include <osmocom/core/bits.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/linuxlist.h> - -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "trx_if.h" -#include "logging.h" - -static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan, - struct sched_burst_req *br); - -static void sched_frame_clck_cb(struct trx_sched *sched) -{ - struct trx_instance *trx = (struct trx_instance *) sched->data; - struct sched_burst_req br[TRX_TS_COUNT]; - const struct trx_frame *frame; - struct trx_lchan_state *lchan; - trx_lchan_tx_func *handler; - enum trx_lchan_type chan; - uint8_t offset; - struct trx_ts *ts; - int i; - - /* Advance TDMA frame number in order to give the transceiver - * more time to handle the burst before the actual transmission. */ - const uint32_t fn = GSM_TDMA_FN_SUM(sched->fn_counter_proc, - sched->fn_counter_advance); - - /* Iterate over timeslot list */ - for (i = 0; i < TRX_TS_COUNT; i++) { - /* Initialize the buffer for this timeslot */ - br[i] = (struct sched_burst_req) { - .fn = fn, - .tn = i, - .pwr = trx->tx_power, - .burst_len = 0, /* NOPE.ind */ - }; - - /* Timeslot is not allocated */ - ts = trx->ts_list[i]; - if (ts == NULL) - continue; - - /* Timeslot is not configured */ - if (ts->mf_layout == NULL) - continue; - - /* Get frame from multiframe */ - offset = fn % ts->mf_layout->period; - frame = ts->mf_layout->frames + offset; - - /* Get required info from frame */ - br[i].bid = frame->ul_bid; - chan = frame->ul_chan; - handler = trx_lchan_desc[chan].tx_fn; - - /* Omit lchans without handler */ - if (!handler) - continue; - - /* Make sure that lchan was allocated and activated */ - lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) - continue; - - /* Omit inactive lchans */ - if (!lchan->active) - continue; - - /** - * If we aren't processing any primitive yet, - * attempt to obtain a new one from queue - */ - if (lchan->prim == NULL) - lchan->prim = sched_prim_dequeue(&ts->tx_prims, fn, lchan); - - /* TODO: report TX buffers health to the higher layers */ - - /* If CBTX (Continuous Burst Transmission) is assumed */ - if (trx_lchan_desc[chan].flags & TRX_CH_FLAG_CBTX) { - /** - * Probably, a TX buffer is empty. Nevertheless, - * we shall continuously transmit anything on - * CBTX channels. - */ - if (lchan->prim == NULL) - sched_prim_dummy(lchan); - } - - /* If there is no primitive, do nothing */ - if (lchan->prim == NULL) - continue; - - /* Handover RACH needs to be handled regardless of the - * current channel type and the associated handler. */ - if (PRIM_IS_RACH(lchan->prim) && lchan->prim->chan != TRXC_RACH) - handler = trx_lchan_desc[TRXC_RACH].tx_fn; - - /* Poke lchan handler */ - handler(trx, ts, lchan, &br[i]); - - /* Perform A5/X burst encryption if required */ - if (lchan->a5.algo) - sched_trx_a5_burst_enc(lchan, &br[i]); - } - - /* Send all bursts for this TDMA frame */ - for (i = 0; i < ARRAY_SIZE(br); i++) - trx_if_tx_burst(trx, &br[i]); -} - -int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance) -{ - struct trx_sched *sched; - - if (!trx) - return -EINVAL; - - LOGP(DSCH, LOGL_NOTICE, "Init scheduler\n"); - - /* Obtain a scheduler instance from TRX */ - sched = &trx->sched; - - /* Register frame clock callback */ - sched->clock_cb = sched_frame_clck_cb; - - /* Set pointers */ - sched = &trx->sched; - sched->data = trx; - - /* Set frame counter advance */ - sched->fn_counter_advance = fn_advance; - - return 0; -} - -int sched_trx_shutdown(struct trx_instance *trx) -{ - int i; - - if (!trx) - return -EINVAL; - - LOGP(DSCH, LOGL_NOTICE, "Shutdown scheduler\n"); - - /* Free all potentially allocated timeslots */ - for (i = 0; i < TRX_TS_COUNT; i++) - sched_trx_del_ts(trx, i); - - return 0; -} - -int sched_trx_reset(struct trx_instance *trx, bool reset_clock) -{ - int i; - - if (!trx) - return -EINVAL; - - LOGP(DSCH, LOGL_NOTICE, "Reset scheduler %s\n", - reset_clock ? "and clock counter" : ""); - - /* Free all potentially allocated timeslots */ - for (i = 0; i < TRX_TS_COUNT; i++) - sched_trx_del_ts(trx, i); - - /* Stop and reset clock counter if required */ - if (reset_clock) - sched_clck_reset(&trx->sched); - - return 0; -} - -struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn) -{ - /* Make sure that ts isn't allocated yet */ - if (trx->ts_list[tn] != NULL) { - LOGP(DSCH, LOGL_ERROR, "Timeslot #%u already allocated\n", tn); - return NULL; - } - - LOGP(DSCH, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn); - - /* Allocate a new one */ - trx->ts_list[tn] = talloc_zero(trx, struct trx_ts); - - /* Add backpointer */ - trx->ts_list[tn]->trx = trx; - - /* Assign TS index */ - trx->ts_list[tn]->index = tn; - - return trx->ts_list[tn]; -} - -void sched_trx_del_ts(struct trx_instance *trx, int tn) -{ - struct trx_lchan_state *lchan, *lchan_next; - struct trx_ts *ts; - - /* Find ts in list */ - ts = trx->ts_list[tn]; - if (ts == NULL) - return; - - LOGP(DSCH, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); - - /* Deactivate all logical channels */ - sched_trx_deactivate_all_lchans(ts); - - /* Free channel states */ - llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { - llist_del(&lchan->list); - talloc_free(lchan); - } - - /* Flush queue primitives for TX */ - sched_prim_flush_queue(&ts->tx_prims); - - /* Remove ts from list and free memory */ - trx->ts_list[tn] = NULL; - talloc_free(ts); - - /* Notify transceiver about that */ - trx_if_cmd_setslot(trx, tn, 0); -} - -#define LAYOUT_HAS_LCHAN(layout, lchan) \ - (layout->lchan_mask & ((uint64_t) 0x01 << lchan)) - -int sched_trx_configure_ts(struct trx_instance *trx, int tn, - enum gsm_phys_chan_config config) -{ - struct trx_lchan_state *lchan; - enum trx_lchan_type type; - struct trx_ts *ts; - - /* Try to find specified ts */ - ts = trx->ts_list[tn]; - if (ts != NULL) { - /* Reconfiguration of existing one */ - sched_trx_reset_ts(trx, tn); - } else { - /* Allocate a new one if doesn't exist */ - ts = sched_trx_add_ts(trx, tn); - if (ts == NULL) - return -ENOMEM; - } - - /* Choose proper multiframe layout */ - ts->mf_layout = sched_mframe_layout(config, tn); - if (!ts->mf_layout) - return -EINVAL; - if (ts->mf_layout->chan_config != config) - return -EINVAL; - - LOGP(DSCH, LOGL_NOTICE, "(Re)configure TDMA timeslot #%u as %s\n", - tn, ts->mf_layout->name); - - /* Init queue primitives for TX */ - INIT_LLIST_HEAD(&ts->tx_prims); - /* Init logical channels list */ - INIT_LLIST_HEAD(&ts->lchans); - - /* Allocate channel states */ - for (type = 0; type < _TRX_CHAN_MAX; type++) { - if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type)) - continue; - - /* Allocate a channel state */ - lchan = talloc_zero(ts, struct trx_lchan_state); - if (!lchan) - return -ENOMEM; - - /* set backpointer */ - lchan->ts = ts; - - /* Set channel type */ - lchan->type = type; - - /* Add to the list of channel states */ - llist_add_tail(&lchan->list, &ts->lchans); - - /* Enable channel automatically if required */ - if (trx_lchan_desc[type].flags & TRX_CH_FLAG_AUTO) - sched_trx_activate_lchan(ts, type); - } - - /* Notify transceiver about TS activation */ - /* FIXME: set proper channel type */ - trx_if_cmd_setslot(trx, tn, 1); - - return 0; -} - -int sched_trx_reset_ts(struct trx_instance *trx, int tn) -{ - struct trx_lchan_state *lchan, *lchan_next; - struct trx_ts *ts; - - /* Try to find specified ts */ - ts = trx->ts_list[tn]; - if (ts == NULL) - return -EINVAL; - - /* Undefine multiframe layout */ - ts->mf_layout = NULL; - - /* Flush queue primitives for TX */ - sched_prim_flush_queue(&ts->tx_prims); - - /* Deactivate all logical channels */ - sched_trx_deactivate_all_lchans(ts); - - /* Free channel states */ - llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { - llist_del(&lchan->list); - talloc_free(lchan); - } - - /* Notify transceiver about that */ - trx_if_cmd_setslot(trx, tn, 0); - - return 0; -} - -int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, - uint8_t *key, uint8_t key_len) -{ - struct trx_lchan_state *lchan; - - /* Prevent NULL-pointer deference */ - if (!ts) - return -EINVAL; - - /* Make sure we can store this key */ - if (key_len > MAX_A5_KEY_LEN) - return -ERANGE; - - /* Iterate over all allocated logical channels */ - llist_for_each_entry(lchan, &ts->lchans, list) { - /* Omit inactive channels */ - if (!lchan->active) - continue; - - /* Set key length and algorithm */ - lchan->a5.key_len = key_len; - lchan->a5.algo = algo; - - /* Copy requested key */ - if (key_len) - memcpy(lchan->a5.key, key, key_len); - } - - return 0; -} - -struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, - enum trx_lchan_type chan) -{ - struct trx_lchan_state *lchan; - - llist_for_each_entry(lchan, &ts->lchans, list) - if (lchan->type == chan) - return lchan; - - return NULL; -} - -int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode) -{ - const struct trx_lchan_desc *lchan_desc; - struct trx_lchan_state *lchan; - int rc = 0; - - /* Prevent NULL-pointer deference */ - if (ts == NULL) { - LOGP(DSCH, LOGL_ERROR, "Timeslot isn't configured\n"); - return -EINVAL; - } - - /* Iterate over all allocated lchans */ - llist_for_each_entry(lchan, &ts->lchans, list) { - lchan_desc = &trx_lchan_desc[lchan->type]; - - if (lchan_desc->chan_nr == (chan_nr & 0xf8)) { - if (active) { - rc |= sched_trx_activate_lchan(ts, lchan->type); - lchan->tch_mode = tch_mode; - } else - rc |= sched_trx_deactivate_lchan(ts, lchan->type); - } - } - - return rc; -} - -int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) -{ - const struct trx_lchan_desc *lchan_desc = &trx_lchan_desc[chan]; - struct trx_lchan_state *lchan; - - /* Try to find requested logical channel */ - lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) - return -EINVAL; - - if (lchan->active) { - LOGP(DSCH, LOGL_ERROR, "Logical channel %s already activated " - "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); - return -EINVAL; - } - - LOGP(DSCH, LOGL_NOTICE, "Activating lchan=%s " - "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); - - /* Conditionally allocate memory for bursts */ - if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) { - lchan->rx_bursts = talloc_zero_size(lchan, - lchan_desc->burst_buf_size); - if (lchan->rx_bursts == NULL) - return -ENOMEM; - } - - if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) { - lchan->tx_bursts = talloc_zero_size(lchan, - lchan_desc->burst_buf_size); - if (lchan->tx_bursts == NULL) - return -ENOMEM; - } - - /* Finally, update channel status */ - lchan->active = 1; - - return 0; -} - -static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) -{ - /* Prevent NULL-pointer deference */ - OSMO_ASSERT(lchan != NULL); - - /* Print some TDMA statistics for Downlink */ - if (trx_lchan_desc[lchan->type].rx_fn && lchan->active) { - LOGP(DSCH, LOGL_DEBUG, "TDMA statistics for lchan=%s on ts=%u: " - "%lu DL frames have been processed, " - "%lu lost (compensated), last fn=%u\n", - trx_lchan_desc[lchan->type].name, lchan->ts->index, - lchan->tdma.num_proc, lchan->tdma.num_lost, - lchan->tdma.last_proc); - } - - /* Reset internal state variables */ - lchan->rx_burst_mask = 0x00; - lchan->tx_burst_mask = 0x00; - - /* Free burst memory */ - talloc_free(lchan->rx_bursts); - talloc_free(lchan->tx_bursts); - - lchan->rx_bursts = NULL; - lchan->tx_bursts = NULL; - - /* Forget the current prim */ - sched_prim_drop(lchan); - - /* Channel specific stuff */ - if (CHAN_IS_TCH(lchan->type)) { - lchan->dl_ongoing_facch = 0; - lchan->ul_facch_blocks = 0; - - lchan->tch_mode = GSM48_CMODE_SIGN; - - /* Reset AMR state */ - memset(&lchan->amr, 0x00, sizeof(lchan->amr)); - } else if (CHAN_IS_SACCH(lchan->type)) { - /* Reset SACCH state */ - memset(&lchan->sacch, 0x00, sizeof(lchan->sacch)); - } - - /* Reset ciphering state */ - memset(&lchan->a5, 0x00, sizeof(lchan->a5)); - - /* Reset TDMA frame statistics */ - memset(&lchan->tdma, 0x00, sizeof(lchan->tdma)); -} - -int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan) -{ - struct trx_lchan_state *lchan; - - /* Try to find requested logical channel */ - lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) - return -EINVAL; - - if (!lchan->active) { - LOGP(DSCH, LOGL_ERROR, "Logical channel %s already deactivated " - "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); - return -EINVAL; - } - - LOGP(DSCH, LOGL_DEBUG, "Deactivating lchan=%s " - "on ts=%d\n", trx_lchan_desc[chan].name, ts->index); - - /* Reset internal state, free memory */ - sched_trx_reset_lchan(lchan); - - /* Update activation flag */ - lchan->active = 0; - - return 0; -} - -void sched_trx_deactivate_all_lchans(struct trx_ts *ts) -{ - struct trx_lchan_state *lchan; - - LOGP(DSCH, LOGL_DEBUG, "Deactivating all logical channels " - "on ts=%d\n", ts->index); - - llist_for_each_entry(lchan, &ts->lchans, list) { - /* Omit inactive channels */ - if (!lchan->active) - continue; - - /* Reset internal state, free memory */ - sched_trx_reset_lchan(lchan); - - /* Update activation flag */ - lchan->active = 0; - } -} - -enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr) -{ - uint8_t cbits = chan_nr >> 3; - - if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs) - return GSM_PCHAN_TCH_F; - else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0)) - return GSM_PCHAN_TCH_H; - else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0)) - return GSM_PCHAN_CCCH_SDCCH4; - else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0)) - return GSM_PCHAN_SDCCH8_SACCH8C; - else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4) - return GSM_PCHAN_CCCH_SDCCH4_CBCH; - else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8) - return GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH) - return GSM_PCHAN_PDCH; - - return GSM_PCHAN_NONE; -} - -enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, - uint8_t link_id) -{ - int i; - - /* Iterate over all known lchan types */ - for (i = 0; i < _TRX_CHAN_MAX; i++) - if (trx_lchan_desc[i].chan_nr == (chan_nr & 0xf8)) - if (trx_lchan_desc[i].link_id == link_id) - return i; - - return TRXC_IDLE; -} - -static void sched_trx_a5_burst_dec(struct trx_lchan_state *lchan, - uint32_t fn, sbit_t *burst) -{ - ubit_t ks[114]; - int i; - - /* Generate keystream for a DL burst */ - osmo_a5(lchan->a5.algo, lchan->a5.key, fn, ks, NULL); - - /* Apply keystream over ciphertext */ - for (i = 0; i < 57; i++) { - if (ks[i]) - burst[i + 3] *= -1; - if (ks[i + 57]) - burst[i + 88] *= -1; - } -} - -static void sched_trx_a5_burst_enc(struct trx_lchan_state *lchan, - struct sched_burst_req *br) -{ - ubit_t ks[114]; - int i; - - /* Generate keystream for an UL burst */ - osmo_a5(lchan->a5.algo, lchan->a5.key, br->fn, NULL, ks); - - /* Apply keystream over plaintext */ - for (i = 0; i < 57; i++) { - br->burst[i + 3] ^= ks[i]; - br->burst[i + 88] ^= ks[i + 57]; - } -} - -static int subst_frame_loss(struct trx_lchan_state *lchan, - trx_lchan_rx_func *handler, - uint32_t fn) -{ - const struct trx_multiframe *mf; - const struct trx_frame *fp; - int elapsed, i; - - /* Wait until at least one TDMA frame is processed */ - if (lchan->tdma.num_proc == 0) - return -EAGAIN; - - /* Short alias for the current multiframe */ - mf = lchan->ts->mf_layout; - - /* Calculate how many frames elapsed since the last received one. - * The algorithm is based on GSM::FNDelta() from osmo-trx. */ - elapsed = fn - lchan->tdma.last_proc; - if (elapsed >= GSM_TDMA_HYPERFRAME / 2) - elapsed -= GSM_TDMA_HYPERFRAME; - else if (elapsed < -GSM_TDMA_HYPERFRAME / 2) - elapsed += GSM_TDMA_HYPERFRAME; - - /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */ - if (elapsed < 0) { - /* This burst has already been substituted by a dummy burst (all bits set to zero), - * so better drop it. Otherwise we risk to get undefined behavior in handler(). */ - LOGP(DSCHD, LOGL_ERROR, "(%s) Rx burst with fn=%u older than the last " - "processed fn=%u (see OS#4658) => dropping\n", - trx_lchan_desc[lchan->type].name, - fn, lchan->tdma.last_proc); - return -EALREADY; - } - - /* Check how many frames we (potentially) need to compensate */ - if (elapsed > mf->period) { - LOGP(DSCHD, LOGL_NOTICE, "Too many (>%u) contiguous TDMA frames elapsed (%d) " - "since the last processed fn=%u (current %u)\n", - mf->period, elapsed, lchan->tdma.last_proc, fn); - return -EIO; - } else if (elapsed == 0) { - LOGP(DSCHD, LOGL_ERROR, "No TDMA frames elapsed since the last processed " - "fn=%u, must be a bug?\n", lchan->tdma.last_proc); - return -EIO; - } - - static const sbit_t bits[148] = { 0 }; - struct trx_meas_set fake_meas = { - .fn = lchan->tdma.last_proc, - .rssi = -120, - .toa256 = 0, - }; - - /* Traverse from fp till the current frame */ - for (i = 0; i < elapsed - 1; i++) { - fp = &mf->frames[GSM_TDMA_FN_INC(fake_meas.fn) % mf->period]; - if (fp->dl_chan != lchan->type) - continue; - - LOGP(DSCHD, LOGL_NOTICE, "Substituting lost TDMA frame %u on %s\n", - fake_meas.fn, trx_lchan_desc[lchan->type].name); - - handler(lchan->ts->trx, lchan->ts, lchan, - fake_meas.fn, fp->dl_bid, - bits, &fake_meas); - - /* Update TDMA frame statistics */ - lchan->tdma.last_proc = fake_meas.fn; - lchan->tdma.num_proc++; - lchan->tdma.num_lost++; - } - - return 0; -} - -int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, - uint32_t fn, sbit_t *bits, uint16_t nbits, - const struct trx_meas_set *meas) -{ - struct trx_lchan_state *lchan; - const struct trx_frame *frame; - struct trx_ts *ts; - - trx_lchan_rx_func *handler; - enum trx_lchan_type chan; - uint8_t offset, bid; - int rc; - - /* Check whether required timeslot is allocated and configured */ - ts = trx->ts_list[tn]; - if (ts == NULL || ts->mf_layout == NULL) { - LOGP(DSCHD, LOGL_DEBUG, "TDMA timeslot #%u isn't configured, " - "ignoring burst...\n", tn); - return -EINVAL; - } - - /* Get frame from multiframe */ - offset = fn % ts->mf_layout->period; - frame = ts->mf_layout->frames + offset; - - /* Get required info from frame */ - bid = frame->dl_bid; - chan = frame->dl_chan; - handler = trx_lchan_desc[chan].rx_fn; - - /* Omit bursts which have no handler, like IDLE bursts. - * TODO: handle noise indications during IDLE frames. */ - if (!handler) - return -ENODEV; - - /* Find required channel state */ - lchan = sched_trx_find_lchan(ts, chan); - if (lchan == NULL) - return -ENODEV; - - /* Ensure that channel is active */ - if (!lchan->active) - return 0; - - /* Compensate lost TDMA frames (if any) */ - rc = subst_frame_loss(lchan, handler, fn); - if (rc == -EALREADY) - return rc; - - /* Perform A5/X decryption if required */ - if (lchan->a5.algo) - sched_trx_a5_burst_dec(lchan, fn, bits); - - /* Put burst to handler */ - handler(trx, ts, lchan, fn, bid, bits, meas); - - /* Update TDMA frame statistics */ - lchan->tdma.last_proc = fn; - - if (++lchan->tdma.num_proc == 0) { - /* Theoretically, we may have an integer overflow of num_proc counter. - * As a consequence, subst_frame_loss() will be unable to compensate - * one (potentionally lost) Downlink burst. On practice, it would - * happen once in 4615 * 10e-6 * (2 ^ 32 - 1) seconds or ~6 years. */ - LOGP(DSCHD, LOGL_NOTICE, "Too many TDMA frames have been processed. " - "Are you running trxcon for more than 6 years?!?\n"); - lchan->tdma.num_proc = 1; - } - - return 0; -} - -#define MEAS_HIST_FIRST(hist) \ - (&hist->buf[0]) -#define MEAS_HIST_LAST(hist) \ - (MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1) - -/* Add a new set of measurements to the history */ -void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas) -{ - struct trx_lchan_meas_hist *hist = &lchan->meas_hist; - - /* Find a new position where to store the measurements */ - if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL) - hist->head = MEAS_HIST_FIRST(hist); - else - hist->head++; - - *hist->head = *meas; -} - -/* Calculate the AVG of n measurements from the history */ -void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n) -{ - struct trx_lchan_meas_hist *hist = &lchan->meas_hist; - struct trx_meas_set *meas = hist->head; - int toa256_sum = 0; - int rssi_sum = 0; - int i; - - OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf)); - OSMO_ASSERT(meas != NULL); - - /* Traverse backwards up to n entries, calculate the sum */ - for (i = 0; i < n; i++) { - toa256_sum += meas->toa256; - rssi_sum += meas->rssi; - - /* Do not go below the first burst */ - if (i + 1 == n) - break; - - if (meas == MEAS_HIST_FIRST(hist)) - meas = MEAS_HIST_LAST(hist); - else - meas--; - } - - /* Calculate the AVG */ - lchan->meas_avg.toa256 = toa256_sum / n; - lchan->meas_avg.rssi = rssi_sum / n; - - /* As a bonus, store TDMA frame number of the first burst */ - lchan->meas_avg.fn = meas->fn; -} diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h deleted file mode 100644 index 74b41e35..00000000 --- a/src/host/trxcon/sched_trx.h +++ /dev/null @@ -1,415 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <stdbool.h> - -#include <osmocom/core/bits.h> -#include <osmocom/core/utils.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/core/linuxlist.h> - -#include "logging.h" -#include "scheduler.h" - -#define GSM_BURST_LEN 148 -#define GSM_BURST_PL_LEN 116 - -#define GPRS_BURST_LEN GSM_BURST_LEN -#define EDGE_BURST_LEN 444 - -#define GPRS_L2_MAX_LEN 54 -#define EDGE_L2_MAX_LEN 155 - -#define TRX_CH_LID_DEDIC 0x00 -#define TRX_CH_LID_SACCH 0x40 - -/* Osmocom-specific extension for PTCCH (see 3GPP TS 45.002, section 3.3.4.2). - * Shall be used to distinguish PTCCH and PDTCH channels on a PDCH time-slot. */ -#define TRX_CH_LID_PTCCH 0x80 - -/* Is a channel related to PDCH (GPRS) */ -#define TRX_CH_FLAG_PDCH (1 << 0) -/* Should a channel be activated automatically */ -#define TRX_CH_FLAG_AUTO (1 << 1) -/* Is continuous burst transmission assumed */ -#define TRX_CH_FLAG_CBTX (1 << 2) - -#define MAX_A5_KEY_LEN (128 / 8) -#define TRX_TS_COUNT 8 - -/* Forward declaration to avoid mutual include */ -struct trx_lchan_state; -struct trx_meas_set; -struct trx_instance; -struct trx_ts; - -enum trx_burst_type { - TRX_BURST_GMSK, - TRX_BURST_8PSK, -}; - -/** - * These types define the different channels on a multiframe. - * Each channel has queues and can be activated individually. - */ -enum trx_lchan_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, - TRXC_SDCCH4_CBCH, - TRXC_SDCCH8_CBCH, - _TRX_CHAN_MAX -}; - -/* Represents a burst to be transmitted */ -struct sched_burst_req { - uint32_t fn; - uint8_t tn; - uint8_t pwr; - - /* Internally used by the scheduler */ - uint8_t bid; - - ubit_t burst[EDGE_BURST_LEN]; - size_t burst_len; -}; - -typedef int trx_lchan_rx_func(struct trx_instance *trx, - struct trx_ts *ts, struct trx_lchan_state *lchan, - uint32_t fn, uint8_t bid, const sbit_t *bits, - const struct trx_meas_set *meas); - -typedef int trx_lchan_tx_func(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br); - -struct trx_lchan_desc { - /*! \brief Human-readable name */ - const char *name; - /*! \brief Human-readable description */ - const char *desc; - - /*! \brief Channel Number (like in RSL) */ - uint8_t chan_nr; - /*! \brief Link ID (like in RSL) */ - uint8_t link_id; - /*! \brief Sub-slot number (for SDCCH and TCH/H) */ - uint8_t ss_nr; - /*! \brief GSMTAP channel type (see GSMTAP_CHANNEL_*) */ - uint8_t gsmtap_chan_type; - - /*! \brief How much memory do we need to store bursts */ - size_t burst_buf_size; - /*! \brief Channel specific flags */ - uint8_t flags; - - /*! \brief Function to call when burst received from PHY */ - trx_lchan_rx_func *rx_fn; - /*! \brief Function to call when data received from L2 */ - trx_lchan_tx_func *tx_fn; -}; - -struct trx_frame { - /*! \brief Downlink TRX channel type */ - enum trx_lchan_type dl_chan; - /*! \brief Downlink block ID */ - uint8_t dl_bid; - /*! \brief Uplink TRX channel type */ - enum trx_lchan_type ul_chan; - /*! \brief Uplink block ID */ - uint8_t ul_bid; -}; - -struct trx_multiframe { - /*! \brief Channel combination */ - enum gsm_phys_chan_config chan_config; - /*! \brief Human-readable name */ - const char *name; - /*! \brief Repeats how many frames */ - uint8_t period; - /*! \brief Applies to which timeslots */ - uint8_t slotmask; - /*! \brief Contains which lchans */ - uint64_t lchan_mask; - /*! \brief Pointer to scheduling structure */ - const struct trx_frame *frames; -}; - -struct trx_meas_set { - /*! \brief TDMA frame number of the first burst this set belongs to */ - uint32_t fn; - /*! \brief ToA256 (Timing of Arrival, 1/256 of a symbol) */ - int16_t toa256; - /*! \brief RSSI (Received Signal Strength Indication) */ - int8_t rssi; -}; - -/* Simple ring buffer (up to 8 unique measurements) */ -struct trx_lchan_meas_hist { - struct trx_meas_set buf[8]; - struct trx_meas_set *head; -}; - -/* States each channel on a multiframe */ -struct trx_lchan_state { - /*! \brief Channel type */ - enum trx_lchan_type type; - /*! \brief Channel status */ - uint8_t active; - /*! \brief Link to a list of channels */ - struct llist_head list; - - /*! \brief Burst type: GMSK or 8PSK */ - enum trx_burst_type burst_type; - /*! \brief Mask of received bursts */ - uint8_t rx_burst_mask; - /*! \brief Mask of transmitted bursts */ - uint8_t tx_burst_mask; - /*! \brief Burst buffer for RX */ - sbit_t *rx_bursts; - /*! \brief Burst buffer for TX */ - ubit_t *tx_bursts; - - /*! \brief A primitive being sent */ - struct trx_ts_prim *prim; - - /*! \brief Mode for TCH channels (see GSM48_CMODE_*) */ - uint8_t tch_mode; - - /*! \brief FACCH/H on downlink */ - bool dl_ongoing_facch; - /*! \brief pending FACCH/H blocks on Uplink */ - uint8_t ul_facch_blocks; - - /*! \brief Downlink measurements history */ - struct trx_lchan_meas_hist meas_hist; - /*! \brief AVG measurements of the last received block */ - struct trx_meas_set meas_avg; - - /*! \brief TDMA loss detection state */ - struct { - /*! \brief Last processed TDMA frame number */ - uint32_t last_proc; - /*! \brief Number of processed TDMA frames */ - unsigned long num_proc; - /*! \brief Number of lost TDMA frames */ - unsigned long num_lost; - } tdma; - - /*! \brief SACCH state */ - struct { - /*! \brief Cached measurement report (last received) */ - uint8_t mr_cache[GSM_MACBLOCK_LEN]; - /*! \brief Cache usage counter */ - uint8_t mr_cache_usage; - /*! \brief Was a MR transmitted last time? */ - bool mr_tx_last; - } sacch; - - /* AMR specific */ - struct { - /*! \brief 4 possible codecs for AMR */ - uint8_t codec[4]; - /*! \brief Number of possible codecs */ - uint8_t codecs; - /*! \brief Current uplink FT index */ - uint8_t ul_ft; - /*! \brief Current downlink FT index */ - uint8_t dl_ft; - /*! \brief Current uplink CMR index */ - uint8_t ul_cmr; - /*! \brief Current downlink CMR index */ - uint8_t dl_cmr; - /*! \brief If AMR loop is enabled */ - uint8_t amr_loop; - /*! \brief Number of bit error rates */ - uint8_t ber_num; - /*! \brief Sum of bit error rates */ - float ber_sum; - } amr; - - /*! \brief A5/X encryption state */ - struct { - uint8_t key[MAX_A5_KEY_LEN]; - uint8_t key_len; - uint8_t algo; - } a5; - - /* TS that this lchan belongs to */ - struct trx_ts *ts; -}; - -struct trx_ts { - /*! \brief Timeslot index within a frame (0..7) */ - uint8_t index; - - /*! \brief Pointer to multiframe layout */ - const struct trx_multiframe *mf_layout; - /*! \brief Channel states for logical channels */ - struct llist_head lchans; - /*! \brief Queue primitives for TX */ - struct llist_head tx_prims; - /* backpointer to its TRX */ - struct trx_instance *trx; -}; - -/* Represents one TX primitive in the queue of trx_ts */ -struct trx_ts_prim { - /*! \brief Link to queue of TS */ - struct llist_head list; - /*! \brief Logical channel type */ - enum trx_lchan_type chan; - /*! \brief Payload length */ - size_t payload_len; - /*! \brief Payload */ - uint8_t payload[0]; -}; - -extern const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX]; -const struct trx_multiframe *sched_mframe_layout( - enum gsm_phys_chan_config config, int tn); - -/* Scheduler management functions */ -int sched_trx_init(struct trx_instance *trx, uint32_t fn_advance); -int sched_trx_reset(struct trx_instance *trx, bool reset_clock); -int sched_trx_shutdown(struct trx_instance *trx); - -/* Timeslot management functions */ -struct trx_ts *sched_trx_add_ts(struct trx_instance *trx, int tn); -void sched_trx_del_ts(struct trx_instance *trx, int tn); -int sched_trx_reset_ts(struct trx_instance *trx, int tn); -int sched_trx_configure_ts(struct trx_instance *trx, int tn, - enum gsm_phys_chan_config config); -int sched_trx_start_ciphering(struct trx_ts *ts, uint8_t algo, - uint8_t *key, uint8_t key_len); - -/* Logical channel management functions */ -enum gsm_phys_chan_config sched_trx_chan_nr2pchan_config(uint8_t chan_nr); -enum trx_lchan_type sched_trx_chan_nr2lchan_type(uint8_t chan_nr, - uint8_t link_id); - -void sched_trx_deactivate_all_lchans(struct trx_ts *ts); -int sched_trx_set_lchans(struct trx_ts *ts, uint8_t chan_nr, int active, uint8_t tch_mode); -int sched_trx_activate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); -int sched_trx_deactivate_lchan(struct trx_ts *ts, enum trx_lchan_type chan); -struct trx_lchan_state *sched_trx_find_lchan(struct trx_ts *ts, - enum trx_lchan_type chan); - -/* Primitive management functions */ -int sched_prim_init(void *ctx, struct trx_ts_prim **prim, - size_t pl_len, uint8_t chan_nr, uint8_t link_id); -int sched_prim_push(struct trx_instance *trx, - struct trx_ts_prim *prim, uint8_t chan_nr); - -#define TCH_MODE_IS_SPEECH(mode) \ - (mode == GSM48_CMODE_SPEECH_V1 \ - || mode == GSM48_CMODE_SPEECH_EFR \ - || mode == GSM48_CMODE_SPEECH_AMR) - -#define TCH_MODE_IS_DATA(mode) \ - (mode == GSM48_CMODE_DATA_14k5 \ - || mode == GSM48_CMODE_DATA_12k0 \ - || mode == GSM48_CMODE_DATA_6k0 \ - || mode == GSM48_CMODE_DATA_3k6) - -#define CHAN_IS_TCH(chan) \ - (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) - -#define CHAN_IS_SACCH(chan) \ - (trx_lchan_desc[chan].link_id & TRX_CH_LID_SACCH) - -/* FIXME: we need a better way to identify / distinguish primitives */ -#define PRIM_IS_RACH11(prim) \ - (prim->payload_len == sizeof(struct l1ctl_ext_rach_req)) - -#define PRIM_IS_RACH8(prim) \ - (prim->payload_len == sizeof(struct l1ctl_rach_req)) - -#define PRIM_IS_RACH(prim) \ - (PRIM_IS_RACH8(prim) || PRIM_IS_RACH11(prim)) - -#define PRIM_IS_TCH(prim) \ - (CHAN_IS_TCH(prim->chan) && prim->payload_len != GSM_MACBLOCK_LEN) - -#define PRIM_IS_FACCH(prim) \ - (CHAN_IS_TCH(prim->chan) && prim->payload_len == GSM_MACBLOCK_LEN) - -struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, - uint32_t fn, struct trx_lchan_state *lchan); -int sched_prim_dummy(struct trx_lchan_state *lchan); -void sched_prim_drop(struct trx_lchan_state *lchan); -void sched_prim_flush_queue(struct llist_head *list); - -int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, - uint32_t fn, sbit_t *bits, uint16_t nbits, - const struct trx_meas_set *meas); - -/* Shared declarations for lchan handlers */ -extern const uint8_t sched_nb_training_bits[8][26]; - -const char *burst_mask2str(const uint8_t *mask, int bits); -size_t sched_bad_frame_ind(uint8_t *l2, struct trx_lchan_state *lchan); -int sched_send_dt_ind(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint8_t *l2, size_t l2_len, - int bit_error_count, bool dec_failed, bool traffic); -int sched_send_dt_conf(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, bool traffic); -int sched_gsmtap_send(enum trx_lchan_type lchan_type, uint32_t fn, uint8_t tn, - uint16_t band_arfcn, int8_t signal_dbm, uint8_t snr, - const uint8_t *data, size_t data_len); - -/* Interleaved TCH/H block TDMA frame mapping */ -uint32_t sched_tchh_block_dl_first_fn(enum trx_lchan_type chan, - uint32_t last_fn, bool facch); -bool sched_tchh_block_map_fn(enum trx_lchan_type chan, - uint32_t fn, bool ul, bool facch, bool start); - -#define sched_tchh_traffic_start(chan, fn, ul) \ - sched_tchh_block_map_fn(chan, fn, ul, 0, 1) -#define sched_tchh_traffic_end(chan, fn, ul) \ - sched_tchh_block_map_fn(chan, fn, ul, 0, 0) - -#define sched_tchh_facch_start(chan, fn, ul) \ - sched_tchh_block_map_fn(chan, fn, ul, 1, 1) -#define sched_tchh_facch_end(chan, fn, ul) \ - sched_tchh_block_map_fn(chan, fn, ul, 1, 0) - -/* Measurement history */ -void sched_trx_meas_push(struct trx_lchan_state *lchan, const struct trx_meas_set *meas); -void sched_trx_meas_avg(struct trx_lchan_state *lchan, unsigned int n); diff --git a/src/host/trxcon/scheduler.h b/src/host/trxcon/scheduler.h deleted file mode 100644 index 43127cc1..00000000 --- a/src/host/trxcon/scheduler.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <time.h> - -#include <osmocom/core/timer.h> -#include <osmocom/gsm/gsm0502.h> - -enum tdma_sched_clck_state { - SCH_CLCK_STATE_WAIT, - SCH_CLCK_STATE_OK, -}; - -/* Forward structure declaration */ -struct trx_sched; - -/*! \brief One scheduler instance */ -struct trx_sched { - /*! \brief Clock state */ - enum tdma_sched_clck_state state; - /*! \brief Local clock source */ - struct timespec clock; - /*! \brief Count of processed frames */ - uint32_t fn_counter_proc; - /*! \brief Local frame counter advance */ - uint32_t fn_counter_advance; - /*! \brief Count of lost frames */ - uint32_t fn_counter_lost; - /*! \brief Frame callback timer */ - struct osmo_timer_list clock_timer; - /*! \brief Frame callback */ - void (*clock_cb)(struct trx_sched *sched); - /*! \brief Private data (e.g. pointer to trx instance) */ - void *data; -}; - -int sched_clck_handle(struct trx_sched *sched, uint32_t fn); -void sched_clck_reset(struct trx_sched *sched); diff --git a/src/host/trxcon/src/Makefile.am b/src/host/trxcon/src/Makefile.am new file mode 100644 index 00000000..7be7de62 --- /dev/null +++ b/src/host/trxcon/src/Makefile.am @@ -0,0 +1,64 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOCODING_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(NULL) + + +noinst_LTLIBRARIES = libl1sched.la + +libl1sched_la_SOURCES = \ + sched_lchan_common.c \ + sched_lchan_pdtch.c \ + sched_lchan_desc.c \ + sched_lchan_xcch.c \ + sched_lchan_tchf.c \ + sched_lchan_tchh.c \ + sched_lchan_rach.c \ + sched_lchan_sch.c \ + sched_mframe.c \ + sched_prim.c \ + sched_trx.c \ + $(NULL) + + +noinst_LTLIBRARIES += libl1gprs.la + +libl1gprs_la_SOURCES = \ + l1gprs.c \ + $(NULL) + + +noinst_LTLIBRARIES += libtrxcon.la + +libtrxcon_la_SOURCES = \ + trxcon_inst.c \ + trxcon_fsm.c \ + trxcon_shim.c \ + l1ctl.c \ + $(NULL) + + +bin_PROGRAMS = trxcon + +trxcon_SOURCES = \ + l1ctl_server.c \ + trxcon_main.c \ + logging.c \ + trx_if.c \ + $(NULL) + +trxcon_LDADD = \ + libtrxcon.la \ + libl1sched.la \ + libl1gprs.la \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCODING_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c new file mode 100644 index 00000000..0f6bd1be --- /dev/null +++ b/src/host/trxcon/src/l1ctl.c @@ -0,0 +1,849 @@ +/* + * OsmocomBB <-> SDR connection bridge + * GSM L1 control interface handlers + * + * (C) 2014 by Sylvain Munaut <tnt@246tNt.com> + * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include <arpa/inet.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/utils.h> + +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/bb/l1ctl_proto.h> +#include <osmocom/bb/trxcon/logging.h> +#include <osmocom/bb/trxcon/trxcon.h> +#include <osmocom/bb/trxcon/trxcon_fsm.h> + +#define L1CTL_LENGTH 512 +#define L1CTL_HEADROOM 32 + +/* Logging categories configurable via trxcon_set_log_cfg() */ +int g_logc_l1c = DLGLOBAL; +int g_logc_l1d = DLGLOBAL; + +static const struct value_string l1ctl_ccch_mode_names[] = { + { CCCH_MODE_NONE, "NONE" }, + { CCCH_MODE_NON_COMBINED, "NON_COMBINED" }, + { CCCH_MODE_COMBINED, "COMBINED" }, + { CCCH_MODE_COMBINED_CBCH, "COMBINED_CBCH" }, + { 0, NULL }, +}; + +static const struct value_string l1ctl_reset_names[] = { + { L1CTL_RES_T_BOOT, "BOOT" }, + { L1CTL_RES_T_FULL, "FULL" }, + { L1CTL_RES_T_SCHED, "SCHED" }, + { 0, NULL }, +}; + +static const char *arfcn2band_name(uint16_t arfcn) +{ + enum gsm_band band; + + if (gsm_arfcn2band_rc(arfcn, &band) < 0) + return "(invalid)"; + + return gsm_band_name(band); +} + +static struct msgb *l1ctl_alloc_msg(uint8_t msg_type) +{ + struct l1ctl_hdr *l1h; + struct msgb *msg; + + /** + * Each L1CTL message gets its own length pushed in front + * before sending. This is why we need this small headroom. + */ + msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, + L1CTL_HEADROOM, "l1ctl_tx_msg"); + if (!msg) { + LOGP(g_logc_l1c, LOGL_ERROR, "Failed to allocate memory\n"); + return NULL; + } + + msg->l1h = msgb_put(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = msg_type; + + return msg; +} + +int l1ctl_tx_pm_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, int dbm, int last) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + struct l1ctl_pm_conf *pmc; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_PM_CONF); + if (!msg) + return -ENOMEM; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, + "Send PM Conf (%s %d = %d dBm)\n", + arfcn2band_name(band_arfcn), + band_arfcn & ~ARFCN_FLAG_MASK, dbm); + + pmc = (struct l1ctl_pm_conf *) msgb_put(msg, sizeof(*pmc)); + pmc->band_arfcn = htons(band_arfcn); + pmc->pm[0] = dbm2rxlev(dbm); + pmc->pm[1] = 0; + + if (last) { + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->flags |= L1CTL_F_DONE; + } + + return trxcon_l1ctl_send(trxcon, msg); +} + +int l1ctl_tx_reset_ind(struct trxcon_inst *trxcon, uint8_t type) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + struct msgb *msg; + struct l1ctl_reset *res; + + msg = l1ctl_alloc_msg(L1CTL_RESET_IND); + if (!msg) + return -ENOMEM; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send Reset Ind (%s)\n", + get_value_string(l1ctl_reset_names, type)); + + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return trxcon_l1ctl_send(trxcon, msg); +} + +int l1ctl_tx_reset_conf(struct trxcon_inst *trxcon, uint8_t type) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + struct msgb *msg; + struct l1ctl_reset *res; + + msg = l1ctl_alloc_msg(L1CTL_RESET_CONF); + if (!msg) + return -ENOMEM; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send Reset Conf (%s)\n", + get_value_string(l1ctl_reset_names, type)); + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return trxcon_l1ctl_send(trxcon, msg); +} + +static struct l1ctl_info_dl *put_dl_info_hdr(struct msgb *msg, + const struct l1ctl_info_dl *dl_info) +{ + size_t len = sizeof(struct l1ctl_info_dl); + struct l1ctl_info_dl *dl = (struct l1ctl_info_dl *) msgb_put(msg, len); + + if (dl_info) /* Copy DL info provided by handler */ + memcpy(dl, dl_info, len); + else /* Init DL info header */ + memset(dl, 0x00, len); + + return dl; +} + +/* Fill in FBSB payload: BSIC and sync result */ +static struct l1ctl_fbsb_conf *fbsb_conf_make(struct msgb *msg, uint8_t result, uint8_t bsic) +{ + struct l1ctl_fbsb_conf *conf = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*conf)); + + conf->result = result; + conf->bsic = bsic; + + return conf; +} + +int l1ctl_tx_fbsb_fail(struct trxcon_inst *trxcon, uint16_t band_arfcn) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + struct l1ctl_info_dl *dl; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); + if (msg == NULL) + return -ENOMEM; + + dl = put_dl_info_hdr(msg, NULL); + + /* Fill in current ARFCN */ + dl->band_arfcn = htons(band_arfcn); + + fbsb_conf_make(msg, 255, 0); + + LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, "Send FBSB Conf (timeout)\n"); + + return trxcon_l1ctl_send(trxcon, msg); +} + +int l1ctl_tx_fbsb_conf(struct trxcon_inst *trxcon, uint16_t band_arfcn, uint8_t bsic) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + struct l1ctl_fbsb_conf *conf; + struct l1ctl_info_dl *dl; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_FBSB_CONF); + if (msg == NULL) + return -ENOMEM; + + dl = put_dl_info_hdr(msg, NULL); + + /* Fill in current ARFCN */ + dl->band_arfcn = htons(band_arfcn); + + conf = fbsb_conf_make(msg, 0, bsic); + + /* FIXME: set proper value */ + conf->initial_freq_err = 0; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_DEBUG, + "Send FBSB Conf (result=%u, bsic=%u)\n", + conf->result, conf->bsic); + + return trxcon_l1ctl_send(trxcon, msg); +} + +int l1ctl_tx_ccch_mode_conf(struct trxcon_inst *trxcon, uint8_t mode) +{ + struct l1ctl_ccch_mode_conf *conf; + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_CCCH_MODE_CONF); + if (msg == NULL) + return -ENOMEM; + + conf = (struct l1ctl_ccch_mode_conf *) msgb_put(msg, sizeof(*conf)); + conf->ccch_mode = mode; + + return trxcon_l1ctl_send(trxcon, msg); +} + +/** + * Handles both L1CTL_DATA_IND and L1CTL_TRAFFIC_IND. + */ +int l1ctl_tx_dt_ind(struct trxcon_inst *trxcon, + const struct trxcon_param_rx_data_ind *ind) +{ + struct msgb *msg; + + msg = l1ctl_alloc_msg(ind->traffic ? L1CTL_TRAFFIC_IND : L1CTL_DATA_IND); + if (msg == NULL) + return -ENOMEM; + + const struct l1ctl_info_dl dl_hdr = { + .chan_nr = ind->chan_nr, + .link_id = ind->link_id, + .frame_nr = htonl(ind->frame_nr), + .band_arfcn = htons(ind->band_arfcn), + .fire_crc = ind->data_len > 0 ? 0 : 2, + .rx_level = dbm2rxlev(ind->rssi), + .num_biterr = ind->n_errors, + /* TODO: set proper .snr */ + }; + + put_dl_info_hdr(msg, &dl_hdr); + + /* Copy the L2 payload if preset */ + if (ind->data && ind->data_len > 0) + memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len); + + /* Put message to upper layers */ + return trxcon_l1ctl_send(trxcon, msg); +} + +int l1ctl_tx_rach_conf(struct trxcon_inst *trxcon, + const struct trxcon_param_tx_access_burst_cnf *cnf) +{ + struct msgb *msg; + + msg = l1ctl_alloc_msg(L1CTL_RACH_CONF); + if (msg == NULL) + return -ENOMEM; + + const struct l1ctl_info_dl dl_hdr = { + .band_arfcn = htons(cnf->band_arfcn), + .frame_nr = htonl(cnf->frame_nr), + }; + + put_dl_info_hdr(msg, &dl_hdr); + + return trxcon_l1ctl_send(trxcon, msg); +} + + +/** + * Handles both L1CTL_DATA_CONF and L1CTL_TRAFFIC_CONF. + */ +int l1ctl_tx_dt_conf(struct trxcon_inst *trxcon, + const struct trxcon_param_tx_data_cnf *cnf) +{ + struct msgb *msg; + + msg = l1ctl_alloc_msg(cnf->traffic ? L1CTL_TRAFFIC_CONF : L1CTL_DATA_CONF); + if (msg == NULL) + return -ENOMEM; + + const struct l1ctl_info_dl dl_hdr = { + .chan_nr = cnf->chan_nr, + .link_id = cnf->link_id, + .frame_nr = htonl(cnf->frame_nr), + .band_arfcn = htons(cnf->band_arfcn), + }; + + /* Copy DL frame header from source message */ + put_dl_info_hdr(msg, &dl_hdr); + + return trxcon_l1ctl_send(trxcon, msg); +} + +static enum gsm_phys_chan_config l1ctl_ccch_mode2pchan_config(enum ccch_mode mode) +{ + switch (mode) { + /* TODO: distinguish extended BCCH */ + case CCCH_MODE_NON_COMBINED: + case CCCH_MODE_NONE: + return GSM_PCHAN_CCCH; + + case CCCH_MODE_COMBINED: + return GSM_PCHAN_CCCH_SDCCH4; + case CCCH_MODE_COMBINED_CBCH: + return GSM_PCHAN_CCCH_SDCCH4_CBCH; + + default: + LOGP(g_logc_l1c, LOGL_NOTICE, "Undandled CCCH mode (%u), " + "assuming non-combined configuration\n", mode); + return GSM_PCHAN_CCCH; + } +} + +static int l1ctl_rx_fbsb_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_fbsb_req *fbsb; + int rc = 0; + + fbsb = (const struct l1ctl_fbsb_req *)msg->l1h; + if (msgb_l1len(msg) < sizeof(*fbsb)) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "MSG too short FBSB Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + struct trxcon_param_fbsb_search_req req = { + .pchan_config = l1ctl_ccch_mode2pchan_config(fbsb->ccch_mode), + .timeout_fns = ntohs(fbsb->timeout), + .band_arfcn = ntohs(fbsb->band_arfcn), + }; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received FBSB request (%s %d, timeout %u TDMA FNs)\n", + arfcn2band_name(req.band_arfcn), + req.band_arfcn & ~ARFCN_FLAG_MASK, + req.timeout_fns); + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_FBSB_SEARCH_REQ, &req); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_pm_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_pm_req *pmr; + int rc = 0; + + pmr = (const struct l1ctl_pm_req *)msg->l1h; + if (msgb_l1len(msg) < sizeof(*pmr)) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "MSG too short PM Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + struct trxcon_param_full_power_scan_req req = { + .band_arfcn_start = ntohs(pmr->range.band_arfcn_from), + .band_arfcn_stop = ntohs(pmr->range.band_arfcn_to), + }; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received power measurement request (%s: %d -> %d)\n", + arfcn2band_name(req.band_arfcn_start), + req.band_arfcn_start & ~ARFCN_FLAG_MASK, + req.band_arfcn_stop & ~ARFCN_FLAG_MASK); + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_FULL_POWER_SCAN_REQ, &req); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_reset_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_reset *res; + int rc = 0; + + res = (const struct l1ctl_reset *)msg->l1h; + if (msgb_l1len(msg) < sizeof(*res)) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "MSG too short Reset Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received reset request (%s)\n", + get_value_string(l1ctl_reset_names, res->type)); + + switch (res->type) { + case L1CTL_RES_T_FULL: + osmo_fsm_inst_dispatch(fi, TRXCON_EV_RESET_FULL_REQ, NULL); + break; + case L1CTL_RES_T_SCHED: + osmo_fsm_inst_dispatch(fi, TRXCON_EV_RESET_SCHED_REQ, NULL); + break; + default: + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "Unknown L1CTL_RESET_REQ type\n"); + goto exit; + } + + /* Confirm */ + rc = l1ctl_tx_reset_conf(trxcon, res->type); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_echo_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + struct l1ctl_hdr *l1h; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Recv Echo Req\n"); + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Send Echo Conf\n"); + + /* Nothing to do, just send it back */ + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = L1CTL_ECHO_CONF; + msg->data = msg->l1h; + + return trxcon_l1ctl_send(trxcon, msg); +} + +static int l1ctl_rx_ccch_mode_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_ccch_mode_req *mode_req; + int rc; + + mode_req = (const struct l1ctl_ccch_mode_req *)msg->l1h; + if (msgb_l1len(msg) < sizeof(*mode_req)) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "MSG too short Reset Req: %u\n", + msgb_l1len(msg)); + rc = -EINVAL; + goto exit; + } + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Received CCCH mode request (%s)\n", + get_value_string(l1ctl_ccch_mode_names, mode_req->ccch_mode)); + + struct trxcon_param_set_ccch_tch_mode_req req = { + /* Choose corresponding channel combination */ + .mode = l1ctl_ccch_mode2pchan_config(mode_req->ccch_mode), + }; + + rc = osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_CCCH_MODE_REQ, &req); + if (rc == 0 && req.applied) + l1ctl_tx_ccch_mode_conf(trxcon, mode_req->ccch_mode); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_rach_req(struct trxcon_inst *trxcon, struct msgb *msg, bool is_11bit) +{ + struct trxcon_param_tx_access_burst_req req; + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_info_ul *ul; + + ul = (const struct l1ctl_info_ul *)msg->l1h; + + if (is_11bit) { + const struct l1ctl_ext_rach_req *rr = (void *)ul->payload; + + req = (struct trxcon_param_tx_access_burst_req) { + .offset = ntohs(rr->offset), + .synch_seq = rr->synch_seq, + .ra = ntohs(rr->ra11), + .is_11bit = true, + }; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received 11-bit RACH request " + "(offset=%u, synch_seq=%u, ra11=0x%02hx)\n", + req.offset, req.synch_seq, req.ra); + } else { + const struct l1ctl_rach_req *rr = (void *)ul->payload; + + req = (struct trxcon_param_tx_access_burst_req) { + .offset = ntohs(rr->offset), + .ra = rr->ra, + }; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received 8-bit RACH request " + "(offset=%u, ra=0x%02x)\n", req.offset, req.ra); + } + + /* The controlling L1CTL side always does include the UL info header, + * but may leave it empty. We assume RACH is on TS0 in this case. */ + if (ul->chan_nr == 0x00) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "The UL info header is empty, assuming RACH is on TS0\n"); + req.chan_nr = RSL_CHAN_RACH; + req.link_id = 0x00; + } else { + req.chan_nr = ul->chan_nr; + req.link_id = ul->link_id; + } + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_ACCESS_BURST_REQ, &req); + + msgb_free(msg); + return 0; +} + +static int l1ctl_proc_est_req_h0(struct osmo_fsm_inst *fi, + struct trxcon_param_dch_est_req *req, + const struct l1ctl_h0 *h) +{ + req->h0.band_arfcn = ntohs(h->band_arfcn); + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "L1CTL_DM_EST_REQ indicates single ARFCN %s %u\n", + arfcn2band_name(req->h0.band_arfcn), + req->h0.band_arfcn & ~ARFCN_FLAG_MASK); + + return 0; +} + +static int l1ctl_proc_est_req_h1(struct osmo_fsm_inst *fi, + struct trxcon_param_dch_est_req *req, + const struct l1ctl_h1 *h) +{ + unsigned int i; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "L1CTL_DM_EST_REQ indicates a Frequency " + "Hopping (hsn=%u, maio=%u, chans=%u) channel\n", + h->hsn, h->maio, h->n); + + /* No channels?!? */ + if (!h->n) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "No channels in mobile allocation?!?\n"); + return -EINVAL; + } else if (h->n > ARRAY_SIZE(h->ma)) { + LOGPFSMSL(fi, g_logc_l1c, LOGL_ERROR, + "More than 64 channels in mobile allocation?!?\n"); + return -EINVAL; + } + + /* Convert from network to host byte order */ + for (i = 0; i < h->n; i++) + req->h1.ma[i] = ntohs(h->ma[i]); + req->h1.n = h->n; + req->h1.hsn = h->hsn; + req->h1.maio = h->maio; + + return 0; +} + +static int l1ctl_rx_dm_est_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_dm_est_req *est_req; + const struct l1ctl_info_ul *ul; + int rc; + + ul = (const struct l1ctl_info_ul *)msg->l1h; + est_req = (const struct l1ctl_dm_est_req *)ul->payload; + + struct trxcon_param_dch_est_req req = { + .chan_nr = ul->chan_nr, + .tch_mode = est_req->tch_mode, + .tsc = est_req->tsc, + .hopping = est_req->h, + }; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received L1CTL_DM_EST_REQ " + "(tn=%u, chan_nr=0x%02x, tsc=%u, tch_mode=%s)\n", + req.chan_nr & 0x07, req.chan_nr, req.tsc, + gsm48_chan_mode_name(est_req->tch_mode)); + + /* Frequency hopping? */ + if (est_req->h) + rc = l1ctl_proc_est_req_h1(fi, &req, &est_req->h1); + else /* Single ARFCN */ + rc = l1ctl_proc_est_req_h0(fi, &req, &est_req->h0); + if (rc) + goto exit; + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_DCH_EST_REQ, &req); + +exit: + msgb_free(msg); + return rc; +} + +static int l1ctl_rx_dm_rel_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, "Received L1CTL_DM_REL_REQ\n"); + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_DCH_REL_REQ, NULL); + + msgb_free(msg); + return 0; +} + +/** + * Handles both L1CTL_DATA_REQ and L1CTL_TRAFFIC_REQ. + */ +static int l1ctl_rx_dt_req(struct trxcon_inst *trxcon, struct msgb *msg, bool traffic) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_info_ul *ul; + + /* Extract UL frame header */ + ul = (const struct l1ctl_info_ul *)msg->l1h; + msg->l2h = (uint8_t *)ul->payload; + + struct trxcon_param_tx_data_req req = { + .traffic = traffic, + .chan_nr = ul->chan_nr, + .link_id = ul->link_id & 0x40, + .data_len = msgb_l2len(msg), + .data = ul->payload, + }; + + LOGPFSMSL(fi, g_logc_l1d, LOGL_DEBUG, + "Recv %s Req (chan_nr=0x%02x, link_id=0x%02x, len=%zu)\n", + traffic ? "TRAFFIC" : "DATA", req.chan_nr, req.link_id, req.data_len); + + switch (fi->state) { + case TRXCON_ST_DEDICATED: + osmo_fsm_inst_dispatch(fi, TRXCON_EV_TX_DATA_REQ, &req); + break; + default: + if (!traffic && req.link_id == 0x40) /* only for SACCH */ + osmo_fsm_inst_dispatch(fi, TRXCON_EV_UPDATE_SACCH_CACHE_REQ, &req); + /* TODO: log an error about uhnandled DATA.req / TRAFFIC.req */ + } + + msgb_free(msg); + return 0; +} + +static int l1ctl_rx_param_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_par_req *par_req; + const struct l1ctl_info_ul *ul; + + ul = (const struct l1ctl_info_ul *)msg->l1h; + par_req = (const struct l1ctl_par_req *)ul->payload; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received L1CTL_PARAM_REQ (ta=%d, tx_power=%u)\n", + par_req->ta, par_req->tx_power); + + struct trxcon_param_set_phy_config_req req = { + .type = TRXCON_PHY_CFGT_TX_PARAMS, + .tx_params = { + .timing_advance = par_req->ta, + .tx_power = par_req->tx_power, + } + }; + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req); + + msgb_free(msg); + return 0; +} + +static int l1ctl_rx_tch_mode_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_tch_mode_req *mode_req; + int rc; + + mode_req = (const struct l1ctl_tch_mode_req *)msg->l1h; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "Received L1CTL_TCH_MODE_REQ (tch_mode=%s, audio_mode=%u)\n", + gsm48_chan_mode_name(mode_req->tch_mode), mode_req->audio_mode); + + /* TODO: do we need to care about audio_mode? */ + + struct trxcon_param_set_ccch_tch_mode_req req = { + .mode = mode_req->tch_mode, + }; + if (mode_req->tch_mode == GSM48_CMODE_SPEECH_AMR) { + req.amr.start_codec = mode_req->amr.start_codec; + req.amr.codecs_bitmask = mode_req->amr.codecs_bitmask; + } + + rc = osmo_fsm_inst_dispatch(fi, TRXCON_EV_SET_TCH_MODE_REQ, &req); + if (rc != 0 || !req.applied) { + talloc_free(msg); + return rc; + } + + /* Re-use the original message as confirmation */ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + l1h->msg_type = L1CTL_TCH_MODE_CONF; + + return trxcon_l1ctl_send(trxcon, msg); +} + +static int l1ctl_rx_crypto_req(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = trxcon->fi; + const struct l1ctl_crypto_req *cr; + const struct l1ctl_info_ul *ul; + + ul = (const struct l1ctl_info_ul *)msg->l1h; + cr = (const struct l1ctl_crypto_req *)ul->payload; + + struct trxcon_param_crypto_req req = { + .chan_nr = ul->chan_nr, + .a5_algo = cr->algo, + .key_len = cr->key_len, + .key = cr->key, + }; + + LOGPFSMSL(fi, g_logc_l1c, LOGL_NOTICE, + "L1CTL_CRYPTO_REQ (algo=A5/%u, key_len=%u)\n", + req.a5_algo, req.key_len); + + osmo_fsm_inst_dispatch(fi, TRXCON_EV_CRYPTO_REQ, &req); + + msgb_free(msg); + return 0; +} + +int trxcon_l1ctl_receive(struct trxcon_inst *trxcon, struct msgb *msg) +{ + const struct l1ctl_hdr *l1h; + int rc; + + l1h = (const struct l1ctl_hdr *)msg->l1h; + msg->l1h = (uint8_t *)l1h->data; + + switch (l1h->msg_type) { + case L1CTL_FBSB_REQ: + return l1ctl_rx_fbsb_req(trxcon, msg); + case L1CTL_PM_REQ: + return l1ctl_rx_pm_req(trxcon, msg); + case L1CTL_RESET_REQ: + return l1ctl_rx_reset_req(trxcon, msg); + case L1CTL_ECHO_REQ: + return l1ctl_rx_echo_req(trxcon, msg); + case L1CTL_CCCH_MODE_REQ: + return l1ctl_rx_ccch_mode_req(trxcon, msg); + case L1CTL_RACH_REQ: + return l1ctl_rx_rach_req(trxcon, msg, false); + case L1CTL_EXT_RACH_REQ: + return l1ctl_rx_rach_req(trxcon, msg, true); + case L1CTL_DM_EST_REQ: + return l1ctl_rx_dm_est_req(trxcon, msg); + case L1CTL_DM_REL_REQ: + return l1ctl_rx_dm_rel_req(trxcon, msg); + case L1CTL_DATA_REQ: + return l1ctl_rx_dt_req(trxcon, msg, false); + case L1CTL_TRAFFIC_REQ: + return l1ctl_rx_dt_req(trxcon, msg, true); + case L1CTL_PARAM_REQ: + return l1ctl_rx_param_req(trxcon, msg); + case L1CTL_TCH_MODE_REQ: + return l1ctl_rx_tch_mode_req(trxcon, msg); + case L1CTL_CRYPTO_REQ: + return l1ctl_rx_crypto_req(trxcon, msg); + case L1CTL_GPRS_UL_TBF_CFG_REQ: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_TBF_CFG_REQ, msg); + msgb_free(msg); + return rc; + case L1CTL_GPRS_DL_TBF_CFG_REQ: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_DL_TBF_CFG_REQ, msg); + msgb_free(msg); + return rc; + case L1CTL_GPRS_UL_BLOCK_REQ: + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_BLOCK_REQ, msg); + msgb_free(msg); + return rc; + /* Not (yet) handled messages */ + case L1CTL_NEIGH_PM_REQ: + case L1CTL_DM_FREQ_REQ: + case L1CTL_SIM_REQ: + LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_NOTICE, + "Ignoring unsupported message (type=%u)\n", + l1h->msg_type); + msgb_free(msg); + return -ENOTSUP; + default: + LOGPFSMSL(trxcon->fi, g_logc_l1c, LOGL_ERROR, "Unknown MSG type %u: %s\n", + l1h->msg_type, osmo_hexdump(msgb_data(msg), msgb_length(msg))); + msgb_free(msg); + return -EINVAL; + } +} diff --git a/src/host/trxcon/src/l1ctl_server.c b/src/host/trxcon/src/l1ctl_server.c new file mode 100644 index 00000000..c0f10158 --- /dev/null +++ b/src/host/trxcon/src/l1ctl_server.c @@ -0,0 +1,282 @@ +/* + * OsmocomBB <-> SDR connection bridge + * UNIX socket server for L1CTL + * + * (C) 2013 by Sylvain Munaut <tnt@246tNt.com> + * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2022 by by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/un.h> +#include <arpa/inet.h> +#include <sys/socket.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/write_queue.h> + +#include <osmocom/bb/trxcon/logging.h> +#include <osmocom/bb/trxcon/l1ctl_server.h> + +#define LOGP_CLI(cli, cat, level, fmt, args...) \ + LOGP(cat, level, "%s" fmt, (cli)->log_prefix, ## args) + +static int l1ctl_client_read_cb(struct osmo_fd *ofd) +{ + struct l1ctl_client *client = (struct l1ctl_client *)ofd->data; + struct msgb *msg; + uint16_t len; + int rc; + + /* Attempt to read from socket */ + rc = read(ofd->fd, &len, L1CTL_MSG_LEN_FIELD); + if (rc != L1CTL_MSG_LEN_FIELD) { + if (rc <= 0) { + LOGP_CLI(client, DL1D, LOGL_NOTICE, + "L1CTL connection error: read() failed (rc=%d): %s\n", + rc, strerror(errno)); + } else { + LOGP_CLI(client, DL1D, LOGL_NOTICE, + "L1CTL connection error: short read\n"); + rc = -EIO; + } + l1ctl_client_conn_close(client); + return -EBADF; /* client fd is gone, avoid processing any other events. */ + } + + /* Check message length */ + len = ntohs(len); + if (len > L1CTL_LENGTH) { + LOGP_CLI(client, DL1D, LOGL_ERROR, "Length is too big: %u\n", len); + return -EINVAL; + } + + /* Allocate a new msg */ + msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, + L1CTL_HEADROOM, "l1ctl_rx_msg"); + if (!msg) { + LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to allocate msg\n"); + return -ENOMEM; + } + + msg->l1h = msgb_put(msg, len); + rc = read(ofd->fd, msg->l1h, msgb_l1len(msg)); + if (rc != len) { + LOGP_CLI(client, DL1D, LOGL_ERROR, + "Can not read data: len=%d < rc=%d: %s\n", + len, rc, strerror(errno)); + msgb_free(msg); + return rc; + } + + /* Debug print */ + LOGP_CLI(client, DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len)); + + /* Call L1CTL handler */ + client->server->cfg->conn_read_cb(client, msg); + + return 0; +} + +static int l1ctl_client_write_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + struct l1ctl_client *client = (struct l1ctl_client *)ofd->data; + int len; + + if (ofd->fd <= 0) + return -EINVAL; + + len = write(ofd->fd, msg->data, msg->len); + if (len != msg->len) { + LOGP_CLI(client, DL1D, LOGL_ERROR, + "Failed to write data: written (%d) < msg_len (%d)\n", + len, msg->len); + return -1; + } + + return 0; +} + +/* Connection handler */ +static int l1ctl_server_conn_cb(struct osmo_fd *sfd, unsigned int flags) +{ + struct l1ctl_server *server = (struct l1ctl_server *)sfd->data; + struct l1ctl_client *client; + int rc, client_fd; + + client_fd = accept(sfd->fd, NULL, NULL); + if (client_fd < 0) { + LOGP(DL1C, LOGL_ERROR, "Failed to accept() a new connection: " + "%s\n", strerror(errno)); + return client_fd; + } + + if (server->cfg->num_clients_max > 0 /* 0 means unlimited */ && + server->num_clients >= server->cfg->num_clients_max) { + LOGP(DL1C, LOGL_NOTICE, "L1CTL server cannot accept more " + "than %u connection(s)\n", server->cfg->num_clients_max); + close(client_fd); + return -ENOMEM; + } + + client = talloc_zero(server, struct l1ctl_client); + if (client == NULL) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate an L1CTL client\n"); + close(client_fd); + return -ENOMEM; + } + + /* Init the client's write queue */ + osmo_wqueue_init(&client->wq, 100); + INIT_LLIST_HEAD(&client->wq.bfd.list); + + client->wq.write_cb = &l1ctl_client_write_cb; + client->wq.read_cb = &l1ctl_client_read_cb; + osmo_fd_setup(&client->wq.bfd, client_fd, OSMO_FD_READ, &osmo_wqueue_bfd_cb, client, 0); + + /* Register the client's write queue */ + rc = osmo_fd_register(&client->wq.bfd); + if (rc != 0) { + LOGP(DL1C, LOGL_ERROR, "Failed to register a new connection fd\n"); + close(client->wq.bfd.fd); + talloc_free(client); + return rc; + } + + llist_add_tail(&client->list, &server->clients); + client->id = server->next_client_id++; + client->server = server; + server->num_clients++; + + LOGP(DL1C, LOGL_NOTICE, "L1CTL server got a new connection (id=%u)\n", client->id); + + if (client->server->cfg->conn_accept_cb != NULL) + client->server->cfg->conn_accept_cb(client); + + return 0; +} + +int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg) +{ + uint8_t *len; + + /* Debug print */ + LOGP_CLI(client, DL1D, LOGL_DEBUG, "TX: '%s'\n", osmo_hexdump(msg->data, msg->len)); + + if (msg->l1h != msg->data) + LOGP_CLI(client, DL1D, LOGL_INFO, "Message L1 header != Message Data\n"); + + /* Prepend 16-bit length before sending */ + len = msgb_push(msg, L1CTL_MSG_LEN_FIELD); + osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len); + + if (osmo_wqueue_enqueue(&client->wq, msg) != 0) { + LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to enqueue msg!\n"); + msgb_free(msg); + return -EIO; + } + + return 0; +} + +void l1ctl_client_conn_close(struct l1ctl_client *client) +{ + struct l1ctl_server *server = client->server; + + LOGP_CLI(client, DL1C, LOGL_NOTICE, "Closing L1CTL connection\n"); + + if (server->cfg->conn_close_cb != NULL) + server->cfg->conn_close_cb(client); + + /* Close connection socket */ + osmo_fd_unregister(&client->wq.bfd); + close(client->wq.bfd.fd); + client->wq.bfd.fd = -1; + + /* Clear pending messages */ + osmo_wqueue_clear(&client->wq); + + client->server->num_clients--; + llist_del(&client->list); + talloc_free(client); + + /* If this was the last client, reset the client IDs generator to 0. + * This way avoid assigning huge unreadable client IDs like 26545. */ + if (llist_empty(&server->clients)) + server->next_client_id = 0; +} + +struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg) +{ + struct l1ctl_server *server; + int rc; + + LOGP(DL1C, LOGL_NOTICE, "Init L1CTL server (sock_path=%s)\n", cfg->sock_path); + + server = talloc(ctx, struct l1ctl_server); + OSMO_ASSERT(server != NULL); + + *server = (struct l1ctl_server) { + .clients = LLIST_HEAD_INIT(server->clients), + .cfg = cfg, + }; + + /* conn_read_cb shall not be NULL */ + OSMO_ASSERT(cfg->conn_read_cb != NULL); + + /* Bind connection handler */ + osmo_fd_setup(&server->ofd, -1, OSMO_FD_READ, &l1ctl_server_conn_cb, server, 0); + + rc = osmo_sock_unix_init_ofd(&server->ofd, SOCK_STREAM, 0, + cfg->sock_path, OSMO_SOCK_F_BIND); + if (rc < 0) { + LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n", + strerror(errno)); + talloc_free(server); + return NULL; + } + + return server; +} + +void l1ctl_server_free(struct l1ctl_server *server) +{ + LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL server\n"); + + /* Close all client connections */ + while (!llist_empty(&server->clients)) { + struct l1ctl_client *client = llist_entry(server->clients.next, + struct l1ctl_client, + list); + l1ctl_client_conn_close(client); + } + + /* Unbind listening socket */ + if (server->ofd.fd != -1) { + osmo_fd_unregister(&server->ofd); + close(server->ofd.fd); + server->ofd.fd = -1; + } + + talloc_free(server); +} diff --git a/src/host/trxcon/src/l1gprs.c b/src/host/trxcon/src/l1gprs.c new file mode 120000 index 00000000..0185f68b --- /dev/null +++ b/src/host/trxcon/src/l1gprs.c @@ -0,0 +1 @@ +../../../shared/l1gprs.c
\ No newline at end of file diff --git a/src/host/trxcon/logging.c b/src/host/trxcon/src/logging.c index 78915f21..e8730450 100644 --- a/src/host/trxcon/logging.c +++ b/src/host/trxcon/src/logging.c @@ -15,19 +15,16 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/application.h> #include <osmocom/core/logging.h> #include <osmocom/core/utils.h> -#include "logging.h" +#include <osmocom/bb/trxcon/trxcon.h> +#include <osmocom/bb/trxcon/logging.h> -static struct log_info_cat trx_log_info_cat[] = { +static struct log_info_cat trxcon_log_info_cat[] = { [DAPP] = { .name = "DAPP", .description = "Application", @@ -46,8 +43,8 @@ static struct log_info_cat trx_log_info_cat[] = { .color = "\033[1;31m", .enabled = 1, .loglevel = LOGL_NOTICE, }, - [DTRX] = { - .name = "DTRX", + [DTRXC] = { + .name = "DTRXC", .description = "Transceiver control interface", .color = "\033[1;33m", .enabled = 1, .loglevel = LOGL_NOTICE, @@ -70,19 +67,37 @@ static struct log_info_cat trx_log_info_cat[] = { .color = "\033[1;36m", .enabled = 1, .loglevel = LOGL_NOTICE, }, + [DGPRS] = { + .name = "DGPRS", + .description = "L1 GPRS (MAC layer)", + .color = "\033[1;36m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, }; -static const struct log_info trx_log_info = { - .cat = trx_log_info_cat, - .num_cat = ARRAY_SIZE(trx_log_info_cat), +static const struct log_info trxcon_log_info = { + .cat = trxcon_log_info_cat, + .num_cat = ARRAY_SIZE(trxcon_log_info_cat), }; -int trx_log_init(void *tall_ctx, const char *category_mask) +static const int trxcon_log_cfg[] = { + [TRXCON_LOGC_FSM] = DAPP, + [TRXCON_LOGC_L1C] = DL1C, + [TRXCON_LOGC_L1D] = DL1D, + [TRXCON_LOGC_SCHC] = DSCH, + [TRXCON_LOGC_SCHD] = DSCHD, + [TRXCON_LOGC_GPRS] = DGPRS, +}; + +int trxcon_logging_init(void *tall_ctx, const char *category_mask) { - osmo_init_logging2(tall_ctx, &trx_log_info); + osmo_init_logging2(tall_ctx, &trxcon_log_info); + log_target_file_switch_to_wqueue(osmo_stderr_target); if (category_mask) log_parse_category_mask(osmo_stderr_target, category_mask); + trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg)); + return 0; } diff --git a/src/host/trxcon/src/sched_lchan_common.c b/src/host/trxcon/src/sched_lchan_common.c new file mode 100644 index 00000000..2b1729ae --- /dev/null +++ b/src/host/trxcon/src/sched_lchan_common.c @@ -0,0 +1,137 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: common routines for lchan handlers + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <string.h> +#include <talloc.h> +#include <stdint.h> +#include <stdbool.h> + +#include <arpa/inet.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/bits.h> + +#include <osmocom/codec/codec.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +/* GSM 05.02 Chapter 5.2.3 Normal Burst (NB) */ +const uint8_t l1sched_nb_training_bits[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, + }, +}; + +/* Get a string representation of the burst buffer's completeness. + * Examples: " ****.." (incomplete, 4/6 bursts) + * " ****" (complete, all 4 bursts) + * "**.***.." (incomplete, 5/8 bursts) */ +const char *l1sched_burst_mask2str(const uint32_t *mask, int bits) +{ + static char buf[32 + 1]; + char *ptr = buf; + + OSMO_ASSERT(bits <= 32 && bits > 0); + + while (--bits >= 0) + *(ptr++) = (*mask & (1 << bits)) ? '*' : '.'; + *ptr = '\0'; + + return buf; +} + +bool l1sched_lchan_amr_prim_is_valid(struct l1sched_lchan_state *lchan, + struct msgb *msg, bool is_cmr) +{ + enum osmo_amr_type ft_codec; + uint8_t cmr_codec; + int ft, cmr, len; + + len = osmo_amr_rtp_dec(msgb_l2(msg), msgb_l2len(msg), + &cmr_codec, NULL, &ft_codec, NULL, NULL); + if (len < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Cannot send invalid AMR payload (%u): %s\n", + msgb_l2len(msg), msgb_hexdump_l2(msg)); + return false; + } + ft = -1; + cmr = -1; + for (unsigned int i = 0; i < lchan->amr.codecs; i++) { + if (lchan->amr.codec[i] == ft_codec) + ft = i; + if (lchan->amr.codec[i] == cmr_codec) + cmr = i; + } + if (ft < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Codec (FT = %d) of RTP frame not in list\n", ft_codec); + return false; + } + if (is_cmr && lchan->amr.ul_ft != ft) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Codec (FT = %d) of RTP cannot be changed now, but in next frame\n", + ft_codec); + return false; + } + lchan->amr.ul_ft = ft; + if (cmr < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Codec (CMR = %d) of RTP frame not in list\n", cmr_codec); + } else { + lchan->amr.ul_cmr = cmr; + } + + return true; +} diff --git a/src/host/trxcon/sched_lchan_desc.c b/src/host/trxcon/src/sched_lchan_desc.c index b6a72b39..db5446e3 100644 --- a/src/host/trxcon/sched_lchan_desc.c +++ b/src/host/trxcon/src/sched_lchan_desc.c @@ -5,6 +5,7 @@ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu> * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co> * (C) 2015 by Harald Welte <laforge@gnumonks.org> + * Contributions by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * @@ -24,114 +25,99 @@ */ #include <osmocom/gsm/protocol/gsm_08_58.h> -#include <osmocom/core/gsmtap.h> -#include "sched_trx.h" +#include <osmocom/bb/l1sched/l1sched.h> /* Forward declaration of handlers */ -int rx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_data_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); -int tx_data_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br); +int tx_data_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); -int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_sch_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); -int tx_rach_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br); +int tx_rach_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); -int rx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_tchf_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); -int tx_tchf_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br); +int tx_tchf_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); -int rx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_tchh_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); -int tx_tchh_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br); +int tx_tchh_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); -int rx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas); +int rx_pdtch_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi); -int tx_pdtch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, - struct sched_burst_req *br); +int tx_pdtch_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); -const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { - [TRXC_IDLE] = { +const struct l1sched_lchan_desc l1sched_lchan_desc[_L1SCHED_CHAN_MAX] = { + [L1SCHED_IDLE] = { .name = "IDLE", .desc = "Idle channel", /* The MS needs to perform neighbour measurements during * IDLE slots, however this is not implemented (yet). */ }, - [TRXC_FCCH] = { + [L1SCHED_FCCH] = { .name = "FCCH", /* 3GPP TS 05.02, section 3.3.2.1 */ .desc = "Frequency correction channel", /* Handled by transceiver, nothing to do. */ }, - [TRXC_SCH] = { + [L1SCHED_SCH] = { .name = "SCH", /* 3GPP TS 05.02, section 3.3.2.2 */ .desc = "Synchronization channel", /* 3GPP TS 05.03, section 4.7. Handled by transceiver, * however we still need to parse BSIC (BCC / NCC). */ - .flags = TRX_CH_FLAG_AUTO, + .flags = L1SCHED_CH_FLAG_AUTO, .rx_fn = rx_sch_fn, }, - [TRXC_BCCH] = { + [L1SCHED_BCCH] = { .name = "BCCH", /* 3GPP TS 05.02, section 3.3.2.3 */ .desc = "Broadcast control channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_BCCH, .chan_nr = RSL_CHAN_BCCH, /* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4), * regular interleaving (3GPP TS 05.02, clause 7, table 3): * a L2 frame is interleaved over 4 consecutive bursts. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_AUTO, + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, + .flags = L1SCHED_CH_FLAG_AUTO, .rx_fn = rx_data_fn, }, - [TRXC_RACH] = { + [L1SCHED_RACH] = { .name = "RACH", /* 3GPP TS 05.02, section 3.3.3.1 */ .desc = "Random access channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_RACH, .chan_nr = RSL_CHAN_RACH, /* Tx only, RACH convolutional coding (3GPP TS 05.03, section 4.6). */ - .flags = TRX_CH_FLAG_AUTO, + .flags = L1SCHED_CH_FLAG_AUTO, .tx_fn = tx_rach_fn, }, - [TRXC_CCCH] = { + [L1SCHED_CCCH] = { .name = "CCCH", /* 3GPP TS 05.02, section 3.3.3.1 */ .desc = "Common control channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_CCCH, .chan_nr = RSL_CHAN_PCH_AGCH, /* Rx only, xCCH convolutional coding (3GPP TS 05.03, section 4.4), * regular interleaving (3GPP TS 05.02, clause 7, table 3): * a L2 frame is interleaved over 4 consecutive bursts. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_AUTO, + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, + .flags = L1SCHED_CH_FLAG_AUTO, .rx_fn = rx_data_fn, }, - [TRXC_TCHF] = { + [L1SCHED_TCHF] = { .name = "TCH/F", /* 3GPP TS 05.02, section 3.2 */ .desc = "Full Rate traffic channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F, .chan_nr = RSL_CHAN_Bm_ACCHs, - .link_id = TRX_CH_LID_DEDIC, + .link_id = L1SCHED_CH_LID_DEDIC, /* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03, * chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7): @@ -144,18 +130,15 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { * * The MS shall continuously transmit bursts, even if there is nothing * to send, unless DTX (Discontinuous Transmission) is used. */ - .burst_buf_size = 8 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_tchf_fn, .tx_fn = tx_tchf_fn, }, - [TRXC_TCHH_0] = { + [L1SCHED_TCHH_0] = { .name = "TCH/H(0)", /* 3GPP TS 05.02, section 3.2 */ .desc = "Half Rate traffic channel (sub-channel 0)", - .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H, .chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 0, + .link_id = L1SCHED_CH_LID_DEDIC, /* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03, * chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7): @@ -173,455 +156,365 @@ const struct trx_lchan_desc trx_lchan_desc[_TRX_CHAN_MAX] = { * * The MS shall continuously transmit bursts, even if there is nothing * to send, unless DTX (Discontinuous Transmission) is used. */ - .burst_buf_size = 6 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_tchh_fn, .tx_fn = tx_tchh_fn, }, - [TRXC_TCHH_1] = { + [L1SCHED_TCHH_1] = { .name = "TCH/H(1)", /* 3GPP TS 05.02, section 3.2 */ .desc = "Half Rate traffic channel (sub-channel 1)", - .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H, .chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 1, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_TCHH_0, see above. */ - .burst_buf_size = 6 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_TCHH_0, see above. */ + .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_tchh_fn, .tx_fn = tx_tchh_fn, }, - [TRXC_SDCCH4_0] = { + [L1SCHED_SDCCH4_0] = { .name = "SDCCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 0)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 0, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH4_1] = { + [L1SCHED_SDCCH4_1] = { .name = "SDCCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 1)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 1, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH4_2] = { + [L1SCHED_SDCCH4_2] = { .name = "SDCCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 2)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 2, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH4_3] = { + [L1SCHED_SDCCH4_3] = { .name = "SDCCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 3)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 3, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_0] = { + [L1SCHED_SDCCH8_0] = { .name = "SDCCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 0)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 0, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_1] = { + [L1SCHED_SDCCH8_1] = { .name = "SDCCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 1)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 1, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_2] = { + [L1SCHED_SDCCH8_2] = { .name = "SDCCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 2)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 2, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_3] = { + [L1SCHED_SDCCH8_3] = { .name = "SDCCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 3)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 3, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_4] = { + [L1SCHED_SDCCH8_4] = { .name = "SDCCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 4)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 4, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_5] = { + [L1SCHED_SDCCH8_5] = { .name = "SDCCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 5)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 5, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_6] = { + [L1SCHED_SDCCH8_6] = { .name = "SDCCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 6)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 6, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SDCCH8_7] = { + [L1SCHED_SDCCH8_7] = { .name = "SDCCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Stand-alone dedicated control channel (sub-channel 7)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3), - .link_id = TRX_CH_LID_DEDIC, - .ss_nr = 7, + .link_id = L1SCHED_CH_LID_DEDIC, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCHTF] = { + [L1SCHED_SACCHTF] = { .name = "SACCH/TF", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow TCH/F associated control channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_F | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_Bm_ACCHs, - .link_id = TRX_CH_LID_SACCH, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCHTH_0] = { + [L1SCHED_SACCHTH_0] = { .name = "SACCH/TH(0)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow TCH/H associated control channel (sub-channel 0)", - .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_Lm_ACCHs + (0 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 0, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCHTH_1] = { + [L1SCHED_SACCHTH_1] = { .name = "SACCH/TH(1)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow TCH/H associated control channel (sub-channel 1)", - .gsmtap_chan_type = GSMTAP_CHANNEL_TCH_H | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_Lm_ACCHs + (1 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 1, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH4_0] = { + [L1SCHED_SACCH4_0] = { .name = "SACCH/4(0)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/4 associated control channel (sub-channel 0)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (0 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 0, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH4_1] = { + [L1SCHED_SACCH4_1] = { .name = "SACCH/4(1)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/4 associated control channel (sub-channel 1)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (1 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 1, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH4_2] = { + [L1SCHED_SACCH4_2] = { .name = "SACCH/4(2)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/4 associated control channel (sub-channel 2)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (2 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 2, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH4_3] = { + [L1SCHED_SACCH4_3] = { .name = "SACCH/4(3)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/4 associated control channel (sub-channel 3)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH4 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH4_ACCH + (3 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 3, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH4_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH4_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_0] = { + [L1SCHED_SACCH8_0] = { .name = "SACCH/8(0)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 0)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (0 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 0, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_1] = { + [L1SCHED_SACCH8_1] = { .name = "SACCH/8(1)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 1)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (1 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 1, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_2] = { + [L1SCHED_SACCH8_2] = { .name = "SACCH/8(2)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 2)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (2 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 2, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_3] = { + [L1SCHED_SACCH8_3] = { .name = "SACCH/8(3)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 3)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (3 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 3, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_4] = { + [L1SCHED_SACCH8_4] = { .name = "SACCH/8(4)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 4)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (4 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 4, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_5] = { + [L1SCHED_SACCH8_5] = { .name = "SACCH/8(5)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 5)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (5 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 5, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_6] = { + [L1SCHED_SACCH8_6] = { .name = "SACCH/8(6)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 6)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (6 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 6, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_SACCH8_7] = { + [L1SCHED_SACCH8_7] = { .name = "SACCH/8(7)", /* 3GPP TS 05.02, section 3.3.4.1 */ .desc = "Slow SDCCH/8 associated control channel (sub-channel 7)", - .gsmtap_chan_type = GSMTAP_CHANNEL_SDCCH8 | GSMTAP_CHANNEL_ACCH, .chan_nr = RSL_CHAN_SDCCH8_ACCH + (7 << 3), - .link_id = TRX_CH_LID_SACCH, - .ss_nr = 7, + .link_id = L1SCHED_CH_LID_SACCH, - /* Same as for TRXC_BCCH and TRXC_SDCCH8_* (xCCH), see above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_CBTX, + /* Same as for L1SCHED_BCCH and L1SCHED_SDCCH8_* (xCCH), see above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, .tx_fn = tx_data_fn, }, - [TRXC_PDTCH] = { + [L1SCHED_PDTCH] = { .name = "PDTCH", /* 3GPP TS 05.02, sections 3.2.4, 3.3.2.4 */ .desc = "Packet data traffic & control channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_PDTCH, .chan_nr = RSL_CHAN_OSMO_PDCH, /* Rx and Tx, multiple coding schemes: CS-1..4 and MCS-1..9 (3GPP TS * 05.03, chapter 5), regular interleaving as specified for xCCH. * NOTE: the burst buffer is three times bigger because the * payload of EDGE bursts is three times longer. */ - .burst_buf_size = 3 * 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_PDCH, + .burst_buf_size = 4 * GSM_NBITS_NB_8PSK_PAYLOAD, + .flags = L1SCHED_CH_FLAG_PDCH, .rx_fn = rx_pdtch_fn, .tx_fn = tx_pdtch_fn, }, - [TRXC_PTCCH] = { + [L1SCHED_PTCCH] = { .name = "PTCCH", /* 3GPP TS 05.02, section 3.3.4.2 */ .desc = "Packet Timing advance control channel", - .gsmtap_chan_type = GSMTAP_CHANNEL_PTCCH, .chan_nr = RSL_CHAN_OSMO_PDCH, - .link_id = TRX_CH_LID_PTCCH, + .link_id = L1SCHED_CH_LID_PTCCH, /* On the Uplink, mobile stations transmit random Access Bursts * to allow estimation of the timing advance for one MS in packet * transfer mode. On Downlink, the network sends timing advance * updates for several mobile stations. The coding scheme used * for PTCCH/D messages is the same as for PDTCH CS-1. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_PDCH, + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, + .flags = L1SCHED_CH_FLAG_PDCH, .rx_fn = rx_pdtch_fn, .tx_fn = tx_rach_fn, }, - [TRXC_SDCCH4_CBCH] = { + [L1SCHED_SDCCH4_CBCH] = { .name = "SDCCH/4(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */ .desc = "Cell Broadcast channel on SDCCH/4", - .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH51, .chan_nr = RSL_CHAN_OSMO_CBCH4, - .ss_nr = 2, - /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, - .flags = TRX_CH_FLAG_AUTO, + /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, + .flags = L1SCHED_CH_FLAG_AUTO, .rx_fn = rx_data_fn, }, - [TRXC_SDCCH8_CBCH] = { + [L1SCHED_SDCCH8_CBCH] = { .name = "SDCCH/8(CBCH)", /* 3GPP TS 05.02, section 3.3.5 */ .desc = "Cell Broadcast channel on SDCCH/8", - .gsmtap_chan_type = GSMTAP_CHANNEL_CBCH52, .chan_nr = RSL_CHAN_OSMO_CBCH8, - .ss_nr = 2, - /* Same as for TRXC_BCCH (xCCH), but Rx only. See above. */ - .burst_buf_size = 4 * GSM_BURST_PL_LEN, + /* Same as for L1SCHED_BCCH (xCCH), but Rx only. See above. */ + .burst_buf_size = 4 * GSM_NBITS_NB_GMSK_PAYLOAD, .rx_fn = rx_data_fn, }, }; diff --git a/src/host/trxcon/src/sched_lchan_pdtch.c b/src/host/trxcon/src/sched_lchan_pdtch.c new file mode 100644 index 00000000..5b884ddc --- /dev/null +++ b/src/host/trxcon/src/sched_lchan_pdtch.c @@ -0,0 +1,195 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2018-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdint.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/bits.h> + +#include <osmocom/gsm/gsm0502.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/coding/gsm0503_coding.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +int rx_pdtch_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi) +{ + uint8_t l2[GPRS_L2_MAX_LEN]; + int n_errors, n_bits_total, rc; + sbit_t *bursts_p, *burst; + size_t l2_len; + uint32_t *mask; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + bursts_p = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, + "Packet data received: fn=%u bid=%u\n", bi->fn, bi->bid); + + /* Align to the first burst of a block */ + if (*mask == 0x00 && bi->bid != 0) + return 0; + + /* Update mask */ + *mask |= (1 << bi->bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, bi); + + /* Copy burst to buffer of 4 bursts */ + burst = bursts_p + bi->bid * 116; + memcpy(burst, bi->burst + 3, 58); + memcpy(burst + 58, bi->burst + 87, 58); + + /* Wait until complete set of bursts */ + if (bi->bid != 3) + return 0; + + /* Calculate AVG of the measurements */ + l1sched_lchan_meas_avg(lchan, 4); + + /* Check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received incomplete (%s) packet data at fn=%u (%u/%u)\n", + l1sched_burst_mask2str(mask, 4), lchan->meas_avg.fn, + lchan->meas_avg.fn % lchan->ts->mf_layout->period, + lchan->ts->mf_layout->period); + /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */ + } + + /* Keep the mask updated */ + *mask = *mask << 4; + + /* Attempt to decode */ + rc = gsm0503_pdtch_decode(l2, bursts_p, + NULL, &n_errors, &n_bits_total); + if (rc < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n", + rc, n_errors, n_bits_total, lchan->meas_avg.fn); + } + + /* Determine L2 length */ + l2_len = rc > 0 ? rc : 0; + + /* Send a L2 frame to the higher layers */ + l1sched_lchan_emit_data_ind(lchan, l2, l2_len, n_errors, n_bits_total, true); + + return 0; +} + +static struct msgb *prim_dequeue_pdtch(struct l1sched_lchan_state *lchan, uint32_t fn) +{ + while (!llist_empty(&lchan->tx_prims)) { + struct msgb *msg = llist_first_entry(&lchan->tx_prims, struct msgb, list); + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + int ret = gsm0502_fncmp(prim->data_req.frame_nr, fn); + + if (OSMO_LIKELY(ret == 0)) { /* it's a match! */ + llist_del(&msg->list); + return msg; + } else if (ret > 0) { /* not now, come back later */ + break; + } /* else: the ship has sailed, drop your ticket */ + + LOGP_LCHAND(lchan, LOGL_ERROR, + "%s(): dropping stale Tx prim (current Fn=%u, prim Fn=%u): %s\n", + __func__, fn, prim->data_req.frame_nr, msgb_hexdump_l2(msg)); + llist_del(&msg->list); + msgb_free(msg); + } + + return NULL; +} + +int tx_pdtch_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + ubit_t *bursts_p, *burst; + const uint8_t *tsc; + uint32_t *mask; + int rc; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + bursts_p = lchan->tx_bursts; + + if (br->bid > 0) { + if ((*mask & 0x01) != 0x01) + return -ENOENT; + goto send_burst; + } + + *mask = *mask << 4; + + struct msgb *msg = prim_dequeue_pdtch(lchan, br->fn); + if (msg == NULL) + return -ENOENT; + + /* Encode payload */ + rc = gsm0503_pdtch_encode(bursts_p, msgb_l2(msg), msgb_l2len(msg)); + if (rc < 0) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n", + msgb_l2len(msg), msgb_hexdump_l2(msg)); + msgb_free(msg); + return -EINVAL; + } + + /* Cache the prim, so that we can confirm it later (see below) */ + OSMO_ASSERT(lchan->prim == NULL); + lchan->prim = msg; + +send_burst: + /* Determine which burst should be sent */ + burst = bursts_p + br->bid * 116; + + /* Update mask */ + *mask |= (1 << br->bid); + + /* Choose proper TSC */ + tsc = l1sched_nb_training_bits[lchan->tsc]; + + /* Compose a new burst */ + memset(br->burst, 0, 3); /* TB */ + memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_NBITS_NB_GMSK_BURST; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled at fn=%u burst=%u\n", br->fn, br->bid); + + if (br->bid == 3) { + /* Confirm data / traffic sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, lchan->prim, + GSM_TDMA_FN_SUB(br->fn, 3)); + lchan->prim = NULL; + } + + return 0; +} diff --git a/src/host/trxcon/src/sched_lchan_rach.c b/src/host/trxcon/src/sched_lchan_rach.c new file mode 100644 index 00000000..905f1d57 --- /dev/null +++ b/src/host/trxcon/src/sched_lchan_rach.c @@ -0,0 +1,136 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/bits.h> + +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/coding/gsm0503_coding.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)" */ +#define RACH_EXT_TAIL_BITS_LEN 8 +#define RACH_SYNCH_SEQ_LEN 41 +#define RACH_PAYLOAD_LEN 36 + +/* Extended tail bits (BN0..BN7) */ +static const ubit_t rach_ext_tail_bits[] = { + 0, 0, 1, 1, 1, 0, 1, 0, +}; + +/* Synchronization (training) sequence types */ +enum rach_synch_seq_t { + RACH_SYNCH_SEQ_UNKNOWN = -1, + RACH_SYNCH_SEQ_TS0, /* GSM, GMSK (default) */ + RACH_SYNCH_SEQ_TS1, /* EGPRS, 8-PSK */ + RACH_SYNCH_SEQ_TS2, /* EGPRS, GMSK */ + RACH_SYNCH_SEQ_NUM +}; + +/* Synchronization (training) sequence bits */ +static const char rach_synch_seq_bits[RACH_SYNCH_SEQ_NUM][RACH_SYNCH_SEQ_LEN] = { + [RACH_SYNCH_SEQ_TS0] = "01001011011111111001100110101010001111000", + [RACH_SYNCH_SEQ_TS1] = "01010100111110001000011000101111001001101", + [RACH_SYNCH_SEQ_TS2] = "11101111001001110101011000001101101110111", +}; + +/* Synchronization (training) sequence names */ +static struct value_string rach_synch_seq_names[] = { + { RACH_SYNCH_SEQ_UNKNOWN, "UNKNOWN" }, + { RACH_SYNCH_SEQ_TS0, "TS0: GSM, GMSK" }, + { RACH_SYNCH_SEQ_TS1, "TS1: EGPRS, 8-PSK" }, + { RACH_SYNCH_SEQ_TS2, "TS2: EGPRS, GMSK" }, + { 0, NULL }, +}; + +/* Obtain a to-be-transmitted RACH burst */ +int tx_rach_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + const uint8_t bsic = lchan->ts->sched->bsic; + uint8_t *burst_ptr = br->burst; + uint8_t payload[36]; + int i, rc; + + if (llist_empty(&lchan->tx_prims)) + return 0; + + struct msgb *msg = llist_first_entry(&lchan->tx_prims, struct msgb, list); + struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + + /* Delay sending according to offset value */ + if (prim->rach_req.offset-- > 0) + return 0; + llist_del(&msg->list); + + /* Check requested synch. sequence */ + if (prim->rach_req.synch_seq >= RACH_SYNCH_SEQ_NUM) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Unknown RACH synch. sequence=0x%02x\n", + prim->rach_req.synch_seq); + msgb_free(msg); + return -ENOTSUP; + } + + /* Encode the payload */ + rc = gsm0503_rach_ext_encode(payload, prim->rach_req.ra, + bsic, prim->rach_req.is_11bit); + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Could not encode %s-bit RACH burst (ra=%u bsic=%u)\n", + prim->rach_req.is_11bit ? "11" : "8", + prim->rach_req.ra, bsic); + msgb_free(msg); + return rc; + } + + /* BN0-7: extended tail bits */ + memcpy(burst_ptr, rach_ext_tail_bits, RACH_EXT_TAIL_BITS_LEN); + burst_ptr += RACH_EXT_TAIL_BITS_LEN; + + /* BN8-48: chosen synch. (training) sequence */ + for (i = 0; i < RACH_SYNCH_SEQ_LEN; i++) + *(burst_ptr++) = rach_synch_seq_bits[prim->rach_req.synch_seq][i] == '1'; + + /* BN49-84: encrypted bits (the payload) */ + memcpy(burst_ptr, payload, RACH_PAYLOAD_LEN); + burst_ptr += RACH_PAYLOAD_LEN; + + /* BN85-156: tail bits & extended guard period */ + memset(burst_ptr, 0, br->burst + GSM_NBITS_NB_GMSK_BURST - burst_ptr); + br->burst_len = GSM_NBITS_NB_GMSK_BURST; + + LOGP_LCHAND(lchan, LOGL_NOTICE, "Scheduled %s-bit RACH (%s) at fn=%u\n", + prim->rach_req.is_11bit ? "11" : "8", + get_value_string(rach_synch_seq_names, prim->rach_req.synch_seq), br->fn); + + /* Confirm RACH request (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + + return 0; +} diff --git a/src/host/trxcon/sched_lchan_sch.c b/src/host/trxcon/src/sched_lchan_sch.c index 18d4c58d..e2420050 100644 --- a/src/host/trxcon/sched_lchan_sch.c +++ b/src/host/trxcon/src/sched_lchan_sch.c @@ -2,7 +2,8 @@ * OsmocomBB <-> SDR connection bridge * TDMA scheduler: handlers for DL / UL bursts on logical channels * - * (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * @@ -16,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <errno.h> @@ -35,12 +32,8 @@ #include <osmocom/gsm/gsm_utils.h> #include <osmocom/coding/gsm0503_coding.h> -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" -#include "logging.h" -#include "trx_if.h" -#include "l1ctl.h" +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) { @@ -68,9 +61,23 @@ static void decode_sb(struct gsm_time *time, uint8_t *bsic, uint8_t *sb_info) time->fn = gsm_gsmtime2fn(time); } -int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, - struct trx_lchan_state *lchan, uint32_t fn, uint8_t bid, - const sbit_t *bits, const struct trx_meas_set *meas) +static int handle_sch_ind(struct l1sched_state *sched, uint32_t fn, uint8_t bsic) +{ + struct l1sched_prim *prim; + struct msgb *msg; + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->sch_ind.frame_nr = fn; + prim->sch_ind.bsic = bsic; + + return l1sched_prim_to_user(sched, msg); +} + +int rx_sch_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi) { sbit_t payload[2 * 39]; struct gsm_time time; @@ -79,55 +86,34 @@ int rx_sch_fn(struct trx_instance *trx, struct trx_ts *ts, int rc; /* Obtain payload from burst */ - memcpy(payload, bits + 3, 39); - memcpy(payload + 39, bits + 3 + 39 + 64, 39); + memcpy(payload, bi->burst + 3, 39); + memcpy(payload + 39, bi->burst + 3 + 39 + 64, 39); /* Attempt to decode */ rc = gsm0503_sch_decode(sb_info, payload); if (rc) { - LOGP(DSCHD, LOGL_ERROR, "Received bad SCH burst at fn=%u\n", fn); + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received bad SCH burst at fn=%u\n", bi->fn); return rc; } /* Decode BSIC and TDMA frame number */ decode_sb(&time, &bsic, sb_info); - LOGP(DSCHD, LOGL_DEBUG, "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", - bsic, time.fn, trx->sched.fn_counter_proc); + LOGP_LCHAND(lchan, LOGL_DEBUG, + "Received SCH: bsic=%u, fn=%u, sched_fn=%u\n", + bsic, time.fn, bi->fn); /* Check if decoded frame number matches */ - if (time.fn != fn) { - LOGP(DSCHD, LOGL_ERROR, "Decoded fn=%u does not match " - "fn=%u provided by scheduler\n", time.fn, fn); + if (time.fn != bi->fn) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Decoded fn=%u does not match sched_fn=%u\n", + time.fn, bi->fn); return -EINVAL; } - /* We don't need to send L1CTL_FBSB_CONF */ - if (trx->l1l->fbsb_conf_sent) - return 0; - - /* Send L1CTL_FBSB_CONF to higher layers */ - struct l1ctl_info_dl *data; - data = talloc_zero_size(ts, sizeof(struct l1ctl_info_dl)); - if (data == NULL) - return -ENOMEM; - - /* Fill in some downlink info */ - data->chan_nr = trx_lchan_desc[lchan->type].chan_nr | ts->index; - data->link_id = trx_lchan_desc[lchan->type].link_id; - data->band_arfcn = htons(trx->band_arfcn); - data->frame_nr = htonl(fn); - data->rx_level = -(meas->rssi); - - /* FIXME: set proper values */ - data->num_biterr = 0; - data->fire_crc = 0; - data->snr = 0; - - l1ctl_tx_fbsb_conf(trx->l1l, 0, data, bsic); - - /* Update BSIC value of trx_instance */ - trx->bsic = bsic; + /* Update BSIC value in the scheduler state */ + lchan->ts->sched->bsic = bsic; - return 0; + return handle_sch_ind(lchan->ts->sched, time.fn, bsic); } diff --git a/src/host/trxcon/src/sched_lchan_tchf.c b/src/host/trxcon/src/sched_lchan_tchf.c new file mode 100644 index 00000000..37e0cea3 --- /dev/null +++ b/src/host/trxcon/src/sched_lchan_tchf.c @@ -0,0 +1,441 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2021-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdint.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/bits.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsm0502.h> + +#include <osmocom/coding/gsm0503_coding.h> +#include <osmocom/coding/gsm0503_amr_dtx.h> +#include <osmocom/codec/codec.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +/* Burst Payload LENgth (short alias) */ +#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD + +/* Burst BUFfer capacity (in BPLEN units) */ +#define BUFMAX 24 + +/* Burst BUFfer position macros */ +#define BUFPOS(buf, n) &buf[(n) * BPLEN] +#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8)) + +/* 3GPP TS 45.009, table 3.2.1.3-{1,3}: AMR on Downlink TCH/F. + * + * +---+---+---+---+---+---+---+---+ + * | a | b | c | d | e | f | g | h | Burst 'a' received first + * +---+---+---+---+---+---+---+---+ + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Speech/FACCH frame (bursts 'a' .. 'h') + * + * TDMA frame number of burst 'h' is always used as the table index. */ +static const uint8_t sched_tchf_dl_amr_cmi_map[26] = { + [11] = 1, /* TCH/F: a=4 / h=11 */ + [20] = 1, /* TCH/F: a=13 / h=20 */ + [3] = 1, /* TCH/F: a=21 / h=3 (21+7=28, 25 is idle -> 29. 29%26=3) */ +}; + +/* TDMA frame number of burst 'a' should be used as the table index. */ +static const uint8_t sched_tchf_ul_amr_cmi_map[26] = { + [0] = 1, /* TCH/F: a=0 */ + [8] = 1, /* TCH/F: a=8 */ + [17] = 1, /* TCH/F: a=17 */ +}; + +static int decode_fr_facch(struct l1sched_lchan_state *lchan) +{ + uint8_t data[GSM_MACBLOCK_LEN]; + int n_errors, n_bits_total; + int rc; + + rc = gsm0503_tch_fr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts), + &n_errors, &n_bits_total); + if (rc != GSM_MACBLOCK_LEN) + return rc; + + /* calculate AVG of the measurements (FACCH/F takes 8 bursts) */ + l1sched_lchan_meas_avg(lchan, 8); + + l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN, + n_errors, n_bits_total, false); + + return GSM_MACBLOCK_LEN; +} + +int rx_tchf_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi) +{ + int n_errors = -1, n_bits_total = 0, rc; + sbit_t *bursts_p, *burst; + uint8_t tch_data[290]; + size_t tch_data_len; + uint32_t *mask; + int amr = 0; + uint8_t ft; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + bursts_p = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, + "Traffic received: fn=%u bid=%u\n", bi->fn, bi->bid); + + if (bi->bid == 0) { + /* Shift the burst buffer by 4 bursts leftwards */ + memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN); + memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN); + *mask = *mask << 4; + } else { + /* Align to the first burst of a block */ + if (*mask == 0x00) + return 0; + } + + /* Update mask */ + *mask |= (1 << bi->bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, bi); + + /* Copy burst to end of buffer of 24 bursts */ + burst = BUFPOS(bursts_p, 20 + bi->bid); + memcpy(burst, bi->burst + 3, 58); + memcpy(burst + 58, bi->burst + 87, 58); + + /* Wait until complete set of bursts */ + if (bi->bid != 3) + return 0; + + /* Calculate AVG of the measurements */ + l1sched_lchan_meas_avg(lchan, 8); // XXX + + /* Check for complete set of bursts */ + if ((*mask & 0xff) != 0xff) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received incomplete (%s) traffic frame at fn=%u (%u/%u)\n", + l1sched_burst_mask2str(mask, 8), lchan->meas_avg.fn, + lchan->meas_avg.fn % lchan->ts->mf_layout->period, + lchan->ts->mf_layout->period); + /* NOTE: do not abort here, give it a try. Maybe we're lucky ;) */ + + } + + /* TCH/F: speech and signalling frames are interleaved over 8 bursts, while + * CSD frames are interleaved over 22 bursts. Unless we're in CSD mode, + * decode only the last 8 bursts to avoid introducing additional delays. */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* FR */ + rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p), + 1, 0, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_EFR: /* EFR */ + rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p), + 1, 1, &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* we store tch_data + 2 header bytes, the amr variable set to + * 2 will allow us to skip the first 2 bytes in case we did + * receive an FACCH frame instead of a voice frame (we do not + * know this before we actually decode the frame) */ + amr = 2; + rc = gsm0503_tch_afs_decode_dtx(&tch_data[amr], BUFTAIL8(bursts_p), + !sched_tchf_dl_amr_cmi_map[bi->fn % 26], + lchan->amr.codec, + lchan->amr.codecs, + &lchan->amr.dl_ft, + &lchan->amr.dl_cmr, + &n_errors, &n_bits_total, + &lchan->amr.last_dtx); + + /* only good speech frames get rtp header */ + if (rc != GSM_MACBLOCK_LEN && rc >= 4) { + if (lchan->amr.last_dtx == AMR_OTHER) { + ft = lchan->amr.codec[lchan->amr.dl_ft]; + } else { + /* SID frames will always get Frame Type Index 8 (AMR_SID) */ + ft = AMR_SID; + } + rc = osmo_amr_rtp_enc(&tch_data[0], + lchan->amr.codec[lchan->amr.dl_cmr], + ft, AMR_GOOD); + if (rc < 0) + LOGP_LCHAND(lchan, LOGL_ERROR, + "osmo_amr_rtp_enc() returned rc=%d\n", rc); + } + break; + /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_14k5: + /* FACCH/F does not steal TCH/F14.4 frames, but only disturbs some bits */ + decode_fr_facch(lchan); + rc = gsm0503_tch_fr144_decode(&tch_data[0], BUFPOS(bursts_p, 0), + &n_errors, &n_bits_total); + break; + /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_12k0: + /* FACCH/F does not steal TCH/F9.6 frames, but only disturbs some bits */ + decode_fr_facch(lchan); + rc = gsm0503_tch_fr96_decode(&tch_data[0], BUFPOS(bursts_p, 0), + &n_errors, &n_bits_total); + break; + /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_6k0: + /* FACCH/F does not steal TCH/F4.8 frames, but only disturbs some bits */ + decode_fr_facch(lchan); + rc = gsm0503_tch_fr48_decode(&tch_data[0], BUFPOS(bursts_p, 0), + &n_errors, &n_bits_total); + break; + /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_3k6: + /* TCH/F2.4 employs the same interleaving as TCH/FS (8 bursts), + * so FACCH/F *does* steal TCH/F2.4 frames completely. */ + if (decode_fr_facch(lchan) == GSM_MACBLOCK_LEN) + return 0; /* TODO: emit BFI? */ + rc = gsm0503_tch_fr24_decode(&tch_data[0], BUFTAIL8(bursts_p), + &n_errors, &n_bits_total); + break; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode); + return -EINVAL; + } + + /* Check decoding result */ + if (rc < 4) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n", + rc, n_errors, n_bits_total, lchan->meas_avg.fn); + + /* Send BFI (DATA.ind without payload) */ + tch_data_len = 0; + } else if (rc == GSM_MACBLOCK_LEN) { + /* FACCH received, forward it to the higher layers */ + l1sched_lchan_emit_data_ind(lchan, &tch_data[amr], GSM_MACBLOCK_LEN, + n_errors, n_bits_total, false); + + /* Send BFI (DATA.ind without payload) */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) + return 0; + tch_data_len = 0; + } else { + /* A good TCH frame received */ + tch_data_len = rc; + } + + /* Send a traffic frame to the higher layers */ + return l1sched_lchan_emit_data_ind(lchan, &tch_data[0], tch_data_len, + n_errors, n_bits_total, true); +} + +int tx_tchf_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + struct msgb *msg_facch, *msg_tch, *msg; + ubit_t *bursts_p, *burst; + const uint8_t *tsc; + uint32_t *mask; + int rc; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + bursts_p = lchan->tx_bursts; + + if (br->bid > 0) { + if ((*mask & 0x01) != 0x01) + return -ENOENT; + goto send_burst; + } + + /* Shift the burst buffer by 4 bursts leftwards for interleaving */ + memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN); + memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN); + *mask = *mask << 4; + + /* dequeue a pair of TCH and FACCH frames */ + msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false); + msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true); + /* prioritize FACCH over TCH */ + msg = (msg_facch != NULL) ? msg_facch : msg_tch; + + /* populate the buffer with bursts */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + if (msg == NULL) + msg = l1sched_lchan_prim_dummy_lapdm(lchan); + /* fall-through */ + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + if (msg == NULL) { + /* transmit a dummy speech block with inverted CRC3 */ + gsm0503_tch_fr_encode(bursts_p, NULL, 0, 1); + goto send_burst; + } + rc = gsm0503_tch_fr_encode(BUFPOS(bursts_p, 0), + msgb_l2(msg), + msgb_l2len(msg), 1); + break; + case GSM48_CMODE_SPEECH_AMR: + { + bool amr_fn_is_cmr = !sched_tchf_ul_amr_cmi_map[br->fn % 26]; + const uint8_t *data = msg ? msgb_l2(msg) : NULL; + size_t data_len = msg ? msgb_l2len(msg) : 0; + + if (msg == NULL) { + /* TODO: It's not clear what to do for TCH/AFS. + * TODO: Send dummy FACCH maybe? */ + goto send_burst; /* send something */ + } + + if (data_len != GSM_MACBLOCK_LEN) { /* TCH/AFS: speech */ + if (!l1sched_lchan_amr_prim_is_valid(lchan, msg, amr_fn_is_cmr)) + goto free_bad_msg; + /* pull the AMR header - sizeof(struct amr_hdr) */ + data_len -= 2; + data += 2; + } + + rc = gsm0503_tch_afs_encode(BUFPOS(bursts_p, 0), + data, data_len, + amr_fn_is_cmr, + lchan->amr.codec, + lchan->amr.codecs, + lchan->amr.ul_ft, + lchan->amr.ul_cmr); + break; + } + /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_14k5: + if ((msg = msg_tch) != NULL) { + OSMO_ASSERT(msgb_l2len(msg) == 290); + gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm data sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } else { + ubit_t idle[290]; + memset(&idle[0], 0x01, sizeof(idle)); + gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), &idle[0]); + } + if ((msg = msg_facch) != NULL) { + gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm FACCH sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } + goto send_burst; + /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_12k0: + if ((msg = msg_tch) != NULL) { + OSMO_ASSERT(msgb_l2len(msg) == 4 * 60); + gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm data sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } else { + ubit_t idle[4 * 60]; + memset(&idle[0], 0x01, sizeof(idle)); + gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), &idle[0]); + } + if ((msg = msg_facch) != NULL) { + gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm FACCH sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } + goto send_burst; + /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_6k0: + if ((msg = msg_tch) != NULL) { + OSMO_ASSERT(msgb_l2len(msg) == 2 * 60); + gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm data sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } else { + ubit_t idle[2 * 60]; + memset(&idle[0], 0x01, sizeof(idle)); + gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), &idle[0]); + } + if ((msg = msg_facch) != NULL) { + gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm FACCH sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } + goto send_burst; + /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_3k6: + if ((msg = msg_facch) != NULL) { + /* FACCH/F does steal a TCH/F2.4 frame completely */ + rc = gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + } else if ((msg = msg_tch) != NULL) { + OSMO_ASSERT(msgb_l2len(msg) == 2 * 36); + rc = gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + } else { + ubit_t idle[2 * 36]; + memset(&idle[0], 0x01, sizeof(idle)); + gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), &idle[0]); + goto send_burst; + } + break; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, + "TCH mode %s is unknown or not supported\n", + gsm48_chan_mode_name(lchan->tch_mode)); + goto free_bad_msg; + } + + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n", + msgb_l2len(msg), msgb_hexdump_l2(msg)); +free_bad_msg: + msgb_free(msg_facch); + msgb_free(msg_tch); + return -EINVAL; + } + + /* Confirm data / traffic sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + msgb_free((msg == msg_facch) ? msg_tch : msg_facch); + +send_burst: + /* Determine which burst should be sent */ + burst = BUFPOS(bursts_p, br->bid); + + /* Update mask */ + *mask |= (1 << br->bid); + + /* Choose proper TSC */ + tsc = l1sched_nb_training_bits[lchan->tsc]; + + /* Compose a new burst */ + memset(br->burst, 0, 3); /* TB */ + memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_NBITS_NB_GMSK_BURST; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid); + + return 0; +} diff --git a/src/host/trxcon/src/sched_lchan_tchh.c b/src/host/trxcon/src/sched_lchan_tchh.c new file mode 100644 index 00000000..99e26808 --- /dev/null +++ b/src/host/trxcon/src/sched_lchan_tchh.c @@ -0,0 +1,622 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2018-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2018 by Harald Welte <laforge@gnumonks.org> + * (C) 2020-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/bits.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsm0502.h> + +#include <osmocom/coding/gsm0503_coding.h> +#include <osmocom/coding/gsm0503_amr_dtx.h> +#include <osmocom/codec/codec.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +/* Burst Payload LENgth (short alias) */ +#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD + +/* Burst BUFfer capacity (in BPLEN units) */ +#define BUFMAX 24 + +/* Burst BUFfer position macros */ +#define BUFPOS(buf, n) &buf[(n) * BPLEN] +#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8)) + +/* 3GPP TS 45.009, table 3.2.1.3-{2,4}: AMR on Downlink TCH/H. + * + * +---+---+---+---+---+---+ + * | a | b | c | d | e | f | Burst 'a' received first + * +---+---+---+---+---+---+ + * ^^^^^^^^^^^^^^^^^^^^^^^ FACCH frame (bursts 'a' .. 'f') + * ^^^^^^^^^^^^^^^ Speech frame (bursts 'a' .. 'd') + * + * TDMA frame number of burst 'f' is always used as the table index. */ +static const uint8_t sched_tchh_dl_amr_cmi_map[26] = { + [15] = 1, /* TCH/H(0): a=4 / d=10 / f=15 */ + [23] = 1, /* TCH/H(0): a=13 / d=19 / f=23 */ + [6] = 1, /* TCH/H(0): a=21 / d=2 / f=6 */ + + [16] = 1, /* TCH/H(1): a=5 / d=11 / f=16 */ + [24] = 1, /* TCH/H(1): a=14 / d=20 / f=24 */ + [7] = 1, /* TCH/H(1): a=22 / d=3 / f=7 */ +}; + +/* TDMA frame number of burst 'a' is always used as the table index. */ +static const uint8_t sched_tchh_ul_amr_cmi_map[26] = { + [0] = 1, /* TCH/H(0): a=0 */ + [8] = 1, /* TCH/H(0): a=8 */ + [17] = 1, /* TCH/H(0): a=17 */ + + [1] = 1, /* TCH/H(1): a=1 */ + [9] = 1, /* TCH/H(1): a=9 */ + [18] = 1, /* TCH/H(1): a=18 */ +}; + +static const uint8_t tch_h0_traffic_block_map[3][4] = { + /* B0(0,2,4,6), B1(4,6,8,10), B2(8,10,0,2) */ + { 0, 2, 4, 6 }, + { 4, 6, 8, 10 }, + { 8, 10, 0, 2 }, +}; + +static const uint8_t tch_h1_traffic_block_map[3][4] = { + /* B0(1,3,5,7), B1(5,7,9,11), B2(9,11,1,3) */ + { 1, 3, 5, 7 }, + { 5, 7, 9, 11 }, + { 9, 11, 1, 3 }, +}; + +static const uint8_t tch_h0_dl_facch_block_map[3][6] = { + /* B0(4,6,8,10,13,15), B1(13,15,17,19,21,23), B2(21,23,0,2,4,6) */ + { 4, 6, 8, 10, 13, 15 }, + { 13, 15, 17, 19, 21, 23 }, + { 21, 23, 0, 2, 4, 6 }, +}; + +static const uint8_t tch_h0_ul_facch_block_map[3][6] = { + /* B0(0,2,4,6,8,10), B1(8,10,13,15,17,19), B2(17,19,21,23,0,2) */ + { 0, 2, 4, 6, 8, 10 }, + { 8, 10, 13, 15, 17, 19 }, + { 17, 19, 21, 23, 0, 2 }, +}; + +static const uint8_t tch_h1_dl_facch_block_map[3][6] = { + /* B0(5,7,9,11,14,16), B1(14,16,18,20,22,24), B2(22,24,1,3,5,7) */ + { 5, 7, 9, 11, 14, 16 }, + { 14, 16, 18, 20, 22, 24 }, + { 22, 24, 1, 3, 5, 7 }, +}; + +const uint8_t tch_h1_ul_facch_block_map[3][6] = { + /* B0(1,3,5,7,9,11), B1(9,11,14,16,18,20), B2(18,20,22,24,1,3) */ + { 1, 3, 5, 7, 9, 11 }, + { 9, 11, 14, 16, 18, 20 }, + { 18, 20, 22, 24, 1, 3 }, +}; + +/* FACCH/H channel mapping for Downlink (see 3GPP TS 45.002, table 1). + * This mapping is valid for both FACCH/H(0) and FACCH/H(1). + * TDMA frame number of burst 'f' is used as the table index. */ +static const uint8_t sched_tchh_dl_facch_map[26] = { + [15] = 1, /* FACCH/H(0): B0(4,6,8,10,13,15) */ + [16] = 1, /* FACCH/H(1): B0(5,7,9,11,14,16) */ + [23] = 1, /* FACCH/H(0): B1(13,15,17,19,21,23) */ + [24] = 1, /* FACCH/H(1): B1(14,16,18,20,22,24) */ + [6] = 1, /* FACCH/H(0): B2(21,23,0,2,4,6) */ + [7] = 1, /* FACCH/H(1): B2(22,24,1,3,5,7) */ +}; + +/* 3GPP TS 45.002, table 2 in clause 7: Mapping tables for TCH/H2.4 and TCH/H4.8. + * + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * + * TCH/H(0): B0(0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19) + * TCH/H(1): B0(1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20) + * TCH/H(0): B1(8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2) + * TCH/H(1): B1(9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3) + * TCH/H(0): B2(17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10) + * TCH/H(1): B2(18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11) + * + * TDMA frame number of burst 'a' % 26 is the table index. + * This mapping is valid for both TCH/H(0) and TCH/H(1). */ +static const uint8_t sched_tchh_ul_csd_map[26] = { + [0] = 1, /* TCH/H(0): B0(0 ... 19) */ + [1] = 1, /* TCH/H(1): B0(1 ... 20) */ + [8] = 1, /* TCH/H(0): B1(8 ... 2) */ + [9] = 1, /* TCH/H(1): B1(9 ... 3) */ + [17] = 1, /* TCH/H(0): B2(17 ... 10) */ + [18] = 1, /* TCH/H(1): B2(18 ... 11) */ +}; + +/* TDMA frame number of burst 'v' % 26 is the table index. + * This mapping is valid for both TCH/H(0) and TCH/H(1). */ +static const uint8_t sched_tchh_dl_csd_map[26] = { + [19] = 1, /* TCH/H(0): B0(0 ... 19) */ + [20] = 1, /* TCH/H(1): B0(1 ... 20) */ + [2] = 1, /* TCH/H(0): B1(8 ... 2) */ + [3] = 1, /* TCH/H(1): B1(9 ... 3) */ + [10] = 1, /* TCH/H(0): B2(17 ... 10) */ + [11] = 1, /* TCH/H(1): B2(18 ... 11) */ +}; + +/** + * Can a TCH/H block transmission be initiated / finished + * on a given frame number and a given channel type? + * + * See GSM 05.02, clause 7, table 1 + * + * @param chan channel type (L1SCHED_TCHH_0 or L1SCHED_TCHH_1) + * @param fn the current frame number + * @param ul Uplink or Downlink? + * @param facch FACCH/H or traffic? + * @param start init or end of transmission? + * @return true (yes) or false (no) + */ +bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan, + uint32_t fn, bool ul, bool facch, bool start) +{ + uint8_t fn_mf; + int i = 0; + + /* Just to be sure */ + OSMO_ASSERT(chan == L1SCHED_TCHH_0 || chan == L1SCHED_TCHH_1); + + /* Calculate a modulo */ + fn_mf = facch ? (fn % 26) : (fn % 13); + +#define MAP_GET_POS(map) \ + (start ? 0 : ARRAY_SIZE(map[i]) - 1) + +#define BLOCK_MAP_FN(map) \ + do { \ + if (map[i][MAP_GET_POS(map)] == fn_mf) \ + return true; \ + } while (++i < ARRAY_SIZE(map)) + + /* Choose a proper block map */ + if (facch) { + if (ul) { + if (chan == L1SCHED_TCHH_0) + BLOCK_MAP_FN(tch_h0_ul_facch_block_map); + else + BLOCK_MAP_FN(tch_h1_ul_facch_block_map); + } else { + if (chan == L1SCHED_TCHH_0) + BLOCK_MAP_FN(tch_h0_dl_facch_block_map); + else + BLOCK_MAP_FN(tch_h1_dl_facch_block_map); + } + } else { + if (chan == L1SCHED_TCHH_0) + BLOCK_MAP_FN(tch_h0_traffic_block_map); + else + BLOCK_MAP_FN(tch_h1_traffic_block_map); + } + + return false; +} + +static int decode_hr_facch(struct l1sched_lchan_state *lchan) +{ + uint8_t data[GSM_MACBLOCK_LEN]; + int n_errors, n_bits_total; + int rc; + + rc = gsm0503_tch_hr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts), + &n_errors, &n_bits_total); + if (rc != GSM_MACBLOCK_LEN) + return rc; + + /* calculate AVG of the measurements (FACCH/H takes 6 bursts) */ + l1sched_lchan_meas_avg(lchan, 6); + + l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN, + n_errors, n_bits_total, false); + + return GSM_MACBLOCK_LEN; +} + +int rx_tchh_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi) +{ + int n_errors = -1, n_bits_total = 0, rc; + sbit_t *bursts_p, *burst; + uint8_t tch_data[240]; + size_t tch_data_len; + uint32_t *mask; + int amr = 0; + uint8_t ft; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + bursts_p = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, + "Traffic received: fn=%u bid=%u\n", bi->fn, bi->bid); + + if (bi->bid == 0) { + /* Shift the burst buffer by 2 bursts leftwards */ + memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN); + memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN); + *mask = *mask << 2; + } + + if (*mask == 0x00) { + /* Align to the first burst */ + if (bi->bid > 0) + return 0; + + /* Align reception of the first FACCH/H frame */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) { + if (!l1sched_tchh_facch_start(lchan->type, bi->fn, 0)) + return 0; + } + } + + /* Update mask */ + *mask |= (1 << bi->bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, bi); + + /* Copy burst to the end of buffer of 24 bursts */ + burst = BUFPOS(bursts_p, 20 + bi->bid); + memcpy(burst, bi->burst + 3, 58); + memcpy(burst + 58, bi->burst + 87, 58); + + /* Wait until the second burst */ + if (bi->bid != 1) + return 0; + + /* Wait for complete set of bursts */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + /* FACCH/H is interleaved over 6 bursts */ + if ((*mask & 0x3f) != 0x3f) + return 0; + break; + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + /* Data (CSD) is interleaved over 22 bursts */ + if ((*mask & 0x3fffff) != 0x3fffff) + return 0; + if (!sched_tchh_dl_csd_map[bi->fn % 26]) + return 0; /* CSD: skip decoding attempt, need 2 more bursts */ + break; + default: + /* Speech is interleaved over 4 bursts */ + if ((*mask & 0x0f) != 0x0f) + return 0; + break; + } + + /* Skip decoding attempt in case of FACCH/H */ + if (lchan->dl_ongoing_facch) { + /* Send BFI (DATA.ind without payload) for the 2nd stolen TCH frame */ + l1sched_lchan_meas_avg(lchan, 4); + l1sched_lchan_emit_data_ind(lchan, NULL, 0, 0, 0, true); + lchan->dl_ongoing_facch = false; + return 0; + } + + /* TCH/H: speech and signalling frames are interleaved over 4 and 6 bursts, + * respectively, while CSD frames are interleaved over 22 bursts. Unless + * we're in CSD mode, decode only the last 6 bursts to avoid introducing + * additional delays. */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + case GSM48_CMODE_SPEECH_V1: /* HR */ + rc = gsm0503_tch_hr_decode(&tch_data[0], BUFTAIL8(bursts_p), + !sched_tchh_dl_facch_map[bi->fn % 26], + &n_errors, &n_bits_total); + break; + case GSM48_CMODE_SPEECH_AMR: /* AMR */ + /* See comment in function rx_tchf_fn() */ + amr = 2; + rc = gsm0503_tch_ahs_decode_dtx(&tch_data[amr], BUFTAIL8(bursts_p), + !sched_tchh_dl_facch_map[bi->fn % 26], + !sched_tchh_dl_amr_cmi_map[bi->fn % 26], + lchan->amr.codec, + lchan->amr.codecs, + &lchan->amr.dl_ft, + &lchan->amr.dl_cmr, + &n_errors, &n_bits_total, + &lchan->amr.last_dtx); + + /* only good speech frames get rtp header */ + if (rc != GSM_MACBLOCK_LEN && rc >= 4) { + if (lchan->amr.last_dtx == AMR_OTHER) { + ft = lchan->amr.codec[lchan->amr.dl_ft]; + } else { + /* SID frames will always get Frame Type Index 8 (AMR_SID) */ + ft = AMR_SID; + } + rc = osmo_amr_rtp_enc(&tch_data[0], + lchan->amr.codec[lchan->amr.dl_cmr], + ft, AMR_GOOD); + if (rc < 0) + LOGP_LCHAND(lchan, LOGL_ERROR, + "osmo_amr_rtp_enc() returned rc=%d\n", rc); + } + break; + /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_6k0: + /* FACCH/H does not steal TCH/H4.8 frames, but only disturbs some bits */ + decode_hr_facch(lchan); + rc = gsm0503_tch_hr48_decode(&tch_data[0], BUFPOS(bursts_p, 0), + &n_errors, &n_bits_total); + break; + /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_3k6: + /* FACCH/H does not steal TCH/H2.4 frames, but only disturbs some bits */ + decode_hr_facch(lchan); + rc = gsm0503_tch_hr24_decode(&tch_data[0], BUFPOS(bursts_p, 0), + &n_errors, &n_bits_total); + break; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", lchan->tch_mode); + return -EINVAL; + } + + /* Check decoding result */ + if (rc < 4) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n", + rc, n_errors, n_bits_total, lchan->meas_avg.fn); + + /* Send BFI (DATA.ind without payload) */ + tch_data_len = 0; + } else if (rc == GSM_MACBLOCK_LEN) { + /* Skip decoding of the next 2 stolen bursts */ + lchan->dl_ongoing_facch = true; + + /* Calculate AVG of the measurements (FACCH/H takes 6 bursts) */ + l1sched_lchan_meas_avg(lchan, 6); + + /* FACCH/H received, forward to the higher layers */ + l1sched_lchan_emit_data_ind(lchan, &tch_data[amr], GSM_MACBLOCK_LEN, + n_errors, n_bits_total, false); + + /* Send BFI (DATA.ind without payload) for the 1st stolen TCH frame */ + if (lchan->tch_mode == GSM48_CMODE_SIGN) + return 0; + tch_data_len = 0; + } else { + /* A good TCH frame received */ + tch_data_len = rc; + } + + /* Calculate AVG of the measurements (traffic takes 4 bursts) */ + l1sched_lchan_meas_avg(lchan, 4); + + /* Send a traffic frame to the higher layers */ + return l1sched_lchan_emit_data_ind(lchan, &tch_data[0], tch_data_len, + n_errors, n_bits_total, true); +} + +int tx_tchh_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + struct msgb *msg_facch, *msg_tch, *msg; + ubit_t *bursts_p, *burst; + const uint8_t *tsc; + uint32_t *mask; + int rc; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + bursts_p = lchan->tx_bursts; + + if (br->bid > 0) { + if ((*mask & 0x01) != 0x01) + return -ENOENT; + goto send_burst; + } + + if (*mask == 0x00) { + /* Align transmission of the first frame */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1)) + return 0; + break; + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + if (!sched_tchh_ul_csd_map[br->fn % 26]) + return 0; + break; + } + } + + /* Shift the burst buffer by 2 bursts leftwards for interleaving */ + memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN); + memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN); + *mask = *mask << 2; + + /* If FACCH/H blocks are still pending */ + if (lchan->ul_facch_blocks > 2) { + struct msgb *msg = l1sched_lchan_prim_dequeue_tch(lchan, false); + msgb_free(msg); /* drop 2nd TCH/HS block */ + goto send_burst; + } + + switch (lchan->tch_mode) { + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + /* CSD: skip dequeueing/encoding, send 2 more bursts */ + if (!sched_tchh_ul_csd_map[br->fn % 26]) + goto send_burst; + break; + } + + /* dequeue a pair of TCH and FACCH frames */ + msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false); + if (l1sched_tchh_facch_start(lchan->type, br->fn, true)) + msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true); + else + msg_facch = NULL; + /* prioritize FACCH over TCH */ + msg = (msg_facch != NULL) ? msg_facch : msg_tch; + + /* populate the buffer with bursts */ + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1)) + goto send_burst; /* XXX: should not happen */ + if (msg == NULL) + msg = l1sched_lchan_prim_dummy_lapdm(lchan); + /* fall-through */ + case GSM48_CMODE_SPEECH_V1: + if (msg == NULL) { + /* transmit a dummy speech block with inverted CRC3 */ + gsm0503_tch_hr_encode(bursts_p, NULL, 0); + goto send_burst; + } + rc = gsm0503_tch_hr_encode(BUFPOS(bursts_p, 0), + msgb_l2(msg), + msgb_l2len(msg)); + break; + case GSM48_CMODE_SPEECH_AMR: + { + bool amr_fn_is_cmr = !sched_tchh_ul_amr_cmi_map[br->fn % 26]; + const uint8_t *data = msg ? msgb_l2(msg) : NULL; + size_t data_len = msg ? msgb_l2len(msg) : 0; + + if (msg == NULL) { + /* TODO: It's not clear what to do for TCH/AHS. + * TODO: Send dummy FACCH maybe? */ + goto send_burst; /* send garbage */ + } + + if (data_len != GSM_MACBLOCK_LEN) { /* TCH/AHS: speech */ + if (!l1sched_lchan_amr_prim_is_valid(lchan, msg, amr_fn_is_cmr)) + goto free_bad_msg; + /* pull the AMR header - sizeof(struct amr_hdr) */ + data_len -= 2; + data += 2; + } + + rc = gsm0503_tch_ahs_encode(BUFPOS(bursts_p, 0), + data, data_len, + amr_fn_is_cmr, + lchan->amr.codec, + lchan->amr.codecs, + lchan->amr.ul_ft, + lchan->amr.ul_cmr); + break; + } + /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_6k0: + if ((msg = msg_tch) != NULL) { + OSMO_ASSERT(msgb_l2len(msg) == 4 * 60); + gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm data sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } else { + ubit_t idle[4 * 60]; + memset(&idle[0], 0x01, sizeof(idle)); + gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), &idle[0]); + } + if ((msg = msg_facch) != NULL) { + gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm FACCH sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } + goto send_burst; + /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */ + case GSM48_CMODE_DATA_3k6: + if ((msg = msg_tch) != NULL) { + OSMO_ASSERT(msgb_l2len(msg) == 4 * 36); + gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm data sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } else { + ubit_t idle[4 * 36]; + memset(&idle[0], 0x01, sizeof(idle)); + gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), &idle[0]); + } + if ((msg = msg_facch) != NULL) { + gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), msgb_l2(msg)); + /* Confirm FACCH sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + } + goto send_burst; + default: + LOGP_LCHAND(lchan, LOGL_ERROR, + "TCH mode %s is unknown or not supported\n", + gsm48_chan_mode_name(lchan->tch_mode)); + goto free_bad_msg; + } + + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n", + msgb_l2len(msg), msgb_hexdump_l2(msg)); +free_bad_msg: + msgb_free(msg_facch); + msgb_free(msg_tch); + return -EINVAL; + } + + if (msgb_l2len(msg) == GSM_MACBLOCK_LEN) + lchan->ul_facch_blocks = 6; + + /* Confirm data / traffic sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + msgb_free((msg == msg_facch) ? msg_tch : msg_facch); + +send_burst: + /* Determine which burst should be sent */ + burst = BUFPOS(bursts_p, br->bid); + + /* Update mask */ + *mask |= (1 << br->bid); + + /* Choose proper TSC */ + tsc = l1sched_nb_training_bits[lchan->tsc]; + + /* Compose a new burst */ + memset(br->burst, 0, 3); /* TB */ + memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_NBITS_NB_GMSK_BURST; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid); + + /* In case of a FACCH/H frame, one block less */ + if (lchan->ul_facch_blocks) + lchan->ul_facch_blocks--; + + return 0; +} diff --git a/src/host/trxcon/src/sched_lchan_xcch.c b/src/host/trxcon/src/sched_lchan_xcch.c new file mode 100644 index 00000000..52b5d1e6 --- /dev/null +++ b/src/host/trxcon/src/sched_lchan_xcch.c @@ -0,0 +1,181 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: handlers for DL / UL bursts on logical channels + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <string.h> +#include <stdint.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/bits.h> + +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/coding/gsm0503_coding.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +int rx_data_fn(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi) +{ + uint8_t l2[GSM_MACBLOCK_LEN]; + int n_errors, n_bits_total, rc; + sbit_t *bursts_p, *burst; + uint32_t *mask; + + /* Set up pointers */ + mask = &lchan->rx_burst_mask; + bursts_p = lchan->rx_bursts; + + LOGP_LCHAND(lchan, LOGL_DEBUG, + "Data received: fn=%u bid=%u\n", bi->fn, bi->bid); + + /* Align to the first burst of a block */ + if (*mask == 0x00 && bi->bid != 0) + return 0; + + /* Update mask */ + *mask |= (1 << bi->bid); + + /* Store the measurements */ + l1sched_lchan_meas_push(lchan, bi); + + /* Copy burst to buffer of 4 bursts */ + burst = bursts_p + bi->bid * 116; + memcpy(burst, bi->burst + 3, 58); + memcpy(burst + 58, bi->burst + 87, 58); + + /* Wait until complete set of bursts */ + if (bi->bid != 3) + return 0; + + /* Calculate AVG of the measurements */ + l1sched_lchan_meas_avg(lchan, 4); + + /* Check for complete set of bursts */ + if ((*mask & 0xf) != 0xf) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received incomplete (%s) data frame at fn=%u (%u/%u)\n", + l1sched_burst_mask2str(mask, 4), lchan->meas_avg.fn, + lchan->meas_avg.fn % lchan->ts->mf_layout->period, + lchan->ts->mf_layout->period); + /* NOTE: xCCH has an insane amount of redundancy for error + * correction, so even just 2 valid bursts might be enough + * to reconstruct some L2 frames. This is why we do not + * abort here. */ + } + + /* Keep the mask updated */ + *mask = *mask << 4; + + /* Attempt to decode */ + rc = gsm0503_xcch_decode(l2, bursts_p, &n_errors, &n_bits_total); + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Received bad frame (rc=%d, ber=%d/%d) at fn=%u\n", + rc, n_errors, n_bits_total, lchan->meas_avg.fn); + } + + /* Send a L2 frame to the higher layers */ + return l1sched_lchan_emit_data_ind(lchan, l2, rc ? 0 : GSM_MACBLOCK_LEN, + n_errors, n_bits_total, false); +} + +static struct msgb *prim_dequeue_xcch(struct l1sched_lchan_state *lchan) +{ + struct msgb *msg; + + if (L1SCHED_CHAN_IS_SACCH(lchan->type)) + return l1sched_lchan_prim_dequeue_sacch(lchan); + if ((msg = msgb_dequeue(&lchan->tx_prims)) == NULL) + return NULL; + + /* Check the prim payload length */ + if (msgb_l2len(msg) != GSM_MACBLOCK_LEN) { + LOGP_LCHAND(lchan, LOGL_ERROR, + "Primitive has odd length %u (expected %u), so dropping...\n", + msgb_l2len(msg), GSM_MACBLOCK_LEN); + msgb_free(msg); + return NULL; + } + + return msg; +} + +int tx_data_fn(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + ubit_t *bursts_p, *burst; + const uint8_t *tsc; + uint32_t *mask; + int rc; + + /* Set up pointers */ + mask = &lchan->tx_burst_mask; + bursts_p = lchan->tx_bursts; + + if (br->bid > 0) { + if ((*mask & 0x01) != 0x01) + return -ENOENT; + goto send_burst; + } + + *mask = *mask << 4; + + struct msgb *msg = prim_dequeue_xcch(lchan); + if (msg == NULL) + msg = l1sched_lchan_prim_dummy_lapdm(lchan); + OSMO_ASSERT(msg != NULL); + + /* Encode payload */ + rc = gsm0503_xcch_encode(bursts_p, msgb_l2(msg)); + if (rc) { + LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%u): %s\n", + msgb_l2len(msg), msgb_hexdump_l2(msg)); + msgb_free(msg); + return -EINVAL; + } + + /* Confirm data sending (pass ownership of the msgb/prim) */ + l1sched_lchan_emit_data_cnf(lchan, msg, br->fn); + +send_burst: + /* Determine which burst should be sent */ + burst = bursts_p + br->bid * 116; + + /* Update mask */ + *mask |= (1 << br->bid); + + /* Choose proper TSC */ + tsc = l1sched_nb_training_bits[lchan->tsc]; + + /* Compose a new burst */ + memset(br->burst, 0, 3); /* TB */ + memcpy(br->burst + 3, burst, 58); /* Payload 1/2 */ + memcpy(br->burst + 61, tsc, 26); /* TSC */ + memcpy(br->burst + 87, burst + 58, 58); /* Payload 2/2 */ + memset(br->burst + 145, 0, 3); /* TB */ + br->burst_len = GSM_NBITS_NB_GMSK_BURST; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "Scheduled fn=%u burst=%u\n", br->fn, br->bid); + + return 0; +} diff --git a/src/host/trxcon/src/sched_mframe.c b/src/host/trxcon/src/sched_mframe.c new file mode 100644 index 00000000..0d95e0ac --- /dev/null +++ b/src/host/trxcon/src/sched_mframe.c @@ -0,0 +1,2102 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: channel combinations, burst mapping + * + * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu> + * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co> + * (C) 2015 by Harald Welte <laforge@gnumonks.org> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +/* Non-combined CCCH */ +static const struct l1sched_tdma_frame frame_bcch[51] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_RACH, 0 }, +}; + +/* Combined CCCH+SDCCH4 */ +static const struct l1sched_tdma_frame frame_bcch_sdcch4[102] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 }, + { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 }, + { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 }, + { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_2, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_2, 1 }, + { L1SCHED_CCCH, 2, L1SCHED_SACCH4_2, 2 }, + { L1SCHED_CCCH, 3, L1SCHED_SACCH4_2, 3 }, + { L1SCHED_FCCH, 0, L1SCHED_SACCH4_3, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SACCH4_3, 1 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_3, 2 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_3, 3 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 }, + { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 }, + { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 }, + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 }, + { L1SCHED_SACCH4_0, 0, L1SCHED_SDCCH4_1, 1 }, + { L1SCHED_SACCH4_0, 1, L1SCHED_SDCCH4_1, 2 }, + { L1SCHED_SACCH4_0, 2, L1SCHED_SDCCH4_1, 3 }, + { L1SCHED_SACCH4_0, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_1, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_1, 1, L1SCHED_SDCCH4_2, 0 }, + { L1SCHED_SACCH4_1, 2, L1SCHED_SDCCH4_2, 1 }, + { L1SCHED_SACCH4_1, 3, L1SCHED_SDCCH4_2, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_2, 3 }, + + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 }, + { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 }, + { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 }, + { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_0, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_0, 1 }, + { L1SCHED_CCCH, 2, L1SCHED_SACCH4_0, 2 }, + { L1SCHED_CCCH, 3, L1SCHED_SACCH4_0, 3 }, + { L1SCHED_FCCH, 0, L1SCHED_SACCH4_1, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SACCH4_1, 1 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_1, 2 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_1, 3 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_2, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 }, + { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 }, + { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 }, + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 }, + { L1SCHED_SACCH4_2, 0, L1SCHED_SDCCH4_1, 1 }, + { L1SCHED_SACCH4_2, 1, L1SCHED_SDCCH4_1, 2 }, + { L1SCHED_SACCH4_2, 2, L1SCHED_SDCCH4_1, 3 }, + { L1SCHED_SACCH4_2, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_3, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_3, 1, L1SCHED_SDCCH4_2, 0 }, + { L1SCHED_SACCH4_3, 2, L1SCHED_SDCCH4_2, 1 }, + { L1SCHED_SACCH4_3, 3, L1SCHED_SDCCH4_2, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_2, 3 }, +}; + +static const struct l1sched_tdma_frame frame_bcch_sdcch4_cbch[102] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 }, + { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 }, + { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 }, + { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_IDLE, 1 }, + { L1SCHED_CCCH, 2, L1SCHED_IDLE, 2 }, + { L1SCHED_CCCH, 3, L1SCHED_IDLE, 3 }, + { L1SCHED_FCCH, 0, L1SCHED_SACCH4_3, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SACCH4_3, 1 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_3, 2 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_3, 3 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 }, + { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 }, + { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 }, + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 }, + { L1SCHED_SACCH4_0, 0, L1SCHED_SDCCH4_1, 1 }, + { L1SCHED_SACCH4_0, 1, L1SCHED_SDCCH4_1, 2 }, + { L1SCHED_SACCH4_0, 2, L1SCHED_SDCCH4_1, 3 }, + { L1SCHED_SACCH4_0, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_1, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_1, 1, L1SCHED_IDLE, 0 }, + { L1SCHED_SACCH4_1, 2, L1SCHED_IDLE, 1 }, + { L1SCHED_SACCH4_1, 3, L1SCHED_IDLE, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 3 }, + + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_3, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_3, 1 }, + { L1SCHED_BCCH, 0, L1SCHED_SDCCH4_3, 2 }, + { L1SCHED_BCCH, 1, L1SCHED_SDCCH4_3, 3 }, + { L1SCHED_BCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_BCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_0, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_0, 1 }, + { L1SCHED_CCCH, 2, L1SCHED_SACCH4_0, 2 }, + { L1SCHED_CCCH, 3, L1SCHED_SACCH4_0, 3 }, + { L1SCHED_FCCH, 0, L1SCHED_SACCH4_1, 0 }, + { L1SCHED_SCH, 0, L1SCHED_SACCH4_1, 1 }, + { L1SCHED_CCCH, 0, L1SCHED_SACCH4_1, 2 }, + { L1SCHED_CCCH, 1, L1SCHED_SACCH4_1, 3 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_CCCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_0, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_1, 3, L1SCHED_RACH, 0 }, + { L1SCHED_FCCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 1, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 2, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_CBCH, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SDCCH4_3, 1, L1SCHED_SDCCH4_0, 0 }, + { L1SCHED_SDCCH4_3, 2, L1SCHED_SDCCH4_0, 1 }, + { L1SCHED_SDCCH4_3, 3, L1SCHED_SDCCH4_0, 2 }, + { L1SCHED_FCCH, 0, L1SCHED_SDCCH4_0, 3 }, + { L1SCHED_SCH, 0, L1SCHED_SDCCH4_1, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_SDCCH4_1, 1 }, + { L1SCHED_IDLE, 1, L1SCHED_SDCCH4_1, 2 }, + { L1SCHED_IDLE, 2, L1SCHED_SDCCH4_1, 3 }, + { L1SCHED_IDLE, 3, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_3, 0, L1SCHED_RACH, 0 }, + { L1SCHED_SACCH4_3, 1, L1SCHED_IDLE, 0 }, + { L1SCHED_SACCH4_3, 2, L1SCHED_IDLE, 1 }, + { L1SCHED_SACCH4_3, 3, L1SCHED_IDLE, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 3 }, +}; + +static const struct l1sched_tdma_frame frame_sdcch8[102] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_5, 0 }, + { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_5, 1 }, + { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_5, 2 }, + { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_5, 3 }, + { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_6, 0 }, + { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_6, 1 }, + { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_6, 2 }, + { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_6, 3 }, + { L1SCHED_SDCCH8_2, 0, L1SCHED_SACCH8_7, 0 }, + { L1SCHED_SDCCH8_2, 1, L1SCHED_SACCH8_7, 1 }, + { L1SCHED_SDCCH8_2, 2, L1SCHED_SACCH8_7, 2 }, + { L1SCHED_SDCCH8_2, 3, L1SCHED_SACCH8_7, 3 }, + { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 }, + { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 }, + { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 }, + { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 }, + { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 }, + { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 }, + { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 }, + { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 }, + { L1SCHED_SDCCH8_5, 3, L1SCHED_SDCCH8_2, 0 }, + { L1SCHED_SDCCH8_6, 0, L1SCHED_SDCCH8_2, 1 }, + { L1SCHED_SDCCH8_6, 1, L1SCHED_SDCCH8_2, 2 }, + { L1SCHED_SDCCH8_6, 2, L1SCHED_SDCCH8_2, 3 }, + { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 }, + { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 }, + { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 }, + { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 }, + { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 }, + { L1SCHED_SACCH8_0, 0, L1SCHED_SDCCH8_4, 1 }, + { L1SCHED_SACCH8_0, 1, L1SCHED_SDCCH8_4, 2 }, + { L1SCHED_SACCH8_0, 2, L1SCHED_SDCCH8_4, 3 }, + { L1SCHED_SACCH8_0, 3, L1SCHED_SDCCH8_5, 0 }, + { L1SCHED_SACCH8_1, 0, L1SCHED_SDCCH8_5, 1 }, + { L1SCHED_SACCH8_1, 1, L1SCHED_SDCCH8_5, 2 }, + { L1SCHED_SACCH8_1, 2, L1SCHED_SDCCH8_5, 3 }, + { L1SCHED_SACCH8_1, 3, L1SCHED_SDCCH8_6, 0 }, + { L1SCHED_SACCH8_2, 0, L1SCHED_SDCCH8_6, 1 }, + { L1SCHED_SACCH8_2, 1, L1SCHED_SDCCH8_6, 2 }, + { L1SCHED_SACCH8_2, 2, L1SCHED_SDCCH8_6, 3 }, + { L1SCHED_SACCH8_2, 3, L1SCHED_SDCCH8_7, 0 }, + { L1SCHED_SACCH8_3, 0, L1SCHED_SDCCH8_7, 1 }, + { L1SCHED_SACCH8_3, 1, L1SCHED_SDCCH8_7, 2 }, + { L1SCHED_SACCH8_3, 2, L1SCHED_SDCCH8_7, 3 }, + { L1SCHED_SACCH8_3, 3, L1SCHED_SACCH8_0, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 1 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 3 }, + + { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_1, 0 }, + { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_1, 1 }, + { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_1, 2 }, + { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_1, 3 }, + { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_2, 0 }, + { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_2, 1 }, + { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_2, 2 }, + { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_2, 3 }, + { L1SCHED_SDCCH8_2, 0, L1SCHED_SACCH8_3, 0 }, + { L1SCHED_SDCCH8_2, 1, L1SCHED_SACCH8_3, 1 }, + { L1SCHED_SDCCH8_2, 2, L1SCHED_SACCH8_3, 2 }, + { L1SCHED_SDCCH8_2, 3, L1SCHED_SACCH8_3, 3 }, + { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 }, + { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 }, + { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 }, + { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 }, + { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 }, + { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 }, + { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 }, + { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 }, + { L1SCHED_SDCCH8_5, 3, L1SCHED_SDCCH8_2, 0 }, + { L1SCHED_SDCCH8_6, 0, L1SCHED_SDCCH8_2, 1 }, + { L1SCHED_SDCCH8_6, 1, L1SCHED_SDCCH8_2, 2 }, + { L1SCHED_SDCCH8_6, 2, L1SCHED_SDCCH8_2, 3 }, + { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 }, + { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 }, + { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 }, + { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 }, + { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 }, + { L1SCHED_SACCH8_4, 0, L1SCHED_SDCCH8_4, 1 }, + { L1SCHED_SACCH8_4, 1, L1SCHED_SDCCH8_4, 2 }, + { L1SCHED_SACCH8_4, 2, L1SCHED_SDCCH8_4, 3 }, + { L1SCHED_SACCH8_4, 3, L1SCHED_SDCCH8_5, 0 }, + { L1SCHED_SACCH8_5, 0, L1SCHED_SDCCH8_5, 1 }, + { L1SCHED_SACCH8_5, 1, L1SCHED_SDCCH8_5, 2 }, + { L1SCHED_SACCH8_5, 2, L1SCHED_SDCCH8_5, 3 }, + { L1SCHED_SACCH8_5, 3, L1SCHED_SDCCH8_6, 0 }, + { L1SCHED_SACCH8_6, 0, L1SCHED_SDCCH8_6, 1 }, + { L1SCHED_SACCH8_6, 1, L1SCHED_SDCCH8_6, 2 }, + { L1SCHED_SACCH8_6, 2, L1SCHED_SDCCH8_6, 3 }, + { L1SCHED_SACCH8_6, 3, L1SCHED_SDCCH8_7, 0 }, + { L1SCHED_SACCH8_7, 0, L1SCHED_SDCCH8_7, 1 }, + { L1SCHED_SACCH8_7, 1, L1SCHED_SDCCH8_7, 2 }, + { L1SCHED_SACCH8_7, 2, L1SCHED_SDCCH8_7, 3 }, + { L1SCHED_SACCH8_7, 3, L1SCHED_SACCH8_4, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 1 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 3 }, +}; + +static const struct l1sched_tdma_frame frame_sdcch8_cbch[102] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_5, 0 }, + { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_5, 1 }, + { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_5, 2 }, + { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_5, 3 }, + { L1SCHED_SDCCH8_1, 0, L1SCHED_SACCH8_6, 0 }, + { L1SCHED_SDCCH8_1, 1, L1SCHED_SACCH8_6, 1 }, + { L1SCHED_SDCCH8_1, 2, L1SCHED_SACCH8_6, 2 }, + { L1SCHED_SDCCH8_1, 3, L1SCHED_SACCH8_6, 3 }, + { L1SCHED_SDCCH8_CBCH, 0, L1SCHED_SACCH8_7, 0 }, + { L1SCHED_SDCCH8_CBCH, 1, L1SCHED_SACCH8_7, 1 }, + { L1SCHED_SDCCH8_CBCH, 2, L1SCHED_SACCH8_7, 2 }, + { L1SCHED_SDCCH8_CBCH, 3, L1SCHED_SACCH8_7, 3 }, + { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 }, + { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 }, + { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 }, + { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 }, + { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 }, + { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 }, + { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 }, + { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 }, + { L1SCHED_SDCCH8_5, 3, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_6, 0, L1SCHED_IDLE, 1 }, + { L1SCHED_SDCCH8_6, 1, L1SCHED_IDLE, 2 }, + { L1SCHED_SDCCH8_6, 2, L1SCHED_IDLE, 3 }, + { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 }, + { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 }, + { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 }, + { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 }, + { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 }, + { L1SCHED_SACCH8_0, 0, L1SCHED_SDCCH8_4, 1 }, + { L1SCHED_SACCH8_0, 1, L1SCHED_SDCCH8_4, 2 }, + { L1SCHED_SACCH8_0, 2, L1SCHED_SDCCH8_4, 3 }, + { L1SCHED_SACCH8_0, 3, L1SCHED_SDCCH8_5, 0 }, + { L1SCHED_SACCH8_1, 0, L1SCHED_SDCCH8_5, 1 }, + { L1SCHED_SACCH8_1, 1, L1SCHED_SDCCH8_5, 2 }, + { L1SCHED_SACCH8_1, 2, L1SCHED_SDCCH8_5, 3 }, + { L1SCHED_SACCH8_1, 3, L1SCHED_SDCCH8_6, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_SDCCH8_6, 1 }, + { L1SCHED_IDLE, 1, L1SCHED_SDCCH8_6, 2 }, + { L1SCHED_IDLE, 2, L1SCHED_SDCCH8_6, 3 }, + { L1SCHED_IDLE, 3, L1SCHED_SDCCH8_7, 0 }, + { L1SCHED_SACCH8_3, 0, L1SCHED_SDCCH8_7, 1 }, + { L1SCHED_SACCH8_3, 1, L1SCHED_SDCCH8_7, 2 }, + { L1SCHED_SACCH8_3, 2, L1SCHED_SDCCH8_7, 3 }, + { L1SCHED_SACCH8_3, 3, L1SCHED_SACCH8_0, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 1 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_0, 3 }, + + { L1SCHED_SDCCH8_0, 0, L1SCHED_SACCH8_1, 0 }, + { L1SCHED_SDCCH8_0, 1, L1SCHED_SACCH8_1, 1 }, + { L1SCHED_SDCCH8_0, 2, L1SCHED_SACCH8_1, 2 }, + { L1SCHED_SDCCH8_0, 3, L1SCHED_SACCH8_1, 3 }, + { L1SCHED_SDCCH8_1, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_1, 1, L1SCHED_IDLE, 1 }, + { L1SCHED_SDCCH8_1, 2, L1SCHED_IDLE, 2 }, + { L1SCHED_SDCCH8_1, 3, L1SCHED_IDLE, 3 }, + { L1SCHED_SDCCH8_CBCH, 0, L1SCHED_SACCH8_3, 0 }, + { L1SCHED_SDCCH8_CBCH, 1, L1SCHED_SACCH8_3, 1 }, + { L1SCHED_SDCCH8_CBCH, 2, L1SCHED_SACCH8_3, 2 }, + { L1SCHED_SDCCH8_CBCH, 3, L1SCHED_SACCH8_3, 3 }, + { L1SCHED_SDCCH8_3, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 1, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 2, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_3, 3, L1SCHED_SDCCH8_0, 0 }, + { L1SCHED_SDCCH8_4, 0, L1SCHED_SDCCH8_0, 1 }, + { L1SCHED_SDCCH8_4, 1, L1SCHED_SDCCH8_0, 2 }, + { L1SCHED_SDCCH8_4, 2, L1SCHED_SDCCH8_0, 3 }, + { L1SCHED_SDCCH8_4, 3, L1SCHED_SDCCH8_1, 0 }, + { L1SCHED_SDCCH8_5, 0, L1SCHED_SDCCH8_1, 1 }, + { L1SCHED_SDCCH8_5, 1, L1SCHED_SDCCH8_1, 2 }, + { L1SCHED_SDCCH8_5, 2, L1SCHED_SDCCH8_1, 3 }, + { L1SCHED_SDCCH8_5, 3, L1SCHED_IDLE, 0 }, + { L1SCHED_SDCCH8_6, 0, L1SCHED_IDLE, 1 }, + { L1SCHED_SDCCH8_6, 1, L1SCHED_IDLE, 2 }, + { L1SCHED_SDCCH8_6, 2, L1SCHED_IDLE, 3 }, + { L1SCHED_SDCCH8_6, 3, L1SCHED_SDCCH8_3, 0 }, + { L1SCHED_SDCCH8_7, 0, L1SCHED_SDCCH8_3, 1 }, + { L1SCHED_SDCCH8_7, 1, L1SCHED_SDCCH8_3, 2 }, + { L1SCHED_SDCCH8_7, 2, L1SCHED_SDCCH8_3, 3 }, + { L1SCHED_SDCCH8_7, 3, L1SCHED_SDCCH8_4, 0 }, + { L1SCHED_SACCH8_4, 0, L1SCHED_SDCCH8_4, 1 }, + { L1SCHED_SACCH8_4, 1, L1SCHED_SDCCH8_4, 2 }, + { L1SCHED_SACCH8_4, 2, L1SCHED_SDCCH8_4, 3 }, + { L1SCHED_SACCH8_4, 3, L1SCHED_SDCCH8_5, 0 }, + { L1SCHED_SACCH8_5, 0, L1SCHED_SDCCH8_5, 1 }, + { L1SCHED_SACCH8_5, 1, L1SCHED_SDCCH8_5, 2 }, + { L1SCHED_SACCH8_5, 2, L1SCHED_SDCCH8_5, 3 }, + { L1SCHED_SACCH8_5, 3, L1SCHED_SDCCH8_6, 0 }, + { L1SCHED_SACCH8_6, 0, L1SCHED_SDCCH8_6, 1 }, + { L1SCHED_SACCH8_6, 1, L1SCHED_SDCCH8_6, 2 }, + { L1SCHED_SACCH8_6, 2, L1SCHED_SDCCH8_6, 3 }, + { L1SCHED_SACCH8_6, 3, L1SCHED_SDCCH8_7, 0 }, + { L1SCHED_SACCH8_7, 0, L1SCHED_SDCCH8_7, 1 }, + { L1SCHED_SACCH8_7, 1, L1SCHED_SDCCH8_7, 2 }, + { L1SCHED_SACCH8_7, 2, L1SCHED_SDCCH8_7, 3 }, + { L1SCHED_SACCH8_7, 3, L1SCHED_SACCH8_4, 0 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 1 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 2 }, + { L1SCHED_IDLE, 0, L1SCHED_SACCH8_4, 3 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts0[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts1[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts2[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts3[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts4[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts5[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts6[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, +}; + +static const struct l1sched_tdma_frame frame_tchf_ts7[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 1, L1SCHED_SACCHTF, 1 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 2, L1SCHED_SACCHTF, 2 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 3, L1SCHED_SACCHTF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_TCHF, 0, L1SCHED_TCHF, 0 }, + { L1SCHED_TCHF, 1, L1SCHED_TCHF, 1 }, + { L1SCHED_TCHF, 2, L1SCHED_TCHF, 2 }, + { L1SCHED_TCHF, 3, L1SCHED_TCHF, 3 }, + { L1SCHED_SACCHTF, 0, L1SCHED_SACCHTF, 0 }, +}; + +static const struct l1sched_tdma_frame frame_tchh_ts01[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 }, +}; + +static const struct l1sched_tdma_frame frame_tchh_ts23[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 }, +}; + +static const struct l1sched_tdma_frame frame_tchh_ts45[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 }, +}; + +static const struct l1sched_tdma_frame frame_tchh_ts67[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 1, L1SCHED_SACCHTH_0, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 1, L1SCHED_SACCHTH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 2, L1SCHED_SACCHTH_0, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 2, L1SCHED_SACCHTH_1, 2 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 3, L1SCHED_SACCHTH_0, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 3, L1SCHED_SACCHTH_1, 3 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_0, 0, L1SCHED_SACCHTH_0, 0 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_TCHH_0, 0, L1SCHED_TCHH_0, 0 }, + { L1SCHED_TCHH_1, 0, L1SCHED_TCHH_1, 0 }, + { L1SCHED_TCHH_0, 1, L1SCHED_TCHH_0, 1 }, + { L1SCHED_TCHH_1, 1, L1SCHED_TCHH_1, 1 }, + { L1SCHED_SACCHTH_1, 0, L1SCHED_SACCHTH_1, 0 }, +}; + +static const struct l1sched_tdma_frame frame_pdch[104] = { + /* dl_chan dl_bid ul_chan ul_bid */ + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PTCCH, 0, L1SCHED_PTCCH, 0 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PTCCH, 1, L1SCHED_PTCCH, 1 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PTCCH, 2, L1SCHED_PTCCH, 2 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PTCCH, 3, L1SCHED_PTCCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_PDTCH, 0, L1SCHED_PDTCH, 0 }, + { L1SCHED_PDTCH, 1, L1SCHED_PDTCH, 1 }, + { L1SCHED_PDTCH, 2, L1SCHED_PDTCH, 2 }, + { L1SCHED_PDTCH, 3, L1SCHED_PDTCH, 3 }, + { L1SCHED_IDLE, 0, L1SCHED_IDLE, 0 }, +}; + +/* Logical channel mask for a single channel */ +#define M64(x) \ + ((uint64_t) 0x01 << x) + +/* Logical channel mask for BCCH+CCCH */ +#define M64_BCCH_CCCH \ + (uint64_t) 0x00 \ + | M64(L1SCHED_FCCH) \ + | M64(L1SCHED_SCH) \ + | M64(L1SCHED_BCCH) \ + | M64(L1SCHED_RACH) \ + | M64(L1SCHED_CCCH) + +/* Logical channel mask for SDCCH4 (with SACCH, all sub-channels) */ +#define M64_SDCCH4 \ + (uint64_t) 0x00 \ + | M64(L1SCHED_SDCCH4_0) | M64(L1SCHED_SACCH4_0) \ + | M64(L1SCHED_SDCCH4_1) | M64(L1SCHED_SACCH4_1) \ + | M64(L1SCHED_SDCCH4_2) | M64(L1SCHED_SACCH4_2) \ + | M64(L1SCHED_SDCCH4_3) | M64(L1SCHED_SACCH4_3) + +/* Logical channel mask for SDCCH8 (with SACCH, all sub-channels) */ +#define M64_SDCCH8 \ + (uint64_t) 0x00 \ + | M64(L1SCHED_SDCCH8_0) | M64(L1SCHED_SACCH8_0) \ + | M64(L1SCHED_SDCCH8_1) | M64(L1SCHED_SACCH8_1) \ + | M64(L1SCHED_SDCCH8_2) | M64(L1SCHED_SACCH8_2) \ + | M64(L1SCHED_SDCCH8_3) | M64(L1SCHED_SACCH8_3) \ + | M64(L1SCHED_SDCCH8_4) | M64(L1SCHED_SACCH8_4) \ + | M64(L1SCHED_SDCCH8_5) | M64(L1SCHED_SACCH8_5) \ + | M64(L1SCHED_SDCCH8_6) | M64(L1SCHED_SACCH8_6) \ + | M64(L1SCHED_SDCCH8_7) | M64(L1SCHED_SACCH8_7) + +/* Logical channel mask for TCH/F (with SACCH) */ +#define M64_TCHF \ + (uint64_t) 0x00 \ + | M64(L1SCHED_TCHF) | M64(L1SCHED_SACCHTF) + +/* Logical channel mask for TCH/H (with SACCH, all sub-channels) */ +#define M64_TCHH \ + (uint64_t) 0x00 \ + | M64(L1SCHED_TCHH_0) | M64(L1SCHED_SACCHTH_0) \ + | M64(L1SCHED_TCHH_1) | M64(L1SCHED_SACCHTH_1) + +/** + * A few notes about frame count: + * + * 26 frame multiframe - traffic multiframe + * 51 frame multiframe - control multiframe + * + * 102 = 2 x 51 frame multiframe + * 104 = 4 x 26 frame multiframe + */ +static const struct l1sched_tdma_multiframe layouts[] = { + { + GSM_PCHAN_NONE, "NONE", + 0, 0xff, + 0x00, + NULL + }, + { + GSM_PCHAN_CCCH, "BCCH+CCCH", + 51, 0xff, + M64_BCCH_CCCH, + frame_bcch + }, + { + GSM_PCHAN_CCCH_SDCCH4, "BCCH+CCCH+SDCCH/4+SACCH/4", + 102, 0xff, + M64_BCCH_CCCH | M64_SDCCH4, + frame_bcch_sdcch4 + }, + { + GSM_PCHAN_CCCH_SDCCH4_CBCH, "BCCH+CCCH+SDCCH/4+SACCH/4+CBCH", + 102, 0xff, + M64_BCCH_CCCH | M64_SDCCH4 | M64(L1SCHED_SDCCH4_CBCH), + frame_bcch_sdcch4_cbch + }, + { + GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH/8+SACCH/8", + 102, 0xff, + M64_SDCCH8, + frame_sdcch8 + }, + { + GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH/8+SACCH/8+CBCH", + 102, 0xff, + M64_SDCCH8 | M64(L1SCHED_SDCCH8_CBCH), + frame_sdcch8_cbch + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x01, + M64_TCHF, + frame_tchf_ts0 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x02, + M64_TCHF, + frame_tchf_ts1 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x04, + M64_TCHF, + frame_tchf_ts2 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x08, + M64_TCHF, + frame_tchf_ts3 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x10, + M64_TCHF, + frame_tchf_ts4 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x20, + M64_TCHF, + frame_tchf_ts5 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x40, + M64_TCHF, + frame_tchf_ts6 + }, + { + GSM_PCHAN_TCH_F, "TCH/F+SACCH", + 104, 0x80, + M64_TCHF, + frame_tchf_ts7 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0x03, + M64_TCHH, + frame_tchh_ts01 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0x0c, + M64_TCHH, + frame_tchh_ts23 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0x30, + M64_TCHH, + frame_tchh_ts45 + }, + { + GSM_PCHAN_TCH_H, "TCH/H+SACCH", + 104, 0xc0, + M64_TCHH, + frame_tchh_ts67 + }, + { + GSM_PCHAN_PDCH, "PDCH", + 104, 0xff, + M64(L1SCHED_PDTCH) | M64(L1SCHED_PTCCH), + frame_pdch + }, +}; + +const struct l1sched_tdma_multiframe * +l1sched_mframe_layout(enum gsm_phys_chan_config config, uint8_t tn) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(layouts); i++) { + if (layouts[i].chan_config != config) + continue; + if (~layouts[i].slotmask & (1 << tn)) + continue; + return &layouts[i]; + } + + return NULL; +} diff --git a/src/host/trxcon/src/sched_prim.c b/src/host/trxcon/src/sched_prim.c new file mode 100644 index 00000000..67be75e5 --- /dev/null +++ b/src/host/trxcon/src/sched_prim.c @@ -0,0 +1,410 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: primitive management + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <talloc.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/linuxlist.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +#define L1SCHED_PRIM_HEADROOM 64 +#define L1SCHED_PRIM_TAILROOM 512 + +osmo_static_assert(sizeof(struct l1sched_prim) <= L1SCHED_PRIM_HEADROOM, l1sched_prim_size); + +const struct value_string l1sched_prim_type_names[] = { + { L1SCHED_PRIM_T_DATA, "DATA" }, + { L1SCHED_PRIM_T_RACH, "RACH" }, + { L1SCHED_PRIM_T_SCH, "SCH" }, + { L1SCHED_PRIM_T_PCHAN_COMB, "PCHAN_COMB" }, + { 0, NULL }, +}; + +void l1sched_prim_init(struct msgb *msg, + enum l1sched_prim_type type, + enum osmo_prim_operation op) +{ + struct l1sched_prim *prim; + + msg->l2h = msg->data; + msg->l1h = msgb_push(msg, sizeof(*prim)); + + prim = l1sched_prim_from_msgb(msg); + osmo_prim_init(&prim->oph, 0, type, op, msg); +} + +struct msgb *l1sched_prim_alloc(enum l1sched_prim_type type, + enum osmo_prim_operation op) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(L1SCHED_PRIM_HEADROOM + L1SCHED_PRIM_TAILROOM, + L1SCHED_PRIM_HEADROOM, "l1sched_prim"); + if (msg == NULL) + return NULL; + + l1sched_prim_init(msg, type, op); + + return msg; +} + +/** + * Composes a new primitive from cached RR Measurement Report. + * + * @param lchan lchan to assign a primitive + * @return SACCH primitive to be transmitted + */ +static struct msgb *prim_compose_mr(struct l1sched_lchan_state *lchan) +{ + struct l1sched_prim *prim; + struct msgb *msg; + bool cached; + + /* Allocate a new primitive */ + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->data_req = (struct l1sched_prim_chdr) { + .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr | lchan->ts->index, + .link_id = L1SCHED_CH_LID_SACCH, + }; + + /* Check if the MR cache is populated (verify LAPDm header) */ + cached = (lchan->sacch.mr_cache[2] != 0x00 + && lchan->sacch.mr_cache[3] != 0x00 + && lchan->sacch.mr_cache[4] != 0x00); + if (!cached) { + memcpy(&lchan->sacch.mr_cache[0], + &lchan->ts->sched->sacch_cache[0], + sizeof(lchan->sacch.mr_cache)); + } + + /* Compose a new Measurement Report primitive */ + memcpy(msgb_put(msg, GSM_MACBLOCK_LEN), + &lchan->sacch.mr_cache[0], + GSM_MACBLOCK_LEN); + + /* Inform about the cache usage count */ + if (++lchan->sacch.mr_cache_usage > 5) { + LOGP_LCHAND(lchan, LOGL_NOTICE, + "SACCH MR cache usage count=%u > 5 " + "=> ancient measurements, please fix!\n", + lchan->sacch.mr_cache_usage); + } + + LOGP_LCHAND(lchan, LOGL_NOTICE, "Using cached Measurement Report\n"); + + return msg; +} + +/** + * Dequeues a SACCH primitive from transmit queue, if present. + * Otherwise dequeues a cached Measurement Report (the last + * received one). Finally, if the cache is empty, a "dummy" + * measurement report is used. + * + * According to 3GPP TS 04.08, section 3.4.1, SACCH channel + * accompanies either a traffic or a signaling channel. It + * has the particularity that continuous transmission must + * occur in both directions, so on the Uplink direction + * measurement result messages are sent at each possible + * occasion when nothing else has to be sent. The LAPDm + * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not + * applicable on SACCH channels! + * + * Unfortunately, 3GPP TS 04.08 doesn't clearly state + * which "else messages" besides Measurement Reports + * can be send by the MS on SACCH channels. However, + * in sub-clause 3.4.1 it's stated that the interval + * between two successive measurement result messages + * shall not exceed one L2 frame. + * + * @param lchan lchan to assign a primitive + * @return SACCH primitive to be transmitted + */ +struct msgb *l1sched_lchan_prim_dequeue_sacch(struct l1sched_lchan_state *lchan) +{ + struct msgb *msg_nmr = NULL; + struct msgb *msg_mr = NULL; + struct msgb *msg; + bool mr_now; + + /* Shall we transmit MR now? */ + mr_now = !lchan->sacch.mr_tx_last; + +#define PRIM_MSGB_IS_MR(msg) \ + (l1sched_prim_data_from_msgb(msg)[5] == GSM48_PDISC_RR && \ + l1sched_prim_data_from_msgb(msg)[6] == GSM48_MT_RR_MEAS_REP) + + /* Iterate over all primitives in the queue */ + llist_for_each_entry(msg, &lchan->tx_prims, list) { + /* Look for a Measurement Report */ + if (!msg_mr && PRIM_MSGB_IS_MR(msg)) + msg_mr = msg; + + /* Look for anything else */ + if (!msg_nmr && !PRIM_MSGB_IS_MR(msg)) + msg_nmr = msg; + + /* Should we look further? */ + if (mr_now && msg_mr) + break; /* MR was found */ + else if (!mr_now && msg_nmr) + break; /* something else was found */ + } + + LOGP_LCHAND(lchan, LOGL_DEBUG, + "SACCH MR selection: mr_tx_last=%d msg_mr=%p msg_nmr=%p\n", + lchan->sacch.mr_tx_last, msg_mr, msg_nmr); + + /* Prioritize non-MR prim if possible */ + if (mr_now && msg_mr) + msg = msg_mr; + else if (!mr_now && msg_nmr) + msg = msg_nmr; + else if (!mr_now && msg_mr) + msg = msg_mr; + else /* Nothing was found */ + msg = NULL; + + /* Have we found what we were looking for? */ + if (msg) /* Dequeue if so */ + llist_del(&msg->list); + else /* Otherwise compose a new MR */ + msg = prim_compose_mr(lchan); + + /* Update the cached report */ + if (msg == msg_mr) { + memcpy(lchan->sacch.mr_cache, msgb_l2(msg), GSM_MACBLOCK_LEN); + lchan->sacch.mr_cache_usage = 0; + + LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH MR cache has been updated\n"); + } + + /* Update the MR transmission state */ + lchan->sacch.mr_tx_last = PRIM_MSGB_IS_MR(msg); + + LOGP_LCHAND(lchan, LOGL_DEBUG, "SACCH decision: %s\n", + PRIM_MSGB_IS_MR(msg) ? "Measurement Report" : "data frame"); + + return msg; +} + +/** + * Dequeues either a FACCH, or a speech TCH primitive + * of a given channel type (Lm or Bm). + * + * @param lchan logical channel state + * @param facch FACCH (true) or speech (false) prim? + * @return either a FACCH, or a TCH primitive if found, + * otherwise NULL + */ +struct msgb *l1sched_lchan_prim_dequeue_tch(struct l1sched_lchan_state *lchan, bool facch) +{ + struct msgb *msg; + + /** + * There is no need to use the 'safe' list iteration here + * as an item removal is immediately followed by return. + */ + llist_for_each_entry(msg, &lchan->tx_prims, list) { + bool is_facch = msgb_l2len(msg) == GSM_MACBLOCK_LEN; + if (is_facch != facch) + continue; + + llist_del(&msg->list); + return msg; + } + + return NULL; +} + +/** + * Allocate a DATA.req with dummy LAPDm func=UI frame for the given logical channel. + * To be used when no suitable DATA.req is present in the Tx queue. + * + * @param lchan lchan to allocate a dummy primitive for + * @return an msgb with DATA.req primitive, or NULL + */ +struct msgb *l1sched_lchan_prim_dummy_lapdm(const struct l1sched_lchan_state *lchan) +{ + struct l1sched_prim *prim; + struct msgb *msg; + uint8_t *ptr; + + /* LAPDm func=UI is not applicable for SACCH */ + OSMO_ASSERT(!L1SCHED_CHAN_IS_SACCH(lchan->type)); + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->data_req = (struct l1sched_prim_chdr) { + .chan_nr = l1sched_lchan_desc[lchan->type].chan_nr | lchan->ts->index, + .link_id = l1sched_lchan_desc[lchan->type].link_id, + }; + + ptr = msgb_put(msg, GSM_MACBLOCK_LEN); + + /** + * TS 144.006, section 8.4.2.3 "Fill frames" + * A fill frame is a UI command frame for SAPI 0, P=0 + * and with an information field of 0 octet length. + */ + *(ptr++) = 0x01; + *(ptr++) = 0x03; + *(ptr++) = 0x01; + + /** + * TS 144.006, section 5.2 "Frame delimitation and fill bits" + * Except for the first octet containing fill bits which shall + * be set to the binary value "00101011", each fill bit should + * be set to a random value when sent by the network. + */ + *(ptr++) = 0x2b; + while (ptr < msg->tail) + *(ptr++) = (uint8_t)rand(); + + return msg; +} + +int l1sched_lchan_emit_data_ind(struct l1sched_lchan_state *lchan, + const uint8_t *data, size_t data_len, + int n_errors, int n_bits_total, + bool traffic) +{ + const struct l1sched_meas_set *meas = &lchan->meas_avg; + const struct l1sched_lchan_desc *lchan_desc; + struct l1sched_prim *prim; + struct msgb *msg; + + lchan_desc = &l1sched_lchan_desc[lchan->type]; + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->data_ind = (struct l1sched_prim_data_ind) { + .chdr = { + .frame_nr = meas->fn, + .chan_nr = lchan_desc->chan_nr | lchan->ts->index, + .link_id = lchan_desc->link_id, + .traffic = traffic, + }, + .toa256 = meas->toa256, + .rssi = meas->rssi, + .n_errors = n_errors, + .n_bits_total = n_bits_total, + }; + + if (data_len > 0) + memcpy(msgb_put(msg, data_len), data, data_len); + + return l1sched_prim_to_user(lchan->ts->sched, msg); +} + +int l1sched_lchan_emit_data_cnf(struct l1sched_lchan_state *lchan, + struct msgb *msg, uint32_t fn) +{ + struct l1sched_prim *prim; + + OSMO_ASSERT(msg != NULL); + + /* convert from DATA.req to DATA.cnf */ + prim = l1sched_prim_from_msgb(msg); + prim->oph.operation = PRIM_OP_CONFIRM; + + switch (prim->oph.primitive) { + case L1SCHED_PRIM_T_DATA: + prim->data_cnf.frame_nr = fn; + break; + case L1SCHED_PRIM_T_RACH: + prim->rach_cnf.chdr.frame_nr = fn; + break; + default: + /* shall not happen */ + OSMO_ASSERT(0); + } + + return l1sched_prim_to_user(lchan->ts->sched, msg); +} + +static int prim_enqeue(struct l1sched_state *sched, struct msgb *msg, + const struct l1sched_prim_chdr *chdr) +{ + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + struct l1sched_lchan_state *lchan; + + lchan = l1sched_find_lchan_by_chan_nr(sched, chdr->chan_nr, chdr->link_id); + if (OSMO_UNLIKELY(lchan == NULL || !lchan->active)) { + LOGP_SCHEDD(sched, LOGL_ERROR, + "No [active] lchan for primitive " L1SCHED_PRIM_STR_FMT " " + "(fn=%u, chan_nr=0x%02x, link_id=0x%02x, len=%u): %s\n", + L1SCHED_PRIM_STR_ARGS(prim), + chdr->frame_nr, chdr->chan_nr, chdr->link_id, + msgb_l2len(msg), msgb_hexdump_l2(msg)); + msgb_free(msg); + return -ENODEV; + } + + LOGP_LCHAND(lchan, LOGL_DEBUG, + "Enqueue primitive " L1SCHED_PRIM_STR_FMT " " + "(fn=%u, chan_nr=0x%02x, link_id=0x%02x, len=%u): %s\n", + L1SCHED_PRIM_STR_ARGS(prim), + chdr->frame_nr, chdr->chan_nr, chdr->link_id, + msgb_l2len(msg), msgb_hexdump_l2(msg)); + + msgb_enqueue(&lchan->tx_prims, msg); + return 0; +} + +int l1sched_prim_from_user(struct l1sched_state *sched, struct msgb *msg) +{ + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + + LOGP_SCHEDD(sched, LOGL_DEBUG, + "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n", + __func__, L1SCHED_PRIM_STR_ARGS(prim)); + + switch (OSMO_PRIM_HDR(&prim->oph)) { + case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST): + return prim_enqeue(sched, msg, &prim->data_req); + case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST): + return prim_enqeue(sched, msg, &prim->rach_req.chdr); + default: + LOGP_SCHEDD(sched, LOGL_ERROR, + "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n", + __func__, L1SCHED_PRIM_STR_ARGS(prim)); + msgb_free(msg); + return -ENOTSUP; + } +} diff --git a/src/host/trxcon/src/sched_trx.c b/src/host/trxcon/src/sched_trx.c new file mode 100644 index 00000000..f4124003 --- /dev/null +++ b/src/host/trxcon/src/sched_trx.c @@ -0,0 +1,894 @@ +/* + * OsmocomBB <-> SDR connection bridge + * TDMA scheduler: GSM PHY routines + * + * (C) 2017-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <error.h> +#include <errno.h> +#include <string.h> +#include <talloc.h> +#include <stdbool.h> + +#include <osmocom/gsm/a5.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/core/bits.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> +#include <osmocom/core/linuxlist.h> + +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> + +/* Logging categories to be used for common/data messages */ +int l1sched_log_cat_common = DLGLOBAL; +int l1sched_log_cat_data = DLGLOBAL; + +/* "Dummy" Measurement Report */ +static const uint8_t meas_rep_dummy[] = { + /* L1 SACCH pseudo-header */ + 0x0f, 0x00, + + /* LAPDm header */ + 0x01, 0x03, 0x49, + + /* RR Management messages, Measurement Report */ + 0x06, 0x15, + + /* Measurement results (see 3GPP TS 44.018, section 10.5.2.20): + * 0... .... = BA-USED: 0 + * .0.. .... = DTX-USED: DTX was not used + * ..11 0110 = RXLEV-FULL-SERVING-CELL: -57 <= x < -56 dBm (54) + * 0... .... = 3G-BA-USED: 0 + * .1.. .... = MEAS-VALID: The measurement results are not valid + * ..11 0110 = RXLEV-SUB-SERVING-CELL: -57 <= x < -56 dBm (54) + * 0... .... = SI23_BA_USED: 0 + * .000 .... = RXQUAL-FULL-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0) + * .... 000. = RXQUAL-SUB-SERVING-CELL: BER < 0.2%, Mean value 0.14% (0) + * .... ...1 11.. .... = NO-NCELL-M: Neighbour cell information not available */ + 0x36, 0x76, 0x01, 0xc0, + + /* 0** -- Padding with zeroes */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static int l1sched_cfg_pchan_comb_ind(struct l1sched_state *sched, + uint8_t tn, enum gsm_phys_chan_config pchan) +{ + struct l1sched_prim *prim; + struct msgb *msg; + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->pchan_comb_ind.tn = tn; + prim->pchan_comb_ind.pchan = pchan; + + return l1sched_prim_to_user(sched, msg); +} + +static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br); + +/* Pull an Uplink burst from the scheduler and store it to br->burst[]. + * The TDMA Fn advance must be applied by the caller (if needed). + * The given *br must be initialized by the caller. */ +void l1sched_pull_burst(struct l1sched_state *sched, struct l1sched_burst_req *br) +{ + struct l1sched_ts *ts = sched->ts[br->tn]; + const struct l1sched_tdma_frame *frame; + struct l1sched_lchan_state *lchan; + l1sched_lchan_tx_func *handler; + enum l1sched_lchan_type chan; + unsigned int offset; + + /* Check if the given timeslot is configured */ + if (ts == NULL || ts->mf_layout == NULL) + return; + + /* Get frame from multiframe */ + offset = br->fn % ts->mf_layout->period; + frame = &ts->mf_layout->frames[offset]; + + /* Get required info from frame */ + br->bid = frame->ul_bid; + chan = frame->ul_chan; + handler = l1sched_lchan_desc[chan].tx_fn; + + /* Omit lchans without handler */ + if (handler == NULL) + return; + + /* Make sure that lchan is allocated and active */ + lchan = l1sched_find_lchan_by_type(ts, chan); + if (lchan == NULL || !lchan->active) + return; + + /* Handover RACH needs to be handled regardless of the + * current channel type and the associated handler. */ + struct msgb *msg = llist_first_entry_or_null(&lchan->tx_prims, struct msgb, list); + if (msg && l1sched_prim_type_from_msgb(msg) == L1SCHED_PRIM_T_RACH) + handler = l1sched_lchan_desc[L1SCHED_RACH].tx_fn; + + /* Poke lchan handler */ + handler(lchan, br); + + /* Perform A5/X burst encryption if required */ + if (lchan->a5.algo) + l1sched_a5_burst_enc(lchan, br); +} + +void l1sched_logging_init(int log_cat_common, int log_cat_data) +{ + l1sched_log_cat_common = log_cat_common; + l1sched_log_cat_data = log_cat_data; +} + +struct l1sched_state *l1sched_alloc(void *ctx, const struct l1sched_cfg *cfg, void *priv) +{ + struct l1sched_state *sched; + + sched = talloc(ctx, struct l1sched_state); + if (!sched) + return NULL; + + *sched = (struct l1sched_state) { + .priv = priv, + }; + + memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy)); + + if (cfg->log_prefix == NULL) + sched->log_prefix = talloc_asprintf(sched, "l1sched[0x%p]: ", sched); + else + sched->log_prefix = talloc_strdup(sched, cfg->log_prefix); + + return sched; +} + +void l1sched_free(struct l1sched_state *sched) +{ + unsigned int tn; + + if (sched == NULL) + return; + + LOGP_SCHEDC(sched, LOGL_NOTICE, "Shutdown scheduler\n"); + + /* Free all potentially allocated timeslots */ + for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++) + l1sched_del_ts(sched, tn); + + talloc_free(sched); +} + +void l1sched_reset(struct l1sched_state *sched, bool reset_clock) +{ + unsigned int tn; + + if (sched == NULL) + return; + + LOGP_SCHEDC(sched, LOGL_NOTICE, "Reset scheduler %s\n", + reset_clock ? "and clock counter" : ""); + + /* Free all potentially allocated timeslots */ + for (tn = 0; tn < ARRAY_SIZE(sched->ts); tn++) + l1sched_del_ts(sched, tn); + + memcpy(&sched->sacch_cache[0], &meas_rep_dummy[0], sizeof(meas_rep_dummy)); +} + +struct l1sched_ts *l1sched_add_ts(struct l1sched_state *sched, int tn) +{ + /* Make sure that ts isn't allocated yet */ + if (sched->ts[tn] != NULL) { + LOGP_SCHEDC(sched, LOGL_ERROR, "Timeslot #%u already allocated\n", tn); + return NULL; + } + + LOGP_SCHEDC(sched, LOGL_NOTICE, "Add a new TDMA timeslot #%u\n", tn); + + sched->ts[tn] = talloc_zero(sched, struct l1sched_ts); + sched->ts[tn]->sched = sched; + sched->ts[tn]->index = tn; + + return sched->ts[tn]; +} + +void l1sched_del_ts(struct l1sched_state *sched, int tn) +{ + struct l1sched_lchan_state *lchan, *lchan_next; + struct l1sched_ts *ts; + + /* Find ts in list */ + ts = sched->ts[tn]; + if (ts == NULL) + return; + + LOGP_SCHEDC(sched, LOGL_NOTICE, "Delete TDMA timeslot #%u\n", tn); + + /* Deactivate all logical channels */ + l1sched_deactivate_all_lchans(ts); + + /* Free channel states */ + llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { + llist_del(&lchan->list); + talloc_free(lchan); + } + + /* Remove ts from list and free memory */ + sched->ts[tn] = NULL; + talloc_free(ts); + + /* Notify transceiver about that */ + l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE); +} + +#define LAYOUT_HAS_LCHAN(layout, lchan) \ + (layout->lchan_mask & ((uint64_t) 0x01 << lchan)) + +int l1sched_configure_ts(struct l1sched_state *sched, int tn, + enum gsm_phys_chan_config config) +{ + struct l1sched_lchan_state *lchan; + enum l1sched_lchan_type type; + struct l1sched_ts *ts; + + /* Try to find specified ts */ + ts = sched->ts[tn]; + if (ts != NULL) { + /* Reconfiguration of existing one */ + l1sched_reset_ts(sched, tn); + } else { + /* Allocate a new one if doesn't exist */ + ts = l1sched_add_ts(sched, tn); + if (ts == NULL) + return -ENOMEM; + } + + /* Choose proper multiframe layout */ + ts->mf_layout = l1sched_mframe_layout(config, tn); + if (!ts->mf_layout) + return -EINVAL; + if (ts->mf_layout->chan_config != config) + return -EINVAL; + + LOGP_SCHEDC(sched, LOGL_NOTICE, + "(Re)configure TDMA timeslot #%u as %s\n", + tn, ts->mf_layout->name); + + /* Init logical channels list */ + INIT_LLIST_HEAD(&ts->lchans); + + /* Allocate channel states */ + for (type = 0; type < _L1SCHED_CHAN_MAX; type++) { + if (!LAYOUT_HAS_LCHAN(ts->mf_layout, type)) + continue; + + /* Allocate a channel state */ + lchan = talloc_zero(ts, struct l1sched_lchan_state); + if (!lchan) + return -ENOMEM; + + /* set backpointer */ + lchan->ts = ts; + + /* Set channel type */ + lchan->type = type; + + /* Init the Tx queue */ + INIT_LLIST_HEAD(&lchan->tx_prims); + + /* Add to the list of channel states */ + llist_add_tail(&lchan->list, &ts->lchans); + + /* Enable channel automatically if required */ + if (l1sched_lchan_desc[type].flags & L1SCHED_CH_FLAG_AUTO) + l1sched_activate_lchan(ts, type); + } + + /* Notify transceiver about TS activation */ + l1sched_cfg_pchan_comb_ind(sched, tn, config); + + return 0; +} + +int l1sched_reset_ts(struct l1sched_state *sched, int tn) +{ + struct l1sched_lchan_state *lchan, *lchan_next; + struct l1sched_ts *ts; + + /* Try to find specified ts */ + ts = sched->ts[tn]; + if (ts == NULL) + return -EINVAL; + + /* Undefine multiframe layout */ + ts->mf_layout = NULL; + + /* Deactivate all logical channels */ + l1sched_deactivate_all_lchans(ts); + + /* Free channel states */ + llist_for_each_entry_safe(lchan, lchan_next, &ts->lchans, list) { + llist_del(&lchan->list); + talloc_free(lchan); + } + + /* Notify transceiver about that */ + l1sched_cfg_pchan_comb_ind(sched, tn, GSM_PCHAN_NONE); + + return 0; +} + +int l1sched_start_ciphering(struct l1sched_ts *ts, uint8_t algo, + const uint8_t *key, uint8_t key_len) +{ + struct l1sched_lchan_state *lchan; + + /* Prevent NULL-pointer deference */ + if (!ts) + return -EINVAL; + + /* Make sure we can store this key */ + if (key_len > MAX_A5_KEY_LEN) + return -ERANGE; + + /* Iterate over all allocated logical channels */ + llist_for_each_entry(lchan, &ts->lchans, list) { + /* Omit inactive channels */ + if (!lchan->active) + continue; + + /* Set key length and algorithm */ + lchan->a5.key_len = key_len; + lchan->a5.algo = algo; + + /* Copy requested key */ + if (key_len) + memcpy(lchan->a5.key, key, key_len); + } + + return 0; +} + +struct l1sched_lchan_state *l1sched_find_lchan_by_type(struct l1sched_ts *ts, + enum l1sched_lchan_type type) +{ + struct l1sched_lchan_state *lchan; + + llist_for_each_entry(lchan, &ts->lchans, list) + if (lchan->type == type) + return lchan; + + return NULL; +} + +struct l1sched_lchan_state *l1sched_find_lchan_by_chan_nr(struct l1sched_state *sched, + uint8_t chan_nr, uint8_t link_id) +{ + const struct l1sched_ts *ts = sched->ts[chan_nr & 0x07]; + const struct l1sched_lchan_desc *lchan_desc; + struct l1sched_lchan_state *lchan; + + if (ts == NULL) + return NULL; + + llist_for_each_entry(lchan, &ts->lchans, list) { + lchan_desc = &l1sched_lchan_desc[lchan->type]; + if (lchan_desc->chan_nr != (chan_nr & RSL_CHAN_NR_MASK)) + continue; + if (lchan_desc->link_id != link_id) + continue; + return lchan; + } + + return NULL; +} + +int l1sched_set_lchans(struct l1sched_ts *ts, uint8_t chan_nr, + int active, uint8_t tch_mode, uint8_t tsc) +{ + const struct l1sched_lchan_desc *lchan_desc; + struct l1sched_lchan_state *lchan; + int rc = 0; + + /* Prevent NULL-pointer deference */ + OSMO_ASSERT(ts != NULL); + + /* Iterate over all allocated lchans */ + llist_for_each_entry(lchan, &ts->lchans, list) { + lchan_desc = &l1sched_lchan_desc[lchan->type]; + + if (lchan_desc->chan_nr == (chan_nr & RSL_CHAN_NR_MASK)) { + if (active) { + rc |= l1sched_activate_lchan(ts, lchan->type); + lchan->tch_mode = tch_mode; + lchan->tsc = tsc; + } else + rc |= l1sched_deactivate_lchan(ts, lchan->type); + } + } + + return rc; +} + +int l1sched_lchan_set_amr_cfg(struct l1sched_lchan_state *lchan, + uint8_t codecs_bitmask, uint8_t start_codec) +{ + int n = 0; + int acum = 0; + int pos; + + while ((pos = ffs(codecs_bitmask)) != 0) { + acum += pos; + LOGP_LCHANC(lchan, LOGL_DEBUG, "AMR codec[%u] = %u\n", n, acum - 1); + lchan->amr.codec[n++] = acum - 1; + codecs_bitmask >>= pos; + } + if (n == 0) { + LOGP_LCHANC(lchan, LOGL_ERROR, "Empty AMR codec mode bitmask!\n"); + return -EINVAL; + } + + lchan->amr.codecs = n; + lchan->amr.dl_ft = start_codec; + lchan->amr.dl_cmr = start_codec; + lchan->amr.ul_ft = start_codec; + lchan->amr.ul_cmr = start_codec; + + return 0; +} + +int l1sched_activate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan) +{ + const struct l1sched_lchan_desc *lchan_desc = &l1sched_lchan_desc[chan]; + struct l1sched_lchan_state *lchan; + + /* Try to find requested logical channel */ + lchan = l1sched_find_lchan_by_type(ts, chan); + if (lchan == NULL) + return -EINVAL; + + if (lchan->active) { + LOGP_LCHANC(lchan, LOGL_ERROR, "is already activated\n"); + return -EINVAL; + } + + LOGP_LCHANC(lchan, LOGL_NOTICE, "activating\n"); + + /* Conditionally allocate memory for bursts */ + if (lchan_desc->rx_fn && lchan_desc->burst_buf_size > 0) { + lchan->rx_bursts = talloc_zero_size(lchan, + lchan_desc->burst_buf_size); + if (lchan->rx_bursts == NULL) + return -ENOMEM; + } + + if (lchan_desc->tx_fn && lchan_desc->burst_buf_size > 0) { + lchan->tx_bursts = talloc_zero_size(lchan, + lchan_desc->burst_buf_size); + if (lchan->tx_bursts == NULL) + return -ENOMEM; + } + + /* Finally, update channel status */ + lchan->active = 1; + + return 0; +} + +static void l1sched_reset_lchan(struct l1sched_lchan_state *lchan) +{ + struct msgb *msg; + + /* Prevent NULL-pointer deference */ + OSMO_ASSERT(lchan != NULL); + + /* Print some TDMA statistics for Downlink */ + if (l1sched_lchan_desc[lchan->type].rx_fn && lchan->active) { + LOGP_LCHANC(lchan, LOGL_DEBUG, "TDMA statistics: " + "%lu DL frames have been processed, " + "%lu lost (compensated), last fn=%u\n", + lchan->tdma.num_proc, + lchan->tdma.num_lost, + lchan->tdma.last_proc); + } + + /* Reset internal state variables */ + lchan->rx_burst_mask = 0x00; + lchan->tx_burst_mask = 0x00; + + /* Free burst memory */ + talloc_free(lchan->rx_bursts); + talloc_free(lchan->tx_bursts); + + lchan->rx_bursts = NULL; + lchan->tx_bursts = NULL; + + /* Flush the queue of pending Tx prims */ + while ((msg = msgb_dequeue(&lchan->tx_prims)) != NULL) { + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + + LOGP_LCHANC(lchan, LOGL_NOTICE, "%s(): dropping Tx prim (fn=%u): %s\n", + __func__, prim->data_req.frame_nr, msgb_hexdump_l2(msg)); + msgb_free(msg); + } + + /* Channel specific stuff */ + if (L1SCHED_CHAN_IS_TCH(lchan->type)) { + lchan->dl_ongoing_facch = 0; + lchan->ul_facch_blocks = 0; + + lchan->tch_mode = GSM48_CMODE_SIGN; + + /* Reset AMR state */ + memset(&lchan->amr, 0x00, sizeof(lchan->amr)); + } else if (L1SCHED_CHAN_IS_SACCH(lchan->type)) { + /* Reset SACCH state */ + memset(&lchan->sacch, 0x00, sizeof(lchan->sacch)); + } + + /* Reset ciphering state */ + memset(&lchan->a5, 0x00, sizeof(lchan->a5)); + + /* Reset TDMA frame statistics */ + memset(&lchan->tdma, 0x00, sizeof(lchan->tdma)); +} + +int l1sched_deactivate_lchan(struct l1sched_ts *ts, enum l1sched_lchan_type chan) +{ + struct l1sched_lchan_state *lchan; + + /* Try to find requested logical channel */ + lchan = l1sched_find_lchan_by_type(ts, chan); + if (lchan == NULL) + return -EINVAL; + + if (!lchan->active) { + LOGP_LCHANC(lchan, LOGL_ERROR, "is already deactivated\n"); + return -EINVAL; + } + + LOGP_LCHANC(lchan, LOGL_DEBUG, "deactivating\n"); + + /* Reset internal state, free memory */ + l1sched_reset_lchan(lchan); + + /* Update activation flag */ + lchan->active = 0; + + return 0; +} + +void l1sched_deactivate_all_lchans(struct l1sched_ts *ts) +{ + struct l1sched_lchan_state *lchan; + + LOGP_SCHEDC(ts->sched, LOGL_DEBUG, + "Deactivating all logical channels on ts=%d\n", + ts->index); + + llist_for_each_entry(lchan, &ts->lchans, list) { + /* Omit inactive channels */ + if (!lchan->active) + continue; + + /* Reset internal state, free memory */ + l1sched_reset_lchan(lchan); + + /* Update activation flag */ + lchan->active = 0; + } +} + +enum gsm_phys_chan_config l1sched_chan_nr2pchan_config(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + + if (cbits == ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs) + return GSM_PCHAN_TCH_F; + else if ((cbits & 0x1e) == ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0)) + return GSM_PCHAN_TCH_H; + else if ((cbits & 0x1c) == ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0)) + return GSM_PCHAN_CCCH_SDCCH4; + else if ((cbits & 0x18) == ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0)) + return GSM_PCHAN_SDCCH8_SACCH8C; + else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH4) + return GSM_PCHAN_CCCH_SDCCH4_CBCH; + else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_CBCH8) + return GSM_PCHAN_SDCCH8_SACCH8C_CBCH; + else if ((cbits & 0x1f) == ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH) + return GSM_PCHAN_PDCH; + + return GSM_PCHAN_NONE; +} + +static void l1sched_a5_burst_dec(struct l1sched_lchan_state *lchan, + struct l1sched_burst_ind *bi) +{ + ubit_t ks[114]; + int i; + + /* Generate keystream for a DL burst */ + osmo_a5(lchan->a5.algo, lchan->a5.key, bi->fn, ks, NULL); + + /* Apply keystream over ciphertext */ + for (i = 0; i < 57; i++) { + if (ks[i]) + bi->burst[i + 3] *= -1; + if (ks[i + 57]) + bi->burst[i + 88] *= -1; + } +} + +static void l1sched_a5_burst_enc(struct l1sched_lchan_state *lchan, + struct l1sched_burst_req *br) +{ + ubit_t ks[114]; + int i; + + /* Generate keystream for an UL burst */ + osmo_a5(lchan->a5.algo, lchan->a5.key, br->fn, NULL, ks); + + /* Apply keystream over plaintext */ + for (i = 0; i < 57; i++) { + br->burst[i + 3] ^= ks[i]; + br->burst[i + 88] ^= ks[i + 57]; + } +} + +static int subst_frame_loss(struct l1sched_lchan_state *lchan, + l1sched_lchan_rx_func *handler, + uint32_t fn) +{ + const struct l1sched_tdma_multiframe *mf; + const struct l1sched_tdma_frame *fp; + int elapsed, i; + + /* Wait until at least one TDMA frame is processed */ + if (lchan->tdma.num_proc == 0) + return -EAGAIN; + + /* Short alias for the current multiframe */ + mf = lchan->ts->mf_layout; + + /* Calculate how many frames elapsed since the last received one. + * The algorithm is based on GSM::FNDelta() from osmo-trx. */ + elapsed = fn - lchan->tdma.last_proc; + if (elapsed >= GSM_TDMA_HYPERFRAME / 2) + elapsed -= GSM_TDMA_HYPERFRAME; + else if (elapsed < -GSM_TDMA_HYPERFRAME / 2) + elapsed += GSM_TDMA_HYPERFRAME; + + /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */ + if (elapsed < 0) { + /* This burst has already been substituted by a dummy burst (all bits set to zero), + * so better drop it. Otherwise we risk to get undefined behavior in handler(). */ + LOGP_LCHAND(lchan, LOGL_ERROR, "Rx burst with fn=%u older than the last " + "processed fn=%u (see OS#4658) => dropping\n", + fn, lchan->tdma.last_proc); + return -EALREADY; + } + + /* Check how many frames we (potentially) need to compensate */ + if (elapsed > mf->period) { + LOGP_LCHANC(lchan, LOGL_NOTICE, + "Too many (>%u) contiguous TDMA frames elapsed (%d) " + "since the last processed fn=%u (current %u)\n", + mf->period, elapsed, lchan->tdma.last_proc, fn); + return -EIO; + } else if (elapsed == 0) { + LOGP_LCHANC(lchan, LOGL_ERROR, + "No TDMA frames elapsed since the last processed " + "fn=%u, must be a bug?\n", lchan->tdma.last_proc); + return -EIO; + } + + struct l1sched_burst_ind bi = { + .fn = lchan->tdma.last_proc, + .tn = lchan->ts->index, + .toa256 = 0, + .rssi = -120, + .burst = { 0 }, + .burst_len = GSM_NBITS_NB_GMSK_BURST, + }; + + /* Traverse from fp till the current frame */ + for (i = 0; i < elapsed - 1; i++) { + fp = &mf->frames[GSM_TDMA_FN_INC(bi.fn) % mf->period]; + if (fp->dl_chan != lchan->type) + continue; + + LOGP_LCHANC(lchan, LOGL_NOTICE, + "Substituting lost TDMA frame fn=%u\n", bi.fn); + + bi.bid = fp->dl_bid; + handler(lchan, &bi); + + /* Update TDMA frame statistics */ + lchan->tdma.last_proc = bi.fn; + lchan->tdma.num_proc++; + lchan->tdma.num_lost++; + } + + return 0; +} + +int l1sched_handle_rx_burst(struct l1sched_state *sched, + struct l1sched_burst_ind *bi) +{ + struct l1sched_lchan_state *lchan; + const struct l1sched_tdma_frame *frame; + struct l1sched_ts *ts = sched->ts[bi->tn]; + + l1sched_lchan_rx_func *handler; + enum l1sched_lchan_type chan; + uint8_t offset; + int rc; + + /* Check whether required timeslot is allocated and configured */ + if (ts == NULL || ts->mf_layout == NULL) { + LOGP_SCHEDD(sched, LOGL_DEBUG, + "Timeslot #%u isn't configured, ignoring burst...\n", bi->tn); + return -EINVAL; + } + + /* Get frame from multiframe */ + offset = bi->fn % ts->mf_layout->period; + frame = ts->mf_layout->frames + offset; + + /* Get required info from frame */ + bi->bid = frame->dl_bid; + chan = frame->dl_chan; + handler = l1sched_lchan_desc[chan].rx_fn; + + /* Omit bursts which have no handler, like IDLE bursts. + * TODO: handle noise indications during IDLE frames. */ + if (!handler) + return -ENODEV; + + /* Find required channel state */ + lchan = l1sched_find_lchan_by_type(ts, chan); + if (lchan == NULL) + return -ENODEV; + + /* Ensure that channel is active */ + if (!lchan->active) + return 0; + + /* Compensate lost TDMA frames (if any) */ + rc = subst_frame_loss(lchan, handler, bi->fn); + if (rc == -EALREADY) + return rc; + + /* Perform A5/X decryption if required */ + if (lchan->a5.algo) + l1sched_a5_burst_dec(lchan, bi); + + /* Put burst to handler */ + handler(lchan, bi); + + /* Update TDMA frame statistics */ + lchan->tdma.last_proc = bi->fn; + + if (++lchan->tdma.num_proc == 0) { + /* Theoretically, we may have an integer overflow of num_proc counter. + * As a consequence, subst_frame_loss() will be unable to compensate + * one (potentionally lost) Downlink burst. On practice, it would + * happen once in 4615 * 10e-6 * (2 ^ 32 - 1) seconds or ~6 years. */ + LOGP_LCHAND(lchan, LOGL_NOTICE, + "Too many TDMA frames have been processed. " + "Are you running trxcon for more than 6 years?!?\n"); + lchan->tdma.num_proc = 1; + } + + return 0; +} + +int l1sched_handle_rx_probe(struct l1sched_state *sched, + struct l1sched_probe *probe) +{ + struct l1sched_ts *ts = sched->ts[probe->tn]; + const struct l1sched_tdma_frame *frame; + struct l1sched_lchan_state *lchan; + unsigned int offset; + + /* Check whether required timeslot is allocated and configured */ + if (ts == NULL || ts->mf_layout == NULL) + return -EINVAL; + + /* Get frame from multiframe */ + offset = probe->fn % ts->mf_layout->period; + frame = &ts->mf_layout->frames[offset]; + + if (l1sched_lchan_desc[frame->dl_chan].rx_fn == NULL) + return -ENODEV; + + /* Find the appropriate logical channel */ + lchan = l1sched_find_lchan_by_type(ts, frame->dl_chan); + if (lchan == NULL) + return -ENODEV; + + if (lchan->active) + probe->flags |= L1SCHED_PROBE_F_ACTIVE; + + return 0; +} + +#define MEAS_HIST_FIRST(hist) \ + (&hist->buf[0]) +#define MEAS_HIST_LAST(hist) \ + (MEAS_HIST_FIRST(hist) + ARRAY_SIZE(hist->buf) - 1) + +/* Add a new set of measurements to the history */ +void l1sched_lchan_meas_push(struct l1sched_lchan_state *lchan, + const struct l1sched_burst_ind *bi) +{ + struct l1sched_lchan_meas_hist *hist = &lchan->meas_hist; + + /* Find a new position where to store the measurements */ + if (hist->head == MEAS_HIST_LAST(hist) || hist->head == NULL) + hist->head = MEAS_HIST_FIRST(hist); + else + hist->head++; + + *hist->head = (struct l1sched_meas_set) { + .fn = bi->fn, + .toa256 = bi->toa256, + .rssi = bi->rssi, + }; +} + +/* Calculate the AVG of n measurements from the history */ +void l1sched_lchan_meas_avg(struct l1sched_lchan_state *lchan, unsigned int n) +{ + struct l1sched_lchan_meas_hist *hist = &lchan->meas_hist; + struct l1sched_meas_set *meas = hist->head; + int toa256_sum = 0; + int rssi_sum = 0; + int i; + + OSMO_ASSERT(n > 0 && n <= ARRAY_SIZE(hist->buf)); + OSMO_ASSERT(meas != NULL); + + /* Traverse backwards up to n entries, calculate the sum */ + for (i = 0; i < n; i++) { + toa256_sum += meas->toa256; + rssi_sum += meas->rssi; + + /* Do not go below the first burst */ + if (i + 1 == n) + break; + + if (meas == MEAS_HIST_FIRST(hist)) + meas = MEAS_HIST_LAST(hist); + else + meas--; + } + + /* Calculate the AVG */ + lchan->meas_avg.toa256 = toa256_sum / n; + lchan->meas_avg.rssi = rssi_sum / n; + + /* As a bonus, store TDMA frame number of the first burst */ + lchan->meas_avg.fn = meas->fn; +} diff --git a/src/host/trxcon/trx_if.c b/src/host/trxcon/src/trx_if.c index 8dbbd128..fad10264 100644 --- a/src/host/trxcon/trx_if.c +++ b/src/host/trxcon/src/trx_if.c @@ -4,6 +4,7 @@ * * Copyright (C) 2013 by Andreas Eversberg <jolly@eversberg.eu> * Copyright (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com> + * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> * * All Rights Reserved * @@ -38,12 +39,17 @@ #include <osmocom/core/fsm.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsm0502.h> -#include "l1ctl.h" -#include "trxcon.h" -#include "trx_if.h" -#include "logging.h" -#include "scheduler.h" +#include <osmocom/bb/trxcon/trx_if.h> +#include <osmocom/bb/trxcon/logging.h> + +#define TRXDv0_HDR_LEN 8 + +#define S(x) (1 << (x)) + +static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause); static struct value_string trx_evt_names[] = { { 0, NULL } /* no events? */ @@ -52,8 +58,8 @@ static struct value_string trx_evt_names[] = { static struct osmo_fsm_state trx_fsm_states[] = { [TRX_STATE_OFFLINE] = { .out_state_mask = ( - GEN_MASK(TRX_STATE_IDLE) | - GEN_MASK(TRX_STATE_RSP_WAIT)), + S(TRX_STATE_IDLE) | + S(TRX_STATE_RSP_WAIT)), .name = "OFFLINE", }, [TRX_STATE_IDLE] = { @@ -62,25 +68,26 @@ static struct osmo_fsm_state trx_fsm_states[] = { }, [TRX_STATE_ACTIVE] = { .out_state_mask = ( - GEN_MASK(TRX_STATE_IDLE) | - GEN_MASK(TRX_STATE_RSP_WAIT)), + S(TRX_STATE_IDLE) | + S(TRX_STATE_RSP_WAIT)), .name = "ACTIVE", }, [TRX_STATE_RSP_WAIT] = { .out_state_mask = ( - GEN_MASK(TRX_STATE_IDLE) | - GEN_MASK(TRX_STATE_ACTIVE) | - GEN_MASK(TRX_STATE_OFFLINE)), + S(TRX_STATE_IDLE) | + S(TRX_STATE_ACTIVE) | + S(TRX_STATE_OFFLINE)), .name = "RSP_WAIT", }, }; static struct osmo_fsm trx_fsm = { - .name = "trx_interface_fsm", + .name = "trx_interface", .states = trx_fsm_states, .num_states = ARRAY_SIZE(trx_fsm_states), - .log_subsys = DTRX, + .log_subsys = DTRXC, .event_names = trx_evt_names, + .cleanup = &trx_fsm_cleanup_cb, }; static int trx_udp_open(void *priv, struct osmo_fd *ofd, const char *host_local, @@ -143,13 +150,13 @@ static void trx_ctrl_send(struct trx_instance *trx) tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); /* Send command */ - LOGP(DTRX, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd); + LOGPFSML(trx->fi, LOGL_DEBUG, "Sending control '%s'\n", tcm->cmd); send(trx->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd) + 1, 0); /* Trigger state machine */ - if (trx->fsm->state != TRX_STATE_RSP_WAIT) { - trx->prev_state = trx->fsm->state; - osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_RSP_WAIT, 0, 0); + if (trx->fi->state != TRX_STATE_RSP_WAIT) { + trx->prev_state = trx->fi->state; + osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_RSP_WAIT, 0, 0); } /* Start expire timer */ @@ -167,13 +174,13 @@ static void trx_ctrl_timer_cb(void *data) if (llist_empty(&trx->trx_ctrl_list)) return; - LOGP(DTRX, LOGL_NOTICE, "No response from transceiver...\n"); + LOGPFSML(trx->fi, LOGL_NOTICE, "No response from transceiver...\n"); tcm = llist_entry(trx->trx_ctrl_list.next, struct trx_ctrl_msg, list); if (++tcm->retry_cnt > 3) { - LOGP(DTRX, LOGL_NOTICE, "Transceiver offline\n"); - osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_OFFLINE, 0, 0); - osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_OFFLINE, trx); + LOGPFSML(trx->fi, LOGL_NOTICE, "Transceiver offline\n"); + osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_OFFLINE, 0, 0); + osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_TIMEOUT, NULL); return; } @@ -212,7 +219,7 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical, tcm->cmd_len = strlen(cmd); tcm->critical = critical; llist_add_tail(&tcm->list, &trx->trx_ctrl_list); - LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd); + LOGPFSML(trx->fi, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd); /* Send message, if no pending messages */ if (!pending) @@ -242,23 +249,22 @@ static int trx_ctrl_cmd(struct trx_instance *trx, int critical, * RSP POWERON <status> */ -int trx_if_cmd_echo(struct trx_instance *trx) +static int trx_if_cmd_echo(struct trx_instance *trx) { return trx_ctrl_cmd(trx, 1, "ECHO", ""); } -int trx_if_cmd_poweroff(struct trx_instance *trx) +static int trx_if_cmd_poweroff(struct trx_instance *trx) { return trx_ctrl_cmd(trx, 1, "POWEROFF", ""); } -int trx_if_cmd_poweron(struct trx_instance *trx) +static int trx_if_cmd_poweron(struct trx_instance *trx) { - if (trx->powered_up) { - /* FIXME: this should be handled by the FSM, not here! */ - LOGP(DTRX, LOGL_ERROR, "Suppressing POWERON as we're already powered up\n"); +#if 0 + if (trx->powered_up) return -EAGAIN; - } +#endif return trx_ctrl_cmd(trx, 1, "POWERON", ""); } @@ -273,9 +279,25 @@ int trx_if_cmd_poweron(struct trx_instance *trx) * RSP SETSLOT <status> <timeslot> <chantype> */ -int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type) +static int trx_if_cmd_setslot(struct trx_instance *trx, + const struct trxcon_phyif_cmdp_setslot *cmdp) { - return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u", tn, type); + /* Values correspond to 'enum ChannelCombination' in osmo-trx.git */ + static const uint8_t chan_types[_GSM_PCHAN_MAX] = { + [GSM_PCHAN_UNKNOWN] = 0, + [GSM_PCHAN_NONE] = 0, + [GSM_PCHAN_CCCH] = 4, + [GSM_PCHAN_CCCH_SDCCH4] = 5, + [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 5, + [GSM_PCHAN_TCH_F] = 1, + [GSM_PCHAN_TCH_H] = 3, + [GSM_PCHAN_SDCCH8_SACCH8C] = 7, + [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 7, + [GSM_PCHAN_PDCH] = 13, + }; + + return trx_ctrl_cmd(trx, 1, "SETSLOT", "%u %u", + cmdp->tn, chan_types[cmdp->pchan]); } /* @@ -290,28 +312,30 @@ int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type) * RSP (RX/TX)TUNE <status> <kHz> */ -int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn) +static int trx_if_cmd_rxtune(struct trx_instance *trx, + const struct trxcon_phyif_cmdp_setfreq_h0 *cmdp) { uint16_t freq10; /* RX is downlink on MS side */ - freq10 = gsm_arfcn2freq10(band_arfcn, 0); + freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 0); if (freq10 == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn); + LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", cmdp->band_arfcn); return -ENOTSUP; } return trx_ctrl_cmd(trx, 1, "RXTUNE", "%u", freq10 * 100); } -int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn) +static int trx_if_cmd_txtune(struct trx_instance *trx, + const struct trxcon_phyif_cmdp_setfreq_h0 *cmdp) { uint16_t freq10; /* TX is uplink on MS side */ - freq10 = gsm_arfcn2freq10(band_arfcn, 1); + freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 1); if (freq10 == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn); + LOGPFSML(trx->fi, LOGL_ERROR, "ARFCN %d not defined\n", cmdp->band_arfcn); return -ENOTSUP; } @@ -330,19 +354,16 @@ int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn) * RSP MEASURE <status> <kHz> <dB> */ -int trx_if_cmd_measure(struct trx_instance *trx, - uint16_t band_arfcn_start, uint16_t band_arfcn_stop) +static int trx_if_cmd_measure(struct trx_instance *trx, + const struct trxcon_phyif_cmdp_measure *cmdp) { uint16_t freq10; - /* Update ARFCN range for measurement */ - trx->pm_band_arfcn_start = band_arfcn_start; - trx->pm_band_arfcn_stop = band_arfcn_stop; - /* Calculate a frequency for current ARFCN (DL) */ - freq10 = gsm_arfcn2freq10(band_arfcn_start, 0); + freq10 = gsm_arfcn2freq10(cmdp->band_arfcn, 0); if (freq10 == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "ARFCN %d not defined\n", band_arfcn_start); + LOGPFSML(trx->fi, LOGL_ERROR, + "ARFCN %d not defined\n", cmdp->band_arfcn); return -ENOTSUP; } @@ -359,23 +380,22 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) sscanf(resp, "%u %d", &freq10, &dbm); freq10 /= 100; - /* Check received ARFCN against expected */ band_arfcn = gsm_freq102arfcn((uint16_t) freq10, 0); - if (band_arfcn != trx->pm_band_arfcn_start) { - LOGP(DTRX, LOGL_ERROR, "Power measurement error: " - "response ARFCN=%u doesn't match expected ARFCN=%u\n", - band_arfcn &~ ARFCN_FLAG_MASK, - trx->pm_band_arfcn_start &~ ARFCN_FLAG_MASK); + if (band_arfcn == 0xffff) { + LOGPFSML(trx->fi, LOGL_ERROR, + "Failed to parse ARFCN from RSP MEASURE: %s\n", resp); return; } - /* Send L1CTL_PM_CONF */ - l1ctl_tx_pm_conf(trx->l1l, band_arfcn, dbm, - band_arfcn == trx->pm_band_arfcn_stop); + const struct trxcon_phyif_rsp rsp = { + .type = TRXCON_PHYIF_CMDT_MEASURE, + .param.measure = { + .band_arfcn = band_arfcn, + .dbm = dbm, + }, + }; - /* Schedule a next measurement */ - if (band_arfcn != trx->pm_band_arfcn_stop) - trx_if_cmd_measure(trx, ++band_arfcn, trx->pm_band_arfcn_stop); + trxcon_phyif_handle_rsp(trx->priv, &rsp); } /* @@ -391,9 +411,10 @@ static void trx_if_measure_rsp_cb(struct trx_instance *trx, char *resp) * RSP SETTA <status> <TA> */ -int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta) +static int trx_if_cmd_setta(struct trx_instance *trx, + const struct trxcon_phyif_cmdp_setta *cmdp) { - return trx_ctrl_cmd(trx, 0, "SETTA", "%d", ta); + return trx_ctrl_cmd(trx, 0, "SETTA", "%d", cmdp->ta); } /* @@ -409,8 +430,8 @@ int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta) * channel list is expected to be sorted in ascending order. */ -int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, - uint8_t maio, uint16_t *ma, size_t ma_len) +static int trx_if_cmd_setfh(struct trx_instance *trx, + const struct trxcon_phyif_cmdp_setfreq_h1 *cmdp) { /* Reserve some room for CMD SETFH <HSN> <MAIO> */ char ma_buf[TRXC_BUF_SIZE - 24]; @@ -420,28 +441,28 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, int i, rc; /* Make sure that Mobile Allocation has at least one ARFCN */ - if (!ma_len || ma == NULL) { - LOGP(DTRX, LOGL_ERROR, "Mobile Allocation is empty?!?\n"); + if (!cmdp->ma_len || cmdp->ma == NULL) { + LOGPFSML(trx->fi, LOGL_ERROR, "Mobile Allocation is empty?!?\n"); return -EINVAL; } /* Compose a sequence of Rx/Tx frequencies (mobile allocation) */ - for (i = 0, ptr = ma_buf; i < ma_len; i++) { + for (i = 0, ptr = ma_buf; i < cmdp->ma_len; i++) { /* Convert ARFCN to a pair of Rx/Tx frequencies (Hz * 10) */ - rx_freq = gsm_arfcn2freq10(ma[i], 0); /* Rx: Downlink */ - tx_freq = gsm_arfcn2freq10(ma[i], 1); /* Tx: Uplink */ + rx_freq = gsm_arfcn2freq10(cmdp->ma[i], 0); /* Rx: Downlink */ + tx_freq = gsm_arfcn2freq10(cmdp->ma[i], 1); /* Tx: Uplink */ if (rx_freq == 0xffff || tx_freq == 0xffff) { - LOGP(DTRX, LOGL_ERROR, "Failed to convert ARFCN %u " + LOGPFSML(trx->fi, LOGL_ERROR, "Failed to convert ARFCN %u " "to a pair of Rx/Tx frequencies\n", - ma[i] & ~ARFCN_FLAG_MASK); + cmdp->ma[i] & ~ARFCN_FLAG_MASK); return -EINVAL; } /* Append a pair of Rx/Tx frequencies (in kHz) to the buffer */ rc = snprintf(ptr, ma_buf_len, "%u %u ", rx_freq * 100, tx_freq * 100); if (rc < 0 || rc > ma_buf_len) { /* Prevent buffer overflow */ - LOGP(DTRX, LOGL_ERROR, "Not enough room to encode " - "Mobile Allocation (N=%zu)\n", ma_len); + LOGPFSML(trx->fi, LOGL_ERROR, "Not enough room to encode " + "Mobile Allocation (N=%u)\n", cmdp->ma_len); return -ENOSPC; } @@ -453,7 +474,7 @@ int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, /* Overwrite the last space */ *(ptr - 1) = '\0'; - return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", hsn, maio, ma_buf); + return trx_ctrl_cmd(trx, 1, "SETFH", "%u %u %s", cmdp->hsn, cmdp->maio, ma_buf); } /* Get response from CTRL socket */ @@ -467,13 +488,13 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) read_len = read(ofd->fd, buf, sizeof(buf) - 1); if (read_len <= 0) { - LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); + LOGPFSML(trx->fi, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); return read_len; } buf[read_len] = '\0'; if (!!strncmp(buf, "RSP ", 4)) { - LOGP(DTRX, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf); + LOGPFSML(trx->fi, LOGL_NOTICE, "Unknown message on CTRL port: %s\n", buf); return 0; } @@ -481,15 +502,14 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) p = strchr(buf + 4, ' '); rsp_len = p ? p - buf - 4 : strlen(buf) - 4; - LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf); + LOGPFSML(trx->fi, LOGL_INFO, "Response message: '%s'\n", buf); /* Abort expire timer */ - if (osmo_timer_pending(&trx->trx_ctrl_timer)) - osmo_timer_del(&trx->trx_ctrl_timer); + osmo_timer_del(&trx->trx_ctrl_timer); /* Get command for response message */ if (llist_empty(&trx->trx_ctrl_list)) { - LOGP(DTRX, LOGL_NOTICE, "Response message without command\n"); + LOGPFSML(trx->fi, LOGL_NOTICE, "Response message without command\n"); return -EINVAL; } @@ -498,7 +518,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) /* Check if response matches command */ if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len)) { - LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, + LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, "Response message '%s' does not match command " "message '%s'\n", buf, tcm->cmd); goto rsp_error; @@ -507,7 +527,7 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) /* Check for response code */ sscanf(p + 1, "%d", &resp); if (resp) { - LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, + LOGPFSML(trx->fi, (tcm->critical) ? LOGL_FATAL : LOGL_ERROR, "Transceiver rejected TRX command with " "response: '%s'\n", buf); @@ -518,18 +538,18 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) /* Trigger state machine */ if (!strncmp(tcm->cmd + 4, "POWERON", 7)) { trx->powered_up = true; - osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_ACTIVE, 0, 0); + osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_ACTIVE, 0, 0); } else if (!strncmp(tcm->cmd + 4, "POWEROFF", 8)) { trx->powered_up = false; - osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0); } else if (!strncmp(tcm->cmd + 4, "MEASURE", 7)) trx_if_measure_rsp_cb(trx, buf + 14); else if (!strncmp(tcm->cmd + 4, "ECHO", 4)) - osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0); else - osmo_fsm_inst_state_chg(trx->fsm, trx->prev_state, 0, 0); + osmo_fsm_inst_state_chg(trx->fi, trx->prev_state, 0, 0); /* Remove command from list */ llist_del(&tcm->list); @@ -541,11 +561,53 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what) return 0; rsp_error: - /* Notify higher layers about the problem */ - osmo_fsm_inst_dispatch(trxcon_fsm, TRX_EVENT_RSP_ERROR, trx); + osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_ERROR, NULL); return -EIO; } +int trx_if_handle_phyif_cmd(struct trx_instance *trx, const struct trxcon_phyif_cmd *cmd) +{ + int rc; + + switch (cmd->type) { + case TRXCON_PHYIF_CMDT_RESET: + if ((rc = trx_if_cmd_poweroff(trx)) != 0) + return rc; + rc = trx_if_cmd_echo(trx); + break; + case TRXCON_PHYIF_CMDT_POWERON: + rc = trx_if_cmd_poweron(trx); + break; + case TRXCON_PHYIF_CMDT_POWEROFF: + rc = trx_if_cmd_poweroff(trx); + break; + case TRXCON_PHYIF_CMDT_MEASURE: + rc = trx_if_cmd_measure(trx, &cmd->param.measure); + break; + case TRXCON_PHYIF_CMDT_SETFREQ_H0: + if ((rc = trx_if_cmd_rxtune(trx, &cmd->param.setfreq_h0)) != 0) + return rc; + if ((rc = trx_if_cmd_txtune(trx, &cmd->param.setfreq_h0)) != 0) + return rc; + break; + case TRXCON_PHYIF_CMDT_SETFREQ_H1: + rc = trx_if_cmd_setfh(trx, &cmd->param.setfreq_h1); + break; + case TRXCON_PHYIF_CMDT_SETSLOT: + rc = trx_if_cmd_setslot(trx, &cmd->param.setslot); + break; + case TRXCON_PHYIF_CMDT_SETTA: + rc = trx_if_cmd_setta(trx, &cmd->param.setta); + break; + default: + LOGPFSML(trx->fi, LOGL_ERROR, + "Unhandled PHYIF command type=0x%02x\n", cmd->type); + rc = -ENODEV; + } + + return rc; +} + /* ------------------------------------------------------------------------ */ /* Data interface handlers */ /* ------------------------------------------------------------------------ */ @@ -571,66 +633,90 @@ rsp_error: static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what) { struct trx_instance *trx = ofd->data; - struct trx_meas_set meas; + struct trxcon_phyif_burst_ind bi; uint8_t buf[TRXD_BUF_SIZE]; - sbit_t bits[148]; - int8_t rssi, tn; - int16_t toa256; - uint32_t fn; ssize_t read_len; + sbit_t *burst; read_len = read(ofd->fd, buf, sizeof(buf)); if (read_len <= 0) { - LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len); + strerror_r(errno, (char *)buf, sizeof(buf)); + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, + "read() failed on TRXD with rc=%zd (%s)\n", + read_len, (const char *)buf); return read_len; } - if (read_len < (8 + 148)) { /* TRXDv0 header + GMSK burst */ - LOGP(DTRXD, LOGL_ERROR, "Got data message with invalid " - "length '%zd'\n", read_len); + if (read_len < TRXDv0_HDR_LEN) { + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, + "Got malformed TRXD PDU (short length=%zd)\n", read_len); return -EINVAL; } - tn = buf[0]; - fn = osmo_load32be(buf + 1); - rssi = -(int8_t) buf[5]; - toa256 = ((int16_t) (buf[6] << 8) | buf[7]); - - /* Copy and convert bits {254..0} to sbits {-127..127} */ - osmo_ubit2sbit(bits, buf + 8, 148); + if ((buf[0] >> 4) != 0) { + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, + "Got TRXD PDU with unexpected version\n"); + return -ENOTSUP; + } - if (tn >= 8) { - LOGP(DTRXD, LOGL_ERROR, "Illegal TS %d\n", tn); + read_len -= TRXDv0_HDR_LEN; + switch (read_len) { + /* TRXDv0 PDUs may have 2 dummy bytes at the end */ + case GSM_NBITS_NB_GMSK_BURST + 2: + case GSM_NBITS_NB_8PSK_BURST + 2: + read_len -= 2; + break; + case GSM_NBITS_NB_GMSK_BURST: + case GSM_NBITS_NB_8PSK_BURST: + break; + default: + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, + "Got TRXD PDU unexpected burst length=%zd\n", read_len); return -EINVAL; } - if (fn >= 2715648) { - LOGP(DTRXD, LOGL_ERROR, "Illegal FN %u\n", fn); + burst = (sbit_t *)&buf[8]; + + bi = (struct trxcon_phyif_burst_ind) { + .tn = buf[0] & 0x07, + .fn = osmo_load32be(buf + 1), + .rssi = -(int8_t) buf[5], + .toa256 = (int16_t) (buf[6] << 8) | buf[7], + .burst = burst, /* at least GSM_NBITS_NB_GMSK_BURST */ + .burst_len = read_len, + }; + + /* Convert ubits {254..0} to sbits {-127..127} in-place */ + for (unsigned int i = 0; i < bi.burst_len; i++) { + if (buf[8 + i] == 255) + burst[i] = -127; + else + burst[i] = 127 - buf[8 + i]; + } + + if (bi.fn >= GSM_TDMA_HYPERFRAME) { + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, "Illegal FN %u\n", bi.fn); return -EINVAL; } - LOGP(DTRXD, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%d\n", - tn, fn, rssi, toa256); + LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG, + "RX burst tn=%u fn=%u rssi=%d toa=%d\n", + bi.tn, bi.fn, bi.rssi, bi.toa256); - /* Group the measurements together */ - meas = (struct trx_meas_set) { - .toa256 = toa256, - .rssi = rssi, - .fn = fn, - }; + trxcon_phyif_handle_burst_ind(trx->priv, &bi); - /* Poke scheduler */ - sched_trx_handle_rx_burst(trx, tn, fn, bits, 148, &meas); + struct trxcon_phyif_rts_ind rts = { + .fn = GSM_TDMA_FN_SUM(bi.fn, trx->fn_advance), + .tn = bi.tn, + }; - /* Correct local clock counter */ - if (fn % 51 == 0) - sched_clck_handle(&trx->sched, fn); + trxcon_phyif_handle_rts_ind(trx->priv, &rts); return 0; } -int trx_if_tx_burst(struct trx_instance *trx, - const struct sched_burst_req *br) +int trx_if_handle_phyif_burst_req(struct trx_instance *trx, + const struct trxcon_phyif_burst_req *br) { uint8_t buf[TRXD_BUF_SIZE]; size_t length; @@ -643,15 +729,16 @@ int trx_if_tx_burst(struct trx_instance *trx, * transceiver and its TRXC interface. */ #if 0 - if (trx->fsm->state != TRX_STATE_ACTIVE) { - LOGP(DTRXD, LOGL_ERROR, "Ignoring TX data, " - "transceiver isn't ready\n"); + if (trx->fi->state != TRX_STATE_ACTIVE) { + LOGPFSMSL(trx->fi, DTRXD, LOGL_ERROR, + "Ignoring TX data, transceiver isn't ready\n"); return -EAGAIN; } #endif - LOGP(DTRXD, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", - br->tn, br->fn, br->pwr); + LOGPFSMSL(trx->fi, DTRXD, LOGL_DEBUG, + "TX burst tn=%u fn=%u pwr=%u\n", + br->tn, br->fn, br->pwr); buf[0] = br->tn; osmo_store32be(br->fn, buf + 1); @@ -671,30 +758,32 @@ int trx_if_tx_burst(struct trx_instance *trx, } /* Init TRX interface (TRXC, TRXD sockets and FSM) */ -struct trx_instance *trx_if_open(void *tall_ctx, - const char *local_host, const char *remote_host, - uint16_t base_port) +struct trx_instance *trx_if_open(const struct trx_if_params *params) { + const unsigned int offset = params->instance * 2; struct trx_instance *trx; + struct osmo_fsm_inst *fi; int rc; - LOGP(DTRX, LOGL_NOTICE, "Init transceiver interface " - "(%s:%u)\n", remote_host, base_port); + LOGPFSML(params->parent_fi, LOGL_NOTICE, + "Init transceiver interface (%s:%u/%u)\n", + params->remote_host, params->base_port, + params->instance); - /* Try to allocate memory */ - trx = talloc_zero(tall_ctx, struct trx_instance); - if (!trx) { - LOGP(DTRX, LOGL_ERROR, "Failed to allocate memory\n"); + /* Allocate a new dedicated state machine */ + fi = osmo_fsm_inst_alloc_child(&trx_fsm, params->parent_fi, + params->parent_term_event); + if (fi == NULL) { + LOGPFSML(params->parent_fi, LOGL_ERROR, + "Failed to allocate an instance of FSM '%s'\n", + trx_fsm.name); return NULL; } - /* Allocate a new dedicated state machine */ - trx->fsm = osmo_fsm_inst_alloc(&trx_fsm, trx, - NULL, LOGL_DEBUG, "trx_interface"); - if (trx->fsm == NULL) { - LOGP(DTRX, LOGL_ERROR, "Failed to allocate an instance " - "of FSM '%s'\n", trx_fsm.name); - talloc_free(trx); + trx = talloc_zero(fi, struct trx_instance); + if (!trx) { + LOGPFSML(params->parent_fi, LOGL_ERROR, "Failed to allocate memory\n"); + osmo_fsm_inst_free(fi); return NULL; } @@ -702,32 +791,40 @@ struct trx_instance *trx_if_open(void *tall_ctx, INIT_LLIST_HEAD(&trx->trx_ctrl_list); /* Open sockets */ - rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, local_host, - base_port + 101, remote_host, base_port + 1, trx_ctrl_read_cb); + rc = trx_udp_open(trx, &trx->trx_ofd_ctrl, /* TRXC */ + params->local_host, params->base_port + 101 + offset, + params->remote_host, params->base_port + 1 + offset, + trx_ctrl_read_cb); if (rc < 0) goto udp_error; - rc = trx_udp_open(trx, &trx->trx_ofd_data, local_host, - base_port + 102, remote_host, base_port + 2, trx_data_rx_cb); + rc = trx_udp_open(trx, &trx->trx_ofd_data, /* TRXD */ + params->local_host, params->base_port + 102 + offset, + params->remote_host, params->base_port + 2 + offset, + trx_data_rx_cb); if (rc < 0) goto udp_error; + trx->fn_advance = params->fn_advance; + trx->priv = params->priv; + fi->priv = trx; + trx->fi = fi; + return trx; udp_error: - LOGP(DTRX, LOGL_ERROR, "Couldn't establish UDP connection\n"); - osmo_fsm_inst_free(trx->fsm); - talloc_free(trx); + LOGPFSML(params->parent_fi, LOGL_ERROR, "Couldn't establish UDP connection\n"); + osmo_fsm_inst_free(trx->fi); return NULL; } /* Flush pending control messages */ -void trx_if_flush_ctrl(struct trx_instance *trx) +static void trx_if_flush_ctrl(struct trx_instance *trx) { struct trx_ctrl_msg *tcm; /* Reset state machine */ - osmo_fsm_inst_state_chg(trx->fsm, TRX_STATE_IDLE, 0, 0); + osmo_fsm_inst_state_chg(trx->fi, TRX_STATE_IDLE, 0, 0); /* Clear command queue */ while (!llist_empty(&trx->trx_ctrl_list)) { @@ -740,21 +837,39 @@ void trx_if_flush_ctrl(struct trx_instance *trx) void trx_if_close(struct trx_instance *trx) { + if (trx == NULL || trx->fi == NULL) + return; + osmo_fsm_inst_term(trx->fi, OSMO_FSM_TERM_REQUEST, NULL); +} + +static void trx_fsm_cleanup_cb(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause) +{ + static const char cmd_poweroff[] = "CMD POWEROFF"; + struct trx_instance *trx = fi->priv; + /* May be unallocated due to init error */ if (!trx) return; - LOGP(DTRX, LOGL_NOTICE, "Shutdown transceiver interface\n"); + LOGPFSML(fi, LOGL_NOTICE, "Shutdown transceiver interface\n"); + + /* Abort TRXC response timer (if pending) */ + osmo_timer_del(&trx->trx_ctrl_timer); /* Flush CTRL message list */ trx_if_flush_ctrl(trx); + /* Power off if the transceiver is up */ + if (trx->powered_up && trx->trx_ofd_ctrl.fd >= 0) + send(trx->trx_ofd_ctrl.fd, &cmd_poweroff[0], sizeof(cmd_poweroff), 0); + /* Close sockets */ trx_udp_close(&trx->trx_ofd_ctrl); trx_udp_close(&trx->trx_ofd_data); /* Free memory */ - osmo_fsm_inst_free(trx->fsm); + trx->fi->priv = NULL; talloc_free(trx); } diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c new file mode 100644 index 00000000..95458838 --- /dev/null +++ b/src/host/trxcon/src/trxcon_fsm.c @@ -0,0 +1,822 @@ +/* + * OsmocomBB <-> SDR connection bridge + * The trxcon state machine (see 3GPP TS 44.004, section 5.1) + * + * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/logging.h> +#include <osmocom/gsm/gsm0502.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/bb/trxcon/trxcon.h> +#include <osmocom/bb/trxcon/trxcon_fsm.h> +#include <osmocom/bb/trxcon/phyif.h> +#include <osmocom/bb/trxcon/l1ctl.h> +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1gprs.h> + +#define S(x) (1 << (x)) + +static void trxcon_allstate_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + struct trxcon_phyif_cmd phycmd = { }; + + switch (event) { + case TRXCON_EV_PHYIF_FAILURE: + trxcon->phyif = NULL; + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + break; + case TRXCON_EV_L2IF_FAILURE: + trxcon->l2if = NULL; + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + break; + case TRXCON_EV_RESET_FULL_REQ: + TALLOC_FREE(trxcon->fi_data); + if (fi->state != TRXCON_ST_RESET) + osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0); + l1sched_reset(trxcon->sched, true); + + /* Reset the L1 parameters */ + trxcon->l1p.band_arfcn = 0xffff; + trxcon->l1p.tx_power = 0; + trxcon->l1p.ta = 0; + + phycmd.type = TRXCON_PHYIF_CMDT_RESET; + trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd); + break; + case TRXCON_EV_RESET_SCHED_REQ: + l1sched_reset(trxcon->sched, false); + break; + case TRXCON_EV_SET_PHY_CONFIG_REQ: + { + const struct trxcon_param_set_phy_config_req *req = data; + + switch (req->type) { + case TRXCON_PHY_CFGT_PCHAN_COMB: + phycmd.type = TRXCON_PHYIF_CMDT_SETSLOT; + phycmd.param.setslot.tn = req->pchan_comb.tn; + phycmd.param.setslot.pchan = req->pchan_comb.pchan; + trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd); + break; + case TRXCON_PHY_CFGT_TX_PARAMS: + if (trxcon->l1p.ta != req->tx_params.timing_advance) { + phycmd.type = TRXCON_PHYIF_CMDT_SETTA; + phycmd.param.setta.ta = req->tx_params.timing_advance; + trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd); + } + trxcon->l1p.tx_power = req->tx_params.tx_power; + trxcon->l1p.ta = req->tx_params.timing_advance; + break; + } + break; + } + case TRXCON_EV_UPDATE_SACCH_CACHE_REQ: + { + const struct trxcon_param_tx_data_req *req = data; + + if (req->link_id != L1SCHED_CH_LID_SACCH) { + LOGPFSML(fi, LOGL_ERROR, "Unexpected link_id=0x%02x\n", req->link_id); + break; + } + if (req->data_len != GSM_MACBLOCK_LEN) { + LOGPFSML(fi, LOGL_ERROR, "Unexpected data length=%zu\n", req->data_len); + break; + } + + memcpy(&trxcon->sched->sacch_cache[0], req->data, req->data_len); + break; + } + default: + OSMO_ASSERT(0); + } +} + +static int trxcon_timer_cb(struct osmo_fsm_inst *fi) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (fi->state) { + case TRXCON_ST_FBSB_SEARCH: + l1ctl_tx_fbsb_fail(trxcon, trxcon->l1p.band_arfcn); + osmo_fsm_inst_state_chg(fi, TRXCON_ST_RESET, 0, 0); + return 0; + default: + OSMO_ASSERT(0); + } +} + +static void handle_full_power_scan_req(struct osmo_fsm_inst *fi, + const struct trxcon_param_full_power_scan_req *req) +{ + struct trxcon_inst *trxcon = fi->priv; + enum gsm_band band_start, band_stop; + + if (trxcon->fi_data != NULL) { + LOGPFSML(fi, LOGL_ERROR, "Full power scan is already in progress\n"); + return; + } + + /* The start and stop ARFCNs must be in the same band */ + if (gsm_arfcn2band_rc(req->band_arfcn_start, &band_start) != 0 || + gsm_arfcn2band_rc(req->band_arfcn_stop, &band_stop) != 0 || + band_start != band_stop) { + LOGPFSML(fi, LOGL_ERROR, "Full power scan request has invalid params\n"); + return; + } + + trxcon->fi_data = talloc_memdup(fi, req, sizeof(*req)); + OSMO_ASSERT(trxcon->fi_data != NULL); + + /* trxcon_st_full_power_scan_onenter() sends the initial TRXCON_PHYIF_CMDT_MEASURE */ + osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */ +} + +static void trxcon_st_reset_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (event) { + case TRXCON_EV_FBSB_SEARCH_REQ: + { + const struct trxcon_param_fbsb_search_req *req = data; + unsigned long timeout_fns, timeout_ms; + + /* Some PHYs need additional time to tune (in TDMA FNs) */ + timeout_fns = req->timeout_fns + trxcon->phy_quirks.fbsb_extend_fns; + timeout_ms = timeout_fns * GSM_TDMA_FN_DURATION_uS / 1000; + osmo_fsm_inst_state_chg_ms(fi, TRXCON_ST_FBSB_SEARCH, timeout_ms, 0); + + l1sched_configure_ts(trxcon->sched, 0, req->pchan_config); + + /* Only if current ARFCN differs */ + if (trxcon->l1p.band_arfcn != req->band_arfcn) { + const struct trxcon_phyif_cmd phycmd = { + .type = TRXCON_PHYIF_CMDT_SETFREQ_H0, + .param.setfreq_h0 = { + .band_arfcn = req->band_arfcn, + }, + }; + + /* Update current ARFCN */ + trxcon->l1p.band_arfcn = req->band_arfcn; + + /* Tune transceiver to required ARFCN */ + trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd); + } + + const struct trxcon_phyif_cmd phycmd = { .type = TRXCON_PHYIF_CMDT_POWERON }; + trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd); + break; + } + case TRXCON_EV_FULL_POWER_SCAN_REQ: + handle_full_power_scan_req(fi, (const struct trxcon_param_full_power_scan_req *)data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void trxcon_st_full_power_scan_onenter(struct osmo_fsm_inst *fi, + uint32_t prev_state) +{ + const struct trxcon_inst *trxcon = fi->priv; + const struct trxcon_param_full_power_scan_req *req = trxcon->fi_data; + + /* req->band_arfcn_start holds the current ARFCN */ + const struct trxcon_phyif_cmd phycmd = { + .type = TRXCON_PHYIF_CMDT_MEASURE, + .param.measure = { + .band_arfcn = req->band_arfcn_start, + }, + }; + + trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd); +} + +static void trxcon_st_full_power_scan_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (event) { + case TRXCON_EV_FULL_POWER_SCAN_RES: + { + struct trxcon_param_full_power_scan_req *req = trxcon->fi_data; + const struct trxcon_param_full_power_scan_res *res = data; + + if (req == NULL) { + LOGPFSML(fi, LOGL_ERROR, "Rx unexpected power scan result\n"); + break; + } + + /* req->band_arfcn_start holds the expected ARFCN */ + if (res->band_arfcn != req->band_arfcn_start) { + LOGPFSML(fi, LOGL_ERROR, "Rx power scan result " + "with unexpected ARFCN %u (expected %u)\n", + res->band_arfcn & ~ARFCN_FLAG_MASK, + req->band_arfcn_start & ~ARFCN_FLAG_MASK); + break; + } + + if (res->band_arfcn < req->band_arfcn_stop) { + l1ctl_tx_pm_conf(trxcon, res->band_arfcn, res->dbm, false); + /* trxcon_st_full_power_scan_onenter() sends the next TRXCON_PHYIF_CMDT_MEASURE */ + req->band_arfcn_start = res->band_arfcn + 1; + osmo_fsm_inst_state_chg(fi, TRXCON_ST_FULL_POWER_SCAN, 0, 0); /* TODO: timeout */ + } else { + l1ctl_tx_pm_conf(trxcon, res->band_arfcn, res->dbm, true); + LOGPFSML(fi, LOGL_INFO, "Full power scan completed\n"); + TALLOC_FREE(trxcon->fi_data); + } + break; + } + case TRXCON_EV_FULL_POWER_SCAN_REQ: + handle_full_power_scan_req(fi, (const struct trxcon_param_full_power_scan_req *)data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void trxcon_st_fbsb_search_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (event) { + case TRXCON_EV_FBSB_SEARCH_RES: + osmo_fsm_inst_state_chg(fi, TRXCON_ST_BCCH_CCCH, 0, 0); + l1ctl_tx_fbsb_conf(trxcon, trxcon->l1p.band_arfcn, trxcon->sched->bsic); + break; + default: + OSMO_ASSERT(0); + } +} + +static void handle_tx_access_burst_req(struct osmo_fsm_inst *fi, + const struct trxcon_param_tx_access_burst_req *req) +{ + struct trxcon_inst *trxcon = fi->priv; + struct l1sched_prim *prim; + struct msgb *msg; + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_RACH, PRIM_OP_REQUEST); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->rach_req = (struct l1sched_prim_rach) { + .chdr = { + .chan_nr = req->chan_nr, + .link_id = req->link_id, + }, + .synch_seq = req->synch_seq, + .offset = req->offset, + .is_11bit = req->is_11bit, + .ra = req->ra, + }; + + l1sched_prim_from_user(trxcon->sched, msg); +} + +static void handle_dch_est_req(struct osmo_fsm_inst *fi, + const struct trxcon_param_dch_est_req *req) +{ + struct trxcon_inst *trxcon = fi->priv; + enum gsm_phys_chan_config config; + struct l1sched_ts *ts; + int rc; + + config = l1sched_chan_nr2pchan_config(req->chan_nr); + if (config == GSM_PCHAN_NONE) { + LOGPFSML(fi, LOGL_ERROR, "Failed to determine channel config\n"); + return; + } + + if (req->hopping) { + const struct trxcon_phyif_cmd phycmd = { + .type = TRXCON_PHYIF_CMDT_SETFREQ_H1, + .param.setfreq_h1 = { + .hsn = req->h1.hsn, + .maio = req->h1.maio, + .ma = &req->h1.ma[0], + .ma_len = req->h1.n, + }, + }; + + /* Apply the freq. hopping parameters */ + if (trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd) != 0) + return; + + /* Set current ARFCN to an invalid value */ + trxcon->l1p.band_arfcn = 0xffff; + } else { + const struct trxcon_phyif_cmd phycmd = { + .type = TRXCON_PHYIF_CMDT_SETFREQ_H0, + .param.setfreq_h0 = { + .band_arfcn = req->h0.band_arfcn, + }, + }; + + /* Tune transceiver to required ARFCN */ + if (trxcon_phyif_handle_cmd(trxcon->phyif, &phycmd) != 0) + return; + + /* Update current ARFCN */ + trxcon->l1p.band_arfcn = req->h0.band_arfcn; + } + + /* Remove all active timeslots */ + l1sched_reset(trxcon->sched, false); + + rc = l1sched_configure_ts(trxcon->sched, req->chan_nr & 0x07, config); + if (rc) + return; + ts = trxcon->sched->ts[req->chan_nr & 0x07]; + OSMO_ASSERT(ts != NULL); + + l1sched_deactivate_all_lchans(ts); + + /* Activate only requested lchans */ + rc = l1sched_set_lchans(ts, req->chan_nr, 1, req->tch_mode, req->tsc); + if (rc) { + LOGPFSML(fi, LOGL_ERROR, "Failed to activate requested lchans\n"); + return; + } + + /* Store TSC for subsequent PDCH timeslot activation(s) */ + trxcon->l1p.tsc = req->tsc; + + if (config == GSM_PCHAN_PDCH) + osmo_fsm_inst_state_chg(fi, TRXCON_ST_PACKET_DATA, 0, 0); + else + osmo_fsm_inst_state_chg(fi, TRXCON_ST_DEDICATED, 0, 0); +} + +static void trxcon_st_bcch_ccch_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (event) { + case TRXCON_EV_TX_ACCESS_BURST_REQ: + handle_tx_access_burst_req(fi, data); + break; + case TRXCON_EV_TX_ACCESS_BURST_CNF: + l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data); + break; + case TRXCON_EV_SET_CCCH_MODE_REQ: + { + struct trxcon_param_set_ccch_tch_mode_req *req = data; + enum gsm_phys_chan_config chan_config = req->mode; + struct l1sched_ts *ts = trxcon->sched->ts[0]; + + /* Make sure that TS0 is allocated and configured */ + if (ts == NULL || ts->mf_layout == NULL) { + LOGPFSML(fi, LOGL_ERROR, "TS0 is not configured\n"); + return; + } + + /* Do nothing if the current mode matches required */ + if (ts->mf_layout->chan_config != chan_config) + l1sched_configure_ts(trxcon->sched, 0, chan_config); + req->applied = true; + break; + } + case TRXCON_EV_DCH_EST_REQ: + handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data); + break; + case TRXCON_EV_RX_DATA_IND: + l1ctl_tx_dt_ind(trxcon, (const struct trxcon_param_rx_data_ind *)data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void trxcon_st_dedicated_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (event) { + case TRXCON_EV_TX_ACCESS_BURST_REQ: + handle_tx_access_burst_req(fi, data); + break; + case TRXCON_EV_TX_ACCESS_BURST_CNF: + l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data); + break; + case TRXCON_EV_DCH_EST_REQ: + handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data); + break; + case TRXCON_EV_DCH_REL_REQ: + l1sched_reset(trxcon->sched, false); + /* TODO: switch to (not implemented) TRXCON_ST_DCH_TUNING? */ + break; + case TRXCON_EV_SET_TCH_MODE_REQ: + { + struct trxcon_param_set_ccch_tch_mode_req *req = data; + unsigned int tn; + + /* Iterate over timeslot list */ + for (tn = 0; tn < ARRAY_SIZE(trxcon->sched->ts); tn++) { + struct l1sched_ts *ts = trxcon->sched->ts[tn]; + struct l1sched_lchan_state *lchan; + + /* Timeslot is not allocated */ + if (ts == NULL || ts->mf_layout == NULL) + continue; + + /* Iterate over all allocated lchans */ + llist_for_each_entry(lchan, &ts->lchans, list) { + /* Omit inactive channels */ + if (!lchan->active) + continue; + if (req->mode == GSM48_CMODE_SPEECH_AMR) { + int rc = l1sched_lchan_set_amr_cfg(lchan, + req->amr.codecs_bitmask, + req->amr.start_codec); + if (rc) + continue; + } + lchan->tch_mode = req->mode; + req->applied = true; + } + } + break; + } + case TRXCON_EV_CRYPTO_REQ: + { + const struct trxcon_param_crypto_req *req = data; + unsigned int tn = req->chan_nr & 0x07; + struct l1sched_ts *ts; + + /* Make sure that required TS is allocated and configured */ + ts = trxcon->sched->ts[tn]; + if (ts == NULL || ts->mf_layout == NULL) { + LOGPFSML(fi, LOGL_ERROR, "TS%u is not configured\n", tn); + return; + } + + if (l1sched_start_ciphering(ts, req->a5_algo, req->key, req->key_len) != 0) { + LOGPFSML(fi, LOGL_ERROR, "Failed to configure ciphering\n"); + return; + } + break; + } + case TRXCON_EV_TX_DATA_REQ: + { + const struct trxcon_param_tx_data_req *req = data; + struct l1sched_prim *prim; + struct msgb *msg; + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->data_req = (struct l1sched_prim_chdr) { + .chan_nr = req->chan_nr, + .link_id = req->link_id, + .traffic = req->traffic, + }; + + memcpy(msgb_put(msg, req->data_len), req->data, req->data_len); + l1sched_prim_from_user(trxcon->sched, msg); + break; + } + case TRXCON_EV_TX_DATA_CNF: + l1ctl_tx_dt_conf(trxcon, (const struct trxcon_param_tx_data_cnf *)data); + break; + case TRXCON_EV_RX_DATA_IND: + l1ctl_tx_dt_ind(trxcon, (const struct trxcon_param_rx_data_ind *)data); + break; + default: + OSMO_ASSERT(0); + } +} + +static void handle_tbf_cfg_req(struct trxcon_inst *trxcon, uint8_t tn, bool active) +{ + struct l1sched_state *sched = trxcon->sched; + + if (active) { + struct l1sched_lchan_state *lchan; + struct l1sched_ts *ts; + + if (sched->ts[tn] != NULL) /* already enabled */ + return; + if (l1sched_configure_ts(sched, tn, GSM_PCHAN_PDCH) != 0) + return; + OSMO_ASSERT(sched->ts[tn] != NULL); + ts = sched->ts[tn]; + + l1sched_activate_lchan(ts, L1SCHED_PDTCH); + l1sched_activate_lchan(ts, L1SCHED_PTCCH); + llist_for_each_entry(lchan, &ts->lchans, list) + lchan->tsc = trxcon->l1p.tsc; + } else { + l1sched_del_ts(sched, tn); + } +} + +static void trxcon_l1gprs_state_changed_cb(struct l1gprs_pdch *pdch, bool active) +{ + handle_tbf_cfg_req(pdch->gprs->priv, pdch->tn, active); +} + +static void trxcon_st_packet_data_onenter(struct osmo_fsm_inst *fi, + uint32_t prev_state) +{ + struct trxcon_inst *trxcon = fi->priv; + + OSMO_ASSERT(trxcon->gprs == NULL); + trxcon->gprs = l1gprs_state_alloc(trxcon, trxcon->log_prefix, trxcon); + l1gprs_state_set_pdch_changed_cb(trxcon->gprs, trxcon_l1gprs_state_changed_cb); + OSMO_ASSERT(trxcon->gprs != NULL); +} + +static void trxcon_st_packet_data_onleave(struct osmo_fsm_inst *fi, + uint32_t next_state) +{ + struct trxcon_inst *trxcon = fi->priv; + + l1gprs_state_free(trxcon->gprs); + trxcon->gprs = NULL; +} + +static void trxcon_st_packet_data_action(struct osmo_fsm_inst *fi, + uint32_t event, void *data) +{ + struct trxcon_inst *trxcon = fi->priv; + + switch (event) { + case TRXCON_EV_TX_ACCESS_BURST_REQ: + handle_tx_access_burst_req(fi, data); + break; + case TRXCON_EV_TX_ACCESS_BURST_CNF: + l1ctl_tx_rach_conf(trxcon, (const struct trxcon_param_tx_access_burst_cnf *)data); + break; + case TRXCON_EV_GPRS_UL_TBF_CFG_REQ: + l1gprs_handle_ul_tbf_cfg_req(trxcon->gprs, (struct msgb *)data); + break; + case TRXCON_EV_GPRS_DL_TBF_CFG_REQ: + l1gprs_handle_dl_tbf_cfg_req(trxcon->gprs, (struct msgb *)data); + break; + case TRXCON_EV_GPRS_UL_BLOCK_REQ: + { + struct l1gprs_prim_ul_block_req block_req; + struct l1sched_prim *prim; + struct msgb *msg = data; + + if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0) + return; + + msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST); + OSMO_ASSERT(msg != NULL); + + prim = l1sched_prim_from_msgb(msg); + prim->data_req = (struct l1sched_prim_chdr) { + .frame_nr = block_req.hdr.fn, + .chan_nr = RSL_CHAN_OSMO_PDCH | block_req.hdr.tn, + .link_id = 0x00, + }; + + memcpy(msgb_put(msg, block_req.data_len), block_req.data, block_req.data_len); + l1sched_prim_from_user(trxcon->sched, msg); + break; + } + case TRXCON_EV_TX_DATA_CNF: + { + const struct trxcon_param_tx_data_cnf *cnf = data; + struct msgb *msg; + + msg = l1gprs_handle_ul_block_cnf(trxcon->gprs, + cnf->frame_nr, cnf->chan_nr & 0x07, + cnf->data, cnf->data_len); + if (msg != NULL) + trxcon_l1ctl_send(trxcon, msg); + break; + } + case TRXCON_EV_RX_DATA_IND: + { + const struct trxcon_param_rx_data_ind *ind = data; + struct l1gprs_prim_dl_block_ind block_ind; + struct msgb *msg; + uint8_t usf = 0xff; + + block_ind = (struct l1gprs_prim_dl_block_ind) { + .hdr = { + .fn = ind->frame_nr, + .tn = ind->chan_nr & 0x07, + }, + .meas = { + /* .ber10k is set below */ + .ci_cb = 180, /* 18 dB */ + .rx_lev = dbm2rxlev(ind->rssi), + }, + .data_len = ind->data_len, + .data = ind->data, + }; + + if (ind->n_bits_total == 0) + block_ind.meas.ber10k = 10000; + else + block_ind.meas.ber10k = 10000 * ind->n_errors / ind->n_bits_total; + + msg = l1gprs_handle_dl_block_ind(trxcon->gprs, &block_ind, &usf); + if (msg != NULL) + trxcon_l1ctl_send(trxcon, msg); + /* Every fn % 13 == 12 we have either a PTCCH or an IDLE slot, thus + * every fn % 13 == 8 we add 5 frames, or 4 frames othrwise. The + * resulting value is first fn of the next block. */ + const uint32_t rts_fn = GSM_TDMA_FN_SUM(ind->frame_nr, (ind->frame_nr % 13 == 8) ? 5 : 4); + msg = l1gprs_handle_rts_ind(trxcon->gprs, rts_fn, ind->chan_nr & 0x07, usf); + if (msg != NULL) + trxcon_l1ctl_send(trxcon, msg); + break; + } + case TRXCON_EV_DCH_EST_REQ: + handle_dch_est_req(fi, (const struct trxcon_param_dch_est_req *)data); + break; + case TRXCON_EV_DCH_REL_REQ: + l1sched_reset(trxcon->sched, false); + /* TODO: switch to (not implemented) TRXCON_ST_DCH_TUNING? */ + break; + default: + OSMO_ASSERT(0); + } +} + +static void trxcon_fsm_pre_term_cb(struct osmo_fsm_inst *fi, + enum osmo_fsm_term_cause cause) +{ + struct trxcon_inst *trxcon = fi->priv; + + if (trxcon == NULL) + return; + + /* Shutdown the scheduler */ + if (trxcon->sched != NULL) + l1sched_free(trxcon->sched); + /* Clean up GPRS L1 state */ + l1gprs_state_free(trxcon->gprs); + + /* Close active connections */ + if (trxcon->l2if != NULL) + trxcon_l1ctl_close(trxcon); + if (trxcon->phyif != NULL) + trxcon_phyif_close(trxcon->phyif); + + talloc_free(trxcon); + fi->priv = NULL; +} + +static const struct osmo_fsm_state trxcon_fsm_states[] = { + [TRXCON_ST_RESET] = { + .name = "RESET", + .out_state_mask = S(TRXCON_ST_FBSB_SEARCH) + | S(TRXCON_ST_FULL_POWER_SCAN), + .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_REQ) + | S(TRXCON_EV_FULL_POWER_SCAN_REQ), + .action = &trxcon_st_reset_action, + }, + [TRXCON_ST_FULL_POWER_SCAN] = { + .name = "FULL_POWER_SCAN", + .out_state_mask = S(TRXCON_ST_RESET) + | S(TRXCON_ST_FULL_POWER_SCAN), + .in_event_mask = S(TRXCON_EV_FULL_POWER_SCAN_RES) + | S(TRXCON_EV_FULL_POWER_SCAN_REQ), + .onenter = &trxcon_st_full_power_scan_onenter, + .action = &trxcon_st_full_power_scan_action, + }, + [TRXCON_ST_FBSB_SEARCH] = { + .name = "FBSB_SEARCH", + .out_state_mask = S(TRXCON_ST_RESET) + | S(TRXCON_ST_BCCH_CCCH), + .in_event_mask = S(TRXCON_EV_FBSB_SEARCH_RES), + .action = &trxcon_st_fbsb_search_action, + }, + [TRXCON_ST_BCCH_CCCH] = { + .name = "BCCH_CCCH", + .out_state_mask = S(TRXCON_ST_RESET) + | S(TRXCON_ST_FBSB_SEARCH) + | S(TRXCON_ST_DEDICATED) + | S(TRXCON_ST_PACKET_DATA), + .in_event_mask = S(TRXCON_EV_RX_DATA_IND) + | S(TRXCON_EV_SET_CCCH_MODE_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_CNF) + | S(TRXCON_EV_DCH_EST_REQ), + .action = &trxcon_st_bcch_ccch_action, + }, + [TRXCON_ST_DEDICATED] = { + .name = "DEDICATED", + .out_state_mask = S(TRXCON_ST_RESET) + | S(TRXCON_ST_FBSB_SEARCH) + | S(TRXCON_ST_DEDICATED) + | S(TRXCON_ST_PACKET_DATA), + .in_event_mask = S(TRXCON_EV_DCH_REL_REQ) + | S(TRXCON_EV_DCH_EST_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_CNF) + | S(TRXCON_EV_SET_TCH_MODE_REQ) + | S(TRXCON_EV_TX_DATA_REQ) + | S(TRXCON_EV_TX_DATA_CNF) + | S(TRXCON_EV_RX_DATA_IND) + | S(TRXCON_EV_CRYPTO_REQ), + .action = &trxcon_st_dedicated_action, + }, + [TRXCON_ST_PACKET_DATA] = { + .name = "PACKET_DATA", + .out_state_mask = S(TRXCON_ST_RESET) + | S(TRXCON_ST_FBSB_SEARCH) + | S(TRXCON_ST_DEDICATED) + | S(TRXCON_ST_PACKET_DATA), + .in_event_mask = S(TRXCON_EV_DCH_REL_REQ) + | S(TRXCON_EV_DCH_EST_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_REQ) + | S(TRXCON_EV_TX_ACCESS_BURST_CNF) + | S(TRXCON_EV_GPRS_UL_TBF_CFG_REQ) + | S(TRXCON_EV_GPRS_DL_TBF_CFG_REQ) + | S(TRXCON_EV_GPRS_UL_BLOCK_REQ) + | S(TRXCON_EV_RX_DATA_IND) + | S(TRXCON_EV_TX_DATA_CNF), + .onenter = &trxcon_st_packet_data_onenter, + .onleave = &trxcon_st_packet_data_onleave, + .action = &trxcon_st_packet_data_action, + }, +}; + +static const struct value_string trxcon_fsm_event_names[] = { + OSMO_VALUE_STRING(TRXCON_EV_PHYIF_FAILURE), + OSMO_VALUE_STRING(TRXCON_EV_L2IF_FAILURE), + OSMO_VALUE_STRING(TRXCON_EV_RESET_FULL_REQ), + OSMO_VALUE_STRING(TRXCON_EV_RESET_SCHED_REQ), + OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_REQ), + OSMO_VALUE_STRING(TRXCON_EV_FULL_POWER_SCAN_RES), + OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_REQ), + OSMO_VALUE_STRING(TRXCON_EV_FBSB_SEARCH_RES), + OSMO_VALUE_STRING(TRXCON_EV_SET_CCCH_MODE_REQ), + OSMO_VALUE_STRING(TRXCON_EV_SET_TCH_MODE_REQ), + OSMO_VALUE_STRING(TRXCON_EV_SET_PHY_CONFIG_REQ), + OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_REQ), + OSMO_VALUE_STRING(TRXCON_EV_TX_ACCESS_BURST_CNF), + OSMO_VALUE_STRING(TRXCON_EV_UPDATE_SACCH_CACHE_REQ), + OSMO_VALUE_STRING(TRXCON_EV_DCH_EST_REQ), + OSMO_VALUE_STRING(TRXCON_EV_DCH_REL_REQ), + OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_REQ), + OSMO_VALUE_STRING(TRXCON_EV_TX_DATA_CNF), + OSMO_VALUE_STRING(TRXCON_EV_RX_DATA_IND), + OSMO_VALUE_STRING(TRXCON_EV_CRYPTO_REQ), + OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_TBF_CFG_REQ), + OSMO_VALUE_STRING(TRXCON_EV_GPRS_DL_TBF_CFG_REQ), + OSMO_VALUE_STRING(TRXCON_EV_GPRS_UL_BLOCK_REQ), + { 0, NULL } +}; + +struct osmo_fsm trxcon_fsm_def = { + .name = "trxcon", + .states = trxcon_fsm_states, + .num_states = ARRAY_SIZE(trxcon_fsm_states), + .log_subsys = DLGLOBAL, + .event_names = trxcon_fsm_event_names, + .allstate_event_mask = S(TRXCON_EV_PHYIF_FAILURE) + | S(TRXCON_EV_L2IF_FAILURE) + | S(TRXCON_EV_RESET_FULL_REQ) + | S(TRXCON_EV_RESET_SCHED_REQ) + | S(TRXCON_EV_SET_PHY_CONFIG_REQ) + | S(TRXCON_EV_UPDATE_SACCH_CACHE_REQ), + .allstate_action = &trxcon_allstate_action, + .timer_cb = &trxcon_timer_cb, + .pre_term = &trxcon_fsm_pre_term_cb, +}; + +static __attribute__((constructor)) void on_dso_load(void) +{ + OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0); +} diff --git a/src/host/trxcon/src/trxcon_inst.c b/src/host/trxcon/src/trxcon_inst.c new file mode 100644 index 00000000..b7ff5810 --- /dev/null +++ b/src/host/trxcon/src/trxcon_inst.c @@ -0,0 +1,107 @@ +/* + * OsmocomBB <-> SDR connection bridge + * + * (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/bb/trxcon/trxcon.h> +#include <osmocom/bb/trxcon/trxcon_fsm.h> +#include <osmocom/bb/l1sched/l1sched.h> +#include <osmocom/bb/l1sched/logging.h> +#include <osmocom/bb/l1gprs.h> + +extern int g_logc_l1c; +extern int g_logc_l1d; + +void trxcon_set_log_cfg(const int *logc, unsigned int logc_num) +{ + int schc = DLGLOBAL; + int schd = DLGLOBAL; + + for (unsigned int i = 0; i < logc_num; i++) { + switch ((enum trxcon_log_cat)i) { + case TRXCON_LOGC_FSM: + trxcon_fsm_def.log_subsys = logc[i]; + break; + case TRXCON_LOGC_L1C: + g_logc_l1c = logc[i]; + break; + case TRXCON_LOGC_L1D: + g_logc_l1d = logc[i]; + break; + case TRXCON_LOGC_SCHC: + schc = logc[i]; + break; + case TRXCON_LOGC_SCHD: + schd = logc[i]; + break; + case TRXCON_LOGC_GPRS: + l1gprs_logging_init(logc[i]); + break; + } + } + + l1sched_logging_init(schc, schd); +} + +struct trxcon_inst *trxcon_inst_alloc(void *ctx, unsigned int id) +{ + struct trxcon_inst *trxcon; + struct osmo_fsm_inst *fi; + + fi = osmo_fsm_inst_alloc(&trxcon_fsm_def, ctx, NULL, LOGL_DEBUG, NULL); + OSMO_ASSERT(fi != NULL); + + trxcon = talloc_zero(fi, struct trxcon_inst); + OSMO_ASSERT(trxcon != NULL); + + fi->priv = trxcon; + trxcon->fi = fi; + + osmo_fsm_inst_update_id_f(fi, "%u", id); + trxcon->id = id; + + /* Logging context to be used by both l1ctl and l1sched modules */ + trxcon->log_prefix = talloc_asprintf(trxcon, "%s: ", osmo_fsm_inst_name(fi)); + + /* Init scheduler */ + const struct l1sched_cfg sched_cfg = { + .log_prefix = trxcon->log_prefix, + }; + + trxcon->sched = l1sched_alloc(trxcon, &sched_cfg, trxcon); + if (trxcon->sched == NULL) { + trxcon_inst_free(trxcon); + return NULL; + } + + trxcon->phy_quirks.fbsb_extend_fns = 0; + + return trxcon; +} + +void trxcon_inst_free(struct trxcon_inst *trxcon) +{ + if (trxcon == NULL || trxcon->fi == NULL) + return; + osmo_fsm_inst_term(trxcon->fi, OSMO_FSM_TERM_REQUEST, NULL); +} diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/src/trxcon_main.c index 158e27f7..3901e336 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/src/trxcon_main.c @@ -1,7 +1,8 @@ /* * OsmocomBB <-> SDR connection bridge * - * (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com> + * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com> + * Contributions by sysmocom - s.f.m.c. GmbH * * All Rights Reserved * @@ -15,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> @@ -28,10 +25,9 @@ #include <getopt.h> #include <unistd.h> #include <signal.h> +#include <errno.h> #include <time.h> -#include <arpa/inet.h> - #include <osmocom/core/fsm.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> @@ -41,19 +37,16 @@ #include <osmocom/core/gsmtap_util.h> #include <osmocom/core/gsmtap.h> -#include <osmocom/gsm/gsm_utils.h> - -#include "trxcon.h" -#include "trx_if.h" -#include "logging.h" -#include "l1ctl.h" -#include "l1ctl_link.h" -#include "l1ctl_proto.h" -#include "scheduler.h" -#include "sched_trx.h" +#include <osmocom/bb/trxcon/trxcon.h> +#include <osmocom/bb/trxcon/trxcon_fsm.h> +#include <osmocom/bb/trxcon/phyif.h> +#include <osmocom/bb/trxcon/trx_if.h> +#include <osmocom/bb/trxcon/logging.h> +#include <osmocom/bb/trxcon/l1ctl_server.h> #define COPYRIGHT \ - "Copyright (C) 2016-2020 by Vadim Yanitskiy <axilirator@gmail.com>\n" \ + "Copyright (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>\n" \ + "Contributions by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>\n" \ "License GPLv2+: GNU GPL version 2 or later " \ "<http://gnu.org/licenses/gpl.html>\n" \ "This is free software: you are free to change and redistribute it.\n" \ @@ -65,87 +58,118 @@ static struct { int quit; /* L1CTL specific */ - struct l1ctl_link *l1l; + unsigned int max_clients; const char *bind_socket; /* TRX specific */ - struct trx_instance *trx; const char *trx_bind_ip; const char *trx_remote_ip; uint16_t trx_base_port; uint32_t trx_fn_advance; + + /* PHY quirk: FBSB timeout extension (in TDMA FNs) */ + unsigned int phyq_fbsb_extend_fns; + + /* GSMTAP specific */ + struct gsmtap_inst *gsmtap; const char *gsmtap_ip; -} app_data; +} app_data = { + .max_clients = 1, /* only one L1CTL client by default */ + .bind_socket = "/tmp/osmocom_l2", + .trx_remote_ip = "127.0.0.1", + .trx_bind_ip = "0.0.0.0", + .trx_base_port = 6700, + .trx_fn_advance = 0, + .phyq_fbsb_extend_fns = 0, +}; static void *tall_trxcon_ctx = NULL; -struct gsmtap_inst *gsmtap = NULL; -struct osmo_fsm_inst *trxcon_fsm; -static void trxcon_fsm_idle_action(struct osmo_fsm_inst *fi, - uint32_t event, void *data) +int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br) { - if (event == L1CTL_EVENT_CONNECT) - osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_MANAGED, 0, 0); + return trx_if_handle_phyif_burst_req(phyif, br); } -static void trxcon_fsm_managed_action(struct osmo_fsm_inst *fi, - uint32_t event, void *data) +int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd) { - switch (event) { - case L1CTL_EVENT_DISCONNECT: - osmo_fsm_inst_state_chg(trxcon_fsm, TRXCON_STATE_IDLE, 0, 0); + return trx_if_handle_phyif_cmd(phyif, cmd); +} - if (app_data.trx->fsm->state != TRX_STATE_OFFLINE) { - /* Reset scheduler and clock counter */ - sched_trx_reset(app_data.trx, true); +void trxcon_phyif_close(void *phyif) +{ + trx_if_close(phyif); +} - /* TODO: implement trx_if_reset() */ - trx_if_cmd_poweroff(app_data.trx); - trx_if_cmd_echo(app_data.trx); - } - break; - case TRX_EVENT_RSP_ERROR: - case TRX_EVENT_OFFLINE: - /* TODO: notify L2 & L3 about that */ - break; - default: - LOGPFSML(fi, LOGL_ERROR, "Unhandled event %u\n", event); +void trxcon_l1ctl_close(struct trxcon_inst *trxcon) +{ + /* Avoid use-after-free: both *fi and *trxcon are children of + * the L2IF (L1CTL connection), so we need to re-parent *fi + * to NULL before calling l1ctl_client_conn_close(). */ + talloc_steal(NULL, trxcon->fi); + l1ctl_client_conn_close(trxcon->l2if); +} + +int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg) +{ + struct l1ctl_client *l1c = trxcon->l2if; + + return l1ctl_client_send(l1c, msg); +} + +static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg) +{ + struct trxcon_inst *trxcon = l1c->priv; + + return trxcon_l1ctl_receive(trxcon, msg); +} + +static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c) +{ + struct trxcon_inst *trxcon; + + trxcon = trxcon_inst_alloc(l1c, l1c->id); + if (trxcon == NULL) { + l1ctl_client_conn_close(l1c); + return; + } + + l1c->log_prefix = talloc_strdup(l1c, trxcon->log_prefix); + l1c->priv = trxcon; + trxcon->l2if = l1c; + + const struct trx_if_params trxcon_phyif_params = { + .local_host = app_data.trx_bind_ip, + .remote_host = app_data.trx_remote_ip, + .base_port = app_data.trx_base_port, + .fn_advance = app_data.trx_fn_advance, + .instance = trxcon->id, + + .parent_fi = trxcon->fi, + .parent_term_event = TRXCON_EV_PHYIF_FAILURE, + .priv = trxcon, + }; + + /* Init transceiver interface */ + trxcon->phyif = trx_if_open(&trxcon_phyif_params); + if (trxcon->phyif == NULL) { + /* TRXCON_EV_PHYIF_FAILURE triggers l1ctl_client_conn_close() */ + osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_PHYIF_FAILURE, NULL); + return; } + + trxcon->gsmtap = app_data.gsmtap; + trxcon->phy_quirks.fbsb_extend_fns = app_data.phyq_fbsb_extend_fns; } -static struct osmo_fsm_state trxcon_fsm_states[] = { - [TRXCON_STATE_IDLE] = { - .in_event_mask = GEN_MASK(L1CTL_EVENT_CONNECT), - .out_state_mask = GEN_MASK(TRXCON_STATE_MANAGED), - .name = "IDLE", - .action = trxcon_fsm_idle_action, - }, - [TRXCON_STATE_MANAGED] = { - .in_event_mask = ( - GEN_MASK(L1CTL_EVENT_DISCONNECT) | - GEN_MASK(TRX_EVENT_RSP_ERROR) | - GEN_MASK(TRX_EVENT_OFFLINE)), - .out_state_mask = GEN_MASK(TRXCON_STATE_IDLE), - .name = "MANAGED", - .action = trxcon_fsm_managed_action, - }, -}; +static void l1ctl_conn_close_cb(struct l1ctl_client *l1c) +{ + struct trxcon_inst *trxcon = l1c->priv; -static const struct value_string app_evt_names[] = { - OSMO_VALUE_STRING(L1CTL_EVENT_CONNECT), - OSMO_VALUE_STRING(L1CTL_EVENT_DISCONNECT), - OSMO_VALUE_STRING(TRX_EVENT_OFFLINE), - OSMO_VALUE_STRING(TRX_EVENT_RSP_ERROR), - { 0, NULL } -}; + if (trxcon == NULL || trxcon->fi == NULL) + return; -static struct osmo_fsm trxcon_fsm_def = { - .name = "trxcon_app_fsm", - .states = trxcon_fsm_states, - .num_states = ARRAY_SIZE(trxcon_fsm_states), - .log_subsys = DAPP, - .event_names = app_evt_names, -}; + osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL); +} static void print_usage(const char *app) { @@ -156,19 +180,22 @@ static void print_help(void) { printf(" Some help...\n"); printf(" -h --help this text\n"); - printf(" -d --debug Change debug flags. Default: %s\n", DEBUG_DEFAULT); + printf(" -d --debug Change debug flags (e.g. DL1C:DSCH)\n"); printf(" -b --trx-bind TRX bind IP address (default 0.0.0.0)\n"); printf(" -i --trx-remote TRX remote IP address (default 127.0.0.1)\n"); printf(" -p --trx-port Base port of TRX instance (default 6700)\n"); - printf(" -f --trx-advance Uplink burst scheduling advance (default 3)\n"); + printf(" -f --trx-advance Uplink burst scheduling advance (default 0)\n"); + printf(" -F --fbsb-extend FBSB timeout extension (in TDMA FNs, default 0)\n"); printf(" -s --socket Listening socket for layer23 (default /tmp/osmocom_l2)\n"); printf(" -g --gsmtap-ip The destination IP used for GSMTAP (disabled by default)\n"); + printf(" -C --max-clients Maximum number of L1CTL connections (default 1)\n"); printf(" -D --daemonize Run as daemon\n"); } static void handle_options(int argc, char **argv) { while (1) { + char *endptr = NULL; int option_index = 0, c; static struct option long_options[] = { {"help", 0, 0, 'h'}, @@ -181,16 +208,20 @@ static void handle_options(int argc, char **argv) {"trx-remote", 1, 0, 'i'}, {"trx-port", 1, 0, 'p'}, {"trx-advance", 1, 0, 'f'}, + {"fbsb-extend", 1, 0, 'F'}, {"gsmtap-ip", 1, 0, 'g'}, + {"max-clients", 1, 0, 'C'}, {"daemonize", 0, 0, 'D'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "d:b:i:p:f:s:g:Dh", + c = getopt_long(argc, argv, "d:b:i:p:f:F:s:g:C:Dh", long_options, &option_index); if (c == -1) break; + errno = 0; + switch (c) { case 'h': print_usage(argv[0]); @@ -207,10 +238,25 @@ static void handle_options(int argc, char **argv) app_data.trx_remote_ip = optarg; break; case 'p': - app_data.trx_base_port = atoi(optarg); + app_data.trx_base_port = strtoul(optarg, &endptr, 10); + if (errno || *endptr != '\0') { + fprintf(stderr, "Failed to parse -p/--trx-port=%s\n", optarg); + exit(EXIT_FAILURE); + } break; case 'f': - app_data.trx_fn_advance = atoi(optarg); + app_data.trx_fn_advance = strtoul(optarg, &endptr, 10); + if (errno || *endptr != '\0') { + fprintf(stderr, "Failed to parse -f/--trx-advance=%s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'F': + app_data.phyq_fbsb_extend_fns = strtoul(optarg, &endptr, 10); + if (errno || *endptr != '\0') { + fprintf(stderr, "Failed to parse -F/--fbsb-extend=%s\n", optarg); + exit(EXIT_FAILURE); + } break; case 's': app_data.bind_socket = optarg; @@ -218,6 +264,13 @@ static void handle_options(int argc, char **argv) case 'g': app_data.gsmtap_ip = optarg; break; + case 'C': + app_data.max_clients = strtoul(optarg, &endptr, 10); + if (errno || *endptr != '\0') { + fprintf(stderr, "Failed to parse -C/--max-clients=%s\n", optarg); + exit(EXIT_FAILURE); + } + break; case 'D': app_data.daemonize = 1; break; @@ -227,26 +280,13 @@ static void handle_options(int argc, char **argv) } } -static void init_defaults(void) -{ - app_data.bind_socket = "/tmp/osmocom_l2"; - app_data.trx_remote_ip = "127.0.0.1"; - app_data.trx_bind_ip = "0.0.0.0"; - app_data.trx_base_port = 6700; - app_data.trx_fn_advance = 3; - - app_data.debug_mask = NULL; - app_data.gsmtap_ip = NULL; - app_data.daemonize = 0; - app_data.quit = 0; -} - static void signal_handler(int signum) { fprintf(stderr, "signal %u received\n", signum); switch (signum) { case SIGINT: + case SIGTERM: app_data.quit++; break; case SIGABRT: @@ -271,10 +311,11 @@ static void signal_handler(int signum) int main(int argc, char **argv) { + struct l1ctl_server_cfg server_cfg; + struct l1ctl_server *server = NULL; int rc = 0; printf("%s", COPYRIGHT); - init_defaults(); handle_options(argc, argv); /* Track the use of talloc NULL memory contexts */ @@ -286,13 +327,14 @@ int main(int argc, char **argv) /* Setup signal handlers */ signal(SIGINT, &signal_handler); + signal(SIGTERM, &signal_handler); signal(SIGABRT, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); osmo_init_ignore_signals(); /* Init logging system */ - trx_log_init(tall_trxcon_ctx, app_data.debug_mask); + trxcon_logging_init(tall_trxcon_ctx, app_data.debug_mask); /* Configure pretty logging */ log_set_print_extended_timestamp(osmo_stderr_target, 1); @@ -300,43 +342,48 @@ int main(int argc, char **argv) log_set_print_category(osmo_stderr_target, 1); log_set_print_level(osmo_stderr_target, 1); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME); + log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END); + + osmo_fsm_log_timeouts(true); + /* Optional GSMTAP */ if (app_data.gsmtap_ip != NULL) { - gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1); - if (!gsmtap) { - LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP\n"); + struct log_target *lt; + + app_data.gsmtap = gsmtap_source_init(app_data.gsmtap_ip, GSMTAP_UDP_PORT, 1); + if (!app_data.gsmtap) { + LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP Um logging\n"); goto exit; } - /* Suppress ICMP "destination unreachable" errors */ - gsmtap_source_add_sink(gsmtap); - } - /* Allocate the application state machine */ - OSMO_ASSERT(osmo_fsm_register(&trxcon_fsm_def) == 0); - trxcon_fsm = osmo_fsm_inst_alloc(&trxcon_fsm_def, tall_trxcon_ctx, - NULL, LOGL_DEBUG, "main"); - - /* Init L1CTL server */ - app_data.l1l = l1ctl_link_init(tall_trxcon_ctx, - app_data.bind_socket); - if (app_data.l1l == NULL) - goto exit; - - /* Init transceiver interface */ - app_data.trx = trx_if_open(tall_trxcon_ctx, - app_data.trx_bind_ip, app_data.trx_remote_ip, - app_data.trx_base_port); - if (!app_data.trx) - goto exit; + lt = log_target_create_gsmtap(app_data.gsmtap_ip, GSMTAP_UDP_PORT, + "trxcon", false, false); + if (lt == NULL) { + LOGP(DAPP, LOGL_ERROR, "Failed to init GSMTAP logging target\n"); + goto exit; + } else { + log_add_target(lt); + } - /* Bind L1CTL with TRX and vice versa */ - app_data.l1l->trx = app_data.trx; - app_data.trx->l1l = app_data.l1l; + /* Suppress ICMP "destination unreachable" errors */ + gsmtap_source_add_sink(app_data.gsmtap); + } - /* Init scheduler */ - rc = sched_trx_init(app_data.trx, app_data.trx_fn_advance); - if (rc) + /* Start the L1CTL server */ + server_cfg = (struct l1ctl_server_cfg) { + .sock_path = app_data.bind_socket, + .num_clients_max = app_data.max_clients, + .conn_read_cb = &l1ctl_rx_cb, + .conn_accept_cb = &l1ctl_conn_accept_cb, + .conn_close_cb = &l1ctl_conn_close_cb, + }; + + server = l1ctl_server_alloc(tall_trxcon_ctx, &server_cfg); + if (server == NULL) { + rc = EXIT_FAILURE; goto exit; + } LOGP(DAPP, LOGL_NOTICE, "Init complete\n"); @@ -355,13 +402,8 @@ int main(int argc, char **argv) osmo_select_main(0); exit: - /* Close active connections */ - l1ctl_link_shutdown(app_data.l1l); - sched_trx_shutdown(app_data.trx); - trx_if_close(app_data.trx); - - /* Shutdown main state machine */ - osmo_fsm_inst_free(trxcon_fsm); + if (server != NULL) + l1ctl_server_free(server); /* Deinitialize logging */ log_fini(); diff --git a/src/host/trxcon/src/trxcon_shim.c b/src/host/trxcon/src/trxcon_shim.c new file mode 100644 index 00000000..ed2d402e --- /dev/null +++ b/src/host/trxcon/src/trxcon_shim.c @@ -0,0 +1,262 @@ +/* + * OsmocomBB <-> SDR connection bridge + * + * (C) 2022-2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> +#include <errno.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/gsmtap_util.h> +#include <osmocom/core/gsmtap.h> +#include <osmocom/gsm/rsl.h> + +#include <osmocom/bb/trxcon/trxcon.h> +#include <osmocom/bb/trxcon/trxcon_fsm.h> +#include <osmocom/bb/trxcon/phyif.h> +#include <osmocom/bb/l1sched/l1sched.h> + +static void trxcon_gsmtap_send(struct trxcon_inst *trxcon, + const struct l1sched_prim_chdr *chdr, + const uint8_t *data, size_t data_len, + int8_t signal_dbm, uint8_t snr, bool uplink) +{ + uint16_t band_arfcn = trxcon->l1p.band_arfcn; + uint8_t chan_type, ss, tn; + + if (uplink) + band_arfcn |= ARFCN_UPLINK; + if (rsl_dec_chan_nr(chdr->chan_nr, &chan_type, &ss, &tn) != 0) + return; + chan_type = chantype_rsl2gsmtap2(chan_type, chdr->link_id, chdr->traffic); + + gsmtap_send(trxcon->gsmtap, band_arfcn, tn, chan_type, ss, + chdr->frame_nr, signal_dbm, snr, + data, data_len); +} + +/* External L1 API for the scheduler */ +int l1sched_handle_burst_req(struct l1sched_state *sched, + const struct l1sched_burst_req *br) +{ + struct trxcon_inst *trxcon = sched->priv; + const struct trxcon_phyif_burst_req phybr = { + .fn = br->fn, + .tn = br->tn, + .pwr = br->pwr, + .burst = &br->burst[0], + .burst_len = br->burst_len, + }; + + return trxcon_phyif_handle_burst_req(trxcon->phyif, &phybr); +} + +/* External L2 API for the scheduler */ +static int handle_prim_data_ind(struct trxcon_inst *trxcon, struct msgb *msg) +{ + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + struct trxcon_param_rx_data_ind ind = { + .traffic = prim->data_ind.chdr.traffic, + .chan_nr = prim->data_ind.chdr.chan_nr, + .link_id = prim->data_ind.chdr.link_id, + .band_arfcn = trxcon->l1p.band_arfcn, + .frame_nr = prim->data_ind.chdr.frame_nr, + .toa256 = prim->data_ind.toa256, + .rssi = prim->data_ind.rssi, + .n_errors = prim->data_ind.n_errors, + .n_bits_total = prim->data_ind.n_bits_total, + .data_len = msgb_l2len(msg), + .data = msgb_l2(msg), + }; + + if (trxcon->gsmtap != NULL && ind.data_len > 0) { + trxcon_gsmtap_send(trxcon, &prim->data_ind.chdr, + ind.data, ind.data_len, + ind.rssi, 0, false); + } + + return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_RX_DATA_IND, &ind); +} + +static int handle_prim_data_cnf(struct trxcon_inst *trxcon, struct msgb *msg) +{ + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + struct trxcon_param_tx_data_cnf cnf = { + .traffic = prim->data_cnf.traffic, + .chan_nr = prim->data_cnf.chan_nr, + .link_id = prim->data_cnf.link_id, + .band_arfcn = trxcon->l1p.band_arfcn, + .frame_nr = prim->data_cnf.frame_nr, + .data_len = msgb_l2len(msg), + .data = msgb_l2(msg), + }; + + if (trxcon->gsmtap != NULL) { + trxcon_gsmtap_send(trxcon, &prim->data_cnf, + msgb_l2(msg), msgb_l2len(msg), + 0, 0, true); + } + + return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_DATA_CNF, &cnf); +} + +static int handle_prim_rach_cnf(struct trxcon_inst *trxcon, struct msgb *msg) +{ + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + struct trxcon_param_tx_access_burst_cnf cnf = { + .band_arfcn = trxcon->l1p.band_arfcn, + .frame_nr = prim->rach_cnf.chdr.frame_nr, + }; + + if (trxcon->gsmtap != NULL) { + if (prim->rach_cnf.is_11bit) { + msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra >> 3)); + msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra & 0x07)); + } else { + msgb_put_u8(msg, (uint8_t)(prim->rach_cnf.ra)); + } + + trxcon_gsmtap_send(trxcon, &prim->rach_cnf.chdr, + msgb_l2(msg), msgb_l2len(msg), + 0, 0, true); + } + + return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_TX_ACCESS_BURST_CNF, &cnf); +} + +int l1sched_prim_to_user(struct l1sched_state *sched, struct msgb *msg) +{ + const struct l1sched_prim *prim = l1sched_prim_from_msgb(msg); + struct trxcon_inst *trxcon = sched->priv; + int rc = 0; + + LOGPFSML(trxcon->fi, LOGL_DEBUG, + "%s(): Rx " L1SCHED_PRIM_STR_FMT "\n", + __func__, L1SCHED_PRIM_STR_ARGS(prim)); + + switch (OSMO_PRIM_HDR(&prim->oph)) { + case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_INDICATION): + rc = handle_prim_data_ind(trxcon, msg); + break; + case OSMO_PRIM(L1SCHED_PRIM_T_DATA, PRIM_OP_CONFIRM): + rc = handle_prim_data_cnf(trxcon, msg); + break; + case OSMO_PRIM(L1SCHED_PRIM_T_RACH, PRIM_OP_CONFIRM): + rc = handle_prim_rach_cnf(trxcon, msg); + break; + case OSMO_PRIM(L1SCHED_PRIM_T_SCH, PRIM_OP_INDICATION): + if (trxcon->fi->state == TRXCON_ST_FBSB_SEARCH) + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FBSB_SEARCH_RES, NULL); + break; + case OSMO_PRIM(L1SCHED_PRIM_T_PCHAN_COMB, PRIM_OP_INDICATION): + { + struct trxcon_param_set_phy_config_req req = { + .type = TRXCON_PHY_CFGT_PCHAN_COMB, + .pchan_comb = { + .tn = prim->pchan_comb_ind.tn, + .pchan = prim->pchan_comb_ind.pchan, + }, + }; + + rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_PHY_CONFIG_REQ, &req); + break; + } + default: + LOGPFSML(trxcon->fi, LOGL_ERROR, + "%s(): Unhandled primitive " L1SCHED_PRIM_STR_FMT "\n", + __func__, L1SCHED_PRIM_STR_ARGS(prim)); + rc = -ENOTSUP; + } + + msgb_free(msg); + return rc; +} + +/* External L1 API for the PHYIF */ +int trxcon_phyif_handle_rts_ind(void *priv, const struct trxcon_phyif_rts_ind *rts) +{ + struct trxcon_inst *trxcon = priv; + struct l1sched_burst_req br = { + .fn = rts->fn, + .tn = rts->tn, + .burst_len = 0, /* NOPE.ind */ + }; + + l1sched_pull_burst(trxcon->sched, &br); + return l1sched_handle_burst_req(trxcon->sched, &br); +} + +int trxcon_phyif_handle_rtr_ind(void *priv, const struct trxcon_phyif_rtr_ind *ind, + struct trxcon_phyif_rtr_rsp *rsp) +{ + struct trxcon_inst *trxcon = priv; + struct l1sched_probe probe = { + .fn = ind->fn, + .tn = ind->tn, + }; + + l1sched_handle_rx_probe(trxcon->sched, &probe); + + memset(rsp, 0x00, sizeof(*rsp)); + + if (probe.flags & L1SCHED_PROBE_F_ACTIVE) + rsp->flags |= TRXCON_PHYIF_RTR_F_ACTIVE; + + return 0; +} + +int trxcon_phyif_handle_burst_ind(void *priv, const struct trxcon_phyif_burst_ind *phybi) +{ + struct trxcon_inst *trxcon = priv; + struct l1sched_burst_ind bi = { + .fn = phybi->fn, + .tn = phybi->tn, + .toa256 = phybi->toa256, + .rssi = phybi->rssi, + /* .burst[] is populated below */ + .burst_len = phybi->burst_len, + }; + + OSMO_ASSERT(phybi->burst_len <= sizeof(bi.burst)); + memcpy(&bi.burst[0], phybi->burst, phybi->burst_len); + + /* Poke scheduler */ + return l1sched_handle_rx_burst(trxcon->sched, &bi); +} + +int trxcon_phyif_handle_rsp(void *priv, const struct trxcon_phyif_rsp *rsp) +{ + struct trxcon_inst *trxcon = priv; + + switch (rsp->type) { + case TRXCON_PHYIF_CMDT_MEASURE: + { + const struct trxcon_phyif_rspp_measure *meas = &rsp->param.measure; + struct trxcon_param_full_power_scan_res res = { + .band_arfcn = meas->band_arfcn, + .dbm = meas->dbm, + }; + + return osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_FULL_POWER_SCAN_RES, &res); + } + default: + LOGPFSML(trxcon->fi, LOGL_ERROR, + "Unhandled PHYIF response (type 0x%02x)\n", rsp->type); + return -ENODEV; + } +} diff --git a/src/host/trxcon/trx_if.h b/src/host/trxcon/trx_if.h deleted file mode 100644 index fa66d4a7..00000000 --- a/src/host/trxcon/trx_if.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/select.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/fsm.h> - -#include "scheduler.h" -#include "sched_trx.h" - -#define TRXC_BUF_SIZE 1024 -#define TRXD_BUF_SIZE 512 - -/* Forward declaration to avoid mutual include */ -struct l1ctl_link; - -enum trx_fsm_states { - TRX_STATE_OFFLINE = 0, - TRX_STATE_IDLE, - TRX_STATE_ACTIVE, - TRX_STATE_RSP_WAIT, -}; - -struct trx_instance { - struct osmo_fd trx_ofd_ctrl; - struct osmo_fd trx_ofd_data; - - struct osmo_timer_list trx_ctrl_timer; - struct llist_head trx_ctrl_list; - struct osmo_fsm_inst *fsm; - - /* HACK: we need proper state machines */ - uint32_t prev_state; - bool powered_up; - - /* GSM L1 specific */ - uint16_t pm_band_arfcn_start; - uint16_t pm_band_arfcn_stop; - uint16_t band_arfcn; - uint8_t tx_power; - uint8_t bsic; - uint8_t tsc; - int8_t ta; - - /* Scheduler stuff */ - struct trx_sched sched; - struct trx_ts *ts_list[TRX_TS_COUNT]; - - /* Bind L1CTL link */ - struct l1ctl_link *l1l; -}; - -struct trx_ctrl_msg { - struct llist_head list; - char cmd[TRXC_BUF_SIZE]; - int retry_cnt; - int critical; - int cmd_len; -}; - -struct trx_instance *trx_if_open(void *tall_ctx, - const char *local_host, const char *remote_host, uint16_t port); -void trx_if_flush_ctrl(struct trx_instance *trx); -void trx_if_close(struct trx_instance *trx); - -int trx_if_cmd_poweron(struct trx_instance *trx); -int trx_if_cmd_poweroff(struct trx_instance *trx); -int trx_if_cmd_echo(struct trx_instance *trx); - -int trx_if_cmd_setta(struct trx_instance *trx, int8_t ta); - -int trx_if_cmd_rxtune(struct trx_instance *trx, uint16_t band_arfcn); -int trx_if_cmd_txtune(struct trx_instance *trx, uint16_t band_arfcn); - -int trx_if_cmd_setslot(struct trx_instance *trx, uint8_t tn, uint8_t type); -int trx_if_cmd_setfh(struct trx_instance *trx, uint8_t hsn, - uint8_t maio, uint16_t *ma, size_t ma_len); - -int trx_if_cmd_measure(struct trx_instance *trx, - uint16_t band_arfcn_start, uint16_t band_arfcn_stop); - -int trx_if_tx_burst(struct trx_instance *trx, - const struct sched_burst_req *br); diff --git a/src/host/trxcon/trxcon.h b/src/host/trxcon/trxcon.h deleted file mode 100644 index 9a0792bf..00000000 --- a/src/host/trxcon/trxcon.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#define GEN_MASK(state) (0x01 << state) - -extern struct osmo_fsm_inst *trxcon_fsm; -extern struct gsmtap_inst *gsmtap; - -enum trxcon_fsm_states { - TRXCON_STATE_IDLE = 0, - TRXCON_STATE_MANAGED, -}; - -enum trxcon_fsm_events { - /* L1CTL specific events */ - L1CTL_EVENT_CONNECT, - L1CTL_EVENT_DISCONNECT, - - /* TRX specific events */ - TRX_EVENT_RSP_ERROR, - TRX_EVENT_OFFLINE, -}; diff --git a/src/host/virt_phy/configure.ac b/src/host/virt_phy/configure.ac index fbff2c11..19e4bc7a 100644 --- a/src/host/virt_phy/configure.ac +++ b/src/host/virt_phy/configure.ac @@ -3,6 +3,8 @@ AC_INIT([virtphy], 0.0.0) AM_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE([foreign dist-bzip2 subdir-objects]) +CFLAGS="$CFLAGS -std=gnu11" + dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) @@ -33,9 +35,34 @@ then CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" fi +AC_ARG_ENABLE(werror, + [AS_HELP_STRING( + [--enable-werror], + [Turn all compiler warnings into errors, with exceptions: + a) deprecation (allow upstream to mark deprecation without breaking builds); + b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) + ] + )], + [werror=$enableval], [werror="no"]) +if test x"$werror" = x"yes" +then + WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Werror=implicit-int -Werror=int-conversion -Werror=old-style-definition" + WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" + WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" + CFLAGS="$CFLAGS $WERROR_FLAGS" + CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" +fi + +AC_MSG_RESULT([CFLAGS="$CFLAGS"]) +AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) + AC_CONFIG_FILES([ Makefile include/Makefile + include/osmocom/Makefile + include/osmocom/bb/Makefile + include/osmocom/bb/virtphy/Makefile src/Makefile ]) AC_OUTPUT diff --git a/src/host/virt_phy/include/Makefile.am b/src/host/virt_phy/include/Makefile.am index 6048a4b3..9d963a02 100644 --- a/src/host/virt_phy/include/Makefile.am +++ b/src/host/virt_phy/include/Makefile.am @@ -1,10 +1,3 @@ -noinst_HEADERS = \ - virtphy/logging.h \ - virtphy/osmo_mcast_sock.h \ - virtphy/l1ctl_sock.h \ - virtphy/virtual_um.h \ - virtphy/gsmtapl1_if.h \ - virtphy/virt_l1_sched.h \ - virtphy/common_util.h \ - virtphy/l1ctl_sap.h \ - virtphy/virt_l1_model.h +SUBDIRS = \ + osmocom \ + $(NULL) diff --git a/src/host/virt_phy/include/osmocom/Makefile.am b/src/host/virt_phy/include/osmocom/Makefile.am new file mode 100644 index 00000000..83c6385c --- /dev/null +++ b/src/host/virt_phy/include/osmocom/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = \ + bb \ + $(NULL) diff --git a/src/host/virt_phy/include/osmocom/bb/Makefile.am b/src/host/virt_phy/include/osmocom/bb/Makefile.am new file mode 100644 index 00000000..9a4b939a --- /dev/null +++ b/src/host/virt_phy/include/osmocom/bb/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = \ + virtphy \ + $(NULL) + +noinst_HEADERS = \ + l1ctl_proto.h \ + l1gprs.h \ + $(NULL) diff --git a/src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h b/src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h new file mode 120000 index 00000000..ee19b80e --- /dev/null +++ b/src/host/virt_phy/include/osmocom/bb/l1ctl_proto.h @@ -0,0 +1 @@ +../../../../../../include/l1ctl_proto.h
\ No newline at end of file diff --git a/src/host/virt_phy/include/osmocom/bb/l1gprs.h b/src/host/virt_phy/include/osmocom/bb/l1gprs.h new file mode 120000 index 00000000..3bf85176 --- /dev/null +++ b/src/host/virt_phy/include/osmocom/bb/l1gprs.h @@ -0,0 +1 @@ +../../../../../../include/l1gprs.h
\ No newline at end of file diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am b/src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am new file mode 100644 index 00000000..75846096 --- /dev/null +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/Makefile.am @@ -0,0 +1,11 @@ +noinst_HEADERS = \ + logging.h \ + osmo_mcast_sock.h \ + l1ctl_sock.h \ + virtual_um.h \ + gsmtapl1_if.h \ + virt_l1_sched.h \ + common_util.h \ + l1ctl_sap.h \ + virt_l1_model.h \ + $(NULL) diff --git a/src/host/virt_phy/include/virtphy/common_util.h b/src/host/virt_phy/include/osmocom/bb/virtphy/common_util.h index 2585d069..2585d069 100644 --- a/src/host/virt_phy/include/virtphy/common_util.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/common_util.h diff --git a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h b/src/host/virt_phy/include/osmocom/bb/virtphy/gsmtapl1_if.h index 8dab6b28..c9e92525 100644 --- a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/gsmtapl1_if.h @@ -2,9 +2,10 @@ #include <osmocom/core/msgb.h> #include <osmocom/core/gsmtap.h> -#include <virtphy/virtual_um.h> -#include <virtphy/l1ctl_sock.h> -#include <virtphy/virt_l1_model.h> + +#include <osmocom/bb/virtphy/virtual_um.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> void gsmtapl1_init(struct l1_model_ms *model); void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui, diff --git a/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h new file mode 100644 index 00000000..e756c140 --- /dev/null +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sap.h @@ -0,0 +1,85 @@ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/msgb.h> + +#include <osmocom/bb/virtphy/virtual_um.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/l1ctl_proto.h> + +/* following sizes are used for message allocation */ +/* size of layer 3 header */ +#define L3_MSG_HEAD 4 +/* size of layer 3 payload */ +#define L3_MSG_DATA 200 +#define L3_MSG_SIZE (sizeof(struct l1ctl_hdr) + L3_MSG_HEAD + L3_MSG_DATA) + +/* lchan link ID */ +#define LID_SACCH 0x40 +#define LID_DEDIC 0x00 + +/* signature strengths for the ms */ +#define MIN_SIG_LEV_DBM -110 +#define MAX_SIG_LEV_DBM -63 + +/* Ignore all flags of the arfcn */ +#define ARFCN_NO_FLAGS_MASK 0x0fff + + +void l1ctl_sap_init(struct l1_model_ms *model); +void l1ctl_sap_exit(struct l1_model_ms *model); +void prim_pm_init(struct l1_model_ms *model); +void prim_pm_exit(struct l1_model_ms *model); +void l1ctl_sap_tx_to_l23_inst(struct l1_model_ms *model, struct msgb *msg); +void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_client *lsc, struct msgb *msg); +void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg); + +/* utility methods */ +struct msgb *l1ctl_msgb_alloc(uint8_t msg_type); +struct msgb *l1ctl_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, + uint16_t arfcn); + +/* receive routines */ +void l1ctl_rx_fbsb_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_param_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_dm_freq_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_crypto_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_rach_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_data_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_pm_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_ccch_mode_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg); +void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg); + +/* transmit routines */ +void l1ctl_tx_reset(struct l1_model_ms *ms, uint8_t msg_type, uint8_t reset_type); +void l1ctl_tx_rach_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t arfcn); +void l1ctl_tx_data_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint16_t arfcn); +void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id, + uint8_t chan_nr, uint32_t fn, uint8_t snr, + uint8_t rxlev, uint8_t num_biterr, + uint8_t fire_crc); +void l1ctl_tx_traffic_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint16_t arfcn); +void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id, + uint8_t chan_nr, uint32_t fn, uint8_t snr, + uint8_t rxlev, uint8_t num_biterr, + uint8_t fire_crc); +void l1ctl_tx_pm_conf(struct l1_model_ms *ms, struct l1ctl_pm_req *pm_req); +void l1ctl_tx_fbsb_conf(struct l1_model_ms *ms, uint8_t res, uint16_t arfcn); +void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *ms, uint8_t ccch_mode); +void l1ctl_tx_tch_mode_conf(struct l1_model_ms *ms, uint8_t tch_mode, uint8_t audio_mode); +void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg, + uint32_t fn, uint8_t tn, uint8_t rxlev); + +/* scheduler functions */ +uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr, + uint8_t link_id); diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sock.h b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sock.h index 2c98fa58..2c98fa58 100644 --- a/src/host/virt_phy/include/virtphy/l1ctl_sock.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/l1ctl_sock.h diff --git a/src/host/virt_phy/include/virtphy/logging.h b/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h index b22db992..1727453e 100644 --- a/src/host/virt_phy/include/virtphy/logging.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/logging.h @@ -7,6 +7,7 @@ enum virtphy_log_cat { DL1C, DL1P, DVIRPHY, + DGPRS, DMAIN }; @@ -15,5 +16,5 @@ enum virtphy_log_cat { extern const struct log_info ms_log_info; -int ms_log_init(char *cat_mask); +int ms_log_init(void *ctx, const char *cat_mask); const char *getL1ctlPrimName(uint8_t type); diff --git a/src/host/virt_phy/include/virtphy/osmo_mcast_sock.h b/src/host/virt_phy/include/osmocom/bb/virtphy/osmo_mcast_sock.h index aa2013c6..aa2013c6 100644 --- a/src/host/virt_phy/include/virtphy/osmo_mcast_sock.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/osmo_mcast_sock.h diff --git a/src/host/virt_phy/include/virtphy/virt_l1_model.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h index ffed7082..94581f61 100644 --- a/src/host/virt_phy/include/virtphy/virt_l1_model.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_model.h @@ -2,11 +2,12 @@ /* Per-MS specific state, closely attached to the L1CTL user progran */ -#include <virtphy/virtual_um.h> -#include <virtphy/l1ctl_sock.h> #include <osmocom/gsm/gsm_utils.h> #include <osmocom/core/timer.h> +#include <osmocom/bb/virtphy/virtual_um.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> + #define L1S_NUM_NEIGH_CELL 6 #define A5_KEY_LEN 8 @@ -15,7 +16,6 @@ enum ms_state { MS_STATE_IDLE_SYNCING, MS_STATE_IDLE_CAMPING, MS_STATE_DEDICATED, - MS_STATE_TBF }; @@ -76,15 +76,6 @@ struct l1_state_ms { uint8_t tsc; // training sequence code (unused in virtual um) uint8_t h; // hopping enabled flag (unused in virtual um) } dedicated; - struct { - struct { - uint8_t usf[8]; - struct llist_head tx_queue; - } ul; - struct { - uint8_t tfi[8]; - } dl; - } tbf; /* fbsb state */ struct { @@ -115,6 +106,8 @@ struct l1_model_ms { struct l1ctl_sock_client *lsc; /* pointer to the (shared) GSMTAP/VirtUM socket to talk to BTS(s) */ struct virt_um_inst *vui; + /* GPRS state (MAC layer) */ + struct l1gprs_state *gprs; /* actual per-MS state */ struct l1_state_ms state; }; diff --git a/src/host/virt_phy/include/virtphy/virt_l1_sched.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_sched.h index b8d401ff..d5a1630f 100644 --- a/src/host/virt_phy/include/virtphy/virt_l1_sched.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virt_l1_sched.h @@ -1,9 +1,11 @@ #pragma once + #include <osmocom/core/msgb.h> -#include <virtphy/virt_l1_model.h> #include <osmocom/core/linuxlist.h> #include <osmocom/gsm/gsm_utils.h> -#include <virtphy/virt_l1_sched.h> + +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> struct l1_model_ms; diff --git a/src/host/virt_phy/include/virtphy/virtual_um.h b/src/host/virt_phy/include/osmocom/bb/virtphy/virtual_um.h index fe060929..fe060929 100644 --- a/src/host/virt_phy/include/virtphy/virtual_um.h +++ b/src/host/virt_phy/include/osmocom/bb/virtphy/virtual_um.h diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/virtphy/l1ctl_sap.h deleted file mode 100644 index d3562a17..00000000 --- a/src/host/virt_phy/include/virtphy/l1ctl_sap.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <osmocom/core/msgb.h> -#include <l1ctl_proto.h> -#include <virtphy/virtual_um.h> -#include <virtphy/l1ctl_sock.h> -#include <virtphy/virt_l1_model.h> - -/* following sizes are used for message allocation */ -/* size of layer 3 header */ -#define L3_MSG_HEAD 4 -/* size of layer 3 payload */ -#define L3_MSG_DATA 200 -#define L3_MSG_SIZE (sizeof(struct l1ctl_hdr) + L3_MSG_HEAD + L3_MSG_DATA) - -/* lchan link ID */ -#define LID_SACCH 0x40 -#define LID_DEDIC 0x00 - -/* signature strengths for the ms */ -#define MIN_SIG_LEV_DBM -110 -#define MAX_SIG_LEV_DBM -63 - -/* Ignore all flags of the arfcn */ -#define ARFCN_NO_FLAGS_MASK 0x0fff - - -void l1ctl_sap_init(struct l1_model_ms *model); -void l1ctl_sap_exit(struct l1_model_ms *model); -void prim_pm_init(struct l1_model_ms *model); -void prim_pm_exit(struct l1_model_ms *model); -void l1ctl_sap_tx_to_l23_inst(struct l1_model_ms *model, struct msgb *msg); -void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_client *lsc, struct msgb *msg); -void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg); - -/* utility methods */ -struct msgb *l1ctl_msgb_alloc(uint8_t msg_type); -struct msgb *l1ctl_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, - uint16_t arfcn); - -/* receive routines */ -void l1ctl_rx_fbsb_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_dm_est_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_dm_rel_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_param_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_dm_freq_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_crypto_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_rach_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_data_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_pm_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_reset_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_ccch_mode_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_tch_mode_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_neigh_pm_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_traffic_req(struct l1_model_ms *, struct msgb *msg); -void l1ctl_rx_sim_req(struct l1_model_ms *, struct msgb *msg); - -/* transmit routines */ -void l1ctl_tx_reset(struct l1_model_ms *, uint8_t msg_type, uint8_t reset_type); -void l1ctl_tx_rach_conf(struct l1_model_ms *, uint32_t fn, uint16_t arfcn); -void l1ctl_tx_data_conf(struct l1_model_ms *, uint32_t fn, uint16_t snr, uint16_t arfcn); -void l1ctl_tx_data_ind(struct l1_model_ms *, struct msgb *msg, uint16_t arfcn, uint8_t link_id, - uint8_t chan_nr, uint32_t fn, uint8_t snr, - uint8_t signal_dbm, uint8_t num_biterr, - uint8_t fire_crc); -void l1ctl_tx_traffic_conf(struct l1_model_ms *, uint32_t fn, uint16_t snr, uint16_t arfcn); -void l1ctl_tx_traffic_ind(struct l1_model_ms *, struct msgb *msg, uint16_t arfcn, uint8_t link_id, - uint8_t chan_nr, uint32_t fn, uint8_t snr, - uint8_t signal_dbm, uint8_t num_biterr, - uint8_t fire_crc); -void l1ctl_tx_pm_conf(struct l1_model_ms *, struct l1ctl_pm_req *pm_req); -void l1ctl_tx_fbsb_conf(struct l1_model_ms *, uint8_t res, uint16_t arfcn); -void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *, uint8_t ccch_mode); -void l1ctl_tx_tch_mode_conf(struct l1_model_ms *, uint8_t tch_mode, uint8_t audio_mode); - -/* scheduler functions */ -uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr, - uint8_t link_id); diff --git a/src/host/virt_phy/src/Makefile.am b/src/host/virt_phy/src/Makefile.am index bfd0dfaa..f10639f5 100644 --- a/src/host/virt_phy/src/Makefile.am +++ b/src/host/virt_phy/src/Makefile.am @@ -1,11 +1,28 @@ AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) -AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)/../layer23/include +AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include bin_PROGRAMS = virtphy -virtphy_SOURCES = virtphy.c l1ctl_sock.c gsmtapl1_if.c l1ctl_sap.c virt_prim_pm.c virt_prim_fbsb.c virt_prim_rach.c virt_prim_data.c virt_prim_traffic.c virt_l1_sched_simple.c logging.c virt_l1_model.c shared/virtual_um.c shared/osmo_mcast_sock.c -virtphy_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -virtphy_LDFLAGS = -pthread -# debug output -all: - $(info $$AM_CPPFLAGS is [${AM_CPPFLAGS}]) +virtphy_SOURCES = \ + virtphy.c \ + l1gprs.c \ + logging.c \ + gsmtapl1_if.c \ + l1ctl_sock.c \ + l1ctl_sap.c \ + virt_prim_pm.c \ + virt_prim_fbsb.c \ + virt_prim_rach.c \ + virt_prim_data.c \ + virt_prim_pdch.c \ + virt_prim_traffic.c \ + virt_l1_sched_simple.c \ + virt_l1_model.c \ + shared/virtual_um.c \ + shared/osmo_mcast_sock.c \ + $(NULL) + +virtphy_LDADD = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) diff --git a/src/host/virt_phy/src/gsmtapl1_if.c b/src/host/virt_phy/src/gsmtapl1_if.c index 83b01fec..ef8d6840 100644 --- a/src/host/virt_phy/src/gsmtapl1_if.c +++ b/src/host/virt_phy/src/gsmtapl1_if.c @@ -20,6 +20,11 @@ * */ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + #include <osmocom/core/gsmtap.h> #include <osmocom/core/gsmtap_util.h> #include <osmocom/core/utils.h> @@ -28,18 +33,15 @@ #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/core/msgb.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <l1ctl_proto.h> -#include <virtphy/virtual_um.h> -#include <virtphy/l1ctl_sock.h> -#include <virtphy/virt_l1_model.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/gsmtapl1_if.h> -#include <virtphy/logging.h> -#include <virtphy/virt_l1_sched.h> + +#include <osmocom/bb/virtphy/virtual_um.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/l1ctl_proto.h> static char *pseudo_lchan_name(uint16_t arfcn, uint8_t ts, uint8_t ss, uint8_t sub_type) { @@ -82,7 +84,7 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn struct gsmtap_hdr *gh; struct msgb *outmsg; /* msg to send with gsmtap header prepended */ uint16_t arfcn; - uint8_t signal_dbm = 63; /* signal strength */ + uint8_t signal_dbm = rxlev2dbm(63); /* signal strength */ uint8_t snr = 63; /* signal noise ratio, 63 is best */ uint8_t *data = msgb_l2(msg); /* data to transmit (whole message without l1 header) */ uint8_t data_len = msgb_l2len(msg); /* length of data */ @@ -92,23 +94,16 @@ void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, uint8_t tn uint8_t timeslot; /* tdma timeslot to send in (0-7) */ uint8_t gsmtap_chan; /* the gsmtap channel */ - switch (ms->state.state) { - case MS_STATE_DEDICATED: - case MS_STATE_TBF: + if (ms->state.state == MS_STATE_DEDICATED) arfcn = ms->state.dedicated.band_arfcn; - break; - default: + else arfcn = ms->state.serving_cell.arfcn; - break; - } switch (l1h->msg_type) { - case L1CTL_DATA_TBF_REQ: - ul = NULL; - rsl_chantype = RSL_CHAN_OSMO_PDCH; + case L1CTL_GPRS_UL_BLOCK_REQ: + gsmtap_chan = GSMTAP_CHANNEL_PDCH; timeslot = tn; subslot = 0; - gsmtap_chan = chantype_rsl2gsmtap2(rsl_chantype, 0, false); break; case L1CTL_TRAFFIC_REQ: ul = (struct l1ctl_info_ul *)l1h->data; @@ -163,85 +158,13 @@ extern void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg); */ extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev); -/* determine if a received Downlink RLC/MAC block matches the current MS configuration */ -static bool gprs_dl_block_matches_ms(struct l1_model_ms *ms, struct msgb *msg, uint8_t timeslot) -{ - uint8_t payload_type; - uint8_t tfi; - - if (msgb_length(msg) < 1) - return false; - - /* FIXME: Ensure this will also work for EGPRS! */ - payload_type = msg->data[0] >> 6; - switch (payload_type) { - case 0: /* RLC Data Block */ - /* forward all RLD Data Blocks destined for TFI of MS */ - tfi = (msg->data[1] >> 1) & 0x1f; - if (ms->state.state == MS_STATE_TBF && ms->state.tbf.dl.tfi[timeslot] == tfi) - return true; - break; - case 1: /* RLC/MAC Control without optional octets */ - /* forward all RLC/MAC control blocks without optional octets, i.e. not addressed - * to a specific TFI */ - return true; - case 2: /* RLC/MAC with optional control octets */ - /* forward all RLD Control Blocks destined for TFI of MS */ - tfi = (msg->data[2] >> 1) & 0x1f; - if (ms->state.state == MS_STATE_TBF && ms->state.tbf.dl.tfi[timeslot] == tfi) - return true; - break; - default: - break; - } - return false; -} - -/* determine if given USF at given timeslot is relevant to given MS or not */ -static bool usf_matches_ms(struct l1_model_ms *ms, uint8_t usf, uint8_t timeslot) -{ - if (ms->state.state == MS_STATE_TBF && ms->state.tbf.ul.usf[timeslot] == usf) - return true; - - return false; -} - -/* extract USF from (E)GPRS RLC/MAC block */ -static uint8_t get_usf_from_block(struct msgb *msg) -{ - /* FIXME: Ensure this will also work for EGPRS! */ - return msg->data[0] & 0x7; -} - -/* MS is authorized to transmit a block in uplink for given USF on timeslot+arfcn at FN */ -static void ms_ul_tbf_may_transmit(struct l1_model_ms *ms, uint16_t arfcn, uint8_t timeslot, - uint32_t fn, uint8_t usf) -{ - struct msgb *msg; - - /* If USF is not for us, bail out */ - if (!usf_matches_ms(ms, usf, timeslot)) - return; - - /* attempt to de-queue pending msgb for this UL TBF and transmit it */ - msg = msgb_dequeue(&ms->state.tbf.ul.tx_queue); - if (!msg) { - printf("FN=%u, TN=%u, USF=%u: empty tx_queue, not transmitting\n", fn, timeslot, usf); - /* FIXME: send some dummy control frame? */ - } else { - printf("FN=%u, TN=%u, USF=%u: transmitting queued msg\n", fn, timeslot, usf); - gsmtapl1_tx_to_virt_um_inst(ms, fn, timeslot, msg); - } -} - static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, uint32_t fn, uint16_t arfcn, uint8_t timeslot, uint8_t subslot, uint8_t gsmtap_chantype, uint8_t chan_nr, uint8_t link_id, uint8_t snr_db) { struct l1_model_ms *ms = lsc->priv; - uint8_t signal_dbm = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM)); /* Power measurement with each received massage */ - uint8_t usf; + uint8_t rxlev = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM)); gsm_fn2gsmtime(&ms->state.downlink_time, fn); @@ -279,7 +202,7 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, * the timeslot and subslot is fitting */ if (ms->state.dedicated.tn == timeslot && ms->state.dedicated.subslot == subslot) { - l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0); + l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, rxlev, 0, 0); } break; case GSMTAP_CHANNEL_VOICE_F: @@ -289,7 +212,7 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, if (ms->state.dedicated.tn == timeslot && ms->state.dedicated.subslot == subslot) { l1ctl_tx_traffic_ind(ms, msg, arfcn, link_id, chan_nr, fn, - snr_db, signal_dbm, 0, 0); + snr_db, rxlev, 0, 0); } break; case GSMTAP_CHANNEL_CBCH51: @@ -303,21 +226,18 @@ static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, case GSMTAP_CHANNEL_CBCH52: /* save to just forward here, as upper layer ignores messages that * do not fit the current state (e.g. gsm48_rr.c:2159) */ - l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0); + l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, rxlev, 0, 0); break; case GSMTAP_CHANNEL_RACH: LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unexpected RACH in downlink ?!?\n"); break; + case GSMTAP_CHANNEL_PTCCH: case GSMTAP_CHANNEL_PACCH: case GSMTAP_CHANNEL_PDCH: - if (gprs_dl_block_matches_ms(ms, msg, timeslot)) - l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0); - usf = get_usf_from_block(msg); - ms_ul_tbf_may_transmit(ms, arfcn, timeslot, fn, usf); + l1ctl_tx_gprs_dl_block_ind(ms, msg, fn, timeslot, rxlev); break; case GSMTAP_CHANNEL_SDCCH: case GSMTAP_CHANNEL_CCCH: - case GSMTAP_CHANNEL_PTCCH: LOGPMS(DVIRPHY, LOGL_NOTICE, ms, "Ignoring unsupported channel type %s\n", get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype)); break; diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c index bab62590..7e4540df 100644 --- a/src/host/virt_phy/src/l1ctl_sap.c +++ b/src/host/virt_phy/src/l1ctl_sap.c @@ -19,6 +19,9 @@ * */ +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> #include <osmocom/core/linuxlist.h> #include <osmocom/core/msgb.h> @@ -27,21 +30,16 @@ #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/rsl.h> -#include <osmocom/gprs/gprs_rlc.h> -#include <stdio.h> -#include <l1ctl_proto.h> -#include <netinet/in.h> -#include <string.h> -#include <virtphy/virtual_um.h> -#include <virtphy/l1ctl_sock.h> -#include <virtphy/virt_l1_model.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/gsmtapl1_if.h> -#include <virtphy/logging.h> -#include <virtphy/virt_l1_sched.h> -static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg); -static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg); +#include <osmocom/bb/virtphy/virtual_um.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/l1ctl_proto.h> +#include <osmocom/bb/l1gprs.h> static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode) { @@ -59,7 +57,6 @@ static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode) void l1ctl_sap_init(struct l1_model_ms *model) { INIT_LLIST_HEAD(&model->state.sched.mframe_items); - INIT_LLIST_HEAD(&model->state.tbf.ul.tx_queue); prim_pm_init(model); } @@ -162,8 +159,8 @@ static bool is_l1ctl_control(uint8_t msg_type) switch (msg_type) { case L1CTL_DATA_REQ: case L1CTL_DATA_CONF: - case L1CTL_DATA_TBF_REQ: - case L1CTL_DATA_TBF_CONF: + case L1CTL_GPRS_UL_BLOCK_REQ: + case L1CTL_GPRS_DL_BLOCK_IND: case L1CTL_TRAFFIC_REQ: case L1CTL_TRAFFIC_CONF: case L1CTL_TRAFFIC_IND: @@ -246,11 +243,12 @@ void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg) case L1CTL_SIM_REQ: l1ctl_rx_sim_req(ms, msg); break; - case L1CTL_TBF_CFG_REQ: - l1ctl_rx_tbf_cfg_req(ms, msg); + case L1CTL_GPRS_UL_TBF_CFG_REQ: + case L1CTL_GPRS_DL_TBF_CFG_REQ: + l1ctl_rx_gprs_uldl_tbf_cfg_req(ms, msg); break; - case L1CTL_DATA_TBF_REQ: - l1ctl_rx_data_tbf_req(ms, msg); + case L1CTL_GPRS_UL_BLOCK_REQ: + l1ctl_rx_gprs_ul_block_req(ms, msg); goto exit_nofree; } @@ -296,6 +294,12 @@ void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg) ms->state.dedicated.subslot = subslot; ms->state.state = MS_STATE_DEDICATED; + if (rsl_chantype == RSL_CHAN_OSMO_PDCH) { + OSMO_ASSERT(ms->gprs == NULL); + ms->gprs = l1gprs_state_alloc(ms, NULL, ms); + OSMO_ASSERT(ms->gprs != NULL); + } + /* TCH config */ if (rsl_chantype == RSL_CHAN_Bm_ACCHs || rsl_chantype == RSL_CHAN_Lm_ACCHs) { ms->state.tch_mode = est_req->tch_mode; @@ -380,6 +384,9 @@ void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg) ms->state.tch_mode = GSM48_CMODE_SIGN; ms->state.state = MS_STATE_IDLE_CAMPING; + l1gprs_state_free(ms->gprs); + ms->gprs = NULL; + /* TODO: disable ciphering */ /* TODO: disable audio recording / playing */ } @@ -428,6 +435,8 @@ void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg) DEBUGPMS(DL1C, ms, "Rx L1CTL_RESET_REQ (type=FULL)\n"); ms->state.state = MS_STATE_IDLE_SEARCHING; virt_l1_sched_stop(ms); + l1gprs_state_free(ms->gprs); + ms->gprs = NULL; l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type); break; case L1CTL_RES_T_SCHED: @@ -486,6 +495,7 @@ void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg) l1_model_tch_mode_set(ms, tch_mode_req->tch_mode); ms->state.audio_mode = tch_mode_req->audio_mode; + /* TODO: Handle AMR codecs from tch_mode_req if tch_mode_req->tch_mode==GSM48_CMODE_SPEECH_AMR */ LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TCH_MODE_REQ (tch_mode=0x%02x audio_mode=0x%02x)\n", tch_mode_req->tch_mode, tch_mode_req->audio_mode); @@ -545,143 +555,12 @@ void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg) } -static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct l1ctl_tbf_cfg_req *in); - -static void l1ctl_rx_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg) -{ - struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; - struct l1ctl_tbf_cfg_req *cfg_req = (struct l1ctl_tbf_cfg_req *) l1h->data; - unsigned int i; - - LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TBF_CFG_REQ (tbf_id=%u, dir=%s, " - "usf=[%d,%d,%d,%d,%d,%d,%d,%d]\n", cfg_req->tbf_nr, - cfg_req->is_uplink ? "UL" : "DL", cfg_req->usf[0], cfg_req->usf[1], - cfg_req->usf[2], cfg_req->usf[3], cfg_req->usf[4], cfg_req->usf[5], - cfg_req->usf[6], cfg_req->usf[7]); - - if (cfg_req->tbf_nr != 0) { - LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported yet!\n"); - return; - } - - if (ms->state.state == MS_STATE_DEDICATED) - LOGPMS(DL1C, LOGL_NOTICE, ms, "Hard termination of DEDICATED mode, fix L23!\n"); - - if (cfg_req->is_uplink) { - for (i = 0; i < 8; i++) - ms->state.tbf.ul.usf[i] = cfg_req->usf[i]; - } else { - for (i = 0; i < 8; i++) - ms->state.tbf.dl.tfi[i] = cfg_req->usf[i]; - } - ms->state.state = MS_STATE_TBF; - - l1ctl_tx_tbf_cfg_conf(ms, cfg_req); -} - -static const enum osmo_gprs_cs osmo_cs_by_l1ctl[] = { - [L1CTL_CS_NONE] = OSMO_GPRS_CS_NONE, - [L1CTL_CS1] = OSMO_GPRS_CS1, - [L1CTL_CS2] = OSMO_GPRS_CS2, - [L1CTL_CS3] = OSMO_GPRS_CS3, - [L1CTL_CS4] = OSMO_GPRS_CS4, - [L1CTL_MCS1] = OSMO_GPRS_MCS1, - [L1CTL_MCS2] = OSMO_GPRS_MCS2, - [L1CTL_MCS3] = OSMO_GPRS_MCS3, - [L1CTL_MCS4] = OSMO_GPRS_MCS4, - [L1CTL_MCS5] = OSMO_GPRS_MCS5, - [L1CTL_MCS6] = OSMO_GPRS_MCS6, - [L1CTL_MCS7] = OSMO_GPRS_MCS7, - [L1CTL_MCS8] = OSMO_GPRS_MCS8, - [L1CTL_MCS9] = OSMO_GPRS_MCS9, -}; - -static int get_osmo_cs_by_l1ctl(enum l1ctl_coding_scheme l1) -{ - if (l1 >= ARRAY_SIZE(osmo_cs_by_l1ctl)) - return -1; - return osmo_cs_by_l1ctl[l1]; -} - -static const enum l1ctl_coding_scheme l1ctl_cs_by_osmo[] = { - [OSMO_GPRS_CS_NONE] = L1CTL_CS_NONE, - [OSMO_GPRS_CS1] = L1CTL_CS1, - [OSMO_GPRS_CS2] = L1CTL_CS2, - [OSMO_GPRS_CS3] = L1CTL_CS3, - [OSMO_GPRS_CS4] = L1CTL_CS4, - [OSMO_GPRS_MCS1] = L1CTL_MCS1, - [OSMO_GPRS_MCS2] = L1CTL_MCS2, - [OSMO_GPRS_MCS3] = L1CTL_MCS3, - [OSMO_GPRS_MCS4] = L1CTL_MCS4, - [OSMO_GPRS_MCS5] = L1CTL_MCS5, - [OSMO_GPRS_MCS6] = L1CTL_MCS6, - [OSMO_GPRS_MCS7] = L1CTL_MCS7, - [OSMO_GPRS_MCS8] = L1CTL_MCS8, - [OSMO_GPRS_MCS9] = L1CTL_MCS9, -}; - -static int get_l1ctl_cs_by_osmo(enum osmo_gprs_cs in) -{ - if (in >= ARRAY_SIZE(l1ctl_cs_by_osmo)) - return -1; - return l1ctl_cs_by_osmo[in]; -} - -static void l1ctl_rx_data_tbf_req(struct l1_model_ms *ms, struct msgb *msg) -{ - struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; - struct l1ctl_info_ul_tbf *udt = (struct l1ctl_info_ul_tbf *) l1h->data; - enum osmo_gprs_cs osmo_cs; - int block_size; - - msg->l2h = udt->payload; - - LOGPMS(DL1P, LOGL_ERROR, ms, "Rx L1CTL_DATA_TBF_REQ (tbf_id=%d, data=%s)\n", - udt->tbf_nr, osmo_hexdump(msg->l2h, msgb_l2len(msg))); - if (udt->tbf_nr != 0) { - LOGPMS(DL1C, LOGL_ERROR, ms, "TBF_NR != 0 not supported yet!\n"); - return; - } - - if (ms->state.state != MS_STATE_TBF) { - LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_TBF_REQ in state != TBF\n"); - return; - } - - osmo_cs = get_l1ctl_cs_by_osmo(udt->coding_scheme); - if (osmo_cs < 0) { - LOGPMS(DL1P, LOGL_ERROR, ms, "DATA_RBF_REQ with invalid CS\n"); - return; - } - block_size = osmo_gprs_ul_block_size_bytes(osmo_cs); - - if (msgb_l2len(msg) < block_size) { - int pad_len = block_size - msgb_l2len(msg); - uint8_t *pad = msgb_put(msg, pad_len); - memset(pad, GSM_MACBLOCK_PADDING, pad_len); - } - - msgb_enqueue(&ms->state.tbf.ul.tx_queue, msg); -} - /*************************************************************** * L1CTL TX ROUTINES ******************************************* * For more routines check the respective handler classes ****** * like virt_prim_rach.c *************************************** ***************************************************************/ -static void l1ctl_tx_tbf_cfg_conf(struct l1_model_ms *ms, const struct l1ctl_tbf_cfg_req *in) -{ - struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TBF_CFG_CONF); - struct l1ctl_tbf_cfg_req *out; - - /* copy over the data from the request */ - out = (struct l1ctl_tbf_cfg_req *) msgb_put(msg, sizeof(*out)); - *out = *in; - - l1ctl_sap_tx_to_l23_inst(ms, msg); -} - /** * @brief Transmit L1CTL_RESET_IND or L1CTL_RESET_CONF to layer 23. * diff --git a/src/host/virt_phy/src/l1ctl_sock.c b/src/host/virt_phy/src/l1ctl_sock.c index 7951f46e..089d9caf 100644 --- a/src/host/virt_phy/src/l1ctl_sock.c +++ b/src/host/virt_phy/src/l1ctl_sock.c @@ -40,8 +40,8 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/socket.h> -#include <virtphy/l1ctl_sock.h> -#include <virtphy/logging.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> +#include <osmocom/bb/virtphy/logging.h> #define L1CTL_SOCK_MSGB_SIZE 256 diff --git a/src/host/virt_phy/src/l1gprs.c b/src/host/virt_phy/src/l1gprs.c new file mode 120000 index 00000000..0185f68b --- /dev/null +++ b/src/host/virt_phy/src/l1gprs.c @@ -0,0 +1 @@ +../../../shared/l1gprs.c
\ No newline at end of file diff --git a/src/host/virt_phy/src/logging.c b/src/host/virt_phy/src/logging.c index 11a21a37..fc37205f 100644 --- a/src/host/virt_phy/src/logging.c +++ b/src/host/virt_phy/src/logging.c @@ -15,53 +15,49 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/utils.h> #include <osmocom/core/application.h> -#include <virtphy/logging.h> +#include <osmocom/bb/virtphy/logging.h> static const char* l1ctlPrimNames[] = { - "_L1CTL_NONE", - "L1CTL_FBSB_REQ", - "L1CTL_FBSB_CONF", - "L1CTL_DATA_IND", - "L1CTL_RACH_REQ", - "L1CTL_DM_EST_REQ", - "L1CTL_DATA_REQ", - "L1CTL_RESET_IND", - "L1CTL_PM_REQ", - "L1CTL_PM_CONF", - "L1CTL_ECHO_REQ", - "L1CTL_ECHO_CONF", - "L1CTL_RACH_CONF", - "L1CTL_RESET_REQ", - "L1CTL_RESET_CONF", - "L1CTL_DATA_CONF", - "L1CTL_CCCH_MODE_REQ", - "L1CTL_CCCH_MODE_CONF", - "L1CTL_DM_REL_REQ", - "L1CTL_PARAM_REQ", - "L1CTL_DM_FREQ_REQ", - "L1CTL_CRYPTO_REQ", - "L1CTL_SIM_REQ", - "L1CTL_SIM_CONF", - "L1CTL_TCH_MODE_REQ", - "L1CTL_TCH_MODE_CONF", - "L1CTL_NEIGH_PM_REQ", - "L1CTL_NEIGH_PM_IND", - "L1CTL_TRAFFIC_REQ", - "L1CTL_TRAFFIC_CONF", - "L1CTL_TRAFFIC_IND", - "L1CTL_BURST_IND", - "L1CTL_TBF_CFG_REQ", - "L1CTL_TBF_CFG_CONF", - "L1CTL_DATA_TBF_REQ", - "L1CTL_DATA_TBF_CONF" + "_L1CTL_NONE", + "L1CTL_FBSB_REQ", + "L1CTL_FBSB_CONF", + "L1CTL_DATA_IND", + "L1CTL_RACH_REQ", + "L1CTL_DM_EST_REQ", + "L1CTL_DATA_REQ", + "L1CTL_RESET_IND", + "L1CTL_PM_REQ", + "L1CTL_PM_CONF", + "L1CTL_ECHO_REQ", + "L1CTL_ECHO_CONF", + "L1CTL_RACH_CONF", + "L1CTL_RESET_REQ", + "L1CTL_RESET_CONF", + "L1CTL_DATA_CONF", + "L1CTL_CCCH_MODE_REQ", + "L1CTL_CCCH_MODE_CONF", + "L1CTL_DM_REL_REQ", + "L1CTL_PARAM_REQ", + "L1CTL_DM_FREQ_REQ", + "L1CTL_CRYPTO_REQ", + "L1CTL_SIM_REQ", + "L1CTL_SIM_CONF", + "L1CTL_TCH_MODE_REQ", + "L1CTL_TCH_MODE_CONF", + "L1CTL_NEIGH_PM_REQ", + "L1CTL_NEIGH_PM_IND", + "L1CTL_TRAFFIC_REQ", + "L1CTL_TRAFFIC_CONF", + "L1CTL_TRAFFIC_IND", + "L1CTL_BURST_IND", + "L1CTL_GPRS_UL_TBF_CFG_REQ", + "L1CTL_GPRS_DL_TBF_CFG_REQ", + "L1CTL_GPRS_UL_BLOCK_REQ", + "L1CTL_GPRS_DL_BLOCK_IND", }; static const struct log_info_cat default_categories[] = { @@ -70,28 +66,35 @@ static const struct log_info_cat default_categories[] = { .description = "Layer 1 Control", .color = "\033[1;31m", .enabled = 1, - .loglevel = LOGL_INFO, + .loglevel = LOGL_NOTICE, }, [DL1P] = { .name = "DL1P", .description = "Layer 1 Data", .color = "\033[1;31m", .enabled = 1, - .loglevel = LOGL_INFO, + .loglevel = LOGL_NOTICE, }, [DVIRPHY] = { .name = "DVIRPHY", .description = "Virtual Layer 1 Interface", .color = "\033[1;31m", .enabled = 1, - .loglevel = LOGL_INFO, + .loglevel = LOGL_NOTICE, + }, + [DGPRS] = { + .name = "DGPRS", + .description = "L1 GPRS (MAC leyer)", + .color = "\033[1;31m", + .enabled = 1, + .loglevel = LOGL_NOTICE, }, [DMAIN] = { .name = "DMAIN", .description = "Main Program / Data Structures", .color = "\033[1;32m", .enabled = 1, - .loglevel = LOGL_INFO, + .loglevel = LOGL_NOTICE, }, }; @@ -104,25 +107,21 @@ const struct log_info ms_log_info = { /** * Initialize the logging system for the virtual physical layer. */ -int ms_log_init(char *cat_mask) +int ms_log_init(void *ctx, const char *cat_mask) { - struct log_target *stderr_target; + int rc; - log_init(&ms_log_info, NULL); - stderr_target = log_target_create_stderr(); - if (!stderr) - return -1; + rc = osmo_init_logging2(ctx, &ms_log_info); + OSMO_ASSERT(rc == 0); - log_add_target(stderr_target); - log_set_all_filter(stderr_target, 1); - //log_set_log_level(stderr_target, 1); - log_set_print_filename2(stderr_target, LOG_FILENAME_PATH); - log_set_use_color(stderr_target, 0); - log_set_print_timestamp(stderr_target, 1); - log_set_print_category_hex(stderr_target, 0); - log_set_print_category(stderr_target, 1); + //log_set_log_level(osmo_stderr_target, 1); + log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_PATH); + log_set_use_color(osmo_stderr_target, 0); + log_set_print_timestamp(osmo_stderr_target, 1); + log_set_print_category_hex(osmo_stderr_target, 0); + log_set_print_category(osmo_stderr_target, 1); if (cat_mask) - log_parse_category_mask(stderr_target, cat_mask); + log_parse_category_mask(osmo_stderr_target, cat_mask); return 0; } diff --git a/src/host/virt_phy/src/shared/osmo_mcast_sock.c b/src/host/virt_phy/src/shared/osmo_mcast_sock.c index d0c5b6df..d3ae4fe9 100644 --- a/src/host/virt_phy/src/shared/osmo_mcast_sock.c +++ b/src/host/virt_phy/src/shared/osmo_mcast_sock.c @@ -1,14 +1,16 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/select.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <talloc.h> #include <unistd.h> -#include <virtphy/osmo_mcast_sock.h> + +#include <osmocom/core/socket.h> +#include <osmocom/core/select.h> + +#include <osmocom/bb/virtphy/osmo_mcast_sock.h> /* server socket is what we use for transmission. It is not subscribed * to a multicast group or locally bound, but it is just a normal UDP diff --git a/src/host/virt_phy/src/shared/virtual_um.c b/src/host/virt_phy/src/shared/virtual_um.c index ef3ad379..e55bb034 100644 --- a/src/host/virt_phy/src/shared/virtual_um.c +++ b/src/host/virt_phy/src/shared/virtual_um.c @@ -19,17 +19,18 @@ * */ +#include <unistd.h> +#include <errno.h> + #include <osmocom/core/select.h> #include <osmocom/core/utils.h> #include <osmocom/core/socket.h> #include <osmocom/core/gsmtap.h> #include <osmocom/core/msgb.h> #include <osmocom/core/talloc.h> -#include <virtphy/osmo_mcast_sock.h> -#include <virtphy/virtual_um.h> -#include <unistd.h> -#include <errno.h> +#include <osmocom/bb/virtphy/osmo_mcast_sock.h> +#include <osmocom/bb/virtphy/virtual_um.h> /** * Virtual UM interface file descriptor callback. diff --git a/src/host/virt_phy/src/virt_l1_model.c b/src/host/virt_phy/src/virt_l1_model.c index 5738bed6..704a54cd 100644 --- a/src/host/virt_phy/src/virt_l1_model.c +++ b/src/host/virt_phy/src/virt_l1_model.c @@ -19,11 +19,12 @@ * */ -#include <virtphy/virt_l1_model.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/logging.h> #include <talloc.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/logging.h> + static uint32_t next_ms_nr; struct l1_model_ms *l1_model_ms_init(void *ctx, struct l1ctl_sock_client *lsc, struct virt_um_inst *vui) diff --git a/src/host/virt_phy/src/virt_l1_sched_simple.c b/src/host/virt_phy/src/virt_l1_sched_simple.c index 486d319a..3cad2ce0 100644 --- a/src/host/virt_phy/src/virt_l1_sched_simple.c +++ b/src/host/virt_phy/src/virt_l1_sched_simple.c @@ -18,13 +18,15 @@ * */ -#include <virtphy/virt_l1_sched.h> -#include <osmocom/core/linuxlist.h> -#include <virtphy/virt_l1_model.h> -#include <virtphy/logging.h> #include <time.h> #include <talloc.h> +#include <osmocom/core/linuxlist.h> + +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/virtphy/logging.h> + /** * @brief Start scheduler thread based on current gsm time from model */ diff --git a/src/host/virt_phy/src/virt_prim_data.c b/src/host/virt_phy/src/virt_prim_data.c index 656ff800..d8897915 100644 --- a/src/host/virt_phy/src/virt_prim_data.c +++ b/src/host/virt_phy/src/virt_prim_data.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -31,12 +27,12 @@ #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/core/msgb.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/virt_l1_sched.h> -#include <virtphy/logging.h> -#include <virtphy/gsmtapl1_if.h> -#include <l1ctl_proto.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/l1ctl_proto.h> /** * @brief Handler callback function for DATA request. @@ -81,8 +77,8 @@ void l1ctl_rx_data_req(struct l1_model_ms *ms, struct msgb *msg) } void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id, - uint8_t chan_nr, uint32_t fn, uint8_t snr, - uint8_t signal_dbm, uint8_t num_biterr, uint8_t fire_crc) + uint8_t chan_nr, uint32_t fn, uint8_t snr, + uint8_t rxlev, uint8_t num_biterr, uint8_t fire_crc) { struct msgb *l1ctl_msg = NULL; struct l1ctl_data_ind * l1di; @@ -98,7 +94,7 @@ void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, l1dl->chan_nr = chan_nr; l1dl->frame_nr = htonl(fn); l1dl->snr = snr; - l1dl->rx_level = signal_dbm; + l1dl->rx_level = rxlev; l1dl->num_biterr = 0; /* no biterrors */ l1dl->fire_crc = 0; diff --git a/src/host/virt_phy/src/virt_prim_fbsb.c b/src/host/virt_phy/src/virt_prim_fbsb.c index c14a4485..d012036f 100644 --- a/src/host/virt_phy/src/virt_prim_fbsb.c +++ b/src/host/virt_phy/src/virt_prim_fbsb.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -29,11 +25,12 @@ #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/core/msgb.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/virt_l1_sched.h> #include <osmocom/core/gsmtap.h> -#include <virtphy/logging.h> -#include <l1ctl_proto.h> + +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/l1ctl_proto.h> static uint16_t sync_count = 0; diff --git a/src/host/virt_phy/src/virt_prim_pdch.c b/src/host/virt_phy/src/virt_prim_pdch.c new file mode 100644 index 00000000..3fa7977b --- /dev/null +++ b/src/host/virt_phy/src/virt_prim_pdch.c @@ -0,0 +1,108 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/gsm/gsm0502.h> + +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/virtphy/logging.h> + +#include <osmocom/bb/l1ctl_proto.h> +#include <osmocom/bb/l1gprs.h> + +void l1ctl_rx_gprs_uldl_tbf_cfg_req(struct l1_model_ms *ms, struct msgb *msg) +{ + const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data; + + if (OSMO_UNLIKELY(ms->gprs == NULL)) { + LOGPMS(DL1C, LOGL_ERROR, ms, "l1gprs is not initialized\n"); + return; + } + + msg->l1h = msgb_pull(msg, sizeof(*l1h)); + + if (l1h->msg_type == L1CTL_GPRS_UL_TBF_CFG_REQ) + l1gprs_handle_ul_tbf_cfg_req(ms->gprs, msg); + else + l1gprs_handle_dl_tbf_cfg_req(ms->gprs, msg); +} + +void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg) +{ + const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data; + struct l1gprs_prim_ul_block_req req; + + if (OSMO_UNLIKELY(ms->gprs == NULL)) { + LOGPMS(DL1P, LOGL_ERROR, ms, "l1gprs is not initialized\n"); + msgb_free(msg); + return; + } + + msg->l1h = (void *)l1h->data; + if (l1gprs_handle_ul_block_req(ms->gprs, &req, msg) != 0) { + msgb_free(msg); + return; + } + msg->l2h = (void *)&req.data[0]; + + virt_l1_sched_schedule(ms, msg, req.hdr.fn, req.hdr.tn, + &gsmtapl1_tx_to_virt_um_inst); +} + +void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg, + uint32_t fn, uint8_t tn, uint8_t rxlev) +{ + struct l1gprs_prim_dl_block_ind ind; + struct msgb *nmsg; + uint8_t usf = 0xff; + + if (ms->gprs == NULL) + return; + + ind = (struct l1gprs_prim_dl_block_ind) { + .hdr = { + .fn = fn, + .tn = tn, + }, + .meas = { + .ber10k = 0, /* perfect Um, no errors */ + .ci_cb = 180, /* 18 dB */ + .rx_lev = rxlev, + }, + .data = msgb_data(msg), + .data_len = msgb_length(msg), + }; + + nmsg = l1gprs_handle_dl_block_ind(ms->gprs, &ind, &usf); + if (nmsg != NULL) + l1ctl_sap_tx_to_l23_inst(ms, nmsg); + /* Every fn % 13 == 12 we have either a PTCCH or an IDLE slot, thus + * every fn % 13 == 8 we add 5 frames, or 4 frames othrwise. The + * resulting value is first fn of the next block. */ + const uint32_t rts_fn = GSM_TDMA_FN_SUM(fn, (fn % 13 == 8) ? 5 : 4); + nmsg = l1gprs_handle_rts_ind(ms->gprs, rts_fn, tn, usf); + if (nmsg != NULL) + l1ctl_sap_tx_to_l23_inst(ms, nmsg); +} diff --git a/src/host/virt_phy/src/virt_prim_pm.c b/src/host/virt_phy/src/virt_prim_pm.c index 08d9b054..5883bbbe 100644 --- a/src/host/virt_phy/src/virt_prim_pm.c +++ b/src/host/virt_phy/src/virt_prim_pm.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -26,14 +22,15 @@ #include <string.h> #include <stdlib.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/protocol/gsm_08_58.h> #include <osmocom/core/msgb.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/virt_l1_sched.h> #include <osmocom/core/gsmtap.h> -#include <virtphy/logging.h> -#include <l1ctl_proto.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> + +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/l1ctl_proto.h> /** * @brief Change the signal strength for a given arfcn. diff --git a/src/host/virt_phy/src/virt_prim_rach.c b/src/host/virt_phy/src/virt_prim_rach.c index 94076cf9..d12e63c6 100644 --- a/src/host/virt_phy/src/virt_prim_rach.c +++ b/src/host/virt_phy/src/virt_prim_rach.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -27,16 +23,17 @@ #include <string.h> #include <stdlib.h> +#include <osmocom/core/msgb.h> #include <osmocom/gsm/rsl.h> #include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/gsm0502.h> #include <osmocom/gsm/protocol/gsm_08_58.h> -#include <osmocom/core/msgb.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/virt_l1_sched.h> -#include <virtphy/logging.h> -#include <virtphy/gsmtapl1_if.h> -#include <l1ctl_proto.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/l1ctl_proto.h> /* use if we have a combined uplink (RACH, SDCCH, ...) (see * http://www.rfwireless-world.com/Terminology/GSM-combined-channel-configuration.html) @@ -79,36 +76,37 @@ void l1ctl_rx_rach_req(struct l1_model_ms *ms, struct msgb *msg) struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload; uint32_t fn_sched; - uint8_t ts = 1; /* FIXME mostly, ts 1 is used for rach, where can i get that info? System info? */ uint16_t offset = ntohs(rach_req->offset); LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_RACH_REQ (ra=0x%02x, offset=%d combined=%d)\n", rach_req->ra, offset, rach_req->combined); - if (rach_req->ra == 0x03) - fn_sched = 42; - /* set ra data to msg (8bits, the 11bit option is not used for GSM) */ msg->l2h = msgb_put(msg, sizeof(uint8_t)); *msg->l2h = rach_req->ra; - /* chan_nr need to be encoded here, as it is not set by l23 for - * the rach request, but needed by virt um */ - ul->chan_nr = rsl_enc_chan_nr(RSL_CHAN_RACH, 0, ts); - ul->link_id = LID_DEDIC; + /* use the indicated RSL chan_nr/link_id, if provided */ + if (ul->chan_nr == 0x00) { + LOGPMS(DL1C, LOGL_NOTICE, ms, + "The UL info header is empty, assuming RACH is on TS0\n"); + ul->chan_nr = RSL_CHAN_RACH; + ul->link_id = LID_DEDIC; + } /* sched fn calculation if we have a combined ccch channel configuration */ if (rach_req->combined) { /* add elapsed RACH slots to offset */ offset += t3_to_rach_comb[l1s->current_time.t3]; /* offset is the number of RACH slots in the future */ - fn_sched = l1s->current_time.fn - l1s->current_time.t3; + fn_sched = GSM_TDMA_FN_SUB(l1s->current_time.fn, l1s->current_time.t3); fn_sched += offset / 27 * 51; fn_sched += rach_to_t3_comb[offset % 27]; + fn_sched %= GSM_TDMA_HYPERFRAME; } else - fn_sched = l1s->current_time.fn + offset; + fn_sched = GSM_TDMA_FN_SUM(l1s->current_time.fn, offset); - virt_l1_sched_schedule(ms, msg, fn_sched, ts, &virt_l1_sched_handler_cb); + virt_l1_sched_schedule(ms, msg, fn_sched, ul->chan_nr & 0x07, + &virt_l1_sched_handler_cb); } /** diff --git a/src/host/virt_phy/src/virt_prim_traffic.c b/src/host/virt_phy/src/virt_prim_traffic.c index 0e08a126..778d67a3 100644 --- a/src/host/virt_phy/src/virt_prim_traffic.c +++ b/src/host/virt_phy/src/virt_prim_traffic.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -27,16 +23,16 @@ #include <string.h> #include <stdlib.h> +#include <osmocom/core/msgb.h> #include <osmocom/gsm/rsl.h> #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/protocol/gsm_08_58.h> -#include <osmocom/core/msgb.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/virt_l1_sched.h> -#include <virtphy/logging.h> -#include <virtphy/gsmtapl1_if.h> -#include <l1ctl_proto.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/l1ctl_proto.h> /** * @brief Handler callback function for TRAFFIC request. @@ -79,7 +75,7 @@ void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg) } void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id, - uint8_t chan_nr, uint32_t fn, uint8_t snr, uint8_t signal_dbm, + uint8_t chan_nr, uint32_t fn, uint8_t snr, uint8_t rxlev, uint8_t num_biterr, uint8_t fire_crc) { struct msgb *l1ctl_msg = NULL; @@ -99,7 +95,7 @@ void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arf l1dl->chan_nr = chan_nr; l1dl->frame_nr = htonl(fn); l1dl->snr = snr; - l1dl->rx_level = signal_dbm; + l1dl->rx_level = rxlev; l1dl->num_biterr = 0; /* no biterrors */ l1dl->fire_crc = 0; diff --git a/src/host/virt_phy/src/virtphy.c b/src/host/virt_phy/src/virtphy.c index 412da9c8..0aa21adc 100644 --- a/src/host/virt_phy/src/virtphy.c +++ b/src/host/virt_phy/src/virtphy.c @@ -20,10 +20,6 @@ * */ -#include <osmocom/core/msgb.h> -#include <osmocom/core/select.h> -#include <osmocom/core/gsmtap.h> -#include <osmocom/core/application.h> #include <stdint.h> #include <string.h> #include <stdio.h> @@ -31,15 +27,22 @@ #include <getopt.h> #include <errno.h> #include <signal.h> -#include <virtphy/virtual_um.h> -#include <virtphy/l1ctl_sock.h> -#include <virtphy/virt_l1_model.h> -#include <virtphy/l1ctl_sap.h> -#include <virtphy/gsmtapl1_if.h> -#include <virtphy/logging.h> -#include <virtphy/virt_l1_sched.h> -#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DMAIN,1" +#include <osmocom/core/msgb.h> +#include <osmocom/core/select.h> +#include <osmocom/core/gsmtap.h> +#include <osmocom/core/application.h> + +#include <osmocom/bb/virtphy/virtual_um.h> +#include <osmocom/bb/virtphy/l1ctl_sock.h> +#include <osmocom/bb/virtphy/virt_l1_model.h> +#include <osmocom/bb/virtphy/l1ctl_sap.h> +#include <osmocom/bb/virtphy/gsmtapl1_if.h> +#include <osmocom/bb/virtphy/logging.h> +#include <osmocom/bb/virtphy/virt_l1_sched.h> +#include <osmocom/bb/l1gprs.h> + +#define DEFAULT_LOG_MASK "DL1C,2:DL1P,2:DVIRPHY,2:DGPRS,1:DMAIN,1" /* this exists once in the program, and contains the state that we * only keep once: L1CTL server socket, GSMTAP/VirtUM socket */ @@ -62,12 +65,12 @@ static char *pm_timeout = NULL; static char *mcast_netdev = NULL; static int mcast_ttl = -1; -static void print_usage() +static void print_usage(void) { printf("Usage: virtphy\n"); } -static void print_help() +static void print_help(void) { printf(" Some useful help...\n"); printf(" -h --help This text.\n"); @@ -239,7 +242,8 @@ int main(int argc, char *argv[]) /* init loginfo */ handle_options(argc, argv); - ms_log_init(log_mask); + ms_log_init(tall_vphy_ctx, log_mask); + l1gprs_logging_init(DGPRS); LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer starting up...\n"); diff --git a/src/shared/l1gprs.c b/src/shared/l1gprs.c new file mode 100644 index 00000000..2bf759d5 --- /dev/null +++ b/src/shared/l1gprs.c @@ -0,0 +1,831 @@ +/* + * l1gprs - GPRS layer 1 implementation + * + * (C) 2022-2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * Author: Vadim Yanitskiy <vyanitskiy@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <errno.h> +#include <stdint.h> +#include <stdbool.h> + +#include <arpa/inet.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_44_060.h> +#include <osmocom/gsm/gsm0502.h> + +#include <osmocom/bb/l1ctl_proto.h> +#include <osmocom/bb/l1gprs.h> + +#define LOGP_GPRS(gprs, level, fmt, args...) \ + LOGP(l1gprs_log_cat, level, "%s" fmt, \ + (gprs)->log_prefix, ## args) + +#define LOGP_PDCH(pdch, level, fmt, args...) \ + LOGP_GPRS((pdch)->gprs, level, "(PDCH-%u) " fmt, \ + (pdch)->tn, ## args) + +#define LOG_TBF_CFG_REQ_FMT "tbf_ref=%u, slotmask=0x%02x, start_fn=%u" +#define LOG_TBF_CFG_REQ_ARGS(req) \ + (req)->tbf_ref, (req)->slotmask, ntohl((req)->start_fn) + +#define LOG_TBF_FMT "%cL-TBF#%03d(slotmask=0x%02x)" +#define LOG_TBF_ARGS(tbf) \ + (tbf)->uplink ? 'U' : 'D', (tbf)->tbf_ref, (tbf)->slotmask + +#define TDMA_FN_INVALID 0xffffffff + +static int l1gprs_log_cat = DLGLOBAL; + +enum gprs_rlcmac_block_type { + GPRS_RLCMAC_DATA_BLOCK = 0x00, + GPRS_RLCMAC_CONTROL_BLOCK = 0x01, + GPRS_RLCMAC_CONTROL_BLOCK_OPT = 0x02, + GPRS_RLCMAC_RESERVED = 0x03, +}; + +static struct l1gprs_tbf_pending_req *l1gprs_tbf_pending_req_alloc(void *talloc_ctx, + bool uplink, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn) +{ + struct l1gprs_tbf_pending_req *preq; + + preq = talloc(talloc_ctx, struct l1gprs_tbf_pending_req); + OSMO_ASSERT(preq != NULL); + + preq->uplink = uplink; + preq->tbf_ref = tbf_ref; + preq->slotmask = slotmask; + preq->start_fn = start_fn; + + return preq; +} + +static void l1gprs_tbf_pending_req_free(struct l1gprs_tbf_pending_req *preq) +{ + if (preq == NULL) + return; + llist_del(&preq->list); + talloc_free(preq); +} + + +static struct l1gprs_tbf *l1gprs_tbf_alloc(void *talloc_ctx, + bool uplink, uint8_t tbf_ref, + uint8_t slotmask) +{ + struct l1gprs_tbf *tbf; + + tbf = talloc(talloc_ctx, struct l1gprs_tbf); + OSMO_ASSERT(tbf != NULL); + + tbf->uplink = uplink; + tbf->tbf_ref = tbf_ref; + tbf->slotmask = slotmask; + + return tbf; +} + +static void l1gprs_tbf_free(struct l1gprs_tbf *tbf) +{ + if (tbf == NULL) + return; + llist_del(&tbf->list); + talloc_free(tbf); +} + +static struct l1gprs_tbf *_l1gprs_find_tbf(const struct llist_head *tbf_list, + bool uplink, uint8_t tbf_ref) +{ + struct l1gprs_tbf *tbf; + + llist_for_each_entry(tbf, tbf_list, list) { + if (tbf->uplink != uplink) + continue; + if (tbf->tbf_ref != tbf_ref) + continue; + return tbf; + } + + return NULL; +} + +static struct l1gprs_tbf *l1gprs_find_tbf(struct l1gprs_state *gprs, + bool uplink, uint8_t tbf_ref) +{ + struct l1gprs_tbf *tbf; + + if ((tbf = _l1gprs_find_tbf(&gprs->tbf_list, uplink, tbf_ref)) != NULL) + return tbf; + return NULL; +} + +static void l1gprs_register_tbf(struct l1gprs_state *gprs, + struct l1gprs_tbf *tbf) +{ + OSMO_ASSERT(tbf->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~tbf->slotmask & (1 << pdch->tn)) + continue; + + if (tbf->uplink) { + pdch->ul_tbf_count++; + } else { + pdch->dl_tbf_count++; + pdch->dl_tfi_mask |= (1 << tbf->dl_tfi); + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Linked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(tbf)); + + /* If just got first use: */ + if (l1gprs_pdch_use_count(pdch) == 1) { + if (gprs->pdch_changed_cb) + gprs->pdch_changed_cb(pdch, true); + } + } + + llist_add_tail(&tbf->list, &gprs->tbf_list); + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is registered as active\n", + LOG_TBF_ARGS(tbf)); +} + +static void l1gprs_update_tbf(struct l1gprs_state *gprs, struct l1gprs_tbf *tbf, uint8_t slotmask) +{ + OSMO_ASSERT(tbf->slotmask != 0x00); + OSMO_ASSERT(slotmask != 0x00); + + if (tbf->slotmask == slotmask) + return; /* No change at all, skip */ + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if ((tbf->slotmask & (1 << tn)) == (slotmask & (1 << tn))) + continue; /* No change, skip */ + + if (tbf->slotmask & (1 << tn)) { + /* slot previously set, remove it */ + if (tbf->uplink) { + OSMO_ASSERT(pdch->ul_tbf_count > 0); + pdch->ul_tbf_count--; + } else { + OSMO_ASSERT(pdch->dl_tbf_count > 0); + pdch->dl_tbf_count--; + pdch->dl_tfi_mask &= ~(1 << tbf->dl_tfi); + } + LOGP_PDCH(pdch, LOGL_DEBUG, "Unlinked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(tbf)); + /* If not more in use: */ + if (l1gprs_pdch_use_count(pdch) == 0) { + if (gprs->pdch_changed_cb) + gprs->pdch_changed_cb(pdch, false); + } + } else { + /* Slot was not set, add it */ + if (tbf->uplink) { + pdch->ul_tbf_count++; + } else { + pdch->dl_tbf_count++; + pdch->dl_tfi_mask |= (1 << tbf->dl_tfi); + } + LOGP_PDCH(pdch, LOGL_DEBUG, "Linked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(tbf)); + /* If just got first use: */ + if (l1gprs_pdch_use_count(pdch) == 1) { + if (gprs->pdch_changed_cb) + gprs->pdch_changed_cb(pdch, true); + } + } + } + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " slotmask updated 0x%02x -> 0x%02x\n", + LOG_TBF_ARGS(tbf), tbf->slotmask, slotmask); + + tbf->slotmask = slotmask; +} + +static void l1gprs_unregister_tbf(struct l1gprs_state *gprs, struct l1gprs_tbf *tbf) +{ + OSMO_ASSERT(tbf->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~tbf->slotmask & (1 << pdch->tn)) + continue; + + if (tbf->uplink) { + OSMO_ASSERT(pdch->ul_tbf_count > 0); + pdch->ul_tbf_count--; + } else { + OSMO_ASSERT(pdch->dl_tbf_count > 0); + pdch->dl_tbf_count--; + pdch->dl_tfi_mask &= ~(1 << tbf->dl_tfi); + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Unlinked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(tbf)); + + /* If not more in use: */ + if (l1gprs_pdch_use_count(pdch) == 0) { + if (gprs->pdch_changed_cb) + gprs->pdch_changed_cb(pdch, false); + } + } + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is unregistered and free()d\n", + LOG_TBF_ARGS(tbf)); + + l1gprs_tbf_free(tbf); +} + +static void l1gprs_add_tbf_pending_req(struct l1gprs_state *gprs, struct l1gprs_tbf_pending_req *preq) +{ + OSMO_ASSERT(preq->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~preq->slotmask & (1 << pdch->tn)) + continue; + + if (preq->uplink) { + pdch->pending_ul_tbf_count++; + } else { + pdch->pending_dl_tbf_count++; + /* We don't care about DL_TFI here, we don't want to activate it */ + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Linked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(preq)); + /* If just got first use: */ + if (l1gprs_pdch_use_count(pdch) == 1) { + if (gprs->pdch_changed_cb) + gprs->pdch_changed_cb(pdch, true); + } + } + + llist_add_tail(&preq->list, &gprs->tbf_list_pending); + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is added as pending (fn=%u)\n", + LOG_TBF_ARGS(preq), preq->start_fn); +} + +static void l1gprs_remove_tbf_pending_req(struct l1gprs_state *gprs, struct l1gprs_tbf_pending_req *preq) +{ + + OSMO_ASSERT(preq->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~preq->slotmask & (1 << pdch->tn)) + continue; + + if (preq->uplink) { + OSMO_ASSERT(pdch->pending_ul_tbf_count > 0); + pdch->pending_ul_tbf_count--; + } else { + OSMO_ASSERT(pdch->pending_dl_tbf_count > 0); + pdch->pending_dl_tbf_count--; + /* We don't care about DL_TFI here, we didn't activate them in first place */ + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Unlinked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(preq)); + /* Note: not calling gprs->pdch_changed_cb since no real + * activate / deactivate change can occur on lower layers as a + * consequence of moving a PDCH from pending to active, hence + * avoid triggering one active=false event here and immediately + * afterwards the opposite event when adding it as active: */ + } + + llist_del(&preq->list); + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is removed as pending (fn=%u)\n", + LOG_TBF_ARGS(preq), preq->start_fn); +} + +/* Check the list of pending TBFs and move those with expired Fn to the active list */ +static void l1gprs_check_pending_tbfs(struct l1gprs_state *gprs, uint32_t fn) +{ + struct l1gprs_tbf_pending_req *preq, *tmp; + struct l1gprs_tbf *tbf; + + llist_for_each_entry_safe(preq, tmp, &gprs->tbf_list_pending, list) { + if (gsm0502_fncmp(fn, preq->start_fn) < 0) + continue; + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " becomes active (current_fn=%u, start_fn=%u)\n", + LOG_TBF_ARGS(preq), fn, preq->start_fn); + + l1gprs_remove_tbf_pending_req(gprs, preq); + + /* If this tbf already exists in the main list, simply update its timeslot: */ + tbf = _l1gprs_find_tbf(&gprs->tbf_list, preq->uplink, preq->tbf_ref); + if (tbf) { + l1gprs_update_tbf(gprs, tbf, preq->slotmask); + tbf->dl_tfi = preq->dl_tfi; + } else { + tbf = l1gprs_tbf_alloc(gprs, preq->uplink, preq->tbf_ref, preq->slotmask); + tbf->dl_tfi = preq->dl_tfi; + l1gprs_register_tbf(gprs, tbf); + } + talloc_free(preq); + } +} + +#define L1GPRS_L1CTL_MSGB_SIZE 256 +#define L1GPRS_L1CTL_MSGB_HEADROOM 32 + +static struct msgb *l1gprs_l1ctl_msgb_alloc(uint8_t msg_type) +{ + struct l1ctl_hdr *l1h; + struct msgb *msg; + + msg = msgb_alloc_headroom(L1GPRS_L1CTL_MSGB_SIZE, + L1GPRS_L1CTL_MSGB_HEADROOM, + "l1gprs_l1ctl_msg"); + if (msg == NULL) + return NULL; + + msg->l1h = msgb_put(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *)msg->l1h; + l1h->msg_type = msg_type; + + return msg; +} + +static bool l1gprs_pdch_filter_dl_block(const struct l1gprs_pdch *pdch, + const uint8_t *data) +{ + enum gprs_rlcmac_block_type block_type = data[0] >> 6; + uint8_t dl_tfi; + + switch (block_type) { + case GPRS_RLCMAC_DATA_BLOCK: + /* see 3GPP TS 44.060, section 10.2.1 */ + dl_tfi = (data[1] >> 1) & 0x1f; + break; + case GPRS_RLCMAC_CONTROL_BLOCK_OPT: + /* see 3GPP TS 44.060, section 10.3.1 */ + dl_tfi = (data[2] >> 1) & 0x1f; + break; + case GPRS_RLCMAC_CONTROL_BLOCK: + /* no optional octets */ + return true; + default: + LOGP_PDCH(pdch, LOGL_NOTICE, + "Rx Downlink block with unknown payload (0x%0x)\n", + block_type); + return false; + } + + if (pdch->dl_tfi_mask & (1 << dl_tfi)) + return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////// + +void l1gprs_logging_init(int logc) +{ + l1gprs_log_cat = logc; +} + +struct l1gprs_state *l1gprs_state_alloc(void *ctx, const char *log_prefix, void *priv) +{ + struct l1gprs_state *gprs; + + gprs = talloc_zero(ctx, struct l1gprs_state); + if (gprs == NULL) + return NULL; + + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + pdch->tn = tn; + pdch->gprs = gprs; + } + + INIT_LLIST_HEAD(&gprs->tbf_list); + INIT_LLIST_HEAD(&gprs->tbf_list_pending); + + if (log_prefix == NULL) + gprs->log_prefix = talloc_asprintf(gprs, "l1gprs[0x%p]: ", gprs); + else + gprs->log_prefix = talloc_strdup(gprs, log_prefix); + gprs->priv = priv; + + return gprs; +} + +void l1gprs_state_free(struct l1gprs_state *gprs) +{ + if (gprs == NULL) + return; + + while (!llist_empty(&gprs->tbf_list)) { + struct l1gprs_tbf *tbf; + + tbf = llist_first_entry(&gprs->tbf_list, struct l1gprs_tbf, list); + LOGP_GPRS(gprs, LOGL_DEBUG, + "%s(): " LOG_TBF_FMT " is free()d\n", + __func__, LOG_TBF_ARGS(tbf)); + l1gprs_tbf_free(tbf); + } + + while (!llist_empty(&gprs->tbf_list_pending)) { + struct l1gprs_tbf_pending_req *preq; + + preq = llist_first_entry(&gprs->tbf_list_pending, struct l1gprs_tbf_pending_req, list); + LOGP_GPRS(gprs, LOGL_DEBUG, + "%s(): " LOG_TBF_FMT " is free()d\n", + __func__, LOG_TBF_ARGS(preq)); + l1gprs_tbf_pending_req_free(preq); + } + + talloc_free(gprs->log_prefix); + talloc_free(gprs); +} + +void l1gprs_state_set_pdch_changed_cb(struct l1gprs_state *gprs, l1gprs_pdch_changed_t pdch_changed_cb) +{ + gprs->pdch_changed_cb = pdch_changed_cb; +} + +int l1gprs_handle_ul_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg) +{ + const struct l1ctl_gprs_ul_tbf_cfg_req *req = (void *)msg->l1h; + struct l1gprs_tbf *tbf = NULL; + + OSMO_ASSERT(req != NULL); + + if (msgb_l1len(msg) < sizeof(*req)) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed Uplink TBF config (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*req)); + return -EINVAL; + } + + LOGP_GPRS(gprs, LOGL_INFO, + "Rx UL TBF config: " LOG_TBF_CFG_REQ_FMT "\n", + LOG_TBF_CFG_REQ_ARGS(req)); + + if (req->slotmask != 0x00) { + uint32_t start_fn = ntohl(req->start_fn); + if (start_fn != TDMA_FN_INVALID) { + /* Create a temporary tbf and keep it in a separate + * list. It will be moved/merged into the main list at + * start_fn time. */ + struct l1gprs_tbf_pending_req *preq; + preq = l1gprs_tbf_pending_req_alloc(gprs, true, req->tbf_ref, + req->slotmask, start_fn); + l1gprs_add_tbf_pending_req(gprs, preq); + return 0; + } + + tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref); + if (tbf) { + l1gprs_update_tbf(gprs, tbf, req->slotmask); + } else { + tbf = l1gprs_tbf_alloc(gprs, true, req->tbf_ref, req->slotmask); + l1gprs_register_tbf(gprs, tbf); + } + } else { + tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref); + if (tbf == NULL) { + LOGP_GPRS(gprs, LOGL_ERROR, "%s(): " LOG_TBF_FMT " not found\n", + __func__, 'U', req->tbf_ref, req->slotmask); + return -ENOENT; + } + l1gprs_unregister_tbf(gprs, tbf); + } + + return 0; +} + +int l1gprs_handle_dl_tbf_cfg_req(struct l1gprs_state *gprs, const struct msgb *msg) +{ + const struct l1ctl_gprs_dl_tbf_cfg_req *req = (void *)msg->l1h; + struct l1gprs_tbf *tbf = NULL; + + OSMO_ASSERT(req != NULL); + + if (msgb_l1len(msg) < sizeof(*req)) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed Downlink TBF config (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*req)); + return -EINVAL; + } + + LOGP_GPRS(gprs, LOGL_INFO, + "Rx DL TBF config: " LOG_TBF_CFG_REQ_FMT ", dl_tfi=%u\n", + LOG_TBF_CFG_REQ_ARGS(req), req->dl_tfi); + + if (req->dl_tfi > 31) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Invalid DL TFI %u (shall be in range 0..31)\n", + req->dl_tfi); + return -EINVAL; + } + + if (req->slotmask != 0x00) { + uint32_t start_fn = ntohl(req->start_fn); + if (start_fn != TDMA_FN_INVALID) { + /* Create a temporary tbf and keep it in a separate + * list. It will be moved/merged into the main list at + * start_fn time. */ + struct l1gprs_tbf_pending_req *preq; + preq = l1gprs_tbf_pending_req_alloc(gprs, false, req->tbf_ref, + req->slotmask, start_fn); + preq->dl_tfi = req->dl_tfi; + l1gprs_add_tbf_pending_req(gprs, preq); + return 0; + } + + tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref); + if (tbf) { + l1gprs_update_tbf(gprs, tbf, req->slotmask); + } else { + tbf = l1gprs_tbf_alloc(gprs, false, req->tbf_ref, + req->slotmask); + tbf->dl_tfi = req->dl_tfi; + l1gprs_register_tbf(gprs, tbf); + } + } else { + tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref); + if (tbf == NULL) { + LOGP_GPRS(gprs, LOGL_ERROR, "%s(): " LOG_TBF_FMT " not found\n", + __func__, 'D', req->tbf_ref, req->slotmask); + return -ENOENT; + } + l1gprs_unregister_tbf(gprs, tbf); + } + + return 0; +} + +int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, + struct l1gprs_prim_ul_block_req *req, + const struct msgb *msg) +{ + const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h; + const struct l1gprs_pdch *pdch = NULL; + size_t data_len; + uint32_t fn; + + OSMO_ASSERT(l1br != NULL); + + if (OSMO_UNLIKELY(msgb_l1len(msg) < sizeof(*l1br))) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed UL BLOCK.req (len=%u < %zu)\n", + msgb_l1len(msg), sizeof(*l1br)); + return -EINVAL; + } + fn = ntohl(l1br->hdr.fn); + if (OSMO_UNLIKELY(l1br->hdr.tn >= ARRAY_SIZE(gprs->pdch))) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed UL BLOCK.req (fn=%u, tn=%u)\n", + fn, l1br->hdr.tn); + return -EINVAL; + } + + pdch = &gprs->pdch[l1br->hdr.tn]; + data_len = msgb_l1len(msg) - sizeof(*l1br); + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx UL BLOCK.req (fn=%u, len=%zu): %s\n", + fn, data_len, osmo_hexdump(l1br->data, data_len)); + + if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) { + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx UL BLOCK.req (fn=%u, len=%zu), but this PDCH has no configured TBFs\n", + fn, data_len); + return -EINVAL; + } + + *req = (struct l1gprs_prim_ul_block_req) { + .hdr = { + .fn = fn, + .tn = l1br->hdr.tn, + }, + .data = &l1br->data[0], + .data_len = data_len, + }; + + return 0; +} + +struct msgb *l1gprs_handle_ul_block_cnf(struct l1gprs_state *gprs, + uint32_t fn, uint8_t tn, + const uint8_t *data, + size_t data_len) +{ + const struct l1gprs_pdch *pdch = NULL; + struct l1ctl_gprs_ul_block_cnf *l1bc; + struct msgb *msg; + + OSMO_ASSERT(tn < ARRAY_SIZE(gprs->pdch)); + pdch = &gprs->pdch[tn]; + + LOGP_PDCH(pdch, LOGL_DEBUG, "Rx UL BLOCK.cnf (fn=%u)\n", fn); + + if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) { + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx UL BLOCK.cnf (fn=%u), but this PDCH has no active TBFs\n", + fn); + return NULL; + } + + msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_UL_BLOCK_CNF); + if (OSMO_UNLIKELY(msg == NULL)) { + LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n"); + return NULL; + } + + l1bc = (void *)msgb_put(msg, sizeof(*l1bc)); + *l1bc = (struct l1ctl_gprs_ul_block_cnf) { + .fn = htonl(fn), + .tn = tn, + }; + + if (data != NULL && data_len > 0) + memcpy(msgb_put(msg, data_len), data, data_len); + + return msg; +} + +/* Check if a Downlink block is a PTCCH/D (see 3GPP TS 45.002, table 6) */ +#define BLOCK_IND_IS_PTCCH(ind) \ + (((ind)->hdr.fn % 104) == 12) + +struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs, + const struct l1gprs_prim_dl_block_ind *ind, + uint8_t *usf) +{ + const struct l1gprs_pdch *pdch = NULL; + struct l1ctl_gprs_dl_block_ind *l1bi; + enum osmo_gprs_cs cs; + struct msgb *msg; + + if (OSMO_UNLIKELY(ind->hdr.tn >= ARRAY_SIZE(gprs->pdch))) { + LOGP_GPRS(gprs, LOGL_ERROR, + "Rx malformed DL BLOCK.ind (tn=%u)\n", + ind->hdr.tn); + return NULL; + } + + pdch = &gprs->pdch[ind->hdr.tn]; + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx DL BLOCK.ind (%s, fn=%u, len=%zu): %s\n", + BLOCK_IND_IS_PTCCH(ind) ? "PTCCH" : "PDTCH", + ind->hdr.fn, ind->data_len, osmo_hexdump(ind->data, ind->data_len)); + + l1gprs_check_pending_tbfs(gprs, ind->hdr.fn); + + if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) { + if (pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count > 0) + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx DL BLOCK.ind (fn=%u), but this PDCH has no active TBFs yet\n", + ind->hdr.fn); + else + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx DL BLOCK.ind (fn=%u), but this PDCH has no configured TBFs\n", + ind->hdr.fn); + return NULL; + } + + msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_DL_BLOCK_IND); + if (OSMO_UNLIKELY(msg == NULL)) { + LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n"); + return NULL; + } + + l1bi = (void *)msgb_put(msg, sizeof(*l1bi)); + *l1bi = (struct l1ctl_gprs_dl_block_ind) { + .hdr = { + .fn = htonl(ind->hdr.fn), + .tn = ind->hdr.tn, + }, + .meas = { + .ber10k = htons(ind->meas.ber10k), + .ci_cb = htons(ind->meas.ci_cb), + .rx_lev = ind->meas.rx_lev, + }, + .usf = 0xff, + }; + + if (ind->data_len == 0) + return msg; + if (BLOCK_IND_IS_PTCCH(ind)) { + memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len); + return msg; + } + + cs = osmo_gprs_dl_cs_by_block_bytes(ind->data_len); + switch (cs) { + case OSMO_GPRS_CS1: + case OSMO_GPRS_CS2: + case OSMO_GPRS_CS3: + case OSMO_GPRS_CS4: + l1bi->usf = ind->data[0] & 0x07; + *usf = l1bi->usf; + /* Determine whether to include the payload or not */ + if (l1gprs_pdch_filter_dl_block(pdch, ind->data)) + memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len); + break; + case OSMO_GPRS_CS_NONE: + LOGP_PDCH(pdch, LOGL_ERROR, + "Failed to determine Coding Scheme (len=%zu)\n", ind->data_len); + break; + default: + LOGP_PDCH(pdch, LOGL_NOTICE, "Coding Scheme %d is not supported\n", cs); + break; + } + + return msg; +} + +struct msgb *l1gprs_handle_rts_ind(struct l1gprs_state *gprs, uint32_t fn, uint8_t tn, uint8_t usf) +{ + const struct l1gprs_pdch *pdch = NULL; + struct l1ctl_gprs_rts_ind *l1bi; + struct msgb *msg; + + OSMO_ASSERT(tn < ARRAY_SIZE(gprs->pdch)); + pdch = &gprs->pdch[tn]; + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx RTS.ind (PDTCH, fn=%u, usf=%u)\n", + fn, usf); + + l1gprs_check_pending_tbfs(gprs, fn); + + if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) { + if (pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count > 0) + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx RTS.ind (fn=%u, usf=%u), but this PDCH has no active TBFs yet\n", + fn, usf); + else + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx RTS.ind (fn=%u, usf=%u), but this PDCH has no configured TBFs\n", + fn, usf); + return NULL; + } + + msg = l1gprs_l1ctl_msgb_alloc(L1CTL_GPRS_RTS_IND); + if (OSMO_UNLIKELY(msg == NULL)) { + LOGP_GPRS(gprs, LOGL_ERROR, "l1gprs_l1ctl_msgb_alloc() failed\n"); + return NULL; + } + + l1bi = (void *)msgb_put(msg, sizeof(*l1bi)); + *l1bi = (struct l1ctl_gprs_rts_ind) { + .fn = htonl(fn), + .tn = tn, + .usf = usf, + }; + + return msg; +} diff --git a/src/shared/libosmocore/include/osmocom/core/bitvec.h b/src/shared/libosmocore/include/osmocom/core/bitvec.h index 9c000d02..d441c844 100644 --- a/src/shared/libosmocore/include/osmocom/core/bitvec.h +++ b/src/shared/libosmocore/include/osmocom/core/bitvec.h @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \defgroup bitvec Bit vectors diff --git a/src/shared/libosmocore/include/osmocom/core/conv.h b/src/shared/libosmocore/include/osmocom/core/conv.h index e5b2a975..e1e5c813 100644 --- a/src/shared/libosmocore/include/osmocom/core/conv.h +++ b/src/shared/libosmocore/include/osmocom/core/conv.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \defgroup conv Convolutional encoding and decoding routines diff --git a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl index 89d083ae..9d17e2d1 100644 --- a/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl +++ b/src/shared/libosmocore/include/osmocom/core/crcXXgen.h.tpl @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __OSMO_CRCXXGEN_H__ diff --git a/src/shared/libosmocore/include/osmocom/core/crcgen.h b/src/shared/libosmocore/include/osmocom/core/crcgen.h index 8e208a74..62c45458 100644 --- a/src/shared/libosmocore/include/osmocom/core/crcgen.h +++ b/src/shared/libosmocore/include/osmocom/core/crcgen.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __OSMO_CRCGEN_H__ diff --git a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h index 079f440d..fccbf7e8 100644 --- a/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h +++ b/src/shared/libosmocore/include/osmocom/core/linuxrbtree.h @@ -12,10 +12,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - linux/include/linux/rbtree.h To use rbtrees you'll have to implement your own insert and search cores. diff --git a/src/shared/libosmocore/include/osmocom/core/msgb.h b/src/shared/libosmocore/include/osmocom/core/msgb.h index a1939ab6..0390a96f 100644 --- a/src/shared/libosmocore/include/osmocom/core/msgb.h +++ b/src/shared/libosmocore/include/osmocom/core/msgb.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/include/osmocom/core/msgfile.h b/src/shared/libosmocore/include/osmocom/core/msgfile.h index c5e67a45..d4e21f8f 100644 --- a/src/shared/libosmocore/include/osmocom/core/msgfile.h +++ b/src/shared/libosmocore/include/osmocom/core/msgfile.h @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef MSG_FILE_H diff --git a/src/shared/libosmocore/include/osmocom/core/serial.h b/src/shared/libosmocore/include/osmocom/core/serial.h index 889bd8a1..8810f332 100644 --- a/src/shared/libosmocore/include/osmocom/core/serial.h +++ b/src/shared/libosmocore/include/osmocom/core/serial.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \defgroup serial Utility functions to deal with serial ports diff --git a/src/shared/libosmocore/include/osmocom/core/timer.h b/src/shared/libosmocore/include/osmocom/core/timer.h index d37af806..bec2f05a 100644 --- a/src/shared/libosmocore/include/osmocom/core/timer.h +++ b/src/shared/libosmocore/include/osmocom/core/timer.h @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \defgroup timer Osmocom timers diff --git a/src/shared/libosmocore/include/osmocom/core/timer_compat.h b/src/shared/libosmocore/include/osmocom/core/timer_compat.h index d86c109e..20240873 100644 --- a/src/shared/libosmocore/include/osmocom/core/timer_compat.h +++ b/src/shared/libosmocore/include/osmocom/core/timer_compat.h @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \defgroup timer Osmocom timers diff --git a/src/shared/libosmocore/include/osmocom/core/write_queue.h b/src/shared/libosmocore/include/osmocom/core/write_queue.h index 816c0364..c4041641 100644 --- a/src/shared/libosmocore/include/osmocom/core/write_queue.h +++ b/src/shared/libosmocore/include/osmocom/core/write_queue.h @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef OSMO_WQUEUE_H #define OSMO_WQUEUE_H diff --git a/src/shared/libosmocore/include/osmocom/gsm/a5.h b/src/shared/libosmocore/include/osmocom/gsm/a5.h index 649dbab1..807a2d83 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/a5.h +++ b/src/shared/libosmocore/include/osmocom/gsm/a5.h @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef __OSMO_A5_H__ diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h index 5380dd9e..11c27b7d 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm0808.h @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef OSMOCORE_GSM0808_H #define OSMOCORE_GSM0808_H diff --git a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h index 6d316727..9e277d12 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h +++ b/src/shared/libosmocore/include/osmocom/gsm/gsm_utils.h @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef GSM_UTILS_H diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h index 172ef678..e5d129ed 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_04_08.h @@ -345,7 +345,7 @@ enum gsm48_chan_mode { GSM48_CMODE_DATA_14k5 = 0x0f, GSM48_CMODE_DATA_12k0 = 0x03, GSM48_CMODE_DATA_6k0 = 0x0b, - GSM48_CMODE_DATA_3k6 = 0x23, + GSM48_CMODE_DATA_3k6 = 0x13, }; /* Chapter 9.1.2 */ diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h index 57a8f687..41d65af6 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_08_58.h @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h index 694df938..76e8bd07 100644 --- a/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h +++ b/src/shared/libosmocore/include/osmocom/gsm/protocol/gsm_12_21.h @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup oml diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h index 3c222014..29e8e13e 100644 --- a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h +++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef TELNET_INTERFACE_H diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h index 22a184d6..f0d269c6 100644 --- a/src/shared/libosmocore/include/osmocom/vty/vector.h +++ b/src/shared/libosmocore/include/osmocom/vty/vector.h @@ -13,10 +13,6 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ diff --git a/src/shared/libosmocore/src/application.c b/src/shared/libosmocore/src/application.c index e0d989e5..98175fb8 100644 --- a/src/shared/libosmocore/src/application.c +++ b/src/shared/libosmocore/src/application.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \file application.c diff --git a/src/shared/libosmocore/src/backtrace.c b/src/shared/libosmocore/src/backtrace.c index 5b93becb..53cece67 100644 --- a/src/shared/libosmocore/src/backtrace.c +++ b/src/shared/libosmocore/src/backtrace.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \file backtrace.c diff --git a/src/shared/libosmocore/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c index 714c11b7..1e3ee6a0 100644 --- a/src/shared/libosmocore/src/bitvec.c +++ b/src/shared/libosmocore/src/bitvec.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup bitvec diff --git a/src/shared/libosmocore/src/codec/gsm610.c b/src/shared/libosmocore/src/codec/gsm610.c index 35f6011d..faa8fb4c 100644 --- a/src/shared/libosmocore/src/codec/gsm610.c +++ b/src/shared/libosmocore/src/codec/gsm610.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/codec/gsm620.c b/src/shared/libosmocore/src/codec/gsm620.c index fa570e4f..67e1a8ba 100644 --- a/src/shared/libosmocore/src/codec/gsm620.c +++ b/src/shared/libosmocore/src/codec/gsm620.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/codec/gsm660.c b/src/shared/libosmocore/src/codec/gsm660.c index c044a2ab..bd003416 100644 --- a/src/shared/libosmocore/src/codec/gsm660.c +++ b/src/shared/libosmocore/src/codec/gsm660.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/codec/gsm690.c b/src/shared/libosmocore/src/codec/gsm690.c index fdf3302f..8d94c5ac 100644 --- a/src/shared/libosmocore/src/codec/gsm690.c +++ b/src/shared/libosmocore/src/codec/gsm690.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/conv.c b/src/shared/libosmocore/src/conv.c index ebc3eda7..77c505c1 100644 --- a/src/shared/libosmocore/src/conv.c +++ b/src/shared/libosmocore/src/conv.c @@ -16,10 +16,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \addtogroup conv diff --git a/src/shared/libosmocore/src/crcXXgen.c.tpl b/src/shared/libosmocore/src/crcXXgen.c.tpl index 80bf1e2a..b4486c55 100644 --- a/src/shared/libosmocore/src/crcXXgen.c.tpl +++ b/src/shared/libosmocore/src/crcXXgen.c.tpl @@ -16,10 +16,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \addtogroup crcgen diff --git a/src/shared/libosmocore/src/gsm/a5.c b/src/shared/libosmocore/src/gsm/a5.c index 356060ab..6001908c 100644 --- a/src/shared/libosmocore/src/gsm/a5.c +++ b/src/shared/libosmocore/src/gsm/a5.c @@ -20,10 +20,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \addtogroup a5 diff --git a/src/shared/libosmocore/src/gsm/auth_comp128v1.c b/src/shared/libosmocore/src/gsm/auth_comp128v1.c index 41aef71c..14e44f4a 100644 --- a/src/shared/libosmocore/src/gsm/auth_comp128v1.c +++ b/src/shared/libosmocore/src/gsm/auth_comp128v1.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/crypt/auth.h> diff --git a/src/shared/libosmocore/src/gsm/auth_core.c b/src/shared/libosmocore/src/gsm/auth_core.c index 5cf8dfcf..de3d26a7 100644 --- a/src/shared/libosmocore/src/gsm/auth_core.c +++ b/src/shared/libosmocore/src/gsm/auth_core.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <errno.h> diff --git a/src/shared/libosmocore/src/gsm/auth_milenage.c b/src/shared/libosmocore/src/gsm/auth_milenage.c index 5b2787dd..cac1a056 100644 --- a/src/shared/libosmocore/src/gsm/auth_milenage.c +++ b/src/shared/libosmocore/src/gsm/auth_milenage.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/crypt/auth.h> diff --git a/src/shared/libosmocore/src/gsm/comp128.c b/src/shared/libosmocore/src/gsm/comp128.c index b7a23820..5aaf91f4 100644 --- a/src/shared/libosmocore/src/gsm/comp128.c +++ b/src/shared/libosmocore/src/gsm/comp128.c @@ -26,10 +26,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* diff --git a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c index b9a22a10..db3b0846 100644 --- a/src/shared/libosmocore/src/gsm/gprs_cipher_core.c +++ b/src/shared/libosmocore/src/gsm/gprs_cipher_core.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <errno.h> diff --git a/src/shared/libosmocore/src/gsm/gsm0480.c b/src/shared/libosmocore/src/gsm/gsm0480.c index b9b3ed97..6a4dd291 100644 --- a/src/shared/libosmocore/src/gsm/gsm0480.c +++ b/src/shared/libosmocore/src/gsm/gsm0480.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/gsm/gsm0480.h> diff --git a/src/shared/libosmocore/src/gsm/gsm0808.c b/src/shared/libosmocore/src/gsm/gsm0808.c index 30098278..55dc41c2 100644 --- a/src/shared/libosmocore/src/gsm/gsm0808.c +++ b/src/shared/libosmocore/src/gsm/gsm0808.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/gsm/gsm0808.h> diff --git a/src/shared/libosmocore/src/gsm/gsm48.c b/src/shared/libosmocore/src/gsm/gsm48.c index ea05d450..b1ef65aa 100644 --- a/src/shared/libosmocore/src/gsm/gsm48.c +++ b/src/shared/libosmocore/src/gsm/gsm48.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/gsm/gsm48_ie.c b/src/shared/libosmocore/src/gsm/gsm48_ie.c index 78619b97..84252c6d 100644 --- a/src/shared/libosmocore/src/gsm/gsm48_ie.c +++ b/src/shared/libosmocore/src/gsm/gsm48_ie.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ diff --git a/src/shared/libosmocore/src/gsm/gsm_utils.c b/src/shared/libosmocore/src/gsm/gsm_utils.c index 8b1fae08..4b170df7 100644 --- a/src/shared/libosmocore/src/gsm/gsm_utils.c +++ b/src/shared/libosmocore/src/gsm/gsm_utils.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \mainpage libosmogsm Documentation diff --git a/src/shared/libosmocore/src/gsm/lapd_core.c b/src/shared/libosmocore/src/gsm/lapd_core.c index 96099edb..47a2a8dd 100644 --- a/src/shared/libosmocore/src/gsm/lapd_core.c +++ b/src/shared/libosmocore/src/gsm/lapd_core.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup lapd diff --git a/src/shared/libosmocore/src/gsm/lapdm.c b/src/shared/libosmocore/src/gsm/lapdm.c index 1c08113e..5e1c2d54 100644 --- a/src/shared/libosmocore/src/gsm/lapdm.c +++ b/src/shared/libosmocore/src/gsm/lapdm.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup lapdm diff --git a/src/shared/libosmocore/src/gsm/rsl.c b/src/shared/libosmocore/src/gsm/rsl.c index 5693b4f0..366eae5b 100644 --- a/src/shared/libosmocore/src/gsm/rsl.c +++ b/src/shared/libosmocore/src/gsm/rsl.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/gsm/rxlev_stat.c b/src/shared/libosmocore/src/gsm/rxlev_stat.c index d226861e..fb3a2a59 100644 --- a/src/shared/libosmocore/src/gsm/rxlev_stat.c +++ b/src/shared/libosmocore/src/gsm/rxlev_stat.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <unistd.h> diff --git a/src/shared/libosmocore/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c index ce722da9..12a1dcf8 100644 --- a/src/shared/libosmocore/src/gsmtap_util.c +++ b/src/shared/libosmocore/src/gsmtap_util.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include "../config.h" diff --git a/src/shared/libosmocore/src/logging.c b/src/shared/libosmocore/src/logging.c index f58265f7..e187df24 100644 --- a/src/shared/libosmocore/src/logging.c +++ b/src/shared/libosmocore/src/logging.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* \addtogroup logging diff --git a/src/shared/libosmocore/src/logging_syslog.c b/src/shared/libosmocore/src/logging_syslog.c index 5b0ae5ff..8bc2dfdf 100644 --- a/src/shared/libosmocore/src/logging_syslog.c +++ b/src/shared/libosmocore/src/logging_syslog.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup logging diff --git a/src/shared/libosmocore/src/msgb.c b/src/shared/libosmocore/src/msgb.c index c8564dbb..4c703feb 100644 --- a/src/shared/libosmocore/src/msgb.c +++ b/src/shared/libosmocore/src/msgb.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup msgb diff --git a/src/shared/libosmocore/src/msgfile.c b/src/shared/libosmocore/src/msgfile.c index d2b180d7..14929ed6 100644 --- a/src/shared/libosmocore/src/msgfile.c +++ b/src/shared/libosmocore/src/msgfile.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/msgfile.h> diff --git a/src/shared/libosmocore/src/panic.c b/src/shared/libosmocore/src/panic.c index be644ff1..19134037 100644 --- a/src/shared/libosmocore/src/panic.c +++ b/src/shared/libosmocore/src/panic.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup utils diff --git a/src/shared/libosmocore/src/plugin.c b/src/shared/libosmocore/src/plugin.c index 998bca35..9f17698f 100644 --- a/src/shared/libosmocore/src/plugin.c +++ b/src/shared/libosmocore/src/plugin.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include "../config.h" diff --git a/src/shared/libosmocore/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c index 8a232e86..79d41dd8 100644 --- a/src/shared/libosmocore/src/rate_ctr.c +++ b/src/shared/libosmocore/src/rate_ctr.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /*! \addtogroup rate_ctr diff --git a/src/shared/libosmocore/src/rbtree.c b/src/shared/libosmocore/src/rbtree.c index 4e7c0f3a..2da7d7e4 100644 --- a/src/shared/libosmocore/src/rbtree.c +++ b/src/shared/libosmocore/src/rbtree.c @@ -13,10 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - linux/lib/rbtree.c */ diff --git a/src/shared/libosmocore/src/select.c b/src/shared/libosmocore/src/select.c index 6b73377a..11f1a2a5 100644 --- a/src/shared/libosmocore/src/select.c +++ b/src/shared/libosmocore/src/select.c @@ -13,10 +13,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <fcntl.h> diff --git a/src/shared/libosmocore/src/serial.c b/src/shared/libosmocore/src/serial.c index a025ae91..780cf440 100644 --- a/src/shared/libosmocore/src/serial.c +++ b/src/shared/libosmocore/src/serial.c @@ -16,10 +16,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*! \addtogroup serial diff --git a/src/shared/libosmocore/src/signal.c b/src/shared/libosmocore/src/signal.c index 63843849..6f8c798e 100644 --- a/src/shared/libosmocore/src/signal.c +++ b/src/shared/libosmocore/src/signal.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/signal.h> diff --git a/src/shared/libosmocore/src/statistics.c b/src/shared/libosmocore/src/statistics.c index e28541ba..d18f4160 100644 --- a/src/shared/libosmocore/src/statistics.c +++ b/src/shared/libosmocore/src/statistics.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <string.h> diff --git a/src/shared/libosmocore/src/timer.c b/src/shared/libosmocore/src/timer.c index 6d4abc26..55d08d33 100644 --- a/src/shared/libosmocore/src/timer.c +++ b/src/shared/libosmocore/src/timer.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* These store the amount of time that we wait until next timer expires. */ diff --git a/src/shared/libosmocore/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c index 6029d58b..4f13852b 100644 --- a/src/shared/libosmocore/src/vty/logging_vty.c +++ b/src/shared/libosmocore/src/vty/logging_vty.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdlib.h> diff --git a/src/shared/libosmocore/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c index 1abf141d..821a6498 100644 --- a/src/shared/libosmocore/src/vty/telnet_interface.c +++ b/src/shared/libosmocore/src/vty/telnet_interface.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <sys/socket.h> diff --git a/src/shared/libosmocore/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c index e9c0d2d7..af49c308 100644 --- a/src/shared/libosmocore/src/vty/utils.c +++ b/src/shared/libosmocore/src/vty/utils.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/shared/libosmocore/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c index 4012f24b..bda96d9c 100644 --- a/src/shared/libosmocore/src/vty/vector.c +++ b/src/shared/libosmocore/src/vty/vector.c @@ -12,10 +12,6 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ diff --git a/src/shared/libosmocore/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c index cef40f83..d3532102 100644 --- a/src/shared/libosmocore/src/write_queue.c +++ b/src/shared/libosmocore/src/write_queue.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/write_queue.h> diff --git a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c index 077063be..e31d0561 100644 --- a/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c +++ b/src/shared/libosmocore/tests/gsm0408/gsm0408_test.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <string.h> diff --git a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c index 7e5e97b5..8cc0fbbd 100644 --- a/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c +++ b/src/shared/libosmocore/tests/gsm0808/gsm0808_test.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/gsm/gsm0808.h> diff --git a/src/shared/libosmocore/tests/lapd/lapd_test.c b/src/shared/libosmocore/tests/lapd/lapd_test.c index d58bec65..0bd89b79 100644 --- a/src/shared/libosmocore/tests/lapd/lapd_test.c +++ b/src/shared/libosmocore/tests/lapd/lapd_test.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/logging.h> diff --git a/src/shared/libosmocore/tests/msgfile/msgfile_test.c b/src/shared/libosmocore/tests/msgfile/msgfile_test.c index ed7aa978..abea9173 100644 --- a/src/shared/libosmocore/tests/msgfile/msgfile_test.c +++ b/src/shared/libosmocore/tests/msgfile/msgfile_test.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/msgfile.h> diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c index 6df4b623..18a7d1ec 100644 --- a/src/shared/libosmocore/tests/sms/sms_test.c +++ b/src/shared/libosmocore/tests/sms/sms_test.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/shared/libosmocore/tests/smscb/smscb_test.c b/src/shared/libosmocore/tests/smscb/smscb_test.c index e10e12d8..34178e28 100644 --- a/src/shared/libosmocore/tests/smscb/smscb_test.c +++ b/src/shared/libosmocore/tests/smscb/smscb_test.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/gsm/protocol/gsm_03_41.h> diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c index ba3127d4..7b973c06 100644 --- a/src/shared/libosmocore/tests/timer/timer_test.c +++ b/src/shared/libosmocore/tests/timer/timer_test.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/shared/libosmocore/tests/ussd/ussd_test.c b/src/shared/libosmocore/tests/ussd/ussd_test.c index 55384f10..0ddab90f 100644 --- a/src/shared/libosmocore/tests/ussd/ussd_test.c +++ b/src/shared/libosmocore/tests/ussd/ussd_test.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <osmocom/core/application.h> diff --git a/src/shared/libosmocore/utils/osmo-arfcn.c b/src/shared/libosmocore/utils/osmo-arfcn.c index 15adbca2..172ee54e 100644 --- a/src/shared/libosmocore/utils/osmo-arfcn.c +++ b/src/shared/libosmocore/utils/osmo-arfcn.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/shared/libosmocore/utils/osmo-auc-gen.c b/src/shared/libosmocore/utils/osmo-auc-gen.c index 7a3c124c..449bcc6f 100644 --- a/src/shared/libosmocore/utils/osmo-auc-gen.c +++ b/src/shared/libosmocore/utils/osmo-auc-gen.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh index 69dfbe11..aec273fa 100755 --- a/src/shared/update-libosmocore.sh +++ b/src/shared/update-libosmocore.sh @@ -1,3 +1,3 @@ #!/bin/sh -(cd ../.. && git subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master) +(cd ../.. && git subtree pull --prefix=src/shared/libosmocore https://gitea.osmocom.org/osmocom/libosmocore master) diff --git a/src/target/firmware/Makefile b/src/target/firmware/Makefile index 104ddac9..2a376bc3 100644 --- a/src/target/firmware/Makefile +++ b/src/target/firmware/Makefile @@ -24,7 +24,8 @@ ENV_e88flash_OBJS=board/compal/start.rom.o board/compal/header.o board/compal/ex # # List of all supported boards (meant to be overridden on command line) -BOARDS?=compal_e88 compal_e86 compal_e99 se_j100 gta0x gtm900b fcdev3b pirelli_dpl10 +BOARDS?=compal_e88 compal_e86 compal_e99 se_j100 se_k2x0 gta0x gtm900b fcdev3b \ + pirelli_dpl10 tr800 # Framebuffer support, board specific drivers FB_OBJS=fb/framebuffer.o fb/font.o fb/helvR08.o fb/helvB14.o fb/c64.o \ @@ -35,6 +36,7 @@ FB_e99_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_ssd1783.o FB_e86_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_td014.o FB_j100_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_ssd1963.o FB_dpl10_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_s6b33b1x.o +FB_k2x0_OBJS=$(FB_OBJS) fb/fb_rgb332.o fb/fb_k2x0.o FB_dummy_OBJS=$(FB_OBJS) fb/fb_dummy.o # TI Calypso @@ -62,6 +64,13 @@ BOARD_fcdev3b_OBJS=$(calypso_COMMON_OBJS) board/fcdev3b/init.o \ board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS) BOARD_fcdev3b_ENVIRONMENTS=highram +# iWOW TR-800 aka FreeCalypso Tango +BOARD_tr800_OBJS=$(calypso_COMMON_OBJS) board/tr800/init.o \ + board/tr800/rffe_leo_quadband.o board/gta0x/rf_tables.o \ + board/tr800/afcparams.o \ + board/common/readcal_tiffs.o battery/dummy.o $(FB_dummy_OBJS) +BOARD_tr800_ENVIRONMENTS=highram + # Pirelli DP-L10 BOARD_pirelli_dpl10_OBJS=$(calypso_COMMON_OBJS) board/pirelli_dpl10/init.o \ board/pirelli_dpl10/rffe_dpl10_triband.o \ @@ -100,6 +109,12 @@ BOARD_se_j100_OBJS=$(compal_COMMON_OBJS) board/se_j100/init.o \ battery/dummy.o $(FB_j100_OBJS) BOARD_se_j100_ENVIRONMENTS=$(compal_COMMON_ENVIRONMENTS) +# Sony Ericsson K200i/K220i +BOARD_se_k2x0_OBJS=$(calypso_COMMON_OBJS) board/se_k2x0/init.o \ + board/se_k2x0/rffe_k2x0.o \ + board/gta0x/rf_tables.o board/gta0x/afcparams.o \ + board/common/readcal_tiffs.o battery/dummy.o $(FB_k2x0_OBJS) +BOARD_se_k2x0_ENVIRONMENTS=highram # # Applications @@ -126,8 +141,7 @@ ANY_APP_LIBS+= calypso/libcalypso.a \ comm/libcomm.a \ tiffs/libtiffs.a \ ../../shared/libosmocore/build-target/src/.libs/libosmocore.a \ - ../../shared/libosmocore/build-target/src/gsm/.libs/libosmogsm.a \ - ../../shared/libosmocore/build-target/src/codec/.libs/libosmocodec.a + ../../shared/libosmocore/build-target/src/gsm/.libs/libosmogsm.a # diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc index 2be240d2..86e8f3ce 100644 --- a/src/target/firmware/Makefile.inc +++ b/src/target/firmware/Makefile.inc @@ -4,7 +4,7 @@ CROSS_COMPILE?=arm-none-eabi- CC=gcc -LD=ld +LD=gcc AR=ar SIZE=size OBJCOPY=objcopy @@ -22,7 +22,7 @@ CFLAGS += -g$(DEBUGF) #ASFLAGS=--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__ ASFLAGS=$(INCLUDES) -D__ASSEMBLY__ -LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections --cref +LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs -Wl,--gc-sections -Wl,--cref #### QUIET OUTPUT #### @@ -99,8 +99,8 @@ $(1)_$(2)_$(3)_OBJS+=board/$(2)/$(1).$(3).manifest.o # define compilation rule, also generates map file board/$(2)/$(1).$(3).elf board/$(2)/$(1).$(3).map: $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) $$(ENV_$(3)_LDS) $$(Q_LD)$(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $$(ENV_$(3)_LDS) -Bstatic \ - -Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \ - --start-group $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) --end-group + -Wl,-Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \ + -Wl,--start-group $$($(1)_$(2)_$(3)_OBJS) $$($(1)_$(2)_$(3)_LIBS) -Wl,--end-group # define size rule board/$(2)/$(1).$(3).size: board/$(2)/$(1).$(3).elf diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c index 5b792d68..4f495bc8 100644 --- a/src/target/firmware/abb/twl3025.c +++ b/src/target/firmware/abb/twl3025.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -377,6 +373,7 @@ void twl3025_unit_enable(enum twl3025_unit unit, int on) togbr1 = (1 << 5); else togbr1 = (1 << 4); + break; case TWL3025_UNIT_VDL: if (on) togbr1 = (1 << 3); diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c index 5e1ab68d..e5521c3d 100644 --- a/src/target/firmware/apps/compal_dsp_dump/main.c +++ b/src/target/firmware/apps/compal_dsp_dump/main.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <memory.h> diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c index 481cf170..ecacc183 100644 --- a/src/target/firmware/apps/hello_world/main.c +++ b/src/target/firmware/apps/hello_world/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -169,8 +165,6 @@ int main(void) void key_handler(enum key_codes code, enum key_states state) { - char test[16]; - if (state != PRESSED) return; diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c index b2e66e2c..81354686 100644 --- a/src/target/firmware/apps/layer1/main.c +++ b/src/target/firmware/apps/layer1/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -61,7 +57,6 @@ static void key_handler(enum key_codes code, enum key_states state); int main(void) { uint8_t atr[20]; - uint8_t atrLength = 0; board_init(1); @@ -105,7 +100,7 @@ int main(void) puts("Power up simcard:\n"); memset(atr,0,sizeof(atr)); - atrLength = calypso_sim_powerup(atr); + calypso_sim_powerup(atr); read_factory_rf_calibration(); layer1_init(); diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c index 9b7a1b59..e828fffd 100644 --- a/src/target/firmware/apps/loader/main.c +++ b/src/target/firmware/apps/loader/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/apps/loader_mtk/main.c b/src/target/firmware/apps/loader_mtk/main.c index f2ebbea1..995d277d 100644 --- a/src/target/firmware/apps/loader_mtk/main.c +++ b/src/target/firmware/apps/loader_mtk/main.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/apps/menu/main.c b/src/target/firmware/apps/menu/main.c index fa369681..5d96cc0a 100644 --- a/src/target/firmware/apps/menu/main.c +++ b/src/target/firmware/apps/menu/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/apps/rssi/main.c b/src/target/firmware/apps/rssi/main.c index b04fe28d..29c5430e 100644 --- a/src/target/firmware/apps/rssi/main.c +++ b/src/target/firmware/apps/rssi/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -920,12 +916,9 @@ static void handle_pm(void) a = arfcn; if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) a |= ARFCN_PCS; - if (uplink) - a |= ARFCN_UPLINK; e = a; pm_mode = PM_SENT; - } - if (mode == MODE_SPECTRUM) { + } else { /* mode == MODE_SPECTRUM */ if (pcs && arfcn >= PCS_MIN && arfcn <= PCS_MAX) { a = PCS_MIN | ARFCN_PCS; e = PCS_MAX | ARFCN_PCS; diff --git a/src/target/firmware/apps/simtest/main.c b/src/target/firmware/apps/simtest/main.c index e20c52a1..d41891df 100755 --- a/src/target/firmware/apps/simtest/main.c +++ b/src/target/firmware/apps/simtest/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/apps/snake_game/main.c b/src/target/firmware/apps/snake_game/main.c index 44dda9af..4945b6f7 100644 --- a/src/target/firmware/apps/snake_game/main.c +++ b/src/target/firmware/apps/snake_game/main.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/firmware/battery/compal_e88.c b/src/target/firmware/battery/compal_e88.c index 609d4063..788d8ab2 100644 --- a/src/target/firmware/battery/compal_e88.c +++ b/src/target/firmware/battery/compal_e88.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* @@ -356,7 +352,6 @@ bat_compal_e88_adc_read(){ static void battery_compal_e88_timer_cb(void *p){ struct osmo_timer_list *tmr = (struct osmo_timer_list*)p; - int i; if(bat_compal_e88_adc_read()){ /* read back ADCs after a brief delay */ osmo_timer_schedule(tmr,ADC_TIMER_DELAY); diff --git a/src/target/firmware/board/common/readcal_tiffs.c b/src/target/firmware/board/common/readcal_tiffs.c index 43519710..175f25a7 100644 --- a/src/target/firmware/board/common/readcal_tiffs.c +++ b/src/target/firmware/board/common/readcal_tiffs.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/firmware/board/common/tx_calchan.c b/src/target/firmware/board/common/tx_calchan.c index 9901e712..b86464d6 100644 --- a/src/target/firmware/board/common/tx_calchan.c +++ b/src/target/firmware/board/common/tx_calchan.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal/readcal_common.c b/src/target/firmware/board/compal/readcal_common.c index 2f7944ce..c0260b67 100644 --- a/src/target/firmware/board/compal/readcal_common.c +++ b/src/target/firmware/board/compal/readcal_common.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/firmware/board/compal/readcal_small.c b/src/target/firmware/board/compal/readcal_small.c index a036e111..32302d20 100644 --- a/src/target/firmware/board/compal/readcal_small.c +++ b/src/target/firmware/board/compal/readcal_small.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal/rf_tables.c b/src/target/firmware/board/compal/rf_tables.c index dd7411da..f115f29a 100644 --- a/src/target/firmware/board/compal/rf_tables.c +++ b/src/target/firmware/board/compal/rf_tables.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal_e86/init.c b/src/target/firmware/board/compal_e86/init.c index 725f1841..82a96ebd 100644 --- a/src/target/firmware/board/compal_e86/init.c +++ b/src/target/firmware/board/compal_e86/init.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal_e86/tx_ramps.c b/src/target/firmware/board/compal_e86/tx_ramps.c index 7bac4d51..195242a1 100644 --- a/src/target/firmware/board/compal_e86/tx_ramps.c +++ b/src/target/firmware/board/compal_e86/tx_ramps.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c index 956f7dc5..09376448 100755 --- a/src/target/firmware/board/compal_e88/init.c +++ b/src/target/firmware/board/compal_e88/init.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal_e88/tx_ramps.c b/src/target/firmware/board/compal_e88/tx_ramps.c index 846a21d4..79b3180d 100644 --- a/src/target/firmware/board/compal_e88/tx_ramps.c +++ b/src/target/firmware/board/compal_e88/tx_ramps.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal_e99/init.c b/src/target/firmware/board/compal_e99/init.c index 0271f16e..18c94fd1 100644 --- a/src/target/firmware/board/compal_e99/init.c +++ b/src/target/firmware/board/compal_e99/init.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/compal_e99/readcal.c b/src/target/firmware/board/compal_e99/readcal.c index c75e1fa5..c4a113fd 100644 --- a/src/target/firmware/board/compal_e99/readcal.c +++ b/src/target/firmware/board/compal_e99/readcal.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/fcdev3b/init.c b/src/target/firmware/board/fcdev3b/init.c index c0705364..03bd6d73 100644 --- a/src/target/firmware/board/fcdev3b/init.c +++ b/src/target/firmware/board/fcdev3b/init.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/gta0x/afcparams.c b/src/target/firmware/board/gta0x/afcparams.c index ba48360d..5bf68acf 100644 --- a/src/target/firmware/board/gta0x/afcparams.c +++ b/src/target/firmware/board/gta0x/afcparams.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/gta0x/init.c b/src/target/firmware/board/gta0x/init.c index 7fba7561..49279d18 100644 --- a/src/target/firmware/board/gta0x/init.c +++ b/src/target/firmware/board/gta0x/init.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/gta0x/rf_tables.c b/src/target/firmware/board/gta0x/rf_tables.c index 9a08b3c7..650c77c7 100644 --- a/src/target/firmware/board/gta0x/rf_tables.c +++ b/src/target/firmware/board/gta0x/rf_tables.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/gtm900b/afcparams.c b/src/target/firmware/board/gtm900b/afcparams.c index ca8908dc..9dbbe7e9 100644 --- a/src/target/firmware/board/gtm900b/afcparams.c +++ b/src/target/firmware/board/gtm900b/afcparams.c @@ -13,10 +13,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/gtm900b/init.c b/src/target/firmware/board/gtm900b/init.c index 934e96e8..58a1e1e2 100644 --- a/src/target/firmware/board/gtm900b/init.c +++ b/src/target/firmware/board/gtm900b/init.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/mediatek/uart.c b/src/target/firmware/board/mediatek/uart.c index f9e3283b..7b4c49af 100644 --- a/src/target/firmware/board/mediatek/uart.c +++ b/src/target/firmware/board/mediatek/uart.c @@ -19,10 +19,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <debug.h> diff --git a/src/target/firmware/board/mt62xx/init.c b/src/target/firmware/board/mt62xx/init.c index dae38cf4..baa6fcd4 100644 --- a/src/target/firmware/board/mt62xx/init.c +++ b/src/target/firmware/board/mt62xx/init.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/pirelli_dpl10/init.c b/src/target/firmware/board/pirelli_dpl10/init.c index 1af6e7c9..273a11dd 100644 --- a/src/target/firmware/board/pirelli_dpl10/init.c +++ b/src/target/firmware/board/pirelli_dpl10/init.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/pirelli_dpl10/readcal.c b/src/target/firmware/board/pirelli_dpl10/readcal.c index 6a4670a0..40f608f8 100644 --- a/src/target/firmware/board/pirelli_dpl10/readcal.c +++ b/src/target/firmware/board/pirelli_dpl10/readcal.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/firmware/board/pirelli_dpl10/rf_tables.c b/src/target/firmware/board/pirelli_dpl10/rf_tables.c index 34fbbcbc..a2d26b1b 100644 --- a/src/target/firmware/board/pirelli_dpl10/rf_tables.c +++ b/src/target/firmware/board/pirelli_dpl10/rf_tables.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/se_j100/init.c b/src/target/firmware/board/se_j100/init.c index 0ae477a4..6352b369 100644 --- a/src/target/firmware/board/se_j100/init.c +++ b/src/target/firmware/board/se_j100/init.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/se_j100/tx_ramps.c b/src/target/firmware/board/se_j100/tx_ramps.c index b1110e06..387febb9 100644 --- a/src/target/firmware/board/se_j100/tx_ramps.c +++ b/src/target/firmware/board/se_j100/tx_ramps.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/board/se_k2x0/init.c b/src/target/firmware/board/se_k2x0/init.c new file mode 100644 index 00000000..71218670 --- /dev/null +++ b/src/target/firmware/board/se_k2x0/init.c @@ -0,0 +1,140 @@ +/* Initialization for the Sony Ericsson K200i/K220i */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010-22 by Steve Markgraf <steve@steve-m.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <flash/cfi_flash.h> +#include <tiffs.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <uart.h> +#include <calypso/backlight.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <fb/framebuffer.h> +#include "keymap.h" + +#define ASIC_CONF_REG 0xfffef008 +#define ARMIO_LATCH_OUT 0xfffe4802 +#define IO_CNTL_REG 0xfffe4804 +#define IO_CONF_REG 0xfffef00a + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* Set LPG and PWL pin mux */ + reg |= (1 << 6) | (1 << 4); + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + writew(0xc060, IO_CNTL_REG); + writew(0x03fd, IO_CONF_REG); + + /* set default IO state */ + writew(0x1f83, ARMIO_LATCH_OUT); +} + +void board_init(int with_irq) +{ + /* Configure the memory interface */ + calypso_mem_cfg(CALYPSO_nCS0, 4, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 4, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 5, CALYPSO_MEM_8bit, 1); /* TODO: add one dummy cycle */ + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + /* Initialize board-specific GPIO */ + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(with_irq); + calypso_exceptions_install(); + + /* Initialize interrupt controller */ + if (with_irq) + irq_init(); + + sercomm_bind_uart(UART_MODEM); + cons_bind_uart(UART_IRDA); + + /* initialize MODEM UART to be used for sercomm */ + uart_init(UART_MODEM, with_irq); + uart_baudrate(UART_MODEM, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(UART_IRDA, with_irq); + uart_baudrate(UART_IRDA, UART_115200); + + /* Initialize hardware timers */ + hwtimer_init(); + + /* Initialize DMA controller */ + dma_init(); + + /* Initialize real time clock */ + rtc_init(); + + /* Initialize system timers (uses hwtimer 2) */ + timer_init(); + + /* Initialize LCD driver and backlight */ + fb_init(); + bl_mode_pwl(1); + bl_level(50); + + /* Initialize keypad driver */ + keypad_init(keymap, with_irq); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); + + /* K200i uses 13 sectors of 256 KiB each */ + if (tiffs_init(0x01800000, 0x40000, 13) < 0) { + /* K220i uses 52 sectors of 64 KiB each */ + tiffs_init(0x01800000, 0x10000, 52); + } +} diff --git a/src/target/firmware/board/se_k2x0/keymap.h b/src/target/firmware/board/se_k2x0/keymap.h new file mode 100644 index 00000000..aa2d8a56 --- /dev/null +++ b/src/target/firmware/board/se_k2x0/keymap.h @@ -0,0 +1,28 @@ +/* Keymap for SE K200i/K220i */ +static const uint8_t keymap[] = { + [KEY_0] = 9, + [KEY_1] = 1, + [KEY_2] = 6, + [KEY_3] = 11, + [KEY_4] = 2, + [KEY_5] = 7, + [KEY_6] = 12, + [KEY_7] = 3, + [KEY_8] = 8, + [KEY_9] = 13, + [KEY_STAR] = 4, + [KEY_HASH] = 14, + [KEY_MENU] = 21, /* not existent */ + [KEY_LEFT_SB] = 0, + [KEY_RIGHT_SB] = 5, + [KEY_UP] = 16, + [KEY_DOWN] = 15, + [KEY_LEFT] = 17, + [KEY_RIGHT] = 18, + [KEY_OK] = 10, +/* power button is not connected to keypad scan matrix but to TWL3025 */ + [KEY_POWER] = 31, + [KEY_MINUS] = 22, /* not existent */ + [KEY_PLUS] = 23, /* not existent */ + [KEY_CAMERA] = 24, /* not existent */ +}; diff --git a/src/target/firmware/board/se_k2x0/rffe_k2x0.c b/src/target/firmware/board/se_k2x0/rffe_k2x0.c new file mode 100644 index 00000000..f9965acd --- /dev/null +++ b/src/target/firmware/board/se_k2x0/rffe_k2x0.c @@ -0,0 +1,122 @@ +/* RF frontend driver for Sony Ericsson K200i/K220i */ + +/* (C) 2022 by Steve Markgraf <steve@steve-m.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <calypso/tsp.h> +#include <rf/trf6151.h> + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* describe how the RF frontend is wired on the SE K200i/K220i */ +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151, correct for K200i */ +#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */ +#define DCS_TXEN TSPACT(3) /* DCS (as opposed to GSM) Transmit */ + +#define ASM_VC1 TSPACT(1) /* GSM1800 TX at antenna switch, inverted */ +#define ASM_VC2 TSPACT(2) /* GSM900 TX at antenna switch, inverted */ + +#define IOTA_STROBE TSPEN(1) /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN(2) /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~(PA_ENABLE | DCS_TXEN); + tspact |= ASM_VC1 | ASM_VC2; + +#ifdef CONFIG_TX_ENABLE + /* Then we selectively set the bits on, if required */ + if (tx) { + if ((band == GSM_BAND_1800) || band == (GSM_BAND_1900)) { + tspact |= DCS_TXEN; + tspact &= ~ASM_VC1; + } else + tspact &= ~ASM_VC2; + + tspact |= PA_ENABLE; + } +#endif /* TRANSMIT_SUPPORT */ + + tsp_act_update(tspact); +} + +uint32_t rffe_get_rx_ports(void) +{ + return (1 << PORT_LO) | (1 << PORT_DCS1800) | (1 << PORT_PCS1900); +} + +uint32_t rffe_get_tx_ports(void) +{ + return (1 << PORT_LO) | (1 << PORT_HI); +} + +/* Returns need for IQ swap */ +int rffe_iq_swapped(uint16_t band_arfcn, int tx) +{ + return trf6151_iq_swapped(band_arfcn, tx); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 +#define ASIC_CONF_REG 0xfffef008 + +void rffe_init(void) +{ + /* Configure the TSPEN which is connected to the TWL3025 */ + tsp_setup(IOTA_STROBE, 1, 0, 0); + + trf6151_init(RITA_STROBE, RITA_RESET); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +void rffe_set_gain(uint8_t dbm) +{ + trf6151_set_gain(dbm); +} + +const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN; + +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_compute_gain(int16_t exp_inp, int16_t target_bb) +{ + trf6151_compute_gain(exp_inp, target_bb); +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + /* FIXME */ +} diff --git a/src/target/firmware/board/tr800/afcparams.c b/src/target/firmware/board/tr800/afcparams.c new file mode 100644 index 00000000..ccbb3608 --- /dev/null +++ b/src/target/firmware/board/tr800/afcparams.c @@ -0,0 +1,52 @@ +/* + * This code was written by Mychaela Falconia <falcon@freecalypso.org> + * who refuses to claim copyright on it and has released it as public domain + * instead. NO rights reserved, all rights relinquished. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdint.h> +#include <rf/vcxocal.h> + +/* + * Here is a representative set of AFC Psi parameters that has been + * calibrated by iWOW's factory on a TR-800 module, as recorded + * in the /gsm/rf/afcparams file: + * + * Psi_sta_inv: 4387 + * Psi_st: 12 + * Psi_st_32: 783154 + * Psi_st_inv: 5484 + * + * The following AFC slope number is the closest OsmocomBB-style afc_slope + * integer corresponding to these Psi numbers; the true value is somewhere + * between 358 and 359. + * + * Please note that all AFC parameters (both Psi and linear) have been + * calibrated per unit by iWOW's factory, and they do differ from unit + * to unit. Both iWOW and FreeCalypso firmwares make direct use of + * per-unit calibrated numbers, but OsmocomBB architecture cannot make + * use of them - hence AFC performance with OBB may be significantly + * poorer than with either iWOW or FC firmware. The present code has + * been contributed by Mother Mychaela solely as a harm reduction measure, + * and does NOT constitute any kind of approved production solution - + * you've been warned! + */ +int16_t afc_slope = 358; + +/* + * The compiled-in AFC initial DAC value below is the same as was used by + * the old OsmocomBB code written for Mot C1xx phones, but it will normally + * be overridden by the per-unit factory calibration value read from the + * /gsm/rf/afcdac file in FFS. + */ +int16_t afc_initial_dac_value = -700; diff --git a/src/target/firmware/board/tr800/init.c b/src/target/firmware/board/tr800/init.c new file mode 100644 index 00000000..a8f3253f --- /dev/null +++ b/src/target/firmware/board/tr800/init.c @@ -0,0 +1,257 @@ +/* Initialization for the iWOW TR-800 modem */ + +/* + * This code was written by Mychaela Falconia <falcon@freecalypso.org> + * who refuses to claim copyright on it and has released it as public domain + * instead. NO rights reserved, all rights relinquished. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <flash/cfi_flash.h> +#include <tiffs.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <uart.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include "keymap.h" + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define IO_CNTL_REG 0xfffe4804 +#define ARM_CONF_REG 0xfffef006 +#define ASIC_CONF_REG 0xfffef008 +#define IO_CONF_REG 0xfffef00a + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* DSR_MODEM/LPG pin is unconnected - make it LPG dummy output */ + reg |= (1 << 6); + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* + * Calypso signals GPIO0, TSPDI/GPIO4, BCLKX/GPIO6, MCUEN1/GPIO8 and + * MCUEN2/GPIO13 are unused and unconnected inside the TR-800 module. + * Configure them as dummy outputs in order to prevent floating inputs. + */ + writew(0x0215, IO_CONF_REG); + writew(0xDC0E, IO_CNTL_REG); + writew(0x0000, ARMIO_LATCH_OUT); + + /* configure ADD(22), needed for second half of flash */ + reg = readw(ARM_CONF_REG); + reg |= (1 << 3); + writew(reg, ARM_CONF_REG); +} + +/* + * A total of 8 Calypso GPIO/multifunction pins (3 pure GPIO, 5 multifunction) + * are brought out on the TR-800 module, with module users (application board + * designers) explicitly allowed to wire them in whichever way is needed for + * the custom application at hand. 6 of these pins power up as inputs. + * Should the firmware leave them as inputs, or switch them to dummy outputs + * to prevent floating inputs? The answer in FreeCalypso (for TR-800 modules + * rebranded as FC Tango) is a special file written into FFS: /etc/tango-pinmux. + * This board wiring config file tells the firmware what it should do with each + * of the 8 GPIO/multifunction pins in question; the format is defined here: + * + * https://www.freecalypso.org/hg/freecalypso-docs/file/tip/Tango-pinmux + * + * The following function reads /etc/tango-pinmux from FFS and applies the pin + * multiplexing configuration encoded therein. If this file is missing, all + * pins in question are left in their default power-up state. + */ +static void board_pinmux_init(void) +{ + uint8_t pinmux[4]; + int rc; + uint16_t conf_reg, cntl_reg, out_reg; + + rc = tiffs_read_file_fixedlen("/etc/tango-pinmux", pinmux, 4); + if (rc < 0) + return; /* error msg already printed */ + if (rc == 0) { + puts("Warning: /etc/tango-pinmux not found, pins left in default power-up state\n"); + return; + } + /* read-modify-write registers */ + conf_reg = readw(IO_CONF_REG); + cntl_reg = readw(IO_CNTL_REG); + out_reg = readw(ARMIO_LATCH_OUT); + /* GPIO1 */ + if (pinmux[0] & 0x80) { + cntl_reg &= ~(1 << 1); + if (pinmux[0] & 0x01) + out_reg |= (1 << 1); + else + out_reg &= ~(1 << 1); + } + /* GPIO2 */ + if (pinmux[1] & 0x08) { + /* pinmux says it's DCD output - set it high */ + cntl_reg &= ~(1 << 2); + out_reg |= (1 << 2); + } else if (pinmux[1] & 0x02) { + /* generic output */ + cntl_reg &= ~(1 << 2); + if (pinmux[1] & 0x01) + out_reg |= (1 << 2); + else + out_reg &= ~(1 << 2); + } + /* GPIO3 */ + if (pinmux[1] & 0x20) { + /* generic output */ + cntl_reg &= ~(1 << 3); + if (pinmux[1] & 0x10) + out_reg |= (1 << 3); + else + out_reg &= ~(1 << 3); + } + /* MCSI or GPIO? */ + if (pinmux[2] & 0x80) { + /* MCSI pins switch to GPIO */ + conf_reg |= 0x1E0; + writew(conf_reg, IO_CONF_REG); + /* GPIO9 */ + if (pinmux[3] & 0x10) { + cntl_reg &= ~(1 << 9); + if (pinmux[3] & 0x01) + out_reg |= (1 << 9); + else + out_reg &= ~(1 << 9); + } else + cntl_reg |= (1 << 9); + /* GPIO10 */ + if (pinmux[3] & 0x20) { + cntl_reg &= ~(1 << 10); + if (pinmux[3] & 0x02) + out_reg |= (1 << 10); + else + out_reg &= ~(1 << 10); + } + /* GPIO11 */ + if (pinmux[3] & 0x40) { + cntl_reg &= ~(1 << 11); + if (pinmux[3] & 0x04) + out_reg |= (1 << 11); + else + out_reg &= ~(1 << 11); + } + /* GPIO12 */ + if (pinmux[3] & 0x80) { + cntl_reg &= ~(1 << 12); + if (pinmux[3] & 0x08) + out_reg |= (1 << 12); + else + out_reg &= ~(1 << 12); + } + } + writew(out_reg, ARMIO_LATCH_OUT); + writew(cntl_reg, IO_CNTL_REG); +} + +void board_init(int with_irq) +{ + /* + * Configure the memory interface. + * nCS0 and nCS1 are internal flash and RAM - please refer to + * this technical article for an explanation of timing parameters: +https://www.freecalypso.org/hg/freecalypso-docs/file/tip/MEMIF-wait-states + */ + calypso_mem_cfg(CALYPSO_nCS0, 4, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 4, CALYPSO_MEM_16bit, 1); + /* nCS2 and nCS3 are brought out for user-added custom hw */ + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + /* Calypso nCS4 is not brought out on TR-800, hence a dummy */ + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + /* Initialize board-specific GPIO */ + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(with_irq); + calypso_exceptions_install(); + + /* Initialize interrupt controller */ + if (with_irq) + irq_init(); + + /* + * The choice of which UART should be used for what is arbitrary - + * change to taste! + */ + sercomm_bind_uart(UART_MODEM); + cons_bind_uart(UART_IRDA); + + /* initialize MODEM UART to be used for sercomm */ + uart_init(UART_MODEM, with_irq); + uart_baudrate(UART_MODEM, UART_115200); + + /* Initialize IRDA UART to be used for old-school console code. */ + uart_init(UART_IRDA, with_irq); + uart_baudrate(UART_IRDA, UART_115200); + + /* Initialize hardware timers */ + hwtimer_init(); + + /* Initialize DMA controller */ + dma_init(); + + /* Initialize real time clock */ + rtc_init(); + + /* Initialize system timers (uses hwtimer 2) */ + timer_init(); + + /* Initialize keypad driver */ + keypad_init(keymap, with_irq); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); + + /* Initialize TIFFS reader (15 sectors of 64 KiB each) */ + tiffs_init(0x700000, 0x10000, 15); + + /* Initialize configurable pin multiplexing */ + board_pinmux_init(); +} diff --git a/src/target/firmware/board/tr800/keymap.h b/src/target/firmware/board/tr800/keymap.h new file mode 100644 index 00000000..8e77ce2f --- /dev/null +++ b/src/target/firmware/board/tr800/keymap.h @@ -0,0 +1,87 @@ +/* + * This code was written by Mychaela Falconia <falcon@freecalypso.org> + * who refuses to claim copyright on it and has released it as public domain + * instead. NO rights reserved, all rights relinquished. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * The TR-800 module itself does not prescribe any particular keypad + * layout - instead all 5 KBC lines and all 5 KBR lines are simply + * brought out, allowing user applications to implement any desired + * keypad up to 5x5 buttons. When designing keypads for development + * boards (whether TR800-based or "raw" Calypso), Mother Mychaela's + * preference is to follow TI's original D-Sample key layout: + + Main keypad (21 buttons): + + L. Soft R. Soft + 5-way nav + Green Red + 1 2 3 + 4 5 6 + 7 8 9 + * 0 # + + Left side buttons: VOL+ / VOL- + Right side button: generic + + Row/column matrix connections: + + KBC0 KBC1 KBC2 KBC3 KBC4 + KBR0 Green VOL- VOL+ L_Soft Nav_left + KBR1 1 2 3 R_Side Nav_right + KBR2 4 5 6 R_Soft Nav_up + KBR3 7 8 9 unused Nav_down + KBR4 * 0 # unused Nav_center + + The red button is out-of-matrix PWON. + + * If anyone has an original iWOW DSK board, the connection of + * "CALL" and "1" buttons on that board also matches the present + * D-Sample keymap. + */ + +static const uint8_t keymap[] = { + [KEY_0] = 9, + [KEY_1] = 1, + [KEY_2] = 6, + [KEY_3] = 11, + [KEY_4] = 2, + [KEY_5] = 7, + [KEY_6] = 12, + [KEY_7] = 3, + [KEY_8] = 8, + [KEY_9] = 13, + [KEY_STAR] = 4, + [KEY_HASH] = 14, + [KEY_MENU] = 24, + [KEY_LEFT_SB] = 15, + [KEY_RIGHT_SB] = 17, + [KEY_UP] = 22, + [KEY_DOWN] = 23, + [KEY_LEFT] = 20, + [KEY_RIGHT] = 21, + [KEY_OK] = 0, +/* power button is not connected to keypad scan matrix but to TWL3025 */ + [KEY_POWER] = 31, +/* D-Sample left side buttons for volume up/down control */ + [KEY_MINUS] = 5, + [KEY_PLUS] = 10, +/* + * D-Sample right side button can be seen as equivalent to + * Pirelli DP-L10 camera button, except for reversed history: + * D-Sample existed first and was used by the designers of the + * Pirelli DP-L10 phone as their starting point. + */ + [KEY_CAMERA] = 16, +}; diff --git a/src/target/firmware/board/tr800/rffe_leo_quadband.c b/src/target/firmware/board/tr800/rffe_leo_quadband.c new file mode 100644 index 00000000..b1989a60 --- /dev/null +++ b/src/target/firmware/board/tr800/rffe_leo_quadband.c @@ -0,0 +1,194 @@ +/* + * This code was written by Mychaela Falconia <falcon@freecalypso.org> + * who refuses to claim copyright on it and has released it as public domain + * instead. NO rights reserved, all rights relinquished. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * This module implements RFFE control for TI's original Leonardo+ + * quadband RFFE, depicted on page 4 of this 2011-find schematic drawing: + * + * https://www.freecalypso.org/pub/GSM/Calypso/Leonardo_plus_quadband_schem.pdf + * + * This TI-original quadband RFFE is reproduced verbatim on the TR-800 + * packaged module by iWOW. + * + * The present C code is based on ../gta0x/rffe_gta0x_triband.c, + * controlling Openmoko's triband RFFE which is very closely based on + * Leonardo, with only a few control signal permutations. + * + * The present code addition by Mother Mychaela merely brings the TR-800 hw + * target to the same level of support that already existed in OBB since + * forever for Compal/Motorola and Openmoko GTA01/02 targets, and more + * recently GTM900 and FCDEV3B - it does NOT fix the problem of overly + * simplistic RFFE control timing and other oversimplifications which OBB + * exhibits in comparison to the official firmware maintained by the + * custodians of the Calypso+Iota+RF chipset (formerly TI, now FreeCalypso). + * These massive oversimplifications which OBB exhibits in comparison to + * officially approved production firmwares result in OBB's radio transmissions + * being SEVERELY out of compliance (as observed with even the simplest tests + * with a CMU200 RF test instrument), thus anyone who runs the present code + * with Tx enabled outside of a Faraday cage will very likely cause + * interference and disruption to public communication networks! Furthermore, + * if you go on with running OBB with Tx enabled after having read this + * warning, the resulting interference and disruption to public communication + * networks can be considered intentional on your part, which is likely to be + * seen as a more severe offense. + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <calypso/tsp.h> +#include <rf/trf6151.h> + +/* + * OsmocomBB's definition of system inherent gain is similar to what is + * called "magic gain" (GMagic) in TI's architecture, except that TI's + * GMagic includes TRF6151 LNA gain whereas OBB's definition of system + * inherent gain does not. TI's GMagic is also reckoned in half-dB units + * instead of integral dB. + * + * The canonical GMagic number for Leonardo/TR-800 RFFE is 200, both in + * iWOW's original calibration and as confirmed with CMU200 measurements + * at FreeCalypso HQ. GMagic=200 in TI's universe is equivalent to + * OsmocomBB's "system inherent gain" of 73 dB. + */ +#define SYSTEM_INHERENT_GAIN 73 + +/* describe how the RF frontend is wired on Leonardo and TR-800 */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */ +#define PA_BAND_SEL TSPACT(3) /* PA band select, 1=DCS/PCS */ + +/* All FEM controls are low-active */ +#define FEM_7 TSPACT(2) /* FEM pin 7 */ +#define FEM_8 TSPACT(1) /* FEM pin 8 */ +#define FEM_9 TSPACT(4) /* FEM pin 9 */ + +#define IOTA_STROBE TSPEN(0) /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN(2) /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact &= ~PA_BAND_SEL; + tspact |= FEM_7 | FEM_8 | FEM_9; /* low-active */ + + switch (band) { + case GSM_BAND_850: + tspact &= ~FEM_9; + break; + case GSM_BAND_900: + case GSM_BAND_1800: + case GSM_BAND_1900: + break; + default: + /* TODO return/signal error here */ + break; + } + +#ifdef CONFIG_TX_ENABLE + /* Then we selectively set the bits on, if required */ + if (tx) { + switch (band) { + case GSM_BAND_850: + case GSM_BAND_900: + tspact |= FEM_9; + tspact &= ~FEM_7; + break; + case GSM_BAND_1800: + case GSM_BAND_1900: + tspact &= ~FEM_8; + tspact |= PA_BAND_SEL; + break; + default: + break; + } + tspact |= PA_ENABLE; + } +#endif /* TRANSMIT_SUPPORT */ + + tsp_act_update(tspact); +} + +/* Returns RF wiring */ +uint32_t rffe_get_rx_ports(void) +{ + return (1 << PORT_LO) | (1 << PORT_DCS1800) | (1 << PORT_PCS1900); +} + +uint32_t rffe_get_tx_ports(void) +{ + return (1 << PORT_LO) | (1 << PORT_HI); +} + +/* Returns need for IQ swap */ +int rffe_iq_swapped(uint16_t band_arfcn, int tx) +{ + return trf6151_iq_swapped(band_arfcn, tx); +} + + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~(1 << 7); /* TSPACT4 I/O function, not nRDYMEM */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 1); /* TSPACT9 I/O function, not MAS(1) */ + writew(reg, MCU_SW_TRACE); + + /* Configure the TSPEN which is connected to the TWL3025 */ + tsp_setup(IOTA_STROBE, 1, 0, 0); + + trf6151_init(RITA_STROBE, RITA_RESET); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +void rffe_set_gain(uint8_t dbm) +{ + trf6151_set_gain(dbm); +} + +const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN; + +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_compute_gain(int16_t exp_inp, int16_t target_bb) +{ + trf6151_compute_gain(exp_inp, target_bb); +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + /* FIXME */ +} diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c index cf29984a..ba41bacb 100644 --- a/src/target/firmware/calypso/backlight.c +++ b/src/target/firmware/calypso/backlight.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/buzzer.c b/src/target/firmware/calypso/buzzer.c index e76906f9..4d9e60ba 100644 --- a/src/target/firmware/calypso/buzzer.c +++ b/src/target/firmware/calypso/buzzer.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c index 246b6e00..36a4ef11 100644 --- a/src/target/firmware/calypso/clock.c +++ b/src/target/firmware/calypso/clock.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c index 35c5be82..5580cb5f 100644 --- a/src/target/firmware/calypso/dma.c +++ b/src/target/firmware/calypso/dma.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <memory.h> diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c index 235d359b..430c991a 100644 --- a/src/target/firmware/calypso/dsp.c +++ b/src/target/firmware/calypso/dsp.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c index 58783b06..776cd74c 100644 --- a/src/target/firmware/calypso/du.c +++ b/src/target/firmware/calypso/du.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <memory.h> diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c index bf441780..ad7d584c 100644 --- a/src/target/firmware/calypso/i2c.c +++ b/src/target/firmware/calypso/i2c.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c index 136fd55e..335bdc41 100644 --- a/src/target/firmware/calypso/irq.c +++ b/src/target/firmware/calypso/irq.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c index c3c1810b..e4d11fb1 100644 --- a/src/target/firmware/calypso/keypad.c +++ b/src/target/firmware/calypso/keypad.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c index 45d759f3..288a1835 100644 --- a/src/target/firmware/calypso/rtc.c +++ b/src/target/firmware/calypso/rtc.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/sim.c b/src/target/firmware/calypso/sim.c index dc5885c4..ddc96946 100644 --- a/src/target/firmware/calypso/sim.c +++ b/src/target/firmware/calypso/sim.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ /* Uncomment to debug sim */ diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c index 049ac080..a9b937ec 100644 --- a/src/target/firmware/calypso/spi.c +++ b/src/target/firmware/calypso/spi.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c index 1dd55f26..5141a837 100644 --- a/src/target/firmware/calypso/timer.c +++ b/src/target/firmware/calypso/timer.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c index cbeef843..d84136c9 100644 --- a/src/target/firmware/calypso/tpu.c +++ b/src/target/firmware/calypso/tpu.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c index 5d24f48e..22ec538f 100644 --- a/src/target/firmware/calypso/tsp.c +++ b/src/target/firmware/calypso/tsp.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c index ec587de5..8dd96dad 100644 --- a/src/target/firmware/calypso/uart.c +++ b/src/target/firmware/calypso/uart.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <debug.h> diff --git a/src/target/firmware/calypso/uwire.c b/src/target/firmware/calypso/uwire.c index ac8f15e4..214433c8 100644 --- a/src/target/firmware/calypso/uwire.c +++ b/src/target/firmware/calypso/uwire.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c index 08f5acbd..c564e790 100644 --- a/src/target/firmware/comm/msgb.c +++ b/src/target/firmware/comm/msgb.c @@ -11,10 +11,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c index cebd37dc..360dafea 100644 --- a/src/target/firmware/comm/sercomm.c +++ b/src/target/firmware/comm/sercomm.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c index e6b6934f..275ca5a4 100644 --- a/src/target/firmware/comm/sercomm_cons.c +++ b/src/target/firmware/comm/sercomm_cons.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/comm/timer.c b/src/target/firmware/comm/timer.c index ce4f06d3..44ef5519 100644 --- a/src/target/firmware/comm/timer.c +++ b/src/target/firmware/comm/timer.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/fb/fb_bw8.c b/src/target/firmware/fb/fb_bw8.c index 0fc12ee5..95ea2dfc 100644 --- a/src/target/firmware/fb/fb_bw8.c +++ b/src/target/firmware/fb/fb_bw8.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdlib.h> diff --git a/src/target/firmware/fb/fb_dummy.c b/src/target/firmware/fb/fb_dummy.c index cb053de4..b34656f5 100644 --- a/src/target/firmware/fb/fb_dummy.c +++ b/src/target/firmware/fb/fb_dummy.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/fb_k2x0.c b/src/target/firmware/fb/fb_k2x0.c new file mode 100644 index 00000000..7e433cd8 --- /dev/null +++ b/src/target/firmware/fb/fb_k2x0.c @@ -0,0 +1,190 @@ +/* Framebuffer implementation for SE K200i/K220i - + * combined driver for Core Logic CL761ST and S6B33B1X derivative */ + +/* (C) 2022 by Steve Markgraf <steve@steve-m.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <fb/framebuffer.h> +#include <fb/fb_rgb332.h> + +#include <stdint.h> +#include <stdio.h> +#include <delay.h> +#include <memory.h> + +#define K2X0_WIDTH 128 +#define K2X0_HEIGHT 128 + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define CS3_ADDR 0x02000000 + +#define DISPLAY_CMD_ADDR (CS3_ADDR + 0) +#define DISPLAY_DATA_ADDR (CS3_ADDR + 2) +#define CL761_INDEX_ADDR (CS3_ADDR + 4) +#define CL761_DATA_ADDR (CS3_ADDR + 6) + +#define CL761_CLK (1 << 4) +#define CL761_RESET (1 << 9) +#define K2X0_ENABLE_BACKLIGHT (1 << 3) + +static uint8_t fb_k2x0_mem[K2X0_WIDTH * K2X0_HEIGHT]; + +static const uint8_t k2x0_initdata[] = { + 0x2c, /* CMD: Standby Mode off */ + 0x02, /* CMD: Oscillation Mode Set */ + 0x01, /* DATA: oscillator on */ + 0x26, /* CMD: DCDC and AMP ON/OFF set */ + 0x01, /* DATA: Booster 1 on */ + 0x26, /* CMD: DCDC and AMP ON/OFF set */ + 0x09, /* DATA: Booster 1 on, OP-AMP on */ + 0x26, /* CMD: DCDC and AMP ON/OFF set */ + 0x0b, /* DATA: Booster 1 + 2 on, OP-AMP on */ + 0x26, /* CMD: DCDC and AMP ON/OFF set */ + 0x0f, /* DATA: Booster 1 + 2 + 3 on, OP-AMP on */ + 0x10, /* CMD: Driver output mode set */ + 0x00, /* DATA: Display duty: 1/66 */ + 0x20, /* CMD: DC-DC Select */ + 0x01, /* DATA: step up x1.5 */ + 0x24, /* CMD: DCDC Clock Division Set */ + 0x08, /* DATA: fPCK = fOSC/32 */ + 0x28, /* CMD: Temperature Compensation set */ + 0x02, /* DATA: slope -0.10%/degC */ + 0x2a, /* CMD: Contrast Control */ + 0x1d, /* DATA: Constrast Level 29 */ + 0x30, /* CMD: Addressing mode set */ + 0x53, /* DATA: 256 color mode (orignal FW uses 0x13, 65k colors) */ + 0x32, /* CMD: ROW vector mode set */ + 0x0e, /* DATA: every subframe */ + 0x34, /* CMD: N-block inversion set */ + 0x8d, /* DATA: inversion on, every 1 block and every 2 frames */ + 0x36, /* CMD: unknown */ + 0x00, /* DATA: unknown */ + 0x40, /* CMD: Entry mode set */ + 0x80, /* DATA: Y address counter mode */ + 0x45, /* CMD: RAM Skip Area Set */ + 0x00, /* DATA: No Skip */ + 0x53, /* CMD: Specified Display Pattern Set */ + 0x00, /* DATA: Normal display */ + 0x55, /* CMD: Partial Display Mode Set */ + 0x00, /* DATA: Partial display OFF */ + 0x51, /* CMD: Display on */ +}; + +uint16_t cl761_read_reg(uint16_t reg) +{ + writew(reg, CL761_INDEX_ADDR); + return readw(CL761_INDEX_ADDR); +} + +void cl761_write_reg(uint8_t reg, uint16_t data) +{ + writew(reg, CL761_INDEX_ADDR); + writew(data, CL761_DATA_ADDR); +} + +static void fb_k2x0_init(void) +{ + unsigned int i; + uint16_t reg; + + printf("%s: initializing LCD.\n", __FUNCTION__); + + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(CL761_RESET | (1 << 1)); + reg |= CL761_CLK; + writew(reg, ARMIO_LATCH_OUT); + delay_ms(10); + reg |= CL761_RESET; + writew(reg, ARMIO_LATCH_OUT); + + /* we need to perform a dummy register read for the + * CL761 to pass through the chip select to the display */ + cl761_read_reg(0x2e); + delay_ms(1); + + reg &= ~CL761_CLK; + reg |= (1 << 1) | K2X0_ENABLE_BACKLIGHT; + writew(reg, ARMIO_LATCH_OUT); + + for (i = 0; i < sizeof(k2x0_initdata); i++) + writew(k2x0_initdata[i], DISPLAY_CMD_ADDR); +} + +static void fb_k2x0_flush(void) +{ + unsigned int i; + int x, y; + uint8_t *p; + uint8_t prepare_disp_write_cmds[] = { + 0x43, /* set column address */ + fb_rgb332->damage_x1, + fb_rgb332->damage_x2 - 1, + 0x42, /* set page address (Y) */ + fb_rgb332->damage_y1, + fb_rgb332->damage_y2 - 1, + }; + + /* If everything's clean, just return */ + if (fb_rgb332->damage_x1 == fb_rgb332->damage_x2 || + fb_rgb332->damage_y1 == fb_rgb332->damage_y2) { + printf("%s: no damage\n", __FUNCTION__); + return; + } + + for (i = 0; i < sizeof(prepare_disp_write_cmds); i++) + writew(prepare_disp_write_cmds[i], DISPLAY_CMD_ADDR); + + for (y = fb_rgb332->damage_y1; y < fb_rgb332->damage_y2; y++) { + p = & fb_rgb332->mem[y * framebuffer->width]; // start of line + p += fb_rgb332->damage_x1; // start of damage area + + for (x = fb_rgb332->damage_x1; x < fb_rgb332->damage_x2; x++) { + /* For whatever reason, the 256 color mode of this + * display uses 'RBG323' */ + uint8_t d = *p++; + d = (d & 0xe0) | ((d & 0x1c) >> 2) | ((d & 0x03) << 3); + + /* We need to transfer the data twice in the 256 color mode. + * Interestingly, the red and green information is taken + * from the first byte written, and the blue information + * from the second byte written. */ + writew(d, DISPLAY_DATA_ADDR); + writew(d, DISPLAY_DATA_ADDR); + } + } + + fb_rgb332->damage_x1 = fb_rgb332->damage_x2 = 0; + fb_rgb332->damage_y1 = fb_rgb332->damage_y2 = 0; +} + +static struct framebuffer fb_k2x0_framebuffer = { + .name = "k2x0", + .init = fb_k2x0_init, + .clear = fb_rgb332_clear, + .boxto = fb_rgb332_boxto, + .lineto = fb_rgb332_lineto, + .putstr = fb_rgb332_putstr, + .flush = fb_k2x0_flush, + .width = K2X0_WIDTH, + .height = K2X0_HEIGHT +}; + +static struct fb_rgb332 fb_k2x0_rgb332 = { + .mem = fb_k2x0_mem +}; + +struct framebuffer *framebuffer = &fb_k2x0_framebuffer; +struct fb_rgb332 *fb_rgb332 = &fb_k2x0_rgb332; diff --git a/src/target/firmware/fb/fb_rgb332.c b/src/target/firmware/fb/fb_rgb332.c index 569ccc7d..73474888 100644 --- a/src/target/firmware/fb/fb_rgb332.c +++ b/src/target/firmware/fb/fb_rgb332.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/fb_s6b33b1x.c b/src/target/firmware/fb/fb_s6b33b1x.c index e36c5471..28fe5552 100644 --- a/src/target/firmware/fb/fb_s6b33b1x.c +++ b/src/target/firmware/fb/fb_s6b33b1x.c @@ -18,10 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/fb_ssd1783.c b/src/target/firmware/fb/fb_ssd1783.c index cacdce03..3b39ad03 100644 --- a/src/target/firmware/fb/fb_ssd1783.c +++ b/src/target/firmware/fb/fb_ssd1783.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/fb_ssd1963.c b/src/target/firmware/fb/fb_ssd1963.c index 361434e4..dee884dc 100644 --- a/src/target/firmware/fb/fb_ssd1963.c +++ b/src/target/firmware/fb/fb_ssd1963.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/fb_st7558.c b/src/target/firmware/fb/fb_st7558.c index f09b12b7..a2c6f502 100644 --- a/src/target/firmware/fb/fb_st7558.c +++ b/src/target/firmware/fb/fb_st7558.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/fb_td014.c b/src/target/firmware/fb/fb_td014.c index c7bde0ca..b96fe94c 100644 --- a/src/target/firmware/fb/fb_td014.c +++ b/src/target/firmware/fb/fb_td014.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/fb/font.c b/src/target/firmware/fb/font.c index 18c1bfe9..7ca99121 100644 --- a/src/target/firmware/fb/font.c +++ b/src/target/firmware/fb/font.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/font.h> diff --git a/src/target/firmware/fb/framebuffer.c b/src/target/firmware/fb/framebuffer.c index ab547694..5132eeaf 100644 --- a/src/target/firmware/fb/framebuffer.c +++ b/src/target/firmware/fb/framebuffer.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <fb/framebuffer.h> diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c index 2f8cde06..e34fa77f 100644 --- a/src/target/firmware/flash/cfi_flash.c +++ b/src/target/firmware/flash/cfi_flash.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <debug.h> diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h index f2eae091..8f71d018 100644 --- a/src/target/firmware/include/calypso/du.h +++ b/src/target/firmware/include/calypso/du.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef _CALYPSO_DU_H diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h index d49866e2..476a8a86 100644 --- a/src/target/firmware/include/calypso/l1_environment.h +++ b/src/target/firmware/include/calypso/l1_environment.h @@ -104,6 +104,8 @@ typedef signed short API_SIGNED; #define B_PLAY_UL (1 << 3) // Play UL #define B_DCO_ON (1 << 4) // DCO ON/OFF #define B_AUDIO_ASYNC (1 << 1) // WCP reserved +#define B_MUTE_VOCODEC_DL (1 << 14) // DL voice decoder +#define B_MUTE_VOCODEC_UL (1 << 15) // UL voice encoder // **************************************************************** // PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS diff --git a/src/target/firmware/include/calypso/sim.h b/src/target/firmware/include/calypso/sim.h index 8f627b18..686e5781 100755 --- a/src/target/firmware/include/calypso/sim.h +++ b/src/target/firmware/include/calypso/sim.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef _CALYPSO_SIM_H diff --git a/src/target/firmware/include/comm/timer.h b/src/target/firmware/include/comm/timer.h index d43b067e..e7c518dd 100644 --- a/src/target/firmware/include/comm/timer.h +++ b/src/target/firmware/include/comm/timer.h @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef TIMER_H diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h index 221ffcf9..b383d477 100644 --- a/src/target/firmware/include/layer1/async.h +++ b/src/target/firmware/include/layer1/async.h @@ -47,6 +47,9 @@ uint8_t l1a_tch_mode_set(uint8_t mode); /* Set Audio routing mode */ uint8_t l1a_audio_mode_set(uint8_t mode); +/* Set TCH flags */ +uint8_t l1a_tch_flags_set(uint8_t flags); + /* Execute pending L1A completions */ void l1a_compl_execute(void); diff --git a/src/target/firmware/include/layer1/prim.h b/src/target/firmware/include/layer1/prim.h index 30c51ae6..4e34bb23 100644 --- a/src/target/firmware/include/layer1/prim.h +++ b/src/target/firmware/include/layer1/prim.h @@ -20,7 +20,7 @@ void l1s_nb_test(uint8_t base_fn); void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req); void l1a_freq_req(uint32_t fn_sched); -void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra); +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint8_t uic); /* Primitives raw scheduling sets */ extern const struct tdma_sched_item nb_sched_set[]; diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h index dd932421..e0a4412f 100644 --- a/src/target/firmware/include/layer1/sync.h +++ b/src/target/firmware/include/layer1/sync.h @@ -82,6 +82,7 @@ struct l1s_state { uint8_t tch_mode; uint8_t tch_sync; uint8_t audio_mode; + uint8_t tch_flags; /* 3GPP TS 44.014, section 5.1 (Calypso DSP specific numbers) */ enum l1ctl_tch_loop_mode tch_loop_mode; @@ -116,6 +117,7 @@ struct l1s_state { struct { uint8_t ra; + uint8_t uic; } rach; struct { diff --git a/src/target/firmware/include/mtk/emi.h b/src/target/firmware/include/mtk/emi.h index 18184992..ee1399c2 100644 --- a/src/target/firmware/include/mtk/emi.h +++ b/src/target/firmware/include/mtk/emi.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef __MTK_EMI_H_ diff --git a/src/target/firmware/include/mtk/mt6235.h b/src/target/firmware/include/mtk/mt6235.h index fb9d368e..2d36bee6 100644 --- a/src/target/firmware/include/mtk/mt6235.h +++ b/src/target/firmware/include/mtk/mt6235.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef __MT6235_H diff --git a/src/target/firmware/include/mtk/system.h b/src/target/firmware/include/mtk/system.h index 45430291..9c28b56c 100644 --- a/src/target/firmware/include/mtk/system.h +++ b/src/target/firmware/include/mtk/system.h @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef __MTK_SYSTEM_H_ diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c index 1a198e3d..f4533ad3 100644 --- a/src/target/firmware/layer1/afc.c +++ b/src/target/firmware/layer1/afc.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c index b72a6e74..66e7f092 100644 --- a/src/target/firmware/layer1/agc.c +++ b/src/target/firmware/layer1/agc.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/async.c b/src/target/firmware/layer1/async.c index cb2a2a8c..8d76e5cc 100644 --- a/src/target/firmware/layer1/async.c +++ b/src/target/firmware/layer1/async.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -113,6 +109,10 @@ uint8_t l1a_tch_mode_set(uint8_t mode) switch (mode) { case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: l1s.tch_mode = mode; break; default: @@ -129,6 +129,13 @@ uint8_t l1a_audio_mode_set(uint8_t mode) return mode; } +/* Set TCH flags */ +uint8_t l1a_tch_flags_set(uint8_t flags) +{ + l1s.tch_flags = flags; + return flags; +} + /* Initialize asynchronous part of Layer1 */ void l1a_init(void) { diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c index a4bf565b..7211c218 100644 --- a/src/target/firmware/layer1/avg.c +++ b/src/target/firmware/layer1/avg.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c index e7fde232..602e2286 100644 --- a/src/target/firmware/layer1/init.c +++ b/src/target/firmware/layer1/init.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index 4621bfca..f4309e2a 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #define DEBUG @@ -77,72 +73,75 @@ static uint32_t chan_nr2mf_task_mask(uint8_t chan_nr, uint8_t neigh_mode) uint8_t cbits = chan_nr >> 3; uint8_t tn = chan_nr & 0x7; uint8_t lch_idx; - enum mframe_task master_task = MF_TASK_BCCH_NORM; - enum mframe_task second_task = -1; /* optional */ enum mf_type multiframe = 0; uint32_t task_mask = 0x00; +#define TASK_SET(task) \ + task_mask |= (1 << (task)) + if (cbits == 0x01) { lch_idx = 0; - master_task = (tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN; + TASK_SET((tn & 1) ? MF_TASK_TCH_F_ODD : MF_TASK_TCH_F_EVEN); multiframe = (tn & 1) ? MF26ODD : MF26EVEN; } else if ((cbits & 0x1e) == 0x02) { lch_idx = cbits & 0x1; - master_task = MF_TASK_TCH_H_0 + lch_idx; + TASK_SET(MF_TASK_TCH_H_0 + lch_idx); multiframe = (lch_idx & 1) ? MF26ODD : MF26EVEN; } else if ((cbits & 0x1c) == 0x04) { lch_idx = cbits & 0x3; - master_task = MF_TASK_SDCCH4_0 + lch_idx; + TASK_SET(MF_TASK_SDCCH4_0 + lch_idx); multiframe = MF51; } else if ((cbits & 0x18) == 0x08) { lch_idx = cbits & 0x7; - master_task = MF_TASK_SDCCH8_0 + lch_idx; + TASK_SET(MF_TASK_SDCCH8_0 + lch_idx); multiframe = MF51; } else if ((cbits & 0x1f) == 0x18) { /* Osmocom specific extension for PDTCH and PTCCH */ - master_task = MF_TASK_GPRS_PDTCH; - second_task = MF_TASK_GPRS_PTCCH; + TASK_SET(MF_TASK_GPRS_PDTCH); + TASK_SET(MF_TASK_GPRS_PTCCH); /* FIXME: PDCH has different multiframe structure */ multiframe = MFNONE; } else if ((cbits & 0x1f) == 0x19) { /* Osmocom specific extension for CBCH on SDCCH/4 */ - master_task = MF_TASK_SDCCH4_CBCH; + TASK_SET(MF_TASK_SDCCH4_CBCH); multiframe = MF51; } else if ((cbits & 0x1f) == 0x1a) { /* Osmocom specific extension for CBCH on SDCCH/8 */ - master_task = MF_TASK_SDCCH8_CBCH; + TASK_SET(MF_TASK_SDCCH8_CBCH); multiframe = MF51; #if 0 } else if (cbits == 0x10) { /* FIXME: when to do extended BCCH? */ - master_task = MF_TASK_BCCH_NORM; + TASK_SET(MF_TASK_BCCH_NORM); } else if (cbits == 0x11 || cbits == 0x12) { /* FIXME: how to decide CCCH norm/extd? */ - master_task = MF_TASK_BCCH_CCCH; + TASK_SET(MF_TASK_BCCH_CCCH); #endif + } else { + TASK_SET(MF_TASK_BCCH_NORM); } - /* Primary and secondary tasks */ - task_mask |= (1 << master_task); - if (second_task >= 0) /* optional */ - task_mask |= (1 << second_task); - switch (neigh_mode) { case NEIGH_MODE_PM: switch (multiframe) { case MF51: - task_mask |= (1 << MF_TASK_NEIGH_PM51); + TASK_SET(MF_TASK_NEIGH_PM51); break; case MF26EVEN: - task_mask |= (1 << MF_TASK_NEIGH_PM26E); + TASK_SET(MF_TASK_NEIGH_PM26E); break; case MF26ODD: - task_mask |= (1 << MF_TASK_NEIGH_PM26O); + TASK_SET(MF_TASK_NEIGH_PM26O); + break; + default: + /* no neighbor measurement */ break; } break; } +#undef TASK_SET + return task_mask; } @@ -258,8 +257,8 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload; - printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n", - ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc); + printd("L1CTL_DM_EST_REQ (chan_nr=0x%02x, tsc=%u)\n", + ul->chan_nr, est_req->tsc); /* disable neighbour cell measurement of C0 TS 0 */ mframe_disable(MF_TASK_NEIGH_PM51_C0T0); @@ -277,8 +276,12 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) l1s.dedicated.h1.n = est_req->h1.n; for (i=0; i<est_req->h1.n; i++) l1s.dedicated.h1.ma[i] = ntohs(est_req->h1.ma[i]); + printd("L1CTL_DM_EST_REQ indicates H1 (HSN=%u, MAIO=%u, chans=%u)\n", + est_req->h1.hsn, est_req->h1.maio, est_req->h1.n); } else { l1s.dedicated.h0.arfcn = ntohs(est_req->h0.band_arfcn); + printd("L1CTL_DM_EST_REQ indicates H0 (ARFCN=%u)\n", + l1s.dedicated.h0.arfcn & ~ARFCN_FLAG_MASK); } /* TCH config */ @@ -286,6 +289,7 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) /* Mode */ l1a_tch_mode_set(est_req->tch_mode); l1a_audio_mode_set(est_req->audio_mode); + l1a_tch_flags_set(est_req->tch_flags); /* Sync */ l1s.tch_sync = 1; /* can be set without locking */ @@ -347,8 +351,6 @@ static void l1ctl_rx_crypto_req(struct msgb *msg) /* receive a L1CTL_DM_REL_REQ from L23 */ static void l1ctl_rx_dm_rel_req(struct msgb *msg) { - struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; - printd("L1CTL_DM_REL_REQ\n"); l1a_mftask_set(0); l1s.dedicated.type = GSM_DCHAN_NONE; @@ -384,11 +386,10 @@ static void l1ctl_rx_rach_req(struct msgb *msg) struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload; - printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d, combined=%d)\n", - rach_req->ra, ntohs(rach_req->offset), rach_req->combined); + printd("L1CTL_RACH_REQ (ra=0x%02x, offset=%d, combined=%d, uic=0x%02x)\n", + rach_req->ra, ntohs(rach_req->offset), rach_req->combined, rach_req->uic); - l1a_rach_req(ntohs(rach_req->offset), rach_req->combined, - rach_req->ra); + l1a_rach_req(ntohs(rach_req->offset), rach_req->combined, rach_req->ra, rach_req->uic); } /* receive a L1CTL_DATA_REQ from L23 */ @@ -521,7 +522,7 @@ static void l1ctl_rx_ccch_mode_req(struct msgb *msg) } /* Transmit a L1CTL_TCH_MODE_CONF */ -static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode) +static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode, uint8_t tch_flags) { struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TCH_MODE_CONF); struct l1ctl_tch_mode_conf *mode_conf; @@ -529,6 +530,7 @@ static void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode) msgb_put(msg, sizeof(*mode_conf)); mode_conf->tch_mode = tch_mode; mode_conf->audio_mode = audio_mode; + mode_conf->tch_flags = l1s.tch_flags; mode_conf->tch_loop_mode = l1s.tch_loop_mode; l1_queue_for_l2(msg); @@ -542,18 +544,22 @@ static void l1ctl_rx_tch_mode_req(struct msgb *msg) (struct l1ctl_tch_mode_req *) l1h->data; uint8_t tch_mode = tch_mode_req->tch_mode; uint8_t audio_mode = tch_mode_req->audio_mode; + uint8_t tch_flags = tch_mode_req->tch_flags; printd("L1CTL_TCH_MODE_REQ (tch_mode=0x%02x audio_mode=0x%02x)\n", tch_mode, audio_mode); + tch_mode = l1a_tch_mode_set(tch_mode); audio_mode = l1a_audio_mode_set(audio_mode); + tch_flags = l1a_tch_flags_set(tch_flags); audio_set_enabled(tch_mode, audio_mode); l1s.tch_sync = 1; /* Needed for audio to work */ l1s.tch_loop_mode = tch_mode_req->tch_loop_mode; + /* TODO: Handle AMR codecs from tch_mode_req if tch_mode_req->tch_mode==GSM48_CMODE_SPEECH_AMR */ - l1ctl_tx_tch_mode_conf(tch_mode, audio_mode); + l1ctl_tx_tch_mode_conf(tch_mode, audio_mode, tch_flags); } /* receive a L1CTL_NEIGH_PM_REQ from L23 */ @@ -725,4 +731,3 @@ void l1a_l23api_init(void) { sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx); } - diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index fac84b43..478fd20a 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c index 8eaeb5af..4f4c4fd5 100644 --- a/src/target/firmware/layer1/prim_fbsb.c +++ b/src/target/firmware/layer1/prim_fbsb.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/prim_freq.c b/src/target/firmware/layer1/prim_freq.c index d5b5df15..a6ddc545 100644 --- a/src/target/firmware/layer1/prim_freq.c +++ b/src/target/firmware/layer1/prim_freq.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c index 5c8c4914..11bee99b 100644 --- a/src/target/firmware/layer1/prim_pm.c +++ b/src/target/firmware/layer1/prim_pm.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c index e6ea6568..580131ec 100644 --- a/src/target/firmware/layer1/prim_rach.c +++ b/src/target/firmware/layer1/prim_rach.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -66,7 +62,11 @@ static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused ui l1s_tx_apc_helper(l1s.serving_cell.arfcn); - data[0] = l1s.serving_cell.bsic << 2; + /* If an invalid UIC is given, use BSIC. */ + if (l1s.rach.uic > 0x3f) + data[0] = l1s.serving_cell.bsic << 2; + else + data[0] = l1s.rach.uic << 2; data[1] = l1s.rach.ra; info_ptr = &dsp_api.ndb->d_rach; @@ -131,8 +131,8 @@ static uint8_t rach_to_t3_comb[27] = { 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 45, 46}; -/* request a RACH request at the next multiframe T3 = fn51 */ -void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) +/* schedule access burst */ +void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra, uint8_t uic) { uint32_t fn_sched; unsigned long flags; @@ -140,7 +140,19 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) offset += 3; local_firq_save(flags); - if (combined) { + if (l1s.dedicated.type == GSM_DCHAN_TCH_F) { + fn_sched = l1s.current_time.fn + offset; + /* go next DCCH frame TCH/F channel */ + if ((fn_sched % 13) == 12) + fn_sched++; + } else if (l1s.dedicated.type == GSM_DCHAN_TCH_H) { + fn_sched = l1s.current_time.fn + offset; + /* go next DCCH frame of TCH/H channel */ + if ((fn_sched % 13) == 12) + fn_sched++; + if ((l1s.dedicated.tn & 1) != ((fn_sched % 13) & 1)) + fn_sched++; + } else if (combined) { /* add elapsed RACH slots to offset */ offset += t3_to_rach_comb[l1s.current_time.t3]; /* offset is the number of RACH slots in the future */ @@ -150,6 +162,8 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) } else fn_sched = l1s.current_time.fn + offset; l1s.rach.ra = ra; + l1s.rach.uic = uic; + fn_sched %= GSM_MAX_FN; sched_gsmtime(rach_sched_set_ul, fn_sched, 0); local_irq_restore(flags); diff --git a/src/target/firmware/layer1/prim_rx_nb.c b/src/target/firmware/layer1/prim_rx_nb.c index c90c98cb..d6dd82a5 100644 --- a/src/target/firmware/layer1/prim_rx_nb.c +++ b/src/target/firmware/layer1/prim_rx_nb.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -126,7 +122,7 @@ static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3) /* Set SACCH indication in Link IDentifier */ if (mf_task_flags & MF_F_SACCH) rxnb.dl->link_id = 0x40; - if (mf_task_flags & MF_F_PTCCH) + else if (mf_task_flags & MF_F_PTCCH) rxnb.dl->link_id = 0x80; else rxnb.dl->link_id = 0x00; diff --git a/src/target/firmware/layer1/prim_tch.c b/src/target/firmware/layer1/prim_tch.c index 40ef8f3c..1bc842b2 100644 --- a/src/target/firmware/layer1/prim_tch.c +++ b/src/target/firmware/layer1/prim_tch.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> @@ -32,7 +28,6 @@ #include <byteorder.h> #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/codec/codec.h> #include <osmocom/core/msgb.h> #include <calypso/dsp_api.h> #include <calypso/irq.h> @@ -56,45 +51,6 @@ #include <l1ctl_proto.h> -static inline int msb_get_bit(uint8_t *buf, int bn) -{ - int pos_byte = bn >> 3; - int pos_bit = 7 - (bn & 7); - - return (buf[pos_byte] >> pos_bit) & 1; -} - -static inline void msb_set_bit(uint8_t *buf, int bn, int bit) -{ - int pos_byte = bn >> 3; - int pos_bit = 7 - (bn & 7); - - buf[pos_byte] |= (bit << pos_bit); -} - -static void tch_fr_bit_magic(uint8_t *frame, int dl) -{ - uint8_t fr[33]; - int i, di, si; - - memset(fr, 0x00, 33); - - if (dl) - fr[0] = 0xd0; - - for (i = 0; i < 260; i++) { - di = gsm610_bitorder[i]; - si = (i > 181) ? i + 4 : i; - - if (dl) - msb_set_bit(fr, 4 + di, msb_get_bit(frame, si)); - else - msb_set_bit(fr, si, msb_get_bit(frame, 4 + di)); - } - - memcpy(frame, fr, 33); -} - /* This computes various parameters both for the DSP and for * our logic. Not all are used all the time, but it's easier * to build all in one place */ @@ -125,10 +81,34 @@ static void tch_get_params(struct gsm_time *time, uint8_t chan_nr, case GSM48_CMODE_SPEECH_EFR: *tch_mode = *tch_f_hn ? TCH_EFR_MODE : SIG_ONLY_MODE; break; + case GSM48_CMODE_DATA_14k5: + *tch_mode = *tch_f_hn ? TCH_144_MODE : SIG_ONLY_MODE; + break; + case GSM48_CMODE_DATA_12k0: + *tch_mode = *tch_f_hn ? TCH_96_MODE : SIG_ONLY_MODE; + break; + case GSM48_CMODE_DATA_6k0: + *tch_mode = *tch_f_hn ? TCH_48F_MODE : TCH_48H_MODE; + break; + case GSM48_CMODE_DATA_3k6: + *tch_mode = *tch_f_hn ? TCH_24F_MODE : TCH_24H_MODE; + break; default: *tch_mode = SIG_ONLY_MODE; } } + + /* enable/disable the voice decoder (Downlink) */ + if (l1s.audio_mode & AUDIO_RX_SPEAKER) + dsp_api.ndb->d_tch_mode &= ~B_MUTE_VOCODEC_DL; /* unmute */ + else + dsp_api.ndb->d_tch_mode |= B_MUTE_VOCODEC_DL; /* mute */ + + /* enable/disable the voice encoder (Uplink) */ + if (l1s.audio_mode & AUDIO_TX_MICROPHONE) + dsp_api.ndb->d_tch_mode &= ~B_MUTE_VOCODEC_UL; /* unmute */ + else + dsp_api.ndb->d_tch_mode |= B_MUTE_VOCODEC_UL; /* mute */ } @@ -188,14 +168,31 @@ static __attribute__ ((constructor)) void prim_tch_init(void) #define FACCH_MEAS_HIST 8 /* Up to 8 bursts history */ struct l1s_rx_tch_state { struct l1s_meas_hdr meas[FACCH_MEAS_HIST]; + uint8_t meas_id; }; static struct l1s_rx_tch_state rx_tch; +static inline void l1s_tch_meas_avg(struct l1ctl_info_dl *dl, + unsigned int meas_num) +{ + uint32_t avg_snr = 0; + int32_t avg_dbm8 = 0; + unsigned int i; + + for (i = 0; i < meas_num; i++) { + int j = (rx_tch.meas_id + FACCH_MEAS_HIST - i) % FACCH_MEAS_HIST; + avg_snr += rx_tch.meas[j].snr; + avg_dbm8 += rx_tch.meas[j].pm_dbm8; + } + + dl->snr = avg_snr / meas_num; + dl->rx_level = dbm2rxlev(avg_dbm8 / (8 * meas_num)); +} + static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) { - static uint8_t meas_id = 0; uint8_t mf_task_id = p3 & 0xff; struct gsm_time rx_time; uint8_t chan_nr; @@ -211,27 +208,27 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) chan_nr = mframe_task2chan_nr(mf_task_id, tn); tch_get_params(&rx_time, chan_nr, &fn_report, &tch_f_hn, &tch_sub, NULL); - meas_id = (meas_id + 1) % FACCH_MEAS_HIST; /* absolute value doesn't matter */ + rx_tch.meas_id = (rx_tch.meas_id + 1) % FACCH_MEAS_HIST; /* absolute value doesn't matter */ /* Collect measurements */ - rx_tch.meas[meas_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; - rx_tch.meas[meas_id].pm_dbm8 = + rx_tch.meas[rx_tch.meas_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + rx_tch.meas[rx_tch.meas_id].pm_dbm8 = agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3); - rx_tch.meas[meas_id].freq_err = + rx_tch.meas[rx_tch.meas_id].freq_err = ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); - rx_tch.meas[meas_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + rx_tch.meas[rx_tch.meas_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; /* feed computed frequency error into AFC loop */ - if (rx_tch.meas[meas_id].snr > AFC_SNR_THRESHOLD) - afc_input(rx_tch.meas[meas_id].freq_err, arfcn, 1); + if (rx_tch.meas[rx_tch.meas_id].snr > AFC_SNR_THRESHOLD) + afc_input(rx_tch.meas[rx_tch.meas_id].freq_err, arfcn, 1); else - afc_input(rx_tch.meas[meas_id].freq_err, arfcn, 0); + afc_input(rx_tch.meas[rx_tch.meas_id].freq_err, arfcn, 0); /* feed computed TOA into TA loop */ - toa_input(rx_tch.meas[meas_id].toa_qbit << 2, rx_tch.meas[meas_id].snr); + toa_input(rx_tch.meas[rx_tch.meas_id].toa_qbit << 2, rx_tch.meas[rx_tch.meas_id].snr); /* Tell the RF frontend to set the gain appropriately */ - rffe_compute_gain(rx_tch.meas[meas_id].pm_dbm8 / 8, + rffe_compute_gain(rx_tch.meas[rx_tch.meas_id].pm_dbm8 / 8, CAL_DSP_TGT_BB_LVL); /* FACCH Block end ? */ @@ -251,9 +248,6 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) struct l1ctl_info_dl *dl; struct l1ctl_data_ind *di; uint16_t num_biterr; - uint32_t avg_snr = 0; - int32_t avg_dbm8 = 0; - int i, n; /* Allocate msgb */ /* FIXME: we actually want all allocation out of L1S! */ @@ -273,15 +267,7 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) dl->frame_nr = htonl(rx_time.fn); /* Average SNR & RX level */ - n = tch_f_hn ? 8 : 6; - for (i=0; i<n; i++) { - int j = (meas_id + FACCH_MEAS_HIST - i) % FACCH_MEAS_HIST; - avg_snr += rx_tch.meas[j].snr; - avg_dbm8 += rx_tch.meas[j].pm_dbm8; - } - - dl->snr = avg_snr / n; - dl->rx_level = dbm2rxlev(avg_dbm8 / (8*n)); + l1s_tch_meas_avg(dl, tch_f_hn ? 8 : 6); /* Errors & CRC status */ num_biterr = dsp_api.ndb->a_fd[2] & 0xffff; @@ -301,7 +287,7 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) /* Give message to up layer */ l1_queue_for_l2(msg); - skip_rx_facch: +skip_rx_facch: /* Reset A_FD header (needed by DSP) */ /* B_FIRE1 =1, B_FIRE0 =0 , BLUD =0 */ dsp_api.ndb->a_fd[0] = (1<<B_FIRE1); @@ -328,48 +314,63 @@ static int l1s_tch_resp(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) if (traffic_rx_now) { volatile uint16_t *traffic_buf; + struct l1ctl_info_dl *dl; + struct l1ctl_traffic_ind *ti; + struct msgb *msg; + uint16_t num_biterr; traffic_buf = tch_sub ? dsp_api.ndb->a_dd_1 : dsp_api.ndb->a_dd_0; - if (traffic_buf[0] & (1<<B_BLUD)) { - /* Send the data to upper layers (if interested and good frame) */ - if ((l1s.audio_mode & AUDIO_RX_TRAFFIC_IND) && - !(dsp_api.ndb->a_dd_0[0] & (1<<B_BFI))) { - struct msgb *msg; - struct l1ctl_info_dl *dl; - struct l1ctl_traffic_ind *ti; - uint8_t *payload; - - /* Allocate msgb */ - /* FIXME: we actually want all allocation out of L1S! */ - msg = l1ctl_msgb_alloc(L1CTL_TRAFFIC_IND); - if(!msg) { - printf("TCH traffic: unable to allocate msgb\n"); - goto skip_rx_traffic; - } - - dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl)); - ti = (struct l1ctl_traffic_ind *) msgb_put(msg, sizeof(*ti)); - payload = (uint8_t *) msgb_put(msg, 33); - - /* Copy actual data, skipping the information block [0,1,2] */ - dsp_memcpy_from_api(payload, &traffic_buf[3], 33, 1); - - /** - * Perform some bit conversations - * FIXME: what about other (than FR) codecs? - */ - tch_fr_bit_magic(payload, 1); - - /* Give message to up layer */ - l1_queue_for_l2(msg); - } - - skip_rx_traffic: - /* Reset traffic buffer header in NDB (needed by DSP) */ - traffic_buf[0] = 0; - traffic_buf[2] = 0xffff; + /* Send the data to upper layers (if interested and good frame) */ + if (~l1s.audio_mode & AUDIO_RX_TRAFFIC_IND) + goto skip_rx_traffic; + if (~traffic_buf[0] & (1 << B_BLUD)) + goto skip_rx_traffic; + + /* Allocate msgb */ + /* FIXME: we actually want all allocation out of L1S! */ + msg = l1ctl_msgb_alloc(L1CTL_TRAFFIC_IND); + if (!msg) { + printf("TCH traffic: unable to allocate msgb\n"); + goto skip_rx_traffic; } + + dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl)); + ti = (struct l1ctl_traffic_ind *) msgb_put(msg, sizeof(*ti)); + + dl->chan_nr = chan_nr; + dl->band_arfcn = htons(arfcn); + dl->frame_nr = htonl(rx_time.fn); + + /* Average SNR & RX level */ + l1s_tch_meas_avg(dl, tch_f_hn ? 8 : 4); + + /* Errors & CRC status */ + num_biterr = traffic_buf[2] & 0xffff; + if (num_biterr > 0xff) + dl->num_biterr = 0xff; + else + dl->num_biterr = num_biterr; + + dl->fire_crc = ((traffic_buf[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + + /* Update rx level for pm report */ + pu_update_rx_level(dl->rx_level); + + /* Copy actual data, skipping the information block [0,1,2] */ + dsp_memcpy_from_api(msgb_put(msg, 33), &traffic_buf[3], 33, 1); + + /* Give message to up layer */ + l1_queue_for_l2(msg); + +skip_rx_traffic: + /* Reset A_DD_0 header in NDB (needed by DSP) */ + dsp_api.ndb->a_dd_0[0] = 0; + dsp_api.ndb->a_dd_0[2] = 0xffff; + + /* Reset A_DD_1 header in NDB (needed by DSP) */ + dsp_api.ndb->a_dd_1[0] = 0; + dsp_api.ndb->a_dd_1[2] = 0xffff; } /* mark READ page as being used */ @@ -472,7 +473,6 @@ static int l1s_tch_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) if (traffic_tx_now) { volatile uint16_t *traffic_buf; struct msgb *msg; - const uint8_t *data; /* Reset play mode */ dsp_api.ndb->d_tch_mode &= ~B_PLAY_UL; @@ -486,37 +486,25 @@ static int l1s_tch_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) /* Pull Traffic data (if any) */ msg = msgb_dequeue(&l1s.tx_queue[L1S_CHAN_TRAFFIC]); - - /** - * Perform some bit conversations - * FIXME: what about other (than FR) codecs? - */ - if (msg) - tch_fr_bit_magic(msg->l2h, 0); + if (msg == NULL) + goto skip_tx_traffic; /* Copy actual data, skipping the information block [0,1,2] */ - if (msg) { - data = msg->l2h; - dsp_memcpy_to_api(&traffic_buf[3], data, 33, 1); + dsp_memcpy_to_api(&traffic_buf[3], msgb_l2(msg), 33, 1); - traffic_buf[0] = (1 << B_BLUD); /* 1st word: Set B_BLU bit. */ - traffic_buf[1] = 0; /* 2nd word: cleared. */ - traffic_buf[2] = 0; /* 3nd word: cleared. */ - } + traffic_buf[0] = (1 << B_BLUD); /* 1st word: Set B_BLU bit. */ + traffic_buf[1] = 0; /* 2nd word: cleared. */ + traffic_buf[2] = 0; /* 3nd word: cleared. */ - if (msg) - dsp_api.ndb->d_tch_mode |= B_PLAY_UL; + dsp_api.ndb->d_tch_mode |= B_PLAY_UL; /* Indicate completion (FIXME: early but easier this way for now) */ - if (msg) { - last_tx_tch_fn = l1s.next_time.fn; - last_tx_tch_type |= TX_TYPE_TRAFFIC; - l1s_compl_sched(L1_COMPL_TX_TCH); - } + last_tx_tch_fn = l1s.next_time.fn; + last_tx_tch_type |= TX_TYPE_TRAFFIC; + l1s_compl_sched(L1_COMPL_TX_TCH); /* Free msg now that we're done with it */ - if (msg) - msgb_free(msg); + msgb_free(msg); } skip_tx_traffic: @@ -535,6 +523,12 @@ skip_tx_traffic: ); l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + /* If transmission is off, use dummy task for DSP and do not open TX window. */ + if ((l1s.tch_flags & L1CTL_TCH_FLAG_RXONLY)) { + dsp_load_tx_task(TCHD_DSP_TASK, 0, tsc); /* burst_id unused for TCH */ + return 0; + } + dsp_load_tx_task( dsp_task_iq_swap(TCHT_DSP_TASK, arfcn, 1), 0, tsc /* burst_id unused for TCH */ @@ -800,6 +794,10 @@ static int l1s_tch_a_cmd(__unused uint8_t p1, __unused uint8_t p2, uint16_t p3) ); l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, 0); + /* If transmission is off, schedule no task for DSP and do not open TX window. */ + if ((l1s.tch_flags & L1CTL_TCH_FLAG_RXONLY)) + return 0; + dsp_load_tx_task( dsp_task_iq_swap(TCHA_DSP_TASK, arfcn, 1), 0, tsc /* burst_id unused for TCHA */ diff --git a/src/target/firmware/layer1/prim_tx_nb.c b/src/target/firmware/layer1/prim_tx_nb.c index 86e8224c..cf4ea44b 100644 --- a/src/target/firmware/layer1/prim_tx_nb.c +++ b/src/target/firmware/layer1/prim_tx_nb.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/prim_utils.c b/src/target/firmware/layer1/prim_utils.c index 7beab5fe..f19200eb 100644 --- a/src/target/firmware/layer1/prim_utils.c +++ b/src/target/firmware/layer1/prim_utils.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/rfch.c b/src/target/firmware/layer1/rfch.c index 3c4e6764..4c34416f 100644 --- a/src/target/firmware/layer1/rfch.c +++ b/src/target/firmware/layer1/rfch.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/sched_gsmtime.c b/src/target/firmware/layer1/sched_gsmtime.c index 01e22ca3..aec041ec 100644 --- a/src/target/firmware/layer1/sched_gsmtime.c +++ b/src/target/firmware/layer1/sched_gsmtime.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c index 978546b5..1b7eb326 100644 --- a/src/target/firmware/layer1/sync.c +++ b/src/target/firmware/layer1/sync.c @@ -16,10 +16,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c index 88129922..44b88b22 100644 --- a/src/target/firmware/layer1/tdma_sched.c +++ b/src/target/firmware/layer1/tdma_sched.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/toa.c b/src/target/firmware/layer1/toa.c index 7d80d952..4c9fff8d 100644 --- a/src/target/firmware/layer1/toa.c +++ b/src/target/firmware/layer1/toa.c @@ -15,10 +15,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c index f4e76c16..7baa1d35 100644 --- a/src/target/firmware/layer1/tpu_window.c +++ b/src/target/firmware/layer1/tpu_window.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c index 6bc8fede..df2adab1 100644 --- a/src/target/firmware/lib/console.c +++ b/src/target/firmware/lib/console.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/rf/mt6139.c b/src/target/firmware/rf/mt6139.c index d48e6529..fab36ba5 100644 --- a/src/target/firmware/rf/mt6139.c +++ b/src/target/firmware/rf/mt6139.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c index ec139282..5c995d64 100644 --- a/src/target/firmware/rf/trf6151.c +++ b/src/target/firmware/rf/trf6151.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> diff --git a/src/target/firmware/solve_envs.py b/src/target/firmware/solve_envs.py index b0108971..da1b0e10 100755 --- a/src/target/firmware/solve_envs.py +++ b/src/target/firmware/solve_envs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys diff --git a/src/target/firmware/tiffs/init.c b/src/target/firmware/tiffs/init.c index 378f8291..a6c38e3a 100644 --- a/src/target/firmware/tiffs/init.c +++ b/src/target/firmware/tiffs/init.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/firmware/tiffs/readfile.c b/src/target/firmware/tiffs/readfile.c index c712c437..c93b1067 100644 --- a/src/target/firmware/tiffs/readfile.c +++ b/src/target/firmware/tiffs/readfile.c @@ -17,10 +17,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> diff --git a/src/target/trx_toolkit/app_common.py b/src/target/trx_toolkit/app_common.py index 7f2f03c5..531d8dcf 100644 --- a/src/target/trx_toolkit/app_common.py +++ b/src/target/trx_toolkit/app_common.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log diff --git a/src/target/trx_toolkit/burst_fwd.py b/src/target/trx_toolkit/burst_fwd.py index 2e9e97b5..69245317 100644 --- a/src/target/trx_toolkit/burst_fwd.py +++ b/src/target/trx_toolkit/burst_fwd.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log @@ -62,8 +58,6 @@ class BurstForwarder(TRXList): # Check transceiver state if not trx.running: continue - if rx_msg.tn not in trx.ts_list: - continue # Match Tx/Rx frequencies of the both transceivers if trx.get_rx_freq(rx_msg.fn) != tx_freq: diff --git a/src/target/trx_toolkit/burst_gen.py b/src/target/trx_toolkit/burst_gen.py index f93d8685..22f72053 100755 --- a/src/target/trx_toolkit/burst_gen.py +++ b/src/target/trx_toolkit/burst_gen.py @@ -18,10 +18,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")] diff --git a/src/target/trx_toolkit/burst_send.py b/src/target/trx_toolkit/burst_send.py index 4e165711..27f585e0 100755 --- a/src/target/trx_toolkit/burst_send.py +++ b/src/target/trx_toolkit/burst_send.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. APP_CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy <axilirator@gmail.com>")] diff --git a/src/target/trx_toolkit/clck_gen.py b/src/target/trx_toolkit/clck_gen.py index 51f04a82..427eb88f 100755 --- a/src/target/trx_toolkit/clck_gen.py +++ b/src/target/trx_toolkit/clck_gen.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")] diff --git a/src/target/trx_toolkit/codec.py b/src/target/trx_toolkit/codec.py index 93875923..c5706009 100644 --- a/src/target/trx_toolkit/codec.py +++ b/src/target/trx_toolkit/codec.py @@ -21,10 +21,6 @@ Inspired by Pycrate and Scapy. # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from typing import Optional, Callable, Tuple, Any import abc diff --git a/src/target/trx_toolkit/ctrl_cmd.py b/src/target/trx_toolkit/ctrl_cmd.py index 7a2bfacf..c6ac6d84 100755 --- a/src/target/trx_toolkit/ctrl_cmd.py +++ b/src/target/trx_toolkit/ctrl_cmd.py @@ -18,10 +18,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. APP_CR_HOLDERS = [("2017-2018", "Vadim Yanitskiy <axilirator@gmail.com>")] diff --git a/src/target/trx_toolkit/ctrl_if.py b/src/target/trx_toolkit/ctrl_if.py index 4f440807..f9d30c19 100644 --- a/src/target/trx_toolkit/ctrl_if.py +++ b/src/target/trx_toolkit/ctrl_if.py @@ -17,12 +17,9 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log +import time from udp_link import UDPLink @@ -31,6 +28,9 @@ class CTRLInterface(UDPLink): UDPLink.__init__(self, *udp_link_args) log.debug("Init TRXC interface (%s)" % self.desc_link()) + # Do not delay RSP messages by default + self.rsp_delay_ms = 0 + def handle_rx(self): # Read data from socket data, remote = self.sock.recvfrom(128) @@ -86,6 +86,9 @@ class CTRLInterface(UDPLink): # Add the response signature, and join back to string response = "RSP " + " ".join(request) + "\0" + # If configured, delay sending the RSP message + if self.rsp_delay_ms > 0: + time.sleep(self.rsp_delay_ms / 1000.0) # Now we have something like "RSP TXTUNE 0 941600" self.sendto(response, remote) diff --git a/src/target/trx_toolkit/ctrl_if_trx.py b/src/target/trx_toolkit/ctrl_if_trx.py index c03dcc60..e6fdaf1d 100644 --- a/src/target/trx_toolkit/ctrl_if_trx.py +++ b/src/target/trx_toolkit/ctrl_if_trx.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log @@ -111,10 +107,7 @@ class CTRLInterfaceTRX(CTRLInterface): return -1 log.info("(%s) Starting transceiver..." % self.trx) - self.trx.running = True - - # Notify transceiver about that - self.trx.power_event_handler("POWERON") + self.trx.power_event_handler(poweron=True) return 0 @@ -122,10 +115,7 @@ class CTRLInterfaceTRX(CTRLInterface): log.debug("(%s) Recv POWEROFF cmd" % self.trx) log.info("(%s) Stopping transceiver..." % self.trx) - self.trx.running = False - - # Notify transceiver about that - self.trx.power_event_handler("POWEROFF") + self.trx.power_event_handler(poweron=False) return 0 @@ -144,32 +134,6 @@ class CTRLInterfaceTRX(CTRLInterface): self.trx._tx_freq = int(request[1]) * 1000 return 0 - elif self.verify_cmd(request, "SETSLOT", 2): - log.debug("(%s) Recv SETSLOT cmd" % self.trx) - - # Obtain TS index - ts = int(request[1]) - if ts not in range(0, 8): - log.error("(%s) TS index should be in " - "range: 0..7" % self.trx) - return -1 - - # Parse TS type - ts_type = int(request[2]) - - # TS activation / deactivation - # We don't care about ts_type - if ts_type == 0: - # Deactivate TS (remove from the list of active timeslots) - if ts in self.trx.ts_list: - self.trx.ts_list.remove(ts) - else: - # Activate TS (add to the list of active timeslots) - if ts not in self.trx.ts_list: - self.trx.ts_list.append(ts) - - return 0 - # Power measurement if self.verify_cmd(request, "MEASURE", 1): log.debug("(%s) Recv MEASURE cmd" % self.trx) diff --git a/src/target/trx_toolkit/data_dump.py b/src/target/trx_toolkit/data_dump.py index 8510e2d0..8475ceb2 100644 --- a/src/target/trx_toolkit/data_dump.py +++ b/src/target/trx_toolkit/data_dump.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log import struct diff --git a/src/target/trx_toolkit/data_if.py b/src/target/trx_toolkit/data_if.py index 1cded9bb..8e808943 100644 --- a/src/target/trx_toolkit/data_if.py +++ b/src/target/trx_toolkit/data_if.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log diff --git a/src/target/trx_toolkit/data_msg.py b/src/target/trx_toolkit/data_msg.py index 72e248da..898a4ae6 100644 --- a/src/target/trx_toolkit/data_msg.py +++ b/src/target/trx_toolkit/data_msg.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import random import struct diff --git a/src/target/trx_toolkit/fake_pm.py b/src/target/trx_toolkit/fake_pm.py index 93312125..205596a9 100644 --- a/src/target/trx_toolkit/fake_pm.py +++ b/src/target/trx_toolkit/fake_pm.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from random import randint diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py index d519a9ae..0daecb40 100755 --- a/src/target/trx_toolkit/fake_trx.py +++ b/src/target/trx_toolkit/fake_trx.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. APP_CR_HOLDERS = [("2017-2019", "Vadim Yanitskiy <axilirator@gmail.com>")] @@ -374,6 +370,15 @@ class FakeTRX(Transceiver): self.burst_drop_period = period return 0 + # Artificial delay for the TRXC interface + # Syntax: CMD FAKE_TRXC_DELAY <DELAY_MS> + elif self.ctrl_if.verify_cmd(request, "FAKE_TRXC_DELAY", 1): + log.debug("(%s) Recv FAKE_TRXC_DELAY cmd", self) + + self.ctrl_if.rsp_delay_ms = int(request[1]) + log.info("(%s) Artificial TRXC delay set to %d", + self, self.ctrl_if.rsp_delay_ms) + # Unhandled command return None @@ -401,34 +406,31 @@ class Application(ApplicationBase): self.fake_pm.trx_list = self.trx_list # Init TRX instance for BTS - self.append_trx(self.argv.bts_addr, - self.argv.bts_base_port, name = "BTS") + self.append_trx(self.argv.bts_addr, self.argv.bts_base_port, name = "BTS") # Init TRX instance for BB - self.append_trx(self.argv.bb_addr, - self.argv.bb_base_port, name = "MS") + self.append_trx(self.argv.bb_addr, self.argv.bb_base_port, name = "MS", child_mgt = False) # Additional transceivers (optional) if self.argv.trx_list is not None: for trx_def in self.argv.trx_list: (name, addr, port, idx) = trx_def - self.append_child_trx(addr, port, idx, name) + self.append_child_trx(addr, port, name = name, child_idx = idx) # Burst forwarding between transceivers self.burst_fwd = BurstForwarder(self.trx_list.trx_list) log.info("Init complete") - def append_trx(self, remote_addr, base_port, name = None): + def append_trx(self, remote_addr, base_port, **kwargs): trx = FakeTRX(self.argv.trx_bind_addr, remote_addr, base_port, - clck_gen = self.clck_gen, pwr_meas = self.fake_pm, - name = name) + clck_gen = self.clck_gen, pwr_meas = self.fake_pm, **kwargs) self.trx_list.add_trx(trx) - def append_child_trx(self, remote_addr, base_port, child_idx, name = None): - # Index 0 corresponds to the first transceiver - if child_idx == 0: - self.append_trx(remote_addr, base_port, name) + def append_child_trx(self, remote_addr, base_port, **kwargs): + child_idx = kwargs.get("child_idx", 0) + if child_idx == 0: # Index 0 indicates parent transceiver + self.append_trx(remote_addr, base_port, **kwargs) return # Find 'parent' transceiver for a new child @@ -439,7 +441,7 @@ class Application(ApplicationBase): # Allocate a new child trx_child = FakeTRX(self.argv.trx_bind_addr, remote_addr, base_port, - child_idx = child_idx, pwr_meas = self.fake_pm, name = name) + pwr_meas = self.fake_pm, **kwargs) self.trx_list.add_trx(trx_child) # Link a new 'child' with its 'parent' diff --git a/src/target/trx_toolkit/gsm_shared.py b/src/target/trx_toolkit/gsm_shared.py index 7c50245a..5f59ba6f 100644 --- a/src/target/trx_toolkit/gsm_shared.py +++ b/src/target/trx_toolkit/gsm_shared.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from enum import Enum diff --git a/src/target/trx_toolkit/rand_burst_gen.py b/src/target/trx_toolkit/rand_burst_gen.py index b9846ab2..c474c759 100644 --- a/src/target/trx_toolkit/rand_burst_gen.py +++ b/src/target/trx_toolkit/rand_burst_gen.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import random diff --git a/src/target/trx_toolkit/test_codec.py b/src/target/trx_toolkit/test_codec.py index ca2f773e..5049f525 100644 --- a/src/target/trx_toolkit/test_codec.py +++ b/src/target/trx_toolkit/test_codec.py @@ -19,10 +19,6 @@ Unit tests for declarative message codec. # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import unittest import struct diff --git a/src/target/trx_toolkit/test_data_dump.py b/src/target/trx_toolkit/test_data_dump.py index f7b4fde4..67ff32b8 100644 --- a/src/target/trx_toolkit/test_data_dump.py +++ b/src/target/trx_toolkit/test_data_dump.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import unittest import tempfile diff --git a/src/target/trx_toolkit/test_data_msg.py b/src/target/trx_toolkit/test_data_msg.py index 24fda673..ec6c2ec1 100644 --- a/src/target/trx_toolkit/test_data_msg.py +++ b/src/target/trx_toolkit/test_data_msg.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import unittest diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py index b48dffb9..ffd18abd 100644 --- a/src/target/trx_toolkit/transceiver.py +++ b/src/target/trx_toolkit/transceiver.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import logging as log @@ -47,16 +43,6 @@ class Transceiver: NOTE: CLCK is not required for some L1 implementations, so it is optional. - == Timeslot configuration - - Transceiver has a list of active (i.e. configured) TDMA timeslots. - The L1 should configure a timeslot before sending or expecting any - data on it. This is done by SETSLOT control command, which also - indicates an associated channel combination (see GSM TS 05.02). - - NOTE: we don't store the associated channel combinations, - as they are only useful for burst detection and demodulation. - == Child transceivers A BTS can (optionally) have more than one transceiver. In this case @@ -69,8 +55,9 @@ class Transceiver: (trx_2) ctrl=5705, data=5706. ... - As soon as the first transceiver is powered on / off, - all child transceivers are also powered on / off. + By default, powering on/off a parent transceiver (child_idx=0) will + automatically power on/off its child transceivers (if any). This + behavior can be disabled by setting "child_mgt" param to False. == Clock distribution (optional) @@ -124,42 +111,42 @@ class Transceiver: """ - def __init__(self, bind_addr, remote_addr, base_port, name = None, - child_idx = 0, clck_gen = None, pwr_meas = None): + def __init__(self, bind_addr, remote_addr, base_port, **kwargs): # Connection info self.remote_addr = remote_addr self.bind_addr = bind_addr self.base_port = base_port - self.child_idx = child_idx + self.child_idx = kwargs.get("child_idx", 0) + self.child_mgt = kwargs.get("child_mgt", True) # Meta info - self.name = name + self.name = kwargs.get("name", None) log.info("Init transceiver '%s'" % self) # Child transceiver cannot have its own clock - if clck_gen is not None and child_idx > 0: + self.clck_gen = kwargs.get("clck_gen", None) + if self.clck_gen is not None and self.child_idx > 0: raise TypeError("Child transceiver cannot have its own clock") # Init DATA interface self.data_if = DATAInterface( - remote_addr, base_port + child_idx * 2 + 102, - bind_addr, base_port + child_idx * 2 + 2) + remote_addr, base_port + self.child_idx * 2 + 102, + bind_addr, base_port + self.child_idx * 2 + 2) # Init CTRL interface self.ctrl_if = CTRLInterfaceTRX(self, - remote_addr, base_port + child_idx * 2 + 101, - bind_addr, base_port + child_idx * 2 + 1) + remote_addr, base_port + self.child_idx * 2 + 101, + bind_addr, base_port + self.child_idx * 2 + 1) # Init optional CLCK interface - self.clck_gen = clck_gen - if clck_gen is not None: + if self.clck_gen is not None: self.clck_if = UDPLink( remote_addr, base_port + 100, bind_addr, base_port) # Optional Power Measurement interface - self.pwr_meas = pwr_meas + self.pwr_meas = kwargs.get("pwr_meas", None) # Internal state self.running = False @@ -171,9 +158,6 @@ class Transceiver: # Frequency hopping parameters (set by CTRL) self.fh = None - # List of active (configured) timeslots - self.ts_list = [] - # List of child transceivers self.child_trx_list = TRXList() @@ -226,19 +210,18 @@ class Transceiver: def ctrl_cmd_handler(self, request): return None - def power_event_handler(self, event): - # Update child transceivers - for trx in self.child_trx_list.trx_list: - if event == "POWERON": - trx.running = True - elif event == "POWEROFF": - trx.running = False + def power_event_handler(self, poweron: bool) -> None: + # If self.child_mgt is True, automatically power on/off children + if self.child_mgt and self.child_idx == 0: + trx_list = [self, *self.child_trx_list.trx_list] + else: + trx_list = [self] + # Update self and optionally child transceivers + for trx in trx_list: + trx.running = poweron + if not poweron: trx.disable_fh() - # Reset frequency hopping parameters - if event == "POWEROFF": - self.disable_fh() - # Trigger clock generator if required if self.clck_gen is not None: clck_links = self.clck_gen.clck_links @@ -268,13 +251,6 @@ class Transceiver: "is not running => dropping..." % (self, msg.desc_hdr())) return None - # Make sure that indicated timeslot is configured - # Pass PDUs without burst bits, they will be sent as NOPE.ind - if msg.tn not in self.ts_list and msg.burst: - log.warning("(%s) RX TRXD message (%s), but timeslot is not " - "configured => dropping..." % (self, msg.desc_hdr())) - return None - return msg def handle_data_msg(self, msg): diff --git a/src/target/trx_toolkit/trx_list.py b/src/target/trx_toolkit/trx_list.py index 0a55cc64..c476a7d0 100644 --- a/src/target/trx_toolkit/trx_list.py +++ b/src/target/trx_toolkit/trx_list.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. class TRXList: """ Transceiver list implementation. diff --git a/src/target/trx_toolkit/trx_sniff.py b/src/target/trx_toolkit/trx_sniff.py index 8b6f80c7..c91e3e0b 100755 --- a/src/target/trx_toolkit/trx_sniff.py +++ b/src/target/trx_toolkit/trx_sniff.py @@ -17,10 +17,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. APP_CR_HOLDERS = [("2018-2020", "Vadim Yanitskiy <axilirator@gmail.com>")] diff --git a/src/target/trx_toolkit/trxd_proto.py b/src/target/trx_toolkit/trxd_proto.py index 21fe31ea..a1bf1b53 100644 --- a/src/target/trx_toolkit/trxd_proto.py +++ b/src/target/trx_toolkit/trxd_proto.py @@ -20,10 +20,6 @@ TRXD PDU definitions based on declarative codec. # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import codec diff --git a/src/target/trx_toolkit/udp_link.py b/src/target/trx_toolkit/udp_link.py index 43a4815a..c3f14765 100644 --- a/src/target/trx_toolkit/udp_link.py +++ b/src/target/trx_toolkit/udp_link.py @@ -16,10 +16,6 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import socket diff --git a/src/target/ui-experiment/font.h b/src/target/ui-experiment/font.h Binary files differindex 5dc6eae7..c75a0f88 100644 --- a/src/target/ui-experiment/font.h +++ b/src/target/ui-experiment/font.h |