aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@netfilter.org>2009-12-21 23:26:57 +0100
committerHarald Welte <laforge@netfilter.org>2009-12-21 23:26:57 +0100
commit766f0422badf1a0484635c3a3cee15cef1780edb (patch)
tree6b9c4ef710b211a715beb252fe0c2e95e38ff577
parent9abe6a43a7b1e2d76672677834dc3ea6a1762ca8 (diff)
parent73d4fce151b587ae28692448ea7094212e7eab7b (diff)
Merge remote branch 'origin/master' into gprs
Conflicts: openbsc/include/openbsc/gsm_data.h openbsc/src/Makefile.am openbsc/src/gsm_data.c openbsc/src/system_information.c openbsc/src/vty_interface.c
-rw-r--r--openbsc/include/openbsc/Makefile.am3
-rw-r--r--openbsc/include/openbsc/abis_rsl.h8
-rw-r--r--openbsc/include/openbsc/bitvec.h6
-rw-r--r--openbsc/include/openbsc/call_handling.h64
-rw-r--r--openbsc/include/openbsc/debug.h13
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h5
-rw-r--r--openbsc/include/openbsc/gsm_data.h98
-rw-r--r--openbsc/include/openbsc/gsm_utils.h4
-rw-r--r--openbsc/include/openbsc/handover.h8
-rw-r--r--openbsc/include/openbsc/meas_rep.h34
-rw-r--r--openbsc/include/openbsc/mncc.h13
-rw-r--r--openbsc/include/openbsc/rtp_proxy.h17
-rw-r--r--openbsc/include/openbsc/signal.h2
-rw-r--r--openbsc/include/openbsc/tlv.h1
-rw-r--r--openbsc/include/openbsc/transaction.h8
-rw-r--r--openbsc/include/openbsc/trau_mux.h2
-rw-r--r--openbsc/src/Makefile.am1
-rw-r--r--[-rwxr-xr-x]openbsc/src/abis_nm.c106
-rw-r--r--openbsc/src/abis_rsl.c359
-rw-r--r--openbsc/src/bitvec.c65
-rw-r--r--openbsc/src/bsc_hack.c9
-rw-r--r--openbsc/src/bsc_init.c72
-rw-r--r--openbsc/src/chan_alloc.c25
-rw-r--r--openbsc/src/db.c10
-rw-r--r--openbsc/src/debug.c1
-rw-r--r--openbsc/src/e1_config.c5
-rw-r--r--openbsc/src/e1_input.c14
-rw-r--r--openbsc/src/gsm_04_08.c337
-rw-r--r--openbsc/src/gsm_04_08_utils.c11
-rw-r--r--openbsc/src/gsm_04_11.c9
-rw-r--r--openbsc/src/gsm_data.c72
-rw-r--r--openbsc/src/gsm_utils.c38
-rw-r--r--openbsc/src/handover_decision.c298
-rw-r--r--openbsc/src/handover_logic.c135
-rw-r--r--openbsc/src/input/ipaccess.c14
-rw-r--r--openbsc/src/meas_rep.c114
-rw-r--r--openbsc/src/mncc.c103
-rw-r--r--openbsc/src/msgb.c5
-rw-r--r--openbsc/src/openbsc.cfg.1-12
-rw-r--r--openbsc/src/openbsc.cfg.2-22
-rw-r--r--openbsc/src/rest_octets.c5
-rw-r--r--openbsc/src/rrlp.c48
-rw-r--r--openbsc/src/rtp_proxy.c274
-rw-r--r--openbsc/src/system_information.c173
-rw-r--r--openbsc/src/telnet_interface.c13
-rw-r--r--openbsc/src/token_auth.c4
-rw-r--r--openbsc/src/transaction.c26
-rw-r--r--openbsc/src/trau_frame.c18
-rw-r--r--openbsc/src/trau_mux.c96
-rw-r--r--openbsc/src/vty/vty.c2
-rw-r--r--openbsc/src/vty_interface.c193
-rw-r--r--wireshark/abis_oml.patch16
52 files changed, 2347 insertions, 614 deletions
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 8bb64eb91..27c88b770 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -4,4 +4,5 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \
- silent_call.h mgcp.h
+ silent_call.h mgcp.h meas_rep.h bitvec.h rest_octets.h \
+ system_information.h handover.h
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index a911be355..797b2f349 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -497,7 +497,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
u_int8_t bs_power, u_int8_t ms_power,
u_int8_t ta);
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
- u_int8_t ta);
+ u_int8_t ta, u_int8_t ho_ref);
int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
int rsl_encryption_cmd(struct msgb *msg);
int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
@@ -537,8 +537,8 @@ int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci);
/* ip.access specfic RSL extensions */
int rsl_ipacc_crcx(struct gsm_lchan *lchan);
int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip,
- u_int16_t port, u_int16_t conn_id,
- u_int8_t rtp_payload2);
+ u_int16_t port, u_int8_t rtp_payload2);
+int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan);
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan);
int abis_rsl_rcvmsg(struct msgb *msg);
@@ -547,7 +547,7 @@ unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans,
int n_pag_blocks);
unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res);
u_int64_t str_to_imsi(const char *imsi_str);
-u_int8_t lchan2chan_nr(struct gsm_lchan *lchan);
+u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan);
int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id);
/* to be provided by external code */
diff --git a/openbsc/include/openbsc/bitvec.h b/openbsc/include/openbsc/bitvec.h
index 80ed4ad0a..b35aebf16 100644
--- a/openbsc/include/openbsc/bitvec.h
+++ b/openbsc/include/openbsc/bitvec.h
@@ -39,6 +39,12 @@ struct bitvec {
u_int8_t *data; /* pointer to data array */
};
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr);
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n);
+
/* Set a bit at given position */
int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
enum bit_value bit);
diff --git a/openbsc/include/openbsc/call_handling.h b/openbsc/include/openbsc/call_handling.h
deleted file mode 100644
index 02027889e..000000000
--- a/openbsc/include/openbsc/call_handling.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2008 by Stefan Schmidt <stefan@datenfreihafen.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.
- *
- */
-
-#ifndef _CALL_HANDLING_H
-#define _CALL_HANDLING_H
-
-#include "linuxlist.h"
-#include "gsm_subscriber.h"
-#include "timer.h"
-
-/*
- * State transitions to be seen from the outside
- */
-#define CALL_STATE_NULL 0
-#define CALL_STATE_SETUP 1
-#define CALL_STATE_PROCEED 2
-#define CALL_STATE_ALERT 3
-#define CALL_STATE_CONNECT 4
-#define CALL_STATE_ACTIVE 5
-#define CALL_STATE_RELEASE 6
-
-struct call_data {
- struct llist_head entry;
- void (*state_change_cb)(int oldstate, int newstate, int event, void *data);
- void *data;
- char *destination_number;
-
- /* Internal */
- int state;
- char tmsi[GSM_TMSI_LENGTH];
- struct timer_list t30x; /* to be added for... */
-};
-
-
-int call_initiate(struct call_data *call, char *tmsi);
-void call_abort(struct call_data *call);
-
-/**
- * Get notified about new incoming calls. The call_data is owned
- * and managed by the internal call handling.
- */
-void call_set_callback(void (*cb)(struct call_data *call, void *data), void* data);
-void call_proceed(struct call_data *call_data);
-void call_connect(struct call_data *call_data);
-
-#endif /* _CALL_HANDLING_H */
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index 1b5059ff3..95f27c7e0 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -26,6 +26,8 @@
#define DMGCP 0x40000
+#define DHO 0x80000
+
#ifdef DEBUG
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
@@ -43,4 +45,15 @@ void debug_use_color(int use_color);
void debug_timestamp(int enable);
extern unsigned int debug_mask;
+/* new logging interface */
+#define LOGP(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ##args)
+#define LOGPC(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ##args)
+
+/* different levels */
+#define LOGL_DEBUG 1 /* debugging information */
+#define LOGL_INFO 3
+#define LOGL_NOTICE 5 /* abnormal/unexpected condition */
+#define LOGL_ERROR 7 /* error condition, requires user action */
+#define LOGL_FATAL 8 /* fatal, program aborted */
+
#endif /* _DEBUG_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index bc4fabf0c..23dffd76b 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -746,7 +746,6 @@ struct gsm_trans;
/* config options controlling the behaviour of the lower leves */
void gsm0408_allow_everyone(int allow);
-void gsm0408_set_reject_cause(int cause);
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
@@ -768,8 +767,8 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
u_int8_t apdu_len, const u_int8_t *apdu);
int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_class);
-int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan,
- struct gsm_lchan *new_lchan, u_int8_t power_command);
+int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
+ u_int8_t power_command, u_int8_t ho_ref);
int bsc_upqueue(struct gsm_network *net);
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 1499a3f62..4464f6340 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -38,6 +38,13 @@ enum gsm_chan_t {
GSM_LCHAN_UNKNOWN,
};
+/* RRLP mode of operation */
+enum rrlp_mode {
+ RRLP_MODE_NONE,
+ RRLP_MODE_MS_BASED,
+ RRLP_MODE_MS_PREF,
+ RRLP_MODE_ASS_PREF,
+};
/* Channel Request reason */
enum gsm_chreq_reason_t {
@@ -53,6 +60,7 @@ enum gsm_chreq_reason_t {
#include <openbsc/abis_rsl.h>
#include <openbsc/mncc.h>
#include <openbsc/tlv.h>
+#include <openbsc/bitvec.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -135,6 +143,20 @@ struct gsm_loc_updating_operation {
unsigned int waiting_for_imei : 1;
};
+/* Maximum number of neighbor cells whose average we track */
+#define MAX_NEIGH_MEAS 10
+/* Maximum size of the averaging window for neighbor cells */
+#define MAX_WIN_NEIGH_AVG 10
+
+/* processed neighbor measurements for one cell */
+struct neigh_meas_proc {
+ u_int16_t arfcn;
+ u_int8_t bsic;
+ u_int8_t rxlev[MAX_WIN_NEIGH_AVG];
+ unsigned int rxlev_cnt;
+ u_int8_t last_seen_nr;
+};
+
#define MAX_A5_KEY_LEN (128/8)
#define RSL_ENC_ALG_A5(x) (x+1)
@@ -143,6 +165,13 @@ struct gsm_loc_updating_operation {
#define LCHAN_SAPI_MS 1
#define LCHAN_SAPI_NET 2
+/* state of a logical channel */
+enum gsm_lchan_state {
+ LCHAN_S_NONE, /* channel is not active */
+ LCHAN_S_ACTIVE, /* channel is active and operational */
+ LCHAN_S_INACTIVE, /* channel is set inactive */
+};
+
struct gsm_lchan {
/* The TS that we're part of */
struct gsm_bts_trx_ts *ts;
@@ -154,6 +183,8 @@ struct gsm_lchan {
enum rsl_cmod_spd rsl_cmode;
/* If TCH, traffic channel mode */
enum gsm48_chan_mode tch_mode;
+ /* State */
+ enum gsm_lchan_state state;
/* Power levels for MS and BTS */
u_int8_t bs_power;
u_int8_t ms_power;
@@ -185,6 +216,24 @@ struct gsm_lchan {
/* use count. how many users use this channel */
unsigned int use_count;
+
+ /* cache of last measurement reports on this lchan */
+ struct gsm_meas_rep meas_rep[6];
+ int meas_rep_idx;
+
+ /* table of neighbor cell measurements */
+ struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
+
+ struct {
+ u_int32_t bound_ip;
+ u_int32_t connect_ip;
+ u_int16_t bound_port;
+ u_int16_t connect_port;
+ u_int16_t conn_id;
+ u_int8_t rtp_payload2;
+ u_int8_t speech_mode;
+ struct rtp_socket *rtp_socket;
+ } abis_ip;
};
struct gsm_e1_subslot {
@@ -212,13 +261,6 @@ struct gsm_bts_trx_ts {
/* To which E1 subslot are we connected */
struct gsm_e1_subslot e1_link;
- struct {
- u_int32_t bound_ip;
- u_int16_t bound_port;
- u_int8_t rtp_payload2;
- u_int16_t conn_id;
- struct rtp_socket *rtp_socket;
- } abis_ip;
struct gsm_lchan lchan[TS_MAX_LCHAN];
};
@@ -340,7 +382,6 @@ struct gsm_bts {
/* should the channel allocator allocate channels from high TRX to TRX0,
* rather than starting from TRX0 and go upwards? */
int chan_alloc_reverse;
- int cell_barred;
/* maximum Tx power that the MS is permitted to use in this cell */
int ms_max_power;
@@ -374,6 +415,13 @@ struct gsm_bts {
struct gsm48_cell_sel_par cell_sel_par;
struct gsm48_cell_options cell_options;
struct gsm48_control_channel_descr chan_desc;
+ struct bitvec neigh_list;
+ struct bitvec cell_alloc;
+ struct {
+ /* bitmask large enough for all possible ARFCN's */
+ u_int8_t neigh_list[1024/8];
+ u_int8_t cell_alloc[1024/8];
+ } data;
} si_common;
/* ip.accesss Unit ID's have Site/BTS/TRX layout */
@@ -381,6 +429,7 @@ struct gsm_bts {
struct {
u_int16_t site_id;
u_int16_t bts_id;
+ u_int32_t flags;
} ip_access;
struct {
struct {
@@ -427,8 +476,26 @@ struct gsm_network {
char *name_long;
char *name_short;
enum gsm_auth_policy auth_policy;
+ enum gsm48_reject_value reject_cause;
int a5_encryption;
int neci;
+ int send_mm_info;
+ struct {
+ int active;
+ /* Window RXLEV averaging */
+ unsigned int win_rxlev_avg; /* number of SACCH frames */
+ /* Window RXQUAL averaging */
+ unsigned int win_rxqual_avg; /* number of SACCH frames */
+ /* Window RXLEV neighbouring cells averaging */
+ unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */
+
+ /* how often should we check for power budget HO */
+ unsigned int pwr_interval; /* SACCH frames */
+ /* how much better does a neighbor cell have to be ? */
+ unsigned int pwr_hysteresis; /* dBm */
+ /* maximum distacne before we try a handover */
+ unsigned int max_distance; /* TA values */
+ } handover;
/* layer 4 */
int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
@@ -450,6 +517,11 @@ struct gsm_network {
int T3117;
int T3119;
int T3141;
+
+ /* Radio Resource Location Protocol (TS 04.31) */
+ struct {
+ enum rrlp_mode mode;
+ } rrlp;
};
#define SMS_HDR_SIZE 128
@@ -481,6 +553,11 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num);
+
+/* Get reference to a neighbor cell on a given BCCH ARFCN */
+struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
+ u_int16_t arfcn, u_int8_t bsic);
+
struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num);
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
@@ -506,6 +583,7 @@ char *gsm_band_name(enum gsm_band band);
enum gsm_band gsm_band_parse(const char *mhz);
extern void *tall_bsc_ctx;
+extern int ipacc_rtp_direct;
static inline int is_ipaccess_bts(struct gsm_bts *bts)
{
@@ -534,6 +612,9 @@ static inline int is_siemens_bts(struct gsm_bts *bts)
enum gsm_auth_policy gsm_auth_policy_parse(const char *arg);
const char *gsm_auth_policy_name(enum gsm_auth_policy policy);
+enum rrlp_mode rrlp_mode_parse(const char *arg);
+const char *rrlp_mode_name(enum rrlp_mode mode);
+
void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
/* A parsed GPRS routing area */
@@ -546,5 +627,6 @@ struct gprs_ra_id {
int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts);
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
+struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);
#endif
diff --git a/openbsc/include/openbsc/gsm_utils.h b/openbsc/include/openbsc/gsm_utils.h
index 1bd1bc55a..5809221a3 100644
--- a/openbsc/include/openbsc/gsm_utils.h
+++ b/openbsc/include/openbsc/gsm_utils.h
@@ -33,5 +33,9 @@ int gsm_7bit_encode(u_int8_t *result, const char *data);
int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl);
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(u_int8_t rxlev);
+u_int8_t dbm2rxlev(int dbm);
+
void generate_backtrace();
#endif
diff --git a/openbsc/include/openbsc/handover.h b/openbsc/include/openbsc/handover.h
new file mode 100644
index 000000000..8ab1b0642
--- /dev/null
+++ b/openbsc/include/openbsc/handover.h
@@ -0,0 +1,8 @@
+#ifndef _HANDOVER_H
+#define _HANDOVER_H
+/* Hand over the specified logical channel to the specified new BTS.
+ * This is the main entry point for the actual handover algorithm,
+ * after it has decided it wants to initiate HO to a specific BTS */
+int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts);
+
+#endif /* _HANDOVER_H */
diff --git a/openbsc/include/openbsc/meas_rep.h b/openbsc/include/openbsc/meas_rep.h
index b1ad2daa8..fd5fced43 100644
--- a/openbsc/include/openbsc/meas_rep.h
+++ b/openbsc/include/openbsc/meas_rep.h
@@ -1,11 +1,14 @@
#ifndef _MEAS_REP_H
#define _MEAS_REP_H
+#define MRC_F_PROCESSED 0x0001
+
/* extracted from a L3 measurement report IE */
struct gsm_meas_rep_cell {
u_int8_t rxlev;
- u_int8_t bcch_freq; /* FIXME: translate to ARFCN */
u_int8_t bsic;
+ u_int16_t arfcn;
+ unsigned int flags;
};
/* RX Level and RX Quality */
@@ -30,11 +33,15 @@ struct gsm_meas_rep_unidir {
/* parsed uplink and downlink measurement result */
struct gsm_meas_rep {
+ /* back-pointer to the logical channel */
struct gsm_lchan *lchan;
+ /* number of the measurement report */
u_int8_t nr;
+ /* flags, see MEAS_REP_F_* */
unsigned int flags;
+ /* uplink and downlink rxlev, rxqual; full and sub */
struct gsm_meas_rep_unidir ul;
struct gsm_meas_rep_unidir dl;
@@ -45,8 +52,33 @@ struct gsm_meas_rep {
u_int8_t ta; /* MS timing advance */
} ms_l1;
+ /* neighbor measurement reports for up to 6 cells */
int num_cell;
struct gsm_meas_rep_cell cell[6];
};
+enum meas_rep_field {
+ MEAS_REP_DL_RXLEV_FULL,
+ MEAS_REP_DL_RXLEV_SUB,
+ MEAS_REP_DL_RXQUAL_FULL,
+ MEAS_REP_DL_RXQUAL_SUB,
+ MEAS_REP_UL_RXLEV_FULL,
+ MEAS_REP_UL_RXLEV_SUB,
+ MEAS_REP_UL_RXQUAL_FULL,
+ MEAS_REP_UL_RXQUAL_SUB,
+};
+
+/* obtain an average over the last 'num' fields in the meas reps */
+int get_meas_rep_avg(const struct gsm_lchan *lchan,
+ enum meas_rep_field field, unsigned int num);
+
+/* Check if N out of M last values for FIELD are >= bd */
+int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
+ enum meas_rep_field field,
+ unsigned int n, unsigned int m, int be);
+
+unsigned int calc_initial_idx(unsigned int array_size,
+ unsigned int meas_rep_idx,
+ unsigned int num_values);
+
#endif /* _MEAS_REP_H */
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
index 68d76abf8..fbf3cab1f 100644
--- a/openbsc/include/openbsc/mncc.h
+++ b/openbsc/include/openbsc/mncc.h
@@ -87,7 +87,8 @@ struct gsm_call {
#define MNCC_FRAME_DROP 0x0202
#define MNCC_LCHAN_MODIFY 0x0203
-#define GSM_TRAU_FRAME 0x0300
+#define GSM_TCHF_FRAME 0x0300
+#define GSM_TCHF_FRAME_EFR 0x0301
#define GSM_MAX_FACILITY 128
#define GSM_MAX_SSVERSION 128
@@ -162,6 +163,14 @@ struct gsm_mncc_cccap {
int pcp;
};
+enum {
+ GSM_MNCC_BCAP_SPEECH = 0,
+ GSM_MNCC_BCAP_UNR_DIG = 1,
+ GSM_MNCC_BCAP_AUDIO = 2,
+ GSM_MNCC_BCAP_FAX_G3 = 3,
+ GSM_MNCC_BCAP_OTHER_ITC = 5,
+ GSM_MNCC_BCAP_RESERVED = 7,
+};
struct gsm_mncc {
/* context based information */
@@ -199,7 +208,7 @@ struct gsm_mncc {
unsigned char lchan_mode;
};
-struct gsm_trau_frame {
+struct gsm_data_frame {
u_int32_t msg_type;
u_int32_t callref;
unsigned char data[0];
diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h
index e9fc157cf..d128e4f23 100644
--- a/openbsc/include/openbsc/rtp_proxy.h
+++ b/openbsc/include/openbsc/rtp_proxy.h
@@ -34,6 +34,11 @@ enum rtp_rx_action {
RTP_RECV_UPSTREAM,
};
+enum rtp_tx_action {
+ RTP_SEND_NONE,
+ RTP_SEND_DOWNSTREAM,
+};
+
struct rtp_sub_socket {
struct sockaddr_in sin_local;
struct sockaddr_in sin_remote;
@@ -56,15 +61,25 @@ struct rtp_socket {
struct rtp_socket *other_sock;
} proxy;
struct {
- void (*recv_cb)(struct msgb *msg);
+ struct gsm_network *net;
+ u_int32_t callref;
} receive;
};
+ enum rtp_tx_action tx_action;
+ struct {
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+ struct timeval last_tv;
+ } transmit;
};
struct rtp_socket *rtp_socket_create(void);
int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip);
int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port);
int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other);
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref);
int rtp_socket_free(struct rtp_socket *rs);
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame);
#endif /* _RTP_PROXY_H */
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index d59bb9726..8c815f89e 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -59,6 +59,7 @@ enum signal_sms {
/* SS_ABISIP signals */
enum signal_abisip {
S_ABISIP_CRCX_ACK,
+ S_ABISIP_MDCX_ACK,
S_ABISIP_DLCX_IND,
};
@@ -91,6 +92,7 @@ enum signal_lchan {
enum signal_subscr {
S_SUBSCR_ATTACHED,
S_SUBSCR_DETACHED,
+ S_SUBSCR_IDENTITY, /* we've received some identity information */
};
/* SS_SCALL signals */
diff --git a/openbsc/include/openbsc/tlv.h b/openbsc/include/openbsc/tlv.h
index e22601494..24bfa13d6 100644
--- a/openbsc/include/openbsc/tlv.h
+++ b/openbsc/include/openbsc/tlv.h
@@ -119,6 +119,7 @@ static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag,
return buf;
}
+/* 'val' is still in host byte order! */
static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag,
u_int16_t val)
{
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 961a64923..cf9410082 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -26,6 +26,9 @@ struct gsm_trans {
/* reference from MNCC or other application */
u_int32_t callref;
+ /* if traffic channel receive was requested */
+ int tch_recv;
+
union {
struct {
@@ -65,4 +68,9 @@ void trans_free(struct gsm_trans *trans);
int trans_assign_trans_id(struct gsm_subscriber *subscr,
u_int8_t protocol, u_int8_t ti_flag);
+
+/* update all transactions to use a different LCHAN, e.g.
+ * after handover has succeeded */
+int trans_lchan_change(struct gsm_lchan *lchan_old,
+ struct gsm_lchan *lchan_new);
#endif
diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h
index 90535add4..8deb708de 100644
--- a/openbsc/include/openbsc/trau_mux.h
+++ b/openbsc/include/openbsc/trau_mux.h
@@ -46,4 +46,4 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref);
/* send trau from application */
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf);
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame);
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index a41559c06..6e1d25afd 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -12,6 +12,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
talloc_ctx.c system_information.c bitvec.c rest_octets.c \
+ handover_decision.c meas_rep.c \
gprs_ns.c gprs_bssgp.c gprs_llc.c gsm_04_08_gprs.c \
crc24.c gprs_sgsn.c
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 86693d930..2dadbb255 100755..100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -636,7 +636,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &trx->bb_transc.nm_state;
break;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr > bts->num_trx) {
+ if (obj_inst->trx_nr >= bts->num_trx) {
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
}
@@ -672,7 +672,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->bs11.rack.nm_state;
break;
case NM_OC_BS11_ENVABTSE:
- if (obj_inst->trx_nr > ARRAY_SIZE(bts->bs11.envabtse))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse))
return NULL;
nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state;
break;
@@ -683,7 +683,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
nm_state = &bts->gprs.cell.nm_state;
break;
case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
return NULL;
nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state;
break;
@@ -720,7 +720,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = &trx->bb_transc;
break;
case NM_OC_CHANNEL:
- if (obj_inst->trx_nr > bts->num_trx) {
+ if (obj_inst->trx_nr >= bts->num_trx) {
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
return NULL;
}
@@ -739,7 +739,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
obj = &bts->gprs.cell;
break;
case NM_OC_GPRS_NSVC:
- if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
+ if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
return NULL;
obj = &bts->gprs.nsvc[obj_inst->trx_nr];
break;
@@ -1081,8 +1081,8 @@ static int abis_nm_rcvmsg_manuf(struct msgb *mb)
rc = abis_nm_rx_ipacc(mb);
break;
default:
- fprintf(stderr, "don't know how to parse OML for this "
- "BTS type (%u)\n", bts_type);
+ LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
+ "BTS type (%u)\n", bts_type);
rc = 0;
break;
}
@@ -1099,12 +1099,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
/* Various consistency checks */
if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
- fprintf(stderr, "ABIS OML placement 0x%x not supported\n",
+ LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
oh->placement);
return -EINVAL;
}
if (oh->sequence != 0) {
- fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n",
+ LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
oh->sequence);
return -EINVAL;
}
@@ -1112,12 +1112,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
unsigned int l2_len = msg->tail - (u_int8_t *)msgb_l2(msg);
unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr);
if (oh->length + hlen > l2_len) {
- fprintf(stderr, "ABIS OML truncated message (%u > %u)\n",
+ LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n",
oh->length + sizeof(*oh), l2_len);
return -EINVAL;
}
if (oh->length + hlen < l2_len)
- fprintf(stderr, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
+ LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
#endif
msg->l3h = (unsigned char *)oh + sizeof(*oh);
@@ -1130,11 +1130,11 @@ int abis_nm_rcvmsg(struct msgb *msg)
break;
case ABIS_OM_MDISC_MMI:
case ABIS_OM_MDISC_TRAU:
- fprintf(stderr, "unimplemented ABIS OML message discriminator 0x%x\n",
+ LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n",
oh->mdisc);
break;
default:
- fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n",
+ LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n",
oh->mdisc);
return -EINVAL;
}
@@ -1748,7 +1748,8 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
/* As it turns out, the BS-11 has some very peculiar restrictions
* on the channel combinations it allows */
- if (ts->trx->bts->type == GSM_BTS_TYPE_BS11) {
+ switch (ts->trx->bts->type) {
+ case GSM_BTS_TYPE_BS11:
switch (chan_comb) {
case NM_CHANC_TCHHalf:
case NM_CHANC_TCHHalf2:
@@ -1794,6 +1795,83 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
/* FIXME: only one CBCH allowed per cell */
break;
}
+ break;
+ case GSM_BTS_TYPE_NANOBTS:
+ switch (ts->nr) {
+ case 0:
+ if (ts->trx->nr == 0) {
+ /* only on TRX0 */
+ switch (chan_comb) {
+ case NM_CHANC_BCCH:
+ case NM_CHANC_mainBCCH:
+ case NM_CHANC_BCCHComb:
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (chan_comb) {
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ case 1:
+ if (ts->trx->nr == 0) {
+ switch (chan_comb) {
+ case NM_CHANC_SDCCH_CBCH:
+ if (ts->trx->ts[0].nm_chan_comb ==
+ NM_CHANC_mainBCCH)
+ return 0;
+ return -EINVAL;
+ case NM_CHANC_SDCCH:
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_PDCH:
+ return 0;
+ }
+ } else {
+ switch (chan_comb) {
+ case NM_CHANC_SDCCH:
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ switch (chan_comb) {
+ case NM_CHANC_TCHFull:
+ case NM_CHANC_TCHHalf:
+ case NM_CHANC_IPAC_TCHFull_TCHHalf:
+ return 0;
+ case NM_CHANC_IPAC_PDCH:
+ case NM_CHANC_IPAC_TCHFull_PDCH:
+ if (ts->trx->nr == 0)
+ return 0;
+ else
+ return -EINVAL;
+ }
+ break;
+ }
+ return -EINVAL;
+ default:
+ /* unknown BTS type */
+ return 0;
}
return 0;
}
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 1d41d2b5d..72ae9dbb6 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -39,6 +39,7 @@
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/meas_rep.h>
+#include <openbsc/rtp_proxy.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128
@@ -206,32 +207,32 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
if (ts->pchan != GSM_PCHAN_TCH_F &&
ts->pchan != GSM_PCHAN_PDCH &&
ts->pchan != GSM_PCHAN_TCH_F_PDCH)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1e) == 0x02) {
lch_idx = cbits & 0x1; /* TCH/H */
if (ts->pchan != GSM_PCHAN_TCH_H)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x1c) == 0x04) {
lch_idx = cbits & 0x3; /* SDCCH/4 */
if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if ((cbits & 0x18) == 0x08) {
lch_idx = cbits & 0x7; /* SDCCH/8 */
if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
lch_idx = 0;
if (ts->pchan != GSM_PCHAN_CCCH &&
ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
- fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
chan_nr, ts->pchan);
/* FIXME: we should not return first sdcch4 !!! */
} else {
- fprintf(stderr, "unknown chan_nr=0x%02x\n", chan_nr);
+ LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr);
return NULL;
}
@@ -241,7 +242,7 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
}
/* See Table 10.5.25 of GSM04.08 */
-u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
+u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
u_int8_t cbits, chan_nr;
@@ -491,7 +492,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
lchan->tch_mode != GSM48_CMODE_SIGN)
- DEBUGP(DRSL, "unsupported: rsl_mode == signalling, "
+ LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
"but tch_mode != signalling\n");
switch (lchan->type) {
@@ -576,7 +577,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
#endif
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
- u_int8_t ta)
+ u_int8_t ta, u_int8_t ho_ref)
{
struct abis_rsl_dchan_hdr *dh;
struct msgb *msg;
@@ -603,9 +604,9 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
dh->chan_nr = chan_nr;
msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
- /* For compatibility with Phase 1 */
msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
(u_int8_t *) &cm);
+ /* For compatibility with Phase 1 */
msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
(u_int8_t *) &ci);
@@ -616,6 +617,15 @@ int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
}
+ switch (act_type) {
+ case RSL_ACT_INTER_ASYNC:
+ case RSL_ACT_INTER_SYNC:
+ msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref);
+ break;
+ default:
+ break;
+ }
+
msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
@@ -839,7 +849,7 @@ int rsl_data_request(struct msgb *msg, u_int8_t link_id)
struct abis_rsl_rll_hdr *rh;
if (msg->lchan == NULL) {
- fprintf(stderr, "cannot send DATA REQUEST to unknown lchan\n");
+ LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n");
return -EINVAL;
}
@@ -908,6 +918,8 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
if (rslh->ie_chan != RSL_IE_CHAN_NR)
return -EINVAL;
+ msg->lchan->state = LCHAN_S_ACTIVE;
+
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan);
return 0;
@@ -928,6 +940,8 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
+ msg->lchan->state = LCHAN_S_NONE;
+
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan);
lchan_free(msg->lchan);
@@ -940,7 +954,8 @@ static int rsl_rx_conn_fail(struct msgb *msg)
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tp;
- DEBUGPC(DRSL, "CONNECTION FAIL: ");
+ /* FIXME: print which channel */
+ LOGP(DRSL, LOGL_NOTICE, "CONNECTION FAIL: RELEASING\n");
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
@@ -948,8 +963,6 @@ static int rsl_rx_conn_fail(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
- DEBUGPC(DRSL, "RELEASING.\n");
-
/* FIXME: only free it after channel release ACK */
return rsl_rf_chan_release(msg->lchan);
}
@@ -957,14 +970,17 @@ static int rsl_rx_conn_fail(struct msgb *msg)
static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
const char *prefix)
{
- DEBUGPC(DMEAS, "RXL-FULL-%s=%d RXL-SUB-%s=%d ",
- prefix, mru->full.rx_lev, prefix, mru->sub.rx_lev);
+ DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
+ prefix, rxlev2dbm(mru->full.rx_lev),
+ prefix, rxlev2dbm(mru->sub.rx_lev));
DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ",
prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
}
static void print_meas_rep(struct gsm_meas_rep *mr)
{
+ int i;
+
DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", mr->nr);
if (mr->flags & MEAS_REP_F_DL_DTX)
@@ -976,7 +992,7 @@ static void print_meas_rep(struct gsm_meas_rep *mr)
DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset);
if (mr->flags & MEAS_REP_F_MS_L1) {
- DEBUGPC(DMEAS, "L1_MS_PWR=%ddBm ", mr->ms_l1.pwr);
+ DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr);
DEBUGPC(DMEAS, "L1_FPC=%u ",
mr->flags & MEAS_REP_F_FPC ? 1 : 0);
DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta);
@@ -992,20 +1008,31 @@ static void print_meas_rep(struct gsm_meas_rep *mr)
print_meas_rep_uni(&mr->dl, "dl");
DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell);
+ if (mr->num_cell == 7)
+ return;
+ for (i = 0; i < mr->num_cell; i++) {
+ struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+ DEBUGP(DMEAS, "ARFCN=%u BSIC=%u => %d dBm\n", mrc->arfcn, mrc->bsic,
+ rxlev2dbm(mrc->rxlev));
+ }
}
static int rsl_rx_meas_res(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tp;
- struct gsm_meas_rep mr;
+ struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan);
u_int8_t len;
const u_int8_t *val;
int rc;
- memset(&mr, 0, sizeof(mr));
+ /* check if this channel is actually active */
+ /* FIXME: maybe this check should be way more generic/centralized */
+ if (msg->lchan->state != LCHAN_S_ACTIVE)
+ return 0;
- mr.lchan = msg->lchan;
+ memset(mr, 0, sizeof(*mr));
+ mr->lchan = msg->lchan;
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
@@ -1015,44 +1042,44 @@ static int rsl_rx_meas_res(struct msgb *msg)
return -EIO;
/* Mandatory Parts */
- mr.nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR);
+ mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR);
len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
if (len >= 3) {
if (val[0] & 0x40)
- mr.flags |= MEAS_REP_F_DL_DTX;
- mr.ul.full.rx_lev = val[0] & 0x3f;
- mr.ul.sub.rx_lev = val[1] & 0x3f;
- mr.ul.full.rx_qual = val[2]>>3 & 0x7;
- mr.ul.sub.rx_qual = val[2] & 0x7;
+ mr->flags |= MEAS_REP_F_DL_DTX;
+ mr->ul.full.rx_lev = val[0] & 0x3f;
+ mr->ul.sub.rx_lev = val[1] & 0x3f;
+ mr->ul.full.rx_qual = val[2]>>3 & 0x7;
+ mr->ul.sub.rx_qual = val[2] & 0x7;
}
- mr.bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
+ mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
/* Optional Parts */
if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET))
- mr.ms_timing_offset =
+ mr->ms_timing_offset =
*TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET);
if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) {
val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
- mr.flags |= MEAS_REP_F_MS_L1;
- mr.ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3);
+ mr->flags |= MEAS_REP_F_MS_L1;
+ mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3);
if (val[0] & 0x04)
- mr.flags |= MEAS_REP_F_FPC;
- mr.ms_l1.ta = val[1];
+ mr->flags |= MEAS_REP_F_FPC;
+ mr->ms_l1.ta = val[1];
}
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
- rc = gsm48_parse_meas_rep(&mr, msg);
+ rc = gsm48_parse_meas_rep(mr, msg);
if (rc < 0)
return rc;
}
- print_meas_rep(&mr);
+ print_meas_rep(mr);
- dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, &mr);
+ dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, mr);
return 0;
}
@@ -1110,6 +1137,7 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
break;
case RSL_MT_RF_CHAN_REL_ACK:
DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n");
+ msg->lchan->state = LCHAN_S_NONE;
lchan_free(msg->lchan);
break;
case RSL_MT_MODE_MODIFY_ACK:
@@ -1155,7 +1183,7 @@ static int rsl_rx_error_rep(struct msgb *msg)
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
struct tlv_parsed tp;
- DEBUGP(DRSL, "ERROR REPORT ");
+ LOGP(DRSL, LOGL_ERROR, "ERROR REPORT ");
rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh));
@@ -1163,7 +1191,7 @@ static int rsl_rx_error_rep(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
- DEBUGPC(DRSL, "\n");
+ LOGPC(DRSL, LOGL_ERROR, "\n");
return 0;
}
@@ -1183,7 +1211,7 @@ static int abis_rsl_rx_trx(struct msgb *msg)
break;
case RSL_MT_OVERLOAD:
/* indicate CCCH / ACCH / processor overload */
- DEBUGP(DRSL, "TRX: CCCH/ACCH/CPU Overload\n");
+ LOGP(DRSL, LOGL_ERROR, "TRX: CCCH/ACCH/CPU Overload\n");
break;
default:
DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n",
@@ -1236,8 +1264,8 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
/* check availability / allocate channel */
lchan = lchan_alloc(bts, lctype);
if (!lchan) {
- DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n",
- lctype, rqd_ref->ra);
+ DEBUGP(DRSL, "CHAN RQD: no resources for %s 0x%x\n",
+ gsm_lchan_name(lctype), rqd_ref->ra);
/* FIXME: send some kind of reject ?!? */
return -ENOMEM;
}
@@ -1251,7 +1279,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
lchan->tch_mode = GSM48_CMODE_SIGN;
- rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
+ rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0);
/* create IMMEDIATE ASSIGN 04.08 messge */
memset(&ia, 0, sizeof(ia));
@@ -1334,12 +1362,12 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
/* CCCH overloaded, IMM_ASSIGN was dropped */
case RSL_MT_CBCH_LOAD_IND:
/* current load on the CBCH */
- fprintf(stderr, "Unimplemented Abis RSL TRX message type "
- "0x%02x\n", rslh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message "
+ "type 0x%02x\n", rslh->c.msg_type);
break;
default:
- fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n",
- rslh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type "
+ "0x%02x\n", rslh->c.msg_type);
return -EINVAL;
}
@@ -1351,7 +1379,7 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
u_int8_t *rlm_cause = rllh->data;
- DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
+ LOGP(DRLL, LOGL_ERROR, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
@@ -1432,38 +1460,111 @@ static int abis_rsl_rx_rll(struct msgb *msg)
rc = rsl_rx_rll_err_ind(msg);
break;
case RSL_MT_UNIT_DATA_IND:
- DEBUGPC(DRLL, "unimplemented Abis RLL message type 0x%02x\n",
- rllh->c.msg_type);
+ LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message "
+ "type 0x%02x\n", rllh->c.msg_type);
break;
default:
- DEBUGPC(DRLL, "unknown Abis RLL message type 0x%02x\n",
- rllh->c.msg_type);
+ LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message "
+ "type 0x%02x\n", rllh->c.msg_type);
}
return rc;
}
-static u_int8_t ipa_smod_s_for_tch_mode(u_int8_t tch_mode)
+static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
{
- switch (tch_mode) {
+ switch (lchan->tch_mode) {
case GSM48_CMODE_SPEECH_V1:
- return 0x00;
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return 0x00;
+ case GSM_LCHAN_TCH_H:
+ return 0x03;
+ default:
+ break;
+ }
case GSM48_CMODE_SPEECH_EFR:
- return 0x01;
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return 0x01;
+ /* there's no half-rate EFR */
+ default:
+ break;
+ }
case GSM48_CMODE_SPEECH_AMR:
- return 0x02;
- /* FIXME: Type1 half-rate and type3 half-rate */
+ switch (lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ return 0x02;
+ case GSM_LCHAN_TCH_H:
+ return 0x05;
+ default:
+ break;
+ }
+ default:
+ break;
}
- DEBUGPC(DRSL, "Cannot determine ip.access speech mode for "
- "tch_mode == 0x%02x\n", tch_mode);
+ LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for "
+ "tch_mode == 0x%02x\n", lchan->tch_mode);
return 0;
}
/* ip.access specific RSL extensions */
+static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv)
+{
+ struct in_addr ip;
+ u_int16_t port, conn_id;
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) {
+ ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_IP));
+ DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip));
+ lchan->abis_ip.bound_ip = ntohl(ip.s_addr);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) {
+ port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_LOCAL_PORT));
+ port = ntohs(port);
+ DEBUGPC(DRSL, "LOCAL_PORT=%u ", port);
+ lchan->abis_ip.bound_port = port;
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) {
+ conn_id = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_CONN_ID));
+ conn_id = ntohs(conn_id);
+ DEBUGPC(DRSL, "CON_ID=%u ", conn_id);
+ lchan->abis_ip.conn_id = conn_id;
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) {
+ lchan->abis_ip.rtp_payload2 =
+ *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2);
+ DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
+ lchan->abis_ip.rtp_payload2);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) {
+ lchan->abis_ip.speech_mode =
+ *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE);
+ DEBUGPC(DRSL, "speech_mode=0x%02x ",
+ lchan->abis_ip.speech_mode);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) {
+ ip.s_addr = *((u_int32_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_IP));
+ DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip));
+ lchan->abis_ip.connect_ip = ntohl(ip.s_addr);
+ }
+
+ if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) {
+ port = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT));
+ port = ntohs(port);
+ DEBUGPC(DRSL, "REMOTE_PORT=%u ", port);
+ lchan->abis_ip.connect_port = port;
+ }
+}
+
int rsl_ipacc_crcx(struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_msgb_alloc();
struct abis_rsl_dchan_hdr *dh;
- u_int8_t speech_mode;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
init_dchan_hdr(dh, RSL_MT_IPAC_CRCX);
@@ -1471,12 +1572,12 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan)
dh->chan_nr = lchan2chan_nr(lchan);
/* 0x1- == receive-only, 0x-1 == EFR codec */
- speech_mode = 0x10 | ipa_smod_s_for_tch_mode(lchan->tch_mode);
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, speech_mode);
+ lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan);
+ msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_BIND "
"speech_mode=0x%02x\n", gsm_ts_name(lchan->ts),
- dh->chan_nr, speech_mode);
+ dh->chan_nr, lchan->abis_ip.speech_mode);
msg->trx = lchan->ts->trx;
@@ -1484,12 +1585,11 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan)
}
int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
- u_int16_t conn_id, u_int8_t rtp_payload2)
+ u_int8_t rtp_payload2)
{
struct msgb *msg = rsl_msgb_alloc();
struct abis_rsl_dchan_hdr *dh;
- u_int8_t *att_f8, *att_ip, *att_port;
- u_int8_t speech_mode;
+ u_int32_t *att_ip;
struct in_addr ia;
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
@@ -1497,34 +1597,26 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
dh->chan_nr = lchan2chan_nr(lchan);
+ /* we need to store these now as MDCX_ACK does not return them :( */
+ lchan->abis_ip.rtp_payload2 = rtp_payload2;
+ lchan->abis_ip.connect_port = port;
+ lchan->abis_ip.connect_ip = ip;
+
/* 0x0- == both directions, 0x-1 == EFR codec */
- speech_mode = 0x00 | ipa_smod_s_for_tch_mode(lchan->tch_mode);
+ lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan);
ia.s_addr = htonl(ip);
DEBUGP(DRSL, "channel=%s chan_nr=0x%02x IPAC_MDCX "
"IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d speech_mode=0x%02x\n",
- gsm_ts_name(lchan->ts), dh->chan_nr,
- inet_ntoa(ia), port, rtp_payload2, conn_id, speech_mode);
-
- att_f8 = msgb_put(msg, sizeof(conn_id)+1);
- att_f8[0] = RSL_IE_IPAC_CONN_ID;
- att_f8[1] = conn_id >> 8;
- att_f8[2] = conn_id & 0xff;
-
- att_ip = msgb_put(msg, sizeof(ip)+1);
- att_ip[0] = RSL_IE_IPAC_REMOTE_IP;
- att_ip[1] = ip >> 24;
- att_ip[2] = ip >> 16;
- att_ip[3] = ip >> 8;
- att_ip[4] = ip & 0xff;
- //att_ip[4] = 11;
-
- att_port = msgb_put(msg, sizeof(port)+1);
- att_port[0] = RSL_IE_IPAC_REMOTE_PORT;
- att_port[1] = port >> 8;
- att_port[2] = port & 0xff;
-
- msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, speech_mode);
+ gsm_ts_name(lchan->ts), dh->chan_nr, inet_ntoa(ia), port,
+ rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode);
+
+ msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id);
+ msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP);
+ att_ip = (u_int32_t *) msgb_put(msg, sizeof(ip));
+ *att_ip = ia.s_addr;
+ msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port);
+ msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode);
if (rtp_payload2)
msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2);
@@ -1533,6 +1625,20 @@ int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port,
return abis_rsl_sendmsg(msg);
}
+/* tell BTS to connect RTP stream to our local RTP socket */
+int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan)
+{
+ struct rtp_socket *rs = lchan->abis_ip.rtp_socket;
+ int rc;
+
+ rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
+ ntohs(rs->rtp.sin_local.sin_port),
+ /* FIXME: use RTP payload of bound socket, not BTS*/
+ lchan->abis_ip.rtp_payload2);
+
+ return rc;
+}
+
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
{
struct msgb *msg = rsl_msgb_alloc();
@@ -1555,9 +1661,7 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
- struct gsm_bts_trx_ts *ts = msg->lchan->ts;
- struct in_addr ip;
- u_int16_t port, attr_f8;
+ struct gsm_lchan *lchan = msg->lchan;
/* the BTS has acknowledged a local bind, it now tells us the IP
* address and port number to which it has bound the given logical
@@ -1567,37 +1671,62 @@ static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg)
if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) ||
!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) ||
!TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) {
- DEBUGPC(DRSL, "mandatory IE missing");
+ LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing");
return -EINVAL;
}
- ip.s_addr = *((u_int32_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_IP));
- port = *((u_int16_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_PORT));
- attr_f8 = *((u_int16_t *) TLVP_VAL(&tv, 0xf8));
- DEBUGPC(DRSL, "IP=%s PORT=%d CONN_ID=%d ",
- inet_ntoa(ip), ntohs(port), ntohs(attr_f8));
+ ipac_parse_rtp(lchan, &tv);
- if (TLVP_PRESENT(&tv, RSL_IE_IPAC_RTP_PAYLOAD2)) {
- ts->abis_ip.rtp_payload2 =
- *TLVP_VAL(&tv, RSL_IE_IPAC_RTP_PAYLOAD2);
- DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
- ts->abis_ip.rtp_payload2);
- }
+ /* in case we don't use direct BTS-to-BTS RTP */
+ if (!ipacc_rtp_direct) {
+ int rc;
+ /* the BTS has successfully bound a TCH to a local ip/port,
+ * which means we can connect our UDP socket to it */
+ if (lchan->abis_ip.rtp_socket) {
+ rtp_socket_free(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket = NULL;
+ }
- /* update our local information about this TS */
- ts->abis_ip.bound_ip = ntohl(ip.s_addr);
- ts->abis_ip.bound_port = ntohs(port);
- ts->abis_ip.conn_id = ntohs(attr_f8);
+ lchan->abis_ip.rtp_socket = rtp_socket_create();
+ if (!lchan->abis_ip.rtp_socket)
+ goto out_err;
+
+ rc = rtp_socket_connect(lchan->abis_ip.rtp_socket,
+ lchan->abis_ip.bound_ip,
+ lchan->abis_ip.bound_port);
+ if (rc < 0)
+ goto out_err;
+ }
dispatch_signal(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan);
return 0;
+out_err:
+ return -EIO;
+}
+
+static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tv;
+ struct gsm_lchan *lchan = msg->lchan;
+
+ /* the BTS has acknowledged a remote connect request and
+ * it now tells us the IP address and port number to which it has
+ * connected the given logical channel */
+
+ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ ipac_parse_rtp(lchan, &tv);
+ dispatch_signal(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan);
+
+ return 0;
}
static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
struct tlv_parsed tv;
+ struct gsm_lchan *lchan = msg->lchan;
rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
@@ -1605,6 +1734,12 @@ static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)
print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE),
TLVP_LEN(&tv, RSL_IE_CAUSE));
+ /* the BTS tells us a RTP stream has been disconnected */
+ if (lchan->abis_ip.rtp_socket) {
+ rtp_socket_free(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket = NULL;
+ }
+
dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan);
return 0;
@@ -1632,6 +1767,7 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
case RSL_MT_IPAC_MDCX_ACK:
/* the BTS tells us that a connect operation was successful */
DEBUGPC(DRSL, "IPAC_MDCX_ACK ");
+ rc = abis_rsl_rx_ipacc_mdcx_ack(msg);
break;
case RSL_MT_IPAC_MDCX_NACK:
/* somehow the BTS was unable to connect the lchan to a remote
@@ -1643,7 +1779,8 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
rc = abis_rsl_rx_ipacc_dlcx_ind(msg);
break;
default:
- DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type);
+ LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x",
+ rllh->c.msg_type);
break;
}
DEBUGPC(DRSL, "\n");
@@ -1672,15 +1809,15 @@ int abis_rsl_rcvmsg(struct msgb *msg)
rc = abis_rsl_rx_trx(msg);
break;
case ABIS_RSL_MDISC_LOC:
- fprintf(stderr, "unimplemented RSL msg disc 0x%02x\n",
+ LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n",
rslh->msg_discr);
break;
case ABIS_RSL_MDISC_IPACCESS:
rc = abis_rsl_rx_ipacc(msg);
break;
default:
- fprintf(stderr, "unknown RSL message discriminator 0x%02x\n",
- rslh->msg_discr);
+ LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator "
+ "0x%02x\n", rslh->msg_discr);
return -EINVAL;
}
msgb_free(msg);
diff --git a/openbsc/src/bitvec.c b/openbsc/src/bitvec.c
index ac2554475..d6f5679cf 100644
--- a/openbsc/src/bitvec.c
+++ b/openbsc/src/bitvec.c
@@ -35,15 +35,10 @@ static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
return bytenum;
}
-int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
- enum bit_value bit)
+/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */
+static u_int8_t bitval2mask(enum bit_value bit, u_int8_t bitnum)
{
- unsigned int bytenum = bytenum_from_bitnum(bitnr);
- unsigned int bitnum = 7 - (bitnr % 8);
- u_int8_t bitval;
-
- if (bytenum >= bv->data_len)
- return -EINVAL;
+ int bitval;
switch (bit) {
case ZERO:
@@ -59,18 +54,68 @@ int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum));
break;
default:
+ return 0;
+ }
+ return bitval;
+}
+
+/* check if the bit is 0 or 1 for a given position inside a bitvec */
+enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ u_int8_t bitval;
+
+ if (bytenum >= bv->data_len)
return -EINVAL;
+
+ bitval = bitval2mask(ONE, bitnum);
+
+ if (bv->data[bytenum] & bitval)
+ return ONE;
+
+ return ZERO;
+}
+
+/* get the Nth set bit inside the bit vector */
+unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n)
+{
+ unsigned int i, k = 0;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i) == ONE) {
+ k++;
+ if (k == n)
+ return i;
+ }
}
+ return 0;
+}
+
+/* set the bit at a given position inside a bitvec */
+int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
+ enum bit_value bit)
+{
+ unsigned int bytenum = bytenum_from_bitnum(bitnr);
+ unsigned int bitnum = 7 - (bitnr % 8);
+ u_int8_t bitval;
+
+ if (bytenum >= bv->data_len)
+ return -EINVAL;
+
/* first clear the bit */
- bv->data[bytenum] &= ~(1 << bitnum);
+ bitval = bitval2mask(ONE, bitnum);
+ bv->data[bytenum] &= ~bitval;
/* then set it to desired value */
+ bitval = bitval2mask(bit, bitnum);
bv->data[bytenum] |= bitval;
return 0;
}
+/* set the next bit inside a bitvec */
int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
{
int rc;
@@ -82,6 +127,7 @@ int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
return rc;
}
+/* set multiple bits (based on array of bitvals) at current pos */
int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
{
int i, rc;
@@ -95,6 +141,7 @@ int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
return 0;
}
+/* set multiple bits (based on numeric value) at current pos */
int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
{
int i, rc;
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index ee37a6115..a9a5d372f 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -41,7 +41,6 @@
struct gsm_network *bsc_gsmnet = 0;
static const char *database_name = "hlr.sqlite3";
static const char *config_file = "openbsc.cfg";
-extern int ipacc_rtp_direct;
extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *),
const char *cfg_file);
@@ -73,9 +72,9 @@ static void print_help()
printf(" -s --disable-color\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -l --database db-name The database to use\n");
- printf(" -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\n");
printf(" -p --pcap file The filename of the pcap file\n");
printf(" -T --timestamp Prefix every log line with a timestamp\n");
+ printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n");
}
static void handle_options(int argc, char** argv)
@@ -89,7 +88,6 @@ static void handle_options(int argc, char** argv)
{"disable-color", 0, 0, 's'},
{"database", 1, 0, 'l'},
{"authorize-everyone", 0, 0, 'a'},
- {"reject-cause", 1, 0, 'r'},
{"pcap", 1, 0, 'p'},
{"timestamp", 0, 0, 'T'},
{"rtp-proxy", 0, 0, 'P'},
@@ -118,9 +116,6 @@ static void handle_options(int argc, char** argv)
case 'c':
config_file = strdup(optarg);
break;
- case 'r':
- gsm0408_set_reject_cause(atoi(optarg));
- break;
case 'p':
create_pcap_file(optarg);
break;
@@ -167,6 +162,7 @@ int main(int argc, char **argv)
talloc_ctx_init();
on_dso_load_token();
on_dso_load_rrlp();
+ on_dso_load_ho_dec();
/* parse options */
handle_options(argc, argv);
@@ -193,6 +189,7 @@ int main(int argc, char **argv)
signal(SIGINT, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
+ signal(SIGPIPE, SIG_IGN);
while (1) {
bsc_upqueue(bsc_gsmnet);
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index c00b78f77..7fbac0a10 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -37,7 +37,6 @@
/* global pointer to the gsm network data structure */
extern struct gsm_network *bsc_gsmnet;
-extern int ipacc_rtp_direct;
static void patch_nm_tables(struct gsm_bts *bts);
@@ -561,7 +560,7 @@ static int sw_activ_rep(struct msgb *mb)
static int oml_msg_nack(u_int8_t mt)
{
if (mt == NM_MT_SET_BTS_ATTR_NACK) {
- fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
+ LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. "
"Was the bts type and frequency properly specified?\n");
exit(-1);
}
@@ -664,7 +663,7 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
trx->nominal_power = 23;
break;
default:
- fprintf(stderr, "Unsupported nanoBTS GSM band %s\n",
+ LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n",
gsm_band_name(trx->bts->band));
break;
}
@@ -683,6 +682,7 @@ static void nm_reconfig_bts(struct gsm_bts *bts)
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
+ patch_nm_tables(bts);
abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts));
abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
@@ -728,7 +728,7 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
static void bootstrap_om(struct gsm_bts *bts)
{
- fprintf(stdout, "bootstrapping OML for BTS %u\n", bts->nr);
+ LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
switch (bts->type) {
case GSM_BTS_TYPE_BS11:
@@ -738,13 +738,13 @@ static void bootstrap_om(struct gsm_bts *bts)
bootstrap_om_nanobts(bts);
break;
default:
- fprintf(stderr, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
+ LOGP(DNM, LOGL_ERROR, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
}
}
static int shutdown_om(struct gsm_bts *bts)
{
- fprintf(stdout, "shutting down OML for BTS %u\n", bts->nr);
+ LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr);
/* stop sending event reports */
abis_nm_event_reports(bts, 0);
@@ -780,29 +780,42 @@ static int set_system_infos(struct gsm_bts_trx *trx)
{
int i, rc;
u_int8_t si_tmp[23];
+ struct gsm_bts *bts = trx->bts;
+
+ bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
+ ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
+ bts->si_common.cell_sel_par.neci = bts->network->neci;
if (trx == trx->bts->c0) {
for (i = 1; i <= 4; i++) {
rc = gsm_generate_si(si_tmp, trx->bts, i);
if (rc < 0)
goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
}
- }
#ifdef GPRS
- rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
- if (rc < 0)
- goto err_out;
- rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc);
+ i = 13;
+ rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
+ if (rc < 0)
+ goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
+ rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc);
#endif
- rc = gsm_generate_si(si_tmp, trx->bts, 5);
+ }
+
+ i = 5;
+ rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_5);
if (rc < 0)
goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si_tmp, rc);
- rc = gsm_generate_si(si_tmp, trx->bts, 6);
+ i = 6;
+ rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_6);
if (rc < 0)
goto err_out;
+ DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si_tmp, rc);
#ifdef GPRS
@@ -811,9 +824,9 @@ static int set_system_infos(struct gsm_bts_trx *trx)
return 0;
err_out:
- fprintf(stderr, "Cannot generate SI for BTS %u, most likely "
+ LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely "
"a problem with neighbor cell list generation\n",
- trx->bts->nr);
+ i, trx->bts->nr);
return rc;
}
@@ -867,7 +880,7 @@ static void patch_nm_tables(struct gsm_bts *bts)
static void bootstrap_rsl(struct gsm_bts_trx *trx)
{
- fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) "
+ LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) "
"using MCC=%u MNC=%u BSIC=%u TSC=%u\n",
trx->bts->nr, trx->nr, bsc_gsmnet->country_code,
bsc_gsmnet->network_code, trx->bts->bsic, trx->bts->tsc);
@@ -890,7 +903,7 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
}
break;
case EVT_E1_TEI_DN:
- fprintf(stderr, "Lost some E1 TEI link\n");
+ LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n");
/* FIXME: deal with TEI or L1 link loss */
break;
default:
@@ -903,33 +916,39 @@ static int bootstrap_bts(struct gsm_bts *bts)
switch (bts->band) {
case GSM_BAND_1800:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
- fprintf(stderr, "GSM1800 channel must be between 512-885.\n");
+ LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n");
return -EINVAL;
}
break;
case GSM_BAND_1900:
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
- fprintf(stderr, "GSM1900 channel must be between 512-810.\n");
+ LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n");
return -EINVAL;
}
break;
case GSM_BAND_900:
if (bts->c0->arfcn < 1 || bts->c0->arfcn > 124) {
- fprintf(stderr, "GSM900 channel must be between 1-124.\n");
+ LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124.\n");
return -EINVAL;
}
break;
default:
- fprintf(stderr, "Unsupported frequency band.\n");
+ LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n");
return -EINVAL;
}
+ if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL &&
+ !bts->si_common.rach_control.cell_bar)
+ LOGP(DNM, LOG_ERROR, "\nWARNING: You are running an 'accept-all' "
+ "network on a BTS that is not barred. This "
+ "configuration is likely to interfere with production "
+ "GSM networks and should only be used in a RF "
+ "shielded environment such as a faraday cage!\n\n");
+
/* Control Channel Description */
bts->si_common.chan_desc.att = 1;
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
- if (bts->cell_barred)
- bts->si_common.rach_control.cell_bar = 1;
/* T3212 is set from vty/config */
/* some defaults for our system information */
@@ -942,12 +961,7 @@ static int bootstrap_bts(struct gsm_bts *bts)
bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */
bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
- bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
- ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
- bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
- bts->si_common.cell_sel_par.rxlev_acc_min = 0;
bts->si_common.cell_sel_par.acs = 0;
- bts->si_common.cell_sel_par.neci = bts->network->neci;
bts->si_common.ncc_permitted = 0xff;
@@ -973,7 +987,7 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, int, void *),
telnet_init(bsc_gsmnet, 4242);
rc = vty_read_config_file(config_file);
if (rc < 0) {
- fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
+ LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index 7ba679c87..c42b60b46 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -203,9 +203,14 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
break;
case GSM_LCHAN_TCH_H:
lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H);
+ /* If we don't have TCH/H available, fall-back to TCH/F */
+ if (!lchan) {
+ lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
+ type = GSM_LCHAN_TCH_F;
+ }
break;
default:
- fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
+ LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
}
if (lchan) {
@@ -230,6 +235,8 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
/* Free a logical channel */
void lchan_free(struct gsm_lchan *lchan)
{
+ int i;
+
lchan->type = GSM_LCHAN_NONE;
if (lchan->subscr) {
subscr_put(lchan->subscr);
@@ -244,6 +251,16 @@ void lchan_free(struct gsm_lchan *lchan)
/* stop the timer */
bsc_del_timer(&lchan->release_timer);
+ bsc_del_timer(&lchan->T3101);
+
+ /* clear cached measuement reports */
+ lchan->meas_rep_idx = 0;
+ for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) {
+ lchan->meas_rep[i].flags = 0;
+ lchan->meas_rep[i].nr = 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
+ lchan->neigh_meas[i].arfcn = 0;
/* FIXME: ts_free() the timeslot, if we're the last logical
* channel using it */
@@ -262,9 +279,9 @@ int lchan_auto_release(struct gsm_lchan *lchan)
}
/* spoofed? message */
- if (lchan->use_count < 0) {
- DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count);
- }
+ if (lchan->use_count < 0)
+ LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
+ lchan->use_count);
DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
rsl_release_request(lchan, 0);
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 369505a2c..d85386548 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -265,7 +265,7 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
{
dbi_result result;
const char *string;
- unsigned int cm1;
+ unsigned char cm1;
const unsigned char *cm2, *cm3;
struct gsm_equipment *equip = &subscr->equipment;
@@ -769,8 +769,9 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
result = dbi_conn_queryf(conn,
"SELECT * FROM SMS,Subscriber "
"WHERE sms.id >= %llu AND sms.sent is NULL "
+ "AND sms.receiver_id = subscriber.id "
"AND subscriber.lac > 0 "
- "ORDER BY id",
+ "ORDER BY sms.id LIMIT 1",
min_id);
if (!result)
return NULL;
@@ -787,7 +788,7 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
return sms;
}
-/* retrieve the next unsent SMS with ID >= min_id */
+/* retrieve the next unsent SMS for a given subscriber */
struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
{
dbi_result result;
@@ -796,8 +797,9 @@ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
result = dbi_conn_queryf(conn,
"SELECT * FROM SMS,Subscriber "
"WHERE sms.receiver_id = %llu AND sms.sent is NULL "
+ "AND sms.receiver_id = subscriber.id "
"AND subscriber.lac > 0 "
- "ORDER BY id",
+ "ORDER BY sms.id LIMIT 1",
subscr->id);
if (!result)
return NULL;
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index 676ec3a33..049dc322a 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -61,6 +61,7 @@ static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
+ DEBUG_CATEGORY(DHO, "DHO", "", "")
};
static int use_color = 1;
diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c
index 62bacf2ca..6a2abd85b 100644
--- a/openbsc/src/e1_config.c
+++ b/openbsc/src/e1_config.c
@@ -10,6 +10,7 @@
#include <openbsc/misdn.h>
#include <openbsc/ipaccess.h>
#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
#define SAPI_L2ML 0
#define SAPI_OML 62
@@ -25,7 +26,7 @@ int e1_reconfig_ts(struct gsm_bts_trx_ts *ts)
struct e1inp_line *line;
struct e1inp_ts *e1_ts;
- printf("e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
+ DEBUGP(DMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr);
if (!e1_link->e1_ts)
return 0;
@@ -87,7 +88,7 @@ int e1_reconfig_bts(struct gsm_bts *bts)
struct e1inp_sign_link *oml_link;
struct gsm_bts_trx *trx;
- printf("e1_reconfig_bts(%u)\n", bts->nr);
+ DEBUGP(DMI, "e1_reconfig_bts(%u)\n", bts->nr);
if (!e1_link->e1_ts)
return -EINVAL;
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index 15495fbbb..083d8f8de 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -235,7 +235,7 @@ int abis_rsl_sendmsg(struct msgb *msg)
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->rsl_link) {
- fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
+ LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL\n");
talloc_free(msg);
return -EINVAL;
}
@@ -264,7 +264,7 @@ int _abis_nm_sendmsg(struct msgb *msg)
msg->l2h = msg->data;
if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
- fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
+ LOGP(DRSL, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n");
return -EINVAL;
}
@@ -306,7 +306,7 @@ int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
subch_demux_init(&ts->trau.demux);
break;
default:
- fprintf(stderr, "unsupported E1 timeslot type %u\n",
+ LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n",
ts->type);
return -EINVAL;
}
@@ -431,7 +431,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
link = e1inp_lookup_sign_link(ts, tei, sapi);
if (!link) {
- fprintf(stderr, "didn't find signalling link for "
+ LOGP(DMI, LOGL_ERROR, "didn't find signalling link for "
"tei %d, sapi %d\n", tei, sapi);
return -EINVAL;
}
@@ -446,7 +446,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
break;
default:
ret = -EINVAL;
- fprintf(stderr, "unknown link type %u\n", link->type);
+ LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type);
break;
}
break;
@@ -455,7 +455,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
break;
default:
ret = -EINVAL;
- fprintf(stderr, "unknown TS type %u\n", ts->type);
+ LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
break;
}
@@ -492,7 +492,7 @@ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
msgb_put(msg, 40);
break;
default:
- fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
+ LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
return NULL;
}
return msg;
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index f9eec59b3..1fcca349f 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -32,6 +32,7 @@
#include <openbsc/db.h>
#include <openbsc/msgb.h>
+#include <openbsc/bitvec.h>
#include <openbsc/tlv.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
@@ -56,8 +57,6 @@
void *tall_locop_ctx;
-extern int ipacc_rtp_direct;
-
static const struct tlv_definition rsl_att_tlvdef = {
.def = {
[GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
@@ -171,6 +170,8 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
struct gsm48_hdr *gh = msgb_l3(msg);
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
u_int8_t *data = gh->data;
+ struct gsm_bts *bts = msg->lchan->ts->trx->bts;
+ struct bitvec *nbv = &bts->si_common.neigh_list;
if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
return -EINVAL;
@@ -187,45 +188,47 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
rep->dl.full.rx_qual = (data[3] >> 4) & 0x7;
rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7;
- rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2);
- if (rep->num_cell < 1)
+ rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2);
+ if (rep->num_cell < 1 || rep->num_cell > 6)
return 0;
/* an encoding nightmare in perfection */
- rep->cell[0].rxlev = data[4] & 0x3f;
- rep->cell[0].bcch_freq = data[5] >> 2;
- rep->cell[0].bsic = ((data[5] & 0x03) << 3) | (data[6] >> 5);
+ rep->cell[0].rxlev = data[3] & 0x3f;
+ rep->cell[0].arfcn = bitvec_get_nth_set_bit(nbv, data[4] >> 2);
+ rep->cell[0].bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
if (rep->num_cell < 2)
return 0;
- rep->cell[1].rxlev = ((data[6] & 0x1f) << 1) | (data[7] >> 7);
- rep->cell[1].bcch_freq = (data[7] >> 2) & 0x1f;
- rep->cell[1].bsic = ((data[7] & 0x03) << 4) | (data[8] >> 4);
+ rep->cell[1].rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
+ rep->cell[1].arfcn = bitvec_get_nth_set_bit(nbv, (data[6] >> 2) & 0x1f);
+ rep->cell[1].bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
if (rep->num_cell < 3)
return 0;
- rep->cell[2].rxlev = ((data[8] & 0x0f) << 2) | (data[9] >> 6);
- rep->cell[2].bcch_freq = (data[9] >> 1) & 0x1f;
- rep->cell[2].bsic = ((data[9] & 0x01) << 6) | (data[10] >> 3);
+ rep->cell[2].rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
+ rep->cell[2].arfcn = bitvec_get_nth_set_bit(nbv, (data[8] >> 1) & 0x1f);
+ rep->cell[2].bsic = ((data[8] & 0x01) << 6) | (data[9] >> 3);
if (rep->num_cell < 4)
return 0;
- rep->cell[3].rxlev = ((data[10] & 0x07) << 3) | (data[11] >> 5);
- rep->cell[3].bcch_freq = data[11] & 0x1f;
- rep->cell[3].bsic = data[12] >> 2;
+ rep->cell[3].rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
+ rep->cell[3].arfcn = bitvec_get_nth_set_bit(nbv, data[10] & 0x1f);
+ rep->cell[3].bsic = data[11] >> 2;
if (rep->num_cell < 5)
return 0;
- rep->cell[4].rxlev = ((data[12] & 0x03) << 4) | (data[13] >> 4);
- rep->cell[4].bcch_freq = ((data[13] & 0xf) << 1) | (data[14] >> 7);
- rep->cell[4].bsic = (data[14] >> 1) & 0x3f;
+ rep->cell[4].rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
+ rep->cell[4].arfcn = bitvec_get_nth_set_bit(nbv,
+ ((data[12] & 0xf) << 1) | (data[13] >> 7));
+ rep->cell[4].bsic = (data[13] >> 1) & 0x3f;
if (rep->num_cell < 6)
return 0;
- rep->cell[5].rxlev = ((data[14] & 0x01) << 5) | (data[15] >> 3);
- rep->cell[5].bcch_freq = ((data[15] & 0x07) << 2) | (data[16] >> 6);
- rep->cell[5].bsic = data[16] & 0x3f;
+ rep->cell[5].rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
+ rep->cell[5].arfcn = bitvec_get_nth_set_bit(nbv,
+ ((data[14] & 0x07) << 2) | (data[15] >> 6));
+ rep->cell[5].bsic = data[15] & 0x3f;
return 0;
}
@@ -241,12 +244,6 @@ struct gsm_lai {
u_int16_t lac;
};
-static int reject_cause = 0;
-void gsm0408_set_reject_cause(int cause)
-{
- reject_cause = cause;
-}
-
static u_int32_t new_callref = 0x80000001;
static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
@@ -305,11 +302,18 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
db_subscriber_alloc_tmsi(lchan->subscr);
release_loc_updating_req(lchan);
rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi);
+ if (lchan->ts->trx->bts->network->send_mm_info) {
+ /* send MM INFO with network name */
+ rc = gsm48_tx_mm_info(msg->lchan);
+ }
+
/* call subscr_update after putting the loc_upd_acc
* in the transmit queue, since S_SUBSCR_ATTACHED might
* trigger further action like SMS delivery */
subscr_update(lchan->subscr, msg->trx->bts,
GSM_SUBSCRIBER_UPDATE_ATTACHED);
+ /* try to close channel ASAP */
+ lchan_auto_release(lchan);
return rc;
}
@@ -440,18 +444,29 @@ static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
bcap->coding = (lv[1] & 0x10) >> 4;
bcap->radio = (lv[1] & 0x60) >> 5;
- i = 1;
- s = 0;
- while(!(lv[i] & 0x80)) {
- i++; /* octet 3a etc */
- if (in_len < i)
- return 0;
- bcap->speech_ver[s++] = lv[i] & 0x0f;
- bcap->speech_ver[s] = -1; /* end of list */
- if (i == 2) /* octet 3a */
- bcap->speech_ctm = (lv[i] & 0x20) >> 5;
- if (s == 7) /* maximum speech versions + end of list */
- return 0;
+ if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+ i = 1;
+ s = 0;
+ while(!(lv[i] & 0x80)) {
+ i++; /* octet 3a etc */
+ if (in_len < i)
+ return 0;
+ bcap->speech_ver[s++] = lv[i] & 0x0f;
+ bcap->speech_ver[s] = -1; /* end of list */
+ if (i == 2) /* octet 3a */
+ bcap->speech_ctm = (lv[i] & 0x20) >> 5;
+ if (s == 7) /* maximum speech versions + end of list */
+ return 0;
+ }
+ } else {
+ i = 1;
+ while (!(lv[i] & 0x80)) {
+ i++; /* octet 3a etc */
+ if (in_len < i)
+ return 0;
+ /* ignore them */
+ }
+ /* FIXME: implement OCTET 4+ parsing */
}
return 0;
@@ -462,21 +477,24 @@ static int encode_bearer_cap(struct msgb *msg, int lv_only,
const struct gsm_mncc_bearer_cap *bcap)
{
u_int8_t lv[32 + 1];
- int i, s;
+ int i = 1, s;
lv[1] = bcap->transfer;
lv[1] |= bcap->mode << 3;
lv[1] |= bcap->coding << 4;
lv[1] |= bcap->radio << 5;
- i = 1;
- for (s = 0; bcap->speech_ver[s] >= 0; s++) {
- i++; /* octet 3a etc */
- lv[i] = bcap->speech_ver[s];
- if (i == 2) /* octet 3a */
- lv[i] |= bcap->speech_ctm << 5;
+ if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
+ for (s = 0; bcap->speech_ver[s] >= 0; s++) {
+ i++; /* octet 3a etc */
+ lv[i] = bcap->speech_ver[s];
+ if (i == 2) /* octet 3a */
+ lv[i] |= bcap->speech_ctm << 5;
+ }
+ lv[i] |= 0x80; /* last IE of octet 3 etc */
+ } else {
+ /* FIXME: implement OCTET 4+ encoding */
}
- lv[i] |= 0x80; /* last IE of octet 3 etc */
lv[0] = i;
if (lv_only)
@@ -891,7 +909,6 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
struct gsm48_hdr *gh;
struct gsm48_loc_area_id *lai;
u_int8_t *mid;
- int ret;
msg->lchan = lchan;
@@ -908,12 +925,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
- ret = gsm48_sendmsg(msg, NULL);
-
- /* send MM INFO with network name */
- ret = gsm48_tx_mm_info(lchan);
-
- return ret;
+ return gsm48_sendmsg(msg, NULL);
}
/* Transmit Chapter 9.2.10 Identity Request */
@@ -947,6 +959,8 @@ static int mm_rx_id_resp(struct msgb *msg)
DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
mi_type, mi_string);
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
+
switch (mi_type) {
case GSM_MI_TYPE_IMSI:
/* look up subscriber based on IMSI, create if not found */
@@ -978,9 +992,10 @@ static int mm_rx_id_resp(struct msgb *msg)
static void loc_upd_rej_cb(void *data)
{
struct gsm_lchan *lchan = data;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
release_loc_updating_req(lchan);
- gsm0408_loc_upd_rej(lchan, reject_cause);
+ gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
lchan_auto_release(lchan);
}
@@ -1026,6 +1041,8 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
DEBUGPC(DMM, "mi_type=0x%02x MI(%s) type=%s ", mi_type, mi_string,
lupd_name(lu->type));
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len);
+
/*
* Pseudo Spoof detection: Just drop a second/concurrent
* location updating request.
@@ -1309,6 +1326,8 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n",
req->cm_service_type, mi_type, mi_string);
+ dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
+
if (is_siemens_bts(bts))
send_siemens_mrpci(msg->lchan, classmark2-1);
@@ -1322,7 +1341,9 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
if (!msg->lchan->subscr)
msg->lchan->subscr = subscr;
- else if (msg->lchan->subscr != subscr) {
+ else if (msg->lchan->subscr == subscr)
+ subscr_put(subscr); /* lchan already has a ref, don't need another one */
+ else {
DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
subscr_put(subscr);
}
@@ -1379,6 +1400,12 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
} else
DEBUGP(DMM, "Unknown Subscriber ?!?\n");
+ /* FIXME: iterate over all transactions and release them,
+ * imagine an IMSI DETACH happening during an active call! */
+
+ /* subscriber is detached: should we release lchan? */
+ lchan_auto_release(msg->lchan);
+
return 0;
}
@@ -1535,13 +1562,13 @@ static int gsm48_rx_rr_status(struct msgb *msg)
static int gsm48_rx_rr_meas_rep(struct msgb *msg)
{
- static struct gsm_meas_rep meas_rep;
+ struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan);
/* This shouldn't actually end up here, as RSL treats
* L3 Info of 08.58 MEASUREMENT REPORT different by calling
* directly into gsm48_parse_meas_rep */
DEBUGP(DMEAS, "DIRECT GSM48 MEASUREMENT REPORT ?!? ");
- gsm48_parse_meas_rep(&meas_rep, msg);
+ gsm48_parse_meas_rep(meas_rep, msg);
return 0;
}
@@ -1564,7 +1591,7 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
}
/* Chapter 9.1.16 Handover complete */
-static gsm48_rx_rr_ho_compl(struct msgb *msg)
+static int gsm48_rx_rr_ho_compl(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -1578,7 +1605,7 @@ static gsm48_rx_rr_ho_compl(struct msgb *msg)
}
/* Chapter 9.1.17 Handover Failure */
-static gsm48_rx_rr_ho_fail(struct msgb *msg)
+static int gsm48_rx_rr_ho_fail(struct msgb *msg)
{
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -1839,13 +1866,16 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
return 0;
}
+static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable);
+
/* some other part of the code sends us a signal */
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct gsm_lchan *lchan = signal_data;
- struct gsm_bts_trx_ts *ts;
int rc;
+ struct gsm_network *net;
+ struct gsm_trans *trans;
if (subsys != SS_ABISIP)
return 0;
@@ -1854,56 +1884,21 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
if (ipacc_rtp_direct)
return 0;
- ts = lchan->ts;
-
switch (signal) {
case S_ABISIP_CRCX_ACK:
- /* the BTS has successfully bound a TCH to a local ip/port,
- * which means we can connect our UDP socket to it */
- if (ts->abis_ip.rtp_socket) {
- rtp_socket_free(ts->abis_ip.rtp_socket);
- ts->abis_ip.rtp_socket = NULL;
- }
-
- ts->abis_ip.rtp_socket = rtp_socket_create();
- if (!ts->abis_ip.rtp_socket)
- goto out_err;
-
- rc = rtp_socket_connect(ts->abis_ip.rtp_socket,
- ts->abis_ip.bound_ip,
- ts->abis_ip.bound_port);
- if (rc < 0)
- goto out_err;
- break;
- case S_ABISIP_DLCX_IND:
- /* the BTS tells us a RTP stream has been disconnected */
- if (ts->abis_ip.rtp_socket) {
- rtp_socket_free(ts->abis_ip.rtp_socket);
- ts->abis_ip.rtp_socket = NULL;
+ /* check if any transactions on this lchan still have
+ * a tch_recv_mncc request pending */
+ net = lchan->ts->trx->bts->network;
+ llist_for_each_entry(trans, &net->trans_list, entry) {
+ if (trans->lchan == lchan && trans->tch_recv) {
+ DEBUGP(DCC, "pending tch_recv_mncc request\n");
+ tch_recv_mncc(net, trans->callref, 1);
+ }
}
break;
}
return 0;
-out_err:
- /* FIXME: do something */
- return 0;
-}
-
-/* bind rtp proxy to local IP/port and tell BTS to connect to it */
-static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
- struct rtp_socket *rs = ts->abis_ip.rtp_socket;
- int rc;
-
- rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
- ntohs(rs->rtp.sin_local.sin_port),
- ts->abis_ip.conn_id,
- /* FIXME: use RTP payload of bound socket, not BTS*/
- ts->abis_ip.rtp_payload2);
-
- return rc;
}
/* map two ipaccess RTP streams onto each other */
@@ -1911,7 +1906,6 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts;
- struct gsm_bts_trx_ts *ts;
int rc;
DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n",
@@ -1922,33 +1916,31 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
return -EINVAL;
}
-
+
+ // todo: map between different bts types
switch (bts->type) {
case GSM_BTS_TYPE_NANOBTS:
if (!ipacc_rtp_direct) {
/* connect the TCH's to our RTP proxy */
- rc = ipacc_connect_proxy_bind(lchan);
+ rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
if (rc < 0)
return rc;
- rc = ipacc_connect_proxy_bind(remote_lchan);
+ rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan);
+#warning do we need a check of rc here?
/* connect them with each other */
- rtp_socket_proxy(lchan->ts->abis_ip.rtp_socket,
- remote_lchan->ts->abis_ip.rtp_socket);
+ rtp_socket_proxy(lchan->abis_ip.rtp_socket,
+ remote_lchan->abis_ip.rtp_socket);
} else {
/* directly connect TCH RTP streams to each other */
- ts = remote_lchan->ts;
- rc = rsl_ipacc_mdcx(lchan, ts->abis_ip.bound_ip,
- ts->abis_ip.bound_port,
- lchan->ts->abis_ip.conn_id,
- ts->abis_ip.rtp_payload2);
+ rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip,
+ remote_lchan->abis_ip.bound_port,
+ remote_lchan->abis_ip.rtp_payload2);
if (rc < 0)
return rc;
- ts = lchan->ts;
- rc = rsl_ipacc_mdcx(remote_lchan, ts->abis_ip.bound_ip,
- ts->abis_ip.bound_port,
- remote_lchan->ts->abis_ip.conn_id,
- ts->abis_ip.rtp_payload2);
+ rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip,
+ lchan->abis_ip.bound_port,
+ lchan->abis_ip.rtp_payload2);
}
break;
case GSM_BTS_TYPE_BS11:
@@ -1956,8 +1948,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
break;
default:
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
- rc = -EINVAL;
- break;
+ return -EINVAL;
}
return 0;
@@ -1979,45 +1970,61 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
return tch_map(trans1->lchan, trans2->lchan);
}
-/* enable receive of channels to upqueue */
-static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+/* enable receive of channels to MNCC upqueue */
+static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable)
{
struct gsm_trans *trans;
+ struct gsm_lchan *lchan;
+ struct gsm_bts *bts;
+ int rc;
/* Find callref */
- trans = trans_find_by_callref(net, data->callref);
+ trans = trans_find_by_callref(net, callref);
if (!trans)
return -EIO;
if (!trans->lchan)
return 0;
+ lchan = trans->lchan;
+ bts = lchan->ts->trx->bts;
- // todo IPACCESS
- if (enable)
- return trau_recv_lchan(trans->lchan, data->callref);
- return trau_mux_unmap(NULL, data->callref);
-}
-
-/* send a frame to channel */
-static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
-{
- struct gsm_trans *trans;
-
- /* Find callref */
- trans = trans_find_by_callref(net, frame->callref);
- if (!trans)
- return -EIO;
- if (!trans->lchan)
- return 0;
- if (trans->lchan->type != GSM_LCHAN_TCH_F &&
- trans->lchan->type != GSM_LCHAN_TCH_H)
- return 0;
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (ipacc_rtp_direct) {
+ DEBUGP(DCC, "Error: RTP proxy is disabled\n");
+ return -EINVAL;
+ }
+ /* in case, we don't have a RTP socket yet, we note this
+ * in the transaction and try later */
+ if (!lchan->abis_ip.rtp_socket) {
+ trans->tch_recv = enable;
+ DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable);
+ return 0;
+ }
+ if (enable) {
+ /* connect the TCH's to our RTP proxy */
+ rc = rsl_ipacc_mdcx_to_rtpsock(lchan);
+ if (rc < 0)
+ return rc;
+ /* assign socket to application interface */
+ rtp_socket_upstream(lchan->abis_ip.rtp_socket,
+ net, callref);
+ } else
+ rtp_socket_upstream(lchan->abis_ip.rtp_socket,
+ net, 0);
+ break;
+ case GSM_BTS_TYPE_BS11:
+ if (enable)
+ return trau_recv_lchan(lchan, callref);
+ return trau_mux_unmap(NULL, callref);
+ break;
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ return -EINVAL;
+ }
- // todo IPACCESS
- return trau_send_lchan(trans->lchan,
- (struct decoded_trau_frame *)frame->data);
+ return 0;
}
-
static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
{
DEBUGP(DCC, "-> STATUS ENQ\n");
@@ -2738,6 +2745,9 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
case GSM_CSTATE_RELEASE_REQ:
rc = mncc_recvmsg(trans->subscr->net, trans,
MNCC_REL_CNF, &rel);
+ /* FIXME: in case of multiple calls, we can't simply
+ * hang up here ! */
+ lchan_auto_release(msg->lchan);
break;
default:
rc = mncc_recvmsg(trans->subscr->net, trans,
@@ -3244,11 +3254,30 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
case MNCC_BRIDGE:
return tch_bridge(net, arg);
case MNCC_FRAME_DROP:
- return tch_recv(net, arg, 0);
+ return tch_recv_mncc(net, data->callref, 0);
case MNCC_FRAME_RECV:
- return tch_recv(net, arg, 1);
- case GSM_TRAU_FRAME:
- return tch_frame(net, arg);
+ return tch_recv_mncc(net, data->callref, 1);
+ case GSM_TCHF_FRAME:
+ /* Find callref */
+ trans = trans_find_by_callref(net, data->callref);
+ if (!trans)
+ return -EIO;
+ if (!trans->lchan)
+ return 0;
+ if (trans->lchan->type != GSM_LCHAN_TCH_F)
+ return 0;
+ bts = trans->lchan->ts->trx->bts;
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS:
+ if (!trans->lchan->abis_ip.rtp_socket)
+ return 0;
+ return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg);
+ case GSM_BTS_TYPE_BS11:
+ return trau_send_frame(trans->lchan, arg);
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ }
+ return -EINVAL;
}
memset(&rel, 0, sizeof(struct gsm_mncc));
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index d3e4689b5..e96a1ca09 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -308,7 +308,7 @@ static const enum gsm_chreq_reason_t reason_by_chreq[] = {
[CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL,
[CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER,
- [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL,
[CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
[CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
[CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG,
@@ -541,21 +541,22 @@ static void gsm48_chan_desc(struct gsm48_chan_desc *cd,
}
/* Chapter 9.1.15: Handover Command */
-int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan,
- struct gsm_lchan *new_lchan, u_int8_t power_command)
+int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
+ u_int8_t power_command, u_int8_t ho_ref)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ho_cmd *ho =
(struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho));
- static u_int8_t ho_ref;
msg->lchan = old_lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_HANDO_CMD;
/* mandatory bits */
gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
gsm48_chan_desc(&ho->chan_desc, new_lchan);
- ho->ho_ref = ho_ref++;
+ ho->ho_ref = ho_ref;
ho->power_command = power_command;
/* FIXME: optional bits for type of synchronization? */
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 8e3c64974..31526e979 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -173,7 +173,7 @@ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
DEBUGP(DSMS, "TX: CP-ACK ");
break;
case GSM411_MT_CP_ERROR:
- DEBUGP(DSMS, "TX: CP-ACK ");
+ DEBUGP(DSMS, "TX: CP-ERROR ");
break;
}
@@ -218,8 +218,7 @@ static u_int8_t unbcdify(u_int8_t value)
DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
ret = (value&0x0F)*10;
- if (ret > 90)
- ret += value>>4;
+ ret += value>>4;
return ret;
}
@@ -907,11 +906,11 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
return -EIO;
/* FIXME: send some error message */
- DEBUGP(DSMS, "trans_id=%x ", gh->proto_discr >> 4);
+ DEBUGP(DSMS, "trans_id=%x ", transaction_id);
trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
transaction_id);
if (!trans) {
- DEBUGPC(DSMS, "(unknown) ");
+ DEBUGPC(DSMS, "(new) ");
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index ddad2ff94..1e2480dd9 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -169,6 +169,14 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
bts->num_trx = 0;
INIT_LLIST_HEAD(&bts->trx_list);
bts->ms_max_power = 15; /* dBm */
+ bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
+ bts->si_common.cell_sel_par.rxlev_acc_min = 0;
+ bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
+ bts->si_common.neigh_list.data_len =
+ sizeof(bts->si_common.data.neigh_list);
+ bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
+ bts->si_common.cell_alloc.data_len =
+ sizeof(bts->si_common.data.cell_alloc);
for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
bts->gprs.nsvc[i].bts = bts;
@@ -204,6 +212,14 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
net->T3113 = GSM_T3113_DEFAULT;
/* FIXME: initialize all other timers! */
+ /* default set of handover parameters */
+ net->handover.win_rxlev_avg = 10;
+ net->handover.win_rxqual_avg = 1;
+ net->handover.win_rxlev_avg_neigh = 10;
+ net->handover.pwr_interval = 6;
+ net->handover.pwr_hysteresis = 3;
+ net->handover.max_distance = 9999;
+
INIT_LLIST_HEAD(&net->trans_list);
INIT_LLIST_HEAD(&net->upqueue);
INIT_LLIST_HEAD(&net->bts_list);
@@ -228,6 +244,25 @@ struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num)
return NULL;
}
+/* Get reference to a neighbor cell on a given BCCH ARFCN */
+struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts,
+ u_int16_t arfcn, u_int8_t bsic)
+{
+ struct gsm_bts *neigh;
+ /* FIXME: use some better heuristics here to determine which cell
+ * using this ARFCN really is closest to the target cell. For
+ * now we simply assume that each ARFCN will only be used by one
+ * cell */
+
+ llist_for_each_entry(neigh, &bts->network->bts_list, list) {
+ if (neigh->c0->arfcn == arfcn &&
+ neigh->bsic == bsic)
+ return neigh;
+ }
+
+ return NULL;
+}
+
struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num)
{
struct gsm_bts_trx *trx;
@@ -410,3 +445,40 @@ int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts)
return gsm48_construct_ra(buf, &raid);
}
+
+static const char *rrlp_mode_names[] = {
+ [RRLP_MODE_NONE] = "none",
+ [RRLP_MODE_MS_BASED] = "ms-based",
+ [RRLP_MODE_MS_PREF] = "ms-preferred",
+ [RRLP_MODE_ASS_PREF] = "ass-preferred",
+};
+
+enum rrlp_mode rrlp_mode_parse(const char *arg)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(rrlp_mode_names); i++) {
+ if (!strcmp(arg, rrlp_mode_names[i]))
+ return i;
+ }
+ return RRLP_MODE_NONE;
+}
+
+const char *rrlp_mode_name(enum rrlp_mode mode)
+{
+ if (mode > ARRAY_SIZE(rrlp_mode_names))
+ return "none";
+ return rrlp_mode_names[mode];
+}
+
+struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
+{
+ struct gsm_meas_rep *meas_rep;
+
+ meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
+ memset(meas_rep, 0, sizeof(*meas_rep));
+ meas_rep->lchan = lchan;
+ lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
+ % ARRAY_SIZE(lchan->meas_rep);
+
+ return meas_rep;
+}
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
index ddfd7f3de..9439993db 100644
--- a/openbsc/src/gsm_utils.c
+++ b/openbsc/src/gsm_utils.c
@@ -90,8 +90,10 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
return 0;
else if (dbm < 5)
return 19;
- else
+ else {
+ /* we are guaranteed to have (5 <= dbm < 39) */
return 2 + ((39 - dbm) / 2);
+ }
break;
case GSM_BAND_1800:
if (dbm >= 36)
@@ -100,16 +102,24 @@ int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
return 30;
else if (dbm >= 32)
return 31;
- else
+ else if (dbm == 31)
+ return 0;
+ else {
+ /* we are guaranteed to have (0 <= dbm < 31) */
return (30 - dbm) / 2;
+ }
break;
case GSM_BAND_1900:
if (dbm >= 33)
return 30;
else if (dbm >= 32)
return 31;
- else
+ else if (dbm == 31)
+ return 0;
+ else {
+ /* we are guaranteed to have (0 <= dbm < 31) */
return (30 - dbm) / 2;
+ }
break;
}
return -EINVAL;
@@ -150,6 +160,28 @@ int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
return -EINVAL;
}
+/* According to TS 08.05 Chapter 8.1.4 */
+int rxlev2dbm(u_int8_t rxlev)
+{
+ if (rxlev > 63)
+ rxlev = 63;
+
+ return -110 + rxlev;
+}
+
+/* According to TS 08.05 Chapter 8.1.4 */
+u_int8_t dbm2rxlev(int dbm)
+{
+ int rxlev = dbm + 110;
+
+ if (rxlev > 63)
+ rxlev = 63;
+ else if (rxlev < 0)
+ rxlev = 0;
+
+ return rxlev;
+}
+
void generate_backtrace()
{
int i, nptrs;
diff --git a/openbsc/src/handover_decision.c b/openbsc/src/handover_decision.c
new file mode 100644
index 000000000..b37cecddb
--- /dev/null
+++ b/openbsc/src/handover_decision.c
@@ -0,0 +1,298 @@
+/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This
+ * only implements the handover algorithm/decision, but not execution
+ * of it */
+
+/* (C) 2009 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 <stdlib.h>
+#include <errno.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/meas_rep.h>
+#include <openbsc/signal.h>
+#include <openbsc/talloc.h>
+#include <openbsc/handover.h>
+#include <openbsc/gsm_utils.h>
+
+/* issue handover to a cell identified by ARFCN and BSIC */
+static int handover_to_arfcn_bsic(struct gsm_lchan *lchan,
+ u_int16_t arfcn, u_int8_t bsic)
+{
+ struct gsm_bts *new_bts;
+
+ /* resolve the gsm_bts structure for the best neighbor */
+ new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic);
+ if (!new_bts) {
+ LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS "
+ "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic);
+ return -EINVAL;
+ }
+
+ /* and actually try to handover to that cell */
+ return bsc_handover_start(lchan, new_bts);
+}
+
+/* did we get a RXLEV for a given cell in the given report? */
+static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr,
+ u_int16_t arfcn, u_int8_t bsic)
+{
+ int i;
+
+ for (i = 0; i < mr->num_cell; i++) {
+ struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+
+ /* search for matching report */
+ if (!(mrc->arfcn == arfcn && mrc->bsic == bsic))
+ continue;
+
+ mrc->flags |= MRC_F_PROCESSED;
+ return mrc->rxlev;
+ }
+ return -ENODEV;
+}
+
+/* obtain averaged rxlev for given neighbor */
+static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window)
+{
+ unsigned int i, idx;
+ int avg = 0;
+
+ idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev),
+ nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev),
+ window);
+
+ for (i = 0; i < window; i++) {
+ int j = (idx+i) % ARRAY_SIZE(nmp->rxlev);
+
+ avg += nmp->rxlev[j];
+ }
+
+ return avg / window;
+}
+
+/* find empty or evict bad neighbor */
+static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan)
+{
+ int j, worst = 999999;
+ struct neigh_meas_proc *nmp_worst;
+
+ /* first try to find an empty/unused slot */
+ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
+ struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
+ if (!nmp->arfcn)
+ return nmp;
+ }
+
+ /* no empty slot found. evict worst neighbor from list */
+ for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) {
+ struct neigh_meas_proc *nmp = &lchan->neigh_meas[j];
+ int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG);
+ if (avg < worst) {
+ worst = avg;
+ nmp_worst = nmp;
+ }
+ }
+
+ return nmp_worst;
+}
+
+/* process neighbor cell measurement reports */
+static void process_meas_neigh(struct gsm_meas_rep *mr)
+{
+ int i, j, idx;
+
+ /* for each reported cell, try to update global state */
+ for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) {
+ struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j];
+ unsigned int idx;
+ int rxlev;
+
+ /* skip unused entries */
+ if (!nmp->arfcn)
+ continue;
+
+ rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic);
+ idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
+ if (rxlev >= 0) {
+ nmp->rxlev[idx] = rxlev;
+ nmp->last_seen_nr = mr->nr;
+ } else
+ nmp->rxlev[idx] = 0;
+ nmp->rxlev_cnt++;
+ }
+
+ /* iterate over list of reported cells, check if we did not
+ * process all of them */
+ for (i = 0; i < mr->num_cell; i++) {
+ struct gsm_meas_rep_cell *mrc = &mr->cell[i];
+ struct neigh_meas_proc *nmp;
+
+ if (mrc->flags & MRC_F_PROCESSED)
+ continue;
+
+ nmp = find_evict_neigh(mr->lchan);
+
+ nmp->arfcn = mrc->arfcn;
+ nmp->bsic = mrc->bsic;
+
+ idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev);
+ nmp->rxlev[idx] = mrc->rxlev;
+ nmp->rxlev_cnt++;
+ nmp->last_seen_nr = mr->nr;
+
+ mrc->flags |= MRC_F_PROCESSED;
+ }
+}
+
+/* attempt to do a handover */
+static int attempt_handover(struct gsm_meas_rep *mr)
+{
+ struct gsm_network *net = mr->lchan->ts->trx->bts->network;
+ struct neigh_meas_proc *best_cell = NULL;
+ unsigned int best_better_db = 0;
+ int i, rc;
+
+ /* find the best cell in this report that is at least RXLEV_HYST
+ * better than the current serving cell */
+
+ for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) {
+ struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i];
+ int avg, better;
+
+ /* skip empty slots */
+ if (nmp->arfcn == 0)
+ continue;
+
+ /* caculate average rxlev for this cell over the window */
+ avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh);
+
+ /* check if hysteresis is fulfilled */
+ if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis)
+ continue;
+
+ better = avg - mr->dl.full.rx_lev;
+ if (better > best_better_db) {
+ best_cell = nmp;
+ best_better_db = better;
+ }
+ }
+
+ if (!best_cell)
+ return 0;
+
+ LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ",
+ gsm_ts_name(mr->lchan->ts), best_cell->arfcn);
+ if (!net->handover.active) {
+ LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n");
+ return 0;
+ }
+
+ rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic);
+ switch (rc) {
+ case 0:
+ LOGPC(DHO, LOGL_INFO, "Starting handover\n");
+ break;
+ case -ENOSPC:
+ LOGPC(DHO, LOGL_INFO, "No channel available\n");
+ break;
+ case -EBUSY:
+ LOGPC(DHO, LOGL_INFO, "Handover already active\n");
+ break;
+ default:
+ LOGPC(DHO, LOGL_ERROR, "Unknown error\n");
+ }
+ return rc;
+}
+
+/* process an already parsed measurement report and decide if we want to
+ * attempt a handover */
+static int process_meas_rep(struct gsm_meas_rep *mr)
+{
+ struct gsm_network *net = mr->lchan->ts->trx->bts->network;
+ int av_rxlev;
+
+ /* we currently only do handover for TCH channels */
+ switch (mr->lchan->type) {
+ case GSM_LCHAN_TCH_F:
+ case GSM_LCHAN_TCH_H:
+ break;
+ default:
+ return 0;
+ }
+
+ /* parse actual neighbor cell info */
+ if (mr->num_cell > 0 && mr->num_cell < 7)
+ process_meas_neigh(mr);
+
+ av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL,
+ net->handover.win_rxlev_avg);
+
+ /* Interference HO */
+ if (rxlev2dbm(av_rxlev) > -85 &&
+ meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL,
+ 3, 4, 5))
+ return attempt_handover(mr);
+
+ /* Bad Quality */
+ if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL,
+ 3, 4, 5))
+ return attempt_handover(mr);
+
+ /* Low Level */
+ if (rxlev2dbm(av_rxlev) <= -110)
+ return attempt_handover(mr);
+
+ /* Distance */
+ if (mr->ms_l1.ta > net->handover.max_distance)
+ return attempt_handover(mr);
+
+ /* Power Budget AKA Better Cell */
+ if ((mr->nr % net->handover.pwr_interval) == 0)
+ return attempt_handover(mr);
+
+ return 0;
+
+}
+
+static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_meas_rep *mr;
+
+ if (subsys != SS_LCHAN)
+ return 0;
+
+ switch (signal) {
+ case S_LCHAN_MEAS_REP:
+ mr = signal_data;
+ process_meas_rep(mr);
+ break;
+ }
+
+ return 0;
+}
+
+void on_dso_load_ho_dec(void)
+{
+ register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
+}
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
index d4a888487..94d3d0d1e 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -40,6 +40,7 @@
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
+#include <openbsc/rtp_proxy.h>
struct bsc_handover {
struct llist_head list;
@@ -85,23 +86,46 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
{
struct gsm_lchan *new_lchan;
struct bsc_handover *ho;
+ static u_int8_t ho_ref;
int rc;
+ /* don't attempt multiple handovers for the same lchan at
+ * the same time */
+ if (bsc_ho_by_old_lchan(old_lchan))
+ return -EBUSY;
+
+ DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
+ old_lchan->ts->trx->bts->nr, bts->nr);
+
new_lchan = lchan_alloc(bts, old_lchan->type);
- if (!new_lchan)
+ if (!new_lchan) {
+ LOGP(DHO, LOGL_NOTICE, "No free channel\n");
return -ENOSPC;
+ }
ho = talloc_zero(NULL, struct bsc_handover);
if (!ho) {
+ LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
lchan_free(new_lchan);
return -ENOMEM;
}
ho->old_lchan = old_lchan;
ho->new_lchan = new_lchan;
+ ho->ho_ref = ho_ref++;
+
+ /* copy some parameters from old lchan */
+ memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
+ new_lchan->ms_power = old_lchan->ms_power;
+ new_lchan->bs_power = old_lchan->bs_power;
+ new_lchan->rsl_cmode = old_lchan->rsl_cmode;
+ new_lchan->tch_mode = old_lchan->tch_mode;
+ new_lchan->subscr = subscr_get(old_lchan->subscr);
/* FIXME: do we have a better idea of the timing advance? */
- rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0);
+ rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
+ ho->ho_ref);
if (rc < 0) {
+ LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
talloc_free(ho);
lchan_free(new_lchan);
return rc;
@@ -118,6 +142,8 @@ static void ho_T3103_cb(void *_ho)
{
struct bsc_handover *ho = _ho;
+ DEBUGP(DHO, "HO T3103 expired\n");
+
lchan_free(ho->new_lchan);
llist_del(&ho->list);
talloc_free(ho);
@@ -129,20 +155,29 @@ static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
struct bsc_handover *ho;
int rc;
+ /* we need to check if this channel activation is related to
+ * a handover at all (and if, which particular handover) */
ho = bsc_ho_by_new_lchan(new_lchan);
if (!ho)
return -ENODEV;
+ DEBUGP(DHO, "handover activate ack, send HO Command\n");
+
/* we can now send the 04.08 HANDOVER COMMAND to the MS
* using the old lchan */
- rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0);
+ rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
/* start T3103. We can continue either with T3103 expiration,
* 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
ho->T3103.cb = ho_T3103_cb;
+ ho->T3103.data = ho;
bsc_schedule_timer(&ho->T3103, 10, 0);
+ /* create a RTP connection */
+ if (is_ipaccess_bts(new_lchan->ts->trx->bts))
+ rsl_ipacc_crcx(new_lchan);
+
return 0;
}
@@ -152,8 +187,10 @@ static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
struct bsc_handover *ho;
ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho)
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
return -ENODEV;
+ }
llist_del(&ho->list);
talloc_free(ho);
@@ -169,18 +206,22 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
struct bsc_handover *ho;
ho = bsc_ho_by_new_lchan(new_lchan);
- if (!ho)
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
return -ENODEV;
+ }
bsc_del_timer(&ho->T3103);
- llist_del(&ho->list);
- /* do something to re-route the actual speech frames ! */
- //tch_remap(ho->old_lchan, ho->new_lchan);
+ /* update lchan pointer of transaction */
+ trans_lchan_change(ho->old_lchan, new_lchan);
+
+ ho->old_lchan->state = LCHAN_S_INACTIVE;
+ lchan_auto_release(ho->old_lchan);
- /* release old lchan */
- put_lchan(ho->old_lchan);
+ /* do something to re-route the actual speech frames ! */
+ llist_del(&ho->list);
talloc_free(ho);
return 0;
@@ -192,8 +233,10 @@ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
struct bsc_handover *ho;
ho = bsc_ho_by_old_lchan(old_lchan);
- if (!ho)
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
return -ENODEV;
+ }
bsc_del_timer(&ho->T3103);
llist_del(&ho->list);
@@ -208,15 +251,72 @@ static int ho_rsl_detect(struct gsm_lchan *new_lchan)
{
struct bsc_handover *ho;
- ho = bsc_ho_by_old_lchan(new_lchan);
- if (!ho)
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
return -ENODEV;
+ }
/* FIXME: do we actually want to do something here ? */
return 0;
}
+static int ho_ipac_crcx_ack(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+ struct rtp_socket *old_rs, *new_rs, *other_rs;
+
+ ho = bsc_ho_by_new_lchan(new_lchan);
+ if (!ho) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ if (ipacc_rtp_direct) {
+ LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
+ return 0;
+ }
+
+ /* RTP Proxy mode */
+ new_rs = new_lchan->abis_ip.rtp_socket;
+ old_rs = ho->old_lchan->abis_ip.rtp_socket;
+
+ if (!new_rs) {
+ LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n");
+ return -EIO;
+ }
+
+ rsl_ipacc_mdcx_to_rtpsock(new_lchan);
+
+ if (!old_rs) {
+ LOGP(DHO, LOGL_ERROR, "no RTP socekt for old_lchan\n");
+ return -EIO;
+ }
+
+ /* copy rx_action and reference to other sock */
+ new_rs->rx_action = old_rs->rx_action;
+ new_rs->tx_action = old_rs->tx_action;
+ new_rs->transmit = old_rs->transmit;
+
+ switch (ho->old_lchan->abis_ip.rtp_socket->rx_action) {
+ case RTP_PROXY:
+ other_rs = old_rs->proxy.other_sock;
+ rtp_socket_proxy(new_rs, other_rs);
+ /* delete reference to other end socket to prevent
+ * rtp_socket_free() from removing the inverse reference */
+ old_rs->proxy.other_sock = NULL;
+ break;
+ case RTP_RECV_UPSTREAM:
+ new_rs->receive = old_rs->receive;
+ break;
+ case RTP_NONE:
+ break;
+ }
+
+ return 0;
+}
+
static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
@@ -238,6 +338,14 @@ static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
return ho_gsm48_ho_fail(lchan);
}
break;
+ case SS_ABISIP:
+ lchan = signal_data;
+ switch (signal) {
+ case S_ABISIP_CRCX_ACK:
+ return ho_ipac_crcx_ack(lchan);
+ break;
+ }
+ break;
default:
break;
}
@@ -248,4 +356,5 @@ static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
static __attribute__((constructor)) void on_dso_load_ho_logic(void)
{
register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
+ register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL);
}
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 7dc2983fe..9d972d8fa 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -239,6 +239,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
trx->rsl_tei, 0);
/* get rid of our old temporary bfd */
memcpy(newbfd, bfd, sizeof(*newbfd));
+ newbfd->priv_nr = 2+trx_id;
bsc_unregister_fd(bfd);
bsc_register_fd(newbfd);
talloc_free(bfd);
@@ -248,9 +249,8 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
return 0;
}
-/* FIXME: this is per BTS */
-static int oml_up = 0;
-static int rsl_up = 0;
+#define OML_UP 0x0001
+#define RSL_UP 0x0002
/*
* read one ipa message from the socket
@@ -349,16 +349,16 @@ static int handle_ts1_read(struct bsc_fd *bfd)
switch (link->type) {
case E1INP_SIGN_RSL:
- if (!rsl_up) {
+ if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) {
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
- rsl_up = 1;
+ msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr);
}
ret = abis_rsl_rcvmsg(msg);
break;
case E1INP_SIGN_OML:
- if (!oml_up) {
+ if (!(msg->trx->bts->ip_access.flags & OML_UP)) {
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
- oml_up = 1;
+ msg->trx->bts->ip_access.flags |= OML_UP;
}
ret = abis_nm_rcvmsg(msg);
break;
diff --git a/openbsc/src/meas_rep.c b/openbsc/src/meas_rep.c
new file mode 100644
index 000000000..4b9cc1a0c
--- /dev/null
+++ b/openbsc/src/meas_rep.c
@@ -0,0 +1,114 @@
+/* Measurement Report Processing */
+
+/* (C) 2009 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 <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/meas_rep.h>
+
+static int get_field(const struct gsm_meas_rep *rep,
+ enum meas_rep_field field)
+{
+ switch (field) {
+ case MEAS_REP_DL_RXLEV_FULL:
+ return rep->dl.full.rx_lev;
+ case MEAS_REP_DL_RXLEV_SUB:
+ return rep->dl.sub.rx_lev;
+ case MEAS_REP_DL_RXQUAL_FULL:
+ return rep->dl.full.rx_qual;
+ case MEAS_REP_DL_RXQUAL_SUB:
+ return rep->dl.sub.rx_qual;
+ case MEAS_REP_UL_RXLEV_FULL:
+ return rep->ul.full.rx_lev;
+ case MEAS_REP_UL_RXLEV_SUB:
+ return rep->ul.sub.rx_lev;
+ case MEAS_REP_UL_RXQUAL_FULL:
+ return rep->ul.full.rx_qual;
+ case MEAS_REP_UL_RXQUAL_SUB:
+ return rep->ul.sub.rx_qual;
+ }
+
+ return 0;
+}
+
+
+unsigned int calc_initial_idx(unsigned int array_size,
+ unsigned int meas_rep_idx,
+ unsigned int num_values)
+{
+ int offs, idx;
+
+ /* from which element do we need to start if we're interested
+ * in an average of 'num' elements */
+ offs = meas_rep_idx - num_values;
+
+ if (offs < 0)
+ idx = array_size + offs;
+ else
+ idx = offs;
+
+ return idx;
+}
+
+/* obtain an average over the last 'num' fields in the meas reps */
+int get_meas_rep_avg(const struct gsm_lchan *lchan,
+ enum meas_rep_field field, unsigned int num)
+{
+ unsigned int i, idx;
+ int avg = 0;
+
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, num);
+
+ for (i = 0; i < num; i++) {
+ int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep);
+
+ avg += get_field(&lchan->meas_rep[j], field);
+ }
+
+ return avg / num;
+}
+
+/* Check if N out of M last values for FIELD are >= bd */
+int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
+ enum meas_rep_field field,
+ unsigned int n, unsigned int m, int be)
+{
+ unsigned int i, idx;
+ int count = 0;
+
+ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
+ lchan->meas_rep_idx, m);
+
+ for (i = 0; i < m; i++) {
+ int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep);
+ int val = get_field(&lchan->meas_rep[j], field);
+
+ if (val >= be)
+ count++;
+
+ if (count >= n)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
index f62541c05..15e2978e6 100644
--- a/openbsc/src/mncc.c
+++ b/openbsc/src/mncc.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <sys/types.h>
#include <openbsc/gsm_04_08.h>
@@ -29,6 +30,8 @@
#include <openbsc/mncc.h>
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
+#include <openbsc/transaction.h>
+#include <openbsc/rtp_proxy.h>
void *tall_call_ctx;
@@ -82,10 +85,9 @@ static struct mncc_names {
{"MNCC_FRAME_DROP", 0x0202},
{"MNCC_LCHAN_MODIFY", 0x0203},
- {"GSM_TRAU_FRAME", 0x0300},
+ {"GSM_TCH_FRAME", 0x0300},
- {NULL, 0}
-};
+ {NULL, 0} };
static LLIST_HEAD(call_list);
@@ -136,19 +138,36 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type,
struct gsm_mncc mncc;
struct gsm_call *remote;
+ memset(&mncc, 0, sizeof(struct gsm_mncc));
+ mncc.callref = call->callref;
+
/* already have remote call */
if (call->remote_ref)
return 0;
+ /* transfer mode 1 would be packet mode, which was never specified */
+ if (setup->bearer_cap.mode != 0) {
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support "
+ "packet mode\n", call->callref);
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+ goto out_reject;
+ }
+
+ /* we currently only do speech */
+ if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) {
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support "
+ "voice calls\n", call->callref);
+ mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
+ GSM48_CC_CAUSE_BEARER_CA_UNAVAIL);
+ goto out_reject;
+ }
+
/* create remote call */
if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
- memset(&mncc, 0, sizeof(struct gsm_mncc));
- mncc.callref = call->callref;
mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU,
GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
- mncc_send(call->net, MNCC_REJ_REQ, &mncc);
- free_call(call);
- return 0;
+ goto out_reject;
}
llist_add_tail(&remote->entry, &call_list);
remote->net = call->net;
@@ -179,6 +198,11 @@ static int mncc_setup_ind(struct gsm_call *call, int msg_type,
setup->callref = remote->callref;
DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref);
return mncc_send(remote->net, MNCC_SETUP_REQ, setup);
+
+out_reject:
+ mncc_send(call->net, MNCC_REJ_REQ, &mncc);
+ free_call(call);
+ return 0;
}
static int mncc_alert_ind(struct gsm_call *call, int msg_type,
@@ -210,7 +234,8 @@ static int mncc_notify_ind(struct gsm_call *call, int msg_type,
static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
struct gsm_mncc *connect)
{
- struct gsm_mncc connect_ack;
+ struct gsm_mncc connect_ack, frame_recv;
+ struct gsm_network *net = call->net;
struct gsm_call *remote;
u_int32_t refs[2];
@@ -231,7 +256,26 @@ static int mncc_setup_cnf(struct gsm_call *call, int msg_type,
refs[0] = call->callref;
refs[1] = call->remote_ref;
DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref);
- return mncc_send(call->net, MNCC_BRIDGE, refs);
+
+ /* in direct mode, we always have to bridge the channels */
+ if (ipacc_rtp_direct)
+ return mncc_send(call->net, MNCC_BRIDGE, refs);
+
+ /* proxy mode */
+ if (!net->handover.active) {
+ /* in the no-handover case, we can bridge, i.e. use
+ * the old RTP proxy code */
+ return mncc_send(call->net, MNCC_BRIDGE, refs);
+ } else {
+ /* in case of handover, we need to re-write the RTP
+ * SSRC, sequence and timestamp values and thus
+ * need to enable RTP receive for both directions */
+ memset(&frame_recv, 0, sizeof(struct gsm_mncc));
+ frame_recv.callref = call->callref;
+ mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
+ frame_recv.callref = call->remote_ref;
+ return mncc_send(call->net, MNCC_FRAME_RECV, &frame_recv);
+ }
}
static int mncc_disc_ind(struct gsm_call *call, int msg_type,
@@ -279,6 +323,28 @@ static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *re
return 0;
}
+/* receiving a TCH/F frame from the BSC code */
+static int mncc_rcv_tchf(struct gsm_call *call, int msg_type,
+ struct gsm_data_frame *dfr)
+{
+ struct gsm_trans *remote_trans;
+
+ remote_trans = trans_find_by_callref(call->net, call->remote_ref);
+
+ /* this shouldn't really happen */
+ if (!remote_trans || !remote_trans->lchan) {
+ LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
+ return -EIO;
+ }
+
+ /* RTP socket of remote end has meanwhile died */
+ if (!remote_trans->lchan->abis_ip.rtp_socket)
+ return -EIO;
+
+ return rtp_send_frame(remote_trans->lchan->abis_ip.rtp_socket, dfr);
+}
+
+
int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
{
struct gsm_mncc *data = arg;
@@ -320,8 +386,15 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref);
}
- DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
- get_mncc_name(msg_type));
+ switch (msg_type) {
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ break;
+ default:
+ DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref,
+ get_mncc_name(msg_type));
+ break;
+ }
switch(msg_type) {
case MNCC_SETUP_IND:
@@ -382,8 +455,12 @@ int mncc_recv(struct gsm_network *net, int msg_type, void *arg)
call->callref, data->cause.value);
rc = mncc_send(net, MNCC_RETRIEVE_REJ, data);
break;
+ case GSM_TCHF_FRAME:
+ case GSM_TCHF_FRAME_EFR:
+ rc = mncc_rcv_tchf(call, msg_type, arg);
+ break;
default:
- DEBUGP(DMNCC, "(call %x) Message unhandled\n", callref);
+ LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref);
break;
}
diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c
index edeb975a9..48a5a7b03 100644
--- a/openbsc/src/msgb.c
+++ b/openbsc/src/msgb.c
@@ -26,6 +26,7 @@
#include <openbsc/msgb.h>
#include <openbsc/gsm_data.h>
#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
static void *tall_msgb_ctx;
@@ -35,8 +36,10 @@ struct msgb *msgb_alloc(u_int16_t size, const char *name)
msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
- if (!msg)
+ if (!msg) {
+ LOGP(DRSL, LOGL_FATAL, "unable to allocate msgb\n");
return NULL;
+ }
msg->data_len = size;
msg->len = 0;
diff --git a/openbsc/src/openbsc.cfg.1-1 b/openbsc/src/openbsc.cfg.1-1
index d312843b0..a25804f63 100644
--- a/openbsc/src/openbsc.cfg.1-1
+++ b/openbsc/src/openbsc.cfg.1-1
@@ -31,7 +31,7 @@ network
phys_chan_config CCCH+SDCCH4
e1 line 0 timeslot 1 sub-slot full
timeslot 1
- phys_chan_config SDCCH8
+ phys_chan_config TCH/F
e1 line 0 timeslot 2 sub-slot 1
timeslot 2
phys_chan_config TCH/F
diff --git a/openbsc/src/openbsc.cfg.2-2 b/openbsc/src/openbsc.cfg.2-2
index c1468a647..9ae800342 100644
--- a/openbsc/src/openbsc.cfg.2-2
+++ b/openbsc/src/openbsc.cfg.2-2
@@ -31,7 +31,7 @@ network
phys_chan_config CCCH+SDCCH4
e1 line 0 timeslot 1 sub-slot full
timeslot 1
- phys_chan_config SDCCH8
+ phys_chan_config TCH/F
e1 line 0 timeslot 2 sub-slot 1
timeslot 2
phys_chan_config TCH/F
diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c
index 1d93dcec8..9c59cd732 100644
--- a/openbsc/src/rest_octets.c
+++ b/openbsc/src/rest_octets.c
@@ -291,7 +291,8 @@ static int encode_drx_timer(unsigned int drx)
<BSS_PAGING_COORDINATION: bit >
<spare bit > ** ;
*/
-static int append_gprs_cell_opt(struct bitvec *bv, struct gprs_cell_options *gco)
+static int append_gprs_cell_opt(struct bitvec *bv,
+ const struct gprs_cell_options *gco)
{
int t3192, drx_timer_max;
@@ -323,7 +324,7 @@ static int append_gprs_cell_opt(struct bitvec *bv, struct gprs_cell_options *gco
}
static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
- struct gprs_power_ctrl_pars *pcp)
+ const struct gprs_power_ctrl_pars *pcp)
{
bitvec_set_uint(bv, pcp->alpha, 4);
bitvec_set_uint(bv, pcp->t_avg_w, 5);
diff --git a/openbsc/src/rrlp.c b/openbsc/src/rrlp.c
index 523b53f0b..d4665d570 100644
--- a/openbsc/src/rrlp.c
+++ b/openbsc/src/rrlp.c
@@ -1,4 +1,4 @@
-
+/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
*
@@ -28,9 +28,42 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/chan_alloc.h>
-/* RRLP MS based position request */
+/* RRLP msPositionReq, nsBased,
+ * Accuracy=60, Method=gps, ResponseTime=2, oneSet */
static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 };
+/* RRLP msPositionReq, msBasedPref,
+ Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */
+static const u_int8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 };
+
+/* RRLP msPositionReq, msAssistedPref,
+ Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */
+static const u_int8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 };
+
+static int send_rrlp_req(struct gsm_lchan *lchan)
+{
+ struct gsm_network *net = lchan->ts->trx->bts->network;
+ const u_int8_t *req;
+
+ switch (net->rrlp.mode) {
+ case RRLP_MODE_MS_BASED:
+ req = ms_based_pos_req;
+ break;
+ case RRLP_MODE_MS_PREF:
+ req = ms_pref_pos_req;
+ break;
+ case RRLP_MODE_ASS_PREF:
+ req = ass_pref_pos_req;
+ break;
+ case RRLP_MODE_NONE:
+ default:
+ return 0;
+ }
+
+ return gsm48_send_rr_app_info(lchan, 0x00,
+ sizeof(ms_based_pos_req), req);
+}
+
static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
@@ -44,8 +77,7 @@ static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
lchan = lchan_for_subscr(subscr);
if (!lchan)
break;
- gsm48_send_rr_app_info(lchan, 0x00, sizeof(ms_based_pos_req),
- ms_based_pos_req);
+ send_rrlp_req(lchan);
break;
}
return 0;
@@ -58,10 +90,12 @@ static int paging_sig_cb(unsigned int subsys, unsigned int signal,
switch (signal) {
case S_PAGING_COMPLETED:
+ /* paging might have "completed' unsucessfully,
+ * in this case we don't have a lchan */
+ if (!psig_data->lchan)
+ break;
/* A subscriber has attached. */
- gsm48_send_rr_app_info(psig_data->lchan, 0x00,
- sizeof(ms_based_pos_req),
- ms_based_pos_req);
+ send_rrlp_req(psig_data->lchan);
break;
}
return 0;
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index 59c0735a5..0f4e32799 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -24,6 +24,10 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <sys/time.h> /* gettimeofday() */
+#include <unistd.h> /* get..() */
+#include <time.h> /* clock() */
+#include <sys/utsname.h> /* uname() */
#include <openbsc/talloc.h>
#include <openbsc/gsm_data.h>
@@ -57,6 +61,213 @@ struct rtcp_hdr {
#define RTCP_IE_CNAME 1
+/* according to RFC 3550 */
+struct rtp_hdr {
+ u_int8_t csrc_count:4,
+ extension:1,
+ padding:1,
+ version:2;
+ u_int8_t payload_type:7,
+ marker:1;
+ u_int16_t sequence;
+ u_int32_t timestamp;
+ u_int32_t ssrc;
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+ u_int16_t by_profile;
+ u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION 2
+
+#define RTP_PT_GSM_FULL 3
+#define RTP_PT_GSM_EFR 97
+
+/* decode an rtp frame and create a new buffer with payload */
+static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
+{
+ struct msgb *new_msg;
+ struct gsm_data_frame *frame;
+ struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
+ struct rtp_x_hdr *rtpxh;
+ u_int8_t *payload;
+ int payload_len;
+ int msg_type;
+ int x_len;
+
+ if (msg->len < 12) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
+ msg->len);
+ return -EINVAL;
+ }
+ if (rtph->version != RTP_VERSION) {
+ DEBUGPC(DMUX, "received RTP version %d not supported.\n",
+ rtph->version);
+ return -EINVAL;
+ }
+ payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+ payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
+ "csrc count = %d)\n", msg->len, rtph->csrc_count);
+ return -EINVAL;
+ }
+ if (rtph->extension) {
+ if (payload_len < sizeof(struct rtp_x_hdr)) {
+ DEBUGPC(DMUX, "received RTP frame too short for "
+ "extension header\n");
+ return -EINVAL;
+ }
+ rtpxh = (struct rtp_x_hdr *)payload;
+ x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+ payload += x_len;
+ payload_len -= x_len;
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short, "
+ "extension header exceeds frame length\n");
+ return -EINVAL;
+ }
+ }
+ if (rtph->padding) {
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame too short for "
+ "padding length\n");
+ return -EINVAL;
+ }
+ payload_len -= payload[payload_len - 1];
+ if (payload_len < 0) {
+ DEBUGPC(DMUX, "received RTP frame with padding "
+ "greater than payload\n");
+ return -EINVAL;
+ }
+ }
+
+ switch (rtph->payload_type) {
+ case RTP_PT_GSM_FULL:
+ msg_type = GSM_TCHF_FRAME;
+ if (payload_len != 33) {
+ DEBUGPC(DMUX, "received RTP full rate frame with "
+ "payload length != 32 (len = %d)\n",
+ payload_len);
+ return -EINVAL;
+ }
+ break;
+ case RTP_PT_GSM_EFR:
+ msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ default:
+ DEBUGPC(DMUX, "received RTP frame with unknown payload "
+ "type %d\n", rtph->payload_type);
+ return -EINVAL;
+ }
+
+ new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
+ "GSM-DATA");
+ if (!new_msg)
+ return -ENOMEM;
+ frame = (struct gsm_data_frame *)(new_msg->data);
+ frame->msg_type = msg_type;
+ frame->callref = callref;
+ memcpy(frame->data, payload, payload_len);
+ msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
+
+ *data = new_msg;
+ return 0;
+}
+
+/* "to - from" */
+static void tv_difference(struct timeval *diff, const struct timeval *from,
+ const struct timeval *__to)
+{
+ struct timeval _to = *__to, *to = &_to;
+
+ if (to->tv_usec < from->tv_usec) {
+ to->tv_sec -= 1;
+ to->tv_usec += 1000000;
+ }
+
+ diff->tv_usec = to->tv_usec - from->tv_usec;
+ diff->tv_sec = to->tv_sec - from->tv_sec;
+}
+
+/* encode and send a rtp frame */
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
+{
+ struct rtp_sub_socket *rss = &rs->rtp;
+ struct msgb *msg;
+ struct rtp_hdr *rtph;
+ int payload_type;
+ int payload_len;
+ int duration; /* in samples */
+
+ if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
+ /* initialize sequences */
+ rs->tx_action = RTP_SEND_DOWNSTREAM;
+ rs->transmit.ssrc = rand();
+ rs->transmit.sequence = random();
+ rs->transmit.timestamp = random();
+ }
+
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ payload_type = RTP_PT_GSM_FULL;
+ payload_len = 33;
+ duration = 160;
+ break;
+ case GSM_TCHF_FRAME_EFR:
+ payload_type = RTP_PT_GSM_EFR;
+ payload_len = 31;
+ duration = 160;
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ {
+ struct timeval tv, tv_diff;
+ long int usec_diff, frame_diff;
+
+ gettimeofday(&tv, NULL);
+ tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
+ rs->transmit.last_tv = tv;
+
+ usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
+ frame_diff = (usec_diff / 20000);
+
+ if (abs(frame_diff) > 1) {
+ long int frame_diff_excess = frame_diff - 1;
+
+ DEBUGP(DMUX, "Correcting frame difference of %ld frames\n", frame_diff_excess);
+ rs->transmit.sequence += frame_diff_excess;
+ rs->transmit.timestamp += frame_diff_excess * duration;
+ }
+ }
+
+ msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+ if (!msg)
+ return -ENOMEM;
+ rtph = (struct rtp_hdr *)msg->data;
+ rtph->version = RTP_VERSION;
+ rtph->padding = 0;
+ rtph->extension = 0;
+ rtph->csrc_count = 0;
+ rtph->marker = 0;
+ rtph->payload_type = payload_type;
+ rtph->sequence = htons(rs->transmit.sequence++);
+ rtph->timestamp = htonl(rs->transmit.timestamp);
+ rs->transmit.timestamp += duration;
+ rtph->ssrc = htonl(rs->transmit.ssrc);
+ memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
+ msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
/* iterate over all chunks in one RTCP message, look for CNAME IEs and
* replace all of those with 'new_cname' */
static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
@@ -123,10 +334,16 @@ static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
if (!mangle_rtcp_cname)
return 0;
+ printf("RTCP\n");
/* iterate over list of RTCP messages */
rtph = (struct rtcp_hdr *)msg->data;
- while ((void *)rtph + sizeof(*rtph) < (void *)msg->data + msg->len) {
+ while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
old_len = (ntohs(rtph->length) + 1) * 4;
+ if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
+ DEBUGPC(DMUX, "received RTCP packet too short for "
+ "length element\n");
+ return -EINVAL;
+ }
if (rtph->type == RTCP_TYPE_SDES) {
char new_cname[255];
strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
@@ -148,6 +365,7 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
{
int rc;
struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+ struct msgb *new_msg;
struct rtp_sub_socket *other_rss;
if (!msg)
@@ -184,13 +402,40 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
break;
case RTP_RECV_UPSTREAM:
- case RTP_NONE:
- /* FIXME: other cases */
- DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
+ if (!rs->receive.callref || !rs->receive.net) {
+ rc = -EIO;
+ goto out_free;
+ }
+ if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+ if (!mangle_rtcp_cname) {
+ msgb_free(msg);
+ break;
+ }
+ /* modify RTCP SDES CNAME */
+ rc = rtcp_mangle(msg, rs);
+ if (rc < 0)
+ goto out_free;
+ msgb_enqueue(&rss->tx_queue, msg);
+ rss->bfd.when |= BSC_FD_WRITE;
+ break;
+ }
+ if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
+ rc = -EINVAL;
+ goto out_free;
+ }
+ rc = rtp_decode(msg, rs->receive.callref, &new_msg);
+ if (rc < 0)
+ goto out_free;
+ msgb_free(msg);
+ msgb_enqueue(&rs->receive.net->upqueue, new_msg);
+ break;
+
+ case RTP_NONE: /* if socket exists, but disabled by app */
+ msgb_free(msg);
break;
}
- return rc;
+ return 0;
out_free:
msgb_free(msg);
@@ -211,7 +456,7 @@ static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
written = write(rss->bfd.fd, msg->data, msg->len);
if (written < msg->len) {
- perror("short write");
+ LOGP(DMIB, LOGL_ERROR, "short write");
msgb_free(msg);
return -EIO;
}
@@ -420,6 +665,23 @@ int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
return 0;
}
+/* bind RTP/RTCP socket to application */
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
+ u_int32_t callref)
+{
+ DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
+ this, callref);
+
+ if (callref) {
+ this->rx_action = RTP_RECV_UPSTREAM;
+ this->receive.net = net;
+ this->receive.callref = callref;
+ } else
+ this->rx_action = RTP_NONE;
+
+ return 0;
+}
+
static void free_tx_queue(struct rtp_sub_socket *rss)
{
struct msgb *msg;
diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c
index aa1b6d46d..830d84709 100644
--- a/openbsc/src/system_information.c
+++ b/openbsc/src/system_information.c
@@ -23,6 +23,7 @@
#include <errno.h>
#include <string.h>
+#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
@@ -30,27 +31,38 @@
#include <openbsc/gsm_data.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/rest_octets.h>
+#include <openbsc/bitvec.h>
+#include <openbsc/debug.h>
#define GSM48_CELL_CHAN_DESC_SIZE 16
#define GSM_MACBLOCK_LEN 23
#define GSM_MACBLOCK_PADDING 0x2b
-static int cchan_list_bm0_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
+/* Frequency Lists as per TS 04.08 10.5.2.13 */
+
+/* 10.5.2.13.2: Bit map 0 format */
+static int freq_list_bm0_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
{
unsigned int byte, bit;
- if (arfcn > 124)
+ if (arfcn > 124 || arfcn < 1) {
+ LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n");
return -EINVAL;
+ }
+
+ /* the bitmask is from 1..124, not from 0..123 */
+ arfcn--;
byte = arfcn / 8;
bit = arfcn % 8;
- chan_list[GSM48_CELL_CHAN_DESC_SIZE-byte] |= (1 << bit);
+ chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit);
return 0;
}
-static int cchan_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
+/* 10.5.2.13.7: Variable bit map format */
+static int freq_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
{
unsigned int byte, bit;
unsigned int min_arfcn;
@@ -64,10 +76,14 @@ static int cchan_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
if (arfcn == min_arfcn)
return 0;
- if (arfcn < min_arfcn)
+ if (arfcn < min_arfcn) {
+ LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn);
return -EINVAL;
- if (arfcn > min_arfcn + 111)
+ }
+ if (arfcn > min_arfcn + 111) {
+ LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn);
return -EINVAL;
+ }
bitno = (arfcn - min_arfcn);
byte = bitno / 8;
@@ -79,20 +95,23 @@ static int cchan_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
}
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-static int generate_cell_chan_list(u_int8_t *chan_list, const struct gsm_bts *bts)
+static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv,
+ const struct gsm_bts *bts)
{
- struct gsm_bts_trx *trx;
- int rc, min = 1024, max = 0;
+ int i, rc, min = 1024, max = 0;
memset(chan_list, 0, 16);
/* GSM900-only handsets only support 'bit map 0 format' */
if (bts->band == GSM_BAND_900) {
chan_list[0] = 0;
- llist_for_each_entry(trx, &bts->trx_list, list) {
- rc = cchan_list_bm0_set_arfcn(chan_list, trx->arfcn);
- if (rc < 0)
- return rc;
+
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i)) {
+ rc = freq_list_bm0_set_arfcn(chan_list, i);
+ if (rc < 0)
+ return rc;
+ }
}
return 0;
}
@@ -100,83 +119,68 @@ static int generate_cell_chan_list(u_int8_t *chan_list, const struct gsm_bts *bt
/* We currently only support the 'Variable bitmap format' */
chan_list[0] = 0x8e;
- llist_for_each_entry(trx, &bts->trx_list, list) {
- if (trx->arfcn < min)
- min = trx->arfcn;
- if (trx->arfcn > max)
- max = trx->arfcn;
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i)) {
+ if (i < min)
+ min = i;
+ if (i > max)
+ max = i;
+ }
}
- if ((max - min) > 111)
+ if ((max - min) > 111) {
+ LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, "
+ "distance > 111\n", min, max);
return -EINVAL;
+ }
chan_list[0] |= (min >> 9) & 1;
chan_list[1] = (min >> 1);
chan_list[2] = (min & 1) << 7;
- llist_for_each_entry(trx, &bts->trx_list, list) {
- rc = cchan_list_bmrel_set_arfcn(chan_list, trx->arfcn);
- if (rc < 0)
- return rc;
+ for (i = 0; i < bv->data_len*8; i++) {
+ if (bitvec_get_bit_pos(bv, i)) {
+ rc = freq_list_bmrel_set_arfcn(chan_list, i);
+ if (rc < 0)
+ return rc;
+ }
}
return 0;
}
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
-static int generate_bcch_chan_list(u_int8_t *chan_list, const struct gsm_bts *bts)
+static int generate_cell_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
{
- struct gsm_bts *cur_bts;
struct gsm_bts_trx *trx;
- int rc, min = 1024, max = 0;
+ struct bitvec *bv = &bts->si_common.cell_alloc;
- memset(chan_list, 0, 16);
+ /* first we generate a bitvec of all TRX ARFCN's in our BTS */
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ bitvec_set_bit_pos(bv, trx->arfcn, 1);
- /* GSM900-only handsets only support 'bit map 0 format' */
- if (bts->band == GSM_BAND_900) {
- chan_list[0] = 0;
- llist_for_each_entry(cur_bts, &bts->list, list) {
- trx = cur_bts->c0;
- rc = cchan_list_bm0_set_arfcn(chan_list, trx->arfcn);
- if (rc < 0)
- return rc;
- }
- return 0;
- }
-
- /* We currently only support the 'Variable bitmap format' */
- chan_list[0] = 0x8e;
-
- llist_for_each_entry(cur_bts, &bts->list, list) {
- if (&cur_bts->list == &bts->network->bts_list)
- continue;
- trx = cur_bts->c0;
- if (trx->arfcn < min)
- min = trx->arfcn;
- if (trx->arfcn > max)
- max = trx->arfcn;
- }
-
- if ((max - min) > 111)
- return -EINVAL;
+ /* then we generate a GSM 04.08 frequency list from the bitvec */
+ return bitvec2freq_list(chan_list, bv, bts);
+}
- chan_list[0] |= (min >> 9) & 1;
- chan_list[1] = (min >> 1);
- chan_list[2] = (min & 1) << 7;
+/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
+static int generate_bcch_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
+{
+ struct gsm_bts *cur_bts;
+ struct bitvec *bv = &bts->si_common.neigh_list;
- llist_for_each_entry(cur_bts, &bts->list, list) {
- if (&cur_bts->list == &bts->network->bts_list)
+ /* first we generate a bitvec of the BCCH ARFCN's in our BSC */
+ llist_for_each_entry(cur_bts, &bts->network->bts_list, list) {
+ if (cur_bts == bts)
continue;
- trx = cur_bts->c0;
- rc = cchan_list_bmrel_set_arfcn(chan_list, trx->arfcn);
- if (rc < 0)
- return rc;
+ bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1);
}
- return 0;
+ /* then we generate a GSM 04.08 frequency list from the bitvec */
+ return bitvec2freq_list(chan_list, bv, bts);
}
-static int generate_si1(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si1(u_int8_t *output, struct gsm_bts *bts)
{
int rc;
struct gsm48_system_information_type_1 *si1 =
@@ -201,7 +205,7 @@ static int generate_si1(u_int8_t *output, const struct gsm_bts *bts)
return GSM_MACBLOCK_LEN;
}
-static int generate_si2(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si2(u_int8_t *output, struct gsm_bts *bts)
{
int rc;
struct gsm48_system_information_type_2 *si2 =
@@ -248,7 +252,7 @@ struct gsm48_si_ro_info si_info = {
.break_ind = 0,
};
-static int generate_si3(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si3(u_int8_t *output, struct gsm_bts *bts)
{
struct gsm48_system_information_type_3 *si3 =
(struct gsm48_system_information_type_3 *) output;
@@ -278,7 +282,7 @@ static int generate_si3(u_int8_t *output, const struct gsm_bts *bts)
return GSM_MACBLOCK_LEN;
}
-static int generate_si4(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si4(u_int8_t *output, struct gsm_bts *bts)
{
struct gsm48_system_information_type_4 *si4 =
(struct gsm48_system_information_type_4 *) output;
@@ -310,12 +314,18 @@ static int generate_si4(u_int8_t *output, const struct gsm_bts *bts)
return GSM_MACBLOCK_LEN;
}
-static int generate_si5(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
{
- struct gsm48_system_information_type_5 *si5 =
- (struct gsm48_system_information_type_5 *) output;
- int rc;
+ struct gsm48_system_information_type_5 *si5;
+ int rc, l2_plen = 18;
+
+ /* ip.access nanoBTS needs l2_plen!! */
+ if (is_ipaccess_bts(bts)) {
+ *output++ = (l2_plen << 2) | 1;
+ l2_plen++;
+ }
+ si5 = (struct gsm48_system_information_type_5 *) output;
memset(si5, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
/* l2 pseudo length, not part of msg: 18 */
@@ -327,14 +337,21 @@ static int generate_si5(u_int8_t *output, const struct gsm_bts *bts)
return rc;
/* 04.08 9.1.37: L2 Pseudo Length of 18 */
- return 18;
+ return l2_plen;
}
-static int generate_si6(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
{
- struct gsm48_system_information_type_6 *si6 =
- (struct gsm48_system_information_type_6 *) output;
+ struct gsm48_system_information_type_6 *si6;
+ int l2_plen = 11;
+
+ /* ip.access nanoBTS needs l2_plen!! */
+ if (is_ipaccess_bts(bts)) {
+ *output++ = (l2_plen << 2) | 1;
+ l2_plen++;
+ }
+ si6 = (struct gsm48_system_information_type_6 *) output;
memset(si6, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
/* l2 pseudo length, not part of msg: 11 */
@@ -350,7 +367,7 @@ static int generate_si6(u_int8_t *output, const struct gsm_bts *bts)
/* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */
- return 18;
+ return l2_plen;
}
static struct gsm48_si13_info si13_default = {
@@ -393,7 +410,7 @@ static u_int8_t si13_template[] = {
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
};
-static int generate_si13(u_int8_t *output, const struct gsm_bts *bts)
+static int generate_si13(u_int8_t *output, struct gsm_bts *bts)
{
struct gsm48_system_information_type_13 *si13 =
(struct gsm48_system_information_type_13 *) output;
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index 128c34e94..2d7b05c70 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -35,6 +35,7 @@
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
+#include <openbsc/debug.h>
#include <vty/buffer.h>
@@ -71,7 +72,7 @@ void telnet_init(struct gsm_network *network, int port) {
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
- perror("Telnet interface socket creation failed");
+ LOGP(DNM, LOGL_ERROR, "Telnet interface socket creation failed\n");
return;
}
@@ -83,12 +84,12 @@ void telnet_init(struct gsm_network *network, int port) {
sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
- perror("Telnet interface failed to bind");
+ LOGP(DNM, LOG_ERROR, "Telnet interface failed to bind\n");
return;
}
if (listen(fd, 0) < 0) {
- perror("Telnet interface failed to listen");
+ LOGP(DNM, LOG_ERROR, "Telnet interface failed to listen\n");
return;
}
@@ -154,7 +155,7 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
if (new_connection < 0) {
- perror("telnet accept failed");
+ LOGP(DNM, LOGL_ERROR, "telnet accept failed\n");
return -1;
}
@@ -171,8 +172,10 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
print_welcome(new_connection);
connection->vty = vty_create(new_connection, connection);
- if (!connection->vty)
+ if (!connection->vty) {
+ LOGP(DNM, LOGL_ERROR, "couldn't create VTY\n");
return -1;
+ }
return 0;
}
diff --git a/openbsc/src/token_auth.c b/openbsc/src/token_auth.c
index 0931007ef..f6be0bc98 100644
--- a/openbsc/src/token_auth.c
+++ b/openbsc/src/token_auth.c
@@ -60,10 +60,10 @@ static int token_subscr_cb(unsigned int subsys, unsigned int signal,
struct gsm_sms *sms;
int rc = 0;
- if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
+ if (signal != S_SUBSCR_ATTACHED)
return 0;
- if (signal != S_SUBSCR_ATTACHED)
+ if (subscr->net->auth_policy != GSM_AUTH_POLICY_TOKEN)
return 0;
if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) {
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index 04eaa3c99..e49f75b28 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -133,10 +133,34 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
used_tid_bitmask |= (1 << trans->transaction_id);
}
- for (i = 0; i <= 7; i++) {
+ for (i = 0; i < 7; i++) {
if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0)
return i | ti_flag;
}
return -1;
}
+
+/* update all transactions to use a different LCHAN, e.g.
+ * after handover has succeeded */
+int trans_lchan_change(struct gsm_lchan *lchan_old,
+ struct gsm_lchan *lchan_new)
+{
+ struct gsm_network *net = lchan_old->ts->trx->bts->network;
+ struct gsm_trans *trans;
+ int num = 0;
+
+ llist_for_each_entry(trans, &net->trans_list, entry) {
+ if (trans->lchan == lchan_old) {
+ /* drop old channel use cound */
+ put_lchan(trans->lchan);
+ /* assign new channel */
+ trans->lchan = lchan_new;
+ /* bump new channel use count */
+ use_lchan(trans->lchan);
+ num++;
+ }
+ }
+
+ return num;
+}
diff --git a/openbsc/src/trau_frame.c b/openbsc/src/trau_frame.c
index aa039574b..2bc61a513 100644
--- a/openbsc/src/trau_frame.c
+++ b/openbsc/src/trau_frame.c
@@ -107,11 +107,13 @@ int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
case TRAU_FT_DATA_DOWN:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
- DEBUGP(DMUX, "can't decode unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU "
+ "Frame Type 0x%02x\n", cbits5);
return -1;
break;
default:
- DEBUGP(DMUX, "can't decode unknown TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU "
+ "Frame Type 0x%02x\n", cbits5);
return -1;
break;
}
@@ -162,11 +164,13 @@ int trau_frame_up2down(struct decoded_trau_frame *fr)
case TRAU_FT_DATA_UP:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
- DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
default:
- DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
}
@@ -224,11 +228,13 @@ int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
case TRAU_FT_DATA_DOWN:
case TRAU_FT_D145_SYNC:
case TRAU_FT_EDATA:
- DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
default:
- DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
+ LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+ "0x%02x\n", cbits5);
return -1;
break;
}
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 6a19f0c99..9930751a5 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -32,6 +32,19 @@
#include <openbsc/debug.h>
#include <openbsc/talloc.h>
+u_int8_t gsm_fr_map[] = {
+ 6, 6, 5, 5, 4, 4, 3, 3,
+ 7, 2, 2, 6, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 7, 2, 2, 6, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 7, 2, 2, 6, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 7, 2, 2, 6, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
struct map_entry {
struct llist_head list;
struct gsm_e1_subslot src, dst;
@@ -56,8 +69,10 @@ int trau_mux_map(const struct gsm_e1_subslot *src,
struct map_entry *me;
me = talloc(tall_map_ctx, struct map_entry);
- if (!me)
+ if (!me) {
+ LOGP(DMIB, LOGL_FATAL, "Out of memory\n");
return -ENOMEM;
+ }
DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
"and (e1=%u,ts=%u,ss=%u)\n",
@@ -142,6 +157,8 @@ lookup_trau_upqueue(const struct gsm_e1_subslot *src)
return NULL;
}
+static const u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
+
/* we get called by subchan_demux */
int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
const u_int8_t *trau_bits, int num_bits)
@@ -151,8 +168,6 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
struct subch_mux *mx;
struct upqueue_entry *ue;
- struct msgb *msg;
- struct gsm_trau_frame *frame;
int rc;
/* decode TRAU, change it to downlink, re-encode */
@@ -161,19 +176,44 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
return rc;
if (!dst_e1_ss) {
+ struct msgb *msg;
+ struct gsm_data_frame *frame;
+ unsigned char *data;
+ int i, j, k, l, o;
/* frame shall be sent to upqueue */
if (!(ue = lookup_trau_upqueue(src_e1_ss)))
return -EINVAL;
if (!ue->callref)
return -EINVAL;
- msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf),
- "TRAU");
+ if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
+ DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n",
+ hexdump(tf.c_bits, sizeof(c_bits_check)));
+ msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+ "GSM-DATA");
if (!msg)
return -ENOMEM;
- frame = (struct gsm_trau_frame *)msg->data;
- frame->msg_type = GSM_TRAU_FRAME;
+
+ frame = (struct gsm_data_frame *)msg->data;
+ memset(frame, 0, sizeof(struct gsm_data_frame));
+ data = frame->data;
+ data[0] = 0xd << 4;
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts output bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset input bits */
+ while (i < 260) {
+ data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ frame->msg_type = GSM_TCHF_FRAME;
frame->callref = ue->callref;
- memcpy(frame->data, &tf, sizeof(tf));
msgb_enqueue(&ue->net->upqueue, msg);
return 0;
@@ -219,17 +259,53 @@ int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
return 0;
}
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
{
u_int8_t trau_bits_out[TRAU_FRAME_BITS];
struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
struct subch_mux *mx;
+ int i, j, k, l, o;
+ unsigned char *data = frame->data;
+ struct decoded_trau_frame tf;
mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
if (!mx)
return -EINVAL;
- encode_trau_frame(trau_bits_out, tf);
+ switch (frame->msg_type) {
+ case GSM_TCHF_FRAME:
+ /* set c-bits and t-bits */
+ tf.c_bits[0] = 1;
+ tf.c_bits[1] = 1;
+ tf.c_bits[2] = 1;
+ tf.c_bits[3] = 0;
+ tf.c_bits[4] = 0;
+ memset(&tf.c_bits[5], 0, 6);
+ memset(&tf.c_bits[11], 1, 10);
+ memset(&tf.t_bits[0], 1, 4);
+ /* reassemble d-bits */
+ i = 0; /* counts bits */
+ j = 4; /* counts input bits */
+ k = gsm_fr_map[0]-1; /* current number bit in element */
+ l = 0; /* counts element bits */
+ o = 0; /* offset output bits */
+ while (i < 260) {
+ tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
+ if (--k < 0) {
+ o += gsm_fr_map[l];
+ k = gsm_fr_map[++l]-1;
+ }
+ i++;
+ j++;
+ }
+ break;
+ default:
+ DEBUGPC(DMUX, "unsupported message type %d\n",
+ frame->msg_type);
+ return -EINVAL;
+ }
+
+ encode_trau_frame(trau_bits_out, &tf);
/* and send it to the muxer */
return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c
index affe28d8d..788c7fd6f 100644
--- a/openbsc/src/vty/vty.c
+++ b/openbsc/src/vty/vty.c
@@ -236,6 +236,8 @@ int vty_out(struct vty *vty, const char *format, ...)
talloc_free(p);
}
+ vty_event(VTY_WRITE, vty->fd, vty);
+
return len;
}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 5400bec1b..dfba6810f 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -85,10 +85,18 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
net->name_short, VTY_NEWLINE);
vty_out(vty, " Authentication policy: %s%s",
gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
+ vty_out(vty, " Location updating reject cause: %u%s",
+ net->reject_cause, VTY_NEWLINE);
vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,
VTY_NEWLINE);
vty_out(vty, " NECI (TCH/H): %u%s", net->neci,
VTY_NEWLINE);
+ vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode),
+ VTY_NEWLINE);
+ vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off",
+ VTY_NEWLINE);
+ vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off",
+ VTY_NEWLINE);
}
DEFUN(show_net, show_net_cmd, "show network",
@@ -126,7 +134,13 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
bts->cell_identity,
bts->location_area_code, bts->bsic, bts->tsc,
bts->num_trx, VTY_NEWLINE);
- if (bts->cell_barred)
+ vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
+ vty_out(vty, "Minimum Rx Level for Access: %i dBm%s",
+ rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
+ VTY_NEWLINE);
+ vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s",
+ bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts))
vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
@@ -235,13 +249,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE);
vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
+ vty_out(vty, " cell reselection hysteresis %u%s",
+ bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
+ vty_out(vty, " rxlev access min %u%s",
+ bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE);
if (bts->si_common.chan_desc.t3212)
vty_out(vty, " periodic location update %u%s",
bts->si_common.chan_desc.t3212 * 10, VTY_NEWLINE);
vty_out(vty, " channel allocator %s%s",
bts->chan_alloc_reverse ? "descending" : "ascending",
VTY_NEWLINE);
- if (bts->cell_barred)
+ if (bts->si_common.rach_control.cell_bar)
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
if (is_ipaccess_bts(bts)) {
vty_out(vty, " ip.access unit_id %u %u%s",
@@ -280,8 +298,26 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
+ vty_out(vty, " location updating reject cause %u%s",
+ gsmnet->reject_cause, VTY_NEWLINE);
vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE);
+ vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),
+ VTY_NEWLINE);
+ vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE);
+ vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE);
+ vty_out(vty, " handover window rxlev averaging %u%s",
+ gsmnet->handover.win_rxlev_avg, VTY_NEWLINE);
+ vty_out(vty, " handover window rxqual averaging %u%s",
+ gsmnet->handover.win_rxqual_avg, VTY_NEWLINE);
+ vty_out(vty, " handover window rxlev neighbor averaging %u%s",
+ gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE);
+ vty_out(vty, " handover power budget interval %u%s",
+ gsmnet->handover.pwr_interval, VTY_NEWLINE);
+ vty_out(vty, " handover power budget hysteresis %u%s",
+ gsmnet->handover.pwr_hysteresis, VTY_NEWLINE);
+ vty_out(vty, " handover maximum distance %u%s",
+ gsmnet->handover.max_distance, VTY_NEWLINE);
vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE);
vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE);
vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE);
@@ -372,24 +408,15 @@ DEFUN(show_trx,
static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
{
- struct in_addr ia;
-
vty_out(vty, "Timeslot %u of TRX %u in BTS %u, phys cfg %s%s",
ts->nr, ts->trx->nr, ts->trx->bts->nr,
gsm_pchan_name(ts->pchan), VTY_NEWLINE);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &ts->nm_state);
- if (is_ipaccess_bts(ts->trx->bts)) {
- ia.s_addr = ts->abis_ip.bound_ip;
- vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
- inet_ntoa(ia), ts->abis_ip.bound_port,
- ts->abis_ip.rtp_payload2, ts->abis_ip.conn_id,
- VTY_NEWLINE);
- } else {
+ if (!is_ipaccess_bts(ts->trx->bts))
vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s",
ts->e1_link.e1_nr, ts->e1_link.e1_ts,
ts->e1_link.e1_ts_ss, VTY_NEWLINE);
- }
}
DEFUN(show_ts,
@@ -471,13 +498,24 @@ static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
lchan->ts->trx->bts->nr, gsm_lchan_name(lchan->type),
VTY_NEWLINE);
vty_out(vty, " Use Count: %u%s", lchan->use_count, VTY_NEWLINE);
- vty_out(vty, " BS Power %u, MS Power %u%s", lchan->bs_power,
- lchan->ms_power, VTY_NEWLINE);
+ vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s",
+ lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
+ - lchan->bs_power*2,
+ ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
+ VTY_NEWLINE);
if (lchan->subscr) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
subscr_dump_vty(vty, lchan->subscr);
} else
vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
+ if (is_ipaccess_bts(lchan->ts->trx->bts)) {
+ struct in_addr ia;
+ ia.s_addr = lchan->abis_ip.bound_ip;
+ vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s",
+ inet_ntoa(ia), lchan->abis_ip.bound_port,
+ lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id,
+ VTY_NEWLINE);
+ }
}
#if 0
@@ -800,6 +838,16 @@ DEFUN(cfg_net_auth_policy,
return CMD_SUCCESS;
}
+DEFUN(cfg_net_reject_cause,
+ cfg_net_reject_cause_cmd,
+ "location updating reject cause <2-111>",
+ "Set the reject cause of location updating reject\n")
+{
+ gsmnet->reject_cause = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_net_encryption,
cfg_net_encryption_cmd,
"encryption a5 (0|1|2)",
@@ -819,6 +867,87 @@ DEFUN(cfg_net_neci,
return CMD_SUCCESS;
}
+DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
+ "rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
+ "Set the Radio Resource Location Protocol Mode")
+{
+ gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd,
+ "mm info (0|1)",
+ "Whether to send MM INFO after LOC UPD ACCEPT")
+{
+ gsmnet->send_mm_info = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_handover, cfg_net_handover_cmd,
+ "handover (0|1)",
+ "Whether or not to use in-call handover")
+{
+ if (ipacc_rtp_direct) {
+ vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode "
+ "is enabled by using the -P command line option%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ gsmnet->handover.active = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
+ "handover window rxlev averaging <1-10>",
+ "How many RxLev measurements are used for averaging")
+{
+ gsmnet->handover.win_rxlev_avg = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
+ "handover window rxqual averaging <1-10>",
+ "How many RxQual measurements are used for averaging")
+{
+ gsmnet->handover.win_rxqual_avg = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
+ "handover window rxlev neighbor averaging <1-10>",
+ "How many RxQual measurements are used for averaging")
+{
+ gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
+ "handover power budget interval <1-99>",
+ "How often to check if we have a better cell (SACCH frames)")
+{
+ gsmnet->handover.pwr_interval = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
+ "handover power budget hysteresis <0-999>",
+ "How many dB does a neighbor to be stronger to become a HO candidate")
+{
+ gsmnet->handover.pwr_hysteresis = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
+ "handover maximum distance <0-9999>",
+ "How big is the maximum timing advance before HO is forced")
+{
+ gsmnet->handover.max_distance = atoi(argv[0]);
+ return CMD_SUCCESS;
+}
+
#define DECLARE_TIMER(number) \
DEFUN(cfg_net_T##number, \
cfg_net_T##number##_cmd, \
@@ -1080,7 +1209,7 @@ DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
{
struct gsm_bts *bts = vty->index;
- bts->cell_barred = atoi(argv[0]);
+ bts->si_common.rach_control.cell_bar = atoi(argv[0]);
return CMD_SUCCESS;
}
@@ -1096,6 +1225,28 @@ DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd,
+ "cell reselection hysteresis <0-14>",
+ "Cell Re-Selection Hysteresis in dB")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd,
+ "rxlev access min <0-63>",
+ "Minimum RxLev needed for cell access (better than -110dBm)")
+{
+ struct gsm_bts *bts = vty->index;
+
+ bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd,
"periodic location update <0-1530>",
"Periodic Location Updating Interval in Minutes")
@@ -1334,8 +1485,18 @@ int bsc_vty_init(struct gsm_network *net)
install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
+ install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
install_element(GSMNET_NODE, &cfg_net_neci_cmd);
+ install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
+ install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
+ install_element(GSMNET_NODE, &cfg_net_handover_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd);
+ install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd);
install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
@@ -1365,6 +1526,8 @@ int bsc_vty_init(struct gsm_network *net)
install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
+ install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
+ install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd);
install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd);
diff --git a/wireshark/abis_oml.patch b/wireshark/abis_oml.patch
index dc51f76c1..1b2439c86 100644
--- a/wireshark/abis_oml.patch
+++ b/wireshark/abis_oml.patch
@@ -1,8 +1,8 @@
Index: wireshark/epan/dissectors/Makefile.common
===================================================================
---- wireshark.orig/epan/dissectors/Makefile.common 2009-10-21 23:03:44.000000000 +0200
-+++ wireshark/epan/dissectors/Makefile.common 2009-10-21 23:03:57.000000000 +0200
-@@ -472,6 +472,7 @@
+--- wireshark.orig/epan/dissectors/Makefile.common
++++ wireshark/epan/dissectors/Makefile.common
+@@ -474,6 +474,7 @@
packet-gsm_a_gm.c \
packet-gsm_a_rp.c \
packet-gsm_a_rr.c \
@@ -12,8 +12,8 @@ Index: wireshark/epan/dissectors/Makefile.common
packet-gsm_bssmap_le.c \
Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c
===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ wireshark/epan/dissectors/packet-gsm_abis_oml.c 2009-10-22 10:06:18.000000000 +0200
+--- /dev/null
++++ wireshark/epan/dissectors/packet-gsm_abis_oml.c
@@ -0,0 +1,1365 @@
+/* packet-abis_oml.c
+ * Routines for packet dissection of GSM A-bis over IP (3GPP TS 12.21)
@@ -1377,13 +1377,13 @@ Index: wireshark/epan/dissectors/packet-gsm_abis_oml.c
+{
+ dissector_handle_t abis_oml_handle;
+
-+ abis_oml_handle = find_dissector("abis_oml");
++ abis_oml_handle = create_dissector_handle(dissect_abis_oml, proto_abis_oml);
+ dissector_add("lapd.gsm.sapi", LAPD_GSM_SAPI_OM_PROC, abis_oml_handle);
+}
Index: wireshark/epan/dissectors/packet-gsm_abis_oml.h
===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ wireshark/epan/dissectors/packet-gsm_abis_oml.h 2009-10-21 23:03:57.000000000 +0200
+--- /dev/null
++++ wireshark/epan/dissectors/packet-gsm_abis_oml.h
@@ -0,0 +1,786 @@
+/* GSM Network Management messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */