aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2009-12-22 07:58:24 +0100
committerHolger Hans Peter Freyther <zecke@selfish.org>2009-12-22 08:02:13 +0100
commit5c18ad08293ae6f95edd75e6bcc370fd6cff069b (patch)
tree120273d6da84a0bdb52b32f981714e9bcaea0f59 /openbsc
parent0d9ed87d5c7d5b6e21dc3bbb282e215068742566 (diff)
parent4f5456c040c2dd0c53fe28cb51219d668d464a21 (diff)
Merge commit 'origin/master' into on-waves/bsc-master
Conflicts: openbsc/include/openbsc/Makefile.am openbsc/include/openbsc/gsm_data.h openbsc/src/Makefile.am openbsc/src/abis_rsl.c openbsc/src/chan_alloc.c openbsc/src/gsm_04_08.c openbsc/src/gsm_data.c openbsc/src/vty_interface.c The biggest problem is the moving of the RTP code into the RSL layer. This may break quite some things...
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/doc/handover.txt89
-rw-r--r--openbsc/include/openbsc/Makefile.am3
-rw-r--r--openbsc/include/openbsc/abis_rsl.h8
-rw-r--r--openbsc/include/openbsc/bitvec.h65
-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.h70
-rw-r--r--openbsc/include/openbsc/gsm_data.h142
-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.h84
-rw-r--r--openbsc/include/openbsc/mncc.h13
-rw-r--r--openbsc/include/openbsc/rest_octets.h122
-rw-r--r--openbsc/include/openbsc/rtp_proxy.h17
-rw-r--r--openbsc/include/openbsc/signal.h13
-rw-r--r--openbsc/include/openbsc/system_information.h6
-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.am7
-rw-r--r--[-rwxr-xr-x]openbsc/src/abis_nm.c106
-rw-r--r--openbsc/src/abis_rsl.c473
-rw-r--r--openbsc/src/bitvec.c170
-rw-r--r--openbsc/src/bsc_hack.c10
-rw-r--r--openbsc/src/bsc_init.c369
-rw-r--r--openbsc/src/bsc_msc_ip.c1
-rw-r--r--openbsc/src/chan_alloc.c27
-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.c31
-rw-r--r--openbsc/src/gsm_04_08.c389
-rw-r--r--openbsc/src/gsm_04_08_utils.c125
-rw-r--r--openbsc/src/gsm_04_11.c264
-rw-r--r--openbsc/src/gsm_data.c71
-rw-r--r--openbsc/src/gsm_utils.c38
-rw-r--r--openbsc/src/handover_decision.c298
-rw-r--r--openbsc/src/handover_logic.c367
-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/paging.c9
-rw-r--r--openbsc/src/rest_octets.c394
-rw-r--r--openbsc/src/rrlp.c48
-rw-r--r--openbsc/src/rtp_proxy.c274
-rw-r--r--openbsc/src/system_information.c444
-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.c217
56 files changed, 4351 insertions, 928 deletions
diff --git a/openbsc/doc/handover.txt b/openbsc/doc/handover.txt
new file mode 100644
index 000000000..ac19e8725
--- /dev/null
+++ b/openbsc/doc/handover.txt
@@ -0,0 +1,89 @@
+Ideas about a handover algorithm
+======================================================================
+
+This is mostly based on the results presented in Chapter 8 of "Performance
+Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen
+and Joeroen Wigard.
+
+
+=== Reasons for performing handover ===
+
+Section 2.1.1: Handover used in their CAPACITY simulation:
+
+1) Interference Handover
+
+Average RXLEV is satisfactory high, but average RXQUAL too low indicates
+interference to the channel. Handover should be made.
+
+2) Bad Quality
+
+Averaged RXQUAL is lower than a threshold
+
+3) Low Level / Signal Strength
+
+Average RXLEV is lower than a threshold
+
+4) Distance Handover
+
+MS is too far away from a cell (measured by TA)
+
+5) Power budget / Better Cell
+
+RX Level of neighbor cell is at least "HO Margin dB" dB better than the
+current serving cell.
+
+=== Ideal parameters for HO algorithm ===
+
+Chapter 8, Section 2.2, Table 24:
+
+Window RXLEV averaging: 10 SACCH frames (no weighting)
+Window RXQUAL averaging: 1 SACCH frame (no averaging)
+Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm
+Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5
+Interference Threshold: 1 of the last AV-RXLEV > -85 dBm &
+ 3 of the last 4 AV-RXQUAL values >= 5
+Power Budget: Level of neighbor cell > 3 dB better
+Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?)
+Distance Handover: Disabled
+Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm
+Evaluation rule 2: Level of candidate cell > 3dB better own cell
+Timer Successful HO: 5 SACCH frames
+Timer Unsuccessful HO: 1 SACCH frame
+
+In a non-frequency hopping case, RXQUAL threshold can be decreased to
+RXLEV >= 4
+
+When frequency hopping is enabled, the following additional parameters
+should be introduced:
+
+* No intra-cell handover
+* Use a HO Margin of 2dB
+
+=== Handover Channel Reservation ===
+
+In loaded network, each cell should reserve some channels for handovers,
+rather than using all of them for new call establishment. This reduces the
+need to drop calls due to failing handovers, at the expense of failing new call
+attempts.
+
+=== Dynamic HO Margin ===
+
+The handover margin (hysteresis) should depend on the RXQUAL. Optimal results
+were achieved with the following settings:
+* RXQUAL <= 4: 9 dB
+* RXQUAL == 5: 6 dB
+* RXQUAL >= 6: 1 dB
+
+
+
+== Actual Handover on a protocol level ==
+
+After the BSC has decided a handover shall be done, it has to
+
+# allocate a channel at the new BTS
+# allocate a handover reference
+# activate the channel on the BTS side using RSL CHANNEL ACTIVATION,
+ indicating the HO reference
+# BTS responds with CHAN ACT ACK, including GSM frame number
+# BSC sends 04.08 HO CMD to MS using old BTS
+
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 9f9928c90..8c9cd2e48 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 bssap.h
+ silent_call.h mgcp.h meas_rep.h bitvec.h rest_octets.h \
+ system_information.h handover.h bssap.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
new file mode 100644
index 000000000..b35aebf16
--- /dev/null
+++ b/openbsc/include/openbsc/bitvec.h
@@ -0,0 +1,65 @@
+#ifndef _BITVEC_H
+#define _BITVEC_H
+
+/* bit vector utility routines */
+
+/* (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.
+ *
+ */
+
+
+/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are
+ * defined relative to the 0x2b padding pattern */
+enum bit_value {
+ ZERO = 0,
+ ONE = 1,
+ L = 2,
+ H = 3,
+};
+
+struct bitvec {
+ unsigned int cur_bit; /* curser to the next unused bit */
+ unsigned int data_len; /* length of data array in bytes */
+ 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);
+
+/* Set the next bit in the vector */
+int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
+
+/* Set multiple bits at the current position */
+int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
+
+/* Add an unsigned integer (of length count bits) to current position */
+int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
+
+
+/* Pad the bit vector up to a certain bit position */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
+
+#endif /* _BITVEC_H */
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 447c3584f..fc387cec3 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -25,6 +25,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)
@@ -42,4 +44,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 b7c8a2662..b8671554e 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -1,6 +1,8 @@
#ifndef _GSM_04_08_H
#define _GSM_04_08_H
+#include <openbsc/meas_rep.h>
+
/* GSM TS 04.08 definitions */
struct gsm_lchan;
@@ -88,6 +90,22 @@ struct gsm48_ass_cmd {
u_int8_t data[0];
} __attribute__((packed));
+/* Chapter 10.5.2.2 */
+struct gsm48_cell_desc {
+ u_int8_t bcc:3,
+ ncc:3,
+ arfcn_hi:2;
+ u_int8_t arfcn_lo;
+} __attribute__((packed));
+
+/* Chapter 9.1.15 */
+struct gsm48_ho_cmd {
+ struct gsm48_cell_desc cell_desc;
+ struct gsm48_chan_desc chan_desc;
+ u_int8_t ho_ref;
+ u_int8_t power_command;
+ u_int8_t data[0];
+} __attribute__((packed));
/* Chapter 9.1.18 */
struct gsm48_imm_ass {
@@ -169,6 +187,13 @@ struct gsm48_control_channel_descr {
u_int8_t t3212;
} __attribute__ ((packed));
+struct gsm48_cell_options {
+ u_int8_t radio_link_timeout:4,
+ dtx:2,
+ pwrc:1,
+ spare:1;
+} __attribute__ ((packed));
+
/* Section 9.2.9 CM service request */
struct gsm48_service_request {
u_int8_t cm_service_type : 4,
@@ -185,7 +210,7 @@ struct gsm48_system_information_type_1 {
struct gsm48_system_information_type_header header;
u_int8_t cell_channel_description[16];
struct gsm48_rach_control rach_control;
- u_int8_t s1_reset;
+ u_int8_t rest_octets[0]; /* NCH position on the CCCH */
} __attribute__ ((packed));
/* Section 9.1.32 System information Type 2 */
@@ -202,10 +227,10 @@ struct gsm48_system_information_type_3 {
u_int16_t cell_identity;
struct gsm48_loc_area_id lai;
struct gsm48_control_channel_descr control_channel_desc;
- u_int8_t cell_options;
+ struct gsm48_cell_options cell_options;
struct gsm48_cell_sel_par cell_sel_par;
struct gsm48_rach_control rach_control;
- u_int8_t s3_reset_octets[4];
+ u_int8_t rest_octets[0];
} __attribute__ ((packed));
/* Section 9.1.36 System information Type 4 */
@@ -235,9 +260,15 @@ struct gsm48_system_information_type_6 {
u_int8_t system_information;
u_int16_t cell_identity;
struct gsm48_loc_area_id lai;
- u_int8_t cell_options;
+ struct gsm48_cell_options cell_options;
u_int8_t ncc_permitted;
- u_int8_t si_6_reset[0];
+ u_int8_t rest_octets[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.43a System Information type 13 */
+struct gsm48_system_information_type_13 {
+ struct gsm48_system_information_type_header header;
+ u_int8_t rest_octets[0];
} __attribute__ ((packed));
/* Section 9.2.12 IMSI Detach Indication */
@@ -618,30 +649,6 @@ enum gsm48_reject_value {
GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16,
};
-
-/* 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;
-};
-
-struct gsm_meas_rep {
- unsigned int flags;
- u_int8_t rxlev_full;
- u_int8_t rxqual_full;
- u_int8_t rxlev_sub;
- u_int8_t rxqual_sub;
- int num_cell;
- struct gsm_meas_rep_cell cell[6];
-};
-#define MEAS_REP_F_DTX 0x01
-#define MEAS_REP_F_VALID 0x02
-#define MEAS_REP_F_BA1 0x04
-
-void gsm48_parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
- int len);
-
enum chreq_type {
CHREQ_T_EMERG_CALL,
CHREQ_T_CALL_REEST_TCH_F,
@@ -741,7 +748,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,
@@ -763,6 +769,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, u_int8_t ho_ref);
int bsc_upqueue(struct gsm_network *net);
@@ -782,5 +790,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr);
int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode);
int gsm48_rx_rr_modif_ack(struct msgb *msg);
+int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg);
+
#endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 874b499c9..4e1977e38 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]))
@@ -148,6 +156,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)
@@ -156,6 +178,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;
@@ -167,6 +196,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;
@@ -202,11 +233,21 @@ 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_int8_t rtp_payload2;
+ 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;
};
@@ -356,7 +397,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;
@@ -373,8 +413,6 @@ struct gsm_bts {
/* number of this BTS on given E1 link */
u_int8_t bts_nr;
- struct gsm48_control_channel_descr chan_desc;
-
/* paging state and control */
struct gsm_bts_paging_state paging;
@@ -385,11 +423,28 @@ struct gsm_bts {
struct gsm_nm_state nm_state;
} site_mgr;
+ /* parameters from which we build SYSTEM INFORMATION */
+ struct {
+ struct gsm48_rach_control rach_control;
+ u_int8_t ncc_permitted;
+ 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 */
union {
struct {
u_int16_t site_id;
u_int16_t bts_id;
+ u_int32_t flags;
} ip_access;
struct {
struct {
@@ -418,6 +473,49 @@ struct gsm_bts {
struct llist_head trx_list;
};
+/* Some statistics of our network */
+struct gsmnet_stats {
+ struct {
+ unsigned long total;
+ unsigned long no_channel;
+ } chreq;
+ struct {
+ unsigned long attempted;
+ unsigned long no_channel; /* no channel available */
+ unsigned long timeout; /* T3103 timeout */
+ unsigned long completed; /* HO COMPL received */
+ unsigned long failed; /* HO FAIL received */
+ } handover;
+ struct {
+ unsigned long attach;
+ unsigned long normal;
+ unsigned long periodic;
+ unsigned long detach;
+ } loc_upd_type;
+ struct {
+ unsigned long reject;
+ unsigned long accept;
+ } loc_upd_resp;
+ struct {
+ unsigned long attempted;
+ unsigned long detached;
+ unsigned long completed;
+ unsigned long expired;
+ } paging;
+ struct {
+ unsigned long submitted; /* MO SMS submissions */
+ unsigned long no_receiver;
+ unsigned long delivered; /* MT SMS deliveries */
+ unsigned long rp_err_mem;
+ unsigned long rp_err_other;
+ } sms;
+ struct {
+ unsigned long dialled; /* total number of dialled calls */
+ unsigned long alerted; /* we alerted the other end */
+ unsigned long connected;/* how many calls were accepted */
+ } call;
+};
+
enum gsm_auth_policy {
GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */
GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */
@@ -442,8 +540,28 @@ 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;
+
+ struct gsmnet_stats stats;
struct gsm_audio_support **audio_support;
int audio_length;
@@ -470,6 +588,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
@@ -501,6 +624,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);
@@ -526,6 +654,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)
{
@@ -554,6 +683,11 @@ 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);
+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
new file mode 100644
index 000000000..fd5fced43
--- /dev/null
+++ b/openbsc/include/openbsc/meas_rep.h
@@ -0,0 +1,84 @@
+#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 bsic;
+ u_int16_t arfcn;
+ unsigned int flags;
+};
+
+/* RX Level and RX Quality */
+struct gsm_rx_lev_qual {
+ u_int8_t rx_lev;
+ u_int8_t rx_qual;
+};
+
+/* unidirectional measumrement report */
+struct gsm_meas_rep_unidir {
+ struct gsm_rx_lev_qual full;
+ struct gsm_rx_lev_qual sub;
+};
+
+#define MEAS_REP_F_UL_DTX 0x01
+#define MEAS_REP_F_DL_VALID 0x02
+#define MEAS_REP_F_BA1 0x04
+#define MEAS_REP_F_DL_DTX 0x08
+#define MEAS_REP_F_MS_TO 0x10
+#define MEAS_REP_F_MS_L1 0x20
+#define MEAS_REP_F_FPC 0x40
+
+/* 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;
+
+ u_int8_t bs_power;
+ u_int8_t ms_timing_offset;
+ struct {
+ int8_t pwr; /* MS power in dBm */
+ 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/rest_octets.h b/openbsc/include/openbsc/rest_octets.h
new file mode 100644
index 000000000..4e72c0f87
--- /dev/null
+++ b/openbsc/include/openbsc/rest_octets.h
@@ -0,0 +1,122 @@
+#ifndef _REST_OCTETS_H
+#define _REST_OCTETS_H
+
+#include <sys/types.h>
+#include <openbsc/gsm_04_08.h>
+
+/* generate SI1 rest octets */
+int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos);
+
+struct gsm48_si_selection_params {
+ u_int16_t penalty_time:5,
+ temp_offs:3,
+ cell_resel_off:6,
+ cbq:1,
+ present:1;
+};
+
+struct gsm48_si_power_offset {
+ u_int8_t power_offset:2,
+ present:1;
+};
+
+struct gsm48_si3_gprs_ind {
+ u_int8_t si13_position:1,
+ ra_colour:3,
+ present:1;
+};
+
+struct gsm48_lsa_params {
+ u_int32_t prio_thr:3,
+ lsa_offset:3,
+ mcc:12,
+ mnc:12;
+ unsigned int present;
+};
+
+struct gsm48_si_ro_info {
+ struct gsm48_si_selection_params selection_params;
+ struct gsm48_si_power_offset power_offset;
+ u_int8_t si2ter_indicator;
+ u_int8_t early_cm_ctrl;
+ struct {
+ u_int8_t where:3,
+ present:1;
+ } scheduling;
+ struct gsm48_si3_gprs_ind gprs_ind;
+
+ /* SI 4 specific */
+ struct gsm48_lsa_params lsa_params;
+ u_int16_t cell_id;
+ u_int8_t break_ind; /* do we have SI7 + SI8 ? */
+};
+
+
+/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
+int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3);
+
+/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
+int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4);
+
+enum pbcch_carrier_type {
+ PBCCH_BCCH,
+ PBCCH_ARFCN,
+ PBCCH_MAIO
+};
+
+/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */
+enum gprs_nmo {
+ GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
+ GPRS_NMO_II = 1, /* all paging on CCCH */
+ GPRS_NMO_III = 2, /* no paging coordination */
+};
+
+struct gprs_cell_options {
+ enum gprs_nmo nmo;
+ /* T3168: wait for packet uplink assignment message */
+ u_int32_t t3168; /* in milliseconds */
+ /* T3192: wait for release of the TBF after reception of the final block */
+ u_int32_t t3192; /* in milliseconds */
+ u_int32_t drx_timer_max;/* in seconds */
+ u_int32_t bs_cv_max;
+};
+
+/* TS 04.60 Table 12.9.2 */
+struct gprs_power_ctrl_pars {
+ u_int8_t alpha;
+ u_int8_t t_avg_w;
+ u_int8_t t_avg_t;
+ u_int8_t pc_meas_chan;
+ u_int8_t n_avg_i;
+};
+
+struct gsm48_si13_info {
+ struct gprs_cell_options cell_opts;
+ struct gprs_power_ctrl_pars pwr_ctrl_pars;
+ u_int8_t bcch_change_mark;
+ u_int8_t si_change_field;
+ u_int8_t pbcch_present;
+
+ union {
+ struct {
+ u_int8_t rac;
+ u_int8_t spgc_ccch_sup;
+ u_int8_t net_ctrl_ord;
+ u_int8_t prio_acc_thr;
+ } no_pbcch;
+ struct {
+ u_int8_t psi1_rep_per;
+ u_int8_t pb;
+ u_int8_t tsc;
+ u_int8_t tn;
+ enum pbcch_carrier_type carrier_type;
+ u_int16_t arfcn;
+ u_int8_t maio;
+ } pbcch;
+ };
+};
+
+/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13);
+
+#endif /* _REST_OCTETS_H */
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 fee9d5bfd..8c815f89e 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -40,6 +40,7 @@ enum signal_subsystems {
SS_LCHAN,
SS_SUBSCR,
SS_SCALL,
+ SS_GLOBAL,
};
/* SS_PAGING signals */
@@ -58,6 +59,7 @@ enum signal_sms {
/* SS_ABISIP signals */
enum signal_abisip {
S_ABISIP_CRCX_ACK,
+ S_ABISIP_MDCX_ACK,
S_ABISIP_DLCX_IND,
};
@@ -78,12 +80,19 @@ enum signal_lchan {
* signal handler.
*/
S_LCHAN_UNEXPECTED_RELEASE,
+ S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */
+ S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */
+ S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */
+ S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */
+ S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */
+ S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */
};
/* SS_SUBSCR signals */
enum signal_subscr {
S_SUBSCR_ATTACHED,
S_SUBSCR_DETACHED,
+ S_SUBSCR_IDENTITY, /* we've received some identity information */
};
/* SS_SCALL signals */
@@ -93,6 +102,10 @@ enum signal_scall {
S_SCALL_DETACHED,
};
+enum signal_global {
+ S_GLOBAL_SHUTDOWN,
+};
+
typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data);
diff --git a/openbsc/include/openbsc/system_information.h b/openbsc/include/openbsc/system_information.h
new file mode 100644
index 000000000..982a9ac63
--- /dev/null
+++ b/openbsc/include/openbsc/system_information.h
@@ -0,0 +1,6 @@
+#ifndef _SYSTEM_INFO_H
+#define _SYSTEM_INFO_H
+
+int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type);
+
+#endif
diff --git a/openbsc/include/openbsc/tlv.h b/openbsc/include/openbsc/tlv.h
index 0cf4388d8..e970ce468 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 fef7ee001..789c52aa0 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -7,14 +7,15 @@ noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
noinst_HEADERS = vty/cardshell.h
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
- msgb.c select.c chan_alloc.c timer.c debug.c \
+ msgb.c select.c chan_alloc.c timer.c debug.c handover_logic.c \
gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.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 telnet_interface.c
+ talloc_ctx.c system_information.c bitvec.c rest_octets.c \
+ handover_decision.c meas_rep.c rtp_proxy.c telnet_interface.c
libmsc_a_SOURCES = gsm_subscriber.c db.c \
- mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
+ mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index b1fe97ddf..bb7248be7 100755..100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -635,7 +635,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;
}
@@ -671,7 +671,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;
@@ -682,7 +682,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;
@@ -719,7 +719,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;
}
@@ -738,7 +738,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;
@@ -1088,8 +1088,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;
}
@@ -1106,12 +1106,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;
}
@@ -1119,12 +1119,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);
@@ -1137,11 +1137,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;
}
@@ -1755,7 +1755,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:
@@ -1801,6 +1802,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 2ada2ac1e..053c71258 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -38,6 +38,8 @@
#include <openbsc/tlv.h>
#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
@@ -205,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;
}
@@ -240,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;
@@ -490,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) {
@@ -575,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;
@@ -602,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);
@@ -615,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);
@@ -838,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;
}
@@ -910,7 +921,11 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
* to assign the activated channel to the MS */
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;
}
@@ -929,6 +944,10 @@ 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);
return 0;
}
@@ -939,7 +958,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));
@@ -947,53 +967,144 @@ 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);
}
+static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
+ const char *prefix)
+{
+ 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)
+ DEBUGPC(DMEAS, "DTXd ");
+
+ print_meas_rep_uni(&mr->ul, "ul");
+ DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power);
+ if (mr->flags & MEAS_REP_F_MS_TO)
+ DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset);
+
+ if (mr->flags & MEAS_REP_F_MS_L1) {
+ 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);
+ }
+
+ if (mr->flags & MEAS_REP_F_UL_DTX)
+ DEBUGPC(DMEAS, "DTXu ");
+ if (mr->flags & MEAS_REP_F_BA1)
+ DEBUGPC(DMEAS, "BA1 ");
+ if (!(mr->flags & MEAS_REP_F_DL_VALID))
+ DEBUGPC(DMEAS, "NOT VALID ");
+ else
+ 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 = lchan_next_meas_rep(msg->lchan);
+ u_int8_t len;
+ const u_int8_t *val;
+ int rc;
+
+ /* 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;
+
+ memset(mr, 0, sizeof(*mr));
+ mr->lchan = msg->lchan;
- DEBUGPC(DMEAS, "MEASUREMENT RESULT ");
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
- if (TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR))
- DEBUGPC(DMEAS, "NR=%d ", *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR));
- if (TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS)) {
- u_int8_t len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
- const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
- if (len >= 3) {
- if (val[0] & 0x40)
- DEBUGPC(DMEAS, "DTXd ");
- DEBUGPC(DMEAS, "RXL-FULL-up=%d RXL-SUB-up=%d ",
- val[0] & 0x3f, val[1] & 0x3f);
- DEBUGPC(DMEAS, "RXQ-FULL-up=%d RXQ-SUB-up=%d ",
- val[2]>>3 & 0x7, val[2] & 0x7);
- }
+ if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) ||
+ !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) ||
+ !TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
+ return -EIO;
+
+ /* Mandatory Parts */
+ 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;
}
- if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
- DEBUGPC(DMEAS, "BS_POWER=%d ", *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))
- DEBUGPC(DMEAS, "MS_TO=%d ",
- *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET));
+ mr->ms_timing_offset =
+ *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET);
+
if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) {
- const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
- u_int8_t pwr_lvl = val[0] >> 3;
- DEBUGPC(DMEAS, "L1_MS_PWR=%ddBm ",
- ms_pwr_dbm(msg->trx->bts->band, pwr_lvl));
- DEBUGPC(DMEAS, "L1_FPC=%u ", val[0] & 0x04 ? 1 : 0);
- DEBUGPC(DMEAS, "L1_TA=%u ", val[1]);
+ 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);
+ if (val[0] & 0x04)
+ mr->flags |= MEAS_REP_F_FPC;
+ mr->ms_l1.ta = val[1];
}
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
- DEBUGPC(DMEAS, "L3\n");
msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
- return gsm0408_rcvmsg(msg, 0);
- } else
- DEBUGPC(DMEAS, "\n");
+ rc = gsm48_parse_meas_rep(mr, msg);
+ if (rc < 0)
+ return rc;
+ }
+
+ print_meas_rep(mr);
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, mr);
+
+ return 0;
+}
+
+/* Chapter 8.4.7 */
+static int rsl_rx_hando_det(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+
+ DEBUGP(DRSL, "HANDOVER DETECT ");
+
+ rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+
+ if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY))
+ DEBUGPC(DRSL, "access delay = %u\n",
+ *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY));
+ else
+ DEBUGPC(DRSL, "\n");
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_DETECT, msg->lchan);
return 0;
}
@@ -1025,8 +1136,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
case RSL_MT_MEAS_RES:
rc = rsl_rx_meas_res(msg);
break;
+ case RSL_MT_HANDO_DET:
+ rc = rsl_rx_hando_det(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:
@@ -1072,7 +1187,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));
@@ -1080,7 +1195,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;
}
@@ -1100,7 +1215,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",
@@ -1150,11 +1265,14 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
+ bts->network->stats.chreq.total++;
+
/* 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);
+ bts->network->stats.chreq.no_channel++;
/* FIXME: send some kind of reject ?!? */
return -ENOMEM;
}
@@ -1168,7 +1286,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));
@@ -1251,12 +1369,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;
}
@@ -1268,7 +1386,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);
@@ -1349,38 +1467,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);
@@ -1388,12 +1579,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;
@@ -1401,12 +1592,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));
@@ -1414,34 +1604,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);
@@ -1450,6 +1632,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();
@@ -1473,8 +1669,6 @@ 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_lchan *lchan = msg->lchan;
- struct in_addr ip;
- u_int16_t port, attr_f8;
/* 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
@@ -1484,37 +1678,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)) {
- lchan->abis_ip.rtp_payload2 =
- *TLVP_VAL(&tv, RSL_IE_IPAC_RTP_PAYLOAD2);
- DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
- lchan->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 */
- lchan->abis_ip.bound_ip = ntohl(ip.s_addr);
- lchan->abis_ip.bound_port = ntohs(port);
- lchan->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));
@@ -1522,6 +1741,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;
@@ -1549,6 +1774,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
@@ -1560,7 +1786,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");
@@ -1601,15 +1828,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);
@@ -1658,11 +1885,11 @@ int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
/* From Table 10.5.33 of GSM 04.08 */
int rsl_number_of_paging_subchannels(struct gsm_bts *bts)
{
- if (bts->chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
- return MAX(1, (3 - bts->chan_desc.bs_ag_blks_res))
- * (bts->chan_desc.bs_pa_mfrms + 2);
+ if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
+ return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res))
+ * (bts->si_common.chan_desc.bs_pa_mfrms + 2);
} else {
- return (9 - bts->chan_desc.bs_ag_blks_res)
- * (bts->chan_desc.bs_pa_mfrms + 2);
+ return (9 - bts->si_common.chan_desc.bs_ag_blks_res)
+ * (bts->si_common.chan_desc.bs_pa_mfrms + 2);
}
}
diff --git a/openbsc/src/bitvec.c b/openbsc/src/bitvec.c
new file mode 100644
index 000000000..d6f5679cf
--- /dev/null
+++ b/openbsc/src/bitvec.c
@@ -0,0 +1,170 @@
+/* bit vector utility routines */
+
+/* (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 <errno.h>
+#include <sys/types.h>
+
+#include <openbsc/bitvec.h>
+
+#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
+
+static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
+{
+ unsigned int bytenum = bitnum / 8;
+
+ return bytenum;
+}
+
+/* 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)
+{
+ int bitval;
+
+ switch (bit) {
+ case ZERO:
+ bitval = (0 << bitnum);
+ break;
+ case ONE:
+ bitval = (1 << bitnum);
+ break;
+ case L:
+ bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum));
+ break;
+ case H:
+ 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 */
+ 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;
+
+ rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit);
+ if (!rc)
+ bv->cur_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;
+
+ for (i = 0; i < count; i++) {
+ rc = bitvec_set_bit(bv, bits[i]);
+ if (rc)
+ return rc;
+ }
+
+ 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;
+
+ for (i = 0; i < num_bits; i++) {
+ int bit = 0;
+ if (ui & (1 << (num_bits - i - 1)))
+ bit = 1;
+ rc = bitvec_set_bit(bv, bit);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* pad all remaining bits up to num_bits */
+int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
+{
+ unsigned int i;
+
+ for (i = bv->cur_bit; i <= up_to_bit; i++)
+ bitvec_set_bit(bv, L);
+
+ return 0;
+}
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 57db51552..a9a5d372f 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -35,12 +35,12 @@
#include <openbsc/debug.h>
#include <openbsc/e1_input.h>
#include <openbsc/talloc.h>
+#include <openbsc/signal.h>
/* MCC and MNC for the Location Area Identifier */
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);
@@ -72,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)
@@ -88,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'},
@@ -117,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;
@@ -143,6 +139,7 @@ static void signal_handler(int signal)
switch (signal) {
case SIGINT:
bsc_shutdown_net(bsc_gsmnet);
+ dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
sleep(3);
exit(0);
break;
@@ -165,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);
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index 0e9c0b830..10b8fd1b5 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -28,16 +28,15 @@
#include <openbsc/debug.h>
#include <openbsc/misdn.h>
#include <openbsc/telnet_interface.h>
+#include <openbsc/system_information.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/talloc.h>
/* 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);
-static void patch_si_tables(struct gsm_bts *bts);
/* The following definitions are for OM and NM packets that we cannot yet
* generate by code but we just pass on */
@@ -457,7 +456,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);
}
@@ -560,7 +559,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;
}
@@ -579,6 +578,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 */
@@ -624,7 +624,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:
@@ -634,13 +634,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);
@@ -671,219 +671,55 @@ int bsc_shutdown_net(struct gsm_network *net)
return 0;
}
-struct bcch_info {
- u_int8_t type;
- u_int8_t len;
- const u_int8_t *data;
-};
-
-/*
-SYSTEM INFORMATION TYPE 1
- Cell channel description
- Format-ID bit map 0
- CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
-*/
-static u_int8_t si1[] = {
- /* header */0x55, 0x06, 0x19,
- /* ccdesc */0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/,
- /* rach */0xD5, 0x04, 0x00,
- /* s1 reset*/0x2B
-};
-
-/*
- SYSTEM INFORMATION TYPE 2
- Neighbour Cells Description
- EXT-IND: Carries the complete BA
- BA-IND = 0
- Format-ID bit map 0
- CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- NCC permitted (NCC) = FF
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
-*/
-static u_int8_t si2[] = {
- /* header */0x59, 0x06, 0x1A,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* ncc */0xFF,
- /* rach*/0xD5, 0x04, 0x00
-};
-
-/*
-SYSTEM INFORMATION TYPE 3
- Cell identity = 00001 (1h)
- Location area identification
- Mobile Country Code (MCC): 001
- Mobile Network Code (MNC): 01
- Location Area Code (LAC): 00001 (1h)
- Control Channel Description
- Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach
- 0 blocks reserved for access grant
- 1 channel used for CCCH, with SDCCH
- 5 multiframes period for PAGING REQUEST
- Time-out T3212 = 0
- Cell Options BCCH
- Power control indicator: not set
- MSs shall not use uplink DTX
- Radio link timeout = 36
- Cell Selection Parameters
- Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
- max.TX power level MS may use for CCH = 2 <- according to GSM05.05 39dBm (max)
- Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
- Half rate support (NECI): New establishment causes are not supported
- min.RX signal level for MS = 0
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
- SI 3 Rest Octets (not present)
-*/
-static u_int8_t si3[] = {
- /* header */0x49, 0x06, 0x1B,
- /* cell */0x00, 0x01,
- /* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
- /* desc */0x01, 0x03, 0x00,
- /* option*/0x28,
- /* selection*/0x62, 0x00,
- /* rach */0xD5, 0x04, 0x00,
- /* rest */ 0x2B, 0x2B, 0x2B, 0x2B
-};
-
-/*
-SYSTEM INFORMATION TYPE 4
- Location area identification
- Mobile Country Code (MCC): 001
- Mobile Network Code (MNC): 01
- Location Area Code (LAC): 00001 (1h)
- Cell Selection Parameters
- Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
- max.TX power level MS may use for CCH = 2
- Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
- Half rate support (NECI): New establishment causes are not supported
- min.RX signal level for MS = 0
- RACH Control Parameters
- maximum 7 retransmissions
- 8 slots used to spread transmission
- cell not barred for access
- call reestablishment not allowed
- Access Control Class = 0000
- CBCH Channel Description
- Type = SDCCH/4[2]
- Timeslot Number: 0
- Training Sequence Code: 7h
- ARFCN: 1
- SI Rest Octets (not present)
-*/
-static u_int8_t si4[] = {
- /* header */0x41, 0x06, 0x1C,
- /* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
- /* sel */0x62, 0x00,
- /* rach*/0xD5, 0x04, 0x00,
- /* cbch chan desc */ 0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/,
- /* rest octets */ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B
-};
-
-/*
- SYSTEM INFORMATION TYPE 5
- Neighbour Cells Description
- EXT-IND: Carries the complete BA
- BA-IND = 0
- Format-ID bit map 0
- CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-*/
-
-static u_int8_t si5[] = {
- /* header without l2 len*/0x06, 0x1D,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-// SYSTEM INFORMATION TYPE 6
-
-/*
-SACCH FILLING
- System Info Type: SYSTEM INFORMATION 6
- L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF
-
-SYSTEM INFORMATION TYPE 6
- Cell identity = 00001 (1h)
- Location area identification
- Mobile Country Code (MCC): 001
- Mobile Network Code (MNC): 01
- Location Area Code (LAC): 00001 (1h)
- Cell Options SACCH
- Power control indicator: not set
- MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H.
- Radio link timeout = 36
- NCC permitted (NCC) = FF
-*/
-
-static u_int8_t si6[] = {
- /* header */0x06, 0x1E,
- /* cell id*/ 0x00, 0x01,
- /* lai */ 0x00, 0xF1, 0x10, 0x00, 0x01,
- /* options */ 0x28,
- /* ncc */ 0xFF,
-};
-
-
-
-static const struct bcch_info bcch_infos[] = {
- {
- .type = RSL_SYSTEM_INFO_1,
- .len = sizeof(si1),
- .data = si1,
- }, {
- .type = RSL_SYSTEM_INFO_2,
- .len = sizeof(si2),
- .data = si2,
- }, {
- .type = RSL_SYSTEM_INFO_3,
- .len = sizeof(si3),
- .data = si3,
- }, {
- .type = RSL_SYSTEM_INFO_4,
- .len = sizeof(si4),
- .data = si4,
- },
-};
-
-static_assert(sizeof(si1) == sizeof(struct gsm48_system_information_type_1), type1)
-static_assert(sizeof(si2) == sizeof(struct gsm48_system_information_type_2), type2)
-static_assert(sizeof(si3) == sizeof(struct gsm48_system_information_type_3), type3)
-static_assert(sizeof(si4) >= sizeof(struct gsm48_system_information_type_4), type4)
-static_assert(sizeof(si5) == sizeof(struct gsm48_system_information_type_5), type5)
-static_assert(sizeof(si6) >= sizeof(struct gsm48_system_information_type_6), type6)
-
/* set all system information types */
static int set_system_infos(struct gsm_bts_trx *trx)
{
- int i;
+ 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 = 0; i < ARRAY_SIZE(bcch_infos); i++) {
- rsl_bcch_info(trx, bcch_infos[i].type,
- bcch_infos[i].data,
- bcch_infos[i].len);
+ 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
+ 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
}
- rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si5, sizeof(si5));
- rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si6, sizeof(si6));
+
+ 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);
+
+ 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);
return 0;
+err_out:
+ LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely "
+ "a problem with neighbor cell list generation\n",
+ i, trx->bts->nr);
+ return rc;
}
/*
@@ -917,81 +753,12 @@ static void patch_nm_tables(struct gsm_bts *bts)
nanobts_attr_radio[1] = bts->c0->max_power_red / 2;
}
-/*
- * Patch the various SYSTEM INFORMATION tables to update
- * the LAI
- */
-static void patch_si_tables(struct gsm_bts *bts)
-{
- u_int8_t arfcn_low = bts->c0->arfcn & 0xff;
- u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
-
- /* covert the raw packet to the struct */
- struct gsm48_system_information_type_1 *type_1 =
- (struct gsm48_system_information_type_1*)&si1;
- struct gsm48_system_information_type_2 *type_2 =
- (struct gsm48_system_information_type_2*)&si2;
- struct gsm48_system_information_type_3 *type_3 =
- (struct gsm48_system_information_type_3*)&si3;
- struct gsm48_system_information_type_4 *type_4 =
- (struct gsm48_system_information_type_4*)&si4;
- struct gsm48_system_information_type_6 *type_6 =
- (struct gsm48_system_information_type_6*)&si6;
- struct gsm48_loc_area_id lai;
-
- gsm0408_generate_lai(&lai, bts->network->country_code,
- bts->network->network_code,
- bts->location_area_code);
-
- /* assign the MCC and MNC */
- type_3->lai = lai;
- type_4->lai = lai;
- type_6->lai = lai;
-
- /* set the CI */
- type_3->cell_identity = htons(bts->cell_identity);
- type_6->cell_identity = htons(bts->cell_identity);
-
- type_4->data[2] &= 0xf0;
- type_4->data[2] |= arfcn_high;
- type_4->data[3] = arfcn_low;
-
- /* patch Control Channel Description 10.5.2.11 */
- type_3->control_channel_desc = bts->chan_desc;
-
- /* patch TSC */
- si4[15] &= ~0xe0;
- si4[15] |= (bts->tsc & 7) << 5;
-
- /* patch MS max power for CCH */
- type_4->cell_sel_par.ms_txpwr_max_ccch =
- ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
-
- /* Set NECI to influence channel request */
- type_3->cell_sel_par.neci = bts->network->neci;
- type_4->cell_sel_par.neci = bts->network->neci;
-
- if (bts->cell_barred) {
- type_1->rach_control.cell_bar = 1;
- type_2->rach_control.cell_bar = 1;
- type_3->rach_control.cell_bar = 1;
- type_4->rach_control.cell_bar = 1;
- } else {
- type_1->rach_control.cell_bar = 0;
- type_2->rach_control.cell_bar = 0;
- type_3->rach_control.cell_bar = 0;
- type_4->rach_control.cell_bar = 0;
- }
-}
-
-
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);
- patch_si_tables(trx->bts);
set_system_infos(trx);
}
@@ -1011,7 +778,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:
@@ -1024,33 +791,55 @@ 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, LOGL_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->chan_desc.att = 1;
- bts->chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
- bts->chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
+ 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;
/* T3212 is set from vty/config */
+ /* some defaults for our system information */
+ bts->si_common.rach_control.re = 1; /* no re-establishment */
+ bts->si_common.rach_control.tx_integer = 5; /* 8 slots spread */
+ bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
+ bts->si_common.rach_control.t2 = 4; /* no emergency calls */
+
+ bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */
+ 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.acs = 0;
+
+ bts->si_common.ncc_permitted = 0xff;
+
paging_init(bts);
return 0;
@@ -1073,7 +862,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/bsc_msc_ip.c b/openbsc/src/bsc_msc_ip.c
index f44974838..491d56c85 100644
--- a/openbsc/src/bsc_msc_ip.c
+++ b/openbsc/src/bsc_msc_ip.c
@@ -467,7 +467,6 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
rc = rsl_ipacc_mdcx(lchan, ntohl(local_addr.s_addr),
lchan->msc_data->rtp_port,
- lchan->abis_ip.conn_id,
rtp_payload);
if (rc < 0) {
DEBUGP(DMSC, "Failed to send connect: %d\n", rc);
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index b4f080585..27d582931 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -201,9 +201,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) {
@@ -227,6 +232,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);
@@ -239,6 +246,16 @@ void lchan_free(struct gsm_lchan *lchan)
lchan->use_count = 0;
}
+ 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 */
}
@@ -259,11 +276,11 @@ int _lchan_release(struct gsm_lchan *lchan)
}
/* spoofed? message */
- if (lchan->use_count < 0) {
- DEBUGP(DRLL, "BUG: 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, "Releasing the channel with: %d (%x)\n", lchan->nr, lchan->nr);
+ DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
rsl_release_request(lchan, 0);
return 1;
}
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 5dc2e0ff7..6e99d4144 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -60,6 +60,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 e46533838..083d8f8de 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -52,6 +52,7 @@
#include <openbsc/trau_frame.h>
#include <openbsc/trau_mux.h>
#include <openbsc/talloc.h>
+#include <openbsc/signal.h>
#include <openbsc/misdn.h>
#define NUM_E1_TS 32
@@ -234,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;
}
@@ -263,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;
}
@@ -305,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;
}
@@ -430,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;
}
@@ -445,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;
@@ -454,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;
}
@@ -491,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;
@@ -523,8 +524,24 @@ int e1inp_line_update(struct e1inp_line *line)
return mi_e1_line_update(line);
}
+static int e1i_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ if (subsys != SS_GLOBAL ||
+ signal != S_GLOBAL_SHUTDOWN)
+ return 0;
+
+ if (pcap_fd) {
+ close(pcap_fd);
+ pcap_fd = -1;
+ }
+
+ return 0;
+}
+
static __attribute__((constructor)) void on_dso_load_e1_inp(void)
{
tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1,
"e1inp_sign_link");
+ register_signal_handler(SS_GLOBAL, e1i_sig_cb, NULL);
}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index bbb79686f..0e419742e 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 },
@@ -166,63 +165,6 @@ static const char *rr_cause_name(u_int8_t cause)
return strbuf;
}
-static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
- int len)
-{
- memset(rep, 0, sizeof(*rep));
-
- if (data[0] & 0x80)
- rep->flags |= MEAS_REP_F_BA1;
- if (data[0] & 0x40)
- rep->flags |= MEAS_REP_F_DTX;
- if ((data[1] & 0x40) == 0x00)
- rep->flags |= MEAS_REP_F_VALID;
-
- rep->rxlev_full = data[0] & 0x3f;
- rep->rxlev_sub = data[1] & 0x3f;
- rep->rxqual_full = (data[3] >> 4) & 0x7;
- rep->rxqual_sub = (data[3] >> 1) & 0x7;
- rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2);
- if (rep->num_cell < 1)
- return;
-
- /* 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);
- if (rep->num_cell < 2)
- return;
-
- 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);
- if (rep->num_cell < 3)
- return;
-
- 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);
- if (rep->num_cell < 4)
- return;
-
- 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;
- if (rep->num_cell < 5)
- return;
-
- 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;
- if (rep->num_cell < 6)
- return;
-
- 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;
-}
-
int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
static int gsm48_tx_simple(struct gsm_lchan *lchan,
u_int8_t pdisc, u_int8_t msg_type);
@@ -234,12 +176,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,
@@ -297,6 +233,11 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
db_subscriber_alloc_tmsi(lchan->subscr);
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 */
@@ -433,18 +374,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;
@@ -455,21 +407,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)
@@ -861,6 +816,7 @@ static int encode_more(struct msgb *msg)
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
@@ -872,6 +828,8 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
gh->data[0] = cause;
DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
+
+ bts->network->stats.loc_upd_resp.reject++;
return gsm48_sendmsg(msg, NULL);
}
@@ -884,7 +842,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;
@@ -901,12 +858,9 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
- ret = gsm48_sendmsg(msg, NULL);
+ bts->network->stats.loc_upd_resp.accept++;
- /* 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 */
@@ -940,6 +894,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 */
@@ -971,8 +927,9 @@ 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;
- gsm0408_loc_upd_rej(lchan, reject_cause);
+ gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
release_loc_updating_req(lchan);
}
@@ -1018,6 +975,20 @@ 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);
+
+ switch (lu->type) {
+ case GSM48_LUPD_NORMAL:
+ bts->network->stats.loc_upd_type.normal++;
+ break;
+ case GSM48_LUPD_IMSI_ATT:
+ bts->network->stats.loc_upd_type.attach++;
+ break;
+ case GSM48_LUPD_PERIODIC:
+ bts->network->stats.loc_upd_type.periodic++;
+ break;
+ }
+
/*
* Pseudo Spoof detection: Just drop a second/concurrent
* location updating request.
@@ -1301,6 +1272,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);
@@ -1314,7 +1287,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);
}
@@ -1340,6 +1315,8 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
mi_type, mi_string);
+ bts->network->stats.loc_upd_type.detach++;
+
switch (mi_type) {
case GSM_MI_TYPE_TMSI:
subscr = subscr_get_by_tmsi(bts->network,
@@ -1371,6 +1348,10 @@ 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? */
return 0;
}
@@ -1527,26 +1508,13 @@ static int gsm48_rx_rr_status(struct msgb *msg)
static int gsm48_rx_rr_meas_rep(struct msgb *msg)
{
- struct gsm48_hdr *gh = msgb_l3(msg);
- unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
- static struct gsm_meas_rep meas_rep;
-
- DEBUGP(DMEAS, "MEASUREMENT REPORT ");
- parse_meas_rep(&meas_rep, gh->data, payload_len);
- if (meas_rep.flags & MEAS_REP_F_DTX)
- DEBUGPC(DMEAS, "DTX ");
- if (meas_rep.flags & MEAS_REP_F_BA1)
- DEBUGPC(DMEAS, "BA1 ");
- if (!(meas_rep.flags & MEAS_REP_F_VALID))
- DEBUGPC(DMEAS, "NOT VALID ");
- else
- DEBUGPC(DMEAS, "FULL(lev=%u, qual=%u) SUB(lev=%u, qual=%u) ",
- meas_rep.rxlev_full, meas_rep.rxqual_full, meas_rep.rxlev_sub,
- meas_rep.rxqual_sub);
+ struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan);
- DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", meas_rep.num_cell);
-
- /* FIXME: put the results somwhere */
+ /* 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);
return 0;
}
@@ -1568,6 +1536,33 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
}
+/* Chapter 9.1.16 Handover complete */
+static int gsm48_rx_rr_ho_compl(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n",
+ rr_cause_name(gh->data[0]));
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, msg->lchan);
+ /* FIXME: release old channel */
+
+ return 0;
+}
+
+/* Chapter 9.1.17 Handover Failure */
+static int gsm48_rx_rr_ho_fail(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DRR, "HANDOVER FAILED cause = %s\n",
+ rr_cause_name(gh->data[0]));
+
+ dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, msg->lchan);
+ /* FIXME: release allocated new channel */
+
+ return 0;
+}
/* Receive a GSM 04.08 Radio Resource (RR) message */
static int gsm0408_rcv_rr(struct msgb *msg)
@@ -1601,6 +1596,12 @@ static int gsm0408_rcv_rr(struct msgb *msg)
DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
/* FIXME: check for MI (if any) */
break;
+ case GSM48_MT_RR_HANDO_COMPL:
+ rc = gsm48_rx_rr_ho_compl(msg);
+ break;
+ case GSM48_MT_RR_HANDO_FAIL:
+ rc = gsm48_rx_rr_ho_fail(msg);
+ break;
default:
fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
gh->msg_type);
@@ -1811,12 +1812,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;
int rc;
+ struct gsm_network *net;
+ struct gsm_trans *trans;
if (subsys != SS_ABISIP)
return 0;
@@ -1827,51 +1832,19 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
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 (lchan->abis_ip.rtp_socket) {
- rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- }
-
- 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;
- break;
- case S_ABISIP_DLCX_IND:
- /* 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;
+ /* 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 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),
- lchan->abis_ip.conn_id,
- /* FIXME: use RTP payload of bound socket, not BTS*/
- lchan->abis_ip.rtp_payload2);
-
- return rc;
}
/* map two ipaccess RTP streams onto each other */
@@ -1889,15 +1862,17 @@ 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->abis_ip.rtp_socket,
@@ -1906,13 +1881,11 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
/* directly connect TCH RTP streams to each other */
rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip,
remote_lchan->abis_ip.bound_port,
- lchan->abis_ip.conn_id,
remote_lchan->abis_ip.rtp_payload2);
if (rc < 0)
return rc;
rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip,
lchan->abis_ip.bound_port,
- remote_lchan->abis_ip.conn_id,
lchan->abis_ip.rtp_payload2);
}
break;
@@ -1921,8 +1894,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;
@@ -1944,45 +1916,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");
@@ -2703,6 +2691,8 @@ 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 ! */
break;
default:
rc = mncc_recvmsg(trans->subscr->net, trans,
@@ -3209,11 +3199,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 3121f9fea..ed0a6c520 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,
@@ -488,6 +488,8 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
sig_data.bts = msg->lchan->ts->trx->bts;
sig_data.lchan = msg->lchan;
+ bts->network->stats.paging.completed++;
+
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
/* Stop paging on the bts we received the paging response */
@@ -519,6 +521,51 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
return rsl_encryption_cmd(msg);
}
+static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
+ const struct gsm_bts *bts)
+{
+ cd->ncc = (bts->bsic >> 3 & 0x7);
+ cd->bcc = (bts->bsic & 0x7);
+ cd->arfcn_hi = bts->c0->arfcn >> 8;
+ cd->arfcn_lo = bts->c0->arfcn & 0xff;
+}
+
+static void gsm48_chan_desc(struct gsm48_chan_desc *cd,
+ const struct gsm_lchan *lchan)
+{
+ u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
+
+ cd->chan_nr = lchan2chan_nr(lchan);
+ cd->h0.tsc = lchan->ts->trx->bts->tsc;
+ cd->h0.h = 0;
+ cd->h0.arfcn_high = arfcn >> 8;
+ cd->h0.arfcn_low = arfcn & 0xff;
+}
+
+/* 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, 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));
+
+ 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->power_command = power_command;
+
+ /* FIXME: optional bits for type of synchronization? */
+
+ return gsm48_sendmsg(msg, NULL);
+}
+
/* Chapter 9.1.2: Assignment Command */
int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
{
@@ -526,7 +573,6 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ass_cmd *ass =
(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
- u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
@@ -542,11 +588,7 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
* the chan_desc. But as long as multi-slot configurations
* are not used we seem to be fine.
*/
- ass->chan_desc.chan_nr = lchan2chan_nr(lchan);
- ass->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
- ass->chan_desc.h0.h = 0;
- ass->chan_desc.h0.arfcn_high = arfcn >> 8;
- ass->chan_desc.h0.arfcn_low = arfcn & 0xff;
+ gsm48_chan_desc(&ass->chan_desc, lchan);
ass->power_command = power_command;
/* in case of multi rate we need to attach a config */
@@ -658,3 +700,72 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
rsl_ipacc_crcx(msg->lchan);
return rc;
}
+
+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;
+
+ if (data[0] & 0x80)
+ rep->flags |= MEAS_REP_F_BA1;
+ if (data[0] & 0x40)
+ rep->flags |= MEAS_REP_F_UL_DTX;
+ if ((data[1] & 0x40) == 0x00)
+ rep->flags |= MEAS_REP_F_DL_VALID;
+
+ rep->dl.full.rx_lev = data[0] & 0x3f;
+ rep->dl.sub.rx_lev = data[1] & 0x3f;
+ rep->dl.full.rx_qual = (data[3] >> 4) & 0x7;
+ rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7;
+
+ 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[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[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[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[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[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[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;
+}
+
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 75ddf9dd9..579bb55d1 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;
}
@@ -198,48 +198,171 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
-static time_t gsm340_scts(u_int8_t *scts);
+/* Turn int into semi-octet representation: 98 => 0x89 */
+static u_int8_t bcdify(u_int8_t value)
+{
+ u_int8_t ret;
-static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+ ret = value / 10;
+ ret |= (value % 10) << 4;
+
+ return ret;
+}
+
+/* Turn semi-octet representation into int: 0x89 => 98 */
+static u_int8_t unbcdify(u_int8_t value)
+{
+ u_int8_t ret;
+
+ if ((value & 0x0F) > 9 || (value >> 4) > 9)
+ DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
+
+ ret = (value&0x0F)*10;
+ ret += value>>4;
+
+ return ret;
+}
+
+/* Generate 03.40 TP-SCTS */
+static void gsm340_gen_scts(u_int8_t *scts, time_t time)
+{
+ struct tm *tm = localtime(&time);
+
+ *scts++ = bcdify(tm->tm_year % 100);
+ *scts++ = bcdify(tm->tm_mon + 1);
+ *scts++ = bcdify(tm->tm_mday);
+ *scts++ = bcdify(tm->tm_hour);
+ *scts++ = bcdify(tm->tm_min);
+ *scts++ = bcdify(tm->tm_sec);
+ *scts++ = bcdify(tm->tm_gmtoff/(60*15));
+}
+
+/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
+static time_t gsm340_scts(u_int8_t *scts)
+{
+ struct tm tm;
+
+ u_int8_t yr = unbcdify(*scts++);
+
+ if (yr <= 80)
+ tm.tm_year = 100 + yr;
+ else
+ tm.tm_year = yr;
+ tm.tm_mon = unbcdify(*scts++) - 1;
+ tm.tm_mday = unbcdify(*scts++);
+ tm.tm_hour = unbcdify(*scts++);
+ tm.tm_min = unbcdify(*scts++);
+ tm.tm_sec = unbcdify(*scts++);
+ /* according to gsm 03.40 time zone is
+ "expressed in quarters of an hour" */
+ tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
+
+ return mktime(&tm);
+}
+
+/* Return the default validity period in minutes */
+static unsigned long gsm340_vp_default(void)
+{
+ unsigned long minutes;
+ /* Default validity: two days */
+ minutes = 24 * 60 * 2;
+ return minutes;
+}
+
+/* Decode validity period format 'relative' */
+static unsigned long gsm340_vp_relative(u_int8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.1 */
+ u_int8_t vp;
+ unsigned long minutes;
+
+ vp = *(sms_vp);
+ if (vp <= 143)
+ minutes = vp + 1 * 5;
+ else if (vp <= 167)
+ minutes = 12*60 + (vp-143) * 30;
+ else if (vp <= 196)
+ minutes = vp-166 * 60 * 24;
+ else
+ minutes = vp-192 * 60 * 24 * 7;
+ return minutes;
+}
+
+/* Decode validity period format 'absolute' */
+static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp)
+{
+ /* Chapter 9.2.3.12.2 */
+ time_t expires, now;
+ unsigned long minutes;
+
+ expires = gsm340_scts(sms_vp);
+ now = mktime(gmtime(NULL));
+ if (expires <= now)
+ minutes = 0;
+ else
+ minutes = (expires-now)/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in integer representation' */
+static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp)
{
u_int8_t vp;
unsigned long minutes;
- time_t expires;
- time_t now;
+ vp = *(sms_vp);
+ if (vp == 0) {
+ DEBUGP(DSMS, "reserved relative_integer validity period\n");
+ return gsm340_vp_default();
+ }
+ minutes = vp/60;
+ return minutes;
+}
+
+/* Decode validity period format 'relative in semi-octet representation' */
+static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp)
+{
+ unsigned long minutes;
+ minutes = unbcdify(*sms_vp++)*60; /* hours */
+ minutes += unbcdify(*sms_vp++); /* minutes */
+ minutes += unbcdify(*sms_vp++)/60; /* seconds */
+ return minutes;
+}
+
+/* decode validity period. return minutes */
+static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
+{
+ u_int8_t fi; /* functionality indicator */
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
- /* Chapter 9.2.3.12.1 */
- vp = *(sms_vp);
- if (vp <= 143)
- minutes = vp + 1 * 5;
- else if (vp <= 167)
- minutes = 12*60 + (vp-143) * 30;
- else if (vp <= 196)
- minutes = vp-166 * 60 * 24;
- else
- minutes = vp-192 * 60 * 24 * 7;
- break;
+ return gsm340_vp_relative(sms_vp);
case GSM340_TP_VPF_ABSOLUTE:
- /* Chapter 9.2.3.12.2 */
- expires = gsm340_scts(sms_vp);
- now = mktime(gmtime(NULL));
- if (expires <= now)
- minutes = 0;
- else
- minutes = (expires-now)/60;
- break;
+ return gsm340_vp_absolute(sms_vp);
case GSM340_TP_VPF_ENHANCED:
/* Chapter 9.2.3.12.3 */
- /* FIXME: implementation */
- DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
- break;
+ fi = *sms_vp++;
+ /* ignore additional fi */
+ if (fi & (1<<7)) sms_vp++;
+ /* read validity period format */
+ switch (fi & 0b111) {
+ case 0b000:
+ return gsm340_vp_default(); /* no vpf specified */
+ case 0b001:
+ return gsm340_vp_relative(sms_vp);
+ case 0b010:
+ return gsm340_vp_relative_integer(sms_vp);
+ case 0b011:
+ return gsm340_vp_relative_semioctet(sms_vp);
+ default:
+ /* The GSM spec says that the SC should reject any
+ unsupported and/or undefined values. FIXME */
+ DEBUGP(DSMS, "Reserved enhanced validity period format\n");
+ return gsm340_vp_default();
+ }
case GSM340_TP_VPF_NONE:
- /* Default validity: two days */
- minutes = 24 * 60 * 2;
- break;
+ default:
+ return gsm340_vp_default();
}
- return minutes;
}
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
@@ -307,69 +430,6 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
return len_in_bytes;
}
-/* Turn int into semi-octet representation: 98 => 0x89 */
-static u_int8_t bcdify(u_int8_t value)
-{
- u_int8_t ret;
-
- ret = value / 10;
- ret |= (value % 10) << 4;
-
- return ret;
-}
-
-/* Turn semi-octet representation into int: 0x89 => 98 */
-static u_int8_t unbcdify(u_int8_t value)
-{
- u_int8_t ret;
-
- if ((value & 0x0F) > 9 || (value >> 4) > 9)
- DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
-
- ret = (value&0x0F)*10;
- if (ret > 90)
- ret += value>>4;
-
- return ret;
-}
-
-/* Generate 03.40 TP-SCTS */
-static void gsm340_gen_scts(u_int8_t *scts, time_t time)
-{
- struct tm *tm = localtime(&time);
-
- *scts++ = bcdify(tm->tm_year % 100);
- *scts++ = bcdify(tm->tm_mon + 1);
- *scts++ = bcdify(tm->tm_mday);
- *scts++ = bcdify(tm->tm_hour);
- *scts++ = bcdify(tm->tm_min);
- *scts++ = bcdify(tm->tm_sec);
- *scts++ = bcdify(tm->tm_gmtoff/(60*15));
-}
-
-/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
-static time_t gsm340_scts(u_int8_t *scts)
-{
- struct tm tm;
-
- u_int8_t yr = unbcdify(*scts++);
-
- if (yr <= 80)
- tm.tm_year = 100 + yr;
- else
- tm.tm_year = yr;
- tm.tm_mon = unbcdify(*scts++) - 1;
- tm.tm_mday = unbcdify(*scts++);
- tm.tm_hour = unbcdify(*scts++);
- tm.tm_min = unbcdify(*scts++);
- tm.tm_sec = unbcdify(*scts++);
- /* according to gsm 03.40 time zone is
- "expressed in quarters of an hour" */
- tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
-
- return mktime(&tm);
-}
-
/* generate a msgb containing a TPDU derived from struct gsm_sms,
* returns total size of TPDU */
static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
@@ -457,6 +517,8 @@ static int gsm340_rx_tpdu(struct msgb *msg)
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
int rc = 0;
+ bts->network->stats.sms.submitted++;
+
gsms = sms_alloc();
if (!gsms)
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
@@ -499,6 +561,9 @@ static int gsm340_rx_tpdu(struct msgb *msg)
case GSM340_TP_VPF_ABSOLUTE:
case GSM340_TP_VPF_ENHANCED:
sms_vp = smsp;
+ /* the additional functionality indicator... */
+ if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7))
+ smsp++;
smsp += 7;
break;
case GSM340_TP_VPF_NONE:
@@ -542,6 +607,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
if (!gsms->receiver) {
rc = 1; /* cause 1: unknown subscriber */
+ bts->network->stats.sms.no_receiver++;
goto out;
}
@@ -728,7 +794,9 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
* to store this in our database and wati for a SMMA message */
/* FIXME */
dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
- }
+ trans->lchan->ts->trx->bts->network->stats.sms.rp_err_mem++;
+ } else
+ trans->lchan->ts->trx->bts->network->stats.sms.rp_err_other++;
sms_free(sms);
trans->sms.sms = NULL;
@@ -844,11 +912,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) {
@@ -1001,6 +1069,8 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
DEBUGP(DSMS, "TX: SMS DELIVER\n");
+ lchan->ts->trx->bts->network->stats.sms.delivered++;
+
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
}
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 88de4c462..12f439be2 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -167,6 +167,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;
@@ -202,6 +210,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);
@@ -226,6 +242,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;
@@ -365,3 +400,39 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
return gsm_auth_policy_names[policy];
}
+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
new file mode 100644
index 000000000..d40374f10
--- /dev/null
+++ b/openbsc/src/handover_logic.c
@@ -0,0 +1,367 @@
+/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
+ * actually implement the handover algorithm/decision, but executes a
+ * handover decision */
+
+/* (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/signal.h>
+#include <openbsc/talloc.h>
+#include <openbsc/transaction.h>
+#include <openbsc/rtp_proxy.h>
+
+struct bsc_handover {
+ struct llist_head list;
+
+ struct gsm_lchan *old_lchan;
+ struct gsm_lchan *new_lchan;
+
+ struct timer_list T3103;
+
+ u_int8_t ho_ref;
+};
+
+static LLIST_HEAD(bsc_handovers);
+
+static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *ho;
+
+ llist_for_each_entry(ho, &bsc_handovers, list) {
+ if (ho->new_lchan == new_lchan)
+ return ho;
+ }
+
+ return NULL;
+}
+
+static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
+{
+ struct bsc_handover *ho;
+
+ llist_for_each_entry(ho, &bsc_handovers, list) {
+ if (ho->old_lchan == old_lchan)
+ return ho;
+ }
+
+ return NULL;
+}
+
+/* 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)
+{
+ 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);
+
+ bts->network->stats.handover.attempted++;
+
+ new_lchan = lchan_alloc(bts, old_lchan->type);
+ if (!new_lchan) {
+ LOGP(DHO, LOGL_NOTICE, "No free channel\n");
+ bts->network->stats.handover.no_channel++;
+ 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,
+ ho->ho_ref);
+ if (rc < 0) {
+ LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
+ talloc_free(ho);
+ lchan_free(new_lchan);
+ return rc;
+ }
+
+ llist_add(&ho->list, &bsc_handovers);
+ /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
+
+ return 0;
+}
+
+/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
+static void ho_T3103_cb(void *_ho)
+{
+ struct bsc_handover *ho = _ho;
+
+ DEBUGP(DHO, "HO T3103 expired\n");
+ ho->new_lchan->ts->trx->bts->network->stats.handover.timeout++;
+
+ lchan_free(ho->new_lchan);
+ llist_del(&ho->list);
+ talloc_free(ho);
+}
+
+/* RSL has acknowledged activation of the new lchan */
+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, 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;
+}
+
+/* RSL has not acknowledged activation of the new lchan */
+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) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ llist_del(&ho->list);
+ talloc_free(ho);
+
+ /* FIXME: maybe we should try to allocate a new LCHAN here? */
+
+ return 0;
+}
+
+/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
+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) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ new_lchan->ts->trx->bts->network->stats.handover.completed++;
+
+ bsc_del_timer(&ho->T3103);
+
+ /* update lchan pointer of transaction */
+ trans_lchan_change(ho->old_lchan, new_lchan);
+
+ ho->old_lchan->state = LCHAN_S_INACTIVE;
+
+ /* do something to re-route the actual speech frames ! */
+
+ llist_del(&ho->list);
+ talloc_free(ho);
+
+ return 0;
+}
+
+/* GSM 04.08 HANDOVER FAIL has been received */
+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) {
+ LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ return -ENODEV;
+ }
+
+ old_lchan->ts->trx->bts->network->stats.handover.failed++;
+
+ bsc_del_timer(&ho->T3103);
+ llist_del(&ho->list);
+ put_lchan(ho->new_lchan);
+ talloc_free(ho);
+
+ return 0;
+}
+
+/* GSM 08.58 HANDOVER DETECT has been received */
+static int ho_rsl_detect(struct gsm_lchan *new_lchan)
+{
+ struct bsc_handover *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)
+{
+ struct gsm_lchan *lchan;
+
+ switch (subsys) {
+ case SS_LCHAN:
+ lchan = signal_data;
+ switch (signal) {
+ case S_LCHAN_ACTIVATE_ACK:
+ return ho_chan_activ_ack(lchan);
+ case S_LCHAN_ACTIVATE_NACK:
+ return ho_chan_activ_nack(lchan);
+ case S_LCHAN_HANDOVER_DETECT:
+ return ho_rsl_detect(lchan);
+ case S_LCHAN_HANDOVER_COMPL:
+ return ho_gsm48_ho_compl(lchan);
+ case S_LCHAN_HANDOVER_FAIL:
+ 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;
+ }
+
+ return 0;
+}
+
+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 2d9f51ef9..6bd501df1 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -238,6 +238,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);
@@ -247,9 +248,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
@@ -348,16 +348,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 0f8627a27..914b36ae7 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/paging.c b/openbsc/src/paging.c
index fe6ea52d1..538e0a8ec 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -55,7 +55,7 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *
int blocks;
unsigned int group;
- ccch_conf = bts->chan_desc.ccch_conf;
+ ccch_conf = bts->si_common.chan_desc.ccch_conf;
bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf);
/* code word + 2, as 2 channels equals 0x0 */
blocks = rsl_number_of_paging_subchannels(bts);
@@ -212,6 +212,8 @@ static void paging_T3113_expired(void *data)
cbfn = req->cbfn;
paging_remove_request(&req->bts->paging, req);
+ req->bts->network->stats.paging.expired++;
+
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
if (cbfn)
cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
@@ -254,6 +256,8 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
struct gsm_bts *bts = NULL;
int num_pages = 0;
+ network->stats.paging.attempted++;
+
/* start paging subscriber on all BTS within Location Area */
do {
int rc;
@@ -269,6 +273,9 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
return rc;
} while (1);
+ if (num_pages == 0)
+ network->stats.paging.detached++;
+
return num_pages;
}
diff --git a/openbsc/src/rest_octets.c b/openbsc/src/rest_octets.c
new file mode 100644
index 000000000..6226203ec
--- /dev/null
+++ b/openbsc/src/rest_octets.c
@@ -0,0 +1,394 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface,
+ * rest octet handling according to
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/bitvec.h>
+#include <openbsc/rest_octets.h>
+
+/* generate SI1 rest octets */
+int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 2;
+
+ if (nch_pos) {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, *nch_pos, 5);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ bitvec_spare_padding(&bv, 15);
+ return 0;
+}
+
+/* Append selection parameters to bitvec */
+static void append_selection_params(struct bitvec *bv,
+ const struct gsm48_si_selection_params *sp)
+{
+ if (sp->present) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_bit(bv, sp->cbq);
+ bitvec_set_uint(bv, sp->cell_resel_off, 6);
+ bitvec_set_uint(bv, sp->temp_offs, 3);
+ bitvec_set_uint(bv, sp->penalty_time, 5);
+ } else
+ bitvec_set_bit(bv, L);
+}
+
+/* Append power offset to bitvec */
+static void append_power_offset(struct bitvec *bv,
+ const struct gsm48_si_power_offset *po)
+{
+ if (po->present) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, po->power_offset, 2);
+ } else
+ bitvec_set_bit(bv, L);
+}
+
+/* Append GPRS indicator to bitvec */
+static void append_gprs_ind(struct bitvec *bv,
+ const struct gsm48_si3_gprs_ind *gi)
+{
+ if (gi->present) {
+ bitvec_set_bit(bv, H);
+ bitvec_set_uint(bv, gi->ra_colour, 3);
+ /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */
+ bitvec_set_bit(bv, gi->si13_position);
+ } else
+ bitvec_set_bit(bv, L);
+}
+
+
+/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
+int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 5;
+
+ /* Optional Selection Parameters */
+ append_selection_params(&bv, &si3->selection_params);
+
+ /* Optional Power Offset */
+ append_power_offset(&bv, &si3->power_offset);
+
+ /* Do we have a SI2ter on the BCCH? */
+ if (si3->si2ter_indicator)
+ bitvec_set_bit(&bv, H);
+ else
+ bitvec_set_bit(&bv, L);
+
+ /* Early Classmark Sending Control */
+ if (si3->early_cm_ctrl)
+ bitvec_set_bit(&bv, H);
+ else
+ bitvec_set_bit(&bv, L);
+
+ /* Do we have a SI Type 9 on the BCCH? */
+ if (si3->scheduling.present) {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, si3->scheduling.where, 3);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ /* GPRS Indicator */
+ append_gprs_ind(&bv, &si3->gprs_ind);
+
+ return bitvec_spare_padding(&bv, (bv.data_len*8)-1);
+}
+
+static int append_lsa_params(struct bitvec *bv,
+ const struct gsm48_lsa_params *lsa_params)
+{
+ /* FIXME */
+}
+
+/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
+int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 11; /* FIXME: up to ? */
+
+ /* SI4 Rest Octets O */
+ append_selection_params(&bv, &si4->selection_params);
+ append_power_offset(&bv, &si4->power_offset);
+ append_gprs_ind(&bv, &si4->gprs_ind);
+
+ if (0 /* FIXME */) {
+ /* H and SI4 Rest Octets S */
+ bitvec_set_bit(&bv, H);
+
+ /* LSA Parameters */
+ if (si4->lsa_params.present) {
+ bitvec_set_bit(&bv, H);
+ append_lsa_params(&bv, &si4->lsa_params);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ /* Cell Identity */
+ if (1) {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, si4->cell_id, 16);
+ } else
+ bitvec_set_bit(&bv, L);
+
+ /* LSA ID Information */
+ if (0) {
+ bitvec_set_bit(&bv, H);
+ /* FIXME */
+ } else
+ bitvec_set_bit(&bv, L);
+ } else {
+ /* L and break indicator */
+ bitvec_set_bit(&bv, L);
+ bitvec_set_bit(&bv, si4->break_ind ? H : L);
+ }
+
+ return 0;
+}
+
+/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a:
+ < GPRS Mobile Allocation IE > ::=
+ < HSN : bit (6) >
+ { 0 | 1 < RFL number list : < RFL number list struct > > }
+ { 0 < MA_LENGTH : bit (6) >
+ < MA_BITMAP: bit (val(MA_LENGTH) + 1) >
+ | 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ;
+
+ < RFL number list struct > :: =
+ < RFL_NUMBER : bit (4) >
+ { 0 | 1 < RFL number list struct > } ;
+ < ARFCN index list struct > ::=
+ < ARFCN_INDEX : bit(6) >
+ { 0 | 1 < ARFCN index list struct > } ;
+ */
+static int append_gprs_mobile_alloc(struct bitvec *bv)
+{
+ /* Hopping Sequence Number */
+ bitvec_set_uint(bv, 0, 6);
+
+ if (0) {
+ /* We want to use a RFL number list */
+ bitvec_set_bit(bv, 1);
+ /* FIXME: RFL number list */
+ } else
+ bitvec_set_bit(bv, 0);
+
+ if (0) {
+ /* We want to use a MA_BITMAP */
+ bitvec_set_bit(bv, 0);
+ /* FIXME: MA_LENGTH, MA_BITMAP, ... */
+ } else {
+ bitvec_set_bit(bv, 1);
+ if (0) {
+ /* We want to provide an ARFCN index list */
+ bitvec_set_bit(bv, 1);
+ /* FIXME */
+ } else
+ bitvec_set_bit(bv, 0);
+ }
+ return 0;
+}
+
+static int encode_t3192(unsigned int t3192)
+{
+ if (t3192 == 0)
+ return 3;
+ else if (t3192 <= 80)
+ return 4;
+ else if (t3192 <= 120)
+ return 5;
+ else if (t3192 <= 160)
+ return 6;
+ else if (t3192 <= 200)
+ return 7;
+ else if (t3192 <= 500)
+ return 0;
+ else if (t3192 <= 1000)
+ return 1;
+ else if (t3192 <= 1500)
+ return 2;
+ else
+ return -EINVAL;
+}
+
+static int encode_drx_timer(unsigned int drx)
+{
+ if (drx == 0)
+ return 0;
+ else if (drx == 1)
+ return 1;
+ else if (drx == 2)
+ return 2;
+ else if (drx <= 4)
+ return 3;
+ else if (drx <= 8)
+ return 4;
+ else if (drx <= 16)
+ return 5;
+ else if (drx <= 32)
+ return 6;
+ else if (drx <= 64)
+ return 7;
+ else
+ return -EINVAL;
+}
+
+/* GPRS Cell Options as per TS 04.60 Chapter 12.24
+ < GPRS Cell Options IE > ::=
+ < NMO : bit(2) >
+ < T3168 : bit(3) >
+ < T3192 : bit(3) >
+ < DRX_TIMER_MAX: bit(3) >
+ < ACCESS_BURST_TYPE: bit >
+ < CONTROL_ACK_TYPE : bit >
+ < BS_CV_MAX: bit(4) >
+ { 0 | 1 < PAN_DEC : bit(3) >
+ < PAN_INC : bit(3) >
+ < PAN_MAX : bit(3) >
+ { 0 | 1 < Extension Length : bit(6) >
+ < bit (val(Extension Length) + 1
+ & { < Extension Information > ! { bit ** = <no string> } } ;
+ < Extension Information > ::=
+ { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit >
+ < BEP_PERIOD : bit(4) > }
+ < PFC_FEATURE_MODE : bit >
+ < DTM_SUPPORT : bit >
+ <BSS_PAGING_COORDINATION: bit >
+ <spare bit > ** ;
+ */
+static int append_gprs_cell_opt(struct bitvec *bv,
+ const struct gprs_cell_options *gco)
+{
+ int t3192, drx_timer_max;
+
+ t3192 = encode_t3192(gco->t3192);
+ if (t3192 < 0)
+ return t3192;
+
+ drx_timer_max = encode_drx_timer(gco->drx_timer_max);
+ if (drx_timer_max < 0)
+ return drx_timer_max;
+
+ bitvec_set_uint(bv, gco->nmo, 2);
+ bitvec_set_uint(bv, gco->t3168 / 500, 3);
+ bitvec_set_uint(bv, t3192, 3);
+ bitvec_set_uint(bv, drx_timer_max, 3);
+ /* ACCESS_BURST_TYPE: Hard-code 8bit */
+ bitvec_set_bit(bv, 0);
+ /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, gco->bs_cv_max, 4);
+
+ /* hard-code no PAN_{DEC,INC,MAX} */
+ bitvec_set_bit(bv, 0);
+
+ /* no extension information (EDGE) */
+ bitvec_set_bit(bv, 0);
+
+ return 0;
+}
+
+static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
+ const struct gprs_power_ctrl_pars *pcp)
+{
+ bitvec_set_uint(bv, pcp->alpha, 4);
+ bitvec_set_uint(bv, pcp->t_avg_w, 5);
+ bitvec_set_uint(bv, pcp->t_avg_t, 5);
+ bitvec_set_uint(bv, pcp->pc_meas_chan, 1);
+ bitvec_set_uint(bv, pcp->n_avg_i, 4);
+}
+
+/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
+{
+ struct bitvec bv;
+
+ memset(&bv, 0, sizeof(bv));
+ bv.data = data;
+ bv.data_len = 21;
+
+ if (0) {
+ /* No rest octets */
+ bitvec_set_bit(&bv, L);
+ } else {
+ bitvec_set_bit(&bv, H);
+ bitvec_set_uint(&bv, si13->bcch_change_mark, 3);
+ bitvec_set_uint(&bv, si13->si_change_field, 4);
+ if (0) {
+ bitvec_set_bit(&bv, 0);
+ } else {
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->bcch_change_mark, 2);
+ append_gprs_mobile_alloc(&bv);
+ }
+ if (!si13->pbcch_present) {
+ /* PBCCH not present in cell */
+ bitvec_set_bit(&bv, 0);
+ bitvec_set_uint(&bv, si13->no_pbcch.rac, 8);
+ bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup);
+ bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3);
+ bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2);
+ append_gprs_cell_opt(&bv, &si13->cell_opts);
+ append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars);
+ } else {
+ /* PBCCH present in cell */
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4);
+ /* PBCCH Descripiton */
+ bitvec_set_uint(&bv, si13->pbcch.pb, 4);
+ bitvec_set_uint(&bv, si13->pbcch.tsc, 3);
+ bitvec_set_uint(&bv, si13->pbcch.tn, 3);
+ switch (si13->pbcch.carrier_type) {
+ case PBCCH_BCCH:
+ bitvec_set_bit(&bv, 0);
+ bitvec_set_bit(&bv, 0);
+ break;
+ case PBCCH_ARFCN:
+ bitvec_set_bit(&bv, 0);
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->pbcch.arfcn, 10);
+ break;
+ case PBCCH_MAIO:
+ bitvec_set_bit(&bv, 1);
+ bitvec_set_uint(&bv, si13->pbcch.maio, 6);
+ break;
+ }
+ }
+ }
+ return bitvec_spare_padding(&bv, (bv.data_len*8)-1);
+}
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
new file mode 100644
index 000000000..df4f1a0c8
--- /dev/null
+++ b/openbsc/src/system_information.c
@@ -0,0 +1,444 @@
+/* GSM 04.08 System Information (SI) encoding and decoding
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <openbsc/gsm_04_08.h>
+#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
+
+/* 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 || 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-1-byte] |= (1 << bit);
+
+ return 0;
+}
+
+/* 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;
+ unsigned int bitno;
+
+ min_arfcn = (chan_list[0] & 1) << 9;
+ min_arfcn |= chan_list[1] << 1;
+ min_arfcn |= (chan_list[2] >> 7) & 1;
+
+ /* The lower end of our bitmaks is always implicitly included */
+ if (arfcn == min_arfcn)
+ return 0;
+
+ if (arfcn < min_arfcn) {
+ LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn);
+ return -EINVAL;
+ }
+ 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;
+ bit = bitno % 8;
+
+ chan_list[2 + byte] |= 1 << (7 - bit);
+
+ return 0;
+}
+
+/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
+static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv,
+ const struct gsm_bts *bts)
+{
+ 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;
+
+ 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;
+ }
+
+ /* We currently only support the 'Variable bitmap format' */
+ chan_list[0] = 0x8e;
+
+ 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) {
+ 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;
+
+ 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_cell_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx;
+ struct bitvec *bv = &bts->si_common.cell_alloc;
+
+ /* 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);
+
+ /* then we generate a GSM 04.08 frequency list from the bitvec */
+ return bitvec2freq_list(chan_list, bv, bts);
+}
+
+/* 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;
+
+ /* 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;
+ bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1);
+ }
+
+ /* 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, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_1 *si1 =
+ (struct gsm48_system_information_type_1 *) output;
+
+ memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si1->header.l2_plen = (21 << 2) | 1;
+ si1->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si1->header.skip_indicator = 0;
+ si1->header.system_information = GSM48_MT_RR_SYSINFO_1;
+
+ rc = generate_cell_chan_list(si1->cell_channel_description, bts);
+ if (rc < 0)
+ return rc;
+
+ si1->rach_control = bts->si_common.rach_control;
+
+ /* SI1 Rest Octets (10.5.2.32), contains NCH position */
+ rest_octets_si1(si1->rest_octets, NULL);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+static int generate_si2(u_int8_t *output, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_2 *si2 =
+ (struct gsm48_system_information_type_2 *) output;
+
+ memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si2->header.l2_plen = (22 << 2) | 1;
+ si2->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si2->header.skip_indicator = 0;
+ si2->header.system_information = GSM48_MT_RR_SYSINFO_2;
+
+ rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts);
+ if (rc < 0)
+ return rc;
+
+ si2->ncc_permitted = bts->si_common.ncc_permitted;
+ si2->rach_control = bts->si_common.rach_control;
+
+ return GSM_MACBLOCK_LEN;
+}
+
+struct gsm48_si_ro_info si_info = {
+ .selection_params = {
+ .present = 0,
+ },
+ .power_offset = {
+ .present = 0,
+ },
+ .si2ter_indicator = 0,
+ .early_cm_ctrl = 1,
+ .scheduling = {
+ .present = 0,
+ },
+ .gprs_ind = {
+ .si13_position = 0,
+ .ra_colour = 0,
+ .present = 0,
+ },
+ .lsa_params = {
+ .present = 0,
+ },
+ .cell_id = 0, /* FIXME: doesn't the bts have this? */
+ .break_ind = 0,
+};
+
+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;
+
+ memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si3->header.l2_plen = (18 << 2) | 1;
+ si3->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si3->header.skip_indicator = 0;
+ si3->header.system_information = GSM48_MT_RR_SYSINFO_3;
+
+ si3->cell_identity = htons(bts->cell_identity);
+ gsm0408_generate_lai(&si3->lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+ si3->control_channel_desc = bts->si_common.chan_desc;
+ si3->cell_options = bts->si_common.cell_options;
+ si3->cell_sel_par = bts->si_common.cell_sel_par;
+ si3->rach_control = bts->si_common.rach_control;
+
+ /* SI3 Rest Octets (10.5.2.34), containing
+ CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME
+ Power Offset, 2ter Indicator, Early Classmark Sending,
+ Scheduling if and WHERE, GPRS Indicator, SI13 position */
+ rest_octets_si3(si3->rest_octets, &si_info);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+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;
+
+ /* length of all IEs present except SI4 rest octets and l2_plen */
+ int l2_plen = sizeof(*si4) - 1;
+
+ memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si4->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si4->header.skip_indicator = 0;
+ si4->header.system_information = GSM48_MT_RR_SYSINFO_4;
+
+ gsm0408_generate_lai(&si4->lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+ si4->cell_sel_par = bts->si_common.cell_sel_par;
+ si4->rach_control = bts->si_common.rach_control;
+
+ /* Optional: CBCH Channel Description + CBCH Mobile Allocation */
+
+ si4->header.l2_plen = (l2_plen << 2) | 1;
+
+ /* SI4 Rest Octets (10.5.2.35), containing
+ Optional Power offset, GPRS Indicator,
+ Cell Identity, LSA ID, Selection Parameter */
+ rest_octets_si4(si4->data, &si_info);
+
+ return GSM_MACBLOCK_LEN;
+}
+
+static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
+{
+ 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 */
+ si5->rr_protocol_discriminator = GSM48_PDISC_RR;
+ si5->skip_indicator = 0;
+ si5->system_information = GSM48_MT_RR_SYSINFO_5;
+ rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts);
+ if (rc < 0)
+ return rc;
+
+ /* 04.08 9.1.37: L2 Pseudo Length of 18 */
+ return l2_plen;
+}
+
+static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
+{
+ 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 */
+ si6->rr_protocol_discriminator = GSM48_PDISC_RR;
+ si6->skip_indicator = 0;
+ si6->system_information = GSM48_MT_RR_SYSINFO_6;
+ si6->cell_identity = htons(bts->cell_identity);
+ gsm0408_generate_lai(&si6->lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+ si6->cell_options = bts->si_common.cell_options;
+ si6->ncc_permitted = bts->si_common.ncc_permitted;
+
+ /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */
+
+ return l2_plen;
+}
+
+static struct gsm48_si13_info si13_default = {
+ .cell_opts = {
+ .nmo = GPRS_NMO_III,
+ .t3168 = 1000,
+ .t3192 = 1000,
+ .drx_timer_max = 1,
+ .bs_cv_max = 15,
+ },
+ .pwr_ctrl_pars = {
+ .alpha = 10, /* a = 1.0 */
+ .t_avg_w = 25,
+ .t_avg_t = 25,
+ .pc_meas_chan = 0, /* downling measured on CCCH */
+ .n_avg_i = 15,
+ },
+ .bcch_change_mark = 0,
+ .si_change_field = 0,
+ .pbcch_present = 0,
+ {
+ .no_pbcch = {
+ .rac = 0,
+ .spgc_ccch_sup = 0,
+ .net_ctrl_ord = 0,
+ .prio_acc_thr = 0,
+ },
+ },
+};
+
+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;
+ int ret;
+
+ memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si13->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si13->header.skip_indicator = 0;
+ si13->header.system_information = GSM48_MT_RR_SYSINFO_13;
+
+ ret = rest_octets_si13(si13->rest_octets, &si13_default);
+ if (ret < 0)
+ return ret;
+
+ si13->header.l2_plen = ret & 0xff;
+
+ return GSM_MACBLOCK_LEN;
+}
+
+int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type)
+{
+ switch (type) {
+ case RSL_SYSTEM_INFO_1:
+ return generate_si1(output, bts);
+ case RSL_SYSTEM_INFO_2:
+ return generate_si2(output, bts);
+ case RSL_SYSTEM_INFO_3:
+ return generate_si3(output, bts);
+ case RSL_SYSTEM_INFO_4:
+ return generate_si4(output, bts);
+ case RSL_SYSTEM_INFO_5:
+ return generate_si5(output, bts);
+ case RSL_SYSTEM_INFO_6:
+ return generate_si6(output, bts);
+ case RSL_SYSTEM_INFO_13:
+ return generate_si13(output, bts);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index 128c34e94..bc91ca333 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, LOGL_ERROR, "Telnet interface failed to bind\n");
return;
}
if (listen(fd, 0) < 0) {
- perror("Telnet interface failed to listen");
+ LOGP(DNM, LOGL_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 03735d4a4..2f5681433 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -134,10 +134,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 934163fb0..582047763 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -87,10 +87,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);
vty_out(vty, " Allowed Audio Codecs: ");
for (i = 0; i < net->audio_length; ++i)
vty_out(vty, "hr: %d ver: %d, ",
@@ -133,7 +141,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",
@@ -241,13 +255,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);
- if (bts->chan_desc.t3212)
+ 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->chan_desc.t3212 * 10, VTY_NEWLINE);
+ 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);
@@ -480,8 +516,11 @@ 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);
@@ -747,6 +786,41 @@ DEFUN(show_paging,
return CMD_SUCCESS;
}
+DEFUN(show_stats,
+ show_stats_cmd,
+ "show statistics",
+ SHOW_STR "Display network statistics\n")
+{
+ struct gsm_network *net = gsmnet;
+
+ vty_out(vty, "Channel Requests: %lu total, %lu no channel%s",
+ net->stats.chreq.total, net->stats.chreq.no_channel,
+ VTY_NEWLINE);
+ vty_out(vty, "Location Update: %lu attach, %lu normal, %lu periodic%s",
+ net->stats.loc_upd_type.attach, net->stats.loc_upd_type.normal,
+ net->stats.loc_upd_type.periodic, VTY_NEWLINE);
+ vty_out(vty, "IMSI Detach Indications: %lu%s\n",
+ net->stats.loc_upd_type.detach, VTY_NEWLINE);
+ vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
+ net->stats.loc_upd_resp.accept,
+ net->stats.loc_upd_resp.reject, VTY_NEWLINE);
+ vty_out(vty, "Paging: %lu attempted, %lu complete, %lu expired%s",
+ net->stats.paging.attempted, net->stats.paging.completed,
+ net->stats.paging.expired, VTY_NEWLINE);
+ vty_out(vty, "Handover: %lu attempted, %lu no_channel, %lu timeout, "
+ "%lu completed, %lu failed%s", net->stats.handover.attempted,
+ net->stats.handover.no_channel, net->stats.handover.timeout,
+ net->stats.handover.completed, net->stats.handover.failed,
+ VTY_NEWLINE);
+ vty_out(vty, "SMS MO: %lu submitted, %lu no receiver%s",
+ net->stats.sms.submitted, net->stats.sms.no_receiver,
+ VTY_NEWLINE);
+ vty_out(vty, "SMS MT: %lu delivered, %lu no memory, %lu other error%s",
+ net->stats.sms.delivered, net->stats.sms.rp_err_mem,
+ net->stats.sms.rp_err_other, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_net,
cfg_net_cmd,
"network",
@@ -817,6 +891,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)",
@@ -836,6 +920,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;
+}
+
DEFUN(cfg_net_supported_codecs,
cfg_net_supported_codecs_cmd,
"codec_list .LIST",
@@ -1058,6 +1223,7 @@ DEFUN(cfg_bts_lac,
return CMD_SUCCESS;
}
+
DEFUN(cfg_bts_tsc,
cfg_bts_tsc_cmd,
"training_sequence_code <0-255>",
@@ -1179,7 +1345,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;
}
@@ -1195,13 +1361,35 @@ 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")
{
struct gsm_bts *bts = vty->index;
- bts->chan_desc.t3212 = atoi(argv[0]) / 10;
+ bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 10;
return CMD_SUCCESS;
}
@@ -1390,6 +1578,7 @@ int bsc_vty_init(struct gsm_network *net)
install_element(VIEW_NODE, &show_e1ts_cmd);
install_element(VIEW_NODE, &show_paging_cmd);
+ install_element(VIEW_NODE, &show_stats_cmd);
install_element(CONFIG_NODE, &cfg_net_cmd);
install_node(&net_node, config_write_net);
@@ -1399,8 +1588,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_supported_codecs_cmd);
install_element(GSMNET_NODE, &cfg_net_ipacc_rtp_payload_cmd);
install_element(GSMNET_NODE, &cfg_net_rtp_base_port_cmd);
@@ -1433,6 +1632,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_trx_cmd);