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