mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-bts.git
synced 2025-11-16 03:41:35 +00:00
Compare commits
149 Commits
shared/31c
...
0.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1fb0fa3af | ||
|
|
caa648d92e | ||
|
|
88a31e2a99 | ||
|
|
307bfc81c1 | ||
|
|
5becc4613a | ||
|
|
b812839dfa | ||
|
|
ae525a8761 | ||
|
|
29ea40f538 | ||
|
|
e9abc5a4f3 | ||
|
|
17be7fa73b | ||
|
|
68e8b2b1d5 | ||
|
|
391ff14977 | ||
|
|
cf18dcd5fd | ||
|
|
6fceaca584 | ||
|
|
ddc0bf14d5 | ||
|
|
deb01a2652 | ||
|
|
3cfc9d5fa3 | ||
|
|
a7d0c5ef5a | ||
|
|
f39c739bd6 | ||
|
|
f66f5b3ddc | ||
|
|
c241afa87c | ||
|
|
178d618d5a | ||
|
|
c9ddb2ba22 | ||
|
|
73d3f46994 | ||
|
|
b2482a8574 | ||
|
|
812fdd92c7 | ||
|
|
ec6225e3e0 | ||
|
|
ef6eb5442c | ||
|
|
f0072a8de8 | ||
|
|
3cf28aa924 | ||
|
|
578340c7a7 | ||
|
|
3caf3b7c45 | ||
|
|
ee47913389 | ||
|
|
f5aaf523c5 | ||
|
|
8c8998e551 | ||
|
|
da0c44a9db | ||
|
|
798c1bba9d | ||
|
|
db0b93ac39 | ||
|
|
86c936cbb1 | ||
|
|
b9a917a138 | ||
|
|
6527dffc94 | ||
|
|
fb04746bce | ||
|
|
05597a7ddb | ||
|
|
82676c13ee | ||
|
|
c2ee307fd4 | ||
|
|
2e4a26a0e9 | ||
|
|
6508f21130 | ||
|
|
c5241c3aa4 | ||
|
|
c910a332b2 | ||
|
|
f62a64e440 | ||
|
|
a7f5e07712 | ||
|
|
5e2341411f | ||
|
|
917cf7018b | ||
|
|
84b9a44535 | ||
|
|
7ff22823ca | ||
|
|
9855e8bd48 | ||
|
|
219ece83a3 | ||
|
|
889890da43 | ||
|
|
23a5183767 | ||
|
|
ce0f20b597 | ||
|
|
7bd6e8b89b | ||
|
|
d692b6e054 | ||
|
|
89e36c0e64 | ||
|
|
801c182c02 | ||
|
|
7451ce29a7 | ||
|
|
450d32919a | ||
|
|
78b2080027 | ||
|
|
9de67ca962 | ||
|
|
b9880bc812 | ||
|
|
d10eaee4cc | ||
|
|
b104aed5ec | ||
|
|
cd463dd72a | ||
|
|
7d684d6866 | ||
|
|
e0959e7929 | ||
|
|
2ea68e2b7b | ||
|
|
2c8787224f | ||
|
|
74d63b7212 | ||
|
|
d0603d96e9 | ||
|
|
414faaca19 | ||
|
|
7a0d11dd68 | ||
|
|
cd0581d815 | ||
|
|
1de7085d31 | ||
|
|
acc71ffb4b | ||
|
|
c64fa4f888 | ||
|
|
79bc80102c | ||
|
|
5fa388c366 | ||
|
|
75f105bbb5 | ||
|
|
2340b88ede | ||
|
|
329085a8ff | ||
|
|
819b50e1a7 | ||
|
|
f449842053 | ||
|
|
9cfbf27d4c | ||
|
|
a450ef73ed | ||
|
|
04b5d65575 | ||
|
|
90e543bd83 | ||
|
|
75caaf2949 | ||
|
|
5027e122a8 | ||
|
|
a313bb0a47 | ||
|
|
923e324abc | ||
|
|
bac087c207 | ||
|
|
80f039973e | ||
|
|
3a381367a6 | ||
|
|
12472df8f0 | ||
|
|
7cc199ea95 | ||
|
|
793e713c4b | ||
|
|
faba84b9b7 | ||
|
|
7cf313c75b | ||
|
|
21b5e6318e | ||
|
|
4fe00da9f8 | ||
|
|
75be092b99 | ||
|
|
c9441b3c0b | ||
|
|
ace9a8742f | ||
|
|
54eceac257 | ||
|
|
d410eb9787 | ||
|
|
9ae5b50d78 | ||
|
|
52476fc1d4 | ||
|
|
e969f08892 | ||
|
|
7b1b832618 | ||
|
|
e0146997a6 | ||
|
|
a391d3691a | ||
|
|
5e90f2a809 | ||
|
|
1eaa3d72ea | ||
|
|
668f8df3be | ||
|
|
cc4a08bdc7 | ||
|
|
862807504b | ||
|
|
a7c276b72b | ||
|
|
f869a95f3b | ||
|
|
0ddd4b6c25 | ||
|
|
24839068f5 | ||
|
|
b631bd21d2 | ||
|
|
579651bf30 | ||
|
|
0d6946741c | ||
|
|
7e10bd6401 | ||
|
|
84e4dd92d4 | ||
|
|
55da9874c0 | ||
|
|
d8d5f5904f | ||
|
|
9acc82ce4a | ||
|
|
c017e309c4 | ||
|
|
2e59b20204 | ||
|
|
fd425b1484 | ||
|
|
50131c125e | ||
|
|
5a03e129a6 | ||
|
|
b7ebf545e6 | ||
|
|
8fc2630dd4 | ||
|
|
bd988f6ad3 | ||
|
|
1e245336ec | ||
|
|
4457c0d9ba | ||
|
|
660116fb9d | ||
|
|
b15d2c9d2f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -29,6 +29,7 @@ src/osmo-bts-sysmo/sysmobts
|
||||
src/osmo-bts-sysmo/sysmobts-remote
|
||||
src/osmo-bts-sysmo/sysmobts-mgr
|
||||
|
||||
src/osmo-bts-trx/osmobts-trx
|
||||
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
@@ -37,6 +38,8 @@ tests/paging/paging_test
|
||||
tests/cipher/cipher_test
|
||||
tests/sysmobts/sysmobts_test
|
||||
tests/misc/misc_test
|
||||
tests/bursts/bursts_test
|
||||
tests/handover/handover_test
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
|
||||
|
||||
13
README
13
README
@@ -1,10 +1,10 @@
|
||||
Repository for new the Osmocom BTS implementation.
|
||||
Repository forw the Osmocom BTS implementation.
|
||||
|
||||
This code implementes the Layer 2 and higher of a more or less
|
||||
conventional GSM BTS (Base Transceiver Station) - however, using an
|
||||
Abis/IP interface, rather than the old-fashioned E1/T1.
|
||||
|
||||
Specificallt, this includes
|
||||
Specifically, this includes
|
||||
* BTS-Side implementation of TS 08.58 (RSL) and TS 12.21 (OML)
|
||||
* BTS-Side implementation of LAPDm (using libosmocore/libosmogsm)
|
||||
* A somewhat separated interface between those higher layer parts
|
||||
@@ -19,12 +19,12 @@ for the Calypso DSP code have not been written yet. This would still
|
||||
require a lot of work.
|
||||
|
||||
Some additional work is being done in using some parts of the OpenBTS
|
||||
L1FEC and glue it against omso-bts. However, this is also still in an
|
||||
early, experimental stage.
|
||||
L1FEC and glue it against omso-bts. This code is called osmo-trx and
|
||||
requires the jolly/trx branch of this repository.
|
||||
|
||||
== Known Limitations ==
|
||||
|
||||
As of June 3, 2012, the following known limitations exist in this
|
||||
As of August 20, 2015, the following known limitations exist in this
|
||||
implementation:
|
||||
|
||||
=== Common Core ===
|
||||
@@ -37,7 +37,6 @@ implementation:
|
||||
* No support for frequency hopping
|
||||
* No reporting of interference levels as part of TS 08.58 RF RES IND
|
||||
* No error reporting in case PAGING COMMAND fails due to queue overflow
|
||||
* No hand-over support (planned)
|
||||
* No use of TS 08.58 BS Power and MS Power parameters
|
||||
* No support of TS 08.58 MultiRate Control
|
||||
* No support of TS 08.58 Supported Codec Types
|
||||
@@ -45,13 +44,11 @@ implementation:
|
||||
|
||||
=== osmo-bts-sysmo ===
|
||||
* No CSD / ECSD support (not planned)
|
||||
* No GPRS/EDGE support (planned)
|
||||
* GSM-R frequency band supported, but no NCH/ASCI/SoLSA
|
||||
* All timeslots on one TRX have to use same training sequence (TSC)
|
||||
* No multi-TRX support yet, though hardware+L1 support stacking
|
||||
* Makes no use of 12.21 Intave Parameters and Interference
|
||||
Level Boundaries
|
||||
* Makes no use of TS 12.21 T3105
|
||||
* Doesn't yet include MAC address in Abis/IP Identity message
|
||||
* MphConfig.CNF can be returned to the wrong callback. E.g. with Tx Power
|
||||
and ciphering. The dispatch should take a look at the hLayer3.
|
||||
|
||||
13
configure.ac
13
configure.ac
@@ -27,6 +27,8 @@ PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 0.0.7)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.3.3)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis)
|
||||
PKG_CHECK_MODULES(LIBGPS, libgps)
|
||||
PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec)
|
||||
|
||||
AC_MSG_CHECKING([whether to enable sysmocom-bts hardware support])
|
||||
AC_ARG_ENABLE(sysmocom-bts,
|
||||
@@ -36,6 +38,14 @@ AC_ARG_ENABLE(sysmocom-bts,
|
||||
AC_MSG_RESULT([$enable_sysmocom_bts])
|
||||
AM_CONDITIONAL(ENABLE_SYSMOBTS, test "x$enable_sysmocom_bts" = "xyes")
|
||||
|
||||
AC_MSG_CHECKING([whether to enable trx hardware support])
|
||||
AC_ARG_ENABLE(trx,
|
||||
AC_HELP_STRING([--enable-trx],
|
||||
[enable code for trx hardware [default=no]]),
|
||||
[enable_trx="yes"],[enable_trx="no"])
|
||||
AC_MSG_RESULT([$enable_trx])
|
||||
AM_CONDITIONAL(ENABLE_TRX, test "x$enable_trx" = "xyes")
|
||||
|
||||
# We share gsm_data.h with OpenBSC and need to be pointed to the source
|
||||
# directory of OpenBSC for now.
|
||||
AC_ARG_WITH([openbsc],
|
||||
@@ -69,6 +79,7 @@ AC_OUTPUT(
|
||||
src/Makefile
|
||||
src/common/Makefile
|
||||
src/osmo-bts-sysmo/Makefile
|
||||
src/osmo-bts-trx/Makefile
|
||||
include/Makefile
|
||||
include/osmo-bts/Makefile
|
||||
tests/Makefile
|
||||
@@ -77,4 +88,6 @@ AC_OUTPUT(
|
||||
tests/cipher/Makefile
|
||||
tests/sysmobts/Makefile
|
||||
tests/misc/Makefile
|
||||
tests/bursts/Makefile
|
||||
tests/handover/Makefile
|
||||
Makefile)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
noinst_HEADERS = abis.h bts.h bts_model.h gsm_data.h logging.h measurement.h \
|
||||
oml.h paging.h rsl.h signal.h vty.h amr.h pcu_if.h pcuif_proto.h \
|
||||
handover.h msg_utils.h tx_power.h control_if.h cbch.h
|
||||
handover.h msg_utils.h tx_power.h control_if.h cbch.h l1sap.h \
|
||||
power_control.h
|
||||
|
||||
@@ -39,5 +39,9 @@ void load_timer_start(struct gsm_bts *bts);
|
||||
|
||||
void bts_update_status(enum bts_global_status which, int on);
|
||||
|
||||
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx);
|
||||
|
||||
struct gsm_time *get_time(struct gsm_bts *bts);
|
||||
|
||||
#endif /* _BTS_H */
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
|
||||
int bts_model_init(struct gsm_bts *bts);
|
||||
|
||||
struct gsm_time *bts_model_get_time(struct gsm_bts *bts);
|
||||
|
||||
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
|
||||
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
|
||||
void *obj);
|
||||
@@ -27,18 +25,9 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
void *obj, uint8_t adm_state);
|
||||
|
||||
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp);
|
||||
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan);
|
||||
int bts_model_rsl_chan_mod(struct gsm_lchan *lchan);
|
||||
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan);
|
||||
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan);
|
||||
|
||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx);
|
||||
int bts_model_trx_close(struct gsm_bts_trx *trx);
|
||||
|
||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
unsigned int rtp_pl_len);
|
||||
|
||||
int bts_model_vty_init(struct gsm_bts *bts);
|
||||
|
||||
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts);
|
||||
@@ -47,5 +36,10 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx);
|
||||
int bts_model_oml_estab(struct gsm_bts *bts);
|
||||
|
||||
int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm);
|
||||
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan);
|
||||
|
||||
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
|
||||
|
||||
void bts_model_abis_close(struct gsm_bts *bts);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -81,11 +81,13 @@ struct gsm_bts_role_bts {
|
||||
struct {
|
||||
uint8_t tc4_ctr;
|
||||
} si;
|
||||
struct gsm_time gsm_time;
|
||||
uint8_t radio_link_timeout;
|
||||
|
||||
int ul_power_target; /* Uplink Rx power target */
|
||||
|
||||
/* used by the sysmoBTS to adjust band */
|
||||
uint8_t auto_band;
|
||||
uint8_t unitid_use_eeprom;
|
||||
|
||||
struct {
|
||||
struct llist_head queue; /* list of struct smscb_msg */
|
||||
@@ -97,8 +99,9 @@ enum lchan_ciph_state {
|
||||
LCHAN_CIPH_NONE,
|
||||
LCHAN_CIPH_RX_REQ,
|
||||
LCHAN_CIPH_RX_CONF,
|
||||
LCHAN_CIPH_TXRX_REQ,
|
||||
LCHAN_CIPH_TXRX_CONF,
|
||||
LCHAN_CIPH_RXTX_REQ,
|
||||
LCHAN_CIPH_RX_CONF_TX_REQ,
|
||||
LCHAN_CIPH_RXTX_CONF,
|
||||
};
|
||||
|
||||
#define bts_role_bts(x) ((struct gsm_bts_role_bts *)(x)->role)
|
||||
@@ -112,6 +115,13 @@ static inline struct femtol1_hdl *trx_femtol1_hdl(struct gsm_bts_trx *trx)
|
||||
return trx->role_bts.l1h;
|
||||
}
|
||||
|
||||
struct trx_l1h;
|
||||
|
||||
static inline struct trx_l1h *trx_l1h_hdl(struct gsm_bts_trx *trx)
|
||||
{
|
||||
return trx->role_bts.l1h;
|
||||
}
|
||||
|
||||
|
||||
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state);
|
||||
|
||||
|
||||
73
include/osmo-bts/l1sap.h
Normal file
73
include/osmo-bts/l1sap.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef L1SAP_H
|
||||
#define L1SAP_H
|
||||
|
||||
/* timeslot and subslot from chan_nr */
|
||||
#define L1SAP_CHAN2TS(chan_nr) (chan_nr & 7)
|
||||
#define L1SAP_CHAN2SS_TCHH(chan_nr) ((chan_nr >> 3) & 1)
|
||||
#define L1SAP_CHAN2SS_SDCCH4(chan_nr) ((chan_nr >> 3) & 3)
|
||||
#define L1SAP_CHAN2SS_SDCCH8(chan_nr) ((chan_nr >> 3) & 7)
|
||||
|
||||
/* logical channel from chan_nr + link_id */
|
||||
#define L1SAP_IS_LINK_SACCH(link_id) ((link_id & 0xC0) == 0x40)
|
||||
#define L1SAP_IS_CHAN_TCHF(chan_nr) ((chan_nr & 0xf8) == 0x08)
|
||||
#define L1SAP_IS_CHAN_TCHH(chan_nr) ((chan_nr & 0xf0) == 0x10)
|
||||
#define L1SAP_IS_CHAN_SDCCH4(chan_nr) ((chan_nr & 0xe0) == 0x20)
|
||||
#define L1SAP_IS_CHAN_SDCCH8(chan_nr) ((chan_nr & 0xc0) == 0x40)
|
||||
#define L1SAP_IS_CHAN_BCCH(chan_nr) ((chan_nr & 0xf8) == 0x80)
|
||||
#define L1SAP_IS_CHAN_RACH(chan_nr) ((chan_nr & 0xf8) == 0x88)
|
||||
#define L1SAP_IS_CHAN_AGCH_PCH(chan_nr) ((chan_nr & 0xf8) == 0x90)
|
||||
|
||||
/* rach type from ra */
|
||||
#define L1SAP_IS_PACKET_RACH(ra) ((ra & 0xf0) == 0x70)
|
||||
|
||||
/* CCCH block from frame number */
|
||||
#define L1SAP_FN2CCCHBLOCK(fn) ((fn % 51) / 5 - 1)
|
||||
|
||||
/* PTCH layout from frame number */
|
||||
#define L1SAP_FN2MACBLOCK(fn) ((fn % 52) / 4)
|
||||
#define L1SAP_FN2PTCCHBLOCK(fn) ((fn / 52) & 7)
|
||||
#define L1SAP_IS_PTCCH(fn) ((fn % 52) == 12)
|
||||
|
||||
/* subslot from any chan_nr */
|
||||
static inline uint8_t l1sap_chan2ss(uint8_t chan_nr)
|
||||
{
|
||||
if (L1SAP_IS_CHAN_SDCCH8(chan_nr))
|
||||
return L1SAP_CHAN2SS_SDCCH8(chan_nr);
|
||||
if (L1SAP_IS_CHAN_SDCCH4(chan_nr))
|
||||
return L1SAP_CHAN2SS_SDCCH4(chan_nr);
|
||||
if (L1SAP_IS_CHAN_TCHH(chan_nr))
|
||||
return L1SAP_CHAN2SS_TCHH(chan_nr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* allocate a msgb containing a osmo_phsap_prim + optional l2 data */
|
||||
struct msgb *l1sap_msgb_alloc(unsigned int l2_len);
|
||||
|
||||
/* any L1 prim received from bts model */
|
||||
int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap);
|
||||
|
||||
/* pcu (socket interface) sends us a data request primitive */
|
||||
int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||
|
||||
/* call-back function for incoming RTP */
|
||||
void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
unsigned int rtp_pl_len);
|
||||
|
||||
/* channel control */
|
||||
int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp);
|
||||
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr);
|
||||
int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr);
|
||||
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr);
|
||||
|
||||
extern const struct value_string gsmtap_sapi_names[];
|
||||
extern struct gsmtap_inst *gsmtap;
|
||||
extern uint32_t gsmtap_sapi_mask;
|
||||
extern uint8_t gsmtap_sapi_acch;
|
||||
|
||||
#define msgb_l1sap_prim(msg) ((struct osmo_phsap_prim *)(msg)->l1h)
|
||||
|
||||
int bts_check_for_first_ciphrd(struct gsm_lchan *lchan,
|
||||
uint8_t *data, int len);
|
||||
#endif /* L1SAP_H */
|
||||
@@ -16,6 +16,8 @@ enum {
|
||||
DDSP,
|
||||
DPCU,
|
||||
DHO,
|
||||
DTRX,
|
||||
DLOOP,
|
||||
DABIS,
|
||||
DRTP,
|
||||
DSUM,
|
||||
|
||||
@@ -49,10 +49,4 @@ int paging_group_queue_empty(struct paging_state *ps, uint8_t group);
|
||||
int paging_queue_length(struct paging_state *ps);
|
||||
int paging_buffer_space(struct paging_state *ps);
|
||||
|
||||
extern uint8_t etws_segment_data[5][17];
|
||||
extern uint8_t etws_segment_len[5];
|
||||
extern uint8_t etws_nr_seg;
|
||||
extern uint8_t etws_data[60];
|
||||
extern size_t etws_len;
|
||||
|
||||
#endif
|
||||
|
||||
7
include/osmo-bts/power_control.h
Normal file
7
include/osmo-bts/power_control.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
|
||||
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
|
||||
const uint8_t ms_power, const int rxLevel);
|
||||
@@ -11,13 +11,15 @@ enum {
|
||||
LCHAN_REL_ACT_OML,
|
||||
};
|
||||
|
||||
int msgb_queue_flush(struct llist_head *list);
|
||||
|
||||
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg);
|
||||
int rsl_tx_rf_res(struct gsm_bts_trx *trx);
|
||||
int rsl_tx_chan_rqd(struct gsm_bts_trx *trx, struct gsm_time *gtime,
|
||||
uint8_t ra, uint8_t acc_delay);
|
||||
int rsl_tx_est_ind(struct gsm_lchan *lchan, uint8_t link_id, uint8_t *data, int len);
|
||||
|
||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime);
|
||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan);
|
||||
int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause);
|
||||
int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause);
|
||||
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan);
|
||||
|
||||
@@ -15,7 +15,7 @@ extern struct cmd_element ournode_end_cmd;
|
||||
enum node_type bts_vty_go_parent(struct vty *vty);
|
||||
int bts_vty_is_config_node(struct vty *vty, int node);
|
||||
|
||||
int bts_vty_init(const struct log_info *cat);
|
||||
int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat);
|
||||
|
||||
extern struct vty_app_info bts_vty_info;
|
||||
|
||||
|
||||
@@ -3,3 +3,6 @@ SUBDIRS = common
|
||||
if ENABLE_SYSMOBTS
|
||||
SUBDIRS += osmo-bts-sysmo
|
||||
endif
|
||||
if ENABLE_TRX
|
||||
SUBDIRS += osmo-bts-trx
|
||||
endif
|
||||
|
||||
@@ -8,4 +8,4 @@ libbts_a_SOURCES = gsm_data_shared.c sysinfo.c logging.c abis.c oml.c bts.c \
|
||||
load_indication.c pcu_sock.c handover.c msg_utils.c \
|
||||
load_indication.c pcu_sock.c handover.c msg_utils.c \
|
||||
tx_power.c bts_ctrl_commands.c bts_ctrl_lookup.c \
|
||||
etws_p1.c cbch.c
|
||||
l1sap.c cbch.c power_control.c
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
|
||||
static struct gsm_bts *g_bts;
|
||||
|
||||
@@ -114,7 +115,7 @@ static void sign_link_down(struct e1inp_line *line)
|
||||
e1inp_sign_link_destroy(g_bts->oml_link);
|
||||
g_bts->oml_link = NULL;
|
||||
|
||||
bts_shutdown(g_bts, "Abis close");
|
||||
bts_model_abis_close(g_bts);
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +204,6 @@ void abis_init(struct gsm_bts *bts)
|
||||
g_bts = bts;
|
||||
|
||||
oml_init();
|
||||
e1inp_vty_init();
|
||||
libosmo_abis_init(NULL);
|
||||
|
||||
osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);
|
||||
|
||||
@@ -16,8 +16,9 @@ void amr_log_mr_conf(int ss, int logl, const char *pfx,
|
||||
|
||||
for (i = 0; i < amr_mrc->num_modes; i++)
|
||||
LOGPC(ss, logl, ", mode[%u] = %u/%u/%u",
|
||||
i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold,
|
||||
amr_mrc->mode[i].hysteresis);
|
||||
i, amr_mrc->mode[i].mode,
|
||||
amr_mrc->mode[i].threshold_bts,
|
||||
amr_mrc->mode[i].hysteresis_bts);
|
||||
LOGPC(ss, logl, "\n");
|
||||
}
|
||||
|
||||
@@ -68,18 +69,18 @@ int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc,
|
||||
}
|
||||
|
||||
if (num_codecs >= 2) {
|
||||
amr_mrc->mode[0].threshold = mr_conf[1] & 0x3F;
|
||||
amr_mrc->mode[0].hysteresis = mr_conf[2] >> 4;
|
||||
amr_mrc->mode[0].threshold_bts = mr_conf[1] & 0x3F;
|
||||
amr_mrc->mode[0].hysteresis_bts = mr_conf[2] >> 4;
|
||||
}
|
||||
if (num_codecs >= 3) {
|
||||
amr_mrc->mode[1].threshold =
|
||||
amr_mrc->mode[1].threshold_bts =
|
||||
((mr_conf[2] & 0xF) << 2) | (mr_conf[3] >> 6);
|
||||
amr_mrc->mode[1].hysteresis = (mr_conf[3] >> 2) & 0x7;
|
||||
amr_mrc->mode[1].hysteresis_bts = (mr_conf[3] >> 2) & 0xF;
|
||||
}
|
||||
if (num_codecs >= 4) {
|
||||
amr_mrc->mode[3].threshold =
|
||||
amr_mrc->mode[2].threshold_bts =
|
||||
((mr_conf[3] & 0x3) << 4) | (mr_conf[4] >> 4);
|
||||
amr_mrc->mode[3].hysteresis = mr_conf[4] & 0xF;
|
||||
amr_mrc->mode[2].hysteresis_bts = mr_conf[4] & 0xF;
|
||||
}
|
||||
|
||||
return num_codecs;
|
||||
@@ -94,10 +95,12 @@ ret_einval:
|
||||
unsigned int amr_get_initial_mode(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
|
||||
struct gsm48_multi_rate_conf *mr_conf =
|
||||
(struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie;
|
||||
|
||||
if (lchan->mr_conf.icmi) {
|
||||
if (mr_conf->icmi) {
|
||||
/* initial mode given, coding in TS 05.09 3.4.1 */
|
||||
return lchan->mr_conf.smod;
|
||||
return mr_conf->smod;
|
||||
} else {
|
||||
/* implicit rule according to TS 05.09 Chapter 3.4.3 */
|
||||
switch (amr_mrc->num_modes) {
|
||||
|
||||
@@ -99,6 +99,7 @@ int bts_init(struct gsm_bts *bts)
|
||||
|
||||
/* configurable via VTY */
|
||||
btsb->paging_state = paging_init(btsb, 200, 0);
|
||||
btsb->ul_power_target = -75; /* dBm default */
|
||||
|
||||
/* configurable via OML */
|
||||
btsb->load.ccch.load_ind_period = 112;
|
||||
@@ -241,6 +242,8 @@ int trx_link_estab(struct gsm_bts_trx *trx)
|
||||
|
||||
if (link)
|
||||
rsl_tx_rf_res(trx);
|
||||
else
|
||||
bts_model_trx_deact_rf(trx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -606,3 +609,15 @@ int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher)
|
||||
sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers;
|
||||
return sup > 0;
|
||||
}
|
||||
|
||||
int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx)
|
||||
{
|
||||
return trx->ms_power_control == 1;
|
||||
}
|
||||
|
||||
struct gsm_time *get_time(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts->role;
|
||||
|
||||
return &btsb->gsm_time;
|
||||
}
|
||||
|
||||
@@ -21,66 +21,63 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_12.h>
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#define SMS_CB_MSG_LEN 88 /* TS 04.12 Section 3.1 */
|
||||
#define SMS_CB_BLOCK_LEN 22 /* TS 04.12 Section 3.1 */
|
||||
|
||||
struct smscb_msg {
|
||||
struct llist_head list; /* list in smscb_state.queue */
|
||||
|
||||
uint8_t msg[SMS_CB_MSG_LEN]; /* message buffer */
|
||||
uint8_t msg[GSM412_MSG_LEN]; /* message buffer */
|
||||
uint8_t next_seg; /* next segment number */
|
||||
uint8_t num_segs; /* total number of segments */
|
||||
};
|
||||
|
||||
/* Figure 3/3GPP TS 04.12 */
|
||||
struct sms_cb_block_type {
|
||||
uint8_t seq_nr:4, /* 0=first, 1=2nd, ... f=null */
|
||||
lb:1, /* last block */
|
||||
lpd:2, /* always 01 */
|
||||
spare:1;
|
||||
};
|
||||
static int get_smscb_null_block(uint8_t *out)
|
||||
{
|
||||
struct gsm412_block_type *block_type = (struct gsm412_block_type *) out;
|
||||
|
||||
block_type->spare = 0;
|
||||
block_type->lpd = 1;
|
||||
block_type->seq_nr = GSM412_SEQ_NULL_MSG;
|
||||
block_type->lb = 0;
|
||||
memset(out+1, GSM_MACBLOCK_PADDING, GSM412_BLOCK_LEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the next block of the current CB message */
|
||||
static int get_smscb_block(struct gsm_bts *bts, uint8_t *out)
|
||||
{
|
||||
int to_copy;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct sms_cb_block_type *block_type = (struct sms_cb_block_type *) out++;
|
||||
struct gsm412_block_type *block_type;
|
||||
struct smscb_msg *msg = btsb->smscb_state.cur_msg;
|
||||
|
||||
/* LPD is always 01 */
|
||||
block_type->lpd = 1;
|
||||
|
||||
if (!msg) {
|
||||
/* No message: Send NULL mesage */
|
||||
block_type->seq_nr = 0xf;
|
||||
block_type->lb = 0;
|
||||
/* padding */
|
||||
memset(out, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
|
||||
return 0;
|
||||
return get_smscb_null_block(out);
|
||||
}
|
||||
|
||||
DEBUGP(DLSMS, "Current SMS-CB %s: ",
|
||||
osmo_hexdump_nospc(msg->msg, sizeof(msg->msg)));
|
||||
block_type = (struct gsm412_block_type *) out++;
|
||||
|
||||
/* LPD is always 01 */
|
||||
block_type->spare = 0;
|
||||
block_type->lpd = 1;
|
||||
|
||||
/* determine how much data to copy */
|
||||
to_copy = SMS_CB_MSG_LEN - (msg->next_seg * SMS_CB_BLOCK_LEN);
|
||||
if (to_copy > SMS_CB_BLOCK_LEN)
|
||||
to_copy = SMS_CB_BLOCK_LEN;
|
||||
to_copy = GSM412_MSG_LEN - (msg->next_seg * GSM412_BLOCK_LEN);
|
||||
if (to_copy > GSM412_BLOCK_LEN)
|
||||
to_copy = GSM412_BLOCK_LEN;
|
||||
|
||||
/* copy data and increment index */
|
||||
memcpy(out, &msg->msg[msg->next_seg * SMS_CB_BLOCK_LEN], to_copy);
|
||||
memcpy(out, &msg->msg[msg->next_seg * GSM412_BLOCK_LEN], to_copy);
|
||||
|
||||
/* set + increment sequence number */
|
||||
block_type->seq_nr = msg->next_seg++;
|
||||
|
||||
DEBUGP(DLSMS, "sending block %u: %s\n",
|
||||
block_type->seq_nr, osmo_hexdump_nospc(out, to_copy));
|
||||
|
||||
/* determine if this is the last block */
|
||||
if (block_type->seq_nr + 1 == msg->num_segs)
|
||||
block_type->lb = 1;
|
||||
@@ -114,7 +111,7 @@ int bts_process_smscb_cmd(struct gsm_bts *bts,
|
||||
|
||||
if (msg_len > sizeof(scm->msg)) {
|
||||
LOGP(DLSMS, LOGL_ERROR,
|
||||
"Cannot process SMSCB of %u bytes (max %u)\n",
|
||||
"Cannot process SMSCB of %u bytes (max %lu)\n",
|
||||
msg_len, sizeof(scm->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -140,8 +137,6 @@ int bts_process_smscb_cmd(struct gsm_bts *bts,
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUGP(DLSMS, "Enqueuing SMS-CB %s from RSL",
|
||||
osmo_hexdump_nospc(scm->msg, sizeof(scm->msg)));
|
||||
llist_add_tail(&scm->list, &btsb->smscb_state.queue);
|
||||
|
||||
return 0;
|
||||
@@ -193,8 +188,7 @@ int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
|
||||
break;
|
||||
case 4: case 5: case 6: case 7:
|
||||
/* always send NULL frame in extended CBCH for now */
|
||||
outbuf[0] = 0x2f;
|
||||
memset(outbuf+1, GSM_MACBLOCK_PADDING, SMS_CB_BLOCK_LEN);
|
||||
rc = get_smscb_null_block(outbuf);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/* Paging Rest Octets / ETWS support code */
|
||||
/*
|
||||
* (C) 2014 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 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 <osmocom/core/bits.h>
|
||||
#include <osmocom/core/bitvec.h>
|
||||
|
||||
/*! \brief construct the Paging 1 Rest Octets
|
||||
* \param[in] bitvec Bit Vector (destionation)
|
||||
* \param[in] seg_nr segment number 0...n
|
||||
* \param[in] num_segs total number of segments
|
||||
* \param[in] payload binary payload for this segment
|
||||
* \param[in] len_of_seg length of the segment
|
||||
*
|
||||
* Put together the P1 Rest Octets according to 10.5.2.23
|
||||
*/
|
||||
int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow)
|
||||
{
|
||||
|
||||
/* no NLN */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* no Priority 1 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* no Priority 2 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Group Call Information */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Packet Page Indication 1 */
|
||||
bitvec_set_bit(bv, L);
|
||||
/* Packet Page Indication 2 */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* No Rel6 extensions */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* No Rel7 extensions */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
/* Rel8 extensions */
|
||||
bitvec_set_bit(bv, H);
|
||||
|
||||
/* priority_uplink_access bit: Use RACH */
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
if (etws_will_follow) {
|
||||
/* We do have ETWS Primary Notification */
|
||||
bitvec_set_bit(bv, ONE);
|
||||
} else {
|
||||
bitvec_set_bit(bv, ZERO);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief construct a ETWS Primary Notification struct
|
||||
* \param[in] bitvec Bit Vector (destionation)
|
||||
* \param[in] pni Primary Notification Identifier
|
||||
* \param[in] seg_nr Segment Number 0...n
|
||||
* \param[in] num_segs Total Number of Segments
|
||||
* \param[in] payload Binary Payload for this Segment
|
||||
* \param[in] num_payload_bits Length of the Segment
|
||||
*/
|
||||
int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
|
||||
uint8_t seg_nr, uint8_t num_segs,
|
||||
const uint8_t *payload,
|
||||
uint8_t num_payload_bits)
|
||||
{
|
||||
uint8_t payload_ubits[num_payload_bits];
|
||||
|
||||
/* Put together a "ETWS Primary Notification struct" as per TS
|
||||
* 44.018 */
|
||||
|
||||
if (seg_nr == 0) {
|
||||
bitvec_set_bit(bv, ZERO);
|
||||
bitvec_set_uint(bv, num_segs, 4);
|
||||
} else {
|
||||
bitvec_set_bit(bv, ONE);
|
||||
/* seg_nr is coounting 1..n, not 0..n */
|
||||
bitvec_set_uint(bv, seg_nr + 1, 4);
|
||||
}
|
||||
bitvec_set_bit(bv, pni ? ONE : ZERO);
|
||||
bitvec_set_uint(bv, num_payload_bits, 7);
|
||||
|
||||
/* expand packed payload bits to unpacked bits and set them in
|
||||
* the bit vector */
|
||||
osmo_pbit2ubit(payload_ubits, payload, num_payload_bits);
|
||||
int i;
|
||||
for (i = 0; i < num_payload_bits; ++i)
|
||||
bitvec_set_bit(bv, payload_ubits[i]);
|
||||
// bitvec_set_bits(bv, (enum bit_value *) payload_ubits, num_payload_bits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
/* Transmit a handover related PHYS INFO on given lchan */
|
||||
static int ho_tx_phys_info(struct gsm_lchan *lchan)
|
||||
@@ -114,7 +115,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
|
||||
|
||||
/* Stop handover detection, wait for valid frame */
|
||||
lchan->ho.active = HANDOVER_WAIT_FRAME;
|
||||
if (bts_model_rsl_chan_mod(lchan) != 0) {
|
||||
if (l1sap_chan_modify(lchan->ts->trx, gsm_lchan2chan_nr(lchan)) != 0) {
|
||||
LOGP(DHO, LOGL_ERROR,
|
||||
"%s failed to modify channel after handover\n",
|
||||
gsm_lchan_name(lchan));
|
||||
|
||||
1084
src/common/l1sap.c
Normal file
1084
src/common/l1sap.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -107,6 +107,18 @@ static struct log_info_cat bts_log_info_cat[] = {
|
||||
.color = "\033[0;37m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DTRX] = {
|
||||
.name = "DTRX",
|
||||
.description = "TRX interface",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DLOOP] = {
|
||||
.name = "DLOOP",
|
||||
.description = "Control loops",
|
||||
.color = "\033[0;34m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
#if 0
|
||||
[DNS] = {
|
||||
.name = "DNS",
|
||||
|
||||
@@ -103,13 +103,6 @@ int msg_verify_ipa_structure(struct msgb *msg)
|
||||
|
||||
hh = (struct ipaccess_head *) msg->l1h;
|
||||
|
||||
if (hh->proto != IPAC_PROTO_OML) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Incorrect ipa header protocol 0x%x 0x%x\n",
|
||||
hh->proto, IPAC_PROTO_OML);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ntohs(hh->len) != msgb_l1len(msg) - sizeof(struct ipaccess_head)) {
|
||||
LOGP(DL1C, LOGL_ERROR,
|
||||
"Incorrect ipa header msg size %d %d\n",
|
||||
@@ -117,7 +110,16 @@ int msg_verify_ipa_structure(struct msgb *msg)
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->l2h = hh->data;
|
||||
if (hh->proto == IPAC_PROTO_OSMO) {
|
||||
struct ipaccess_head_ext *hh_ext = (struct ipaccess_head_ext *) hh->data;
|
||||
if (ntohs(hh->len) < sizeof(*hh_ext)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "IPA length shorter than OSMO header\n");
|
||||
return -1;
|
||||
}
|
||||
msg->l2h = hh_ext->data;
|
||||
} else
|
||||
msg->l2h = hh->data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -590,6 +590,25 @@ static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
trx->arfcn_num = length;
|
||||
} else
|
||||
trx->arfcn_num = 0;
|
||||
#else
|
||||
if (trx != trx->bts->c0 && TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
|
||||
const uint8_t *value = TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
|
||||
uint16_t _value;
|
||||
uint16_t length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST);
|
||||
uint16_t arfcn;
|
||||
if (length != 2) {
|
||||
LOGP(DOML, LOGL_ERROR, "Expecting only one ARFCN, "
|
||||
"because hopping not supported\n");
|
||||
/* FIXME: send NACK */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
memcpy(&_value, value, 2);
|
||||
arfcn = ntohs(_value);
|
||||
value += 2;
|
||||
if (arfcn > 1024)
|
||||
return oml_fom_ack_nack(msg, NM_NACK_FREQ_NOTAVAIL);
|
||||
trx->arfcn = arfcn;
|
||||
}
|
||||
#endif
|
||||
/* call into BTS driver to apply new attributes to hardware */
|
||||
return bts_model_apply_oml(trx->bts, msg, tp_merged, NM_OC_RADIO_CARRIER, trx);
|
||||
|
||||
@@ -46,9 +46,6 @@
|
||||
#define MAX_PAGING_BLOCKS_CCCH 9
|
||||
#define MAX_BS_PA_MFRMS 9
|
||||
|
||||
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
|
||||
|
||||
|
||||
enum paging_record_type {
|
||||
PAGING_RECORD_PAGING,
|
||||
PAGING_RECORD_IMM_ASS
|
||||
@@ -265,14 +262,7 @@ int paging_add_imm_ass(struct paging_state *ps, const uint8_t *data,
|
||||
|
||||
#define L2_PLEN(len) (((len - 1) << 2) | 0x01)
|
||||
|
||||
static int current_segment[MAX_PAGING_BLOCKS_CCCH*MAX_BS_PA_MFRMS];
|
||||
uint8_t etws_segment_data[5][17];
|
||||
uint8_t etws_segment_len[5];
|
||||
uint8_t etws_nr_seg;
|
||||
uint8_t etws_data[60];
|
||||
size_t etws_len;
|
||||
|
||||
static int fill_paging_type_1(int group, uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
static int fill_paging_type_1(uint8_t *out_buf, const uint8_t *identity1_lv,
|
||||
uint8_t chan1, const uint8_t *identity2_lv,
|
||||
uint8_t chan2)
|
||||
{
|
||||
@@ -289,20 +279,7 @@ static int fill_paging_type_1(int group, uint8_t *out_buf, const uint8_t *identi
|
||||
cur = lv_put(pt1->data, identity1_lv[0], identity1_lv+1);
|
||||
if (identity2_lv)
|
||||
cur = lv_put(cur, identity2_lv[0], identity2_lv+1);
|
||||
else if (identity1_lv == empty_id_lv && etws_nr_seg > 0) {
|
||||
int cur_segment = current_segment[group];
|
||||
current_segment[group] += 1;
|
||||
current_segment[group] %= etws_nr_seg;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "paging group(%d) segment(%d)\n",
|
||||
group, cur_segment);
|
||||
|
||||
/* move the pointer */
|
||||
memcpy(cur, etws_segment_data[cur_segment], etws_segment_len[cur_segment]);
|
||||
cur += etws_segment_len[cur_segment];
|
||||
}
|
||||
|
||||
/* do we need to include it */
|
||||
pt1->l2_plen = L2_PLEN(cur - out_buf);
|
||||
|
||||
return cur - out_buf;
|
||||
@@ -359,6 +336,8 @@ static int fill_paging_type_3(uint8_t *out_buf, const uint8_t *tmsi1_lv,
|
||||
return cur - out_buf;
|
||||
}
|
||||
|
||||
static const uint8_t empty_id_lv[] = { 0x01, 0xF0 };
|
||||
|
||||
static struct paging_record *dequeue_pr(struct llist_head *group_q)
|
||||
{
|
||||
struct paging_record *pr;
|
||||
@@ -421,7 +400,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
/* There is nobody to be paged, send Type1 with two empty ID */
|
||||
if (llist_empty(group_q)) {
|
||||
//DEBUGP(DPAG, "Tx PAGING TYPE 1 (empty)\n");
|
||||
len = fill_paging_type_1(group, out_buf, empty_id_lv, 0,
|
||||
len = fill_paging_type_1(out_buf, empty_id_lv, 0,
|
||||
NULL, 0);
|
||||
*is_empty = 1;
|
||||
} else {
|
||||
@@ -495,7 +474,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
}
|
||||
} else if (num_pr == 1) {
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (1 xMSI,1 empty)\n");
|
||||
len = fill_paging_type_1(group, out_buf,
|
||||
len = fill_paging_type_1(out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
NULL, 0);
|
||||
@@ -503,7 +482,7 @@ int paging_gen_msg(struct paging_state *ps, uint8_t *out_buf, struct gsm_time *g
|
||||
/* 2 (any type) or
|
||||
* 3 or 4, of which only 2 will be sent */
|
||||
DEBUGP(DPAG, "Tx PAGING TYPE 1 (2 xMSI)\n");
|
||||
len = fill_paging_type_1(group, out_buf,
|
||||
len = fill_paging_type_1(out_buf,
|
||||
pr[0]->u.paging.identity_lv,
|
||||
pr[0]->u.paging.chan_needed,
|
||||
pr[1]->u.paging.identity_lv,
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx);
|
||||
|
||||
@@ -57,10 +57,6 @@ static const char *sapi_string[] = {
|
||||
[PCU_IF_SAPI_PTCCH] = "PTCCH",
|
||||
};
|
||||
|
||||
/* FIXME: common l1if include ? */
|
||||
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len);
|
||||
|
||||
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
|
||||
/* FIXME: move this to libosmocore */
|
||||
int osmo_unixsock_listen(struct osmo_fd *bfd, int type, const char *path);
|
||||
@@ -334,6 +330,7 @@ int pcu_tx_data_ind(struct gsm_bts_trx_ts *ts, uint8_t is_ptcch, uint32_t fn,
|
||||
data_ind = &pcu_prim->u.data_ind;
|
||||
|
||||
data_ind->sapi = (is_ptcch) ? PCU_IF_SAPI_PTCCH : PCU_IF_SAPI_PDTCH;
|
||||
data_ind->rssi = rssi;
|
||||
data_ind->fn = fn;
|
||||
data_ind->arfcn = arfcn;
|
||||
data_ind->trx_nr = ts->trx->nr;
|
||||
@@ -512,7 +509,7 @@ static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type,
|
||||
}
|
||||
ts = &trx->ts[data_req->ts_nr];
|
||||
is_ptcch = (data_req->sapi == PCU_IF_SAPI_PTCCH);
|
||||
rc = l1if_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
|
||||
rc = l1sap_pdch_req(ts, is_ptcch, data_req->fn, data_req->arfcn,
|
||||
data_req->block_nr, data_req->data, data_req->len);
|
||||
break;
|
||||
default:
|
||||
@@ -546,9 +543,9 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
|
||||
return -EINVAL;
|
||||
}
|
||||
if (act_req->activate)
|
||||
bts_model_rsl_chan_act(lchan, NULL);
|
||||
l1sap_chan_act(trx, gsm_lchan2chan_nr(lchan), NULL);
|
||||
else
|
||||
bts_model_rsl_chan_rel(lchan);
|
||||
l1sap_chan_rel(trx, gsm_lchan2chan_nr(lchan));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -653,7 +650,8 @@ static void pcu_sock_close(struct pcu_sock_state *state)
|
||||
if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
|
||||
&& ts->pchan == GSM_PCHAN_PDCH) {
|
||||
ts->lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
|
||||
bts_model_rsl_chan_rel(ts->lchan);
|
||||
l1sap_chan_rel(trx,
|
||||
gsm_lchan2chan_nr(ts->lchan));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
99
src/common/power_control.c
Normal file
99
src/common/power_control.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/* MS Power Control Loop L1 */
|
||||
|
||||
/* (C) 2014 by Holger Hans Peter Freyther
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
/*
|
||||
* Check if manual power control is needed
|
||||
* Check if fixed power was selected
|
||||
* Check if the MS is already using our level if not
|
||||
* the value is bogus..
|
||||
* TODO: Add a timeout.. e.g. if the ms is not capable of reaching
|
||||
* the value we have set.
|
||||
*/
|
||||
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
|
||||
const uint8_t ms_power, const int rxLevel)
|
||||
{
|
||||
int rx;
|
||||
int cur_dBm, new_dBm, new_pwr;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
const enum gsm_band band = bts->band;
|
||||
|
||||
if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
|
||||
return 0;
|
||||
if (lchan->ms_power_ctrl.fixed)
|
||||
return 0;
|
||||
|
||||
/* The phone hasn't reached the power level yet */
|
||||
if (lchan->ms_power_ctrl.current != ms_power)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* What is the difference between what we want and received?
|
||||
* Ignore a margin that is within the range of measurement
|
||||
* and MS output issues.
|
||||
*/
|
||||
rx = btsb->ul_power_target - rxLevel;
|
||||
if (rx >= 0 && rx < 1)
|
||||
return 0;
|
||||
if (rx < 0 && rx > -1)
|
||||
return 0;
|
||||
|
||||
/* We don't really care about the truncation of int + float */
|
||||
cur_dBm = ms_pwr_dbm(band, ms_power);
|
||||
new_dBm = cur_dBm + rx;
|
||||
|
||||
/* Clamp negative values and do it depending on the band */
|
||||
if (new_dBm < 0)
|
||||
new_dBm = 0;
|
||||
|
||||
switch (band) {
|
||||
case GSM_BAND_1800:
|
||||
/* If MS_TX_PWR_MAX_CCH is set the values 29,
|
||||
* 30, 31 are not used. Avoid specifying a dBm
|
||||
* that would lead to these power levels. The
|
||||
* phone might not be able to reach them. */
|
||||
if (new_dBm > 30)
|
||||
new_dBm = 30;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
new_pwr = ms_pwr_ctl_lvl(band, new_dBm);
|
||||
if (lchan->ms_power_ctrl.current != new_pwr) {
|
||||
lchan->ms_power_ctrl.current = new_pwr;
|
||||
bts_model_adjst_ms_pwr(lchan);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
136
src/common/rsl.c
136
src/common/rsl.c
@@ -42,11 +42,11 @@
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/amr.h>
|
||||
#include <osmo-bts/signal.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
#include <osmo-bts/cbch.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
//#define FAKE_CIPH_MODE_COMPL
|
||||
|
||||
@@ -543,8 +543,9 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
|
||||
}
|
||||
|
||||
/* 8.4.2 sending CHANnel ACTIVation ACKnowledge */
|
||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan, struct gsm_time *gtime)
|
||||
int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
|
||||
struct msgb *msg;
|
||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||
uint8_t ie[2];
|
||||
@@ -735,6 +736,11 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
||||
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
|
||||
}
|
||||
|
||||
/* Initialize channel defaults */
|
||||
lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0);
|
||||
lchan->ms_power_ctrl.current = lchan->ms_power;
|
||||
lchan->ms_power_ctrl.fixed = 0;
|
||||
|
||||
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
|
||||
|
||||
/* 9.3.3 Activation Type */
|
||||
@@ -774,8 +780,11 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
|
||||
lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
|
||||
/* 9.3.13 MS Power */
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER))
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) {
|
||||
lchan->ms_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER);
|
||||
lchan->ms_power_ctrl.current = lchan->ms_power;
|
||||
lchan->ms_power_ctrl.fixed = 0;
|
||||
}
|
||||
/* 9.3.24 Timing Advance */
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_TIMING_ADVANCE))
|
||||
lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
|
||||
@@ -828,12 +837,12 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
||||
}
|
||||
/* 9.3.52 MultiRate Configuration */
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
|
||||
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) {
|
||||
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) {
|
||||
LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
|
||||
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
|
||||
}
|
||||
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||
memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1,
|
||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1);
|
||||
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
|
||||
@@ -848,7 +857,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
||||
|
||||
/* actually activate the channel in the BTS */
|
||||
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
|
||||
rc = bts_model_rsl_chan_act(msg->lchan, &tp);
|
||||
rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp);
|
||||
if (rc < 0)
|
||||
return rsl_tx_chan_act_nack(lchan, -rc);
|
||||
|
||||
@@ -856,10 +865,8 @@ static int rsl_rx_chan_activ(struct msgb *msg)
|
||||
}
|
||||
|
||||
/* 8.4.14 RF CHANnel RELease is received */
|
||||
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
|
||||
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (lchan->abis_ip.rtp_socket) {
|
||||
rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
|
||||
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
|
||||
@@ -871,9 +878,11 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan)
|
||||
handover_reset(lchan);
|
||||
|
||||
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
|
||||
rc = bts_model_rsl_chan_rel(lchan);
|
||||
l1sap_chan_rel(lchan->ts->trx, chan_nr);
|
||||
|
||||
return rc;
|
||||
lapdm_channel_exit(&lchan->lapdm_ch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef FAKE_CIPH_MODE_COMPL
|
||||
@@ -1056,10 +1065,10 @@ static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
|
||||
/* 8.4.9 MODE MODIFY */
|
||||
static int rsl_rx_mode_modif(struct msgb *msg)
|
||||
{
|
||||
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
|
||||
struct gsm_lchan *lchan = msg->lchan;
|
||||
struct rsl_ie_chan_mode *cm;
|
||||
struct tlv_parsed tp;
|
||||
int rc;
|
||||
|
||||
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
|
||||
|
||||
@@ -1085,12 +1094,12 @@ static int rsl_rx_mode_modif(struct msgb *msg)
|
||||
|
||||
/* 9.3.52 MultiRate Configuration */
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) {
|
||||
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_conf)) {
|
||||
if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) {
|
||||
LOGP(DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n");
|
||||
return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT);
|
||||
}
|
||||
memcpy(&lchan->mr_conf, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||
memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1,
|
||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1);
|
||||
amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG),
|
||||
TLVP_LEN(&tp, RSL_IE_MR_CONFIG));
|
||||
amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan),
|
||||
@@ -1100,12 +1109,32 @@ static int rsl_rx_mode_modif(struct msgb *msg)
|
||||
/* 9.3.53 MultiRate Control */
|
||||
/* 9.3.54 Supported Codec Types */
|
||||
|
||||
rc = bts_model_rsl_mode_modify(msg->lchan);
|
||||
l1sap_chan_modify(lchan->ts->trx, dch->chan_nr);
|
||||
|
||||
/* FIXME: delay this until L1 says OK? */
|
||||
rsl_tx_mode_modif_ack(msg->lchan);
|
||||
rsl_tx_mode_modif_ack(lchan);
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 8.4.15 MS POWER CONTROL */
|
||||
static int rsl_rx_ms_pwr_ctrl(struct msgb *msg)
|
||||
{
|
||||
struct gsm_lchan *lchan = msg->lchan;
|
||||
struct tlv_parsed tp;
|
||||
|
||||
rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg));
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) {
|
||||
uint8_t pwr = *TLVP_VAL(&tp, RSL_IE_MS_POWER) & 0x1F;
|
||||
lchan->ms_power_ctrl.fixed = 1;
|
||||
lchan->ms_power_ctrl.current = pwr;
|
||||
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s forcing power to %d\n",
|
||||
gsm_lchan_name(lchan), lchan->ms_power_ctrl.current);
|
||||
bts_model_adjst_ms_pwr(lchan);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 8.4.20 SACCH INFO MODify */
|
||||
@@ -1163,6 +1192,37 @@ static int rsl_rx_sacch_inf_mod(struct msgb *msg)
|
||||
/*
|
||||
* ip.access related messages
|
||||
*/
|
||||
static void rsl_add_rtp_stats(struct gsm_lchan *lchan, struct msgb *msg)
|
||||
{
|
||||
struct ipa_stats {
|
||||
uint32_t packets_sent;
|
||||
uint32_t octets_sent;
|
||||
uint32_t packets_recv;
|
||||
uint32_t octets_recv;
|
||||
uint32_t packets_lost;
|
||||
uint32_t arrival_jitter;
|
||||
uint32_t avg_tx_delay;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ipa_stats stats;
|
||||
|
||||
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
|
||||
|
||||
osmo_rtp_socket_stats(lchan->abis_ip.rtp_socket,
|
||||
&stats.packets_sent, &stats.octets_sent,
|
||||
&stats.packets_recv, &stats.octets_recv,
|
||||
&stats.packets_lost, &stats.arrival_jitter);
|
||||
/* convert to network byte order */
|
||||
stats.packets_sent = htonl(stats.packets_sent);
|
||||
stats.octets_sent = htonl(stats.octets_sent);
|
||||
stats.packets_recv = htonl(stats.packets_recv);
|
||||
stats.octets_recv = htonl(stats.octets_recv);
|
||||
stats.packets_lost = htonl(stats.packets_lost);
|
||||
|
||||
msgb_tlv_put(msg, RSL_IE_IPAC_CONN_STAT, sizeof(stats), (uint8_t *) &stats);
|
||||
}
|
||||
|
||||
int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
|
||||
{
|
||||
@@ -1175,6 +1235,8 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
|
||||
if (!nmsg)
|
||||
return -ENOMEM;
|
||||
|
||||
msgb_tv16_put(nmsg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
|
||||
rsl_add_rtp_stats(lchan, nmsg);
|
||||
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
|
||||
rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
|
||||
|
||||
@@ -1246,8 +1308,10 @@ static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
if (inc_conn_id)
|
||||
if (inc_conn_id) {
|
||||
msgb_tv_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
|
||||
rsl_add_rtp_stats(lchan, msg);
|
||||
}
|
||||
|
||||
rsl_ipa_push_hdr(msg, RSL_MT_IPAC_DLCX_ACK, chan_nr);
|
||||
msg->trx = lchan->ts->trx;
|
||||
@@ -1416,7 +1480,7 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg)
|
||||
OSMO_RTP_P_JITBUF,
|
||||
btsb->rtp_jitter_buf_ms);
|
||||
lchan->abis_ip.rtp_socket->priv = lchan;
|
||||
lchan->abis_ip.rtp_socket->rx_cb = &bts_model_rtp_rx_cb;
|
||||
lchan->abis_ip.rtp_socket->rx_cb = &l1sap_rtp_rx_cb;
|
||||
|
||||
if (connect_ip && connect_port) {
|
||||
/* if CRCX specifies a remote IP, we can bind()
|
||||
@@ -1526,11 +1590,11 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_IPAC_CONN_ID))
|
||||
inc_conn_id = 1;
|
||||
|
||||
rc = rsl_tx_ipac_dlcx_ack(lchan, inc_conn_id);
|
||||
osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
|
||||
lchan->abis_ip.rtp_socket = NULL;
|
||||
msgb_queue_flush(&lchan->dl_tch_queue);
|
||||
|
||||
return rsl_tx_ipac_dlcx_ack(lchan, inc_conn_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1611,22 +1675,25 @@ static int rslms_is_meas_rep(struct msgb *msg)
|
||||
static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint8_t meas_res[16];
|
||||
uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
|
||||
int res_valid = lchan->meas.flags & LC_UL_M_F_RES_VALID;
|
||||
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES\n", gsm_lchan_name(lchan));
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s Tx MEAS RES valid(%d)\n",
|
||||
gsm_lchan_name(lchan), res_valid);
|
||||
|
||||
if (!res_valid)
|
||||
return -EINPROGRESS;
|
||||
|
||||
msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr));
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
|
||||
if (lchan->meas.flags & LC_UL_M_F_RES_VALID) {
|
||||
uint8_t meas_res[16];
|
||||
int ie_len = lchan_build_rsl_ul_meas(lchan, meas_res);
|
||||
if (ie_len >= 3) {
|
||||
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
|
||||
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
|
||||
}
|
||||
int ie_len = lchan_build_rsl_ul_meas(lchan, meas_res);
|
||||
if (ie_len >= 3) {
|
||||
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
|
||||
lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
|
||||
}
|
||||
msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->meas.bts_tx_pwr);
|
||||
if (lchan->meas.flags & LC_UL_M_F_L1_VALID) {
|
||||
@@ -1758,13 +1825,13 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
ret = rsl_rx_chan_activ(msg);
|
||||
break;
|
||||
case RSL_MT_RF_CHAN_REL:
|
||||
ret = rsl_rx_rf_chan_rel(msg->lchan);
|
||||
ret = rsl_rx_rf_chan_rel(msg->lchan, dch->chan_nr);
|
||||
break;
|
||||
case RSL_MT_SACCH_INFO_MODIFY:
|
||||
ret = rsl_rx_sacch_inf_mod(msg);
|
||||
break;
|
||||
case RSL_MT_DEACTIVATE_SACCH:
|
||||
ret = bts_model_rsl_deact_sacch(msg->lchan);
|
||||
ret = l1sap_chan_deact_sacch(trx, dch->chan_nr);
|
||||
break;
|
||||
case RSL_MT_ENCR_CMD:
|
||||
ret = rsl_rx_encr_cmd(msg);
|
||||
@@ -1772,6 +1839,9 @@ static int rsl_rx_dchan(struct gsm_bts_trx *trx, struct msgb *msg)
|
||||
case RSL_MT_MODE_MODIFY_REQ:
|
||||
ret = rsl_rx_mode_modif(msg);
|
||||
break;
|
||||
case RSL_MT_MS_POWER_CONTROL:
|
||||
ret = rsl_rx_ms_pwr_ctrl(msg);
|
||||
break;
|
||||
case RSL_MT_PHY_CONTEXT_REQ:
|
||||
case RSL_MT_PREPROC_CONFIG:
|
||||
case RSL_MT_RTD_REP:
|
||||
|
||||
204
src/common/vty.c
204
src/common/vty.c
@@ -24,12 +24,15 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/gsm/abis_nm.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
@@ -44,8 +47,7 @@
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/paging.h>
|
||||
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
enum node_type bts_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
@@ -139,7 +141,7 @@ static struct cmd_node trx_node = {
|
||||
};
|
||||
|
||||
DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
|
||||
"trx <0-0>",
|
||||
"trx <0-254>",
|
||||
"Select a TRX to configure\n" "TRX number\n")
|
||||
{
|
||||
int trx_nr = atoi(argv[0]);
|
||||
@@ -148,7 +150,8 @@ DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
|
||||
|
||||
trx = gsm_bts_trx_num(bts, trx_nr);
|
||||
if (!trx) {
|
||||
vty_out(vty, "Unknown TRX %u%s", trx_nr, VTY_NEWLINE);
|
||||
vty_out(vty, "Unknown TRX %u. Aavialable TRX are: 0..%d%s",
|
||||
trx_nr, bts->num_trx - 1, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
@@ -159,10 +162,38 @@ DEFUN(cfg_bts_trx, cfg_bts_trx_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: move to libosmocore ? */
|
||||
static char buf_casecnvt[256];
|
||||
char *osmo_str_tolower(const char *in)
|
||||
{
|
||||
int len, i;
|
||||
|
||||
if (!in)
|
||||
return NULL;
|
||||
|
||||
len = strlen(in);
|
||||
if (len > sizeof(buf_casecnvt))
|
||||
len = sizeof(buf_casecnvt);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf_casecnvt[i] = tolower(in[i]);
|
||||
if (in[i] == '\0')
|
||||
break;
|
||||
}
|
||||
if (i < sizeof(buf_casecnvt))
|
||||
buf_casecnvt[i] = '\0';
|
||||
|
||||
/* just to make sure we're always zero-terminated */
|
||||
buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
|
||||
|
||||
return buf_casecnvt;
|
||||
}
|
||||
|
||||
static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
struct gsm_bts_trx *trx;
|
||||
int i;
|
||||
|
||||
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
|
||||
if (bts->description)
|
||||
@@ -177,6 +208,7 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " paging lifetime %u%s", paging_get_lifetime(btsb->paging_state),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " uplink-power-target %d%s", btsb->ul_power_target, VTY_NEWLINE);
|
||||
if (btsb->agch_queue_thresh_level != GSM_BTS_AGCH_QUEUE_THRESH_LEVEL_DEFAULT
|
||||
|| btsb->agch_queue_low_level != GSM_BTS_AGCH_QUEUE_LOW_LEVEL_DEFAULT
|
||||
|| btsb->agch_queue_high_level != GSM_BTS_AGCH_QUEUE_HIGH_LEVEL_DEFAULT)
|
||||
@@ -184,6 +216,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
btsb->agch_queue_thresh_level, btsb->agch_queue_low_level,
|
||||
btsb->agch_queue_high_level, VTY_NEWLINE);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (gsmtap_sapi_mask & (1 << i)) {
|
||||
const char *name = get_value_string(gsmtap_sapi_names, i);
|
||||
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name), VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
if (gsmtap_sapi_acch) {
|
||||
const char *name = get_value_string(gsmtap_sapi_names, GSMTAP_CHANNEL_ACCH);
|
||||
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
bts_model_config_write_bts(vty, bts);
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
@@ -199,6 +242,9 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
tpp->ramp.step_size_mdB, VTY_NEWLINE);
|
||||
vty_out(vty, " power-ramp step-interval %d%s",
|
||||
tpp->ramp.step_interval_sec, VTY_NEWLINE);
|
||||
vty_out(vty, " ms-power-control %s%s",
|
||||
trx->ms_power_control == 0 ? "dsp" : "osmo",
|
||||
VTY_NEWLINE);
|
||||
|
||||
bts_model_config_write_trx(vty, trx);
|
||||
}
|
||||
@@ -397,6 +443,19 @@ DEFUN(cfg_bts_agch_queue_mgmt_default,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_ul_power_target, cfg_bts_ul_power_target_cmd,
|
||||
"uplink-power-target <-110-0>",
|
||||
"Set the nominal target Rx Level for uplink power control loop\n"
|
||||
"Target uplink Rx level in dBm\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->ul_power_target = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DB_DBM_STR \
|
||||
"Unit is dB (decibels)\n" \
|
||||
"Unit is mdB (milli-decibels, or rather 1/10000 bel)\n"
|
||||
@@ -461,6 +520,16 @@ DEFUN(cfg_trx_pr_step_interval, cfg_trx_pr_step_interval_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_ms_power_control, cfg_trx_ms_power_control_cmd,
|
||||
"ms-power-control (dsp|osmo)",
|
||||
"Mobile Station Power Level Control (change requires restart)\n"
|
||||
"Handled by DSP\n" "Handled by OsmoBTS\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
trx->ms_power_control = argv[0][0] == 'd' ? 0 : 1;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
@@ -602,6 +671,36 @@ static struct gsm_lchan *resolve_lchan(struct gsm_network *net,
|
||||
"logical channel commands\n" \
|
||||
"logical channel number\n"
|
||||
|
||||
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
|
||||
"HIDDEN", "HIDDEN")
|
||||
{
|
||||
int sapi;
|
||||
|
||||
sapi = get_string_value(gsmtap_sapi_names, argv[0]);
|
||||
|
||||
if (sapi == GSMTAP_CHANNEL_ACCH)
|
||||
gsmtap_sapi_acch = 1;
|
||||
else
|
||||
gsmtap_sapi_mask |= (1 << sapi);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
|
||||
"HIDDEN", "HIDDEN")
|
||||
{
|
||||
int sapi;
|
||||
|
||||
sapi = get_string_value(gsmtap_sapi_names, argv[0]);
|
||||
|
||||
if (sapi == GSMTAP_CHANNEL_ACCH)
|
||||
gsmtap_sapi_acch = 0;
|
||||
else
|
||||
gsmtap_sapi_mask &= ~(1 << sapi);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(bts_t_t_l_jitter_buf,
|
||||
bts_t_t_l_jitter_buf_cmd,
|
||||
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> rtp jitter-buffer <0-10000>",
|
||||
@@ -628,61 +727,58 @@ DEFUN(bts_t_t_l_jitter_buf,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
extern int construct_p1_rest_octets(struct bitvec *bv, int etws_will_follow);
|
||||
extern int construct_etws_prim_notif(struct bitvec *bv, uint8_t pni,
|
||||
uint8_t seg_nr, uint8_t num_segs,
|
||||
const uint8_t *payload,
|
||||
uint8_t num_payload_bits);
|
||||
|
||||
DEFUN(bts_etsw_idle, bts_etsw_idle_cmd,
|
||||
"etsw-message MESSAGE",
|
||||
"ETSW Message\nMessage in Hex\n")
|
||||
DEFUN(bts_t_t_l_loopback,
|
||||
bts_t_t_l_loopback_cmd,
|
||||
"bts <0-0> trx <0-0> ts <0-7> lchan <0-1> loopback",
|
||||
BTS_T_T_L_STR "Set loopback\n")
|
||||
{
|
||||
int rc;
|
||||
int segment = 0, rest = 56;
|
||||
|
||||
rc = osmo_hexparse(argv[0], etws_data, sizeof(etws_data));
|
||||
if (rc != 56) {
|
||||
vty_out(vty, "%%we expect 56 bytes of the data.%s", VTY_NEWLINE);
|
||||
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
lchan = resolve_lchan(net, argv, 0);
|
||||
if (!lchan) {
|
||||
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
lchan->loopback = 1;
|
||||
|
||||
vty_out(vty, "%% parsed: %s%s",
|
||||
osmo_hexdump(etws_data, rc), VTY_NEWLINE);
|
||||
etws_len = rc;
|
||||
|
||||
|
||||
for (segment = 0; segment < 5; ++segment) {
|
||||
struct bitvec bv = { 0, };
|
||||
bv.data_len = 14;
|
||||
bv.data = &etws_segment_data[segment][0];
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "Goint to create segment(%d) offset %d len %d\n",
|
||||
segment, segment * 12,
|
||||
rest >= 12 ? 12 : rest);
|
||||
construct_p1_rest_octets(&bv, 1);
|
||||
printf("CUR BIT: %d %s\n", bv.cur_bit,
|
||||
osmo_hexdump(&etws_data[segment * 12],
|
||||
rest >= 12 ? 12 : rest));
|
||||
construct_etws_prim_notif(&bv, 1, segment, 5,
|
||||
&etws_data[segment * 12],
|
||||
rest >= 12 ? 12 * 8 : rest * 8);
|
||||
etws_segment_len[segment] = (bv.cur_bit + 7) / 8;
|
||||
rest -= 12;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE,
|
||||
"Created segment(%d) with len %d %s\n",
|
||||
segment, etws_segment_len[segment],
|
||||
osmo_hexdump(etws_segment_data[segment], etws_segment_len[segment]));
|
||||
}
|
||||
|
||||
etws_nr_seg = 5;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int bts_vty_init(const struct log_info *cat)
|
||||
DEFUN(no_bts_t_t_l_loopback,
|
||||
no_bts_t_t_l_loopback_cmd,
|
||||
"no bts <0-0> trx <0-0> ts <0-7> lchan <0-1> loopback",
|
||||
NO_STR BTS_T_T_L_STR "Set loopback\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet_from_vty(vty);
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
lchan = resolve_lchan(net, argv, 0);
|
||||
if (!lchan) {
|
||||
vty_out(vty, "%% can't find BTS%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
lchan->loopback = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int bts_vty_init(struct gsm_bts *bts, const struct log_info *cat)
|
||||
{
|
||||
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
|
||||
"gsmtap-sapi (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
|
||||
"GSMTAP SAPI\n",
|
||||
"\n", "", 0);
|
||||
|
||||
cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
|
||||
"no gsmtap-sapi (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, gsmtap_sapi_names,
|
||||
NO_STR "GSMTAP SAPI\n",
|
||||
"\n", "", 0);
|
||||
|
||||
install_element_ve(&show_bts_cmd);
|
||||
|
||||
logging_vty_add_cmds(cat);
|
||||
@@ -701,6 +797,10 @@ int bts_vty_init(const struct log_info *cat)
|
||||
install_element(BTS_NODE, &cfg_bts_paging_lifetime_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_default_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_agch_queue_mgmt_params_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_ul_power_target_cmd);
|
||||
|
||||
install_element(BTS_NODE, &cfg_trx_gsmtap_sapi_cmd);
|
||||
install_element(BTS_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
|
||||
|
||||
/* add and link to TRX config node */
|
||||
install_element(BTS_NODE, &cfg_bts_trx_cmd);
|
||||
@@ -711,9 +811,9 @@ int bts_vty_init(const struct log_info *cat)
|
||||
install_element(TRX_NODE, &cfg_trx_pr_max_initial_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_pr_step_size_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_ms_power_control_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
|
||||
install_element(ENABLE_NODE, &bts_etsw_idle_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBGPS_CFLAGS)
|
||||
COMMON_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) -lortp
|
||||
|
||||
EXTRA_DIST = misc/sysmobts_mgr.h misc/sysmobts_misc.h misc/sysmobts_par.h \
|
||||
@@ -27,8 +27,9 @@ sysmobts_mgr_SOURCES = \
|
||||
misc/sysmobts_mgr_vty.c \
|
||||
misc/sysmobts_mgr_nl.c \
|
||||
misc/sysmobts_mgr_temp.c \
|
||||
misc/sysmobts_mgr_calib.c \
|
||||
eeprom.c
|
||||
sysmobts_mgr_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS)
|
||||
sysmobts_mgr_LDADD = $(LIBGPS_LIBS) $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCTRL_LIBS) $(top_builddir)/src/common/libbts.a
|
||||
|
||||
sysmobts_util_SOURCES = misc/sysmobts_util.c misc/sysmobts_par.c eeprom.c
|
||||
sysmobts_util_LDADD = $(LIBOSMOCORE_LIBS)
|
||||
|
||||
@@ -77,7 +77,12 @@ int l1if_handle_l1prim(int wq, struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||
struct l1fwd_hdl *l1fh = fl1h->priv;
|
||||
|
||||
/* Enqueue message to UDP socket */
|
||||
return osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg);
|
||||
if (osmo_wqueue_enqueue(&l1fh->udp_wq[wq], msg) != 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Write queue %d full. dropping msg\n", wq);
|
||||
msgb_free(msg);
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback when there's a new SYS primitive coming in from the HW */
|
||||
@@ -86,7 +91,12 @@ int l1if_handle_sysprim(struct femtol1_hdl *fl1h, struct msgb *msg)
|
||||
struct l1fwd_hdl *l1fh = fl1h->priv;
|
||||
|
||||
/* Enqueue message to UDP socket */
|
||||
return osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_SYS_WRITE], msg);
|
||||
if (osmo_wqueue_enqueue(&l1fh->udp_wq[MQ_SYS_WRITE], msg) != 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "MQ_SYS_WRITE ful. dropping msg\n");
|
||||
msgb_free(msg);
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,9 +131,13 @@ static int udp_read_cb(struct osmo_fd *ofd)
|
||||
ofd->priv_nr);
|
||||
|
||||
/* put the message into the right queue */
|
||||
rc = osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg);
|
||||
|
||||
return rc;
|
||||
if (osmo_wqueue_enqueue(&fl1h->write_q[ofd->priv_nr], msg) != 0) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Write queue %d full. dropping msg\n",
|
||||
ofd->priv_nr);
|
||||
msgb_free(msg);
|
||||
return -EAGAIN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback when we can write to the UDP socket */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,16 +46,12 @@ struct femtol1_hdl {
|
||||
uint32_t dsp_trace_f;
|
||||
uint8_t clk_use_eeprom;
|
||||
int clk_cal;
|
||||
int ul_power_target;
|
||||
uint8_t clk_src;
|
||||
float min_qual_rach;
|
||||
float min_qual_norm;
|
||||
char *calib_path;
|
||||
struct llist_head wlc_list;
|
||||
|
||||
struct gsmtap_inst *gsmtap;
|
||||
uint32_t gsmtap_sapi_mask;
|
||||
|
||||
void *priv; /* user reference */
|
||||
|
||||
struct osmo_timer_list alive_timer;
|
||||
@@ -110,7 +106,9 @@ uint32_t l1if_lchan_to_hLayer(struct gsm_lchan *lchan);
|
||||
struct gsm_lchan *l1if_hLayer_to_lchan(struct gsm_bts_trx *trx, uint32_t hLayer);
|
||||
|
||||
/* tch.c */
|
||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg);
|
||||
void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
|
||||
const uint8_t *rtp_pl, unsigned int rtp_pl_len);
|
||||
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg);
|
||||
int l1if_tch_fill(struct gsm_lchan *lchan, uint8_t *l1_buffer);
|
||||
struct msgb *gen_empty_tch_msg(struct gsm_lchan *lchan);
|
||||
|
||||
@@ -119,6 +117,13 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
|
||||
struct gsm_lchan *lchan,
|
||||
int dir_downlink);
|
||||
|
||||
/* channel control */
|
||||
int l1if_rsl_chan_act(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_chan_rel(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_chan_mod(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_deact_sacch(struct gsm_lchan *lchan);
|
||||
int l1if_rsl_mode_modify(struct gsm_lchan *lchan);
|
||||
|
||||
/* calibration loading */
|
||||
int calib_load(struct femtol1_hdl *fl1h);
|
||||
|
||||
@@ -129,7 +134,6 @@ int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h);
|
||||
/* public helpers for test */
|
||||
int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h,
|
||||
struct msgb *msg, struct gsm_lchan *lchan);
|
||||
void bts_check_for_first_ciphrd(struct femtol1_hdl *fl1h,
|
||||
GsmL1_MsgUnitParam_t *msgUnitParam,
|
||||
struct gsm_lchan *lchan);
|
||||
inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int uplink_target,
|
||||
const uint8_t ms_power, const float rxLevel);
|
||||
#endif /* _FEMTO_L1_H */
|
||||
|
||||
@@ -95,7 +95,7 @@ static int fwd_read_cb(struct osmo_fd *ofd)
|
||||
static int prim_write_cb(struct osmo_fd *ofd, struct msgb *msg)
|
||||
{
|
||||
/* write to the fd */
|
||||
return write(ofd->fd, msg->head, msg->len);
|
||||
return write(ofd->fd, msg->l1h, msgb_l1len(msg));
|
||||
}
|
||||
|
||||
int l1if_transport_open(int q, struct femtol1_hdl *fl1h)
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
@@ -47,6 +49,7 @@
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/control_if.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
#define SYSMOBTS_RF_LOCK_PATH "/var/lock/bts_rf_lock"
|
||||
|
||||
@@ -62,6 +65,7 @@ static const char *config_file = "osmo-bts.cfg";
|
||||
static int daemonize = 0;
|
||||
static unsigned int dsp_trace = 0x71c00020;
|
||||
static int rt_prio = -1;
|
||||
static char *gsmtap_ip = 0;
|
||||
|
||||
int bts_model_init(struct gsm_bts *bts)
|
||||
{
|
||||
@@ -165,6 +169,7 @@ static void print_help()
|
||||
" -w --hw-version Print the targeted HW Version\n"
|
||||
" -M --pcu-direct Force PCU to access message queue for "
|
||||
"PDCH dchannel directly\n"
|
||||
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -196,10 +201,11 @@ static void handle_options(int argc, char **argv)
|
||||
{ "hw-version", 0, 0, 'w' },
|
||||
{ "pcu-direct", 0, 0, 'M' },
|
||||
{ "realtime", 1, 0, 'r' },
|
||||
{ "gsmtap-ip", 1, 0, 'i' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:",
|
||||
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:p:w:Mr:i:",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -244,6 +250,9 @@ static void handle_options(int argc, char **argv)
|
||||
case 'r':
|
||||
rt_prio = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
gsmtap_ip = optarg;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -308,8 +317,10 @@ int main(int argc, char **argv)
|
||||
|
||||
bts_log_init(NULL);
|
||||
|
||||
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||
vty_init(&bts_vty_info);
|
||||
bts_vty_init(&bts_log_info);
|
||||
e1inp_vty_init();
|
||||
bts_vty_init(bts, &bts_log_info);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
@@ -325,7 +336,15 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||
if (gsmtap_ip) {
|
||||
gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
|
||||
if (!gsmtap) {
|
||||
fprintf(stderr, "Failed during gsmtap_init()\n");
|
||||
exit(1);
|
||||
}
|
||||
gsmtap_source_add_sink(gsmtap);
|
||||
}
|
||||
|
||||
if (bts_init(bts) < 0) {
|
||||
fprintf(stderr, "unable to open bts\n");
|
||||
exit(1);
|
||||
@@ -401,3 +420,9 @@ int main(int argc, char **argv)
|
||||
osmo_select_main(0);
|
||||
}
|
||||
}
|
||||
|
||||
void bts_model_abis_close(struct gsm_bts *bts)
|
||||
{
|
||||
/* for now, we simply terminate the program and re-spawn */
|
||||
bts_shutdown(bts, "Abis close");
|
||||
}
|
||||
|
||||
@@ -224,6 +224,12 @@ static struct log_info_cat mgr_log_info_cat[] = {
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DCALIB] = {
|
||||
.name = "DCALIB",
|
||||
.description = "Calibration handling",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info mgr_log_info = {
|
||||
@@ -292,6 +298,9 @@ int main(int argc, char **argv)
|
||||
/* Initialize the temperature control */
|
||||
sysmobts_mgr_temp_init(&manager);
|
||||
|
||||
if (sysmobts_mgr_calib_init(&manager) != 0)
|
||||
exit(3);
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
|
||||
@@ -4,10 +4,18 @@
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <gps.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
DTEMP,
|
||||
DFW,
|
||||
DFIND,
|
||||
DCALIB,
|
||||
};
|
||||
|
||||
|
||||
@@ -74,6 +82,27 @@ struct sysmobts_mgr_instance {
|
||||
int action_crit;
|
||||
|
||||
enum sysmobts_temp_state state;
|
||||
|
||||
struct {
|
||||
int initial_calib_started;
|
||||
int is_up;
|
||||
struct osmo_timer_list recon_timer;
|
||||
struct ipa_client_conn *bts_conn;
|
||||
|
||||
int state;
|
||||
struct osmo_timer_list timer;
|
||||
uint32_t last_seqno;
|
||||
|
||||
/* gps structure to see if there is a fix */
|
||||
int gps_open;
|
||||
struct osmo_fd gpsfd;
|
||||
struct gps_data_t gpsdata;
|
||||
struct osmo_timer_list fix_timeout;
|
||||
|
||||
/* Loop/Re-try control */
|
||||
int calib_from_loop;
|
||||
struct osmo_timer_list calib_timeout;
|
||||
} calib;
|
||||
};
|
||||
|
||||
int sysmobts_mgr_vty_init(void);
|
||||
@@ -82,4 +111,11 @@ int sysmobts_mgr_nl_init(void);
|
||||
int sysmobts_mgr_temp_init(struct sysmobts_mgr_instance *mgr);
|
||||
const char *sysmobts_mgr_temp_get_state(enum sysmobts_temp_state state);
|
||||
|
||||
|
||||
int sysmobts_mgr_calib_init(struct sysmobts_mgr_instance *mgr);
|
||||
int sysmobts_mgr_calib_run(struct sysmobts_mgr_instance *mgr);
|
||||
|
||||
|
||||
extern void *tall_mgr_ctx;
|
||||
|
||||
#endif
|
||||
|
||||
538
src/osmo-bts-sysmo/misc/sysmobts_mgr_calib.c
Normal file
538
src/osmo-bts-sysmo/misc/sysmobts_mgr_calib.c
Normal file
@@ -0,0 +1,538 @@
|
||||
/* OCXO/TCXO calibration control for SysmoBTS management daemon */
|
||||
|
||||
/*
|
||||
* (C) 2014,2015 by Holger Hans Peter Freyther
|
||||
* (C) 2014 by Harald Welte for the IPA code from the oml router
|
||||
*
|
||||
* 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 "misc/sysmobts_mgr.h"
|
||||
#include "misc/sysmobts_misc.h"
|
||||
#include "osmo-bts/msg_utils.h"
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
|
||||
#include <osmocom/gsm/ipa.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
|
||||
#include <osmocom/abis/abis.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
static int calib_run(struct sysmobts_mgr_instance *mgr, int from_loop);
|
||||
static void calib_state_reset(struct sysmobts_mgr_instance *mgr, int reason);
|
||||
static void request_clock_reset(struct sysmobts_mgr_instance *mgr);
|
||||
static void bts_updown_cb(struct ipa_client_conn *link, int up);
|
||||
|
||||
enum calib_state {
|
||||
CALIB_INITIAL,
|
||||
CALIB_GPS_WAIT_FOR_FIX,
|
||||
CALIB_CTR_RESET,
|
||||
CALIB_CTR_WAIT,
|
||||
CALIB_COR_SET,
|
||||
};
|
||||
|
||||
enum calib_result {
|
||||
CALIB_FAIL_START,
|
||||
CALIB_FAIL_GPS,
|
||||
CALIB_FAIL_CTRL,
|
||||
CALIB_SUCESS,
|
||||
};
|
||||
|
||||
static void calib_loop_run(void *_data)
|
||||
{
|
||||
int rc;
|
||||
struct sysmobts_mgr_instance *mgr = _data;
|
||||
|
||||
LOGP(DCALIB, LOGL_NOTICE, "Going to calibrate the system.\n");
|
||||
rc = calib_run(mgr, 1);
|
||||
if (rc != 0)
|
||||
calib_state_reset(mgr, CALIB_FAIL_START);
|
||||
}
|
||||
|
||||
static void mgr_gps_close(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
if (!mgr->calib.gps_open)
|
||||
return;
|
||||
|
||||
osmo_timer_del(&mgr->calib.fix_timeout);
|
||||
|
||||
osmo_fd_unregister(&mgr->calib.gpsfd);
|
||||
gps_close(&mgr->calib.gpsdata);
|
||||
memset(&mgr->calib.gpsdata, 0, sizeof(mgr->calib.gpsdata));
|
||||
mgr->calib.gps_open = 0;
|
||||
}
|
||||
|
||||
static void mgr_gps_checkfix(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
struct gps_data_t *data = &mgr->calib.gpsdata;
|
||||
|
||||
/* No 2D fix yet */
|
||||
if (data->fix.mode < MODE_2D) {
|
||||
LOGP(DCALIB, LOGL_DEBUG, "Fix mode not enough: %d\n",
|
||||
data->fix.mode);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The trimble driver is broken...add some sanity checking */
|
||||
if (data->satellites_used < 1) {
|
||||
LOGP(DCALIB, LOGL_DEBUG, "Not enough satellites used: %d\n",
|
||||
data->satellites_used);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DCALIB, LOGL_NOTICE, "Got a GPS fix continuing.\n");
|
||||
osmo_timer_del(&mgr->calib.fix_timeout);
|
||||
mgr_gps_close(mgr);
|
||||
request_clock_reset(mgr);
|
||||
}
|
||||
|
||||
static int mgr_gps_read(struct osmo_fd *fd, unsigned int what)
|
||||
{
|
||||
int rc;
|
||||
struct sysmobts_mgr_instance *mgr = fd->data;
|
||||
|
||||
rc = gps_read(&mgr->calib.gpsdata);
|
||||
if (rc == -1) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "gpsd vanished during read.\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPS);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc > 0)
|
||||
mgr_gps_checkfix(mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mgr_gps_fix_timeout(void *_data)
|
||||
{
|
||||
struct sysmobts_mgr_instance *mgr = _data;
|
||||
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to acquire GPRS fix.\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPS);
|
||||
}
|
||||
|
||||
static void mgr_gps_open(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = gps_open("localhost", DEFAULT_GPSD_PORT, &mgr->calib.gpsdata);
|
||||
if (rc != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to connect to GPS %d\n", rc);
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPS);
|
||||
return;
|
||||
}
|
||||
|
||||
mgr->calib.gps_open = 1;
|
||||
gps_stream(&mgr->calib.gpsdata, WATCH_ENABLE, NULL);
|
||||
|
||||
mgr->calib.gpsfd.data = mgr;
|
||||
mgr->calib.gpsfd.cb = mgr_gps_read;
|
||||
mgr->calib.gpsfd.when = BSC_FD_READ | BSC_FD_EXCEPT;
|
||||
mgr->calib.gpsfd.fd = mgr->calib.gpsdata.gps_fd;
|
||||
if (osmo_fd_register(&mgr->calib.gpsfd) < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to register GPSD fd\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_GPS);
|
||||
}
|
||||
|
||||
mgr->calib.state = CALIB_GPS_WAIT_FOR_FIX;
|
||||
mgr->calib.fix_timeout.data = mgr;
|
||||
mgr->calib.fix_timeout.cb = mgr_gps_fix_timeout;
|
||||
osmo_timer_schedule(&mgr->calib.fix_timeout, 60, 0);
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Opened the GPSD connection waiting for fix: %d\n",
|
||||
mgr->calib.gpsfd.fd);
|
||||
}
|
||||
|
||||
static void send_ctrl_cmd(struct sysmobts_mgr_instance *mgr,
|
||||
struct msgb *msg)
|
||||
{
|
||||
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_CTRL);
|
||||
ipa_prepend_header(msg, IPAC_PROTO_OSMO);
|
||||
ipa_client_conn_send(mgr->calib.bts_conn, msg);
|
||||
}
|
||||
|
||||
static void send_set_ctrl_cmd_int(struct sysmobts_mgr_instance *mgr,
|
||||
const char *key, const int val)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "CTRL SET");
|
||||
ret = snprintf((char *) msg->data, 4096, "SET %u %s %d",
|
||||
mgr->calib.last_seqno++, key, val);
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
return send_ctrl_cmd(mgr, msg);
|
||||
}
|
||||
|
||||
static void send_set_ctrl_cmd(struct sysmobts_mgr_instance *mgr,
|
||||
const char *key, const char *val)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "CTRL SET");
|
||||
ret = snprintf((char *) msg->data, 4096, "SET %u %s %s",
|
||||
mgr->calib.last_seqno++, key, val);
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
return send_ctrl_cmd(mgr, msg);
|
||||
}
|
||||
|
||||
static void send_get_ctrl_cmd(struct sysmobts_mgr_instance *mgr,
|
||||
const char *key)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc_headroom(1024, 128, "CTRL GET");
|
||||
ret = snprintf((char *) msg->data, 4096, "GET %u %s",
|
||||
mgr->calib.last_seqno++, key);
|
||||
msg->l2h = msgb_put(msg, ret);
|
||||
return send_ctrl_cmd(mgr, msg);
|
||||
}
|
||||
|
||||
static int calib_run(struct sysmobts_mgr_instance *mgr, int from_loop)
|
||||
{
|
||||
if (!mgr->calib.is_up) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Control interface not connected.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mgr->calib.state != CALIB_INITIAL) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Calib is already in progress.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
mgr->calib.calib_from_loop = from_loop;
|
||||
|
||||
/* From now on everything will be handled from the failure */
|
||||
mgr->calib.initial_calib_started = 1;
|
||||
mgr_gps_open(mgr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysmobts_mgr_calib_run(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
return calib_run(mgr, 0);
|
||||
}
|
||||
|
||||
static void request_clock_reset(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
send_set_ctrl_cmd(mgr, "trx.0.clock-info", "1");
|
||||
mgr->calib.state = CALIB_CTR_RESET;
|
||||
}
|
||||
|
||||
static void calib_state_reset(struct sysmobts_mgr_instance *mgr, int outcome)
|
||||
{
|
||||
if (mgr->calib.calib_from_loop) {
|
||||
/*
|
||||
* In case of success calibrate in two hours again
|
||||
* and in case of a failure in some minutes.
|
||||
*/
|
||||
int timeout = 2 * 60 * 60;
|
||||
if (outcome != CALIB_SUCESS)
|
||||
timeout = 5 * 60;
|
||||
|
||||
mgr->calib.calib_timeout.data = mgr;
|
||||
mgr->calib.calib_timeout.cb = calib_loop_run;
|
||||
osmo_timer_schedule(&mgr->calib.calib_timeout, timeout, 0);
|
||||
}
|
||||
|
||||
mgr->calib.state = CALIB_INITIAL;
|
||||
osmo_timer_del(&mgr->calib.timer);
|
||||
|
||||
mgr_gps_close(mgr);
|
||||
}
|
||||
|
||||
static void calib_get_clock_err_cb(void *_data)
|
||||
{
|
||||
struct sysmobts_mgr_instance *mgr = _data;
|
||||
|
||||
LOGP(DCALIB, LOGL_DEBUG,
|
||||
"Requesting current clock-info.\n");
|
||||
send_get_ctrl_cmd(mgr, "trx.0.clock-info");
|
||||
}
|
||||
|
||||
static void handle_ctrl_reset_resp(
|
||||
struct sysmobts_mgr_instance *mgr,
|
||||
struct ctrl_cmd *cmd)
|
||||
{
|
||||
if (strcmp(cmd->variable, "trx.0.clock-info") != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unexpected variable: %s\n", cmd->variable);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(cmd->reply, "success") != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unexpected reply: %s\n", cmd->variable);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
mgr->calib.state = CALIB_CTR_WAIT;
|
||||
mgr->calib.timer.cb = calib_get_clock_err_cb;
|
||||
mgr->calib.timer.data = mgr;
|
||||
osmo_timer_schedule(&mgr->calib.timer, 60, 0);
|
||||
LOGP(DCALIB, LOGL_DEBUG,
|
||||
"Reset the calibration counter. Waiting 60 seconds.\n");
|
||||
}
|
||||
|
||||
static void handle_ctrl_get_resp(
|
||||
struct sysmobts_mgr_instance *mgr,
|
||||
struct ctrl_cmd *cmd)
|
||||
{
|
||||
char *saveptr = NULL;
|
||||
char *clk_cur;
|
||||
char *clk_src;
|
||||
char *cal_err;
|
||||
char *cal_res;
|
||||
char *cal_src;
|
||||
int cal_err_int;
|
||||
|
||||
if (strcmp(cmd->variable, "trx.0.clock-info") != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unexpected variable: %s\n", cmd->variable);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
clk_cur = strtok_r(cmd->reply, ",", &saveptr);
|
||||
clk_src = strtok_r(NULL, ",", &saveptr);
|
||||
cal_err = strtok_r(NULL, ",", &saveptr);
|
||||
cal_res = strtok_r(NULL, ",", &saveptr);
|
||||
cal_src = strtok_r(NULL, ",", &saveptr);
|
||||
|
||||
if (!clk_cur || !clk_src || !cal_err || !cal_res || !cal_src) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Parse error on clock-info reply\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
|
||||
}
|
||||
cal_err_int = atoi(cal_err);
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Calibration CUR(%s) SRC(%s) ERR(%s/%d) RES(%s) SRC(%s)\n",
|
||||
clk_cur, clk_src, cal_err, cal_err_int, cal_res, cal_src);
|
||||
|
||||
if (strcmp(cal_res, "0") == 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Invalid clock resolution. Giving up\n");
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now we can finally set the new value */
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Going to apply %d as new clock correction.\n",
|
||||
-cal_err_int);
|
||||
send_set_ctrl_cmd_int(mgr, "trx.0.clock-correction", -cal_err_int);
|
||||
mgr->calib.state = CALIB_COR_SET;
|
||||
}
|
||||
|
||||
static void handle_ctrl_set_cor(
|
||||
struct sysmobts_mgr_instance *mgr,
|
||||
struct ctrl_cmd *cmd)
|
||||
{
|
||||
if (strcmp(cmd->variable, "trx.0.clock-correction") != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unexpected variable: %s\n", cmd->variable);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(cmd->reply, "success") != 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unexpected reply: %s\n", cmd->variable);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Calibration process completed\n");
|
||||
calib_state_reset(mgr, CALIB_SUCESS);
|
||||
}
|
||||
|
||||
static void handle_ctrl(struct sysmobts_mgr_instance *mgr, struct msgb *msg)
|
||||
{
|
||||
struct ctrl_cmd *cmd = ctrl_cmd_parse(tall_mgr_ctx, msg);
|
||||
if (!cmd) {
|
||||
LOGP(DCALIB, LOGL_ERROR, "Failed to parse command/response\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd->type) {
|
||||
case CTRL_TYPE_GET_REPLY:
|
||||
switch (mgr->calib.state) {
|
||||
case CALIB_CTR_WAIT:
|
||||
handle_ctrl_get_resp(mgr, cmd);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unhandled response in state: %d %s/%s\n",
|
||||
mgr->calib.state, cmd->variable, cmd->reply);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case CTRL_TYPE_SET_REPLY:
|
||||
switch (mgr->calib.state) {
|
||||
case CALIB_CTR_RESET:
|
||||
handle_ctrl_reset_resp(mgr, cmd);
|
||||
break;
|
||||
case CALIB_COR_SET:
|
||||
handle_ctrl_set_cor(mgr, cmd);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unhandled response in state: %d %s/%s\n",
|
||||
mgr->calib.state, cmd->variable, cmd->reply);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case CTRL_TYPE_TRAP:
|
||||
/* ignore any form of trap */
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Unhandled CTRL response: %d. Resetting state\n",
|
||||
cmd->type);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
break;
|
||||
}
|
||||
|
||||
talloc_free(cmd);
|
||||
}
|
||||
|
||||
/* Schedule a connect towards the BTS */
|
||||
static void schedule_bts_connect(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
DEBUGP(DLCTRL, "Scheduling BTS connect\n");
|
||||
osmo_timer_schedule(&mgr->calib.recon_timer, 1, 0);
|
||||
}
|
||||
|
||||
/* BTS re-connect timer call-back */
|
||||
static void bts_recon_timer_cb(void *data)
|
||||
{
|
||||
int rc;
|
||||
struct sysmobts_mgr_instance *mgr = data;
|
||||
|
||||
/* The connection failures are to be expected during boot */
|
||||
mgr->calib.bts_conn->ofd->when |= BSC_FD_WRITE;
|
||||
rc = ipa_client_conn_open(mgr->calib.bts_conn);
|
||||
if (rc < 0) {
|
||||
LOGP(DLCTRL, LOGL_NOTICE, "Failed to connect to BTS.\n");
|
||||
schedule_bts_connect(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
static int bts_read_cb(struct ipa_client_conn *link, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
struct ipaccess_head *hh = (struct ipaccess_head *) msgb_l1(msg);
|
||||
struct ipaccess_head_ext *hh_ext;
|
||||
|
||||
DEBUGP(DCALIB, "Received data from BTS: %s\n",
|
||||
osmo_hexdump(msgb_data(msg), msgb_length(msg)));
|
||||
|
||||
/* regular message handling */
|
||||
rc = msg_verify_ipa_structure(msg);
|
||||
if (rc < 0) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Invalid IPA message from BTS (rc=%d)\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (hh->proto) {
|
||||
case IPAC_PROTO_IPACCESS:
|
||||
/* handle the core IPA CCM messages in libosmoabis */
|
||||
ipa_ccm_rcvmsg_bts_base(msg, link->ofd);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
case IPAC_PROTO_OSMO:
|
||||
hh_ext = (struct ipaccess_head_ext *) hh->data;
|
||||
switch (hh_ext->proto) {
|
||||
case IPAC_PROTO_EXT_CTRL:
|
||||
handle_ctrl(link->data, msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Unhandled osmo ID %u from BTS\n", hh_ext->proto);
|
||||
};
|
||||
msgb_free(msg);
|
||||
break;
|
||||
default:
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Unhandled stream ID %u from BTS\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* link to BSC has gone up or down */
|
||||
static void bts_updown_cb(struct ipa_client_conn *link, int up)
|
||||
{
|
||||
struct sysmobts_mgr_instance *mgr = link->data;
|
||||
|
||||
LOGP(DLCTRL, LOGL_INFO, "BTS connection %s\n", up ? "up" : "down");
|
||||
|
||||
if (up) {
|
||||
mgr->calib.is_up = 1;
|
||||
mgr->calib.last_seqno = 0;
|
||||
|
||||
if (!mgr->calib.initial_calib_started)
|
||||
calib_run(mgr, 1);
|
||||
} else {
|
||||
mgr->calib.is_up = 0;
|
||||
schedule_bts_connect(mgr);
|
||||
calib_state_reset(mgr, CALIB_FAIL_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
int sysmobts_mgr_calib_init(struct sysmobts_mgr_instance *mgr)
|
||||
{
|
||||
if (!is_sbts2050_master()) {
|
||||
LOGP(DCALIB, LOGL_NOTICE,
|
||||
"Calib is only possible on the sysmoBTS2050 master\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mgr->calib.bts_conn = ipa_client_conn_create(tall_mgr_ctx, NULL, 0,
|
||||
"localhost", 4238,
|
||||
bts_updown_cb, bts_read_cb,
|
||||
NULL, mgr);
|
||||
if (!mgr->calib.bts_conn) {
|
||||
LOGP(DCALIB, LOGL_ERROR,
|
||||
"Failed to create IPA connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mgr->calib.recon_timer.cb = bts_recon_timer_cb;
|
||||
mgr->calib.recon_timer.data = mgr;
|
||||
schedule_bts_connect(mgr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -375,6 +375,8 @@ DEFUN(cfg_no_action_slave_off, cfg_no_action_slave_off_cmd,
|
||||
DEFUN(show_mgr, show_mgr_cmd, "show manager",
|
||||
SHOW_STR "Display information about the manager")
|
||||
{
|
||||
vty_out(vty, "BTS Control Interface: %s%s",
|
||||
s_mgr->calib.is_up ? "connected" : "disconnected", VTY_NEWLINE);
|
||||
vty_out(vty, "Temperature control state: %s%s",
|
||||
sysmobts_mgr_temp_get_state(s_mgr->state), VTY_NEWLINE);
|
||||
vty_out(vty, "Current Temperatures%s", VTY_NEWLINE);
|
||||
@@ -422,6 +424,18 @@ DEFUN(show_mgr, show_mgr_cmd, "show manager",
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(calibrate_trx, calibrate_trx_cmd,
|
||||
"trx 0 calibrate-clock",
|
||||
"Transceiver commands\n" "Transceiver 0\n"
|
||||
"Calibrate clock against GPS PPS\n")
|
||||
{
|
||||
if (sysmobts_mgr_calib_run(s_mgr) < 0) {
|
||||
vty_out(vty, "%%Failed to start calibration.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void register_limit(int limit)
|
||||
{
|
||||
install_element(limit, &cfg_thresh_warning_cmd);
|
||||
@@ -462,6 +476,8 @@ int sysmobts_mgr_vty_init(void)
|
||||
|
||||
install_element_ve(&show_mgr_cmd);
|
||||
|
||||
install_element(ENABLE_NODE, &calibrate_trx_cmd);
|
||||
|
||||
install_node(&mgr_node, config_write_mgr);
|
||||
install_element(CONFIG_NODE, &cfg_mgr_cmd);
|
||||
vty_install_default(MGR_NODE);
|
||||
|
||||
@@ -37,11 +37,27 @@
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "femtobts.h"
|
||||
#include "utils.h"
|
||||
|
||||
static int mph_info_chan_confirm(struct gsm_lchan *lchan,
|
||||
enum osmo_mph_info_type type, uint8_t cause)
|
||||
{
|
||||
struct osmo_phsap_prim l1sap;
|
||||
|
||||
memset(&l1sap, 0, sizeof(l1sap));
|
||||
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
|
||||
NULL);
|
||||
l1sap.u.info.type = type;
|
||||
l1sap.u.info.u.act_cnf.chan_nr = gsm_lchan2chan_nr(lchan);
|
||||
l1sap.u.info.u.act_cnf.cause = cause;
|
||||
|
||||
return l1sap_up(lchan->ts->trx, &l1sap);
|
||||
}
|
||||
|
||||
enum sapi_cmd_type {
|
||||
SAPI_CMD_ACTIVATE,
|
||||
SAPI_CMD_CONFIG_CIPHERING,
|
||||
@@ -213,8 +229,10 @@ static int opstart_compl(struct gsm_abis_mo *mo, struct msgb *l1_msg)
|
||||
DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
|
||||
mo->bts->c0->ts[0].lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
|
||||
lchan_activate(&mo->bts->c0->ts[0].lchan[4]);
|
||||
if (cbch)
|
||||
if (cbch) {
|
||||
cbch->rel_act_kind = LCHAN_REL_ACT_OML;
|
||||
lchan_activate(cbch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send OPSTART ack */
|
||||
@@ -306,6 +324,7 @@ static const uint8_t trx_rqd_attr[] = { NM_ATT_RF_MAXPOWR_R };
|
||||
static int trx_init(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
|
||||
struct msgb *msg;
|
||||
GsmL1_MphInitReq_t *mi_req;
|
||||
GsmL1_DeviceParam_t *dev_par;
|
||||
@@ -333,7 +352,8 @@ static int trx_init(struct gsm_bts_trx *trx)
|
||||
dev_par->u16Arfcn = trx->arfcn;
|
||||
dev_par->u16BcchArfcn = trx->bts->c0->arfcn;
|
||||
dev_par->u8NbTsc = trx->bts->bsic & 7;
|
||||
dev_par->fRxPowerLevel = fl1h->ul_power_target;
|
||||
dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx)
|
||||
? 0.0 : btsb->ul_power_target;
|
||||
|
||||
dev_par->fTxPowerLevel = 0.0;
|
||||
LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, "
|
||||
@@ -798,6 +818,9 @@ static void set_payload_format(GsmL1_LogChParam_t *lch_par)
|
||||
|
||||
static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
|
||||
{
|
||||
struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr;
|
||||
struct gsm48_multi_rate_conf *mr_conf =
|
||||
(struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie;
|
||||
int j;
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "%s: %s tch_mode=0x%02x\n",
|
||||
@@ -837,41 +860,41 @@ static void lchan2lch_par(GsmL1_LogChParam_t *lch_par, struct gsm_lchan *lchan)
|
||||
lch_par->tch.amrActiveCodecSet[j] = GsmL1_AmrCodec_Unset;
|
||||
|
||||
j = 0;
|
||||
if (lchan->mr_conf.m4_75)
|
||||
if (mr_conf->m4_75)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_4_75;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
|
||||
if (lchan->mr_conf.m5_15)
|
||||
if (mr_conf->m5_15)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_15;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
|
||||
if (lchan->mr_conf.m5_90)
|
||||
if (mr_conf->m5_90)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_5_9;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
|
||||
if (lchan->mr_conf.m6_70)
|
||||
if (mr_conf->m6_70)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_6_7;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
|
||||
if (lchan->mr_conf.m7_40)
|
||||
if (mr_conf->m7_40)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_4;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
|
||||
if (lchan->mr_conf.m7_95)
|
||||
if (mr_conf->m7_95)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_7_95;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
|
||||
if (lchan->mr_conf.m10_2)
|
||||
if (mr_conf->m10_2)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_10_2;
|
||||
if (j >= ARRAY_SIZE(lch_par->tch.amrActiveCodecSet))
|
||||
break;
|
||||
if (lchan->mr_conf.m12_2)
|
||||
if (mr_conf->m12_2)
|
||||
lch_par->tch.amrActiveCodecSet[j++] = GsmL1_AmrCodec_12_2;
|
||||
break;
|
||||
case GSM48_CMODE_DATA_14k5:
|
||||
@@ -920,12 +943,16 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
|
||||
case GsmL1_Sapi_Prach:
|
||||
lch_par->prach.u8Bsic = lchan->ts->trx->bts->bsic;
|
||||
break;
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
case GsmL1_Sapi_Sacch:
|
||||
/*
|
||||
* TODO: For the SACCH we need to set the u8MsPowerLevel when
|
||||
* doing manual MS power control. */
|
||||
* For the SACCH we need to set the u8MsPowerLevel when
|
||||
* doing manual MS power control.
|
||||
*/
|
||||
if (trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx))
|
||||
lch_par->sacch.u8MsPowerLevel = lchan->ms_power_ctrl.current;
|
||||
/* fall through */
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
case GsmL1_Sapi_Pacch:
|
||||
/*
|
||||
* Be sure that every packet is received, even if it
|
||||
* fails. In this case the length might be lower or 0.
|
||||
@@ -967,7 +994,7 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
|
||||
gsm_lchan_name(lchan), status);
|
||||
lchan_set_state(lchan, LCHAN_S_BROKEN);
|
||||
sapi_clear_queue(&lchan->sapi_cmds);
|
||||
rsl_tx_chan_act_nack(lchan, RSL_ERR_PROCESSOR_OVERLOAD);
|
||||
mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, RSL_ERR_PROCESSOR_OVERLOAD);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -977,14 +1004,16 @@ static int sapi_activate_cb(struct gsm_lchan *lchan, int status)
|
||||
if (lchan->state != LCHAN_S_ACT_REQ)
|
||||
return 0;
|
||||
|
||||
struct gsm_time *time;
|
||||
lchan_set_state(lchan, LCHAN_S_ACTIVE);
|
||||
time = bts_model_get_time(lchan->ts->trx->bts);
|
||||
rsl_tx_chan_act_ack(lchan, time);
|
||||
mph_info_chan_confirm(lchan, PRIM_INFO_ACTIVATE, 0);
|
||||
|
||||
/* set the initial ciphering parameters for both directions */
|
||||
l1if_set_ciphering(fl1h, lchan, 0);
|
||||
l1if_set_ciphering(fl1h, lchan, 1);
|
||||
l1if_set_ciphering(fl1h, lchan, 0);
|
||||
if (lchan->encr.alg_id)
|
||||
lchan->ciph_state = LCHAN_CIPH_RXTX_REQ;
|
||||
else
|
||||
lchan->ciph_state = LCHAN_CIPH_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1002,7 +1031,6 @@ static void enqueue_sapi_act_cmd(struct gsm_lchan *lchan, int sapi, int dir)
|
||||
|
||||
int lchan_activate(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb = lchan->ts->trx->bts->role;
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
|
||||
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
|
||||
unsigned int i;
|
||||
@@ -1036,8 +1064,6 @@ int lchan_activate(struct gsm_lchan *lchan)
|
||||
#warning "FIXME: Should this be in sapi_activate_cb?"
|
||||
lchan_init_lapdm(lchan);
|
||||
|
||||
lchan->s = btsb->radio_link_timeout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1136,9 +1162,16 @@ static int chmod_modif_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
|
||||
LOGPC(DL1C, LOGL_INFO, "RX_REQ -> RX_CONF\n");
|
||||
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
|
||||
break;
|
||||
case LCHAN_CIPH_TXRX_REQ:
|
||||
LOGPC(DL1C, LOGL_INFO, "TX_REQ -> TX_CONF\n");
|
||||
lchan->ciph_state = LCHAN_CIPH_TXRX_CONF;
|
||||
case LCHAN_CIPH_RX_CONF_TX_REQ:
|
||||
LOGPC(DL1C, LOGL_INFO, "RX_CONF_TX_REQ -> RXTX_CONF\n");
|
||||
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
|
||||
break;
|
||||
case LCHAN_CIPH_RXTX_REQ:
|
||||
LOGPC(DL1C, LOGL_INFO, "RXTX_REQ -> RX_CONF_TX_REQ\n");
|
||||
lchan->ciph_state = LCHAN_CIPH_RX_CONF_TX_REQ;
|
||||
break;
|
||||
case LCHAN_CIPH_NONE:
|
||||
LOGPC(DL1C, LOGL_INFO, "\n");
|
||||
break;
|
||||
default:
|
||||
LOGPC(DL1C, LOGL_INFO, "unhandled state %u\n", lchan->ciph_state);
|
||||
@@ -1167,7 +1200,8 @@ err:
|
||||
|
||||
static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx);
|
||||
struct gsm_bts_trx *trx = lchan->ts->trx;
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
struct msgb *msg = l1p_msgb_alloc();
|
||||
GsmL1_MphConfigReq_t *conf_req;
|
||||
GsmL1_LogChParam_t *lch_par;
|
||||
@@ -1177,7 +1211,7 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm
|
||||
/* update multi-rate config */
|
||||
conf_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h);
|
||||
conf_req->cfgParamId = GsmL1_ConfigParamId_SetLogChParams;
|
||||
conf_req->cfgParams.setLogChParams.sapi = lchan_to_GsmL1_Sapi_t(lchan);
|
||||
conf_req->cfgParams.setLogChParams.sapi = cmd->sapi;
|
||||
conf_req->cfgParams.setLogChParams.u8Tn = lchan->ts->nr;
|
||||
conf_req->cfgParams.setLogChParams.subCh = lchan_to_GsmL1_SubCh_t(lchan);
|
||||
conf_req->cfgParams.setLogChParams.dir = cmd->dir;
|
||||
@@ -1186,6 +1220,10 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm
|
||||
lch_par = &conf_req->cfgParams.setLogChParams.logChParams;
|
||||
lchan2lch_par(lch_par, lchan);
|
||||
|
||||
/* Update the MS Power Level */
|
||||
if (cmd->sapi == GsmL1_Sapi_Sacch && trx_ms_pwr_ctrl_is_osmo(trx))
|
||||
lch_par->sacch.u8MsPowerLevel = lchan->ms_power_ctrl.current;
|
||||
|
||||
/* FIXME: update encryption */
|
||||
|
||||
LOGP(DL1C, LOGL_INFO, "%s MPH-CONFIG.req (%s) ",
|
||||
@@ -1203,19 +1241,19 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm
|
||||
return l1if_gsm_req_compl(fl1h, msg, chmod_modif_compl_cb, NULL);
|
||||
}
|
||||
|
||||
static void enqueue_sapi_logchpar_cmd(struct gsm_lchan *lchan, int dir)
|
||||
static void enqueue_sapi_logchpar_cmd(struct gsm_lchan *lchan, int dir, GsmL1_Sapi_t sapi)
|
||||
{
|
||||
struct sapi_cmd *cmd = talloc_zero(lchan->ts->trx, struct sapi_cmd);
|
||||
|
||||
cmd->dir = dir;
|
||||
cmd->sapi = sapi;
|
||||
cmd->type = SAPI_CMD_CONFIG_LOGCH_PARAM;
|
||||
queue_sapi_command(lchan, cmd);
|
||||
}
|
||||
|
||||
static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction)
|
||||
{
|
||||
enqueue_sapi_logchpar_cmd(lchan, direction);
|
||||
|
||||
enqueue_sapi_logchpar_cmd(lchan, direction, lchan_to_GsmL1_Sapi_t(lchan));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1298,7 +1336,16 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_model_rsl_mode_modify(struct gsm_lchan *lchan)
|
||||
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
|
||||
{
|
||||
if (lchan->state != LCHAN_S_ACTIVE)
|
||||
return -1;
|
||||
|
||||
enqueue_sapi_logchpar_cmd(lchan, GsmL1_Dir_RxUplink, GsmL1_Sapi_Sacch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_rsl_mode_modify(struct gsm_lchan *lchan)
|
||||
{
|
||||
if (lchan->state != LCHAN_S_ACTIVE)
|
||||
return -1;
|
||||
@@ -1408,7 +1455,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
|
||||
gsm_lchan_name(lchan));
|
||||
lchan_set_state(lchan, LCHAN_S_BROKEN);
|
||||
sapi_clear_queue(&lchan->sapi_cmds);
|
||||
rsl_tx_rf_rel_ack(lchan);
|
||||
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1420,7 +1467,7 @@ static int sapi_deactivate_cb(struct gsm_lchan *lchan, int status)
|
||||
return 0;
|
||||
|
||||
lchan_set_state(lchan, LCHAN_S_NONE);
|
||||
rsl_tx_rf_rel_ack(lchan);
|
||||
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1495,7 +1542,7 @@ static int lchan_deactivate_sapis(struct gsm_lchan *lchan)
|
||||
LOGP(DL1C, LOGL_ERROR, "%s all SAPIs already released?\n",
|
||||
gsm_lchan_name(lchan));
|
||||
lchan_set_state(lchan, LCHAN_S_BROKEN);
|
||||
rsl_tx_rf_rel_ack(lchan);
|
||||
mph_info_chan_confirm(lchan, PRIM_INFO_DEACTIVATE, 0);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -1535,13 +1582,6 @@ static int lchan_deactivate_sacch(struct gsm_lchan *lchan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_time *bts_model_get_time(struct gsm_bts *bts)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(bts->c0);
|
||||
|
||||
return &fl1h->gsm_time;
|
||||
}
|
||||
|
||||
/* callback from OML */
|
||||
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
|
||||
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
|
||||
@@ -1665,30 +1705,11 @@ int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
return oml_mo_statechg_nack(mo, NM_NACK_REQ_NOT_GRANT);
|
||||
|
||||
}
|
||||
int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
|
||||
|
||||
int l1if_rsl_chan_act(struct gsm_lchan *lchan)
|
||||
{
|
||||
//uint8_t mode = *TLVP_VAL(tp, RSL_IE_CHAN_MODE);
|
||||
//uint8_t type = *TLVP_VAL(tp, RSL_IE_ACT_TYPE);
|
||||
struct gsm48_chan_desc *cd;
|
||||
|
||||
/* osmo-pcu calls this without a valid 'tp' parameter, so we
|
||||
* need to make sure we don't crash here */
|
||||
if (tp && TLVP_PRESENT(tp, GSM48_IE_CHANDESC_2) &&
|
||||
TLVP_LEN(tp, GSM48_IE_CHANDESC_2) >= sizeof(*cd)) {
|
||||
cd = (struct gsm48_chan_desc *)
|
||||
TLVP_VAL(tp, GSM48_IE_CHANDESC_2);
|
||||
|
||||
/* our L1 only supports one global TSC for all channels
|
||||
* one one TRX, so we need to make sure not to activate
|
||||
* channels with a different TSC!! */
|
||||
if (cd->h0.tsc != (lchan->ts->trx->bts->bsic & 7)) {
|
||||
LOGP(DRSL, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n",
|
||||
cd->h0.tsc, lchan->ts->trx->bts->bsic & 7);
|
||||
return -RSL_ERR_SERV_OPT_UNIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
lchan->sacch_deact = 0;
|
||||
lchan_activate(lchan);
|
||||
return 0;
|
||||
}
|
||||
@@ -1697,7 +1718,7 @@ int bts_model_rsl_chan_act(struct gsm_lchan *lchan, struct tlv_parsed *tp)
|
||||
* Modify the given lchan in the handover scenario. This is a lot like
|
||||
* second channel activation but with some additional activation.
|
||||
*/
|
||||
int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
|
||||
int l1if_rsl_chan_mod(struct gsm_lchan *lchan)
|
||||
{
|
||||
const struct lchan_sapis *s4l = &sapis_for_lchan[lchan->type];
|
||||
unsigned int i;
|
||||
@@ -1721,7 +1742,7 @@ int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
|
||||
int l1if_rsl_chan_rel(struct gsm_lchan *lchan)
|
||||
{
|
||||
/* A duplicate RF Release Request, ignore it */
|
||||
if (lchan->state == LCHAN_S_REL_REQ) {
|
||||
@@ -1734,7 +1755,7 @@ int bts_model_rsl_chan_rel(struct gsm_lchan *lchan)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_model_rsl_deact_sacch(struct gsm_lchan *lchan)
|
||||
int l1if_rsl_deact_sacch(struct gsm_lchan *lchan)
|
||||
{
|
||||
/* Only de-activate the SACCH if the lchan is active */
|
||||
if (lchan->state != LCHAN_S_ACTIVE)
|
||||
|
||||
@@ -121,6 +121,13 @@ static int ctrl_set_clkinfo_cb(struct gsm_bts_trx *trx, struct msgb *resp,
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int clock_setup_cb(struct gsm_bts_trx *trx, struct msgb *resp,
|
||||
void *data)
|
||||
{
|
||||
msgb_free(resp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_clock_info(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct gsm_bts_trx *trx = cmd->node;
|
||||
@@ -132,6 +139,16 @@ static int set_clock_info(struct ctrl_cmd *cmd, void *data)
|
||||
/* geneate a deferred control command */
|
||||
cd = ctrl_cmd_def_make(fl1h, cmd, NULL, 10);
|
||||
|
||||
/* Set GPS/PPS as reference */
|
||||
sysp->id = SuperFemto_PrimId_RfClockSetupReq;
|
||||
sysp->u.rfClockSetupReq.rfTrx.iClkCor = fl1h->clk_cal; /* !!! use get_clk_cal */
|
||||
sysp->u.rfClockSetupReq.rfTrx.clkSrc = fl1h->clk_src;
|
||||
sysp->u.rfClockSetupReq.rfTrxClkCal.clkSrc = SuperFemto_ClkSrcId_GpsPps;
|
||||
l1if_req_compl(fl1h, msg, clock_setup_cb, NULL);
|
||||
|
||||
/* Reset the error counters */
|
||||
msg = sysp_msgb_alloc();
|
||||
sysp = msgb_sysprim(msg);
|
||||
sysp->id = SuperFemto_PrimId_RfClockInfoReq;
|
||||
sysp->u.rfClockInfoReq.u8RstClkCal = 1;
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
|
||||
#include "misc/sysmobts_par.h"
|
||||
#include "femtobts.h"
|
||||
#include "l1_if.h"
|
||||
#include "utils.h"
|
||||
@@ -85,75 +84,6 @@ DEFUN(cfg_bts_no_auto_band, cfg_bts_no_auto_band_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_unit_id, cfg_bts_unit_id_cmd,
|
||||
"ipa unit-id eeprom",
|
||||
"ip.access RSL commands\n"
|
||||
"Set the Unit ID of this BTS\n"
|
||||
"Use serial number as Unit ID\n")
|
||||
{
|
||||
int serial_nr, rc;
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
rc = sysmobts_par_get_int(SYSMOBTS_PAR_SERNR, &serial_nr);
|
||||
if (rc != 0) {
|
||||
vty_out(vty, "Failed to read serial number%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (serial_nr < 0) {
|
||||
vty_out(vty, "Serial number(%d) not valid%s",
|
||||
serial_nr, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
btsb->unitid_use_eeprom = 1;
|
||||
bts->ip_access.bts_id = 0;
|
||||
bts->ip_access.site_id = serial_nr;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_no_unit_id, cfg_bts_no_unit_id_cmd,
|
||||
"no ipa unit-id eeprom",
|
||||
NO_STR "ip.access RSL commands\n"
|
||||
"Set the Unit ID of this BTS\n"
|
||||
"Use serial number as Unit ID\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
btsb->unitid_use_eeprom = 0;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_gsmtap_sapi, cfg_trx_gsmtap_sapi_cmd,
|
||||
"HIDDEN", "HIDDEN")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
int sapi;
|
||||
|
||||
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
|
||||
|
||||
fl1h->gsmtap_sapi_mask |= (1 << sapi);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_no_gsmtap_sapi, cfg_trx_no_gsmtap_sapi_cmd,
|
||||
"HIDDEN", "HIDDEN")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
int sapi;
|
||||
|
||||
sapi = get_string_value(femtobts_l1sapi_names, argv[0]);
|
||||
|
||||
fl1h->gsmtap_sapi_mask &= ~(1 << sapi);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_clkcal_eeprom, cfg_trx_clkcal_eeprom_cmd,
|
||||
"clock-calibration eeprom",
|
||||
"Use the eeprom clock calibration value\n")
|
||||
@@ -245,15 +175,15 @@ DEFUN(cfg_trx_cal_path, cfg_trx_cal_path_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
|
||||
DEFUN_DEPRECATED(cfg_trx_ul_power_target, cfg_trx_ul_power_target_cmd,
|
||||
"uplink-power-target <-110-0>",
|
||||
"Set the nominal target Rx Level for uplink power control loop\n"
|
||||
"Obsolete alias for bts uplink-power-target\n"
|
||||
"Target uplink Rx level in dBm\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(trx->bts);
|
||||
|
||||
fl1h->ul_power_target = atoi(argv[0]);
|
||||
btsb->ul_power_target = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -537,41 +467,11 @@ void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
|
||||
|
||||
if (btsb->auto_band)
|
||||
vty_out(vty, " auto-band%s", VTY_NEWLINE);
|
||||
if (btsb->unitid_use_eeprom)
|
||||
vty_out(vty, " ipa unit-id eeprom%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
/* FIXME: move to libosmocore ? */
|
||||
static char buf_casecnvt[256];
|
||||
char *osmo_str_tolower(const char *in)
|
||||
{
|
||||
int len, i;
|
||||
|
||||
if (!in)
|
||||
return NULL;
|
||||
|
||||
len = strlen(in);
|
||||
if (len > sizeof(buf_casecnvt))
|
||||
len = sizeof(buf_casecnvt);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf_casecnvt[i] = tolower(in[i]);
|
||||
if (in[i] == '\0')
|
||||
break;
|
||||
}
|
||||
if (i < sizeof(buf_casecnvt))
|
||||
buf_casecnvt[i] = '\0';
|
||||
|
||||
/* just to make sure we're always zero-terminated */
|
||||
buf_casecnvt[sizeof(buf_casecnvt)-1] = '\0';
|
||||
|
||||
return buf_casecnvt;
|
||||
}
|
||||
|
||||
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
|
||||
int i;
|
||||
|
||||
if (fl1h->clk_use_eeprom)
|
||||
vty_out(vty, " clock-calibration eeprom%s", VTY_NEWLINE);
|
||||
@@ -584,8 +484,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
||||
vty_out(vty, " clock-source %s%s",
|
||||
get_value_string(femtobts_clksrc_names, fl1h->clk_src),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " uplink-power-target %d%s", fl1h->ul_power_target,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " min-qual-rach %.0f%s", fl1h->min_qual_rach * 10.0f,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " min-qual-norm %.0f%s", fl1h->min_qual_norm * 10.0f,
|
||||
@@ -593,14 +491,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
||||
if (trx->nominal_power != sysmobts_get_nominal_power(trx))
|
||||
vty_out(vty, " nominal-tx-power %d%s", trx->nominal_power,
|
||||
VTY_NEWLINE);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (fl1h->gsmtap_sapi_mask & (1 << i)) {
|
||||
const char *name = get_value_string(femtobts_l1sapi_names, i);
|
||||
vty_out(vty, " gsmtap-sapi %s%s", osmo_str_tolower(name),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int bts_model_vty_init(struct gsm_bts *bts)
|
||||
@@ -622,20 +512,6 @@ int bts_model_vty_init(struct gsm_bts *bts)
|
||||
NO_STR TRX_STR DSP_TRACE_F_STR,
|
||||
"\n", "", 0);
|
||||
|
||||
cfg_trx_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||
"gsmtap-sapi (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
cfg_trx_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||
"GSMTAP SAPI\n",
|
||||
"\n", "", 0);
|
||||
|
||||
cfg_trx_no_gsmtap_sapi_cmd.string = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||
"no gsmtap-sapi (",
|
||||
"|",")", VTY_DO_LOWER);
|
||||
cfg_trx_no_gsmtap_sapi_cmd.doc = vty_cmd_string_from_valstr(bts, femtobts_l1sapi_names,
|
||||
NO_STR "GSMTAP SAPI\n",
|
||||
"\n", "", 0);
|
||||
|
||||
install_element_ve(&show_dsp_trace_f_cmd);
|
||||
install_element_ve(&show_sys_info_cmd);
|
||||
install_element_ve(&show_trx_clksrc_cmd);
|
||||
@@ -652,17 +528,12 @@ int bts_model_vty_init(struct gsm_bts *bts)
|
||||
|
||||
install_element(BTS_NODE, &cfg_bts_auto_band_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_auto_band_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_unit_id_cmd);
|
||||
|
||||
install_element(TRX_NODE, &cfg_trx_clkcal_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_clkcal_eeprom_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_clkcal_def_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_clksrc_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_cal_path_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_gsmtap_sapi_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_no_gsmtap_sapi_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_ul_power_target_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_min_qual_rach_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_min_qual_norm_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/measurement.h>
|
||||
#include <osmo-bts/amr.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
#include <sysmocom/femtobts/superfemto.h>
|
||||
#include <sysmocom/femtobts/gsml1prim.h>
|
||||
@@ -425,7 +426,7 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||
|
||||
#define RTP_MSGB_ALLOC_SIZE 512
|
||||
|
||||
/*! \brief call-back function for incoming RTP
|
||||
/*! \brief function for incoming RTP via TCH.req
|
||||
* \param rs RTP Socket
|
||||
* \param[in] rtp_pl buffer containing RTP payload
|
||||
* \param[in] rtp_pl_len length of \a rtp_pl
|
||||
@@ -437,34 +438,18 @@ static int rtppayload_to_l1_amr(uint8_t *l1_payload, const uint8_t *rtp_payload,
|
||||
* yet, as things like the frame number, etc. are unknown at the time we
|
||||
* pre-fill the primtive.
|
||||
*/
|
||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
unsigned int rtp_pl_len)
|
||||
void l1if_tch_encode(struct gsm_lchan *lchan, uint8_t *data, uint8_t *len,
|
||||
const uint8_t *rtp_pl, unsigned int rtp_pl_len)
|
||||
{
|
||||
struct gsm_lchan *lchan = rs->priv;
|
||||
struct msgb *msg;
|
||||
GsmL1_Prim_t *l1p;
|
||||
GsmL1_PhDataReq_t *data_req;
|
||||
GsmL1_MsgUnitParam_t *msu_param;
|
||||
uint8_t *payload_type;
|
||||
uint8_t *l1_payload;
|
||||
int rc;
|
||||
|
||||
/* skip processing of incoming RTP frames if we are in loopback mode */
|
||||
if (lchan->loopback)
|
||||
return;
|
||||
DEBUGP(DRTP, "%s RTP IN: %s\n", gsm_lchan_name(lchan),
|
||||
osmo_hexdump(rtp_pl, rtp_pl_len));
|
||||
|
||||
msg = l1p_msgb_alloc();
|
||||
if (!msg) {
|
||||
LOGP(DRTP, LOGL_ERROR, "%s: Failed to allocate Rx payload.\n",
|
||||
gsm_lchan_name(lchan));
|
||||
return;
|
||||
}
|
||||
|
||||
l1p = msgb_l1prim(msg);
|
||||
data_req = &l1p->u.phDataReq;
|
||||
msu_param = &data_req->msgUnitParam;
|
||||
payload_type = &msu_param->u8Buffer[0];
|
||||
l1_payload = &msu_param->u8Buffer[1];
|
||||
payload_type = &data[0];
|
||||
l1_payload = &data[1];
|
||||
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
@@ -499,38 +484,22 @@ void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "%s unable to parse RTP payload\n",
|
||||
gsm_lchan_name(lchan));
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
msu_param->u8Size = rc + 1;
|
||||
*len = rc + 1;
|
||||
|
||||
DEBUGP(DRTP, "%s RTP->L1: %s\n", gsm_lchan_name(lchan),
|
||||
osmo_hexdump(data, *len));
|
||||
}
|
||||
|
||||
/* make sure the number of entries in the dl_tch_queue is never
|
||||
* more than 3 */
|
||||
{
|
||||
struct msgb *tmp;
|
||||
int count = 0;
|
||||
|
||||
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
|
||||
count++;
|
||||
|
||||
DEBUGP(DL1C, "%s DL TCH queue length = %u\n",
|
||||
gsm_lchan_name(lchan), count);
|
||||
|
||||
while (count >= 2) {
|
||||
tmp = msgb_dequeue(&lchan->dl_tch_queue);
|
||||
msgb_free(tmp);
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* enqueue msgb to be transmitted to L1 */
|
||||
msgb_enqueue(&lchan->dl_tch_queue, msg);
|
||||
static int is_recv_only(uint8_t speech_mode)
|
||||
{
|
||||
return (speech_mode & 0xF0) == (1 << 4);
|
||||
}
|
||||
|
||||
/*! \brief receive a traffic L1 primitive for a given lchan */
|
||||
int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
||||
int l1if_tch_rx(struct gsm_bts_trx *trx, uint8_t chan_nr, struct msgb *l1p_msg)
|
||||
{
|
||||
GsmL1_Prim_t *l1p = msgb_l1prim(l1p_msg);
|
||||
GsmL1_PhDataInd_t *data_ind = &l1p->u.phDataInd;
|
||||
@@ -538,50 +507,18 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
||||
uint8_t *payload = data_ind->msgUnitParam.u8Buffer + 1;
|
||||
uint8_t payload_len;
|
||||
struct msgb *rmsg = NULL;
|
||||
struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)].lchan[l1sap_chan2ss(chan_nr)];
|
||||
|
||||
if (is_recv_only(lchan->abis_ip.speech_mode))
|
||||
return -EAGAIN;
|
||||
|
||||
if (data_ind->msgUnitParam.u8Size < 1) {
|
||||
LOGP(DL1C, LOGL_ERROR, "%s Rx Payload size 0\n",
|
||||
gsm_lchan_name(lchan));
|
||||
LOGP(DL1C, LOGL_ERROR, "chan_nr %d Rx Payload size 0\n",
|
||||
chan_nr);
|
||||
return -EINVAL;
|
||||
}
|
||||
payload_len = data_ind->msgUnitParam.u8Size - 1;
|
||||
|
||||
if (lchan->loopback) {
|
||||
GsmL1_Prim_t *rl1p;
|
||||
GsmL1_PhDataReq_t *data_req;
|
||||
GsmL1_MsgUnitParam_t *msu_param;
|
||||
|
||||
struct msgb *tmp;
|
||||
int count = 0;
|
||||
|
||||
/* generate a new msgb from the paylaod */
|
||||
rmsg = l1p_msgb_alloc();
|
||||
if (!rmsg)
|
||||
return -ENOMEM;
|
||||
|
||||
rl1p = msgb_l1prim(rmsg);
|
||||
data_req = &rl1p->u.phDataReq;
|
||||
msu_param = &data_req->msgUnitParam;
|
||||
|
||||
memcpy(msu_param->u8Buffer,
|
||||
data_ind->msgUnitParam.u8Buffer,
|
||||
data_ind->msgUnitParam.u8Size);
|
||||
msu_param->u8Size = data_ind->msgUnitParam.u8Size;
|
||||
|
||||
/* make sure the queue doesn't get too long */
|
||||
llist_for_each_entry(tmp, &lchan->dl_tch_queue, list)
|
||||
count++;
|
||||
while (count >= 1) {
|
||||
tmp = msgb_dequeue(&lchan->dl_tch_queue);
|
||||
msgb_free(tmp);
|
||||
count--;
|
||||
}
|
||||
|
||||
msgb_enqueue(&lchan->dl_tch_queue, rmsg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (payload_type) {
|
||||
case GsmL1_TchPlType_Fr:
|
||||
#if defined(L1_HAS_EFR) && defined(USE_L1_RTP_MODE)
|
||||
@@ -625,11 +562,20 @@ int l1if_tch_rx(struct gsm_lchan *lchan, struct msgb *l1p_msg)
|
||||
}
|
||||
|
||||
if (rmsg) {
|
||||
/* hand rmsg to RTP code for transmission */
|
||||
if (lchan->abis_ip.rtp_socket)
|
||||
osmo_rtp_send_frame(lchan->abis_ip.rtp_socket,
|
||||
rmsg->data, rmsg->len, 160);
|
||||
msgb_free(rmsg);
|
||||
struct osmo_phsap_prim *l1sap;
|
||||
|
||||
LOGP(DL1C, LOGL_DEBUG, "%s Rx -> RTP: %s\n",
|
||||
gsm_lchan_name(lchan), osmo_hexdump(rmsg->data, rmsg->len));
|
||||
|
||||
/* add l1sap header */
|
||||
rmsg->l2h = rmsg->data;
|
||||
msgb_push(rmsg, sizeof(*l1sap));
|
||||
rmsg->l1h = rmsg->data;
|
||||
l1sap = msgb_l1sap_prim(rmsg);
|
||||
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_INDICATION, rmsg);
|
||||
l1sap->u.tch.chan_nr = chan_nr;
|
||||
|
||||
return l1sap_up(trx, l1sap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
11
src/osmo-bts-trx/Makefile.am
Normal file
11
src/osmo-bts-trx/Makefile.am
Normal file
@@ -0,0 +1,11 @@
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
|
||||
AM_CFLAGS = -Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOABIS_CFLAGS)
|
||||
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS) -lortp
|
||||
|
||||
EXTRA_DIST = trx_if.h l1_if.h scheduler.h gsm0503_parity.h gsm0503_conv.h gsm0503_interleaving.h gsm0503_mapping.h gsm0503_coding.h gsm0503_tables.h loops.h amr.h
|
||||
|
||||
bin_PROGRAMS = osmobts-trx
|
||||
|
||||
osmobts_trx_SOURCES = main.c trx_if.c l1_if.c scheduler.c trx_vty.c gsm0503_parity.c gsm0503_conv.c gsm0503_interleaving.c gsm0503_mapping.c gsm0503_coding.c gsm0503_tables.c loops.c amr.c
|
||||
osmobts_trx_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
|
||||
81
src/osmo-bts-trx/amr.c
Normal file
81
src/osmo-bts-trx/amr.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/* AMR support for OsmoBTS-TRX */
|
||||
|
||||
/* (C) 2013 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 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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static int amr_len_by_ft[16] = {
|
||||
12, 13, 15, 17, 19, 20, 26, 31,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
int amr_decompose_payload(uint8_t *payload, int payload_len, uint8_t *_cmr,
|
||||
uint8_t *_ft, uint8_t *_bfi)
|
||||
{
|
||||
uint8_t cmr, f, ft, q;
|
||||
|
||||
if (payload_len < 2)
|
||||
return -EINVAL;
|
||||
|
||||
cmr = payload[0] >> 4;
|
||||
if (_cmr)
|
||||
*_cmr = cmr;
|
||||
|
||||
f = payload[1] >> 7;
|
||||
|
||||
ft = (payload[1] >> 3) & 0xf;
|
||||
if (_ft)
|
||||
*_ft = ft;
|
||||
|
||||
q = (payload[1] >> 2) & 0x1;
|
||||
if (_bfi)
|
||||
*_bfi = !q;
|
||||
|
||||
if (f) {
|
||||
fprintf(stderr, "%s: multiple payloads not supported\n",
|
||||
__func__);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (payload_len - 2 < amr_len_by_ft[ft])
|
||||
return -EINVAL;
|
||||
|
||||
return 2 + amr_len_by_ft[ft];
|
||||
}
|
||||
|
||||
int amr_compose_payload(uint8_t *payload, uint8_t cmr, uint8_t ft, uint8_t bfi)
|
||||
{
|
||||
if (cmr >= 16)
|
||||
return -EINVAL;
|
||||
|
||||
if (ft >= 16)
|
||||
return -EINVAL;
|
||||
|
||||
payload[0] = cmr << 4;
|
||||
|
||||
payload[1] = (ft << 3) | ((!bfi) << 2); /* F = 0 */
|
||||
|
||||
return 2 + amr_len_by_ft[ft];
|
||||
}
|
||||
|
||||
8
src/osmo-bts-trx/amr.h
Normal file
8
src/osmo-bts-trx/amr.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef _TRX_AMR_H
|
||||
#define _TRX_AMR_H
|
||||
|
||||
int amr_decompose_payload(uint8_t *payload, int payload_len, uint8_t *_cmr,
|
||||
uint8_t *_ft, uint8_t *_bfi);
|
||||
int amr_compose_payload(uint8_t *payload, uint8_t cmr, uint8_t ft, uint8_t bfi);
|
||||
|
||||
#endif /* _TRX_AMR_H */
|
||||
1696
src/osmo-bts-trx/gsm0503_coding.c
Normal file
1696
src/osmo-bts-trx/gsm0503_coding.c
Normal file
File diff suppressed because it is too large
Load Diff
53
src/osmo-bts-trx/gsm0503_coding.h
Normal file
53
src/osmo-bts-trx/gsm0503_coding.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _0503_CODING_H
|
||||
#define _0503_CODING_H
|
||||
|
||||
int xcch_decode(uint8_t *l2_data, sbit_t *bursts,
|
||||
int *n_errors, int *n_bits_total);
|
||||
int xcch_encode(ubit_t *bursts, uint8_t *l2_data);
|
||||
int pdtch_decode(uint8_t *l2_data, sbit_t *bursts, uint8_t *usf_p,
|
||||
int *n_errors, int *n_bits_total);
|
||||
int pdtch_encode(ubit_t *bursts, uint8_t *l2_data, uint8_t l2_len);
|
||||
int tch_fr_decode(uint8_t *tch_data, sbit_t *bursts, int net_order,
|
||||
int efr, int *n_errors, int *n_bits_total);
|
||||
int tch_fr_encode(ubit_t *bursts, uint8_t *tch_data, int len, int net_order);
|
||||
int tch_hr_decode(uint8_t *tch_data, sbit_t *bursts, int odd,
|
||||
int *n_errors, int *n_bits_total);
|
||||
int tch_hr_encode(ubit_t *bursts, uint8_t *tch_data, int len);
|
||||
int tch_afs_decode(uint8_t *tch_data, sbit_t *bursts, int codec_mode_req,
|
||||
uint8_t *codec, int codecs, uint8_t *ft, uint8_t *cmr,
|
||||
int *n_errors, int *n_bits_total);
|
||||
int tch_afs_encode(ubit_t *bursts, uint8_t *tch_data, int len,
|
||||
int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
|
||||
uint8_t cmr);
|
||||
int tch_ahs_decode(uint8_t *tch_data, sbit_t *bursts, int odd,
|
||||
int codec_mode_req, uint8_t *codec, int codecs, uint8_t *ft,
|
||||
uint8_t *cmr, int *n_errors, int *n_bits_total);
|
||||
int tch_ahs_encode(ubit_t *bursts, uint8_t *tch_data, int len,
|
||||
int codec_mode_req, uint8_t *codec, int codecs, uint8_t ft,
|
||||
uint8_t cmr);
|
||||
int rach_decode(uint8_t *ra, sbit_t *burst, uint8_t bsic);
|
||||
int rach_encode(ubit_t *burst, uint8_t *ra, uint8_t bsic);
|
||||
int sch_decode(uint8_t *sb_info, sbit_t *burst);
|
||||
int sch_encode(ubit_t *burst, uint8_t *sb_info);
|
||||
|
||||
#endif /* _0503_CODING_H */
|
||||
950
src/osmo-bts-trx/gsm0503_conv.c
Normal file
950
src/osmo-bts-trx/gsm0503_conv.c
Normal file
@@ -0,0 +1,950 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/conv.h>
|
||||
|
||||
#include "gsm0503_conv.h"
|
||||
|
||||
/*
|
||||
* GSM convolutional coding
|
||||
*
|
||||
* G_0 = 1 + x^3 + x^4
|
||||
* G_1 = 1 + x + x^3 + x^4
|
||||
*/
|
||||
|
||||
static const uint8_t conv_xcch_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
|
||||
{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_xcch_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_xcch = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 224,
|
||||
.next_output = conv_xcch_next_output,
|
||||
.next_state = conv_xcch_next_state,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_cs2 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 290,
|
||||
.next_output = conv_xcch_next_output,
|
||||
.next_state = conv_xcch_next_state,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_cs3 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 334,
|
||||
.next_output = conv_xcch_next_output,
|
||||
.next_state = conv_xcch_next_state,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_rach = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 14,
|
||||
.next_output = conv_xcch_next_output,
|
||||
.next_state = conv_xcch_next_state,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_sch = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 35,
|
||||
.next_output = conv_xcch_next_output,
|
||||
.next_state = conv_xcch_next_state,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_fr = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 185,
|
||||
.next_output = conv_xcch_next_output,
|
||||
.next_state = conv_xcch_next_state,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* GSM HR convolutional coding
|
||||
*
|
||||
* G_0 = 1 + x^2 + x^3 + x^5 + x^6
|
||||
* G_1 = 1 + x + x^4 + x^6
|
||||
* G_3 = 1 + x + x^2 + x^3 + x^4 + x^6
|
||||
*/
|
||||
|
||||
static const uint8_t conv_tch_hr_next_output[][2] = {
|
||||
{ 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 },
|
||||
{ 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 },
|
||||
{ 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 },
|
||||
{ 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 },
|
||||
{ 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 },
|
||||
{ 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 },
|
||||
{ 7, 0 }, { 4, 3 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 7, 0 }, { 4, 3 },
|
||||
{ 4, 3 }, { 7, 0 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 4, 3 }, { 7, 0 },
|
||||
{ 3, 4 }, { 0, 7 }, { 6, 1 }, { 5, 2 },
|
||||
{ 6, 1 }, { 5, 2 }, { 3, 4 }, { 0, 7 },
|
||||
{ 0, 7 }, { 3, 4 }, { 5, 2 }, { 6, 1 },
|
||||
{ 5, 2 }, { 6, 1 }, { 0, 7 }, { 3, 4 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_hr_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
{ 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
|
||||
{ 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
|
||||
{ 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
|
||||
{ 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
|
||||
{ 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
|
||||
{ 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
{ 16, 17 }, { 18, 19 }, { 20, 21 }, { 22, 23 },
|
||||
{ 24, 25 }, { 26, 27 }, { 28, 29 }, { 30, 31 },
|
||||
{ 32, 33 }, { 34, 35 }, { 36, 37 }, { 38, 39 },
|
||||
{ 40, 41 }, { 42, 43 }, { 44, 45 }, { 46, 47 },
|
||||
{ 48, 49 }, { 50, 51 }, { 52, 53 }, { 54, 55 },
|
||||
{ 56, 57 }, { 58, 59 }, { 60, 61 }, { 62, 63 },
|
||||
};
|
||||
|
||||
static const int conv_tch_hr_puncture[] = {
|
||||
/* Class 1 bits */
|
||||
1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34,
|
||||
37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70,
|
||||
73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106,
|
||||
109, 112, 115, 118, 121, 124, 127, 130, 133, 136, 139, 142,
|
||||
145, 148, 151, 154, 157, 160, 163, 166, 169, 172, 175, 178,
|
||||
181, 184, 187, 190, 193, 196, 199, 202, 205, 208, 211, 214,
|
||||
217, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250,
|
||||
253, 256, 259, 262, 265, 268, 271, 274, 277, 280, 283,
|
||||
|
||||
/* Tail bits */
|
||||
295, 298, 301, 304, 307, 310,
|
||||
|
||||
/* End */
|
||||
-1,
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_hr = {
|
||||
.N = 3,
|
||||
.K = 7,
|
||||
.len = 98,
|
||||
.next_output = conv_tch_hr_next_output,
|
||||
.next_state = conv_tch_hr_next_state,
|
||||
.puncture = conv_tch_hr_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS12.2 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_12_2_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_12_2_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
|
||||
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_12_2_next_term_output[] = {
|
||||
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_12_2_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_12_2_puncture[] = {
|
||||
321, 325, 329, 333, 337, 341, 345, 349, 353, 357, 361, 363,
|
||||
365, 369, 373, 377, 379, 381, 385, 389, 393, 395, 397, 401,
|
||||
405, 409, 411, 413, 417, 421, 425, 427, 429, 433, 437, 441,
|
||||
443, 445, 449, 453, 457, 459, 461, 465, 469, 473, 475, 477,
|
||||
481, 485, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_12_2 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 250,
|
||||
.next_output = conv_tch_afs_12_2_next_output,
|
||||
.next_state = conv_tch_afs_12_2_next_state,
|
||||
.next_term_output = conv_tch_afs_12_2_next_term_output,
|
||||
.next_term_state = conv_tch_afs_12_2_next_term_state,
|
||||
.puncture = conv_tch_afs_12_2_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS10.2 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_10_2_next_output[][2] = {
|
||||
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
|
||||
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
|
||||
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
|
||||
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_10_2_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_10_2_next_term_output[] = {
|
||||
0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_10_2_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_10_2_puncture[] = {
|
||||
1, 4, 7, 10, 16, 19, 22, 28, 31, 34, 40, 43,
|
||||
46, 52, 55, 58, 64, 67, 70, 76, 79, 82, 88, 91,
|
||||
94, 100, 103, 106, 112, 115, 118, 124, 127, 130, 136, 139,
|
||||
142, 148, 151, 154, 160, 163, 166, 172, 175, 178, 184, 187,
|
||||
190, 196, 199, 202, 208, 211, 214, 220, 223, 226, 232, 235,
|
||||
238, 244, 247, 250, 256, 259, 262, 268, 271, 274, 280, 283,
|
||||
286, 292, 295, 298, 304, 307, 310, 316, 319, 322, 325, 328,
|
||||
331, 334, 337, 340, 343, 346, 349, 352, 355, 358, 361, 364,
|
||||
367, 370, 373, 376, 379, 382, 385, 388, 391, 394, 397, 400,
|
||||
403, 406, 409, 412, 415, 418, 421, 424, 427, 430, 433, 436,
|
||||
439, 442, 445, 448, 451, 454, 457, 460, 463, 466, 469, 472,
|
||||
475, 478, 481, 484, 487, 490, 493, 496, 499, 502, 505, 508,
|
||||
511, 514, 517, 520, 523, 526, 529, 532, 535, 538, 541, 544,
|
||||
547, 550, 553, 556, 559, 562, 565, 568, 571, 574, 577, 580,
|
||||
583, 586, 589, 592, 595, 598, 601, 604, 607, 609, 610, 613,
|
||||
616, 619, 621, 622, 625, 627, 628, 631, 633, 634, 636, 637,
|
||||
639, 640,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_10_2 = {
|
||||
.N = 3,
|
||||
.K = 5,
|
||||
.len = 210,
|
||||
.next_output = conv_tch_afs_10_2_next_output,
|
||||
.next_state = conv_tch_afs_10_2_next_state,
|
||||
.next_term_output = conv_tch_afs_10_2_next_term_output,
|
||||
.next_term_state = conv_tch_afs_10_2_next_term_state,
|
||||
.puncture = conv_tch_afs_10_2_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS7.95 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_7_95_next_output[][2] = {
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_7_95_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
|
||||
{ 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
|
||||
{ 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
|
||||
{ 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
|
||||
{ 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
|
||||
{ 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
|
||||
{ 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
|
||||
{ 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
|
||||
{ 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
|
||||
{ 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
|
||||
{ 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
|
||||
{ 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
|
||||
{ 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
|
||||
{ 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
|
||||
{ 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_7_95_next_term_output[] = {
|
||||
0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0,
|
||||
4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4,
|
||||
7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
|
||||
3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_7_95_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_7_95_puncture[] = {
|
||||
1, 2, 4, 5, 8, 22, 70, 118, 166, 214, 262, 310,
|
||||
317, 319, 325, 332, 334, 341, 343, 349, 356, 358, 365, 367,
|
||||
373, 380, 382, 385, 389, 391, 397, 404, 406, 409, 413, 415,
|
||||
421, 428, 430, 433, 437, 439, 445, 452, 454, 457, 461, 463,
|
||||
469, 476, 478, 481, 485, 487, 490, 493, 500, 502, 503, 505,
|
||||
506, 508, 509, 511, 512,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_7_95 = {
|
||||
.N = 3,
|
||||
.K = 7,
|
||||
.len = 165,
|
||||
.next_output = conv_tch_afs_7_95_next_output,
|
||||
.next_state = conv_tch_afs_7_95_next_state,
|
||||
.next_term_output = conv_tch_afs_7_95_next_term_output,
|
||||
.next_term_state = conv_tch_afs_7_95_next_term_state,
|
||||
.puncture = conv_tch_afs_7_95_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS7.4 */
|
||||
/* ---------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_7_4_next_output[][2] = {
|
||||
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
|
||||
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
|
||||
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
|
||||
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_7_4_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_7_4_next_term_output[] = {
|
||||
0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_7_4_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_7_4_puncture[] = {
|
||||
0, 355, 361, 367, 373, 379, 385, 391, 397, 403, 409, 415,
|
||||
421, 427, 433, 439, 445, 451, 457, 460, 463, 466, 468, 469,
|
||||
471, 472,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_7_4 = {
|
||||
.N = 3,
|
||||
.K = 5,
|
||||
.len = 154,
|
||||
.next_output = conv_tch_afs_7_4_next_output,
|
||||
.next_state = conv_tch_afs_7_4_next_state,
|
||||
.next_term_output = conv_tch_afs_7_4_next_term_output,
|
||||
.next_term_state = conv_tch_afs_7_4_next_term_state,
|
||||
.puncture = conv_tch_afs_7_4_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS6.7 */
|
||||
/* ---------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_6_7_next_output[][2] = {
|
||||
{ 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 },
|
||||
{ 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 },
|
||||
{ 0, 15 }, { 4, 11 }, { 8, 7 }, { 12, 3 },
|
||||
{ 4, 11 }, { 0, 15 }, { 12, 3 }, { 8, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_6_7_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static int conv_tch_afs_6_7_puncture[] = {
|
||||
1, 3, 7, 11, 15, 27, 39, 55, 67, 79, 95, 107,
|
||||
119, 135, 147, 159, 175, 187, 199, 215, 227, 239, 255, 267,
|
||||
279, 287, 291, 295, 299, 303, 307, 311, 315, 319, 323, 327,
|
||||
331, 335, 339, 343, 347, 351, 355, 359, 363, 367, 369, 371,
|
||||
375, 377, 379, 383, 385, 387, 391, 393, 395, 399, 401, 403,
|
||||
407, 409, 411, 415, 417, 419, 423, 425, 427, 431, 433, 435,
|
||||
439, 441, 443, 447, 449, 451, 455, 457, 459, 463, 465, 467,
|
||||
471, 473, 475, 479, 481, 483, 487, 489, 491, 495, 497, 499,
|
||||
503, 505, 507, 511, 513, 515, 519, 521, 523, 527, 529, 531,
|
||||
535, 537, 539, 543, 545, 547, 549, 551, 553, 555, 557, 559,
|
||||
561, 563, 565, 567, 569, 571, 573, 575,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_6_7_next_term_output[] = {
|
||||
0, 11, 7, 12, 11, 0, 12, 7, 15, 4, 8, 3, 4, 15, 3, 8,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_6_7_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_6_7 = {
|
||||
.N = 4,
|
||||
.K = 5,
|
||||
.len = 140,
|
||||
.next_output = conv_tch_afs_6_7_next_output,
|
||||
.next_state = conv_tch_afs_6_7_next_state,
|
||||
.next_term_output = conv_tch_afs_6_7_next_term_output,
|
||||
.next_term_state = conv_tch_afs_6_7_next_term_state,
|
||||
.puncture = conv_tch_afs_6_7_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS5.9 */
|
||||
/* ---------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_5_9_next_output[][2] = {
|
||||
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
|
||||
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
|
||||
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
|
||||
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
|
||||
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
|
||||
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
|
||||
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
|
||||
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
|
||||
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
|
||||
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
|
||||
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
|
||||
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
|
||||
{ 8, 7 }, { 0, 15 }, { 12, 3 }, { 4, 11 },
|
||||
{ 12, 3 }, { 4, 11 }, { 8, 7 }, { 0, 15 },
|
||||
{ 0, 15 }, { 8, 7 }, { 4, 11 }, { 12, 3 },
|
||||
{ 4, 11 }, { 12, 3 }, { 0, 15 }, { 8, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_5_9_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 },
|
||||
{ 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 },
|
||||
{ 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 },
|
||||
{ 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 },
|
||||
{ 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 },
|
||||
{ 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
{ 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 },
|
||||
{ 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 },
|
||||
{ 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 },
|
||||
{ 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 },
|
||||
{ 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 },
|
||||
{ 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_5_9_next_term_output[] = {
|
||||
0, 7, 11, 12, 11, 12, 0, 7, 7, 0, 12, 11, 12, 11, 7, 0,
|
||||
8, 15, 3, 4, 3, 4, 8, 15, 15, 8, 4, 3, 4, 3, 15, 8,
|
||||
15, 8, 4, 3, 4, 3, 15, 8, 8, 15, 3, 4, 3, 4, 8, 15,
|
||||
7, 0, 12, 11, 12, 11, 7, 0, 0, 7, 11, 12, 11, 12, 0, 7,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_5_9_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_5_9_puncture[] = {
|
||||
0, 1, 3, 5, 7, 11, 15, 31, 47, 63, 79, 95,
|
||||
111, 127, 143, 159, 175, 191, 207, 223, 239, 255, 271, 287,
|
||||
303, 319, 327, 331, 335, 343, 347, 351, 359, 363, 367, 375,
|
||||
379, 383, 391, 395, 399, 407, 411, 415, 423, 427, 431, 439,
|
||||
443, 447, 455, 459, 463, 467, 471, 475, 479, 483, 487, 491,
|
||||
495, 499, 503, 507, 509, 511, 512, 513, 515, 516, 517, 519,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_5_9 = {
|
||||
.N = 4,
|
||||
.K = 7,
|
||||
.len = 124,
|
||||
.next_output = conv_tch_afs_5_9_next_output,
|
||||
.next_state = conv_tch_afs_5_9_next_state,
|
||||
.next_term_output = conv_tch_afs_5_9_next_term_output,
|
||||
.next_term_state = conv_tch_afs_5_9_next_term_state,
|
||||
.puncture = conv_tch_afs_5_9_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS5.15 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_5_15_next_output[][2] = {
|
||||
{ 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 },
|
||||
{ 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 },
|
||||
{ 0, 31 }, { 4, 27 }, { 24, 7 }, { 28, 3 },
|
||||
{ 4, 27 }, { 0, 31 }, { 28, 3 }, { 24, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_5_15_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_5_15_next_term_output[] = {
|
||||
0, 27, 7, 28, 27, 0, 28, 7, 31, 4, 24, 3, 4, 31, 3, 24,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_5_15_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_5_15_puncture[] = {
|
||||
0, 4, 5, 9, 10, 14, 15, 20, 25, 30, 35, 40,
|
||||
50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160,
|
||||
170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280,
|
||||
290, 300, 310, 315, 320, 325, 330, 334, 335, 340, 344, 345,
|
||||
350, 354, 355, 360, 364, 365, 370, 374, 375, 380, 384, 385,
|
||||
390, 394, 395, 400, 404, 405, 410, 414, 415, 420, 424, 425,
|
||||
430, 434, 435, 440, 444, 445, 450, 454, 455, 460, 464, 465,
|
||||
470, 474, 475, 480, 484, 485, 490, 494, 495, 500, 504, 505,
|
||||
510, 514, 515, 520, 524, 525, 529, 530, 534, 535, 539, 540,
|
||||
544, 545, 549, 550, 554, 555, 559, 560, 564,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_5_15 = {
|
||||
.N = 5,
|
||||
.K = 5,
|
||||
.len = 109,
|
||||
.next_output = conv_tch_afs_5_15_next_output,
|
||||
.next_state = conv_tch_afs_5_15_next_state,
|
||||
.next_term_output = conv_tch_afs_5_15_next_term_output,
|
||||
.next_term_state = conv_tch_afs_5_15_next_term_state,
|
||||
.puncture = conv_tch_afs_5_15_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AFS4.75 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_afs_4_75_next_output[][2] = {
|
||||
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
|
||||
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
|
||||
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
|
||||
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
|
||||
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
|
||||
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
|
||||
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
|
||||
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
|
||||
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
|
||||
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
|
||||
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
|
||||
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
|
||||
{ 24, 7 }, { 0, 31 }, { 28, 3 }, { 4, 27 },
|
||||
{ 28, 3 }, { 4, 27 }, { 24, 7 }, { 0, 31 },
|
||||
{ 0, 31 }, { 24, 7 }, { 4, 27 }, { 28, 3 },
|
||||
{ 4, 27 }, { 28, 3 }, { 0, 31 }, { 24, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_4_75_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 17, 16 }, { 18, 19 }, { 20, 21 }, { 23, 22 },
|
||||
{ 24, 25 }, { 27, 26 }, { 29, 28 }, { 30, 31 },
|
||||
{ 32, 33 }, { 35, 34 }, { 37, 36 }, { 38, 39 },
|
||||
{ 41, 40 }, { 42, 43 }, { 44, 45 }, { 47, 46 },
|
||||
{ 49, 48 }, { 50, 51 }, { 52, 53 }, { 55, 54 },
|
||||
{ 56, 57 }, { 59, 58 }, { 61, 60 }, { 62, 63 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
{ 16, 17 }, { 19, 18 }, { 21, 20 }, { 22, 23 },
|
||||
{ 25, 24 }, { 26, 27 }, { 28, 29 }, { 31, 30 },
|
||||
{ 33, 32 }, { 34, 35 }, { 36, 37 }, { 39, 38 },
|
||||
{ 40, 41 }, { 43, 42 }, { 45, 44 }, { 46, 47 },
|
||||
{ 48, 49 }, { 51, 50 }, { 53, 52 }, { 54, 55 },
|
||||
{ 57, 56 }, { 58, 59 }, { 60, 61 }, { 63, 62 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_4_75_next_term_output[] = {
|
||||
0, 7, 27, 28, 27, 28, 0, 7, 7, 0, 28, 27, 28, 27, 7, 0,
|
||||
24, 31, 3, 4, 3, 4, 24, 31, 31, 24, 4, 3, 4, 3, 31, 24,
|
||||
31, 24, 4, 3, 4, 3, 31, 24, 24, 31, 3, 4, 3, 4, 24, 31,
|
||||
7, 0, 28, 27, 28, 27, 7, 0, 0, 7, 27, 28, 27, 28, 0, 7,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_afs_4_75_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
};
|
||||
|
||||
static int conv_tch_afs_4_75_puncture[] = {
|
||||
0, 1, 2, 4, 5, 7, 9, 15, 25, 35, 45, 55,
|
||||
65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175,
|
||||
185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295,
|
||||
305, 315, 325, 335, 345, 355, 365, 375, 385, 395, 400, 405,
|
||||
410, 415, 420, 425, 430, 435, 440, 445, 450, 455, 459, 460,
|
||||
465, 470, 475, 479, 480, 485, 490, 495, 499, 500, 505, 509,
|
||||
510, 515, 517, 519, 520, 522, 524, 525, 526, 527, 529, 530,
|
||||
531, 532, 534,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_afs_4_75 = {
|
||||
.N = 5,
|
||||
.K = 7,
|
||||
.len = 101,
|
||||
.next_output = conv_tch_afs_4_75_next_output,
|
||||
.next_state = conv_tch_afs_4_75_next_state,
|
||||
.next_term_output = conv_tch_afs_4_75_next_term_output,
|
||||
.next_term_state = conv_tch_afs_4_75_next_term_state,
|
||||
.puncture = conv_tch_afs_4_75_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AHS7.95 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_95_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_95_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
|
||||
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_95_next_term_output[] = {
|
||||
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_95_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_ahs_7_95_puncture[] = {
|
||||
1, 3, 5, 7, 11, 15, 19, 23, 27, 31, 35, 43,
|
||||
47, 51, 55, 59, 63, 67, 71, 79, 83, 87, 91, 95,
|
||||
99, 103, 107, 115, 119, 123, 127, 131, 135, 139, 143, 151,
|
||||
155, 159, 163, 167, 171, 175, 177, 179, 183, 185, 187, 191,
|
||||
193, 195, 197, 199, 203, 205, 207, 211, 213, 215, 219, 221,
|
||||
223, 227, 229, 231, 233, 235, 239, 241, 243, 247, 249, 251,
|
||||
255, 257, 259, 261, 263, 265,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 129,
|
||||
.next_output = conv_tch_ahs_7_95_next_output,
|
||||
.next_state = conv_tch_ahs_7_95_next_state,
|
||||
.next_term_output = conv_tch_ahs_7_95_next_term_output,
|
||||
.next_term_state = conv_tch_ahs_7_95_next_term_state,
|
||||
.puncture = conv_tch_ahs_7_95_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AHS7.4 */
|
||||
/* ---------- */
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_4_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_4_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
|
||||
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_4_next_term_output[] = {
|
||||
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_7_4_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_ahs_7_4_puncture[] = {
|
||||
1, 3, 7, 11, 19, 23, 27, 35, 39, 43, 51, 55,
|
||||
59, 67, 71, 75, 83, 87, 91, 99, 103, 107, 115, 119,
|
||||
123, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171,
|
||||
175, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219,
|
||||
221, 223, 227, 229, 231, 235, 237, 239, 243, 245, 247, 251,
|
||||
253, 255, 257, 259,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 126,
|
||||
.next_output = conv_tch_ahs_7_4_next_output,
|
||||
.next_state = conv_tch_ahs_7_4_next_state,
|
||||
.next_term_output = conv_tch_ahs_7_4_next_term_output,
|
||||
.next_term_state = conv_tch_ahs_7_4_next_term_state,
|
||||
.puncture = conv_tch_ahs_7_4_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AHS6.7 */
|
||||
/* ---------- */
|
||||
|
||||
static const uint8_t conv_tch_ahs_6_7_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_6_7_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
|
||||
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_6_7_next_term_output[] = {
|
||||
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_6_7_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_ahs_6_7_puncture[] = {
|
||||
1, 3, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99,
|
||||
109, 119, 129, 139, 149, 159, 167, 169, 177, 179, 187, 189,
|
||||
197, 199, 203, 207, 209, 213, 217, 219, 223, 227, 229, 231,
|
||||
233, 235, 237, 239,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 116,
|
||||
.next_output = conv_tch_ahs_6_7_next_output,
|
||||
.next_state = conv_tch_ahs_6_7_next_state,
|
||||
.next_term_output = conv_tch_ahs_6_7_next_term_output,
|
||||
.next_term_state = conv_tch_ahs_6_7_next_term_state,
|
||||
.puncture = conv_tch_ahs_6_7_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AHS5.9 */
|
||||
/* ---------- */
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_9_next_output[][2] = {
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_9_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
|
||||
{ 9, 8 }, { 11, 10 }, { 13, 12 }, { 15, 14 },
|
||||
{ 1, 0 }, { 3, 2 }, { 5, 4 }, { 7, 6 },
|
||||
{ 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_9_next_term_output[] = {
|
||||
0, 1, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 0, 1, 0, 1,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_9_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_ahs_5_9_puncture[] = {
|
||||
1, 15, 71, 127, 139, 151, 163, 175, 187, 195, 203, 211,
|
||||
215, 219, 221, 223,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9 = {
|
||||
.N = 2,
|
||||
.K = 5,
|
||||
.len = 108,
|
||||
.next_output = conv_tch_ahs_5_9_next_output,
|
||||
.next_state = conv_tch_ahs_5_9_next_state,
|
||||
.next_term_output = conv_tch_ahs_5_9_next_term_output,
|
||||
.next_term_state = conv_tch_ahs_5_9_next_term_state,
|
||||
.puncture = conv_tch_ahs_5_9_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AHS5.15 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_15_next_output[][2] = {
|
||||
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
|
||||
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
|
||||
{ 0, 7 }, { 2, 5 }, { 4, 3 }, { 6, 1 },
|
||||
{ 2, 5 }, { 0, 7 }, { 6, 1 }, { 4, 3 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_15_next_state[][2] = {
|
||||
{ 0, 1 }, { 3, 2 }, { 5, 4 }, { 6, 7 },
|
||||
{ 9, 8 }, { 10, 11 }, { 12, 13 }, { 15, 14 },
|
||||
{ 1, 0 }, { 2, 3 }, { 4, 5 }, { 7, 6 },
|
||||
{ 8, 9 }, { 11, 10 }, { 13, 12 }, { 14, 15 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_15_next_term_output[] = {
|
||||
0, 5, 3, 6, 5, 0, 6, 3, 7, 2, 4, 1, 2, 7, 1, 4,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_5_15_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14,
|
||||
};
|
||||
|
||||
static int conv_tch_ahs_5_15_puncture[] = {
|
||||
0, 1, 3, 4, 6, 9, 12, 15, 18, 21, 27, 33,
|
||||
39, 45, 51, 54, 57, 63, 69, 75, 81, 87, 90, 93,
|
||||
99, 105, 111, 117, 123, 126, 129, 135, 141, 147, 153, 159,
|
||||
162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 192, 195,
|
||||
198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231,
|
||||
234, 237, 240, 243, 244, 246, 249, 252, 255, 256, 258, 261,
|
||||
264, 267, 268, 270, 273, 276, 279, 280, 282, 285, 288, 289,
|
||||
291, 294, 295, 297, 298, 300, 301,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15 = {
|
||||
.N = 3,
|
||||
.K = 5,
|
||||
.len = 97,
|
||||
.next_output = conv_tch_ahs_5_15_next_output,
|
||||
.next_state = conv_tch_ahs_5_15_next_state,
|
||||
.next_term_output = conv_tch_ahs_5_15_next_term_output,
|
||||
.next_term_state = conv_tch_ahs_5_15_next_term_state,
|
||||
.puncture = conv_tch_ahs_5_15_puncture,
|
||||
};
|
||||
|
||||
|
||||
/* TCH/AHS4.75 */
|
||||
/* ----------- */
|
||||
|
||||
static const uint8_t conv_tch_ahs_4_75_next_output[][2] = {
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 3, 4 }, { 0, 7 }, { 1, 6 }, { 2, 5 },
|
||||
{ 1, 6 }, { 2, 5 }, { 3, 4 }, { 0, 7 },
|
||||
{ 0, 7 }, { 3, 4 }, { 2, 5 }, { 1, 6 },
|
||||
{ 2, 5 }, { 1, 6 }, { 0, 7 }, { 3, 4 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_4_75_next_state[][2] = {
|
||||
{ 0, 1 }, { 2, 3 }, { 5, 4 }, { 7, 6 },
|
||||
{ 9, 8 }, { 11, 10 }, { 12, 13 }, { 14, 15 },
|
||||
{ 16, 17 }, { 18, 19 }, { 21, 20 }, { 23, 22 },
|
||||
{ 25, 24 }, { 27, 26 }, { 28, 29 }, { 30, 31 },
|
||||
{ 33, 32 }, { 35, 34 }, { 36, 37 }, { 38, 39 },
|
||||
{ 40, 41 }, { 42, 43 }, { 45, 44 }, { 47, 46 },
|
||||
{ 49, 48 }, { 51, 50 }, { 52, 53 }, { 54, 55 },
|
||||
{ 56, 57 }, { 58, 59 }, { 61, 60 }, { 63, 62 },
|
||||
{ 1, 0 }, { 3, 2 }, { 4, 5 }, { 6, 7 },
|
||||
{ 8, 9 }, { 10, 11 }, { 13, 12 }, { 15, 14 },
|
||||
{ 17, 16 }, { 19, 18 }, { 20, 21 }, { 22, 23 },
|
||||
{ 24, 25 }, { 26, 27 }, { 29, 28 }, { 31, 30 },
|
||||
{ 32, 33 }, { 34, 35 }, { 37, 36 }, { 39, 38 },
|
||||
{ 41, 40 }, { 43, 42 }, { 44, 45 }, { 46, 47 },
|
||||
{ 48, 49 }, { 50, 51 }, { 53, 52 }, { 55, 54 },
|
||||
{ 57, 56 }, { 59, 58 }, { 60, 61 }, { 62, 63 },
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_4_75_next_term_output[] = {
|
||||
0, 3, 5, 6, 5, 6, 0, 3, 3, 0, 6, 5, 6, 5, 3, 0,
|
||||
4, 7, 1, 2, 1, 2, 4, 7, 7, 4, 2, 1, 2, 1, 7, 4,
|
||||
7, 4, 2, 1, 2, 1, 7, 4, 4, 7, 1, 2, 1, 2, 4, 7,
|
||||
3, 0, 6, 5, 6, 5, 3, 0, 0, 3, 5, 6, 5, 6, 0, 3,
|
||||
};
|
||||
|
||||
static const uint8_t conv_tch_ahs_4_75_next_term_state[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
|
||||
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
|
||||
};
|
||||
|
||||
static int conv_tch_ahs_4_75_puncture[] = {
|
||||
1, 2, 4, 5, 7, 8, 10, 13, 16, 22, 28, 34,
|
||||
40, 46, 52, 58, 64, 70, 76, 82, 88, 94, 100, 106,
|
||||
112, 118, 124, 130, 136, 142, 148, 151, 154, 160, 163, 166,
|
||||
172, 175, 178, 184, 187, 190, 196, 199, 202, 208, 211, 214,
|
||||
220, 223, 226, 232, 235, 238, 241, 244, 247, 250, 253, 256,
|
||||
259, 262, 265, 268, 271, 274, 275, 277, 278, 280, 281, 283,
|
||||
284,
|
||||
-1, /* end */
|
||||
};
|
||||
|
||||
const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75 = {
|
||||
.N = 3,
|
||||
.K = 7,
|
||||
.len = 89,
|
||||
.next_output = conv_tch_ahs_4_75_next_output,
|
||||
.next_state = conv_tch_ahs_4_75_next_state,
|
||||
.next_term_output = conv_tch_ahs_4_75_next_term_output,
|
||||
.next_term_state = conv_tch_ahs_4_75_next_term_state,
|
||||
.puncture = conv_tch_ahs_4_75_puncture,
|
||||
};
|
||||
|
||||
26
src/osmo-bts-trx/gsm0503_conv.h
Normal file
26
src/osmo-bts-trx/gsm0503_conv.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _0503_CONV_H
|
||||
#define _0503_CONV_H
|
||||
|
||||
extern const struct osmo_conv_code gsm0503_conv_xcch;
|
||||
extern const struct osmo_conv_code gsm0503_conv_cs2;
|
||||
extern const struct osmo_conv_code gsm0503_conv_cs3;
|
||||
extern const struct osmo_conv_code gsm0503_conv_rach;
|
||||
extern const struct osmo_conv_code gsm0503_conv_sch;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_fr;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_hr;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_12_2;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_10_2;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_95;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_7_4;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_6_7;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_9;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_5_15;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_afs_4_75;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_95;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_7_4;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_6_7;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_9;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_5_15;
|
||||
extern const struct osmo_conv_code gsm0503_conv_tch_ahs_4_75;
|
||||
|
||||
#endif /* _0503_CONV_H */
|
||||
144
src/osmo-bts-trx/gsm0503_interleaving.c
Normal file
144
src/osmo-bts-trx/gsm0503_interleaving.c
Normal file
@@ -0,0 +1,144 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include "gsm0503_tables.h"
|
||||
#include "gsm0503_interleaving.h"
|
||||
|
||||
/*
|
||||
* GSM xCCH interleaving and burst mapping
|
||||
*
|
||||
* Interleaving:
|
||||
*
|
||||
* Given 456 coded input bits, form 4 blocks of 114 bits:
|
||||
*
|
||||
* i(B, j) = c(n, k) k = 0, ..., 455
|
||||
* n = 0, ..., N, N + 1, ...
|
||||
* B = B_0 + 4n + (k mod 4)
|
||||
* j = 2(49k mod 57) + ((k mod 8) div 4)
|
||||
*
|
||||
* Mapping on Burst:
|
||||
*
|
||||
* e(B, j) = i(B, j)
|
||||
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
|
||||
* e(B, 57) = h_l(B)
|
||||
* e(B, 58) = h_n(B)
|
||||
*
|
||||
* Where hl(B) and hn(B) are bits in burst B indicating flags.
|
||||
*/
|
||||
|
||||
void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB)
|
||||
{
|
||||
int j, k, B;
|
||||
|
||||
for (k=0; k<456; k++) {
|
||||
B = k & 3;
|
||||
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
|
||||
cB[k] = iB[B * 114 + j];
|
||||
}
|
||||
}
|
||||
|
||||
void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB)
|
||||
{
|
||||
int j, k, B;
|
||||
|
||||
for (k=0; k<456; k++) {
|
||||
B = k & 3;
|
||||
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
|
||||
iB[B * 114 + j] = cB[k];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* GSM TCH FR/EFR/AFS interleaving and burst mapping
|
||||
*
|
||||
* Interleaving:
|
||||
*
|
||||
* Given 456 coded input bits, form 8 blocks of 114 bits,
|
||||
* where even bits of the first 4 blocks and odd bits of the last 4 blocks
|
||||
* are used:
|
||||
*
|
||||
* i(B, j) = c(n, k) k = 0, ..., 455
|
||||
* n = 0, ..., N, N + 1, ...
|
||||
* B = B_0 + 4n + (k mod 8)
|
||||
* j = 2(49k mod 57) + ((k mod 8) div 4)
|
||||
*
|
||||
* Mapping on Burst:
|
||||
*
|
||||
* e(B, j) = i(B, j)
|
||||
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
|
||||
* e(B, 57) = h_l(B)
|
||||
* e(B, 58) = h_n(B)
|
||||
*
|
||||
* Where hl(B) and hn(B) are bits in burst B indicating flags.
|
||||
*/
|
||||
|
||||
void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB)
|
||||
{
|
||||
int j, k, B;
|
||||
|
||||
for (k=0; k<456; k++) {
|
||||
B = k & 7;
|
||||
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
|
||||
cB[k] = iB[B * 114 + j];
|
||||
}
|
||||
}
|
||||
|
||||
void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB)
|
||||
{
|
||||
int j, k, B;
|
||||
|
||||
for (k=0; k<456; k++) {
|
||||
B = k & 7;
|
||||
j = 2 * ((49 * k) % 57) + ((k & 7) >> 2);
|
||||
iB[B * 114 + j] = cB[k];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* GSM TCH HR/AHS interleaving and burst mapping
|
||||
*
|
||||
* Interleaving:
|
||||
*
|
||||
* Given 288 coded input bits, form 4 blocks of 114 bits,
|
||||
* where even bits of the first 2 blocks and odd bits of the last 2 blocks
|
||||
* are used:
|
||||
*
|
||||
* i(B, j) = c(n, k) k = 0, ..., 227
|
||||
* n = 0, ..., N, N + 1, ...
|
||||
* B = B_0 + 2n + b
|
||||
* j, b = table[k];
|
||||
*
|
||||
* Mapping on Burst:
|
||||
*
|
||||
* e(B, j) = i(B, j)
|
||||
* e(B, 59 + j) = i(B, 57 + j) j = 0, ..., 56
|
||||
* e(B, 57) = h_l(B)
|
||||
* e(B, 58) = h_n(B)
|
||||
*
|
||||
* Where hl(B) and hn(B) are bits in burst B indicating flags.
|
||||
*/
|
||||
|
||||
void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB)
|
||||
{
|
||||
int j, k, B;
|
||||
|
||||
for (k=0; k<228; k++) {
|
||||
B = gsm0503_tch_hr_interleaving[k][1];
|
||||
j = gsm0503_tch_hr_interleaving[k][0];
|
||||
cB[k] = iB[B * 114 + j];
|
||||
}
|
||||
}
|
||||
|
||||
void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB)
|
||||
{
|
||||
int j, k, B;
|
||||
|
||||
for (k=0; k<228; k++) {
|
||||
B = gsm0503_tch_hr_interleaving[k][1];
|
||||
j = gsm0503_tch_hr_interleaving[k][0];
|
||||
iB[B * 114 + j] = cB[k];
|
||||
}
|
||||
}
|
||||
|
||||
11
src/osmo-bts-trx/gsm0503_interleaving.h
Normal file
11
src/osmo-bts-trx/gsm0503_interleaving.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _0503_INTERLEAVING_H
|
||||
#define _0503_INTERLEAVING_H
|
||||
|
||||
void gsm0503_xcch_deinterleave(sbit_t *cB, sbit_t *iB);
|
||||
void gsm0503_xcch_interleave(ubit_t *cB, ubit_t *iB);
|
||||
void gsm0503_tch_fr_deinterleave(sbit_t *cB, sbit_t *iB);
|
||||
void gsm0503_tch_fr_interleave(ubit_t *cB, ubit_t *iB);
|
||||
void gsm0503_tch_hr_deinterleave(sbit_t *cB, sbit_t *iB);
|
||||
void gsm0503_tch_hr_interleave(ubit_t *cB, ubit_t *iB);
|
||||
|
||||
#endif /* _0503_INTERLEAVING_H */
|
||||
72
src/osmo-bts-trx/gsm0503_mapping.c
Normal file
72
src/osmo-bts-trx/gsm0503_mapping.c
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include "gsm0503_mapping.h"
|
||||
|
||||
void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn)
|
||||
{
|
||||
memcpy(iB, eB, 57);
|
||||
memcpy(iB+57, eB+59, 57);
|
||||
|
||||
if (hl)
|
||||
*hl = eB[57];
|
||||
|
||||
if (hn)
|
||||
*hn = eB[58];
|
||||
}
|
||||
|
||||
void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl,
|
||||
const ubit_t *hn)
|
||||
{
|
||||
memcpy(eB, iB, 57);
|
||||
memcpy(eB+59, iB+57, 57);
|
||||
|
||||
if (hl)
|
||||
eB[57] = *hl;
|
||||
if (hn)
|
||||
eB[58] = *hn;
|
||||
}
|
||||
|
||||
void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* brainfuck: only copy even or odd bits */
|
||||
if (iB) {
|
||||
for (i=odd; i<57; i+=2)
|
||||
iB[i] = eB[i];
|
||||
for (i=58-odd; i<114; i+=2)
|
||||
iB[i] = eB[i+2];
|
||||
}
|
||||
|
||||
if (h) {
|
||||
if (!odd)
|
||||
*h = eB[58];
|
||||
else
|
||||
*h = eB[57];
|
||||
}
|
||||
}
|
||||
|
||||
void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* brainfuck: only copy even or odd bits */
|
||||
if (eB) {
|
||||
for (i=odd; i<57; i+=2)
|
||||
eB[i] = iB[i];
|
||||
for (i=58-odd; i<114; i+=2)
|
||||
eB[i+2] = iB[i];
|
||||
}
|
||||
|
||||
if (h) {
|
||||
if (!odd)
|
||||
eB[58] = *h;
|
||||
else
|
||||
eB[57] = *h;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/osmo-bts-trx/gsm0503_mapping.h
Normal file
10
src/osmo-bts-trx/gsm0503_mapping.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _0503_MAPPING_H
|
||||
#define _0503_MAPPING_H
|
||||
|
||||
void gsm0503_xcch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *hl, sbit_t *hn);
|
||||
void gsm0503_xcch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *hl,
|
||||
const ubit_t *hn);
|
||||
void gsm0503_tch_burst_unmap(sbit_t *iB, sbit_t *eB, sbit_t *h, int odd);
|
||||
void gsm0503_tch_burst_map(ubit_t *iB, ubit_t *eB, const ubit_t *h, int odd);
|
||||
|
||||
#endif /* _0503_INTERLEAVING_H */
|
||||
103
src/osmo-bts-trx/gsm0503_parity.c
Normal file
103
src/osmo-bts-trx/gsm0503_parity.c
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/crcgen.h>
|
||||
|
||||
#include "gsm0503_parity.h"
|
||||
|
||||
/*
|
||||
* GSM (SACCH) parity (FIRE code)
|
||||
*
|
||||
* g(x) = (x^23 + 1)(x^17 + x^3 + 1)
|
||||
* = x^40 + x^26 + x^23 + x^17 + x^3 + a1
|
||||
*/
|
||||
|
||||
const struct osmo_crc64gen_code gsm0503_fire_crc40 = {
|
||||
.bits = 40,
|
||||
.poly = 0x0004820009ULL,
|
||||
.init = 0x0000000000ULL,
|
||||
.remainder = 0xffffffffffULL,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* GSM PDTCH CS-2, CS-3, CS-4 parity
|
||||
*
|
||||
* g(x) = x^16 + x^12 + x^5 + 1
|
||||
*/
|
||||
|
||||
const struct osmo_crc16gen_code gsm0503_cs234_crc16 = {
|
||||
.bits = 16,
|
||||
.poly = 0x1021,
|
||||
.init = 0x0000,
|
||||
.remainder = 0xffff,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* GSM RACH parity
|
||||
*
|
||||
* g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1
|
||||
*/
|
||||
|
||||
const struct osmo_crc8gen_code gsm0503_rach_crc6 = {
|
||||
.bits = 6,
|
||||
.poly = 0x2f,
|
||||
.init = 0x00,
|
||||
.remainder = 0x3f,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* GSM SCH parity
|
||||
*
|
||||
* g(x) = x^10 + x^8 + x^6 + x^5 + x^4 + x^2 + 1
|
||||
*/
|
||||
|
||||
const struct osmo_crc16gen_code gsm0503_sch_crc10 = {
|
||||
.bits = 10,
|
||||
.poly = 0x175,
|
||||
.init = 0x000,
|
||||
.remainder = 0x3ff,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* GSM TCH FR/HR/EFR parity
|
||||
*
|
||||
* g(x) = x^3 + x + 1
|
||||
*/
|
||||
|
||||
const struct osmo_crc8gen_code gsm0503_tch_fr_crc3 = {
|
||||
.bits = 3,
|
||||
.poly = 0x3,
|
||||
.init = 0x0,
|
||||
.remainder = 0x7,
|
||||
};
|
||||
|
||||
/*
|
||||
* GSM TCH EFR parity
|
||||
*
|
||||
* g(x) = x^8 + x^4 + x^3 + x^2 + 1
|
||||
*/
|
||||
|
||||
const struct osmo_crc8gen_code gsm0503_tch_efr_crc8 = {
|
||||
.bits = 8,
|
||||
.poly = 0x1d,
|
||||
.init = 0x00,
|
||||
.remainder = 0x00,
|
||||
};
|
||||
|
||||
/*
|
||||
* GSM AMR parity
|
||||
*
|
||||
* g(x) = x^6 + x^5 + x^3 + x^2 + x^1 + 1
|
||||
*/
|
||||
|
||||
const struct osmo_crc8gen_code gsm0503_amr_crc6 = {
|
||||
.bits = 6,
|
||||
.poly = 0x2f,
|
||||
.init = 0x00,
|
||||
.remainder = 0x3f,
|
||||
};
|
||||
|
||||
12
src/osmo-bts-trx/gsm0503_parity.h
Normal file
12
src/osmo-bts-trx/gsm0503_parity.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _0503_PARITY_H
|
||||
#define _0503_PARITY_H
|
||||
|
||||
const struct osmo_crc64gen_code gsm0503_fire_crc40;
|
||||
const struct osmo_crc16gen_code gsm0503_cs234_crc16;
|
||||
const struct osmo_crc8gen_code gsm0503_rach_crc6;
|
||||
const struct osmo_crc16gen_code gsm0503_sch_crc10;
|
||||
const struct osmo_crc8gen_code gsm0503_tch_fr_crc3;
|
||||
const struct osmo_crc8gen_code gsm0503_tch_efr_crc8;
|
||||
const struct osmo_crc8gen_code gsm0503_amr_crc6;
|
||||
|
||||
#endif /* _0503_PARITY_H */
|
||||
193
src/osmo-bts-trx/gsm0503_tables.c
Normal file
193
src/osmo-bts-trx/gsm0503_tables.c
Normal file
@@ -0,0 +1,193 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include "gsm0503_tables.h"
|
||||
|
||||
const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8] = {
|
||||
{ 1,1, 1,1, 1,1, 1,1 },
|
||||
{ 1,1, 0,0, 1,0, 0,0 },
|
||||
{ 0,0, 1,0, 0,0, 0,1 },
|
||||
{ 0,0, 0,1, 0,1, 1,0 },
|
||||
};
|
||||
|
||||
const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8] = {
|
||||
{ -127,-127, -127,-127, -127,-127, -127,-127 },
|
||||
{ -127,-127, 127, 127, -127, 127, 127, 127 },
|
||||
{ 127, 127, -127, 127, 127, 127, 127,-127 },
|
||||
{ 127, 127, 127,-127, 127,-127, -127, 127 },
|
||||
};
|
||||
|
||||
const ubit_t gsm0503_usf2six[8][6] = {
|
||||
{ 0,0,0, 0,0,0 },
|
||||
{ 1,0,0, 1,0,1 },
|
||||
{ 0,1,0, 1,1,0 },
|
||||
{ 1,1,0, 0,1,1 },
|
||||
{ 0,0,1, 0,1,1 },
|
||||
{ 1,0,1, 1,1,0 },
|
||||
{ 0,1,1, 1,0,1 },
|
||||
{ 1,1,1, 0,0,0 },
|
||||
};
|
||||
|
||||
const ubit_t gsm0503_usf2twelve_ubit[8][12] = {
|
||||
{ 0,0,0, 0,0,0, 0,0,0, 0,0,0 },
|
||||
{ 1,1,0, 1,0,0, 0,0,1, 0,1,1 },
|
||||
{ 0,0,1, 1,0,1, 1,1,0, 1,1,0 },
|
||||
{ 1,1,1, 0,0,1, 1,1,1, 1,0,1 },
|
||||
{ 0,0,0, 0,1,1, 0,1,1, 1,0,1 },
|
||||
{ 1,1,0, 1,1,1, 0,1,0, 1,1,0 },
|
||||
{ 0,0,1, 1,1,0, 1,0,1, 0,1,1 },
|
||||
{ 1,1,1, 0,1,0, 1,0,0, 0,0,0 },
|
||||
};
|
||||
|
||||
const sbit_t gsm0503_usf2twelve_sbit[8][12] = {
|
||||
{ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127 },
|
||||
{ -127,-127, 127, -127, 127, 127, 127, 127,-127, 127,-127,-127 },
|
||||
{ 127, 127,-127, -127, 127,-127, -127,-127, 127, -127,-127, 127 },
|
||||
{ -127,-127,-127, 127, 127,-127, -127,-127,-127, -127, 127,-127 },
|
||||
{ 127, 127, 127, 127,-127,-127, 127,-127,-127, -127, 127,-127 },
|
||||
{ -127,-127, 127, -127,-127,-127, 127,-127, 127, -127,-127, 127 },
|
||||
{ 127, 127,-127, -127,-127, 127, -127, 127,-127, 127,-127,-127 },
|
||||
{ -127,-127,-127, 127,-127, 127, -127, 127, 127, 127, 127, 127 },
|
||||
};
|
||||
|
||||
const uint8_t gsm0503_puncture_cs2[588] = {
|
||||
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0, 0,0,0,1, 0,0,0,1,
|
||||
0,0,0,1, 0,0,0,1, 0,0,0,1
|
||||
};
|
||||
|
||||
const uint8_t gsm0503_puncture_cs3[676] = {
|
||||
0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,1,0,1,
|
||||
0,0,0,1,0,1, 0,0,0,1,0,1, 0,0,0,0
|
||||
};
|
||||
|
||||
/* this corresponds to the bit-lengths of the individual codec
|
||||
* parameters as indicated in Table 1.1 of TS 06.10 */
|
||||
const uint8_t gsm0503_gsm_fr_map[76] = {
|
||||
6, 6, 5, 5, 4, 4, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 2, 2, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3
|
||||
};
|
||||
|
||||
/* this table describes the 65 most importaint bits from EFR coded
|
||||
* bits as indicated in TS 05.03 (3.1.1.1) */
|
||||
const uint8_t gsm0503_gsm_efr_protected_bits[65] = {
|
||||
39, 40, 41, 42, 43, 44, 48, 87, 45, 2,
|
||||
3, 8, 10, 18, 19, 24, 46, 47,142,143,
|
||||
144,145,146,147, 92, 93,195,196, 98,137,
|
||||
148, 94,197,149,150, 95,198, 4, 5, 11,
|
||||
12, 16, 9, 6, 7, 13, 17, 20, 96,199,
|
||||
1, 14, 15, 21, 25, 26, 28,151,201,190,
|
||||
240, 88,138,191,241
|
||||
};
|
||||
|
||||
/* Encoded in-band data for speech frames */
|
||||
const ubit_t gsm0503_afs_ic_ubit[4][8] = {
|
||||
{ 0,0,0,0,0,0,0,0 },
|
||||
{ 0,1,0,1,1,1,0,1 },
|
||||
{ 1,0,1,1,1,0,1,0 },
|
||||
{ 1,1,1,0,0,1,1,1 },
|
||||
};
|
||||
|
||||
const sbit_t gsm0503_afs_ic_sbit[4][8] = {
|
||||
{ 127, 127, 127, 127, 127, 127, 127, 127 },
|
||||
{ 127,-127, 127,-127,-127,-127, 127,-127 },
|
||||
{ -127, 127,-127,-127,-127, 127,-127, 127 },
|
||||
{ -127,-127,-127, 127, 127,-127,-127,-127 },
|
||||
};
|
||||
|
||||
const ubit_t gsm0503_ahs_ic_ubit[4][4] = {
|
||||
{ 0,0,0,0 },
|
||||
{ 1,0,0,1 },
|
||||
{ 1,1,1,0 },
|
||||
{ 0,1,1,1 },
|
||||
};
|
||||
|
||||
const sbit_t gsm0503_ahs_ic_sbit[4][4] = {
|
||||
{ 127, 127, 127, 127 },
|
||||
{ -127, 127, 127,-127 },
|
||||
{ -127,-127,-127, 127 },
|
||||
{ 127,-127,-127,-127 },
|
||||
};
|
||||
|
||||
const uint8_t gsm0503_tch_hr_interleaving[228][2] = {
|
||||
{ 0 ,0 }, { 1 ,2 }, { 78 ,1 }, { 79 ,3 }, { 48 ,0 }, { 49 ,2 },
|
||||
{ 54 ,1 }, { 55 ,3 }, { 24 ,0 }, { 25 ,2 }, { 30 ,1 }, { 31 ,3 },
|
||||
{ 72 ,0 }, { 73 ,2 }, { 6 ,1 }, { 7 ,3 }, { 96 ,0 }, { 97 ,2 },
|
||||
{ 12 ,0 }, { 13 ,2 }, { 102,1 }, { 103,3 }, { 60 ,0 }, { 61 ,2 },
|
||||
{ 66 ,1 }, { 67 ,3 }, { 90 ,1 }, { 91 ,3 }, { 36 ,0 }, { 37 ,2 },
|
||||
{ 42 ,1 }, { 43 ,3 }, { 18 ,1 }, { 19 ,3 }, { 84 ,0 }, { 85 ,2 },
|
||||
{ 108,0 }, { 109,2 }, { 2 ,0 }, { 3 ,2 }, { 80 ,1 }, { 81 ,3 },
|
||||
{ 50 ,0 }, { 51 ,2 }, { 56 ,1 }, { 57 ,3 }, { 26 ,0 }, { 27 ,2 },
|
||||
{ 32 ,1 }, { 33 ,3 }, { 74 ,0 }, { 75 ,2 }, { 8 ,1 }, { 9 ,3 },
|
||||
{ 98 ,0 }, { 99 ,2 }, { 14 ,0 }, { 15 ,2 }, { 104,1 }, { 105,3 },
|
||||
{ 62 ,0 }, { 63 ,2 }, { 68 ,1 }, { 69 ,3 }, { 92 ,1 }, { 93 ,3 },
|
||||
{ 38 ,0 }, { 39 ,2 }, { 44 ,1 }, { 45 ,3 }, { 20 ,1 }, { 21 ,3 },
|
||||
{ 86 ,0 }, { 87 ,2 }, { 110,0 }, { 111,2 }, { 4 ,0 }, { 5 ,2 },
|
||||
{ 82 ,1 }, { 83 ,3 }, { 52 ,0 }, { 53 ,2 }, { 58 ,1 }, { 59 ,3 },
|
||||
{ 28 ,0 }, { 29 ,2 }, { 34 ,1 }, { 35 ,3 }, { 76 ,0 }, { 77 ,2 },
|
||||
{ 10 ,1 }, { 12 ,3 }, { 100,0 }, { 101,2 }, { 16 ,0 }, { 17 ,2 },
|
||||
{ 106,1 }, { 107,3 }, { 64 ,0 }, { 65 ,2 }, { 70 ,1 }, { 71 ,3 },
|
||||
{ 94 ,1 }, { 95 ,3 }, { 40 ,0 }, { 41 ,2 }, { 46 ,1 }, { 47 ,3 },
|
||||
{ 22 ,1 }, { 23 ,3 }, { 88 ,0 }, { 89 ,2 }, { 112,0 }, { 113,2 },
|
||||
{ 6 ,0 }, { 7 ,2 }, { 84 ,1 }, { 85 ,3 }, { 54 ,0 }, { 55 ,2 },
|
||||
{ 60 ,1 }, { 61 ,3 }, { 30 ,0 }, { 31 ,2 }, { 36 ,1 }, { 37 ,3 },
|
||||
{ 78 ,0 }, { 79 ,2 }, { 12 ,1 }, { 13 ,3 }, { 102,0 }, { 103,2 },
|
||||
{ 18 ,0 }, { 19 ,2 }, { 108,1 }, { 109,3 }, { 66 ,0 }, { 67 ,2 },
|
||||
{ 72 ,1 }, { 73 ,3 }, { 96 ,1 }, { 97 ,3 }, { 42 ,0 }, { 43 ,2 },
|
||||
{ 48 ,1 }, { 49 ,3 }, { 24 ,1 }, { 25 ,3 }, { 90 ,0 }, { 91 ,2 },
|
||||
{ 0 ,1 }, { 1 ,3 }, { 8 ,0 }, { 9 ,2 }, { 86 ,1 }, { 87 ,3 },
|
||||
{ 56 ,0 }, { 57 ,2 }, { 62 ,1 }, { 63 ,3 }, { 32 ,0 }, { 33 ,2 },
|
||||
{ 38 ,1 }, { 39 ,3 }, { 80 ,0 }, { 81 ,2 }, { 14 ,1 }, { 15 ,3 },
|
||||
{ 104,0 }, { 105,2 }, { 20 ,0 }, { 21 ,2 }, { 110,1 }, { 111,3 },
|
||||
{ 68 ,0 }, { 69 ,2 }, { 74 ,1 }, { 75 ,3 }, { 98 ,1 }, { 99 ,3 },
|
||||
{ 44 ,0 }, { 45 ,2 }, { 50 ,1 }, { 51 ,3 }, { 26 ,1 }, { 27 ,3 },
|
||||
{ 92 ,0 }, { 93 ,2 }, { 2 ,1 }, { 3 ,3 }, { 10 ,0 }, { 11 ,2 },
|
||||
{ 88 ,1 }, { 89 ,3 }, { 58 ,0 }, { 59 ,2 }, { 64 ,1 }, { 65 ,3 },
|
||||
{ 34 ,0 }, { 35 ,2 }, { 40 ,1 }, { 41 ,3 }, { 82 ,0 }, { 83 ,2 },
|
||||
{ 16 ,1 }, { 17 ,3 }, { 106,0 }, { 107,2 }, { 22 ,0 }, { 23 ,2 },
|
||||
{ 112,1 }, { 113,3 }, { 70 ,0 }, { 71 ,2 }, { 76 ,1 }, { 77 ,3 },
|
||||
{ 100,1 }, { 101,3 }, { 46 ,0 }, { 47 ,2 }, { 52 ,1 }, { 53 ,3 },
|
||||
{ 28 ,1 }, { 29 ,3 }, { 94 ,0 }, { 95 ,2 }, { 4 ,1 }, { 5 ,3 },
|
||||
};
|
||||
|
||||
19
src/osmo-bts-trx/gsm0503_tables.h
Normal file
19
src/osmo-bts-trx/gsm0503_tables.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _0503_TABLES_H
|
||||
#define _0503_TABLES_H
|
||||
|
||||
extern const ubit_t gsm0503_pdtch_hl_hn_ubit[4][8];
|
||||
extern const sbit_t gsm0503_pdtch_hl_hn_sbit[4][8];
|
||||
extern const ubit_t gsm0503_usf2six[8][6];
|
||||
extern const ubit_t gsm0503_usf2twelve_ubit[8][12];
|
||||
extern const sbit_t gsm0503_usf2twelve_sbit[8][12];
|
||||
extern const uint8_t gsm0503_puncture_cs2[588];
|
||||
extern const uint8_t gsm0503_puncture_cs3[676];
|
||||
extern const uint8_t gsm0503_gsm_fr_map[76];
|
||||
extern const uint8_t gsm0503_gsm_efr_protected_bits[65];
|
||||
extern const ubit_t gsm0503_afs_ic_ubit[4][8];
|
||||
extern const sbit_t gsm0503_afs_ic_sbit[4][8];
|
||||
extern const ubit_t gsm0503_ahs_ic_ubit[4][4];
|
||||
extern const sbit_t gsm0503_ahs_ic_sbit[4][4];
|
||||
extern const uint8_t gsm0503_tch_hr_interleaving[228][2];
|
||||
|
||||
#endif /* _0503_TABLES_H */
|
||||
716
src/osmo-bts-trx/l1_if.c
Normal file
716
src/osmo-bts-trx/l1_if.c
Normal file
@@ -0,0 +1,716 @@
|
||||
/*
|
||||
* layer 1 primitive handling and interface
|
||||
*
|
||||
* Copyright (C) 2013 Andreas Eversberg <jolly@eversberg.eu>
|
||||
* Copyright (C) 2015 Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||
*
|
||||
* 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/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/oml.h>
|
||||
#include <osmo-bts/rsl.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/amr.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "trx_if.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
|
||||
static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = {
|
||||
[GSM_PCHAN_NONE] = 8,
|
||||
[GSM_PCHAN_CCCH] = 4,
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = 5,
|
||||
[GSM_PCHAN_TCH_F] = 1,
|
||||
[GSM_PCHAN_TCH_H] = 2,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 7,
|
||||
[GSM_PCHAN_PDCH] = 13,
|
||||
//[GSM_PCHAN_TCH_F_PDCH] = FIXME,
|
||||
[GSM_PCHAN_UNKNOWN] = 0,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* create/destroy trx l1 instance
|
||||
*/
|
||||
|
||||
struct trx_l1h *l1if_open(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_l1h *l1h;
|
||||
int rc;
|
||||
|
||||
l1h = talloc_zero(tall_bts_ctx, struct trx_l1h);
|
||||
if (!l1h)
|
||||
return NULL;
|
||||
l1h->trx = trx;
|
||||
trx->role_bts.l1h = l1h;
|
||||
|
||||
trx_sched_init(l1h);
|
||||
|
||||
rc = trx_if_open(l1h);
|
||||
if (rc < 0) {
|
||||
LOGP(DL1C, LOGL_FATAL, "Cannot initialize scheduler\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return l1h;
|
||||
|
||||
err:
|
||||
l1if_close(l1h);
|
||||
trx->role_bts.l1h = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void l1if_close(struct trx_l1h *l1h)
|
||||
{
|
||||
trx_if_close(l1h);
|
||||
trx_sched_exit(l1h);
|
||||
talloc_free(l1h);
|
||||
}
|
||||
|
||||
void l1if_reset(struct trx_l1h *l1h)
|
||||
{
|
||||
}
|
||||
|
||||
static void check_transceiver_availability_trx(struct trx_l1h *l1h, int avail)
|
||||
{
|
||||
struct gsm_bts_trx *trx = l1h->trx;
|
||||
uint8_t tn;
|
||||
|
||||
/* HACK, we should change state when we receive first clock from
|
||||
* transceiver */
|
||||
if (avail) {
|
||||
/* signal availability */
|
||||
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OK);
|
||||
oml_mo_tx_sw_act_rep(&trx->mo);
|
||||
oml_mo_state_chg(&trx->bb_transc.mo, -1, NM_AVSTATE_OK);
|
||||
oml_mo_tx_sw_act_rep(&trx->bb_transc.mo);
|
||||
|
||||
for (tn = 0; tn < 8; tn++)
|
||||
oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
|
||||
(l1h->config.slotmask & (1 << tn)) ?
|
||||
NM_AVSTATE_DEPENDENCY :
|
||||
NM_AVSTATE_NOT_INSTALLED);
|
||||
} else {
|
||||
oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED,
|
||||
NM_AVSTATE_OFF_LINE);
|
||||
oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED,
|
||||
NM_AVSTATE_OFF_LINE);
|
||||
|
||||
for (tn = 0; tn < 8; tn++)
|
||||
oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
|
||||
NM_AVSTATE_OFF_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
int check_transceiver_availability(struct gsm_bts *bts, int avail)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct trx_l1h *l1h;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
l1h = trx_l1h_hdl(trx);
|
||||
check_transceiver_availability_trx(l1h, avail);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* transceiver provisioning
|
||||
*/
|
||||
int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
|
||||
{
|
||||
uint8_t tn;
|
||||
|
||||
if (!transceiver_available)
|
||||
return -EIO;
|
||||
|
||||
if (l1h->config.poweron
|
||||
&& l1h->config.tsc_valid
|
||||
&& l1h->config.bsic_valid
|
||||
&& l1h->config.arfcn_valid) {
|
||||
/* before power on */
|
||||
if (l1h->config.arfcn_valid && !l1h->config.arfcn_sent) {
|
||||
trx_if_cmd_rxtune(l1h, l1h->config.arfcn);
|
||||
trx_if_cmd_txtune(l1h, l1h->config.arfcn);
|
||||
l1h->config.arfcn_sent = 1;
|
||||
}
|
||||
if (l1h->config.tsc_valid && !l1h->config.tsc_sent) {
|
||||
trx_if_cmd_settsc(l1h, l1h->config.tsc);
|
||||
l1h->config.tsc_sent = 1;
|
||||
}
|
||||
if (l1h->config.bsic_valid && !l1h->config.bsic_sent) {
|
||||
trx_if_cmd_setbsic(l1h, l1h->config.bsic);
|
||||
l1h->config.bsic_sent = 1;
|
||||
}
|
||||
|
||||
if (!l1h->config.poweron_sent) {
|
||||
trx_if_cmd_poweron(l1h);
|
||||
l1h->config.poweron_sent = 1;
|
||||
}
|
||||
|
||||
/* after power on */
|
||||
if (l1h->config.rxgain_valid && !l1h->config.rxgain_sent) {
|
||||
trx_if_cmd_setrxgain(l1h, l1h->config.rxgain);
|
||||
l1h->config.rxgain_sent = 1;
|
||||
}
|
||||
if (l1h->config.power_valid && !l1h->config.power_sent) {
|
||||
trx_if_cmd_setpower(l1h, l1h->config.power);
|
||||
l1h->config.power_sent = 1;
|
||||
}
|
||||
if (l1h->config.maxdly_valid && !l1h->config.maxdly_sent) {
|
||||
trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly);
|
||||
l1h->config.maxdly_sent = 1;
|
||||
}
|
||||
for (tn = 0; tn < 8; tn++) {
|
||||
if (l1h->config.slottype_valid[tn]
|
||||
&& !l1h->config.slottype_sent[tn]) {
|
||||
trx_if_cmd_setslot(l1h, tn,
|
||||
l1h->config.slottype[tn]);
|
||||
l1h->config.slottype_sent[tn] = 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!l1h->config.poweron && !l1h->config.poweron_sent) {
|
||||
trx_if_cmd_poweroff(l1h);
|
||||
l1h->config.poweron_sent = 1;
|
||||
l1h->config.rxgain_sent = 0;
|
||||
l1h->config.power_sent = 0;
|
||||
l1h->config.maxdly_sent = 0;
|
||||
for (tn = 0; tn < 8; tn++)
|
||||
l1h->config.slottype_sent[tn] = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int l1if_provision_transceiver(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct trx_l1h *l1h;
|
||||
uint8_t tn;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
l1h = trx_l1h_hdl(trx);
|
||||
l1h->config.arfcn_sent = 0;
|
||||
l1h->config.tsc_sent = 0;
|
||||
l1h->config.bsic_sent = 0;
|
||||
l1h->config.poweron_sent = 0;
|
||||
l1h->config.rxgain_sent = 0;
|
||||
l1h->config.power_sent = 0;
|
||||
l1h->config.maxdly_sent = 0;
|
||||
for (tn = 0; tn < 8; tn++)
|
||||
l1h->config.slottype_sent[tn] = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* activation/configuration/deactivation of transceiver's TRX
|
||||
*/
|
||||
|
||||
/* initialize the layer1 */
|
||||
static int trx_init(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
/* power on transceiver, if not already */
|
||||
if (!l1h->config.poweron) {
|
||||
l1h->config.poweron = 1;
|
||||
l1h->config.poweron_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
|
||||
if (trx == trx->bts->c0)
|
||||
lchan_init_lapdm(&trx->ts[0].lchan[4]);
|
||||
|
||||
/* Set to Operational State: Enabled */
|
||||
oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
|
||||
|
||||
/* Send OPSTART ack */
|
||||
return oml_mo_opstart_ack(&trx->mo);
|
||||
}
|
||||
|
||||
/* deactivate transceiver */
|
||||
int bts_model_trx_close(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
enum gsm_phys_chan_config pchan = trx->ts[0].pchan;
|
||||
|
||||
/* close all logical channels and reset timeslots */
|
||||
trx_sched_reset(l1h);
|
||||
|
||||
/* deactivate lchan for CCCH */
|
||||
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
|
||||
lchan_set_state(&trx->ts[0].lchan[4], LCHAN_S_INACTIVE);
|
||||
}
|
||||
|
||||
/* power off transceiver, if not already */
|
||||
if (l1h->config.poweron) {
|
||||
l1h->config.poweron = 0;
|
||||
l1h->config.poweron_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
|
||||
/* Set to Operational State: Disabled */
|
||||
check_transceiver_availability_trx(l1h, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* on RSL failure, deactivate transceiver */
|
||||
void bts_model_abis_close(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list)
|
||||
bts_model_trx_close(trx);
|
||||
}
|
||||
|
||||
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
|
||||
{
|
||||
/* we always implement the power control loop in osmo-bts software, as
|
||||
* there is no automatism in the underlying osmo-trx */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set bts attributes */
|
||||
static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct trx_l1h *l1h;
|
||||
uint8_t bsic = bts->bsic;
|
||||
struct gsm_bts_role_bts *btsb = bts_role_bts(bts);
|
||||
|
||||
if (TLVP_PRESENT(new_attr, NM_ATT_CONN_FAIL_CRIT)) {
|
||||
const uint8_t *val = TLVP_VAL(new_attr, NM_ATT_CONN_FAIL_CRIT);
|
||||
btsb->radio_link_timeout = val[1];
|
||||
}
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
l1h = trx_l1h_hdl(trx);
|
||||
if (l1h->config.bsic != bsic || !l1h->config.bsic_valid) {
|
||||
l1h->config.bsic = bsic;
|
||||
l1h->config.bsic_valid = 1;
|
||||
l1h->config.bsic_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
}
|
||||
check_transceiver_availability(bts, transceiver_available);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set trx attributes */
|
||||
static uint8_t trx_set_trx(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
uint16_t arfcn = trx->arfcn;
|
||||
|
||||
if (l1h->config.arfcn != arfcn || !l1h->config.arfcn_valid) {
|
||||
l1h->config.arfcn = arfcn;
|
||||
l1h->config.arfcn_valid = 1;
|
||||
l1h->config.arfcn_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
|
||||
if (l1h->config.power_oml) {
|
||||
l1h->config.power = trx->max_power_red;
|
||||
l1h->config.power_valid = 1;
|
||||
l1h->config.power_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set ts attributes */
|
||||
static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts)
|
||||
{
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(ts->trx);
|
||||
uint8_t tn = ts->nr;
|
||||
uint16_t tsc = ts->tsc;
|
||||
enum gsm_phys_chan_config pchan = ts->pchan;
|
||||
uint8_t slottype;
|
||||
int rc;
|
||||
|
||||
/* all TSC of all timeslots must be equal, because transceiver only
|
||||
* supports one TSC per TRX */
|
||||
|
||||
if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) {
|
||||
l1h->config.tsc = tsc;
|
||||
l1h->config.tsc_valid = 1;
|
||||
l1h->config.tsc_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
|
||||
/* set physical channel */
|
||||
rc = trx_sched_set_pchan(l1h, tn, pchan);
|
||||
if (rc)
|
||||
return NM_NACK_RES_NOTAVAIL;
|
||||
|
||||
/* activate lchan for CCCH */
|
||||
if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
|
||||
ts->lchan[4].rel_act_kind = LCHAN_REL_ACT_OML;
|
||||
lchan_set_state(&ts->lchan[4], LCHAN_S_ACTIVE);
|
||||
}
|
||||
|
||||
slottype = transceiver_chan_types[pchan];
|
||||
|
||||
if (l1h->config.slottype[tn] != slottype
|
||||
|| !l1h->config.slottype_valid[tn]) {
|
||||
l1h->config.slottype[tn] = slottype;
|
||||
l1h->config.slottype_valid[tn] = 1;
|
||||
l1h->config.slottype_sent[tn] = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* primitive handling
|
||||
*/
|
||||
|
||||
/* enable ciphering */
|
||||
static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan,
|
||||
uint8_t chan_nr, int downlink)
|
||||
{
|
||||
/* ciphering already enabled in both directions */
|
||||
if (lchan->ciph_state == LCHAN_CIPH_RXTX_CONF)
|
||||
return -EINVAL;
|
||||
|
||||
if (!downlink) {
|
||||
/* set uplink */
|
||||
trx_sched_set_cipher(l1h, chan_nr, 0, lchan->encr.alg_id - 1,
|
||||
lchan->encr.key, lchan->encr.key_len);
|
||||
lchan->ciph_state = LCHAN_CIPH_RX_CONF;
|
||||
} else {
|
||||
/* set downlink and also set uplink, if not already */
|
||||
if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) {
|
||||
trx_sched_set_cipher(l1h, chan_nr, 0,
|
||||
lchan->encr.alg_id - 1, lchan->encr.key,
|
||||
lchan->encr.key_len);
|
||||
}
|
||||
trx_sched_set_cipher(l1h, chan_nr, 1, lchan->encr.alg_id - 1,
|
||||
lchan->encr.key, lchan->encr.key_len);
|
||||
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mph_info_chan_confirm(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
enum osmo_mph_info_type type, uint8_t cause)
|
||||
{
|
||||
struct osmo_phsap_prim l1sap;
|
||||
|
||||
memset(&l1sap, 0, sizeof(l1sap));
|
||||
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM,
|
||||
NULL);
|
||||
l1sap.u.info.type = type;
|
||||
l1sap.u.info.u.act_cnf.chan_nr = chan_nr;
|
||||
l1sap.u.info.u.act_cnf.cause = cause;
|
||||
|
||||
return l1sap_up(l1h->trx, &l1sap);
|
||||
}
|
||||
|
||||
int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn)
|
||||
{
|
||||
struct osmo_phsap_prim l1sap;
|
||||
|
||||
memset(&l1sap, 0, sizeof(l1sap));
|
||||
osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO,
|
||||
PRIM_OP_INDICATION, NULL);
|
||||
l1sap.u.info.type = PRIM_INFO_TIME;
|
||||
l1sap.u.info.u.time_ind.fn = fn;
|
||||
|
||||
if (!bts->c0)
|
||||
return -EINVAL;
|
||||
|
||||
return l1sap_up(bts->c0, &l1sap);
|
||||
}
|
||||
|
||||
|
||||
void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta,
|
||||
float ber, float rssi)
|
||||
{
|
||||
memset(l1sap, 0, sizeof(*l1sap));
|
||||
osmo_prim_init(&l1sap->oph, SAP_GSM_PH, PRIM_MPH_INFO,
|
||||
PRIM_OP_INDICATION, NULL);
|
||||
l1sap->u.info.type = PRIM_INFO_MEAS;
|
||||
l1sap->u.info.u.meas_ind.chan_nr = chan_nr;
|
||||
l1sap->u.info.u.meas_ind.ta_offs_qbits = (int16_t)(ta*4);
|
||||
l1sap->u.info.u.meas_ind.ber10k = (unsigned int) (ber * 10000);
|
||||
l1sap->u.info.u.meas_ind.inv_rssi = (uint8_t) (rssi * -1);
|
||||
}
|
||||
|
||||
int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
|
||||
int n_errors, int n_bits_total, float rssi, float toa)
|
||||
{
|
||||
struct gsm_lchan *lchan = &trx->ts[tn].lchan[l1sap_chan2ss(chan_nr)];
|
||||
struct osmo_phsap_prim l1sap;
|
||||
/* 100% BER is n_bits_total is 0 */
|
||||
float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total;
|
||||
|
||||
LOGP(DMEAS, LOGL_DEBUG, "RX L1 frame %s fn=%u chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
|
||||
"ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
|
||||
gsm_lchan_name(lchan), fn, chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
|
||||
rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info[1], lchan->rqd_ta, toa);
|
||||
|
||||
l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi);
|
||||
|
||||
return l1sap_up(trx, &l1sap);
|
||||
}
|
||||
|
||||
|
||||
/* primitive from common part */
|
||||
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
||||
{
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
struct msgb *msg = l1sap->oph.msg;
|
||||
uint8_t chan_nr;
|
||||
uint8_t tn, ss;
|
||||
int rc = 0;
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
|
||||
case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST):
|
||||
if (!msg)
|
||||
break;
|
||||
/* put data into scheduler's queue */
|
||||
return trx_sched_ph_data_req(l1h, l1sap);
|
||||
case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
|
||||
if (!msg)
|
||||
break;
|
||||
/* put data into scheduler's queue */
|
||||
return trx_sched_tch_req(l1h, l1sap);
|
||||
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
|
||||
switch (l1sap->u.info.type) {
|
||||
case PRIM_INFO_ACT_CIPH:
|
||||
chan_nr = l1sap->u.info.u.ciph_req.chan_nr;
|
||||
tn = L1SAP_CHAN2TS(chan_nr);
|
||||
ss = l1sap_chan2ss(chan_nr);
|
||||
lchan = &trx->ts[tn].lchan[ss];
|
||||
if (l1sap->u.info.u.ciph_req.uplink)
|
||||
l1if_set_ciphering(l1h, lchan, chan_nr, 0);
|
||||
if (l1sap->u.info.u.ciph_req.downlink)
|
||||
l1if_set_ciphering(l1h, lchan, chan_nr, 1);
|
||||
break;
|
||||
case PRIM_INFO_ACTIVATE:
|
||||
case PRIM_INFO_DEACTIVATE:
|
||||
case PRIM_INFO_MODIFY:
|
||||
chan_nr = l1sap->u.info.u.act_req.chan_nr;
|
||||
tn = L1SAP_CHAN2TS(chan_nr);
|
||||
ss = l1sap_chan2ss(chan_nr);
|
||||
lchan = &trx->ts[tn].lchan[ss];
|
||||
if (l1sap->u.info.type == PRIM_INFO_ACTIVATE) {
|
||||
if ((chan_nr & 0x80)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Cannot activate"
|
||||
" chan_nr 0x%02x\n", chan_nr);
|
||||
break;
|
||||
}
|
||||
/* activate dedicated channel */
|
||||
trx_sched_set_lchan(l1h, chan_nr, 0x00, 1);
|
||||
/* activate associated channel */
|
||||
trx_sched_set_lchan(l1h, chan_nr, 0x40, 1);
|
||||
/* set mode */
|
||||
trx_sched_set_mode(l1h, chan_nr,
|
||||
lchan->rsl_cmode, lchan->tch_mode,
|
||||
lchan->tch.amr_mr.num_modes,
|
||||
lchan->tch.amr_mr.mode[0].mode,
|
||||
lchan->tch.amr_mr.mode[1].mode,
|
||||
lchan->tch.amr_mr.mode[2].mode,
|
||||
lchan->tch.amr_mr.mode[3].mode,
|
||||
amr_get_initial_mode(lchan),
|
||||
(lchan->ho.active == 1));
|
||||
/* init lapdm */
|
||||
lchan_init_lapdm(lchan);
|
||||
/* set lchan active */
|
||||
lchan_set_state(lchan, LCHAN_S_ACTIVE);
|
||||
/* set initial ciphering */
|
||||
l1if_set_ciphering(l1h, lchan, chan_nr, 0);
|
||||
l1if_set_ciphering(l1h, lchan, chan_nr, 1);
|
||||
if (lchan->encr.alg_id)
|
||||
lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
|
||||
else
|
||||
lchan->ciph_state = LCHAN_CIPH_NONE;
|
||||
|
||||
/* confirm */
|
||||
mph_info_chan_confirm(l1h, chan_nr,
|
||||
PRIM_INFO_ACTIVATE, 0);
|
||||
break;
|
||||
}
|
||||
if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
|
||||
/* change mode */
|
||||
trx_sched_set_mode(l1h, chan_nr,
|
||||
lchan->rsl_cmode, lchan->tch_mode,
|
||||
lchan->tch.amr_mr.num_modes,
|
||||
lchan->tch.amr_mr.mode[0].mode,
|
||||
lchan->tch.amr_mr.mode[1].mode,
|
||||
lchan->tch.amr_mr.mode[2].mode,
|
||||
lchan->tch.amr_mr.mode[3].mode,
|
||||
amr_get_initial_mode(lchan),
|
||||
0);
|
||||
break;
|
||||
}
|
||||
if ((chan_nr & 0x80)) {
|
||||
LOGP(DL1C, LOGL_ERROR, "Cannot deactivate "
|
||||
"chan_nr 0x%02x\n", chan_nr);
|
||||
break;
|
||||
}
|
||||
/* deactivate associated channel */
|
||||
trx_sched_set_lchan(l1h, chan_nr, 0x40, 0);
|
||||
if (!l1sap->u.info.u.act_req.sacch_only) {
|
||||
/* set lchan inactive */
|
||||
lchan_set_state(lchan, LCHAN_S_NONE);
|
||||
/* deactivate dedicated channel */
|
||||
trx_sched_set_lchan(l1h, chan_nr, 0x00, 0);
|
||||
/* confirm only on dedicated channel */
|
||||
mph_info_chan_confirm(l1h, chan_nr,
|
||||
PRIM_INFO_DEACTIVATE, 0);
|
||||
lchan->ciph_state = 0; /* FIXME: do this in common/\*.c */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
|
||||
l1sap->u.info.type);
|
||||
rc = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
|
||||
l1sap->oph.primitive, l1sap->oph.operation);
|
||||
rc = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (msg)
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* oml handling
|
||||
*/
|
||||
|
||||
/* callback from OML */
|
||||
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
|
||||
struct tlv_parsed *old_attr, struct tlv_parsed *new_attr,
|
||||
void *obj)
|
||||
{
|
||||
/* FIXME: check if the attributes are valid */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback from OML */
|
||||
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
|
||||
struct tlv_parsed *new_attr, int kind, void *obj)
|
||||
{
|
||||
struct abis_om_fom_hdr *foh = msgb_l3(msg);
|
||||
int cause = 0;
|
||||
|
||||
switch (foh->msg_type) {
|
||||
case NM_MT_SET_BTS_ATTR:
|
||||
cause = trx_set_bts(obj, new_attr);
|
||||
break;
|
||||
case NM_MT_SET_RADIO_ATTR:
|
||||
cause = trx_set_trx(obj);
|
||||
break;
|
||||
case NM_MT_SET_CHAN_ATTR:
|
||||
cause = trx_set_ts(obj);
|
||||
break;
|
||||
}
|
||||
|
||||
return oml_fom_ack_nack(msg, cause);
|
||||
}
|
||||
|
||||
/* callback from OML */
|
||||
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
void *obj)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (mo->obj_class) {
|
||||
case NM_OC_RADIO_CARRIER:
|
||||
/* activate transceiver */
|
||||
rc = trx_init(obj);
|
||||
break;
|
||||
case NM_OC_CHANNEL:
|
||||
/* configure timeslot */
|
||||
rc = 0; //ts_connect(obj);
|
||||
|
||||
/* Set to Operational State: Enabled */
|
||||
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
|
||||
|
||||
/* Send OPSTART ack */
|
||||
rc = oml_mo_opstart_ack(mo);
|
||||
|
||||
break;
|
||||
case NM_OC_BTS:
|
||||
case NM_OC_SITE_MANAGER:
|
||||
case NM_OC_BASEB_TRANSC:
|
||||
case NM_OC_GPRS_NSE:
|
||||
case NM_OC_GPRS_CELL:
|
||||
case NM_OC_GPRS_NSVC:
|
||||
oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
|
||||
rc = oml_mo_opstart_ack(mo);
|
||||
break;
|
||||
default:
|
||||
rc = oml_mo_opstart_nack(mo, NM_NACK_OBJCLASS_NOTSUPP);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo,
|
||||
void *obj, uint8_t adm_state)
|
||||
{
|
||||
/* blindly accept all state changes */
|
||||
mo->nm_state.administrative = adm_state;
|
||||
return oml_mo_statechg_ack(mo);
|
||||
}
|
||||
|
||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_model_oml_estab(struct gsm_bts *bts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
179
src/osmo-bts-trx/l1_if.h
Normal file
179
src/osmo-bts-trx/l1_if.h
Normal file
@@ -0,0 +1,179 @@
|
||||
#ifndef L1_IF_H_TRX
|
||||
#define L1_IF_H_TRX
|
||||
|
||||
/* These types define the different channels on a multiframe.
|
||||
* Each channel has queues and can be activated individually.
|
||||
*/
|
||||
enum trx_chan_type {
|
||||
TRXC_IDLE = 0,
|
||||
TRXC_FCCH,
|
||||
TRXC_SCH,
|
||||
TRXC_BCCH,
|
||||
TRXC_RACH,
|
||||
TRXC_CCCH,
|
||||
TRXC_TCHF,
|
||||
TRXC_TCHH_0,
|
||||
TRXC_TCHH_1,
|
||||
TRXC_SDCCH4_0,
|
||||
TRXC_SDCCH4_1,
|
||||
TRXC_SDCCH4_2,
|
||||
TRXC_SDCCH4_3,
|
||||
TRXC_SDCCH8_0,
|
||||
TRXC_SDCCH8_1,
|
||||
TRXC_SDCCH8_2,
|
||||
TRXC_SDCCH8_3,
|
||||
TRXC_SDCCH8_4,
|
||||
TRXC_SDCCH8_5,
|
||||
TRXC_SDCCH8_6,
|
||||
TRXC_SDCCH8_7,
|
||||
TRXC_SACCHTF,
|
||||
TRXC_SACCHTH_0,
|
||||
TRXC_SACCHTH_1,
|
||||
TRXC_SACCH4_0,
|
||||
TRXC_SACCH4_1,
|
||||
TRXC_SACCH4_2,
|
||||
TRXC_SACCH4_3,
|
||||
TRXC_SACCH8_0,
|
||||
TRXC_SACCH8_1,
|
||||
TRXC_SACCH8_2,
|
||||
TRXC_SACCH8_3,
|
||||
TRXC_SACCH8_4,
|
||||
TRXC_SACCH8_5,
|
||||
TRXC_SACCH8_6,
|
||||
TRXC_SACCH8_7,
|
||||
TRXC_PDTCH,
|
||||
TRXC_PTCCH,
|
||||
_TRX_CHAN_MAX
|
||||
};
|
||||
|
||||
/* States each channel on a multiframe */
|
||||
struct trx_chan_state {
|
||||
/* scheduler */
|
||||
uint8_t active; /* Channel is active */
|
||||
ubit_t *dl_bursts; /* burst buffer for TX */
|
||||
sbit_t *ul_bursts; /* burst buffer for RX */
|
||||
uint32_t ul_first_fn; /* fn of first burst */
|
||||
uint8_t ul_mask; /* mask of received bursts */
|
||||
|
||||
/* RSSI / TOA */
|
||||
uint8_t rssi_num; /* number of RSSI values */
|
||||
float rssi_sum; /* sum of RSSI values */
|
||||
uint8_t toa_num; /* number of TOA values */
|
||||
float toa_sum; /* sum of TOA values */
|
||||
|
||||
/* loss detection */
|
||||
uint8_t lost; /* (SACCH) loss detection */
|
||||
|
||||
/* mode */
|
||||
uint8_t rsl_cmode, tch_mode; /* mode for TCH channels */
|
||||
|
||||
/* AMR */
|
||||
uint8_t codec[4]; /* 4 possible codecs for amr */
|
||||
int codecs; /* number of possible codecs */
|
||||
float ber_sum; /* sum of bit error rates */
|
||||
int ber_num; /* number of bit error rates */
|
||||
uint8_t ul_ft; /* current uplink FT index */
|
||||
uint8_t dl_ft; /* current downlink FT index */
|
||||
uint8_t ul_cmr; /* current uplink CMR index */
|
||||
uint8_t dl_cmr; /* current downlink CMR index */
|
||||
uint8_t amr_loop; /* if AMR loop is enabled */
|
||||
|
||||
/* TCH/H */
|
||||
uint8_t dl_ongoing_facch; /* FACCH/H on downlink */
|
||||
uint8_t ul_ongoing_facch; /* FACCH/H on uplink */
|
||||
|
||||
/* encryption */
|
||||
int ul_encr_algo; /* A5/x encry algo downlink */
|
||||
int dl_encr_algo; /* A5/x encry algo uplink */
|
||||
int ul_encr_key_len;
|
||||
int dl_encr_key_len;
|
||||
uint8_t ul_encr_key[8];
|
||||
uint8_t dl_encr_key[8];
|
||||
|
||||
/* measurements */
|
||||
struct {
|
||||
uint8_t clock; /* cyclic clock counter */
|
||||
int8_t rssi[32]; /* last RSSI values */
|
||||
int rssi_count; /* received RSSI values */
|
||||
int rssi_valid_count; /* number of stored value */
|
||||
int rssi_got_burst; /* any burst received so far */
|
||||
float toa_sum; /* sum of TOA values */
|
||||
int toa_num; /* number of TOA value */
|
||||
} meas;
|
||||
|
||||
/* handover */
|
||||
uint8_t ho_rach_detect; /* if rach detection is on */
|
||||
};
|
||||
|
||||
struct trx_config {
|
||||
uint8_t poweron; /* poweron(1) or poweroff(0) */
|
||||
int poweron_sent;
|
||||
|
||||
int arfcn_valid;
|
||||
uint16_t arfcn;
|
||||
int arfcn_sent;
|
||||
|
||||
int tsc_valid;
|
||||
uint8_t tsc;
|
||||
int tsc_sent;
|
||||
|
||||
int bsic_valid;
|
||||
uint8_t bsic;
|
||||
int bsic_sent;
|
||||
|
||||
int rxgain_valid;
|
||||
int rxgain;
|
||||
int rxgain_sent;
|
||||
|
||||
int power_valid;
|
||||
int power;
|
||||
int power_oml;
|
||||
int power_sent;
|
||||
|
||||
int maxdly_valid;
|
||||
int maxdly;
|
||||
int maxdly_sent;
|
||||
|
||||
uint8_t slotmask;
|
||||
|
||||
int slottype_valid[8];
|
||||
uint8_t slottype[8];
|
||||
int slottype_sent[8];
|
||||
};
|
||||
|
||||
struct trx_l1h {
|
||||
struct llist_head trx_ctrl_list;
|
||||
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
struct osmo_fd trx_ofd_ctrl;
|
||||
struct osmo_timer_list trx_ctrl_timer;
|
||||
struct osmo_fd trx_ofd_data;
|
||||
|
||||
/* transceiver config */
|
||||
struct trx_config config;
|
||||
|
||||
uint8_t mf_index[8]; /* selected multiframe index */
|
||||
uint32_t mf_last_fn[8]; /* last received frame */
|
||||
uint8_t mf_period[8]; /* period of multiframe */
|
||||
struct trx_sched_frame *mf_frames[8]; /* pointer to frame layout */
|
||||
|
||||
/* Channel states for all channels on all timeslots */
|
||||
struct trx_chan_state chan_states[8][_TRX_CHAN_MAX];
|
||||
struct llist_head dl_prims[8]; /* Queue primitves for TX */
|
||||
uint8_t ho_rach_detect[8][8];
|
||||
};
|
||||
|
||||
struct trx_l1h *l1if_open(struct gsm_bts_trx *trx);
|
||||
void l1if_close(struct trx_l1h *l1h);
|
||||
void l1if_reset(struct trx_l1h *l1h);
|
||||
int check_transceiver_availability(struct gsm_bts *bts, int avail);
|
||||
int l1if_provision_transceiver_trx(struct trx_l1h *l1h);
|
||||
int l1if_provision_transceiver(struct gsm_bts *bts);
|
||||
int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn);
|
||||
void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta,
|
||||
float ber, float rssi);
|
||||
int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
|
||||
int n_errors, int n_bits_total, float rssi, float toa);
|
||||
|
||||
#endif /* L1_IF_H_TRX */
|
||||
339
src/osmo-bts-trx/loops.c
Normal file
339
src/osmo-bts-trx/loops.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/* Loop control for OsmoBTS-TRX */
|
||||
|
||||
/* (C) 2013 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 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include "trx_if.h"
|
||||
#include "l1_if.h"
|
||||
#include "loops.h"
|
||||
|
||||
#define MS_PWR_DBM(lvl) ms_pwr_dbm(gsm_arfcn2band(l1h->config.arfcn), lvl)
|
||||
|
||||
/*
|
||||
* MS Power loop
|
||||
*/
|
||||
|
||||
int trx_ms_power_loop = 0;
|
||||
int8_t trx_target_rssi = -10;
|
||||
|
||||
static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
|
||||
uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t diff)
|
||||
{
|
||||
int8_t new_power;
|
||||
|
||||
new_power = lchan->ms_power - (diff >> 1);
|
||||
|
||||
if (diff == 0)
|
||||
return 0;
|
||||
|
||||
if (new_power < 0)
|
||||
new_power = 0;
|
||||
|
||||
// FIXME: to go above 1W, we need to know classmark of MS
|
||||
if (l1h->config.arfcn >= 512 && l1h->config.arfcn <= 885) {
|
||||
if (new_power > 15)
|
||||
new_power = 15;
|
||||
} else {
|
||||
if (new_power > 19)
|
||||
new_power = 19;
|
||||
}
|
||||
|
||||
/* a higher value means a lower level (and vice versa) */
|
||||
if (new_power > lchan->ms_power + MS_LOWER_MAX)
|
||||
new_power = lchan->ms_power + MS_LOWER_MAX;
|
||||
else if (new_power < lchan->ms_power - MS_RAISE_MAX)
|
||||
new_power = lchan->ms_power - MS_RAISE_MAX;
|
||||
|
||||
if (lchan->ms_power == new_power) {
|
||||
LOGP(DLOOP, LOGL_INFO, "Keeping MS new_power of trx=%u "
|
||||
"chan_nr=0x%02x at control level %d (%d dBm)\n",
|
||||
l1h->trx->nr, chan_nr, new_power,
|
||||
MS_PWR_DBM(new_power));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGP(DLOOP, LOGL_INFO, "%s MS new_power of trx=%u chan_nr=0x%02x from "
|
||||
"control level %d (%d dBm) to %d (%d dBm)\n",
|
||||
(diff > 0) ? "Raising" : "Lowering",
|
||||
l1h->trx->nr, chan_nr, lchan->ms_power,
|
||||
MS_PWR_DBM(lchan->ms_power), new_power, MS_PWR_DBM(new_power));
|
||||
|
||||
lchan->ms_power = new_power;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi)
|
||||
{
|
||||
/* ignore inserted dummy frames, treat as lost frames */
|
||||
if (rssi < -127)
|
||||
return 0;
|
||||
|
||||
LOGP(DLOOP, LOGL_DEBUG, "Got RSSI value of %d\n", rssi);
|
||||
|
||||
chan_state->meas.rssi_count++;
|
||||
|
||||
chan_state->meas.rssi_got_burst = 1;
|
||||
|
||||
/* store and process RSSI */
|
||||
if (chan_state->meas.rssi_valid_count
|
||||
== ARRAY_SIZE(chan_state->meas.rssi))
|
||||
return 0;
|
||||
chan_state->meas.rssi[chan_state->meas.rssi_valid_count++] = rssi;
|
||||
chan_state->meas.rssi_valid_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
|
||||
uint8_t chan_nr, struct trx_chan_state *chan_state)
|
||||
{
|
||||
int rssi;
|
||||
int i;
|
||||
|
||||
/* skip every second clock, to prevent oscillating due to roundtrip
|
||||
* delay */
|
||||
if (!(chan_state->meas.clock & 1))
|
||||
return 0;
|
||||
|
||||
LOGP(DLOOP, LOGL_DEBUG, "Got SACCH master clock at RSSI count %d\n",
|
||||
chan_state->meas.rssi_count);
|
||||
|
||||
/* wait for initial burst */
|
||||
if (!chan_state->meas.rssi_got_burst)
|
||||
return 0;
|
||||
|
||||
/* if no burst was received from MS at clock */
|
||||
if (chan_state->meas.rssi_count == 0) {
|
||||
LOGP(DLOOP, LOGL_NOTICE, "LOST SACCH frame of trx=%u "
|
||||
"chan_nr=0x%02x, so we raise MS power\n",
|
||||
l1h->trx->nr, chan_nr);
|
||||
return ms_power_diff(l1h, lchan, chan_nr, chan_state,
|
||||
MS_RAISE_MAX);
|
||||
}
|
||||
|
||||
/* reset total counter */
|
||||
chan_state->meas.rssi_count = 0;
|
||||
|
||||
/* check the minimum level received after MS acknowledged the ordered
|
||||
* power level */
|
||||
if (chan_state->meas.rssi_valid_count == 0)
|
||||
return 0;
|
||||
for (rssi = 999, i = 0; i < chan_state->meas.rssi_valid_count; i++) {
|
||||
if (rssi > chan_state->meas.rssi[i])
|
||||
rssi = chan_state->meas.rssi[i];
|
||||
}
|
||||
|
||||
/* reset valid counter */
|
||||
chan_state->meas.rssi_valid_count = 0;
|
||||
|
||||
/* change RSSI */
|
||||
LOGP(DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current "
|
||||
"MS power: %d (%d dBm) of trx=%u chan_nr=0x%02x\n", rssi,
|
||||
trx_target_rssi, lchan->ms_power, MS_PWR_DBM(lchan->ms_power),
|
||||
l1h->trx->nr, chan_nr);
|
||||
ms_power_diff(l1h, lchan, chan_nr, chan_state, trx_target_rssi - rssi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Timing Advance loop
|
||||
*/
|
||||
|
||||
int trx_ta_loop = 1;
|
||||
|
||||
int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state, float toa)
|
||||
{
|
||||
/* check if the current L1 header acks to the current ordered TA */
|
||||
if (lchan->meas.l1_info[1] != lchan->rqd_ta)
|
||||
return 0;
|
||||
|
||||
/* sum measurement */
|
||||
chan_state->meas.toa_sum += toa;
|
||||
if (++(chan_state->meas.toa_num) < 16)
|
||||
return 0;
|
||||
|
||||
/* complete set */
|
||||
toa = chan_state->meas.toa_sum / chan_state->meas.toa_num;
|
||||
|
||||
/* check for change of TOA */
|
||||
if (toa < -0.9F && lchan->rqd_ta > 0) {
|
||||
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
|
||||
"early (%.2f), now lowering TA from %d to %d\n",
|
||||
l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
|
||||
lchan->rqd_ta - 1);
|
||||
lchan->rqd_ta--;
|
||||
} else if (toa > 0.9F && lchan->rqd_ta < 63) {
|
||||
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
|
||||
"late (%.2f), now raising TA from %d to %d\n",
|
||||
l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
|
||||
lchan->rqd_ta + 1);
|
||||
lchan->rqd_ta++;
|
||||
} else
|
||||
LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is "
|
||||
"correct (%.2f), keeping current TA of %d\n",
|
||||
l1h->trx->nr, chan_nr, toa, lchan->rqd_ta);
|
||||
|
||||
chan_state->meas.toa_num = 0;
|
||||
chan_state->meas.toa_sum = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state, int8_t rssi, float toa)
|
||||
{
|
||||
struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
||||
.lchan[l1sap_chan2ss(chan_nr)];
|
||||
|
||||
if (trx_ms_power_loop)
|
||||
ms_power_val(chan_state, rssi);
|
||||
|
||||
if (trx_ta_loop)
|
||||
ta_val(l1h, lchan, chan_nr, chan_state, toa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state)
|
||||
{
|
||||
struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
||||
.lchan[l1sap_chan2ss(chan_nr)];
|
||||
|
||||
if (trx_ms_power_loop)
|
||||
ms_power_clock(l1h, lchan, chan_nr, chan_state);
|
||||
|
||||
/* count the number of SACCH clocks */
|
||||
chan_state->meas.clock++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state, float ber)
|
||||
{
|
||||
struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
|
||||
.lchan[l1sap_chan2ss(chan_nr)];
|
||||
int c_i;
|
||||
|
||||
/* check if loop is enabled */
|
||||
if (!chan_state->amr_loop)
|
||||
return 0;
|
||||
|
||||
/* wait for MS to use the requested codec */
|
||||
if (chan_state->ul_ft != chan_state->dl_cmr)
|
||||
return 0;
|
||||
|
||||
/* count bit errors */
|
||||
if (L1SAP_IS_CHAN_TCHH(chan_nr)) {
|
||||
chan_state->ber_num += 2;
|
||||
chan_state->ber_sum += (ber + ber);
|
||||
} else {
|
||||
chan_state->ber_num++;
|
||||
chan_state->ber_sum += ber;
|
||||
}
|
||||
|
||||
/* count frames */
|
||||
if (chan_state->ber_num < 48)
|
||||
return 0;
|
||||
|
||||
/* calculate average (reuse ber variable) */
|
||||
ber = chan_state->ber_sum / chan_state->ber_num;
|
||||
|
||||
/* FIXME: calculate C/I from BER */
|
||||
c_i = ber * 100;
|
||||
|
||||
/* reset bit errors */
|
||||
chan_state->ber_num = 0;
|
||||
chan_state->ber_sum = 0;
|
||||
|
||||
LOGP(DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
|
||||
"codec id %d of trx=%u chan_nr=0x%02x\n", ber,
|
||||
chan_state->ul_ft, l1h->trx->nr, chan_nr);
|
||||
|
||||
/* degrade */
|
||||
if (chan_state->dl_cmr > 0) {
|
||||
/* degrade, if ber is above threshold FIXME: C/I */
|
||||
if (ber >
|
||||
lchan->tch.amr_mr.mode[chan_state->dl_cmr-1].threshold_bts) {
|
||||
LOGP(DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
|
||||
"from codec id %d to %d of trx=%u "
|
||||
"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
|
||||
chan_state->dl_cmr - 1, l1h->trx->nr, chan_nr);
|
||||
chan_state->dl_cmr--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* upgrade */
|
||||
if (chan_state->dl_cmr < chan_state->codecs - 1) {
|
||||
/* degrade, if ber is above threshold FIXME: C/I*/
|
||||
if (ber <
|
||||
lchan->tch.amr_mr.mode[chan_state->dl_cmr].threshold_bts
|
||||
- lchan->tch.amr_mr.mode[chan_state->dl_cmr].hysteresis_bts) {
|
||||
LOGP(DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
|
||||
"from codec id %d to %d of trx=%u "
|
||||
"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
|
||||
chan_state->dl_cmr + 1, l1h->trx->nr, chan_nr);
|
||||
chan_state->dl_cmr++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop)
|
||||
{
|
||||
if (chan_state->amr_loop && !loop) {
|
||||
chan_state->amr_loop = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!chan_state->amr_loop && loop) {
|
||||
chan_state->amr_loop = 1;
|
||||
|
||||
/* reset bit errors */
|
||||
chan_state->ber_num = 0;
|
||||
chan_state->ber_sum = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
31
src/osmo-bts-trx/loops.h
Normal file
31
src/osmo-bts-trx/loops.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef _TRX_LOOPS_H
|
||||
#define _TRX_LOOPS_H
|
||||
|
||||
/*
|
||||
* calibration of loops
|
||||
*/
|
||||
|
||||
/* how much power levels do we raise/lower as maximum (1 level = 2 dB) */
|
||||
#define MS_RAISE_MAX 4
|
||||
#define MS_LOWER_MAX 1
|
||||
|
||||
/*
|
||||
* loops api
|
||||
*/
|
||||
|
||||
extern int trx_ms_power_loop;
|
||||
extern int8_t trx_target_rssi;
|
||||
extern int trx_ta_loop;
|
||||
|
||||
int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state, int8_t rssi, float toa);
|
||||
|
||||
int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state);
|
||||
|
||||
int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
|
||||
struct trx_chan_state *chan_state, float ber);
|
||||
|
||||
int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop);
|
||||
|
||||
#endif /* _TRX_LOOPS_H */
|
||||
406
src/osmo-bts-trx/main.c
Normal file
406
src/osmo-bts-trx/main.c
Normal file
@@ -0,0 +1,406 @@
|
||||
/* Main program for OsmoBTS-TRX */
|
||||
|
||||
/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2013 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 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 <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <sched.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "trx_if.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
const int pcu_direct = 0;
|
||||
|
||||
int quit = 0;
|
||||
static const char *config_file = "osmo-bts.cfg";
|
||||
static int daemonize = 0;
|
||||
static char *gsmtap_ip = 0;
|
||||
static int rt_prio = -1;
|
||||
static int trx_num = 1;
|
||||
char *software_version = "0.0";
|
||||
uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 };
|
||||
char *bsc_host = "localhost";
|
||||
char *bts_id = "1801/0";
|
||||
|
||||
// FIXME this is a hack
|
||||
static void get_mac(void)
|
||||
{
|
||||
struct if_nameindex *ifn = if_nameindex();
|
||||
struct ifreq ifr;
|
||||
int sock;
|
||||
int ret;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return;
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
if (!ifn)
|
||||
return;
|
||||
while (ifn->if_name) {
|
||||
strncpy(ifr.ifr_name, ifn->if_name, sizeof(ifr.ifr_name)-1);
|
||||
ret = ioctl(sock, SIOCGIFHWADDR, &ifr);
|
||||
if (ret == 0 && !!memcmp(ifr.ifr_hwaddr.sa_data,
|
||||
"\0\0\0\0\0\0", 6)) {
|
||||
memcpy(abis_mac, ifr.ifr_hwaddr.sa_data, 6);
|
||||
printf("Using MAC address of %s: "
|
||||
"'%02x:%02x:%02x:%02x:%02x:%02x'\n",
|
||||
ifn->if_name,
|
||||
abis_mac[0], abis_mac[1], abis_mac[2],
|
||||
abis_mac[3], abis_mac[4], abis_mac[5]);
|
||||
break;
|
||||
}
|
||||
ifn++;
|
||||
}
|
||||
// if_freenameindex(ifn);
|
||||
}
|
||||
|
||||
int bts_model_init(struct gsm_bts *bts)
|
||||
{
|
||||
void *l1h;
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
l1h = l1if_open(trx);
|
||||
if (!l1h) {
|
||||
LOGP(DL1C, LOGL_FATAL, "Cannot open L1 Interface\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
trx->role_bts.l1h = l1h;
|
||||
trx->nominal_power = 23;
|
||||
|
||||
l1if_reset(l1h);
|
||||
}
|
||||
|
||||
bts_model_vty_init(bts);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
l1h = trx->role_bts.l1h;
|
||||
if (l1h)
|
||||
l1if_close(l1h);
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* dummy, since no direct dsp support */
|
||||
uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf( "Some useful options:\n"
|
||||
" -h --help this text\n"
|
||||
" -d --debug MASK Enable debugging (e.g. -d DRSL:DOML:DLAPDM)\n"
|
||||
" -D --daemonize For the process into a background daemon\n"
|
||||
" -c --config-file Specify the filename of the config file\n"
|
||||
" -s --disable-color Don't use colors in stderr log output\n"
|
||||
" -T --timestamp Prefix every log line with a timestamp\n"
|
||||
" -V --version Print version information and exit\n"
|
||||
" -e --log-level Set a global log-level\n"
|
||||
" -t --trx-num Set number of TRX (default=%d)\n"
|
||||
" -i --gsmtap-ip The destination IP used for GSMTAP.\n"
|
||||
" -r --realtime PRIO Set realtime scheduler with given prio\n"
|
||||
" -I --local-trx-ip Local IP for transceiver to connect (default=%s)\n"
|
||||
,trx_num, transceiver_ip);
|
||||
}
|
||||
|
||||
/* FIXME: finally get some option parsing code into libosmocore */
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_idx = 0, c;
|
||||
static const struct option long_options[] = {
|
||||
/* FIXME: all those are generic Osmocom app options */
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "debug", 1, 0, 'd' },
|
||||
{ "daemonize", 0, 0, 'D' },
|
||||
{ "config-file", 1, 0, 'c' },
|
||||
{ "disable-color", 0, 0, 's' },
|
||||
{ "timestamp", 0, 0, 'T' },
|
||||
{ "version", 0, 0, 'V' },
|
||||
{ "log-level", 1, 0, 'e' },
|
||||
{ "trx-num", 1, 0, 't' },
|
||||
{ "gsmtap-ip", 1, 0, 'i' },
|
||||
{ "realtime", 1, 0, 'r' },
|
||||
{ "local-trx-ip", 1, 0, 'I' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:d:Dc:sTVe:t:i:r:I:",
|
||||
long_options, &option_idx);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 's':
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(osmo_stderr_target, optarg);
|
||||
break;
|
||||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'c':
|
||||
config_file = strdup(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||
break;
|
||||
case 'V':
|
||||
print_version(1);
|
||||
exit(0);
|
||||
break;
|
||||
case 'e':
|
||||
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||
break;
|
||||
case 't':
|
||||
trx_num = atoi(optarg);
|
||||
if (trx_num < 1)
|
||||
trx_num = 1;
|
||||
break;
|
||||
case 'i':
|
||||
gsmtap_ip = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
rt_prio = atoi(optarg);
|
||||
break;
|
||||
case 'I':
|
||||
transceiver_ip = strdup(optarg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct gsm_bts *bts;
|
||||
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
fprintf(stderr, "signal %u received\n", signal);
|
||||
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
//osmo_signal_dispatch(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
|
||||
if (!quit)
|
||||
bts_shutdown(bts, "SIGINT");
|
||||
quit++;
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGUSR1:
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_bts_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int write_pid_file(char *procname)
|
||||
{
|
||||
FILE *outf;
|
||||
char tmp[PATH_MAX+1];
|
||||
|
||||
snprintf(tmp, sizeof(tmp)-1, "/var/run/%s.pid", procname);
|
||||
tmp[PATH_MAX-1] = '\0';
|
||||
|
||||
outf = fopen(tmp, "w");
|
||||
if (!outf)
|
||||
return -1;
|
||||
|
||||
fprintf(outf, "%d\n", getpid());
|
||||
|
||||
fclose(outf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct e1inp_line *line;
|
||||
void *tall_msgb_ctx;
|
||||
int rc, i;
|
||||
|
||||
printf("((*))\n |\n / \\ OsmoBTS\n");
|
||||
|
||||
get_mac();
|
||||
|
||||
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
|
||||
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
|
||||
msgb_set_talloc_ctx(tall_msgb_ctx);
|
||||
|
||||
bts_log_init(NULL);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||
if (!bts) {
|
||||
fprintf(stderr, "Failed to create BTS structure\n");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 1; i < trx_num; i++) {
|
||||
trx = gsm_bts_trx_alloc(bts);
|
||||
if (!trx) {
|
||||
fprintf(stderr, "Failed to TRX structure\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
vty_init(&bts_vty_info);
|
||||
e1inp_vty_init();
|
||||
bts_vty_init(bts, &bts_log_info);
|
||||
|
||||
if (bts_init(bts) < 0) {
|
||||
fprintf(stderr, "unable to to open bts\n");
|
||||
exit(1);
|
||||
}
|
||||
btsb = bts_role_bts(bts);
|
||||
btsb->support.ciphers = CIPHER_A5(1) | CIPHER_A5(2);
|
||||
|
||||
if (gsmtap_ip) {
|
||||
gsmtap = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 1);
|
||||
if (!gsmtap) {
|
||||
fprintf(stderr, "Failed during gsmtap_init()\n");
|
||||
exit(1);
|
||||
}
|
||||
gsmtap_source_add_sink(gsmtap);
|
||||
}
|
||||
|
||||
abis_init(bts);
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n",
|
||||
config_file);
|
||||
exit(1);
|
||||
}
|
||||
if (!settsc_enabled && !setbsic_enabled)
|
||||
settsc_enabled = setbsic_enabled = 1;
|
||||
|
||||
write_pid_file("osmo-bts");
|
||||
|
||||
rc = telnet_init(tall_bts_ctx, NULL, 4241);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error initializing telnet\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (pcu_sock_init()) {
|
||||
fprintf(stderr, "PCU L1 socket failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
//signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
|
||||
if (!btsb->bsc_oml_host) {
|
||||
fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
line = abis_open(bts, btsb->bsc_oml_host, "sysmoBTS");
|
||||
if (!line) {
|
||||
fprintf(stderr, "unable to connect to BSC\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
perror("Error during daemonize");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (rt_prio != -1) {
|
||||
struct sched_param schedp;
|
||||
|
||||
/* high priority scheduling required for handling bursts */
|
||||
memset(&schedp, 0, sizeof(schedp));
|
||||
schedp.sched_priority = rt_prio;
|
||||
rc = sched_setscheduler(0, SCHED_RR, &schedp);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Error setting SCHED_RR with prio %d\n",
|
||||
rt_prio);
|
||||
}
|
||||
}
|
||||
|
||||
while (quit < 2) {
|
||||
log_reset_context();
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
telnet_exit();
|
||||
|
||||
talloc_report_full(tall_bts_ctx, stderr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
3044
src/osmo-bts-trx/scheduler.c
Normal file
3044
src/osmo-bts-trx/scheduler.c
Normal file
File diff suppressed because it is too large
Load Diff
43
src/osmo-bts-trx/scheduler.h
Normal file
43
src/osmo-bts-trx/scheduler.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef TRX_SCHEDULER_H
|
||||
#define TRX_SCHEDULER_H
|
||||
|
||||
extern uint32_t trx_clock_advance;
|
||||
extern uint32_t trx_rts_advance;
|
||||
extern uint32_t transceiver_last_fn;
|
||||
|
||||
|
||||
int trx_sched_init(struct trx_l1h *l1h);
|
||||
|
||||
void trx_sched_exit(struct trx_l1h *l1h);
|
||||
|
||||
int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
|
||||
|
||||
int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
|
||||
|
||||
int trx_sched_clock(uint32_t fn);
|
||||
|
||||
int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
|
||||
sbit_t *bits, int8_t rssi, float toa);
|
||||
|
||||
/* set multiframe scheduler to given pchan */
|
||||
int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
|
||||
enum gsm_phys_chan_config pchan);
|
||||
|
||||
/* setting all logical channels given attributes to active/inactive */
|
||||
int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
|
||||
int active);
|
||||
|
||||
/* setting all logical channels given attributes to active/inactive */
|
||||
int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
|
||||
uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1,
|
||||
uint8_t codec2, uint8_t codec3, uint8_t initial_codec,
|
||||
uint8_t handover);
|
||||
|
||||
/* setting cipher on logical channels */
|
||||
int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
|
||||
int algo, uint8_t *key, int key_len);
|
||||
|
||||
/* close all logical channels and reset timeslots */
|
||||
void trx_sched_reset(struct trx_l1h *l1h);
|
||||
|
||||
#endif /* TRX_SCHEDULER_H */
|
||||
560
src/osmo-bts-trx/trx_if.c
Normal file
560
src/osmo-bts-trx/trx_if.c
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* OpenBTS TRX interface handling
|
||||
*
|
||||
* Copyright (C) 2013 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 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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "trx_if.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
/* enable to print RSSI level graph */
|
||||
//#define TOA_RSSI_DEBUG
|
||||
|
||||
int transceiver_available = 0;
|
||||
const char *transceiver_ip = "127.0.0.1";
|
||||
int settsc_enabled = 0;
|
||||
int setbsic_enabled = 0;
|
||||
|
||||
/*
|
||||
* socket
|
||||
*/
|
||||
|
||||
static uint16_t base_port_local = 5800;
|
||||
|
||||
/* open socket */
|
||||
static int trx_udp_open(void *priv, struct osmo_fd *ofd, uint16_t port,
|
||||
int (*cb)(struct osmo_fd *fd, unsigned int what))
|
||||
{
|
||||
struct sockaddr_storage sas;
|
||||
struct sockaddr *sa = (struct sockaddr *)&sas;
|
||||
socklen_t sa_len;
|
||||
|
||||
int rc;
|
||||
|
||||
/* Init */
|
||||
ofd->fd = -1;
|
||||
ofd->cb = cb;
|
||||
ofd->data = priv;
|
||||
|
||||
/* Listen / Binds */
|
||||
rc = osmo_sock_init_ofd(ofd, AF_UNSPEC, SOCK_DGRAM, 0, transceiver_ip,
|
||||
port, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Connect */
|
||||
sa_len = sizeof(sas);
|
||||
rc = getsockname(ofd->fd, sa, &sa_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (sa->sa_family == AF_INET) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
||||
sin->sin_port = htons(ntohs(sin->sin_port) - 100);
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
||||
sin6->sin6_port = htons(ntohs(sin6->sin6_port) - 100);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = connect(ofd->fd, sa, sa_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close socket */
|
||||
static void trx_udp_close(struct osmo_fd *ofd)
|
||||
{
|
||||
if (ofd->fd > 0) {
|
||||
osmo_fd_unregister(ofd);
|
||||
close(ofd->fd);
|
||||
ofd->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* clock
|
||||
*/
|
||||
|
||||
static struct osmo_fd trx_ofd_clk;
|
||||
|
||||
|
||||
/* get clock from clock socket */
|
||||
static int trx_clk_read_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
char buf[1500];
|
||||
int len;
|
||||
uint32_t fn;
|
||||
|
||||
len = recv(ofd->fd, buf, sizeof(buf) - 1, 0);
|
||||
if (len <= 0)
|
||||
return len;
|
||||
buf[len] = '\0';
|
||||
|
||||
if (!!strncmp(buf, "IND CLOCK ", 10)) {
|
||||
LOGP(DTRX, LOGL_NOTICE, "Unknown message on clock port: %s\n",
|
||||
buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sscanf(buf, "IND CLOCK %u", &fn);
|
||||
LOGP(DTRX, LOGL_INFO, "Clock indication: fn=%u\n", fn);
|
||||
|
||||
if (fn >= 2715648) {
|
||||
fn %= 2715648;
|
||||
LOGP(DTRX, LOGL_ERROR, "Indicated clock's FN is not wrapping "
|
||||
"correctly, correcting to fn=%u\n", fn);
|
||||
}
|
||||
|
||||
trx_sched_clock(fn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ctrl
|
||||
*/
|
||||
|
||||
static void trx_ctrl_timer_cb(void *data);
|
||||
|
||||
/* send first ctrl message and start timer */
|
||||
static void trx_ctrl_send(struct trx_l1h *l1h)
|
||||
{
|
||||
struct trx_ctrl_msg *tcm;
|
||||
|
||||
/* get first command */
|
||||
if (llist_empty(&l1h->trx_ctrl_list))
|
||||
return;
|
||||
tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg, list);
|
||||
|
||||
LOGP(DTRX, LOGL_DEBUG, "Sending control '%s' to trx=%u\n", tcm->cmd,
|
||||
l1h->trx->nr);
|
||||
/* send command */
|
||||
send(l1h->trx_ofd_ctrl.fd, tcm->cmd, strlen(tcm->cmd)+1, 0);
|
||||
|
||||
/* start timer */
|
||||
l1h->trx_ctrl_timer.cb = trx_ctrl_timer_cb;
|
||||
l1h->trx_ctrl_timer.data = l1h;
|
||||
osmo_timer_schedule(&l1h->trx_ctrl_timer, 2, 0);
|
||||
}
|
||||
|
||||
/* send first ctrl message and start timer */
|
||||
static void trx_ctrl_timer_cb(void *data)
|
||||
{
|
||||
struct trx_l1h *l1h = data;
|
||||
|
||||
LOGP(DTRX, LOGL_NOTICE, "No response from transceiver for trx=%d\n",
|
||||
l1h->trx->nr);
|
||||
|
||||
trx_ctrl_send(l1h);
|
||||
}
|
||||
|
||||
/* add a new ctrl command */
|
||||
static int trx_ctrl_cmd(struct trx_l1h *l1h, int critical, const char *cmd,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct trx_ctrl_msg *tcm;
|
||||
va_list ap;
|
||||
int l, pending = 0;
|
||||
|
||||
if (!transceiver_available && !!strcmp(cmd, "POWEROFF")) {
|
||||
LOGP(DTRX, LOGL_ERROR, "CTRL ignored: No clock from "
|
||||
"transceiver, please fix!\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!llist_empty(&l1h->trx_ctrl_list))
|
||||
pending = 1;
|
||||
|
||||
/* create message */
|
||||
tcm = talloc_zero(tall_bts_ctx, struct trx_ctrl_msg);
|
||||
if (!tcm)
|
||||
return -ENOMEM;
|
||||
if (fmt && fmt[0]) {
|
||||
l = snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s ", cmd);
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(tcm->cmd + l, sizeof(tcm->cmd) - l - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
} else
|
||||
snprintf(tcm->cmd, sizeof(tcm->cmd)-1, "CMD %s", cmd);
|
||||
tcm->cmd_len = strlen(cmd);
|
||||
tcm->critical = critical;
|
||||
llist_add_tail(&tcm->list, &l1h->trx_ctrl_list);
|
||||
LOGP(DTRX, LOGL_INFO, "Adding new control '%s'\n", tcm->cmd);
|
||||
|
||||
/* send message, if no pending message */
|
||||
if (!pending)
|
||||
trx_ctrl_send(l1h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_if_cmd_poweroff(struct trx_l1h *l1h)
|
||||
{
|
||||
if (l1h->trx->nr == 0)
|
||||
return trx_ctrl_cmd(l1h, 1, "POWEROFF", "");
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_if_cmd_poweron(struct trx_l1h *l1h)
|
||||
{
|
||||
if (l1h->trx->nr == 0)
|
||||
return trx_ctrl_cmd(l1h, 1, "POWERON", "");
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc)
|
||||
{
|
||||
if (!settsc_enabled)
|
||||
return 0;
|
||||
/* if TSC is enabled only, the positive response is mandatory */
|
||||
return trx_ctrl_cmd(l1h, (setbsic_enabled) ? 0 : 1, "SETTSC", "%d",
|
||||
tsc);
|
||||
}
|
||||
|
||||
int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic)
|
||||
{
|
||||
if (!setbsic_enabled)
|
||||
return 0;
|
||||
/* if BSIC is enabled only, the positive response is mandatory */
|
||||
return trx_ctrl_cmd(l1h, (settsc_enabled) ? 0 : 1, "SETBSIC", "%d",
|
||||
bsic);
|
||||
}
|
||||
|
||||
int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db)
|
||||
{
|
||||
return trx_ctrl_cmd(l1h, 0, "SETRXGAIN", "%d", db);
|
||||
}
|
||||
|
||||
int trx_if_cmd_setpower(struct trx_l1h *l1h, int db)
|
||||
{
|
||||
return trx_ctrl_cmd(l1h, 0, "SETPOWER", "%d", db);
|
||||
}
|
||||
|
||||
int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly)
|
||||
{
|
||||
return trx_ctrl_cmd(l1h, 0, "SETMAXDLY", "%d", dly);
|
||||
}
|
||||
|
||||
int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type)
|
||||
{
|
||||
return trx_ctrl_cmd(l1h, 1, "SETSLOT", "%d %d", tn, type);
|
||||
}
|
||||
|
||||
int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn)
|
||||
{
|
||||
uint16_t freq10;
|
||||
|
||||
freq10 = gsm_arfcn2freq10(arfcn, 1); /* RX = uplink */
|
||||
if (freq10 == 0xffff) {
|
||||
LOGP(DTRX, LOGL_ERROR, "Arfcn %d not defined.\n", arfcn);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return trx_ctrl_cmd(l1h, 1, "RXTUNE", "%d", freq10 * 100);
|
||||
}
|
||||
|
||||
int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn)
|
||||
{
|
||||
uint16_t freq10;
|
||||
|
||||
freq10 = gsm_arfcn2freq10(arfcn, 0); /* TX = downlink */
|
||||
if (freq10 == 0xffff) {
|
||||
LOGP(DTRX, LOGL_ERROR, "Arfcn %d not defined.\n", arfcn);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return trx_ctrl_cmd(l1h, 1, "TXTUNE", "%d", freq10 * 100);
|
||||
}
|
||||
|
||||
int trx_if_cmd_handover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss)
|
||||
{
|
||||
return trx_ctrl_cmd(l1h, 1, "HANDOVER", "%d %d", tn, ss);
|
||||
}
|
||||
|
||||
int trx_if_cmd_nohandover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss)
|
||||
{
|
||||
return trx_ctrl_cmd(l1h, 1, "NOHANDOVER", "%d %d", tn, ss);
|
||||
}
|
||||
|
||||
/* get response from ctrl socket */
|
||||
static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct trx_l1h *l1h = ofd->data;
|
||||
char buf[1500];
|
||||
int len, resp;
|
||||
|
||||
len = recv(ofd->fd, buf, sizeof(buf) - 1, 0);
|
||||
if (len <= 0)
|
||||
return len;
|
||||
buf[len] = '\0';
|
||||
|
||||
if (!strncmp(buf, "RSP ", 4)) {
|
||||
struct trx_ctrl_msg *tcm;
|
||||
char *p;
|
||||
int rsp_len = 0;
|
||||
|
||||
/* calculate the length of response item */
|
||||
p = strchr(buf + 4, ' ');
|
||||
if (p)
|
||||
rsp_len = p - buf - 4;
|
||||
else
|
||||
rsp_len = strlen(buf) - 4;
|
||||
|
||||
LOGP(DTRX, LOGL_INFO, "Response message: '%s'\n", buf);
|
||||
|
||||
/* abort timer and send next message, if any */
|
||||
if (osmo_timer_pending(&l1h->trx_ctrl_timer))
|
||||
osmo_timer_del(&l1h->trx_ctrl_timer);
|
||||
|
||||
/* get command for response message */
|
||||
if (llist_empty(&l1h->trx_ctrl_list)) {
|
||||
LOGP(DTRX, LOGL_NOTICE, "Response message without "
|
||||
"command\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg,
|
||||
list);
|
||||
|
||||
/* check if respose matches command */
|
||||
if (rsp_len != tcm->cmd_len) {
|
||||
notmatch:
|
||||
LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE,
|
||||
"Response message '%s' does not match command "
|
||||
"message '%s'\n", buf, tcm->cmd);
|
||||
goto rsp_error;
|
||||
}
|
||||
if (!!strncmp(buf + 4, tcm->cmd + 4, rsp_len))
|
||||
goto notmatch;
|
||||
|
||||
/* check for response code */
|
||||
sscanf(p + 1, "%d", &resp);
|
||||
if (resp) {
|
||||
LOGP(DTRX, (tcm->critical) ? LOGL_FATAL : LOGL_NOTICE,
|
||||
"transceiver (trx=%d) rejected TRX command "
|
||||
"with response: '%s'\n", l1h->trx->nr, buf);
|
||||
rsp_error:
|
||||
if (tcm->critical) {
|
||||
bts_shutdown(l1h->trx->bts, "SIGINT");
|
||||
/* keep tcm list, so process is stopped */
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove command from list */
|
||||
llist_del(&tcm->list);
|
||||
talloc_free(tcm);
|
||||
|
||||
trx_ctrl_send(l1h);
|
||||
} else
|
||||
LOGP(DTRX, LOGL_NOTICE, "Unknown message on ctrl port: %s\n",
|
||||
buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* data
|
||||
*/
|
||||
|
||||
static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
{
|
||||
struct trx_l1h *l1h = ofd->data;
|
||||
uint8_t buf[256];
|
||||
int len;
|
||||
uint8_t tn;
|
||||
int8_t rssi;
|
||||
float toa = 0.0;
|
||||
uint32_t fn;
|
||||
sbit_t bits[148];
|
||||
int i;
|
||||
|
||||
len = recv(ofd->fd, buf, sizeof(buf), 0);
|
||||
if (len <= 0)
|
||||
return len;
|
||||
if (len != 158) {
|
||||
LOGP(DTRX, LOGL_NOTICE, "Got data message with invalid lenght "
|
||||
"'%d'\n", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
tn = buf[0];
|
||||
fn = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4];
|
||||
rssi = -(int8_t)buf[5];
|
||||
toa = ((int16_t)(buf[6] << 8) | buf[7]) / 256.0F;
|
||||
|
||||
/* copy and convert bits {254..0} to sbits {-127..127} */
|
||||
for (i = 0; i < 148; i++) {
|
||||
if (buf[8 + i] == 255)
|
||||
bits[i] = -127;
|
||||
else
|
||||
bits[i] = 127 - buf[8 + i];
|
||||
}
|
||||
|
||||
if (tn >= 8) {
|
||||
LOGP(DTRX, LOGL_ERROR, "Illegal TS %d\n", tn);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (fn >= 2715648) {
|
||||
LOGP(DTRX, LOGL_ERROR, "Illegal FN %u\n", fn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DTRX, LOGL_DEBUG, "RX burst tn=%u fn=%u rssi=%d toa=%.2f\n",
|
||||
tn, fn, rssi, toa);
|
||||
|
||||
#ifdef TOA_RSSI_DEBUG
|
||||
char deb[128];
|
||||
|
||||
sprintf(deb, "| 0 "
|
||||
" | rssi=%4d toa=%4.2f fn=%u", rssi, toa, fn);
|
||||
deb[1 + (128 + rssi) / 4] = '*';
|
||||
fprintf(stderr, "%s\n", deb);
|
||||
#endif
|
||||
|
||||
trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr,
|
||||
const ubit_t *bits)
|
||||
{
|
||||
uint8_t buf[256];
|
||||
|
||||
LOGP(DTRX, LOGL_DEBUG, "TX burst tn=%u fn=%u pwr=%u\n", tn, fn, pwr);
|
||||
|
||||
buf[0] = tn;
|
||||
buf[1] = (fn >> 24) & 0xff;
|
||||
buf[2] = (fn >> 16) & 0xff;
|
||||
buf[3] = (fn >> 8) & 0xff;
|
||||
buf[4] = (fn >> 0) & 0xff;
|
||||
buf[5] = pwr;
|
||||
|
||||
/* copy ubits {0,1} */
|
||||
memcpy(buf + 6, bits, 148);
|
||||
|
||||
/* we must be sure that we have clock, and we have sent all control
|
||||
* data */
|
||||
if (transceiver_available && llist_empty(&l1h->trx_ctrl_list)) {
|
||||
send(l1h->trx_ofd_data.fd, buf, 154, 0);
|
||||
} else
|
||||
LOGP(DTRX, LOGL_DEBUG, "Ignoring TX data, transceiver "
|
||||
"offline.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* open/close
|
||||
*/
|
||||
|
||||
int trx_if_open(struct trx_l1h *l1h)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DTRX, LOGL_NOTICE, "Open transceiver for trx=%u\n", l1h->trx->nr);
|
||||
|
||||
/* initialize ctrl queue */
|
||||
INIT_LLIST_HEAD(&l1h->trx_ctrl_list);
|
||||
|
||||
/* open sockets */
|
||||
if (l1h->trx->nr == 0) {
|
||||
rc = trx_udp_open(NULL, &trx_ofd_clk, base_port_local,
|
||||
trx_clk_read_cb);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
LOGP(DTRX, LOGL_NOTICE, "Waiting for transceiver send clock\n");
|
||||
}
|
||||
rc = trx_udp_open(l1h, &l1h->trx_ofd_ctrl,
|
||||
base_port_local + (l1h->trx->nr << 1) + 1, trx_ctrl_read_cb);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
rc = trx_udp_open(l1h, &l1h->trx_ofd_data,
|
||||
base_port_local + (l1h->trx->nr << 1) + 2, trx_data_read_cb);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
/* enable all slots */
|
||||
l1h->config.slotmask = 0xff;
|
||||
|
||||
if (l1h->trx->nr == 0)
|
||||
trx_if_cmd_poweroff(l1h);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
trx_if_close(l1h);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* flush pending control messages */
|
||||
void trx_if_flush(struct trx_l1h *l1h)
|
||||
{
|
||||
struct trx_ctrl_msg *tcm;
|
||||
|
||||
/* free ctrl message list */
|
||||
while (!llist_empty(&l1h->trx_ctrl_list)) {
|
||||
tcm = llist_entry(l1h->trx_ctrl_list.next, struct trx_ctrl_msg,
|
||||
list);
|
||||
llist_del(&tcm->list);
|
||||
talloc_free(tcm);
|
||||
}
|
||||
}
|
||||
|
||||
void trx_if_close(struct trx_l1h *l1h)
|
||||
{
|
||||
LOGP(DTRX, LOGL_NOTICE, "Close transceiver for trx=%u\n", l1h->trx->nr);
|
||||
|
||||
trx_if_flush(l1h);
|
||||
|
||||
/* close sockets */
|
||||
if (l1h->trx->nr == 0)
|
||||
trx_udp_close(&trx_ofd_clk);
|
||||
trx_udp_close(&l1h->trx_ofd_ctrl);
|
||||
trx_udp_close(&l1h->trx_ofd_data);
|
||||
}
|
||||
|
||||
35
src/osmo-bts-trx/trx_if.h
Normal file
35
src/osmo-bts-trx/trx_if.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef TRX_IF_H
|
||||
#define TRX_IF_H
|
||||
|
||||
extern int transceiver_available;
|
||||
extern const char *transceiver_ip;
|
||||
extern int settsc_enabled;
|
||||
extern int setbsic_enabled;
|
||||
|
||||
|
||||
struct trx_ctrl_msg {
|
||||
struct llist_head list;
|
||||
char cmd[128];
|
||||
int cmd_len;
|
||||
int critical;
|
||||
};
|
||||
|
||||
int trx_if_cmd_poweroff(struct trx_l1h *l1h);
|
||||
int trx_if_cmd_poweron(struct trx_l1h *l1h);
|
||||
int trx_if_cmd_settsc(struct trx_l1h *l1h, uint8_t tsc);
|
||||
int trx_if_cmd_setbsic(struct trx_l1h *l1h, uint8_t bsic);
|
||||
int trx_if_cmd_setrxgain(struct trx_l1h *l1h, int db);
|
||||
int trx_if_cmd_setpower(struct trx_l1h *l1h, int db);
|
||||
int trx_if_cmd_setmaxdly(struct trx_l1h *l1h, int dly);
|
||||
int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn, uint8_t type);
|
||||
int trx_if_cmd_rxtune(struct trx_l1h *l1h, uint16_t arfcn);
|
||||
int trx_if_cmd_txtune(struct trx_l1h *l1h, uint16_t arfcn);
|
||||
int trx_if_cmd_handover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss);
|
||||
int trx_if_cmd_nohandover(struct trx_l1h *l1h, uint8_t tn, uint8_t ss);
|
||||
int trx_if_data(struct trx_l1h *l1h, uint8_t tn, uint32_t fn, uint8_t pwr,
|
||||
const ubit_t *bits);
|
||||
int trx_if_open(struct trx_l1h *l1h);
|
||||
void trx_if_flush(struct trx_l1h *l1h);
|
||||
void trx_if_close(struct trx_l1h *l1h);
|
||||
|
||||
#endif /* TRX_IF_H */
|
||||
421
src/osmo-bts-trx/trx_vty.c
Normal file
421
src/osmo-bts-trx/trx_vty.c
Normal file
@@ -0,0 +1,421 @@
|
||||
/* VTY interface for sysmoBTS */
|
||||
|
||||
/* (C) 2013 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 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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
|
||||
#include "l1_if.h"
|
||||
#include "scheduler.h"
|
||||
#include "trx_if.h"
|
||||
#include "loops.h"
|
||||
|
||||
static struct gsm_bts *vty_bts;
|
||||
|
||||
DEFUN(show_transceiver, show_transceiver_cmd, "show transceiver",
|
||||
SHOW_STR "Display information about transceivers\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty_bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct trx_l1h *l1h;
|
||||
uint8_t tn;
|
||||
|
||||
if (!transceiver_available) {
|
||||
vty_out(vty, "transceiver is not connected%s", VTY_NEWLINE);
|
||||
} else {
|
||||
vty_out(vty, "transceiver is connected, current fn=%u%s",
|
||||
transceiver_last_fn, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
l1h = trx_l1h_hdl(trx);
|
||||
vty_out(vty, "TRX %d%s", trx->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " %s%s",
|
||||
(l1h->config.poweron) ? "poweron":"poweroff",
|
||||
VTY_NEWLINE);
|
||||
if (l1h->config.arfcn_valid)
|
||||
vty_out(vty, " arfcn : %d%s%s",
|
||||
(l1h->config.arfcn & ~ARFCN_PCS),
|
||||
(l1h->config.arfcn & ARFCN_PCS) ? " (PCS)" : "",
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " arfcn : undefined%s", VTY_NEWLINE);
|
||||
if (l1h->config.tsc_valid)
|
||||
vty_out(vty, " tsc : %d%s", l1h->config.tsc,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " tsc : undefined%s", VTY_NEWLINE);
|
||||
if (l1h->config.bsic_valid)
|
||||
vty_out(vty, " bsic : %d%s", l1h->config.bsic,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " bisc : undefined%s", VTY_NEWLINE);
|
||||
if (l1h->config.rxgain_valid)
|
||||
vty_out(vty, " rxgain : %d%s", l1h->config.rxgain,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " rxgain : undefined%s", VTY_NEWLINE);
|
||||
if (l1h->config.power_valid)
|
||||
vty_out(vty, " power : %d%s", l1h->config.power,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " power : undefined%s", VTY_NEWLINE);
|
||||
if (l1h->config.maxdly_valid)
|
||||
vty_out(vty, " maxdly : %d%s", l1h->config.maxdly,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " maxdly : undefined%s", VTY_NEWLINE);
|
||||
for (tn = 0; tn < 8; tn++) {
|
||||
if (!((1 << tn) & l1h->config.slotmask))
|
||||
vty_out(vty, " slot #%d: unsupported%s", tn,
|
||||
VTY_NEWLINE);
|
||||
else if (l1h->config.slottype_valid[tn])
|
||||
vty_out(vty, " slot #%d: type %d%s", tn,
|
||||
l1h->config.slottype[tn],
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " slot #%d: undefined%s", tn,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_fn_advance, cfg_bts_fn_advance_cmd,
|
||||
"fn-advance <0-30>",
|
||||
"Set the number of frames to be transmitted to transceiver in advance "
|
||||
"of current FN\n"
|
||||
"Advance in frames\n")
|
||||
{
|
||||
trx_clock_advance = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_rts_advance, cfg_bts_rts_advance_cmd,
|
||||
"rts-advance <0-30>",
|
||||
"Set the number of frames to be requested (PCU) in advance of current "
|
||||
"FN. Do not change this, unless you have a good reason!\n"
|
||||
"Advance in frames\n")
|
||||
{
|
||||
trx_rts_advance = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_ms_power_loop, cfg_bts_ms_power_loop_cmd,
|
||||
"ms-power-loop <-127-127>",
|
||||
"Enable MS power control loop\nTarget RSSI value (transceiver specific, "
|
||||
"should be 6dB or more above noise floor)\n")
|
||||
{
|
||||
trx_ms_power_loop = 1;
|
||||
trx_target_rssi = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_no_ms_power_loop, cfg_bts_no_ms_power_loop_cmd,
|
||||
"no ms-power-loop",
|
||||
NO_STR "Disable MS power control loop\n")
|
||||
{
|
||||
trx_ms_power_loop = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_timing_advance_loop, cfg_bts_timing_advance_loop_cmd,
|
||||
"timing-advance-loop",
|
||||
"Enable timing advance control loop\n")
|
||||
{
|
||||
trx_ta_loop = 1;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
DEFUN(cfg_bts_no_timing_advance_loop, cfg_bts_no_timing_advance_loop_cmd,
|
||||
"no timing-advance-loop",
|
||||
NO_STR "Disable timing advance control loop\n")
|
||||
{
|
||||
trx_ta_loop = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_settsc, cfg_bts_settsc_cmd,
|
||||
"settsc",
|
||||
"Use SETTSC to configure transceiver\n")
|
||||
{
|
||||
settsc_enabled = 1;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_setbsic, cfg_bts_setbsic_cmd,
|
||||
"setbsic",
|
||||
"Use SETBSIC to configure transceiver\n")
|
||||
{
|
||||
setbsic_enabled = 1;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_no_settsc, cfg_bts_no_settsc_cmd,
|
||||
"no settsc",
|
||||
NO_STR "Disable SETTSC to configure transceiver\n")
|
||||
{
|
||||
settsc_enabled = 0;
|
||||
if (!setbsic_enabled) {
|
||||
vty_out(vty, "%% Auto enabling SETBSIC.%s", VTY_NEWLINE);
|
||||
setbsic_enabled = 1;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_no_setbsic, cfg_bts_no_setbsic_cmd,
|
||||
"no setbsic",
|
||||
NO_STR "Disable SETBSIC to configure transceiver\n")
|
||||
{
|
||||
setbsic_enabled = 0;
|
||||
if (!settsc_enabled) {
|
||||
vty_out(vty, "%% Auto enabling SETTSC.%s", VTY_NEWLINE);
|
||||
settsc_enabled = 1;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_rxgain, cfg_trx_rxgain_cmd,
|
||||
"rxgain <0-50>",
|
||||
"Set the receiver gain in dB\n"
|
||||
"Gain in dB\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.rxgain = atoi(argv[0]);
|
||||
l1h->config.rxgain_valid = 1;
|
||||
l1h->config.rxgain_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_power, cfg_trx_power_cmd,
|
||||
"power <0-50>",
|
||||
"Set the transmitter power dampening\n"
|
||||
"Power dampening in dB\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.power = atoi(argv[0]);
|
||||
l1h->config.power_oml = 0;
|
||||
l1h->config.power_valid = 1;
|
||||
l1h->config.power_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_poweroml_, cfg_trx_power_oml_cmd,
|
||||
"power oml",
|
||||
"Set the transmitter power dampening\n"
|
||||
"Given by NM_ATT_RF_MAXPOWR_R (max power reduction) via OML\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.power = trx->max_power_red;
|
||||
l1h->config.power_oml = 1;
|
||||
l1h->config.power_valid = 1;
|
||||
l1h->config.power_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_maxdly, cfg_trx_maxdly_cmd,
|
||||
"maxdly <0-31>",
|
||||
"Set the maximum delay of GSM symbols\n"
|
||||
"GSM symbols (approx. 1.1km per symbol)\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.maxdly = atoi(argv[0]);
|
||||
l1h->config.maxdly_valid = 1;
|
||||
l1h->config.maxdly_sent = 0;
|
||||
l1if_provision_transceiver_trx(l1h);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_slotmask, cfg_trx_slotmask_cmd,
|
||||
"slotmask (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0) (1|0)",
|
||||
"Set the supported slots\n"
|
||||
"TS0 supported\nTS0 unsupported\nTS1 supported\nTS1 unsupported\n"
|
||||
"TS2 supported\nTS2 unsupported\nTS3 supported\nTS3 unsupported\n"
|
||||
"TS4 supported\nTS4 unsupported\nTS5 supported\nTS5 unsupported\n"
|
||||
"TS6 supported\nTS6 unsupported\nTS7 supported\nTS7 unsupported\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
uint8_t tn;
|
||||
|
||||
l1h->config.slotmask = 0;
|
||||
for (tn = 0; tn < 8; tn++)
|
||||
if (argv[tn][0] == '1')
|
||||
l1h->config.slotmask |= (1 << tn);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_no_rxgain, cfg_trx_no_rxgain_cmd,
|
||||
"no rxgain <0-50>",
|
||||
NO_STR "Unset the receiver gain in dB\n"
|
||||
"Gain in dB\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.rxgain_valid = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_no_power, cfg_trx_no_power_cmd,
|
||||
"no power <0-50>",
|
||||
NO_STR "Unset the transmitter power dampening\n"
|
||||
"Power dampening in dB\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.power_valid = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_no_maxdly, cfg_trx_no_maxdly_cmd,
|
||||
"no maxdly <0-31>",
|
||||
NO_STR "Unset the maximum delay of GSM symbols\n"
|
||||
"GSM symbols (approx. 1.1km per symbol)\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
l1h->config.maxdly_valid = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void bts_model_config_write_bts(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
vty_out(vty, " fn-advance %d%s", trx_clock_advance, VTY_NEWLINE);
|
||||
vty_out(vty, " rts-advance %d%s", trx_rts_advance, VTY_NEWLINE);
|
||||
|
||||
if (trx_ms_power_loop)
|
||||
vty_out(vty, " ms-power-loop %d%s", trx_target_rssi,
|
||||
VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " no ms-power-loop%s", VTY_NEWLINE);
|
||||
vty_out(vty, " %stiming-advance-loop%s", (trx_ta_loop) ? "":"no ",
|
||||
VTY_NEWLINE);
|
||||
if (settsc_enabled)
|
||||
vty_out(vty, " settsc%s", VTY_NEWLINE);
|
||||
if (setbsic_enabled)
|
||||
vty_out(vty, " setbsic%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct trx_l1h *l1h = trx_l1h_hdl(trx);
|
||||
|
||||
if (l1h->config.rxgain_valid)
|
||||
vty_out(vty, " rxgain %d%s", l1h->config.rxgain, VTY_NEWLINE);
|
||||
if (l1h->config.power_valid) {
|
||||
if (l1h->config.power_oml)
|
||||
vty_out(vty, " power oml%s", VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " power %d%s", l1h->config.power,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
if (l1h->config.maxdly_valid)
|
||||
vty_out(vty, " maxdly %d%s", l1h->config.maxdly, VTY_NEWLINE);
|
||||
if (l1h->config.slotmask != 0xff)
|
||||
vty_out(vty, " slotmask %d %d %d %d %d %d %d %d%s",
|
||||
l1h->config.slotmask & 1,
|
||||
(l1h->config.slotmask >> 1) & 1,
|
||||
(l1h->config.slotmask >> 2) & 1,
|
||||
(l1h->config.slotmask >> 3) & 1,
|
||||
(l1h->config.slotmask >> 4) & 1,
|
||||
(l1h->config.slotmask >> 5) & 1,
|
||||
(l1h->config.slotmask >> 6) & 1,
|
||||
l1h->config.slotmask >> 7,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
int bts_model_vty_init(struct gsm_bts *bts)
|
||||
{
|
||||
vty_bts = bts;
|
||||
|
||||
install_element_ve(&show_transceiver_cmd);
|
||||
|
||||
install_element(BTS_NODE, &cfg_bts_fn_advance_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_rts_advance_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_ms_power_loop_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_ms_power_loop_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_timing_advance_loop_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_timing_advance_loop_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_settsc_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_setbsic_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_settsc_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_no_setbsic_cmd);
|
||||
|
||||
install_element(TRX_NODE, &cfg_trx_rxgain_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_power_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_power_oml_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_maxdly_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_slotmask_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_no_rxgain_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_no_power_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_no_maxdly_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
SUBDIRS = paging cipher sysmobts agch misc
|
||||
SUBDIRS = paging cipher agch misc bursts handover
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
SUBDIRS += sysmobts
|
||||
endif
|
||||
|
||||
if ENABLE_SYSMOBTS
|
||||
SUBDIRS += sysmobts
|
||||
endif
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||
|
||||
14
tests/bursts/Makefile.am
Normal file
14
tests/bursts/Makefile.am
Normal file
@@ -0,0 +1,14 @@
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)
|
||||
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS)
|
||||
noinst_PROGRAMS = bursts_test
|
||||
EXTRA_DIST = bursts_test.ok
|
||||
|
||||
bursts_test_SOURCES = bursts_test.c \
|
||||
$(top_builddir)/src/osmo-bts-trx/gsm0503_coding.c \
|
||||
$(top_builddir)/src/osmo-bts-trx/gsm0503_conv.c \
|
||||
$(top_builddir)/src/osmo-bts-trx/gsm0503_interleaving.c \
|
||||
$(top_builddir)/src/osmo-bts-trx/gsm0503_mapping.c \
|
||||
$(top_builddir)/src/osmo-bts-trx/gsm0503_tables.c \
|
||||
$(top_builddir)/src/osmo-bts-trx/gsm0503_parity.c
|
||||
bursts_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
495
tests/bursts/bursts_test.c
Normal file
495
tests/bursts/bursts_test.c
Normal file
@@ -0,0 +1,495 @@
|
||||
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "../../src/osmo-bts-trx/gsm0503_coding.h"
|
||||
|
||||
#include <osmo-bts/logging.h>
|
||||
|
||||
#define ASSERT_TRUE(rc) \
|
||||
if (!(rc)) { \
|
||||
printf("Assert failed in %s:%d.\n", \
|
||||
__FILE__, __LINE__); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
/* set condition to 1, to show debugging */
|
||||
#define printd if (0) printf
|
||||
|
||||
static int ubits2sbits(ubit_t *ubits, sbit_t *sbits, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (*ubits == 0x23) {
|
||||
ubits++;
|
||||
sbits++;
|
||||
continue;
|
||||
}
|
||||
if ((*ubits++) & 1)
|
||||
*sbits++ = -127;
|
||||
else
|
||||
*sbits++ = 127;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void test_xcch(uint8_t *l2)
|
||||
{
|
||||
uint8_t result[23];
|
||||
ubit_t bursts_u[116 * 4];
|
||||
sbit_t bursts_s[116 * 4];
|
||||
int n_errors, n_bits_total;
|
||||
|
||||
printd("Encoding: %s\n", osmo_hexdump(l2, 23));
|
||||
|
||||
/* encode */
|
||||
xcch_encode(bursts_u, l2);
|
||||
|
||||
printd("U-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
|
||||
bursts_u[57], bursts_u[58]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
|
||||
bursts_u[57 + 116], bursts_u[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
|
||||
bursts_u[57 + 232], bursts_u[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
|
||||
bursts_u[57 + 348], bursts_u[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
|
||||
ubits2sbits(bursts_u, bursts_s, 116 * 4);
|
||||
printd("S-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
|
||||
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
|
||||
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
|
||||
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
|
||||
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
|
||||
|
||||
/* destroy */
|
||||
memset(bursts_s, 0, 30);
|
||||
memset(bursts_s + 116, 0, 30);
|
||||
|
||||
/* decode */
|
||||
xcch_decode(result, bursts_s, &n_errors, &n_bits_total);
|
||||
|
||||
ASSERT_TRUE(n_bits_total == 456);
|
||||
|
||||
printd("Decoded: %s\n", osmo_hexdump(result, 23));
|
||||
printf("xcch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
|
||||
n_errors, n_bits_total, (float)n_errors/n_bits_total);
|
||||
|
||||
ASSERT_TRUE(!memcmp(l2, result, 23));
|
||||
|
||||
printd("\n");
|
||||
}
|
||||
|
||||
static void test_rach(uint8_t bsic, uint8_t ra)
|
||||
{
|
||||
uint8_t result;
|
||||
ubit_t bursts_u[36];
|
||||
sbit_t bursts_s[36];
|
||||
|
||||
printd("Encoding: %02x\n", ra);
|
||||
|
||||
/* encode */
|
||||
rach_encode(bursts_u, &ra, bsic);
|
||||
|
||||
printd("U-Bits:\n");
|
||||
printd("%s\n", osmo_hexdump(bursts_u, 36));
|
||||
ubits2sbits(bursts_u, bursts_s, 36);
|
||||
printd("S-Bits:\n");
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s, 36));
|
||||
|
||||
/* destroy */
|
||||
memset(bursts_s + 6, 0, 8);
|
||||
|
||||
/* decode */
|
||||
rach_decode(&result, bursts_s, bsic);
|
||||
|
||||
printd("Decoded: %02x\n", result);
|
||||
|
||||
ASSERT_TRUE(ra == result);
|
||||
|
||||
printd("\n");
|
||||
}
|
||||
|
||||
static void test_sch(uint8_t *info)
|
||||
{
|
||||
uint8_t result[4];
|
||||
ubit_t bursts_u[78];
|
||||
sbit_t bursts_s[78];
|
||||
|
||||
/* zero bits 25 and above */
|
||||
info[3] &= 1;
|
||||
result[3] = 0;
|
||||
|
||||
printd("Encoding: %s\n", osmo_hexdump(info, 4));
|
||||
|
||||
/* encode */
|
||||
sch_encode(bursts_u, info);
|
||||
|
||||
printd("U-Bits:\n");
|
||||
printd("%s\n", osmo_hexdump(bursts_u, 78));
|
||||
ubits2sbits(bursts_u, bursts_s, 78);
|
||||
printd("S-Bits:\n");
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s, 78));
|
||||
|
||||
/* destroy */
|
||||
memset(bursts_s + 6, 0, 10);
|
||||
|
||||
/* decode */
|
||||
sch_decode(result, bursts_s);
|
||||
|
||||
printd("Decoded: %s\n", osmo_hexdump(result, 4));
|
||||
|
||||
ASSERT_TRUE(!memcmp(info, result, 4));
|
||||
|
||||
printd("\n");
|
||||
}
|
||||
|
||||
static void test_fr(uint8_t *speech, int len)
|
||||
{
|
||||
uint8_t result[33];
|
||||
ubit_t bursts_u[116 * 8];
|
||||
sbit_t bursts_s[116 * 8];
|
||||
int n_errors, n_bits_total;
|
||||
int rc;
|
||||
|
||||
memset(bursts_u, 0x23, sizeof(bursts_u));
|
||||
memset(bursts_s, 0, sizeof(bursts_s));
|
||||
|
||||
printd("Encoding: %s\n", osmo_hexdump(speech, len));
|
||||
|
||||
/* encode */
|
||||
tch_fr_encode(bursts_u, speech, len, 1);
|
||||
|
||||
printd("U-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
|
||||
bursts_u[57], bursts_u[58]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
|
||||
bursts_u[57 + 116], bursts_u[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
|
||||
bursts_u[57 + 232], bursts_u[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
|
||||
bursts_u[57 + 348], bursts_u[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57),
|
||||
bursts_u[57 + 464], bursts_u[58 + 464]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57),
|
||||
bursts_u[57 + 580], bursts_u[58 + 580]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 696, 57),
|
||||
bursts_u[57 + 696], bursts_u[58 + 696]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 696, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 812, 57),
|
||||
bursts_u[57 + 812], bursts_u[58 + 812]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 812, 57));
|
||||
ubits2sbits(bursts_u, bursts_s, 116 * 8);
|
||||
printd("S-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
|
||||
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
|
||||
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
|
||||
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
|
||||
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57),
|
||||
(uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57),
|
||||
(uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 696, 57),
|
||||
(uint8_t)bursts_s[57 + 696], (uint8_t)bursts_s[58 + 696]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 696, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 812, 57),
|
||||
(uint8_t)bursts_s[57 + 812], (uint8_t)bursts_s[58 + 812]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 812, 57));
|
||||
|
||||
/* destroy */
|
||||
memset(bursts_s + 6, 0, 20);
|
||||
|
||||
/* decode */
|
||||
rc = tch_fr_decode(result, bursts_s, 1, len == 31, &n_errors, &n_bits_total);
|
||||
|
||||
ASSERT_TRUE(rc == len);
|
||||
|
||||
printd("Decoded: %s\n", osmo_hexdump(result, len));
|
||||
printf("tch_fr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
|
||||
n_errors, n_bits_total, (float)n_errors/n_bits_total);
|
||||
|
||||
ASSERT_TRUE(!memcmp(speech, result, len));
|
||||
|
||||
printd("\n");
|
||||
}
|
||||
|
||||
static void test_hr(uint8_t *speech, int len)
|
||||
{
|
||||
uint8_t result[23];
|
||||
ubit_t bursts_u[116 * 6];
|
||||
sbit_t bursts_s[116 * 6];
|
||||
int n_errors, n_bits_total;
|
||||
int rc;
|
||||
|
||||
memset(bursts_u, 0x23, sizeof(bursts_u));
|
||||
memset(bursts_s, 0, sizeof(bursts_s));
|
||||
|
||||
printd("Encoding: %s\n", osmo_hexdump(speech, len));
|
||||
|
||||
/* encode */
|
||||
tch_hr_encode(bursts_u, speech, len);
|
||||
|
||||
printd("U-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
|
||||
bursts_u[57], bursts_u[58]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
|
||||
bursts_u[57 + 116], bursts_u[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
|
||||
bursts_u[57 + 232], bursts_u[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
|
||||
bursts_u[57 + 348], bursts_u[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 464, 57),
|
||||
bursts_u[57 + 464], bursts_u[58 + 464]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 464, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 580, 57),
|
||||
bursts_u[57 + 580], bursts_u[58 + 580]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 580, 57));
|
||||
ubits2sbits(bursts_u, bursts_s, 116 * 6);
|
||||
printd("S-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
|
||||
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
|
||||
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
|
||||
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
|
||||
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 464, 57),
|
||||
(uint8_t)bursts_s[57 + 464], (uint8_t)bursts_s[58 + 464]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 464, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 580, 57),
|
||||
(uint8_t)bursts_s[57 + 580], (uint8_t)bursts_s[58 + 580]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 580, 57));
|
||||
|
||||
/* destroy */
|
||||
memset(bursts_s + 6, 0, 20);
|
||||
|
||||
/* decode */
|
||||
rc = tch_hr_decode(result, bursts_s, 0, &n_errors, &n_bits_total);
|
||||
|
||||
ASSERT_TRUE(rc == len);
|
||||
|
||||
printd("Decoded: %s\n", osmo_hexdump(result, len));
|
||||
printf("tch_hr_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
|
||||
n_errors, n_bits_total, (float)n_errors/n_bits_total);
|
||||
|
||||
ASSERT_TRUE(!memcmp(speech, result, len));
|
||||
|
||||
printd("\n");
|
||||
}
|
||||
|
||||
static void test_pdtch(uint8_t *l2, int len)
|
||||
{
|
||||
uint8_t result[len];
|
||||
ubit_t bursts_u[116 * 4];
|
||||
sbit_t bursts_s[116 * 4];
|
||||
int n_errors, n_bits_total;
|
||||
int rc;
|
||||
|
||||
/* zero the not coded tail bits */
|
||||
switch (len) {
|
||||
case 34:
|
||||
case 54:
|
||||
l2[len - 1] &= 0x7f;
|
||||
result[len - 1] &= 0x7f;
|
||||
break;
|
||||
case 40:
|
||||
l2[len - 1] &= 0x07;
|
||||
result[len - 1] &= 0x07;
|
||||
break;
|
||||
}
|
||||
|
||||
printd("Encoding: %s\n", osmo_hexdump(l2, len));
|
||||
|
||||
/* encode */
|
||||
pdtch_encode(bursts_u, l2, len);
|
||||
|
||||
printd("U-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u, 57),
|
||||
bursts_u[57], bursts_u[58]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 116, 57),
|
||||
bursts_u[57 + 116], bursts_u[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 232, 57),
|
||||
bursts_u[57 + 232], bursts_u[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump(bursts_u + 348, 57),
|
||||
bursts_u[57 + 348], bursts_u[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump(bursts_u + 59 + 348, 57));
|
||||
ubits2sbits(bursts_u, bursts_s, 116 * 4);
|
||||
printd("S-Bits:\n");
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s, 57),
|
||||
(uint8_t)bursts_s[57], (uint8_t)bursts_s[58]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 116, 57),
|
||||
(uint8_t)bursts_s[57 + 116], (uint8_t)bursts_s[58 + 116]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 116, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 232, 57),
|
||||
(uint8_t)bursts_s[57 + 232], (uint8_t)bursts_s[58 + 232]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 232, 57));
|
||||
printd("%s %02x %02x ", osmo_hexdump((uint8_t *)bursts_s + 348, 57),
|
||||
(uint8_t)bursts_s[57 + 348], (uint8_t)bursts_s[58 + 348]);
|
||||
printd("%s\n", osmo_hexdump((uint8_t *)bursts_s + 59 + 348, 57));
|
||||
|
||||
/* decode */
|
||||
rc = pdtch_decode(result, bursts_s, NULL, &n_errors, &n_bits_total);
|
||||
|
||||
ASSERT_TRUE(rc == len);
|
||||
|
||||
printd("Decoded: %s\n", osmo_hexdump(result, len));
|
||||
printf("pdtch_decode: n_errors=%d n_bits_total=%d ber=%.2f\n",
|
||||
n_errors, n_bits_total, (float)n_errors/n_bits_total);
|
||||
|
||||
ASSERT_TRUE(!memcmp(l2, result, len));
|
||||
|
||||
printd("\n");
|
||||
}
|
||||
|
||||
uint8_t test_l2[][23] = {
|
||||
/* dummy frame */
|
||||
{ 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
/* random frame */
|
||||
{ 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
|
||||
0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
|
||||
0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8 },
|
||||
/* jolly frame */
|
||||
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
|
||||
};
|
||||
|
||||
uint8_t test_macblock[][54] = {
|
||||
/* random frame */
|
||||
{ 0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
|
||||
0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
|
||||
0xfa, 0x27, 0x5d, 0x71, 0x7a, 0x59, 0xa8, 0x42,
|
||||
0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
|
||||
0xa3, 0xaf, 0x5f, 0xc6, 0x36, 0x43, 0x44, 0xab,
|
||||
0xd9, 0x6d, 0x7d, 0x62, 0x24, 0xc9, 0xd2, 0x92,
|
||||
0xfa, 0x27, 0x5d, 0x71, 0x7a, 0xa8 },
|
||||
/* jolly frame */
|
||||
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
|
||||
};
|
||||
|
||||
uint8_t test_speech_fr[33];
|
||||
uint8_t test_speech_efr[31];
|
||||
uint8_t test_speech_hr[15];
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
bts_log_init(NULL);
|
||||
|
||||
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
|
||||
test_xcch(test_l2[i]);
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
test_rach(0x3f, i);
|
||||
test_rach(0x00, i);
|
||||
test_rach(0x1a, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
|
||||
test_sch(test_l2[i]);
|
||||
|
||||
for (i = 0; i < sizeof(test_speech_fr); i++)
|
||||
test_speech_fr[i] = i;
|
||||
test_speech_fr[0] = 0xd0;
|
||||
test_fr(test_speech_fr, sizeof(test_speech_fr));
|
||||
|
||||
for (i = 0; i < sizeof(test_speech_efr); i++)
|
||||
test_speech_efr[i] = i;
|
||||
test_speech_efr[0] = 0xc0;
|
||||
test_fr(test_speech_efr, sizeof(test_speech_efr));
|
||||
|
||||
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
|
||||
test_fr(test_l2[i], sizeof(test_l2[0]));
|
||||
|
||||
for (i = 0; i < sizeof(test_speech_hr); i++)
|
||||
test_speech_hr[i] = i*17;
|
||||
test_speech_hr[0] = 0x00;
|
||||
test_hr(test_speech_hr, sizeof(test_speech_hr));
|
||||
|
||||
for (i = 0; i < sizeof(test_l2) / sizeof(test_l2[0]); i++)
|
||||
test_hr(test_l2[i], sizeof(test_l2[0]));
|
||||
|
||||
for (i = 0; i < sizeof(test_macblock) / sizeof(test_macblock[0]); i++) {
|
||||
test_pdtch(test_macblock[i], 23);
|
||||
test_pdtch(test_macblock[i], 34);
|
||||
test_pdtch(test_macblock[i], 40);
|
||||
test_pdtch(test_macblock[i], 54);
|
||||
}
|
||||
|
||||
printf("Success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
21
tests/bursts/bursts_test.ok
Normal file
21
tests/bursts/bursts_test.ok
Normal file
@@ -0,0 +1,21 @@
|
||||
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
|
||||
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
|
||||
xcch_decode: n_errors=60 n_bits_total=456 ber=0.13
|
||||
tch_fr_decode: n_errors=8 n_bits_total=378 ber=0.02
|
||||
tch_fr_decode: n_errors=8 n_bits_total=378 ber=0.02
|
||||
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
|
||||
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
|
||||
tch_fr_decode: n_errors=10 n_bits_total=456 ber=0.02
|
||||
tch_hr_decode: n_errors=11 n_bits_total=211 ber=0.05
|
||||
tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02
|
||||
tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02
|
||||
tch_hr_decode: n_errors=10 n_bits_total=456 ber=0.02
|
||||
pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
|
||||
pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
|
||||
pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
|
||||
pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00
|
||||
pdtch_decode: n_errors=0 n_bits_total=456 ber=0.00
|
||||
pdtch_decode: n_errors=132 n_bits_total=588 ber=0.22
|
||||
pdtch_decode: n_errors=220 n_bits_total=676 ber=0.33
|
||||
pdtch_decode: n_errors=0 n_bits_total=444 ber=0.00
|
||||
Success
|
||||
8
tests/handover/Makefile.am
Normal file
8
tests/handover/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(OPENBSC_INCDIR)
|
||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOCODEC_CFLAGS)$(LIBOSMOTRAU_CFLAGS)
|
||||
LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOCODEC_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOABIS_LIBS)
|
||||
noinst_PROGRAMS = handover_test
|
||||
EXTRA_DIST = handover_test.ok
|
||||
|
||||
handover_test_SOURCES = handover_test.c
|
||||
handover_test_LDADD = $(top_builddir)/src/common/libbts.a $(LDADD)
|
||||
278
tests/handover/handover_test.c
Normal file
278
tests/handover/handover_test.c
Normal file
@@ -0,0 +1,278 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <sched.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/bits.h>
|
||||
#include <osmocom/core/backtrace.h>
|
||||
#include <osmocom/abis/abis.h>
|
||||
|
||||
#include <osmo-bts/gsm_data.h>
|
||||
#include <osmo-bts/logging.h>
|
||||
#include <osmo-bts/abis.h>
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/vty.h>
|
||||
#include <osmo-bts/bts_model.h>
|
||||
#include <osmo-bts/pcu_if.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
#include <osmo-bts/handover.h>
|
||||
|
||||
uint8_t phys_info[] = { 0x03, 0x03, 0x0d, 0x06, 0x2d, 0x00, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b };
|
||||
|
||||
static struct gsm_bts *bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
int quit = 0;
|
||||
uint8_t abis_mac[6] = { 0, 1, 2, 3, 4, 5 };
|
||||
const int pcu_direct = 0;
|
||||
int modify_count = 0;
|
||||
|
||||
static void expect_phys_info(struct lapdm_entity *le)
|
||||
{
|
||||
struct osmo_phsap_prim pp;
|
||||
int rc;
|
||||
|
||||
rc = lapdm_phsap_dequeue_prim(le, &pp);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(sizeof(phys_info) == pp.oph.msg->len);
|
||||
OSMO_ASSERT(!memcmp(phys_info, pp.oph.msg->data, pp.oph.msg->len));
|
||||
msgb_free(pp.oph.msg);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct gsm_bts_role_bts *btsb;
|
||||
void *tall_bts_ctx;
|
||||
void *tall_msgb_ctx;
|
||||
struct e1inp_line *line;
|
||||
struct gsm_lchan *lchan;
|
||||
struct osmo_phsap_prim nl1sap;
|
||||
struct msgb *msg;
|
||||
struct abis_rsl_dchan_hdr *rslh;
|
||||
int i;
|
||||
|
||||
tall_bts_ctx = talloc_named_const(NULL, 1, "OsmoBTS context");
|
||||
tall_msgb_ctx = talloc_named_const(tall_bts_ctx, 1, "msgb");
|
||||
msgb_set_talloc_ctx(tall_msgb_ctx);
|
||||
|
||||
bts_log_init(NULL);
|
||||
osmo_stderr_target->categories[DHO].loglevel = LOGL_DEBUG;
|
||||
|
||||
bts = gsm_bts_alloc(tall_bts_ctx);
|
||||
if (!bts) {
|
||||
fprintf(stderr, "Failed to create BTS structure\n");
|
||||
exit(1);
|
||||
}
|
||||
trx = gsm_bts_trx_alloc(bts);
|
||||
if (!trx) {
|
||||
fprintf(stderr, "Failed to TRX structure\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (bts_init(bts) < 0) {
|
||||
fprintf(stderr, "unable to to open bts\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
btsb = bts_role_bts(bts);
|
||||
|
||||
libosmo_abis_init(NULL);
|
||||
|
||||
line = e1inp_line_create(0, "ipa");
|
||||
OSMO_ASSERT(line);
|
||||
|
||||
e1inp_ts_config_sign(&line->ts[E1INP_SIGN_RSL-1], line);
|
||||
trx->rsl_link = e1inp_sign_link_create(&line->ts[E1INP_SIGN_RSL-1], E1INP_SIGN_RSL, NULL, 0, 0);
|
||||
OSMO_ASSERT(trx->rsl_link);
|
||||
trx->rsl_link->trx = trx;
|
||||
|
||||
fprintf(stderr, "test 1: without timeout\n");
|
||||
|
||||
/* create two lchans for handover */
|
||||
lchan = &trx->ts[1].lchan[0];
|
||||
l1sap_chan_act(lchan->ts->trx, 0x09, NULL);
|
||||
lchan = &trx->ts[2].lchan[0];
|
||||
lchan->ho.active = HANDOVER_ENABLED;
|
||||
lchan->ho.ref = 23;
|
||||
l1sap_chan_act(lchan->ts->trx, 0x0a, NULL);
|
||||
OSMO_ASSERT(msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
OSMO_ASSERT(msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
|
||||
/* send access burst with wrong ref */
|
||||
memset(&nl1sap, 0, sizeof(nl1sap));
|
||||
osmo_prim_init(&nl1sap.oph, SAP_GSM_PH, PRIM_PH_RACH, PRIM_OP_INDICATION, NULL);
|
||||
nl1sap.u.rach_ind.chan_nr = 0x0a;
|
||||
nl1sap.u.rach_ind.ra = 42;
|
||||
l1sap_up(trx, &nl1sap);
|
||||
|
||||
/* expect no action */
|
||||
OSMO_ASSERT(modify_count == 0);
|
||||
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
|
||||
/* send access burst with correct ref */
|
||||
nl1sap.u.rach_ind.ra = 23;
|
||||
l1sap_up(trx, &nl1sap);
|
||||
OSMO_ASSERT(modify_count == 1);
|
||||
|
||||
/* expect PHYS INFO */
|
||||
expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch);
|
||||
|
||||
/* expect exactly one HO.DET */
|
||||
OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
rslh = msgb_l2(msg);
|
||||
OSMO_ASSERT(rslh->c.msg_type == RSL_MT_HANDO_DET);
|
||||
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
|
||||
/* expect T3105 running */
|
||||
OSMO_ASSERT(osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
|
||||
|
||||
/* indicate frame */
|
||||
handover_frame(&trx->ts[2].lchan[0]);
|
||||
|
||||
/* expect T3105 not running */
|
||||
OSMO_ASSERT(!osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
|
||||
|
||||
fprintf(stderr, "test 2: with timeout\n");
|
||||
|
||||
/* enable handover again */
|
||||
lchan = &trx->ts[2].lchan[0];
|
||||
lchan->ho.active = HANDOVER_ENABLED;
|
||||
lchan->ho.ref = 23;
|
||||
modify_count = 0;
|
||||
|
||||
/* send access burst with correct ref */
|
||||
nl1sap.u.rach_ind.ra = 23;
|
||||
l1sap_up(trx, &nl1sap);
|
||||
OSMO_ASSERT(modify_count == 1);
|
||||
|
||||
/* expect PHYS INFO */
|
||||
expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch);
|
||||
|
||||
/* expect exactly one HO.DET */
|
||||
OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
rslh = msgb_l2(msg);
|
||||
OSMO_ASSERT(rslh->c.msg_type == RSL_MT_HANDO_DET);
|
||||
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
|
||||
for (i = 0; i < btsb->ny1 - 1; i++) {
|
||||
/* expect T3105 running */
|
||||
OSMO_ASSERT(osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
|
||||
|
||||
/* timeout T3105 */
|
||||
gettimeofday(&trx->ts[2].lchan[0].ho.t3105.timeout, NULL);
|
||||
osmo_select_main(0);
|
||||
|
||||
/* expect PHYS INFO */
|
||||
expect_phys_info(&trx->ts[2].lchan[0].lapdm_ch.lapdm_dcch);
|
||||
}
|
||||
|
||||
/* timeout T3105 */
|
||||
gettimeofday(&trx->ts[2].lchan[0].ho.t3105.timeout, NULL);
|
||||
osmo_select_main(0);
|
||||
|
||||
/* expect T3105 not running */
|
||||
OSMO_ASSERT(!osmo_timer_pending(&trx->ts[2].lchan[0].ho.t3105))
|
||||
|
||||
/* expect exactly one CONN.FAIL */
|
||||
OSMO_ASSERT(msg = msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
rslh = msgb_l2(msg);
|
||||
OSMO_ASSERT(rslh->c.msg_type == RSL_MT_CONN_FAIL);
|
||||
OSMO_ASSERT(!msgb_dequeue(&trx->rsl_link->tx_list));
|
||||
|
||||
#if 0
|
||||
while (!quit) {
|
||||
log_reset_context();
|
||||
osmo_select_main(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("Success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bts_model_abis_close(struct gsm_bts *bts)
|
||||
{
|
||||
}
|
||||
|
||||
int bts_model_oml_estab(struct gsm_bts *bts)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
||||
{
|
||||
int rc = 0;
|
||||
uint8_t chan_nr;
|
||||
uint8_t tn, ss;
|
||||
struct gsm_lchan *lchan;
|
||||
struct msgb *msg = l1sap->oph.msg;
|
||||
struct osmo_phsap_prim nl1sap;
|
||||
|
||||
switch (OSMO_PRIM_HDR(&l1sap->oph)) {
|
||||
case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
|
||||
switch (l1sap->u.info.type) {
|
||||
case PRIM_INFO_ACTIVATE:
|
||||
chan_nr = l1sap->u.info.u.act_req.chan_nr;
|
||||
tn = L1SAP_CHAN2TS(chan_nr);
|
||||
ss = l1sap_chan2ss(chan_nr);
|
||||
lchan = &trx->ts[tn].lchan[ss];
|
||||
|
||||
lchan_init_lapdm(lchan);
|
||||
|
||||
lchan_set_state(lchan, LCHAN_S_ACTIVE);
|
||||
|
||||
memset(&nl1sap, 0, sizeof(nl1sap));
|
||||
osmo_prim_init(&nl1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_CONFIRM, NULL);
|
||||
nl1sap.u.info.type = PRIM_INFO_ACTIVATE;
|
||||
nl1sap.u.info.u.act_cnf.chan_nr = chan_nr;
|
||||
return l1sap_up(trx, &nl1sap);
|
||||
case PRIM_INFO_MODIFY:
|
||||
modify_count++;
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1C, LOGL_NOTICE, "unknown MPH-INFO.req %d\n",
|
||||
l1sap->u.info.type);
|
||||
rc = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1C, LOGL_NOTICE, "unknown prim %d op %d\n",
|
||||
l1sap->oph.primitive, l1sap->oph.operation);
|
||||
rc = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (msg)
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type, struct tlv_parsed *old_attr, struct tlv_parsed *new_attr, void *obj) { return 0; }
|
||||
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg, struct tlv_parsed *new_attr, int obj_kind, void *obj) { return 0; }
|
||||
int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj) { return 0; }
|
||||
int bts_model_chg_adm_state(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj, uint8_t adm_state) { return 0; }
|
||||
int bts_model_init(struct gsm_bts *bts) { return 0; }
|
||||
int bts_model_trx_deact_rf(struct gsm_bts_trx *trx) { return 0; }
|
||||
int bts_model_trx_close(struct gsm_bts_trx *trx) { return 0; }
|
||||
void trx_get_hlayer1(void) {}
|
||||
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) { return 0; }
|
||||
1
tests/handover/handover_test.ok
Normal file
1
tests/handover/handover_test.ok
Normal file
@@ -0,0 +1 @@
|
||||
Success
|
||||
@@ -78,7 +78,7 @@ static void test_msg_utils_ipa(void)
|
||||
memcpy(msg->l1h, ipa_rsl_connect, sizeof(ipa_rsl_connect));
|
||||
msg->l1h[2] = 0x23;
|
||||
rc = msg_verify_ipa_structure(msg);
|
||||
OSMO_ASSERT(rc == -1);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ int bts_model_rsl_chan_mod(struct gsm_lchan *lchan)
|
||||
{ return 0; }
|
||||
void bts_model_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
unsigned int rtp_pl_len) {}
|
||||
int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
|
||||
{ return 0; }
|
||||
|
||||
int l1if_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
|
||||
uint16_t arfcn, uint8_t block_nr, uint8_t *data, uint8_t len)
|
||||
@@ -53,3 +55,10 @@ int bts_model_oml_estab(struct gsm_bts *bts)
|
||||
|
||||
int l1if_set_txpower(struct femtol1_hdl *fl1h, float tx_power)
|
||||
{ return 0; }
|
||||
|
||||
|
||||
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
|
||||
{ return 0; }
|
||||
|
||||
void bts_model_abis_close(struct gsm_bts *bts)
|
||||
{ }
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
|
||||
#include <osmo-bts/bts.h>
|
||||
#include <osmo-bts/l1sap.h>
|
||||
#include <osmo-bts/power_control.h>
|
||||
|
||||
#include "femtobts.h"
|
||||
#include "l1_if.h"
|
||||
@@ -169,14 +171,77 @@ static void test_sysmobts_cipher(void)
|
||||
/* Handle message sent before ciphering was received */
|
||||
memcpy(&unit.u8Buffer[0], too_early_classmark, ARRAY_SIZE(too_early_classmark));
|
||||
unit.u8Size = ARRAY_SIZE(too_early_classmark);
|
||||
bts_check_for_first_ciphrd(&fl1h, &unit, &lchan);
|
||||
rc = bts_check_for_first_ciphrd(&lchan, unit.u8Buffer, unit.u8Size);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_RX_CONF);
|
||||
|
||||
/* Now send the first ciphered message */
|
||||
memcpy(&unit.u8Buffer[0], first_ciphered_cipher_cmpl, ARRAY_SIZE(first_ciphered_cipher_cmpl));
|
||||
unit.u8Size = ARRAY_SIZE(first_ciphered_cipher_cmpl);
|
||||
bts_check_for_first_ciphrd(&fl1h, &unit, &lchan);
|
||||
OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_TXRX_REQ);
|
||||
rc = bts_check_for_first_ciphrd(&lchan, unit.u8Buffer, unit.u8Size);
|
||||
OSMO_ASSERT(rc == 1);
|
||||
/* we cannot test for lchan.ciph_state == * LCHAN_CIPH_RX_CONF_TX_REQ, as
|
||||
* this happens asynchronously on the other side of the l1sap queue */
|
||||
}
|
||||
|
||||
static void test_sysmobts_loop(void)
|
||||
{
|
||||
struct gsm_bts bts;
|
||||
struct gsm_bts_role_bts btsb;
|
||||
struct gsm_bts_trx trx;
|
||||
struct gsm_bts_trx_ts ts;
|
||||
struct gsm_lchan *lchan;
|
||||
int ret;
|
||||
|
||||
memset(&bts, 0, sizeof(bts));
|
||||
memset(&btsb, 0, sizeof(btsb));
|
||||
memset(&trx, 0, sizeof(trx));
|
||||
memset(&ts, 0, sizeof(ts));
|
||||
|
||||
lchan = &ts.lchan[0];
|
||||
lchan->ts = &ts;
|
||||
ts.trx = &trx;
|
||||
trx.bts = &bts;
|
||||
bts.role = &btsb;
|
||||
bts.band = GSM_BAND_1800;
|
||||
trx.ms_power_control = 1;
|
||||
btsb.ul_power_target = -75;
|
||||
|
||||
printf("Testing sysmobts power control\n");
|
||||
|
||||
/* Simply clamping */
|
||||
lchan->state = LCHAN_S_NONE;
|
||||
lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
|
||||
OSMO_ASSERT(ret == 0);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
|
||||
|
||||
|
||||
/*
|
||||
* Now 15 dB too little and we should power it up. Could be a
|
||||
* power level of 7 or 8 for 15 dBm
|
||||
*/
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -90);
|
||||
OSMO_ASSERT(ret == 1);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 7);
|
||||
|
||||
/* It should be clamped to level 0 and 30 dBm */
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -100);
|
||||
OSMO_ASSERT(ret == 1);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
|
||||
|
||||
/* Fix it and jump down */
|
||||
lchan->ms_power_ctrl.fixed = 1;
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -60);
|
||||
OSMO_ASSERT(ret == 0);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 0);
|
||||
|
||||
/* And leave it again */
|
||||
lchan->ms_power_ctrl.fixed = 0;
|
||||
ret = lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, -40);
|
||||
OSMO_ASSERT(ret == 1);
|
||||
OSMO_ASSERT(lchan->ms_power_ctrl.current == 15);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -184,6 +249,7 @@ int main(int argc, char **argv)
|
||||
printf("Testing sysmobts routines\n");
|
||||
test_sysmobts_auto_band();
|
||||
test_sysmobts_cipher();
|
||||
test_sysmobts_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -199,3 +265,5 @@ int bts_model_init(struct gsm_bts *bts)
|
||||
{ return 0; }
|
||||
int bts_model_oml_estab(struct gsm_bts *bts)
|
||||
{ return 0; }
|
||||
void bts_model_abis_close(struct gsm_bts *bts)
|
||||
{ }
|
||||
|
||||
@@ -17,3 +17,4 @@ Checking PCS to PCS
|
||||
PCS to PCS band(1) arfcn(512) want(3) got(3)
|
||||
PCS to PCS band(8) arfcn(128) want(0) got(0)
|
||||
PCS to PCS band(2) arfcn(438) want(-1) got(-1)
|
||||
Testing sysmobts power control
|
||||
|
||||
@@ -24,3 +24,15 @@ AT_KEYWORDS([misc])
|
||||
cat $abs_srcdir/misc/misc_test.ok > expout
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/misc/misc_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([bursts])
|
||||
AT_KEYWORDS([bursts])
|
||||
cat $abs_srcdir/bursts/bursts_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/bursts/bursts_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([handover])
|
||||
AT_KEYWORDS([handover])
|
||||
cat $abs_srcdir/handover/handover_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/handover/handover_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
Reference in New Issue
Block a user