mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-10-23 08:12:01 +00:00
Compare commits
151 Commits
on-waves/0
...
on-waves/0
Author | SHA1 | Date | |
---|---|---|---|
|
bb53004d47 | ||
|
6af20842cb | ||
|
cc41cb07e7 | ||
|
d6fb23523a | ||
|
2aa0b45cc0 | ||
|
619df61ad2 | ||
|
893ea65f38 | ||
|
64b811f113 | ||
|
91fc9bf862 | ||
|
111a58dd37 | ||
|
d1a2563a74 | ||
|
7d3ef919ce | ||
|
cba98d87d6 | ||
|
5c18ad0829 | ||
|
4f5456c040 | ||
|
92ffd926ef | ||
|
24ff6ee0a3 | ||
|
a992a36d5f | ||
|
ac0c13c02c | ||
|
73d4fce151 | ||
|
713550120c | ||
|
1394fea03f | ||
|
6a22c0135a | ||
|
31b0347e72 | ||
|
306b7219ac | ||
|
17c24c9057 | ||
|
b720bd3678 | ||
|
f7c28b099f | ||
|
ade773f441 | ||
|
b8bfc567b7 | ||
|
38fe2a67e1 | ||
|
56315f02bb | ||
|
f77af1fb30 | ||
|
d5778fc4c7 | ||
|
a4e2d04b35 | ||
|
9fb1f1065a | ||
|
a037902323 | ||
|
a72273e176 | ||
|
17f5bf64f1 | ||
|
fe03f0d002 | ||
|
edbc0f7aea | ||
|
f88c8a02fc | ||
|
392736d38b | ||
|
31981a0051 | ||
|
0b7b61c6b7 | ||
|
5e68183a20 | ||
|
ea09002e73 | ||
|
9f16c87106 | ||
|
aca8f158bc | ||
|
da7ab74298 | ||
|
bc814501e8 | ||
|
e786c32bc9 | ||
|
50e7fec9b8 | ||
|
e2d0d5fa8c | ||
|
479015bc1f | ||
|
51cec5f725 | ||
|
5e3d91bff7 | ||
|
f314f6899b | ||
|
b9f3dce0ae | ||
|
ea3f674710 | ||
|
feaca92fc0 | ||
|
c56a14181c | ||
|
1d9efd6c9c | ||
|
18750cf1df | ||
|
3b10499694 | ||
|
a9fa8dca33 | ||
|
6f7a5a7843 | ||
|
386cd2b777 | ||
|
e47f96b80c | ||
|
fa5aad789b | ||
|
b1d4c8ed9d | ||
|
6670681251 | ||
|
7cb7a73b4f | ||
|
fe18d5cba4 | ||
|
cc9beb5366 | ||
|
8d77b9540a | ||
|
7a7a0d5428 | ||
|
33e6597720 | ||
|
73ddaeddd2 | ||
|
88a412ac80 | ||
|
6739dfb705 | ||
|
a2f74b8477 | ||
|
680e2eccef | ||
|
152b6261f8 | ||
|
7f2d25b095 | ||
|
d12b0fdf51 | ||
|
0b12103965 | ||
|
84874c9005 | ||
|
f1dae1924a | ||
|
7f73a1ac58 | ||
|
6c40def716 | ||
|
d57f163bd4 | ||
|
da760d3d19 | ||
|
1e191c59f6 | ||
|
ade7a14e75 | ||
|
3d23db43a4 | ||
|
648b6ce083 | ||
|
37600be76c | ||
|
7659de1bcb | ||
|
eab84a112c | ||
|
09b7e7fa43 | ||
|
487e6befb8 | ||
|
b83d938565 | ||
|
210c850a36 | ||
|
9385c11727 | ||
|
9e2748ed3a | ||
|
7322528ca1 | ||
|
1d8dbc4630 | ||
|
4bb4738d21 | ||
|
854b9b33af | ||
|
49685d7bdd | ||
|
d6aa52488a | ||
|
4669f3d9c8 | ||
|
24766091d8 | ||
|
7bcb893eb7 | ||
|
1085c097e3 | ||
|
0d9ed87d5c | ||
|
7fc57adf16 | ||
|
f49fac2785 | ||
|
ec7be0c969 | ||
|
9be3347601 | ||
|
3eef7b7d81 | ||
|
ad69d7f8b2 | ||
|
a897bf3ded | ||
|
1dfd0e254a | ||
|
d6575f9d29 | ||
|
0603c9d9e5 | ||
|
2c82899135 | ||
|
a43f789a0a | ||
|
29b9cf8446 | ||
|
3961fcc031 | ||
|
b84ddfc22f | ||
|
dbb1d88359 | ||
|
798418a068 | ||
|
d011e8b958 | ||
|
8c83af65c1 | ||
|
ccd5a8892d | ||
|
3c7dc6ed50 | ||
|
a5312fdd2b | ||
|
1a79d36440 | ||
|
ad3c844b2d | ||
|
f326267fb7 | ||
|
9de4a6daa9 | ||
|
2d501ea26a | ||
|
1ce10f3854 | ||
|
fdd0ddf7e9 | ||
|
4642d4917f | ||
|
23975e718f | ||
|
c4d88ad971 | ||
|
d61654cf54 | ||
|
a8816dd9c7 |
@@ -1,7 +1,7 @@
|
||||
dnl Process this file with autoconf to produce a configure script
|
||||
AC_INIT
|
||||
|
||||
AM_INIT_AUTOMAKE(openbsc, 0.1onwaves)
|
||||
AM_INIT_AUTOMAKE(openbsc, 0.2onwaves)
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
89
openbsc/doc/handover.txt
Normal file
89
openbsc/doc/handover.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
Ideas about a handover algorithm
|
||||
======================================================================
|
||||
|
||||
This is mostly based on the results presented in Chapter 8 of "Performance
|
||||
Enhancements in a Frequency Hopping GSM Network" by Thomas Toftegaard Nielsen
|
||||
and Joeroen Wigard.
|
||||
|
||||
|
||||
=== Reasons for performing handover ===
|
||||
|
||||
Section 2.1.1: Handover used in their CAPACITY simulation:
|
||||
|
||||
1) Interference Handover
|
||||
|
||||
Average RXLEV is satisfactory high, but average RXQUAL too low indicates
|
||||
interference to the channel. Handover should be made.
|
||||
|
||||
2) Bad Quality
|
||||
|
||||
Averaged RXQUAL is lower than a threshold
|
||||
|
||||
3) Low Level / Signal Strength
|
||||
|
||||
Average RXLEV is lower than a threshold
|
||||
|
||||
4) Distance Handover
|
||||
|
||||
MS is too far away from a cell (measured by TA)
|
||||
|
||||
5) Power budget / Better Cell
|
||||
|
||||
RX Level of neighbor cell is at least "HO Margin dB" dB better than the
|
||||
current serving cell.
|
||||
|
||||
=== Ideal parameters for HO algorithm ===
|
||||
|
||||
Chapter 8, Section 2.2, Table 24:
|
||||
|
||||
Window RXLEV averaging: 10 SACCH frames (no weighting)
|
||||
Window RXQUAL averaging: 1 SACCH frame (no averaging)
|
||||
Level Threashold: 1 of the last 1 AV-RXLEV values < -110dBm
|
||||
Quality Threshold: 3 of the last 4 AV-RXQUAL values >= 5
|
||||
Interference Threshold: 1 of the last AV-RXLEV > -85 dBm &
|
||||
3 of the last 4 AV-RXQUAL values >= 5
|
||||
Power Budget: Level of neighbor cell > 3 dB better
|
||||
Power Budget Interval: Every 6 SACCH frames (6 seconds ?!?)
|
||||
Distance Handover: Disabled
|
||||
Evaluation rule 1: RXLEV of the candidate cell a tleast -104 dBm
|
||||
Evaluation rule 2: Level of candidate cell > 3dB better own cell
|
||||
Timer Successful HO: 5 SACCH frames
|
||||
Timer Unsuccessful HO: 1 SACCH frame
|
||||
|
||||
In a non-frequency hopping case, RXQUAL threshold can be decreased to
|
||||
RXLEV >= 4
|
||||
|
||||
When frequency hopping is enabled, the following additional parameters
|
||||
should be introduced:
|
||||
|
||||
* No intra-cell handover
|
||||
* Use a HO Margin of 2dB
|
||||
|
||||
=== Handover Channel Reservation ===
|
||||
|
||||
In loaded network, each cell should reserve some channels for handovers,
|
||||
rather than using all of them for new call establishment. This reduces the
|
||||
need to drop calls due to failing handovers, at the expense of failing new call
|
||||
attempts.
|
||||
|
||||
=== Dynamic HO Margin ===
|
||||
|
||||
The handover margin (hysteresis) should depend on the RXQUAL. Optimal results
|
||||
were achieved with the following settings:
|
||||
* RXQUAL <= 4: 9 dB
|
||||
* RXQUAL == 5: 6 dB
|
||||
* RXQUAL >= 6: 1 dB
|
||||
|
||||
|
||||
|
||||
== Actual Handover on a protocol level ==
|
||||
|
||||
After the BSC has decided a handover shall be done, it has to
|
||||
|
||||
# allocate a channel at the new BTS
|
||||
# allocate a handover reference
|
||||
# activate the channel on the BTS side using RSL CHANNEL ACTIVATION,
|
||||
indicating the HO reference
|
||||
# BTS responds with CHAN ACT ACK, including GSM frame number
|
||||
# BSC sends 04.08 HO CMD to MS using old BTS
|
||||
|
@@ -4,4 +4,5 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
|
||||
subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
|
||||
gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
|
||||
bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \
|
||||
silent_call.h mgcp.h bssap.h
|
||||
silent_call.h mgcp.h meas_rep.h bitvec.h rest_octets.h \
|
||||
system_information.h handover.h bssap.h
|
||||
|
@@ -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 */
|
||||
|
65
openbsc/include/openbsc/bitvec.h
Normal file
65
openbsc/include/openbsc/bitvec.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef _BITVEC_H
|
||||
#define _BITVEC_H
|
||||
|
||||
/* bit vector utility routines */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are
|
||||
* defined relative to the 0x2b padding pattern */
|
||||
enum bit_value {
|
||||
ZERO = 0,
|
||||
ONE = 1,
|
||||
L = 2,
|
||||
H = 3,
|
||||
};
|
||||
|
||||
struct bitvec {
|
||||
unsigned int cur_bit; /* curser to the next unused bit */
|
||||
unsigned int data_len; /* length of data array in bytes */
|
||||
u_int8_t *data; /* pointer to data array */
|
||||
};
|
||||
|
||||
/* check if the bit is 0 or 1 for a given position inside a bitvec */
|
||||
enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr);
|
||||
|
||||
/* get the Nth set bit inside the bit vector */
|
||||
unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n);
|
||||
|
||||
/* Set a bit at given position */
|
||||
int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum,
|
||||
enum bit_value bit);
|
||||
|
||||
/* Set the next bit in the vector */
|
||||
int bitvec_set_bit(struct bitvec *bv, enum bit_value bit);
|
||||
|
||||
/* Set multiple bits at the current position */
|
||||
int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count);
|
||||
|
||||
/* Add an unsigned integer (of length count bits) to current position */
|
||||
int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count);
|
||||
|
||||
|
||||
/* Pad the bit vector up to a certain bit position */
|
||||
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit);
|
||||
|
||||
#endif /* _BITVEC_H */
|
@@ -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 */
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#define DMGCP 0x40000
|
||||
|
||||
#define DHO 0x80000
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
||||
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
||||
@@ -42,4 +44,15 @@ void debug_use_color(int use_color);
|
||||
void debug_timestamp(int enable);
|
||||
extern unsigned int debug_mask;
|
||||
|
||||
/* new logging interface */
|
||||
#define LOGP(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ##args)
|
||||
#define LOGPC(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ##args)
|
||||
|
||||
/* different levels */
|
||||
#define LOGL_DEBUG 1 /* debugging information */
|
||||
#define LOGL_INFO 3
|
||||
#define LOGL_NOTICE 5 /* abnormal/unexpected condition */
|
||||
#define LOGL_ERROR 7 /* error condition, requires user action */
|
||||
#define LOGL_FATAL 8 /* fatal, program aborted */
|
||||
|
||||
#endif /* _DEBUG_H */
|
||||
|
@@ -1,6 +1,8 @@
|
||||
#ifndef _GSM_04_08_H
|
||||
#define _GSM_04_08_H
|
||||
|
||||
#include <openbsc/meas_rep.h>
|
||||
|
||||
/* GSM TS 04.08 definitions */
|
||||
struct gsm_lchan;
|
||||
|
||||
@@ -88,6 +90,22 @@ struct gsm48_ass_cmd {
|
||||
u_int8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Chapter 10.5.2.2 */
|
||||
struct gsm48_cell_desc {
|
||||
u_int8_t bcc:3,
|
||||
ncc:3,
|
||||
arfcn_hi:2;
|
||||
u_int8_t arfcn_lo;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Chapter 9.1.15 */
|
||||
struct gsm48_ho_cmd {
|
||||
struct gsm48_cell_desc cell_desc;
|
||||
struct gsm48_chan_desc chan_desc;
|
||||
u_int8_t ho_ref;
|
||||
u_int8_t power_command;
|
||||
u_int8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Chapter 9.1.18 */
|
||||
struct gsm48_imm_ass {
|
||||
@@ -169,6 +187,13 @@ struct gsm48_control_channel_descr {
|
||||
u_int8_t t3212;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm48_cell_options {
|
||||
u_int8_t radio_link_timeout:4,
|
||||
dtx:2,
|
||||
pwrc:1,
|
||||
spare:1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.2.9 CM service request */
|
||||
struct gsm48_service_request {
|
||||
u_int8_t cm_service_type : 4,
|
||||
@@ -185,7 +210,7 @@ struct gsm48_system_information_type_1 {
|
||||
struct gsm48_system_information_type_header header;
|
||||
u_int8_t cell_channel_description[16];
|
||||
struct gsm48_rach_control rach_control;
|
||||
u_int8_t s1_reset;
|
||||
u_int8_t rest_octets[0]; /* NCH position on the CCCH */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.32 System information Type 2 */
|
||||
@@ -202,10 +227,10 @@ struct gsm48_system_information_type_3 {
|
||||
u_int16_t cell_identity;
|
||||
struct gsm48_loc_area_id lai;
|
||||
struct gsm48_control_channel_descr control_channel_desc;
|
||||
u_int8_t cell_options;
|
||||
struct gsm48_cell_options cell_options;
|
||||
struct gsm48_cell_sel_par cell_sel_par;
|
||||
struct gsm48_rach_control rach_control;
|
||||
u_int8_t s3_reset_octets[4];
|
||||
u_int8_t rest_octets[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.36 System information Type 4 */
|
||||
@@ -235,9 +260,15 @@ struct gsm48_system_information_type_6 {
|
||||
u_int8_t system_information;
|
||||
u_int16_t cell_identity;
|
||||
struct gsm48_loc_area_id lai;
|
||||
u_int8_t cell_options;
|
||||
struct gsm48_cell_options cell_options;
|
||||
u_int8_t ncc_permitted;
|
||||
u_int8_t si_6_reset[0];
|
||||
u_int8_t rest_octets[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.43a System Information type 13 */
|
||||
struct gsm48_system_information_type_13 {
|
||||
struct gsm48_system_information_type_header header;
|
||||
u_int8_t rest_octets[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.2.12 IMSI Detach Indication */
|
||||
@@ -618,30 +649,6 @@ enum gsm48_reject_value {
|
||||
GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16,
|
||||
};
|
||||
|
||||
|
||||
/* extracted from a L3 measurement report IE */
|
||||
struct gsm_meas_rep_cell {
|
||||
u_int8_t rxlev;
|
||||
u_int8_t bcch_freq; /* fixme: translate to ARFCN */
|
||||
u_int8_t bsic;
|
||||
};
|
||||
|
||||
struct gsm_meas_rep {
|
||||
unsigned int flags;
|
||||
u_int8_t rxlev_full;
|
||||
u_int8_t rxqual_full;
|
||||
u_int8_t rxlev_sub;
|
||||
u_int8_t rxqual_sub;
|
||||
int num_cell;
|
||||
struct gsm_meas_rep_cell cell[6];
|
||||
};
|
||||
#define MEAS_REP_F_DTX 0x01
|
||||
#define MEAS_REP_F_VALID 0x02
|
||||
#define MEAS_REP_F_BA1 0x04
|
||||
|
||||
void gsm48_parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
|
||||
int len);
|
||||
|
||||
enum chreq_type {
|
||||
CHREQ_T_EMERG_CALL,
|
||||
CHREQ_T_CALL_REEST_TCH_F,
|
||||
@@ -656,6 +663,9 @@ enum chreq_type {
|
||||
CHREQ_T_PAG_R_ANY_NECI1,
|
||||
CHREQ_T_PAG_R_TCH_F,
|
||||
CHREQ_T_PAG_R_TCH_FH,
|
||||
CHREQ_T_LMU,
|
||||
CHREQ_T_RESERVED_SDCCH,
|
||||
CHREQ_T_RESERVED_IGNORE,
|
||||
};
|
||||
|
||||
/* Chapter 11.3 */
|
||||
@@ -738,7 +748,6 @@ struct gsm_trans;
|
||||
|
||||
/* config options controlling the behaviour of the lower leves */
|
||||
void gsm0408_allow_everyone(int allow);
|
||||
void gsm0408_set_reject_cause(int cause);
|
||||
|
||||
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
|
||||
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
|
||||
@@ -760,6 +769,8 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
|
||||
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
|
||||
u_int8_t apdu_len, const u_int8_t *apdu);
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_class);
|
||||
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
|
||||
u_int8_t power_command, u_int8_t ho_ref);
|
||||
|
||||
int bsc_upqueue(struct gsm_network *net);
|
||||
|
||||
@@ -779,5 +790,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr);
|
||||
|
||||
int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode);
|
||||
int gsm48_rx_rr_modif_ack(struct msgb *msg);
|
||||
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg);
|
||||
|
||||
|
||||
#endif
|
||||
|
@@ -38,6 +38,13 @@ enum gsm_chan_t {
|
||||
GSM_LCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
/* RRLP mode of operation */
|
||||
enum rrlp_mode {
|
||||
RRLP_MODE_NONE,
|
||||
RRLP_MODE_MS_BASED,
|
||||
RRLP_MODE_MS_PREF,
|
||||
RRLP_MODE_ASS_PREF,
|
||||
};
|
||||
|
||||
/* Channel Request reason */
|
||||
enum gsm_chreq_reason_t {
|
||||
@@ -53,6 +60,7 @@ enum gsm_chreq_reason_t {
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/mncc.h>
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/bitvec.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
@@ -148,6 +156,20 @@ struct gsm_loc_updating_operation {
|
||||
unsigned int waiting_for_imei : 1;
|
||||
};
|
||||
|
||||
/* Maximum number of neighbor cells whose average we track */
|
||||
#define MAX_NEIGH_MEAS 10
|
||||
/* Maximum size of the averaging window for neighbor cells */
|
||||
#define MAX_WIN_NEIGH_AVG 10
|
||||
|
||||
/* processed neighbor measurements for one cell */
|
||||
struct neigh_meas_proc {
|
||||
u_int16_t arfcn;
|
||||
u_int8_t bsic;
|
||||
u_int8_t rxlev[MAX_WIN_NEIGH_AVG];
|
||||
unsigned int rxlev_cnt;
|
||||
u_int8_t last_seen_nr;
|
||||
};
|
||||
|
||||
#define MAX_A5_KEY_LEN (128/8)
|
||||
#define RSL_ENC_ALG_A5(x) (x+1)
|
||||
|
||||
@@ -156,6 +178,13 @@ struct gsm_loc_updating_operation {
|
||||
#define LCHAN_SAPI_MS 1
|
||||
#define LCHAN_SAPI_NET 2
|
||||
|
||||
/* state of a logical channel */
|
||||
enum gsm_lchan_state {
|
||||
LCHAN_S_NONE, /* channel is not active */
|
||||
LCHAN_S_ACTIVE, /* channel is active and operational */
|
||||
LCHAN_S_INACTIVE, /* channel is set inactive */
|
||||
};
|
||||
|
||||
struct gsm_lchan {
|
||||
/* The TS that we're part of */
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
@@ -167,6 +196,8 @@ struct gsm_lchan {
|
||||
enum rsl_cmod_spd rsl_cmode;
|
||||
/* If TCH, traffic channel mode */
|
||||
enum gsm48_chan_mode tch_mode;
|
||||
/* State */
|
||||
enum gsm_lchan_state state;
|
||||
/* Power levels for MS and BTS */
|
||||
u_int8_t bs_power;
|
||||
u_int8_t ms_power;
|
||||
@@ -201,6 +232,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 {
|
||||
@@ -228,13 +277,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];
|
||||
};
|
||||
@@ -305,7 +347,6 @@ struct gsm_paging_request {
|
||||
gsm_cbfn *cbfn;
|
||||
void *cbfn_param;
|
||||
};
|
||||
#define T3113_VALUE 60, 0
|
||||
|
||||
/*
|
||||
* This keeps track of the paging status of one BTS. It
|
||||
@@ -356,7 +397,6 @@ struct gsm_bts {
|
||||
/* should the channel allocator allocate channels from high TRX to TRX0,
|
||||
* rather than starting from TRX0 and go upwards? */
|
||||
int chan_alloc_reverse;
|
||||
int cell_barred;
|
||||
/* maximum Tx power that the MS is permitted to use in this cell */
|
||||
int ms_max_power;
|
||||
|
||||
@@ -373,8 +413,6 @@ struct gsm_bts {
|
||||
/* number of this BTS on given E1 link */
|
||||
u_int8_t bts_nr;
|
||||
|
||||
struct gsm48_control_channel_descr chan_desc;
|
||||
|
||||
/* paging state and control */
|
||||
struct gsm_bts_paging_state paging;
|
||||
|
||||
@@ -385,11 +423,28 @@ struct gsm_bts {
|
||||
struct gsm_nm_state nm_state;
|
||||
} site_mgr;
|
||||
|
||||
/* parameters from which we build SYSTEM INFORMATION */
|
||||
struct {
|
||||
struct gsm48_rach_control rach_control;
|
||||
u_int8_t ncc_permitted;
|
||||
struct gsm48_cell_sel_par cell_sel_par;
|
||||
struct gsm48_cell_options cell_options;
|
||||
struct gsm48_control_channel_descr chan_desc;
|
||||
struct bitvec neigh_list;
|
||||
struct bitvec cell_alloc;
|
||||
struct {
|
||||
/* bitmask large enough for all possible ARFCN's */
|
||||
u_int8_t neigh_list[1024/8];
|
||||
u_int8_t cell_alloc[1024/8];
|
||||
} data;
|
||||
} si_common;
|
||||
|
||||
/* ip.accesss Unit ID's have Site/BTS/TRX layout */
|
||||
union {
|
||||
struct {
|
||||
u_int16_t site_id;
|
||||
u_int16_t bts_id;
|
||||
u_int32_t flags;
|
||||
} ip_access;
|
||||
struct {
|
||||
struct {
|
||||
@@ -418,12 +473,58 @@ struct gsm_bts {
|
||||
struct llist_head trx_list;
|
||||
};
|
||||
|
||||
/* Some statistics of our network */
|
||||
struct gsmnet_stats {
|
||||
struct {
|
||||
unsigned long total;
|
||||
unsigned long no_channel;
|
||||
} chreq;
|
||||
struct {
|
||||
unsigned long attempted;
|
||||
unsigned long no_channel; /* no channel available */
|
||||
unsigned long timeout; /* T3103 timeout */
|
||||
unsigned long completed; /* HO COMPL received */
|
||||
unsigned long failed; /* HO FAIL received */
|
||||
} handover;
|
||||
struct {
|
||||
unsigned long attach;
|
||||
unsigned long normal;
|
||||
unsigned long periodic;
|
||||
unsigned long detach;
|
||||
} loc_upd_type;
|
||||
struct {
|
||||
unsigned long reject;
|
||||
unsigned long accept;
|
||||
} loc_upd_resp;
|
||||
struct {
|
||||
unsigned long attempted;
|
||||
unsigned long detached;
|
||||
unsigned long completed;
|
||||
unsigned long expired;
|
||||
} paging;
|
||||
struct {
|
||||
unsigned long submitted; /* MO SMS submissions */
|
||||
unsigned long no_receiver;
|
||||
unsigned long delivered; /* MT SMS deliveries */
|
||||
unsigned long rp_err_mem;
|
||||
unsigned long rp_err_other;
|
||||
} sms;
|
||||
struct {
|
||||
unsigned long dialled; /* total number of dialled calls */
|
||||
unsigned long alerted; /* we alerted the other end */
|
||||
unsigned long connected;/* how many calls were accepted */
|
||||
} call;
|
||||
};
|
||||
|
||||
enum gsm_auth_policy {
|
||||
GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */
|
||||
GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */
|
||||
GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */
|
||||
};
|
||||
|
||||
#define GSM_T3101_DEFAULT 10
|
||||
#define GSM_T3113_DEFAULT 60
|
||||
|
||||
/*
|
||||
* internal data for audio management
|
||||
*/
|
||||
@@ -439,8 +540,28 @@ struct gsm_network {
|
||||
char *name_long;
|
||||
char *name_short;
|
||||
enum gsm_auth_policy auth_policy;
|
||||
enum gsm48_reject_value reject_cause;
|
||||
int a5_encryption;
|
||||
int neci;
|
||||
int send_mm_info;
|
||||
struct {
|
||||
int active;
|
||||
/* Window RXLEV averaging */
|
||||
unsigned int win_rxlev_avg; /* number of SACCH frames */
|
||||
/* Window RXQUAL averaging */
|
||||
unsigned int win_rxqual_avg; /* number of SACCH frames */
|
||||
/* Window RXLEV neighbouring cells averaging */
|
||||
unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */
|
||||
|
||||
/* how often should we check for power budget HO */
|
||||
unsigned int pwr_interval; /* SACCH frames */
|
||||
/* how much better does a neighbor cell have to be ? */
|
||||
unsigned int pwr_hysteresis; /* dBm */
|
||||
/* maximum distacne before we try a handover */
|
||||
unsigned int max_distance; /* TA values */
|
||||
} handover;
|
||||
|
||||
struct gsmnet_stats stats;
|
||||
|
||||
struct gsm_audio_support **audio_support;
|
||||
int audio_length;
|
||||
@@ -454,6 +575,28 @@ struct gsm_network {
|
||||
|
||||
unsigned int num_bts;
|
||||
struct llist_head bts_list;
|
||||
|
||||
/* timer values */
|
||||
int T3101;
|
||||
int T3103;
|
||||
int T3105;
|
||||
int T3107;
|
||||
int T3109;
|
||||
int T3111;
|
||||
int T3113;
|
||||
int T3115;
|
||||
int T3117;
|
||||
int T3119;
|
||||
int T3141;
|
||||
|
||||
/* Radio Resource Location Protocol (TS 04.31) */
|
||||
struct {
|
||||
enum rrlp_mode mode;
|
||||
} rrlp;
|
||||
|
||||
/* a hack for On Waves. It must be signed */
|
||||
int32_t core_country_code;
|
||||
int32_t core_network_code;
|
||||
};
|
||||
|
||||
#define SMS_HDR_SIZE 128
|
||||
@@ -485,6 +628,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);
|
||||
@@ -510,6 +658,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)
|
||||
{
|
||||
@@ -538,6 +687,11 @@ static inline int is_siemens_bts(struct gsm_bts *bts)
|
||||
enum gsm_auth_policy gsm_auth_policy_parse(const char *arg);
|
||||
const char *gsm_auth_policy_name(enum gsm_auth_policy policy);
|
||||
|
||||
enum rrlp_mode rrlp_mode_parse(const char *arg);
|
||||
const char *rrlp_mode_name(enum rrlp_mode mode);
|
||||
|
||||
void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
|
||||
|
||||
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan);
|
||||
|
||||
#endif
|
||||
|
@@ -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
|
||||
|
8
openbsc/include/openbsc/handover.h
Normal file
8
openbsc/include/openbsc/handover.h
Normal file
@@ -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 */
|
84
openbsc/include/openbsc/meas_rep.h
Normal file
84
openbsc/include/openbsc/meas_rep.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef _MEAS_REP_H
|
||||
#define _MEAS_REP_H
|
||||
|
||||
#define MRC_F_PROCESSED 0x0001
|
||||
|
||||
/* extracted from a L3 measurement report IE */
|
||||
struct gsm_meas_rep_cell {
|
||||
u_int8_t rxlev;
|
||||
u_int8_t bsic;
|
||||
u_int16_t arfcn;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/* RX Level and RX Quality */
|
||||
struct gsm_rx_lev_qual {
|
||||
u_int8_t rx_lev;
|
||||
u_int8_t rx_qual;
|
||||
};
|
||||
|
||||
/* unidirectional measumrement report */
|
||||
struct gsm_meas_rep_unidir {
|
||||
struct gsm_rx_lev_qual full;
|
||||
struct gsm_rx_lev_qual sub;
|
||||
};
|
||||
|
||||
#define MEAS_REP_F_UL_DTX 0x01
|
||||
#define MEAS_REP_F_DL_VALID 0x02
|
||||
#define MEAS_REP_F_BA1 0x04
|
||||
#define MEAS_REP_F_DL_DTX 0x08
|
||||
#define MEAS_REP_F_MS_TO 0x10
|
||||
#define MEAS_REP_F_MS_L1 0x20
|
||||
#define MEAS_REP_F_FPC 0x40
|
||||
|
||||
/* parsed uplink and downlink measurement result */
|
||||
struct gsm_meas_rep {
|
||||
/* back-pointer to the logical channel */
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
/* number of the measurement report */
|
||||
u_int8_t nr;
|
||||
/* flags, see MEAS_REP_F_* */
|
||||
unsigned int flags;
|
||||
|
||||
/* uplink and downlink rxlev, rxqual; full and sub */
|
||||
struct gsm_meas_rep_unidir ul;
|
||||
struct gsm_meas_rep_unidir dl;
|
||||
|
||||
u_int8_t bs_power;
|
||||
u_int8_t ms_timing_offset;
|
||||
struct {
|
||||
int8_t pwr; /* MS power in dBm */
|
||||
u_int8_t ta; /* MS timing advance */
|
||||
} ms_l1;
|
||||
|
||||
/* neighbor measurement reports for up to 6 cells */
|
||||
int num_cell;
|
||||
struct gsm_meas_rep_cell cell[6];
|
||||
};
|
||||
|
||||
enum meas_rep_field {
|
||||
MEAS_REP_DL_RXLEV_FULL,
|
||||
MEAS_REP_DL_RXLEV_SUB,
|
||||
MEAS_REP_DL_RXQUAL_FULL,
|
||||
MEAS_REP_DL_RXQUAL_SUB,
|
||||
MEAS_REP_UL_RXLEV_FULL,
|
||||
MEAS_REP_UL_RXLEV_SUB,
|
||||
MEAS_REP_UL_RXQUAL_FULL,
|
||||
MEAS_REP_UL_RXQUAL_SUB,
|
||||
};
|
||||
|
||||
/* obtain an average over the last 'num' fields in the meas reps */
|
||||
int get_meas_rep_avg(const struct gsm_lchan *lchan,
|
||||
enum meas_rep_field field, unsigned int num);
|
||||
|
||||
/* Check if N out of M last values for FIELD are >= bd */
|
||||
int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan,
|
||||
enum meas_rep_field field,
|
||||
unsigned int n, unsigned int m, int be);
|
||||
|
||||
unsigned int calc_initial_idx(unsigned int array_size,
|
||||
unsigned int meas_rep_idx,
|
||||
unsigned int num_values);
|
||||
|
||||
#endif /* _MEAS_REP_H */
|
@@ -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];
|
||||
|
122
openbsc/include/openbsc/rest_octets.h
Normal file
122
openbsc/include/openbsc/rest_octets.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef _REST_OCTETS_H
|
||||
#define _REST_OCTETS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
|
||||
/* generate SI1 rest octets */
|
||||
int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos);
|
||||
|
||||
struct gsm48_si_selection_params {
|
||||
u_int16_t penalty_time:5,
|
||||
temp_offs:3,
|
||||
cell_resel_off:6,
|
||||
cbq:1,
|
||||
present:1;
|
||||
};
|
||||
|
||||
struct gsm48_si_power_offset {
|
||||
u_int8_t power_offset:2,
|
||||
present:1;
|
||||
};
|
||||
|
||||
struct gsm48_si3_gprs_ind {
|
||||
u_int8_t si13_position:1,
|
||||
ra_colour:3,
|
||||
present:1;
|
||||
};
|
||||
|
||||
struct gsm48_lsa_params {
|
||||
u_int32_t prio_thr:3,
|
||||
lsa_offset:3,
|
||||
mcc:12,
|
||||
mnc:12;
|
||||
unsigned int present;
|
||||
};
|
||||
|
||||
struct gsm48_si_ro_info {
|
||||
struct gsm48_si_selection_params selection_params;
|
||||
struct gsm48_si_power_offset power_offset;
|
||||
u_int8_t si2ter_indicator;
|
||||
u_int8_t early_cm_ctrl;
|
||||
struct {
|
||||
u_int8_t where:3,
|
||||
present:1;
|
||||
} scheduling;
|
||||
struct gsm48_si3_gprs_ind gprs_ind;
|
||||
|
||||
/* SI 4 specific */
|
||||
struct gsm48_lsa_params lsa_params;
|
||||
u_int16_t cell_id;
|
||||
u_int8_t break_ind; /* do we have SI7 + SI8 ? */
|
||||
};
|
||||
|
||||
|
||||
/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
|
||||
int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3);
|
||||
|
||||
/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
|
||||
int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4);
|
||||
|
||||
enum pbcch_carrier_type {
|
||||
PBCCH_BCCH,
|
||||
PBCCH_ARFCN,
|
||||
PBCCH_MAIO
|
||||
};
|
||||
|
||||
/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */
|
||||
enum gprs_nmo {
|
||||
GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
|
||||
GPRS_NMO_II = 1, /* all paging on CCCH */
|
||||
GPRS_NMO_III = 2, /* no paging coordination */
|
||||
};
|
||||
|
||||
struct gprs_cell_options {
|
||||
enum gprs_nmo nmo;
|
||||
/* T3168: wait for packet uplink assignment message */
|
||||
u_int32_t t3168; /* in milliseconds */
|
||||
/* T3192: wait for release of the TBF after reception of the final block */
|
||||
u_int32_t t3192; /* in milliseconds */
|
||||
u_int32_t drx_timer_max;/* in seconds */
|
||||
u_int32_t bs_cv_max;
|
||||
};
|
||||
|
||||
/* TS 04.60 Table 12.9.2 */
|
||||
struct gprs_power_ctrl_pars {
|
||||
u_int8_t alpha;
|
||||
u_int8_t t_avg_w;
|
||||
u_int8_t t_avg_t;
|
||||
u_int8_t pc_meas_chan;
|
||||
u_int8_t n_avg_i;
|
||||
};
|
||||
|
||||
struct gsm48_si13_info {
|
||||
struct gprs_cell_options cell_opts;
|
||||
struct gprs_power_ctrl_pars pwr_ctrl_pars;
|
||||
u_int8_t bcch_change_mark;
|
||||
u_int8_t si_change_field;
|
||||
u_int8_t pbcch_present;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u_int8_t rac;
|
||||
u_int8_t spgc_ccch_sup;
|
||||
u_int8_t net_ctrl_ord;
|
||||
u_int8_t prio_acc_thr;
|
||||
} no_pbcch;
|
||||
struct {
|
||||
u_int8_t psi1_rep_per;
|
||||
u_int8_t pb;
|
||||
u_int8_t tsc;
|
||||
u_int8_t tn;
|
||||
enum pbcch_carrier_type carrier_type;
|
||||
u_int16_t arfcn;
|
||||
u_int8_t maio;
|
||||
} pbcch;
|
||||
};
|
||||
};
|
||||
|
||||
/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
|
||||
int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13);
|
||||
|
||||
#endif /* _REST_OCTETS_H */
|
@@ -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 */
|
||||
|
@@ -40,6 +40,7 @@ enum signal_subsystems {
|
||||
SS_LCHAN,
|
||||
SS_SUBSCR,
|
||||
SS_SCALL,
|
||||
SS_GLOBAL,
|
||||
};
|
||||
|
||||
/* SS_PAGING signals */
|
||||
@@ -58,6 +59,7 @@ enum signal_sms {
|
||||
/* SS_ABISIP signals */
|
||||
enum signal_abisip {
|
||||
S_ABISIP_CRCX_ACK,
|
||||
S_ABISIP_MDCX_ACK,
|
||||
S_ABISIP_DLCX_IND,
|
||||
};
|
||||
|
||||
@@ -78,12 +80,19 @@ enum signal_lchan {
|
||||
* signal handler.
|
||||
*/
|
||||
S_LCHAN_UNEXPECTED_RELEASE,
|
||||
S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */
|
||||
S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */
|
||||
S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */
|
||||
S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */
|
||||
S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */
|
||||
S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */
|
||||
};
|
||||
|
||||
/* SS_SUBSCR signals */
|
||||
enum signal_subscr {
|
||||
S_SUBSCR_ATTACHED,
|
||||
S_SUBSCR_DETACHED,
|
||||
S_SUBSCR_IDENTITY, /* we've received some identity information */
|
||||
};
|
||||
|
||||
/* SS_SCALL signals */
|
||||
@@ -93,6 +102,10 @@ enum signal_scall {
|
||||
S_SCALL_DETACHED,
|
||||
};
|
||||
|
||||
enum signal_global {
|
||||
S_GLOBAL_SHUTDOWN,
|
||||
};
|
||||
|
||||
typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data);
|
||||
|
||||
|
6
openbsc/include/openbsc/system_information.h
Normal file
6
openbsc/include/openbsc/system_information.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _SYSTEM_INFO_H
|
||||
#define _SYSTEM_INFO_H
|
||||
|
||||
int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type);
|
||||
|
||||
#endif
|
@@ -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)
|
||||
{
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -11,11 +11,13 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
|
||||
gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.c \
|
||||
trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
|
||||
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
|
||||
talloc_ctx.c telnet_interface.c
|
||||
talloc_ctx.c system_information.c bitvec.c rest_octets.c \
|
||||
rtp_proxy.c telnet_interface.c
|
||||
|
||||
libmsc_a_SOURCES = gsm_subscriber.c db.c \
|
||||
mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
|
||||
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c
|
||||
mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
|
||||
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \
|
||||
handover_logic.c handover_decision.c meas_rep.c
|
||||
|
||||
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
|
||||
|
||||
|
109
openbsc/src/abis_nm.c
Executable file → Normal file
109
openbsc/src/abis_nm.c
Executable file → Normal file
@@ -635,7 +635,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
|
||||
nm_state = &trx->bb_transc.nm_state;
|
||||
break;
|
||||
case NM_OC_CHANNEL:
|
||||
if (obj_inst->trx_nr > bts->num_trx) {
|
||||
if (obj_inst->trx_nr >= bts->num_trx) {
|
||||
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
|
||||
return NULL;
|
||||
}
|
||||
@@ -671,7 +671,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
|
||||
nm_state = &bts->bs11.rack.nm_state;
|
||||
break;
|
||||
case NM_OC_BS11_ENVABTSE:
|
||||
if (obj_inst->trx_nr > ARRAY_SIZE(bts->bs11.envabtse))
|
||||
if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse))
|
||||
return NULL;
|
||||
nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state;
|
||||
break;
|
||||
@@ -682,7 +682,7 @@ objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
|
||||
nm_state = &bts->gprs.cell.nm_state;
|
||||
break;
|
||||
case NM_OC_GPRS_NSVC:
|
||||
if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
|
||||
if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
|
||||
return NULL;
|
||||
nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state;
|
||||
break;
|
||||
@@ -719,7 +719,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
|
||||
obj = &trx->bb_transc;
|
||||
break;
|
||||
case NM_OC_CHANNEL:
|
||||
if (obj_inst->trx_nr > bts->num_trx) {
|
||||
if (obj_inst->trx_nr >= bts->num_trx) {
|
||||
DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr);
|
||||
return NULL;
|
||||
}
|
||||
@@ -738,7 +738,7 @@ objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
|
||||
obj = &bts->gprs.cell;
|
||||
break;
|
||||
case NM_OC_GPRS_NSVC:
|
||||
if (obj_inst->trx_nr > ARRAY_SIZE(bts->gprs.nsvc))
|
||||
if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc))
|
||||
return NULL;
|
||||
obj = &bts->gprs.nsvc[obj_inst->trx_nr];
|
||||
break;
|
||||
@@ -1088,8 +1088,8 @@ static int abis_nm_rcvmsg_manuf(struct msgb *mb)
|
||||
rc = abis_nm_rx_ipacc(mb);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "don't know how to parse OML for this "
|
||||
"BTS type (%u)\n", bts_type);
|
||||
LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this "
|
||||
"BTS type (%u)\n", bts_type);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
@@ -1106,12 +1106,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
|
||||
|
||||
/* Various consistency checks */
|
||||
if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
|
||||
fprintf(stderr, "ABIS OML placement 0x%x not supported\n",
|
||||
LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n",
|
||||
oh->placement);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (oh->sequence != 0) {
|
||||
fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n",
|
||||
LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n",
|
||||
oh->sequence);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1119,12 +1119,12 @@ int abis_nm_rcvmsg(struct msgb *msg)
|
||||
unsigned int l2_len = msg->tail - (u_int8_t *)msgb_l2(msg);
|
||||
unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr);
|
||||
if (oh->length + hlen > l2_len) {
|
||||
fprintf(stderr, "ABIS OML truncated message (%u > %u)\n",
|
||||
LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n",
|
||||
oh->length + sizeof(*oh), l2_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (oh->length + hlen < l2_len)
|
||||
fprintf(stderr, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
|
||||
LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
|
||||
#endif
|
||||
msg->l3h = (unsigned char *)oh + sizeof(*oh);
|
||||
|
||||
@@ -1137,11 +1137,11 @@ int abis_nm_rcvmsg(struct msgb *msg)
|
||||
break;
|
||||
case ABIS_OM_MDISC_MMI:
|
||||
case ABIS_OM_MDISC_TRAU:
|
||||
fprintf(stderr, "unimplemented ABIS OML message discriminator 0x%x\n",
|
||||
LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n",
|
||||
oh->mdisc);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n",
|
||||
LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n",
|
||||
oh->mdisc);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1755,7 +1755,8 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
|
||||
|
||||
/* As it turns out, the BS-11 has some very peculiar restrictions
|
||||
* on the channel combinations it allows */
|
||||
if (ts->trx->bts->type == GSM_BTS_TYPE_BS11) {
|
||||
switch (ts->trx->bts->type) {
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
switch (chan_comb) {
|
||||
case NM_CHANC_TCHHalf:
|
||||
case NM_CHANC_TCHHalf2:
|
||||
@@ -1801,6 +1802,83 @@ static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
|
||||
/* FIXME: only one CBCH allowed per cell */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GSM_BTS_TYPE_NANOBTS:
|
||||
switch (ts->nr) {
|
||||
case 0:
|
||||
if (ts->trx->nr == 0) {
|
||||
/* only on TRX0 */
|
||||
switch (chan_comb) {
|
||||
case NM_CHANC_BCCH:
|
||||
case NM_CHANC_mainBCCH:
|
||||
case NM_CHANC_BCCHComb:
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
switch (chan_comb) {
|
||||
case NM_CHANC_TCHFull:
|
||||
case NM_CHANC_TCHHalf:
|
||||
case NM_CHANC_IPAC_TCHFull_TCHHalf:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (ts->trx->nr == 0) {
|
||||
switch (chan_comb) {
|
||||
case NM_CHANC_SDCCH_CBCH:
|
||||
if (ts->trx->ts[0].nm_chan_comb ==
|
||||
NM_CHANC_mainBCCH)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
case NM_CHANC_SDCCH:
|
||||
case NM_CHANC_TCHFull:
|
||||
case NM_CHANC_TCHHalf:
|
||||
case NM_CHANC_IPAC_TCHFull_TCHHalf:
|
||||
case NM_CHANC_IPAC_TCHFull_PDCH:
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
switch (chan_comb) {
|
||||
case NM_CHANC_SDCCH:
|
||||
case NM_CHANC_TCHFull:
|
||||
case NM_CHANC_TCHHalf:
|
||||
case NM_CHANC_IPAC_TCHFull_TCHHalf:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
switch (chan_comb) {
|
||||
case NM_CHANC_TCHFull:
|
||||
case NM_CHANC_TCHHalf:
|
||||
case NM_CHANC_IPAC_TCHFull_TCHHalf:
|
||||
return 0;
|
||||
case NM_CHANC_IPAC_PDCH:
|
||||
case NM_CHANC_IPAC_TCHFull_PDCH:
|
||||
if (ts->trx->nr == 0)
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
default:
|
||||
/* unknown BTS type */
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2704,6 +2782,9 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
|
||||
int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
|
||||
|
||||
trx->rf_locked = locked;
|
||||
if (!trx->bts || !trx->bts->oml_link)
|
||||
return;
|
||||
|
||||
abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
|
||||
trx->bts->bts_nr, trx->nr, 0xff,
|
||||
new_state);
|
||||
|
@@ -38,6 +38,8 @@
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/meas_rep.h>
|
||||
#include <openbsc/rtp_proxy.h>
|
||||
|
||||
#define RSL_ALLOC_SIZE 1024
|
||||
#define RSL_ALLOC_HEADROOM 128
|
||||
@@ -205,32 +207,32 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
|
||||
if (ts->pchan != GSM_PCHAN_TCH_F &&
|
||||
ts->pchan != GSM_PCHAN_PDCH &&
|
||||
ts->pchan != GSM_PCHAN_TCH_F_PDCH)
|
||||
fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
} else if ((cbits & 0x1e) == 0x02) {
|
||||
lch_idx = cbits & 0x1; /* TCH/H */
|
||||
if (ts->pchan != GSM_PCHAN_TCH_H)
|
||||
fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
} else if ((cbits & 0x1c) == 0x04) {
|
||||
lch_idx = cbits & 0x3; /* SDCCH/4 */
|
||||
if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
|
||||
fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
} else if ((cbits & 0x18) == 0x08) {
|
||||
lch_idx = cbits & 0x7; /* SDCCH/8 */
|
||||
if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C)
|
||||
fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
|
||||
lch_idx = 0;
|
||||
if (ts->pchan != GSM_PCHAN_CCCH &&
|
||||
ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
|
||||
fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
|
||||
LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
|
||||
chan_nr, ts->pchan);
|
||||
/* FIXME: we should not return first sdcch4 !!! */
|
||||
} else {
|
||||
fprintf(stderr, "unknown chan_nr=0x%02x\n", chan_nr);
|
||||
LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -240,7 +242,7 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
|
||||
}
|
||||
|
||||
/* See Table 10.5.25 of GSM04.08 */
|
||||
u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
|
||||
u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_bts_trx_ts *ts = lchan->ts;
|
||||
u_int8_t cbits, chan_nr;
|
||||
@@ -488,6 +490,11 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
|
||||
/* set TCH Speech/Data */
|
||||
cm->spd_ind = lchan->rsl_cmode;
|
||||
|
||||
if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
|
||||
lchan->tch_mode != GSM48_CMODE_SIGN)
|
||||
LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
|
||||
"but tch_mode != signalling\n");
|
||||
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_SDCCH:
|
||||
cm->chan_rt = RSL_CMOD_CRT_SDCCH;
|
||||
@@ -570,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;
|
||||
@@ -597,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);
|
||||
|
||||
@@ -610,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);
|
||||
@@ -833,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;
|
||||
}
|
||||
|
||||
@@ -905,7 +921,11 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
|
||||
* to assign the activated channel to the MS */
|
||||
if (rslh->ie_chan != RSL_IE_CHAN_NR)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
msg->lchan->state = LCHAN_S_ACTIVE;
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -924,6 +944,10 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
|
||||
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
|
||||
TLVP_LEN(&tp, RSL_IE_CAUSE));
|
||||
|
||||
msg->lchan->state = LCHAN_S_NONE;
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan);
|
||||
|
||||
lchan_free(msg->lchan);
|
||||
return 0;
|
||||
}
|
||||
@@ -934,7 +958,8 @@ static int rsl_rx_conn_fail(struct msgb *msg)
|
||||
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
|
||||
struct tlv_parsed tp;
|
||||
|
||||
DEBUGPC(DRSL, "CONNECTION FAIL: ");
|
||||
/* FIXME: print which channel */
|
||||
LOGP(DRSL, LOGL_NOTICE, "CONNECTION FAIL: RELEASING\n");
|
||||
|
||||
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
|
||||
|
||||
@@ -942,53 +967,144 @@ static int rsl_rx_conn_fail(struct msgb *msg)
|
||||
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
|
||||
TLVP_LEN(&tp, RSL_IE_CAUSE));
|
||||
|
||||
DEBUGPC(DRSL, "RELEASING.\n");
|
||||
|
||||
/* FIXME: only free it after channel release ACK */
|
||||
return rsl_rf_chan_release(msg->lchan);
|
||||
}
|
||||
|
||||
static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
|
||||
const char *prefix)
|
||||
{
|
||||
DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ",
|
||||
prefix, rxlev2dbm(mru->full.rx_lev),
|
||||
prefix, rxlev2dbm(mru->sub.rx_lev));
|
||||
DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ",
|
||||
prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual);
|
||||
}
|
||||
|
||||
static void print_meas_rep(struct gsm_meas_rep *mr)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", mr->nr);
|
||||
|
||||
if (mr->flags & MEAS_REP_F_DL_DTX)
|
||||
DEBUGPC(DMEAS, "DTXd ");
|
||||
|
||||
print_meas_rep_uni(&mr->ul, "ul");
|
||||
DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power);
|
||||
if (mr->flags & MEAS_REP_F_MS_TO)
|
||||
DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset);
|
||||
|
||||
if (mr->flags & MEAS_REP_F_MS_L1) {
|
||||
DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr);
|
||||
DEBUGPC(DMEAS, "L1_FPC=%u ",
|
||||
mr->flags & MEAS_REP_F_FPC ? 1 : 0);
|
||||
DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta);
|
||||
}
|
||||
|
||||
if (mr->flags & MEAS_REP_F_UL_DTX)
|
||||
DEBUGPC(DMEAS, "DTXu ");
|
||||
if (mr->flags & MEAS_REP_F_BA1)
|
||||
DEBUGPC(DMEAS, "BA1 ");
|
||||
if (!(mr->flags & MEAS_REP_F_DL_VALID))
|
||||
DEBUGPC(DMEAS, "NOT VALID ");
|
||||
else
|
||||
print_meas_rep_uni(&mr->dl, "dl");
|
||||
|
||||
DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell);
|
||||
if (mr->num_cell == 7)
|
||||
return;
|
||||
for (i = 0; i < mr->num_cell; i++) {
|
||||
struct gsm_meas_rep_cell *mrc = &mr->cell[i];
|
||||
DEBUGP(DMEAS, "ARFCN=%u BSIC=%u => %d dBm\n", mrc->arfcn, mrc->bsic,
|
||||
rxlev2dbm(mrc->rxlev));
|
||||
}
|
||||
}
|
||||
|
||||
static int rsl_rx_meas_res(struct msgb *msg)
|
||||
{
|
||||
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
|
||||
struct tlv_parsed tp;
|
||||
struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan);
|
||||
u_int8_t len;
|
||||
const u_int8_t *val;
|
||||
int rc;
|
||||
|
||||
/* check if this channel is actually active */
|
||||
/* FIXME: maybe this check should be way more generic/centralized */
|
||||
if (msg->lchan->state != LCHAN_S_ACTIVE)
|
||||
return 0;
|
||||
|
||||
memset(mr, 0, sizeof(*mr));
|
||||
mr->lchan = msg->lchan;
|
||||
|
||||
DEBUGPC(DMEAS, "MEASUREMENT RESULT ");
|
||||
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
|
||||
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR))
|
||||
DEBUGPC(DMEAS, "NR=%d ", *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR));
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS)) {
|
||||
u_int8_t len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
|
||||
const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
|
||||
if (len >= 3) {
|
||||
if (val[0] & 0x40)
|
||||
DEBUGPC(DMEAS, "DTXd ");
|
||||
DEBUGPC(DMEAS, "RXL-FULL-up=%d RXL-SUB-up=%d ",
|
||||
val[0] & 0x3f, val[1] & 0x3f);
|
||||
DEBUGPC(DMEAS, "RXQ-FULL-up=%d RXQ-SUB-up=%d ",
|
||||
val[2]>>3 & 0x7, val[2] & 0x7);
|
||||
}
|
||||
if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) ||
|
||||
!TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) ||
|
||||
!TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
|
||||
return -EIO;
|
||||
|
||||
/* Mandatory Parts */
|
||||
mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR);
|
||||
|
||||
len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
|
||||
val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
|
||||
if (len >= 3) {
|
||||
if (val[0] & 0x40)
|
||||
mr->flags |= MEAS_REP_F_DL_DTX;
|
||||
mr->ul.full.rx_lev = val[0] & 0x3f;
|
||||
mr->ul.sub.rx_lev = val[1] & 0x3f;
|
||||
mr->ul.full.rx_qual = val[2]>>3 & 0x7;
|
||||
mr->ul.sub.rx_qual = val[2] & 0x7;
|
||||
}
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
|
||||
DEBUGPC(DMEAS, "BS_POWER=%d ", *TLVP_VAL(&tp, RSL_IE_BS_POWER));
|
||||
|
||||
mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER);
|
||||
|
||||
/* Optional Parts */
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET))
|
||||
DEBUGPC(DMEAS, "MS_TO=%d ",
|
||||
*TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET));
|
||||
mr->ms_timing_offset =
|
||||
*TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET);
|
||||
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) {
|
||||
const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
|
||||
u_int8_t pwr_lvl = val[0] >> 3;
|
||||
DEBUGPC(DMEAS, "L1_MS_PWR=%ddBm ",
|
||||
ms_pwr_dbm(msg->trx->bts->band, pwr_lvl));
|
||||
DEBUGPC(DMEAS, "L1_FPC=%u ", val[0] & 0x04 ? 1 : 0);
|
||||
DEBUGPC(DMEAS, "L1_TA=%u ", val[1]);
|
||||
val = TLVP_VAL(&tp, RSL_IE_L1_INFO);
|
||||
mr->flags |= MEAS_REP_F_MS_L1;
|
||||
mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3);
|
||||
if (val[0] & 0x04)
|
||||
mr->flags |= MEAS_REP_F_FPC;
|
||||
mr->ms_l1.ta = val[1];
|
||||
}
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
|
||||
DEBUGPC(DMEAS, "L3\n");
|
||||
msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO);
|
||||
return gsm0408_rcvmsg(msg, 0);
|
||||
} else
|
||||
DEBUGPC(DMEAS, "\n");
|
||||
rc = gsm48_parse_meas_rep(mr, msg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
print_meas_rep(mr);
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_MEAS_REP, mr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Chapter 8.4.7 */
|
||||
static int rsl_rx_hando_det(struct msgb *msg)
|
||||
{
|
||||
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
|
||||
struct tlv_parsed tp;
|
||||
|
||||
DEBUGP(DRSL, "HANDOVER DETECT ");
|
||||
|
||||
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
|
||||
|
||||
if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY))
|
||||
DEBUGPC(DRSL, "access delay = %u\n",
|
||||
*TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY));
|
||||
else
|
||||
DEBUGPC(DRSL, "\n");
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_DETECT, msg->lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1020,8 +1136,12 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
|
||||
case RSL_MT_MEAS_RES:
|
||||
rc = rsl_rx_meas_res(msg);
|
||||
break;
|
||||
case RSL_MT_HANDO_DET:
|
||||
rc = rsl_rx_hando_det(msg);
|
||||
break;
|
||||
case RSL_MT_RF_CHAN_REL_ACK:
|
||||
DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n");
|
||||
msg->lchan->state = LCHAN_S_NONE;
|
||||
lchan_free(msg->lchan);
|
||||
break;
|
||||
case RSL_MT_MODE_MODIFY_ACK:
|
||||
@@ -1067,7 +1187,7 @@ static int rsl_rx_error_rep(struct msgb *msg)
|
||||
struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
|
||||
struct tlv_parsed tp;
|
||||
|
||||
DEBUGP(DRSL, "ERROR REPORT ");
|
||||
LOGP(DRSL, LOGL_ERROR, "ERROR REPORT ");
|
||||
|
||||
rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh));
|
||||
|
||||
@@ -1075,7 +1195,7 @@ static int rsl_rx_error_rep(struct msgb *msg)
|
||||
print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
|
||||
TLVP_LEN(&tp, RSL_IE_CAUSE));
|
||||
|
||||
DEBUGPC(DRSL, "\n");
|
||||
LOGPC(DRSL, LOGL_ERROR, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1095,7 +1215,7 @@ static int abis_rsl_rx_trx(struct msgb *msg)
|
||||
break;
|
||||
case RSL_MT_OVERLOAD:
|
||||
/* indicate CCCH / ACCH / processor overload */
|
||||
DEBUGP(DRSL, "TRX: CCCH/ACCH/CPU Overload\n");
|
||||
LOGP(DRSL, LOGL_ERROR, "TRX: CCCH/ACCH/CPU Overload\n");
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n",
|
||||
@@ -1145,11 +1265,14 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
|
||||
lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
|
||||
chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
|
||||
|
||||
bts->network->stats.chreq.total++;
|
||||
|
||||
/* check availability / allocate channel */
|
||||
lchan = lchan_alloc(bts, lctype);
|
||||
if (!lchan) {
|
||||
DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n",
|
||||
lctype, rqd_ref->ra);
|
||||
DEBUGP(DRSL, "CHAN RQD: no resources for %s 0x%x\n",
|
||||
gsm_lchan_name(lctype), rqd_ref->ra);
|
||||
bts->network->stats.chreq.no_channel++;
|
||||
/* FIXME: send some kind of reject ?!? */
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -1163,7 +1286,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
|
||||
lchan->bs_power = 0; /* 0dB reduction, output power = Pn */
|
||||
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
|
||||
lchan->tch_mode = GSM48_CMODE_SIGN;
|
||||
rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
|
||||
rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0);
|
||||
|
||||
/* create IMMEDIATE ASSIGN 04.08 messge */
|
||||
memset(&ia, 0, sizeof(ia));
|
||||
@@ -1190,7 +1313,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
|
||||
/* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
|
||||
lchan->T3101.cb = t3101_expired;
|
||||
lchan->T3101.data = lchan;
|
||||
bsc_schedule_timer(&lchan->T3101, 10, 0);
|
||||
bsc_schedule_timer(&lchan->T3101, bts->network->T3101, 0);
|
||||
|
||||
/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
|
||||
ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
|
||||
@@ -1246,12 +1369,12 @@ static int abis_rsl_rx_cchan(struct msgb *msg)
|
||||
/* CCCH overloaded, IMM_ASSIGN was dropped */
|
||||
case RSL_MT_CBCH_LOAD_IND:
|
||||
/* current load on the CBCH */
|
||||
fprintf(stderr, "Unimplemented Abis RSL TRX message type "
|
||||
"0x%02x\n", rslh->c.msg_type);
|
||||
LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message "
|
||||
"type 0x%02x\n", rslh->c.msg_type);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n",
|
||||
rslh->c.msg_type);
|
||||
LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type "
|
||||
"0x%02x\n", rslh->c.msg_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1263,7 +1386,7 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
|
||||
struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
|
||||
u_int8_t *rlm_cause = rllh->data;
|
||||
|
||||
DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
|
||||
LOGP(DRLL, LOGL_ERROR, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
|
||||
|
||||
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
|
||||
|
||||
@@ -1344,38 +1467,91 @@ 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;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
return 0x01;
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
return 0x02;
|
||||
/* FIXME: Type1 half-rate and type3 half-rate */
|
||||
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);
|
||||
@@ -1383,12 +1559,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;
|
||||
|
||||
@@ -1396,12 +1572,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));
|
||||
@@ -1409,34 +1584,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);
|
||||
gsm_ts_name(lchan->ts), dh->chan_nr, inet_ntoa(ia), port,
|
||||
rtp_payload2, lchan->abis_ip.conn_id, lchan->abis_ip.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);
|
||||
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);
|
||||
|
||||
@@ -1445,6 +1612,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();
|
||||
@@ -1467,9 +1648,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
|
||||
@@ -1479,30 +1658,54 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1510,6 +1713,7 @@ 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));
|
||||
|
||||
@@ -1517,6 +1721,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;
|
||||
@@ -1544,6 +1754,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
|
||||
@@ -1555,7 +1766,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");
|
||||
@@ -1596,15 +1808,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);
|
||||
@@ -1653,11 +1865,11 @@ int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
|
||||
/* From Table 10.5.33 of GSM 04.08 */
|
||||
int rsl_number_of_paging_subchannels(struct gsm_bts *bts)
|
||||
{
|
||||
if (bts->chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
|
||||
return MAX(1, (3 - bts->chan_desc.bs_ag_blks_res))
|
||||
* (bts->chan_desc.bs_pa_mfrms + 2);
|
||||
if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
|
||||
return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res))
|
||||
* (bts->si_common.chan_desc.bs_pa_mfrms + 2);
|
||||
} else {
|
||||
return (9 - bts->chan_desc.bs_ag_blks_res)
|
||||
* (bts->chan_desc.bs_pa_mfrms + 2);
|
||||
return (9 - bts->si_common.chan_desc.bs_ag_blks_res)
|
||||
* (bts->si_common.chan_desc.bs_pa_mfrms + 2);
|
||||
}
|
||||
}
|
||||
|
170
openbsc/src/bitvec.c
Normal file
170
openbsc/src/bitvec.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/* bit vector utility routines */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/bitvec.h>
|
||||
|
||||
#define BITNUM_FROM_COMP(byte, bit) ((byte*8)+bit)
|
||||
|
||||
static inline unsigned int bytenum_from_bitnum(unsigned int bitnum)
|
||||
{
|
||||
unsigned int bytenum = bitnum / 8;
|
||||
|
||||
return bytenum;
|
||||
}
|
||||
|
||||
/* convert ZERO/ONE/L/H to a bitmask at given pos in a byte */
|
||||
static u_int8_t bitval2mask(enum bit_value bit, u_int8_t bitnum)
|
||||
{
|
||||
int bitval;
|
||||
|
||||
switch (bit) {
|
||||
case ZERO:
|
||||
bitval = (0 << bitnum);
|
||||
break;
|
||||
case ONE:
|
||||
bitval = (1 << bitnum);
|
||||
break;
|
||||
case L:
|
||||
bitval = ((0x2b ^ (0 << bitnum)) & (1 << bitnum));
|
||||
break;
|
||||
case H:
|
||||
bitval = ((0x2b ^ (1 << bitnum)) & (1 << bitnum));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return bitval;
|
||||
}
|
||||
|
||||
/* check if the bit is 0 or 1 for a given position inside a bitvec */
|
||||
enum bit_value bitvec_get_bit_pos(struct bitvec *bv, unsigned int bitnr)
|
||||
{
|
||||
unsigned int bytenum = bytenum_from_bitnum(bitnr);
|
||||
unsigned int bitnum = 7 - (bitnr % 8);
|
||||
u_int8_t bitval;
|
||||
|
||||
if (bytenum >= bv->data_len)
|
||||
return -EINVAL;
|
||||
|
||||
bitval = bitval2mask(ONE, bitnum);
|
||||
|
||||
if (bv->data[bytenum] & bitval)
|
||||
return ONE;
|
||||
|
||||
return ZERO;
|
||||
}
|
||||
|
||||
/* get the Nth set bit inside the bit vector */
|
||||
unsigned int bitvec_get_nth_set_bit(struct bitvec *bv, unsigned int n)
|
||||
{
|
||||
unsigned int i, k = 0;
|
||||
|
||||
for (i = 0; i < bv->data_len*8; i++) {
|
||||
if (bitvec_get_bit_pos(bv, i) == ONE) {
|
||||
k++;
|
||||
if (k == n)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set the bit at a given position inside a bitvec */
|
||||
int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnr,
|
||||
enum bit_value bit)
|
||||
{
|
||||
unsigned int bytenum = bytenum_from_bitnum(bitnr);
|
||||
unsigned int bitnum = 7 - (bitnr % 8);
|
||||
u_int8_t bitval;
|
||||
|
||||
if (bytenum >= bv->data_len)
|
||||
return -EINVAL;
|
||||
|
||||
/* first clear the bit */
|
||||
bitval = bitval2mask(ONE, bitnum);
|
||||
bv->data[bytenum] &= ~bitval;
|
||||
|
||||
/* then set it to desired value */
|
||||
bitval = bitval2mask(bit, bitnum);
|
||||
bv->data[bytenum] |= bitval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set the next bit inside a bitvec */
|
||||
int bitvec_set_bit(struct bitvec *bv, enum bit_value bit)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = bitvec_set_bit_pos(bv, bv->cur_bit, bit);
|
||||
if (!rc)
|
||||
bv->cur_bit++;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* set multiple bits (based on array of bitvals) at current pos */
|
||||
int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
rc = bitvec_set_bit(bv, bits[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set multiple bits (based on numeric value) at current pos */
|
||||
int bitvec_set_uint(struct bitvec *bv, unsigned int ui, int num_bits)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < num_bits; i++) {
|
||||
int bit = 0;
|
||||
if (ui & (1 << (num_bits - i - 1)))
|
||||
bit = 1;
|
||||
rc = bitvec_set_bit(bv, bit);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* pad all remaining bits up to num_bits */
|
||||
int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = bv->cur_bit; i <= up_to_bit; i++)
|
||||
bitvec_set_bit(bv, L);
|
||||
|
||||
return 0;
|
||||
}
|
@@ -35,12 +35,12 @@
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
/* MCC and MNC for the Location Area Identifier */
|
||||
struct gsm_network *bsc_gsmnet = 0;
|
||||
static const char *database_name = "hlr.sqlite3";
|
||||
static const char *config_file = "openbsc.cfg";
|
||||
extern int ipacc_rtp_direct;
|
||||
|
||||
extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *),
|
||||
const char *cfg_file);
|
||||
@@ -72,9 +72,9 @@ static void print_help()
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
printf(" -l --database db-name The database to use\n");
|
||||
printf(" -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\n");
|
||||
printf(" -p --pcap file The filename of the pcap file\n");
|
||||
printf(" -T --timestamp Prefix every log line with a timestamp\n");
|
||||
printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char** argv)
|
||||
@@ -88,7 +88,6 @@ static void handle_options(int argc, char** argv)
|
||||
{"disable-color", 0, 0, 's'},
|
||||
{"database", 1, 0, 'l'},
|
||||
{"authorize-everyone", 0, 0, 'a'},
|
||||
{"reject-cause", 1, 0, 'r'},
|
||||
{"pcap", 1, 0, 'p'},
|
||||
{"timestamp", 0, 0, 'T'},
|
||||
{"rtp-proxy", 0, 0, 'P'},
|
||||
@@ -117,9 +116,6 @@ static void handle_options(int argc, char** argv)
|
||||
case 'c':
|
||||
config_file = strdup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
gsm0408_set_reject_cause(atoi(optarg));
|
||||
break;
|
||||
case 'p':
|
||||
create_pcap_file(optarg);
|
||||
break;
|
||||
@@ -143,6 +139,7 @@ static void signal_handler(int signal)
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
bsc_shutdown_net(bsc_gsmnet);
|
||||
dispatch_signal(SS_GLOBAL, S_GLOBAL_SHUTDOWN, NULL);
|
||||
sleep(3);
|
||||
exit(0);
|
||||
break;
|
||||
@@ -165,6 +162,7 @@ int main(int argc, char **argv)
|
||||
talloc_ctx_init();
|
||||
on_dso_load_token();
|
||||
on_dso_load_rrlp();
|
||||
on_dso_load_ho_dec();
|
||||
|
||||
/* parse options */
|
||||
handle_options(argc, argv);
|
||||
@@ -191,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);
|
||||
|
@@ -28,16 +28,15 @@
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/misdn.h>
|
||||
#include <openbsc/telnet_interface.h>
|
||||
#include <openbsc/system_information.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/talloc.h>
|
||||
|
||||
/* global pointer to the gsm network data structure */
|
||||
extern struct gsm_network *bsc_gsmnet;
|
||||
extern int ipacc_rtp_direct;
|
||||
|
||||
static void patch_nm_tables(struct gsm_bts *bts);
|
||||
static void patch_si_tables(struct gsm_bts *bts);
|
||||
|
||||
/* The following definitions are for OM and NM packets that we cannot yet
|
||||
* generate by code but we just pass on */
|
||||
@@ -396,11 +395,9 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
case NM_OC_RADIO_CARRIER:
|
||||
trx = obj;
|
||||
if (new_state->operational == 1 &&
|
||||
new_state->availability == NM_AVSTATE_OK) {
|
||||
printf("STARTING NM Radio Carrier...\n");
|
||||
new_state->availability == NM_AVSTATE_OK)
|
||||
abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
|
||||
trx->nr, 0xff);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -418,7 +415,6 @@ static int sw_activ_rep(struct msgb *mb)
|
||||
|
||||
switch (foh->obj_class) {
|
||||
case NM_OC_BASEB_TRANSC:
|
||||
printf("Starting baseband\n");
|
||||
abis_nm_chg_adm_state(trx->bts, foh->obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff,
|
||||
NM_STATE_UNLOCKED);
|
||||
@@ -439,7 +435,6 @@ static int sw_activ_rep(struct msgb *mb)
|
||||
*/
|
||||
int rc_state = trx->rf_locked ?
|
||||
NM_STATE_LOCKED : NM_STATE_UNLOCKED;
|
||||
printf("Starting radio: %d %d\n", rc_state, trx->rf_locked);
|
||||
/* Patch ARFCN into radio attribute */
|
||||
nanobts_attr_radio[5] &= 0xf0;
|
||||
nanobts_attr_radio[5] |= trx->arfcn >> 8;
|
||||
@@ -461,7 +456,7 @@ static int sw_activ_rep(struct msgb *mb)
|
||||
static int oml_msg_nack(u_int8_t mt)
|
||||
{
|
||||
if (mt == NM_MT_SET_BTS_ATTR_NACK) {
|
||||
fprintf(stderr, "Failed to set BTS attributes. That is fatal. "
|
||||
LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. "
|
||||
"Was the bts type and frequency properly specified?\n");
|
||||
exit(-1);
|
||||
}
|
||||
@@ -564,7 +559,7 @@ static void nm_reconfig_trx(struct gsm_bts_trx *trx)
|
||||
trx->nominal_power = 23;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported nanoBTS GSM band %s\n",
|
||||
LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n",
|
||||
gsm_band_name(trx->bts->band));
|
||||
break;
|
||||
}
|
||||
@@ -583,6 +578,7 @@ static void nm_reconfig_bts(struct gsm_bts *bts)
|
||||
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
patch_nm_tables(bts);
|
||||
abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
|
||||
abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts));
|
||||
abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
|
||||
@@ -628,7 +624,7 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
|
||||
|
||||
static void bootstrap_om(struct gsm_bts *bts)
|
||||
{
|
||||
fprintf(stdout, "bootstrapping OML for BTS %u\n", bts->nr);
|
||||
LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr);
|
||||
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
@@ -638,13 +634,13 @@ static void bootstrap_om(struct gsm_bts *bts)
|
||||
bootstrap_om_nanobts(bts);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
|
||||
LOGP(DNM, LOGL_ERROR, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
|
||||
}
|
||||
}
|
||||
|
||||
static int shutdown_om(struct gsm_bts *bts)
|
||||
{
|
||||
fprintf(stdout, "shutting down OML for BTS %u\n", bts->nr);
|
||||
LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr);
|
||||
|
||||
/* stop sending event reports */
|
||||
abis_nm_event_reports(bts, 0);
|
||||
@@ -675,219 +671,55 @@ int bsc_shutdown_net(struct gsm_network *net)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bcch_info {
|
||||
u_int8_t type;
|
||||
u_int8_t len;
|
||||
const u_int8_t *data;
|
||||
};
|
||||
|
||||
/*
|
||||
SYSTEM INFORMATION TYPE 1
|
||||
Cell channel description
|
||||
Format-ID bit map 0
|
||||
CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
|
||||
RACH Control Parameters
|
||||
maximum 7 retransmissions
|
||||
8 slots used to spread transmission
|
||||
cell not barred for access
|
||||
call reestablishment not allowed
|
||||
Access Control Class = 0000
|
||||
*/
|
||||
static u_int8_t si1[] = {
|
||||
/* header */0x55, 0x06, 0x19,
|
||||
/* ccdesc */0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/,
|
||||
/* rach */0xD5, 0x04, 0x00,
|
||||
/* s1 reset*/0x2B
|
||||
};
|
||||
|
||||
/*
|
||||
SYSTEM INFORMATION TYPE 2
|
||||
Neighbour Cells Description
|
||||
EXT-IND: Carries the complete BA
|
||||
BA-IND = 0
|
||||
Format-ID bit map 0
|
||||
CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
NCC permitted (NCC) = FF
|
||||
RACH Control Parameters
|
||||
maximum 7 retransmissions
|
||||
8 slots used to spread transmission
|
||||
cell not barred for access
|
||||
call reestablishment not allowed
|
||||
Access Control Class = 0000
|
||||
*/
|
||||
static u_int8_t si2[] = {
|
||||
/* header */0x59, 0x06, 0x1A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* ncc */0xFF,
|
||||
/* rach*/0xD5, 0x04, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
SYSTEM INFORMATION TYPE 3
|
||||
Cell identity = 00001 (1h)
|
||||
Location area identification
|
||||
Mobile Country Code (MCC): 001
|
||||
Mobile Network Code (MNC): 01
|
||||
Location Area Code (LAC): 00001 (1h)
|
||||
Control Channel Description
|
||||
Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach
|
||||
0 blocks reserved for access grant
|
||||
1 channel used for CCCH, with SDCCH
|
||||
5 multiframes period for PAGING REQUEST
|
||||
Time-out T3212 = 0
|
||||
Cell Options BCCH
|
||||
Power control indicator: not set
|
||||
MSs shall not use uplink DTX
|
||||
Radio link timeout = 36
|
||||
Cell Selection Parameters
|
||||
Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
|
||||
max.TX power level MS may use for CCH = 2 <- according to GSM05.05 39dBm (max)
|
||||
Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
|
||||
Half rate support (NECI): New establishment causes are not supported
|
||||
min.RX signal level for MS = 0
|
||||
RACH Control Parameters
|
||||
maximum 7 retransmissions
|
||||
8 slots used to spread transmission
|
||||
cell not barred for access
|
||||
call reestablishment not allowed
|
||||
Access Control Class = 0000
|
||||
SI 3 Rest Octets (not present)
|
||||
*/
|
||||
static u_int8_t si3[] = {
|
||||
/* header */0x49, 0x06, 0x1B,
|
||||
/* cell */0x00, 0x01,
|
||||
/* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
|
||||
/* desc */0x01, 0x03, 0x00,
|
||||
/* option*/0x28,
|
||||
/* selection*/0x62, 0x00,
|
||||
/* rach */0xD5, 0x04, 0x00,
|
||||
/* rest */ 0x2B, 0x2B, 0x2B, 0x2B
|
||||
};
|
||||
|
||||
/*
|
||||
SYSTEM INFORMATION TYPE 4
|
||||
Location area identification
|
||||
Mobile Country Code (MCC): 001
|
||||
Mobile Network Code (MNC): 01
|
||||
Location Area Code (LAC): 00001 (1h)
|
||||
Cell Selection Parameters
|
||||
Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
|
||||
max.TX power level MS may use for CCH = 2
|
||||
Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
|
||||
Half rate support (NECI): New establishment causes are not supported
|
||||
min.RX signal level for MS = 0
|
||||
RACH Control Parameters
|
||||
maximum 7 retransmissions
|
||||
8 slots used to spread transmission
|
||||
cell not barred for access
|
||||
call reestablishment not allowed
|
||||
Access Control Class = 0000
|
||||
CBCH Channel Description
|
||||
Type = SDCCH/4[2]
|
||||
Timeslot Number: 0
|
||||
Training Sequence Code: 7h
|
||||
ARFCN: 1
|
||||
SI Rest Octets (not present)
|
||||
*/
|
||||
static u_int8_t si4[] = {
|
||||
/* header */0x41, 0x06, 0x1C,
|
||||
/* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
|
||||
/* sel */0x62, 0x00,
|
||||
/* rach*/0xD5, 0x04, 0x00,
|
||||
/* cbch chan desc */ 0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/,
|
||||
/* rest octets */ 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B
|
||||
};
|
||||
|
||||
/*
|
||||
SYSTEM INFORMATION TYPE 5
|
||||
Neighbour Cells Description
|
||||
EXT-IND: Carries the complete BA
|
||||
BA-IND = 0
|
||||
Format-ID bit map 0
|
||||
CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
*/
|
||||
|
||||
static u_int8_t si5[] = {
|
||||
/* header without l2 len*/0x06, 0x1D,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
// SYSTEM INFORMATION TYPE 6
|
||||
|
||||
/*
|
||||
SACCH FILLING
|
||||
System Info Type: SYSTEM INFORMATION 6
|
||||
L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF
|
||||
|
||||
SYSTEM INFORMATION TYPE 6
|
||||
Cell identity = 00001 (1h)
|
||||
Location area identification
|
||||
Mobile Country Code (MCC): 001
|
||||
Mobile Network Code (MNC): 01
|
||||
Location Area Code (LAC): 00001 (1h)
|
||||
Cell Options SACCH
|
||||
Power control indicator: not set
|
||||
MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H.
|
||||
Radio link timeout = 36
|
||||
NCC permitted (NCC) = FF
|
||||
*/
|
||||
|
||||
static u_int8_t si6[] = {
|
||||
/* header */0x06, 0x1E,
|
||||
/* cell id*/ 0x00, 0x01,
|
||||
/* lai */ 0x00, 0xF1, 0x10, 0x00, 0x01,
|
||||
/* options */ 0x28,
|
||||
/* ncc */ 0xFF,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const struct bcch_info bcch_infos[] = {
|
||||
{
|
||||
.type = RSL_SYSTEM_INFO_1,
|
||||
.len = sizeof(si1),
|
||||
.data = si1,
|
||||
}, {
|
||||
.type = RSL_SYSTEM_INFO_2,
|
||||
.len = sizeof(si2),
|
||||
.data = si2,
|
||||
}, {
|
||||
.type = RSL_SYSTEM_INFO_3,
|
||||
.len = sizeof(si3),
|
||||
.data = si3,
|
||||
}, {
|
||||
.type = RSL_SYSTEM_INFO_4,
|
||||
.len = sizeof(si4),
|
||||
.data = si4,
|
||||
},
|
||||
};
|
||||
|
||||
static_assert(sizeof(si1) == sizeof(struct gsm48_system_information_type_1), type1)
|
||||
static_assert(sizeof(si2) == sizeof(struct gsm48_system_information_type_2), type2)
|
||||
static_assert(sizeof(si3) == sizeof(struct gsm48_system_information_type_3), type3)
|
||||
static_assert(sizeof(si4) >= sizeof(struct gsm48_system_information_type_4), type4)
|
||||
static_assert(sizeof(si5) == sizeof(struct gsm48_system_information_type_5), type5)
|
||||
static_assert(sizeof(si6) >= sizeof(struct gsm48_system_information_type_6), type6)
|
||||
|
||||
/* set all system information types */
|
||||
static int set_system_infos(struct gsm_bts_trx *trx)
|
||||
{
|
||||
int i;
|
||||
int i, rc;
|
||||
u_int8_t si_tmp[23];
|
||||
struct gsm_bts *bts = trx->bts;
|
||||
|
||||
bts->si_common.cell_sel_par.ms_txpwr_max_ccch =
|
||||
ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
|
||||
bts->si_common.cell_sel_par.neci = bts->network->neci;
|
||||
|
||||
if (trx == trx->bts->c0) {
|
||||
for (i = 0; i < ARRAY_SIZE(bcch_infos); i++) {
|
||||
rsl_bcch_info(trx, bcch_infos[i].type,
|
||||
bcch_infos[i].data,
|
||||
bcch_infos[i].len);
|
||||
for (i = 1; i <= 4; i++) {
|
||||
rc = gsm_generate_si(si_tmp, trx->bts, i);
|
||||
if (rc < 0)
|
||||
goto err_out;
|
||||
DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
|
||||
rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
|
||||
}
|
||||
#ifdef GPRS
|
||||
i = 13
|
||||
rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
|
||||
if (rc < 0)
|
||||
goto err_out;
|
||||
DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
|
||||
rsl_bcch_info(trx, RSL_SYSTEM_INFO_13, si_tmp, rc);
|
||||
#endif
|
||||
}
|
||||
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si5, sizeof(si5));
|
||||
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si6, sizeof(si6));
|
||||
|
||||
i = 5;
|
||||
rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_5);
|
||||
if (rc < 0)
|
||||
goto err_out;
|
||||
DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
|
||||
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si_tmp, rc);
|
||||
|
||||
i = 6;
|
||||
rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_6);
|
||||
if (rc < 0)
|
||||
goto err_out;
|
||||
DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
|
||||
rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si_tmp, rc);
|
||||
|
||||
return 0;
|
||||
err_out:
|
||||
LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely "
|
||||
"a problem with neighbor cell list generation\n",
|
||||
i, trx->bts->nr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -921,81 +753,12 @@ static void patch_nm_tables(struct gsm_bts *bts)
|
||||
nanobts_attr_radio[1] = bts->c0->max_power_red / 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Patch the various SYSTEM INFORMATION tables to update
|
||||
* the LAI
|
||||
*/
|
||||
static void patch_si_tables(struct gsm_bts *bts)
|
||||
{
|
||||
u_int8_t arfcn_low = bts->c0->arfcn & 0xff;
|
||||
u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
|
||||
|
||||
/* covert the raw packet to the struct */
|
||||
struct gsm48_system_information_type_1 *type_1 =
|
||||
(struct gsm48_system_information_type_1*)&si1;
|
||||
struct gsm48_system_information_type_2 *type_2 =
|
||||
(struct gsm48_system_information_type_2*)&si2;
|
||||
struct gsm48_system_information_type_3 *type_3 =
|
||||
(struct gsm48_system_information_type_3*)&si3;
|
||||
struct gsm48_system_information_type_4 *type_4 =
|
||||
(struct gsm48_system_information_type_4*)&si4;
|
||||
struct gsm48_system_information_type_6 *type_6 =
|
||||
(struct gsm48_system_information_type_6*)&si6;
|
||||
struct gsm48_loc_area_id lai;
|
||||
|
||||
gsm0408_generate_lai(&lai, bts->network->country_code,
|
||||
bts->network->network_code,
|
||||
bts->location_area_code);
|
||||
|
||||
/* assign the MCC and MNC */
|
||||
type_3->lai = lai;
|
||||
type_4->lai = lai;
|
||||
type_6->lai = lai;
|
||||
|
||||
/* set the CI */
|
||||
type_3->cell_identity = htons(bts->cell_identity);
|
||||
type_6->cell_identity = htons(bts->cell_identity);
|
||||
|
||||
type_4->data[2] &= 0xf0;
|
||||
type_4->data[2] |= arfcn_high;
|
||||
type_4->data[3] = arfcn_low;
|
||||
|
||||
/* patch Control Channel Description 10.5.2.11 */
|
||||
type_3->control_channel_desc = bts->chan_desc;
|
||||
|
||||
/* patch TSC */
|
||||
si4[15] &= ~0xe0;
|
||||
si4[15] |= (bts->tsc & 7) << 5;
|
||||
|
||||
/* patch MS max power for CCH */
|
||||
type_4->cell_sel_par.ms_txpwr_max_ccch =
|
||||
ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
|
||||
|
||||
/* Set NECI to influence channel request */
|
||||
type_3->cell_sel_par.neci = bts->network->neci;
|
||||
type_4->cell_sel_par.neci = bts->network->neci;
|
||||
|
||||
if (bts->cell_barred) {
|
||||
type_1->rach_control.cell_bar = 1;
|
||||
type_2->rach_control.cell_bar = 1;
|
||||
type_3->rach_control.cell_bar = 1;
|
||||
type_4->rach_control.cell_bar = 1;
|
||||
} else {
|
||||
type_1->rach_control.cell_bar = 0;
|
||||
type_2->rach_control.cell_bar = 0;
|
||||
type_3->rach_control.cell_bar = 0;
|
||||
type_4->rach_control.cell_bar = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void bootstrap_rsl(struct gsm_bts_trx *trx)
|
||||
{
|
||||
fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) "
|
||||
LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) "
|
||||
"using MCC=%u MNC=%u BSIC=%u TSC=%u\n",
|
||||
trx->bts->nr, trx->nr, bsc_gsmnet->country_code,
|
||||
bsc_gsmnet->network_code, trx->bts->bsic, trx->bts->tsc);
|
||||
patch_si_tables(trx->bts);
|
||||
set_system_infos(trx);
|
||||
}
|
||||
|
||||
@@ -1015,7 +778,7 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
}
|
||||
break;
|
||||
case EVT_E1_TEI_DN:
|
||||
fprintf(stderr, "Lost some E1 TEI link\n");
|
||||
LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n");
|
||||
/* FIXME: deal with TEI or L1 link loss */
|
||||
break;
|
||||
default:
|
||||
@@ -1028,33 +791,55 @@ static int bootstrap_bts(struct gsm_bts *bts)
|
||||
switch (bts->band) {
|
||||
case GSM_BAND_1800:
|
||||
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) {
|
||||
fprintf(stderr, "GSM1800 channel must be between 512-885.\n");
|
||||
LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case GSM_BAND_1900:
|
||||
if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) {
|
||||
fprintf(stderr, "GSM1900 channel must be between 512-810.\n");
|
||||
LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case GSM_BAND_900:
|
||||
if (bts->c0->arfcn < 1 || bts->c0->arfcn > 124) {
|
||||
fprintf(stderr, "GSM900 channel must be between 1-124.\n");
|
||||
LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-124.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unsupported frequency band.\n");
|
||||
LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL &&
|
||||
!bts->si_common.rach_control.cell_bar)
|
||||
LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' "
|
||||
"network on a BTS that is not barred. This "
|
||||
"configuration is likely to interfere with production "
|
||||
"GSM networks and should only be used in a RF "
|
||||
"shielded environment such as a faraday cage!\n\n");
|
||||
|
||||
/* Control Channel Description */
|
||||
bts->chan_desc.att = 1;
|
||||
bts->chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
|
||||
bts->chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
|
||||
bts->si_common.chan_desc.att = 1;
|
||||
bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
|
||||
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
|
||||
/* T3212 is set from vty/config */
|
||||
|
||||
/* some defaults for our system information */
|
||||
bts->si_common.rach_control.re = 1; /* no re-establishment */
|
||||
bts->si_common.rach_control.tx_integer = 5; /* 8 slots spread */
|
||||
bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
|
||||
bts->si_common.rach_control.t2 = 4; /* no emergency calls */
|
||||
|
||||
bts->si_common.cell_options.radio_link_timeout = 2; /* 12 */
|
||||
bts->si_common.cell_options.dtx = 2; /* MS shall not use upplink DTX */
|
||||
bts->si_common.cell_options.pwrc = 0; /* PWRC not set */
|
||||
|
||||
bts->si_common.cell_sel_par.acs = 0;
|
||||
|
||||
bts->si_common.ncc_permitted = 0xff;
|
||||
|
||||
paging_init(bts);
|
||||
|
||||
return 0;
|
||||
@@ -1077,7 +862,7 @@ int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, int, void *),
|
||||
telnet_init(bsc_gsmnet, 4242);
|
||||
rc = vty_read_config_file(config_file);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
|
||||
LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@@ -65,6 +65,7 @@ static int audio_loop = 0;
|
||||
static int early_bind = 0;
|
||||
static int rtp_base_port = 0;
|
||||
|
||||
static char *forward_ip = NULL;
|
||||
static char *config_file = "mgcp.cfg";
|
||||
|
||||
/* used by msgb and mgcp */
|
||||
@@ -910,6 +911,8 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, " sdp audio payload name %s%s", audio_name, VTY_NEWLINE);
|
||||
vty_out(vty, " loop %u%s", !!audio_loop, VTY_NEWLINE);
|
||||
vty_out(vty, " endpoints %u%s", number_endpoints, VTY_NEWLINE);
|
||||
if (forward_ip)
|
||||
vty_out(vty, " forward audio %s%s", forward_ip, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1056,6 +1059,17 @@ DEFUN(cfg_mgcp_number_endp,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_forward,
|
||||
cfg_mgcp_forward_cmd,
|
||||
"forward audio IP",
|
||||
"Forward packets from and to the IP. This disables most of the MGCP feature.")
|
||||
{
|
||||
if (forward_ip)
|
||||
talloc_free(forward_ip);
|
||||
forward_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int bsc_vty_init(struct gsm_network *dummy)
|
||||
{
|
||||
cmd_init(1);
|
||||
@@ -1077,6 +1091,7 @@ int bsc_vty_init(struct gsm_network *dummy)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_loop_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_forward_cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1117,37 +1132,66 @@ int main(int argc, char** argv)
|
||||
endpoints[i].ci = CI_UNUSED;
|
||||
}
|
||||
|
||||
/* initialize the socket */
|
||||
bfd.when = BSC_FD_READ;
|
||||
bfd.cb = read_call_agent;
|
||||
bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (bfd.fd < 0) {
|
||||
perror("Gateway failed to listen");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* This application supports two modes.
|
||||
* 1.) a true MGCP gateway with support for AUEP, CRCX, MDCX, DLCX
|
||||
* 2.) plain forwarding of RTP packets on the endpoints.
|
||||
* both modes are mutual exclusive
|
||||
*/
|
||||
if (forward_ip) {
|
||||
|
||||
setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
if (!early_bind) {
|
||||
DEBUGP(DMGCP, "Forwarding requires early bind.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(source_port);
|
||||
inet_aton(source_addr, &addr.sin_addr);
|
||||
/*
|
||||
* Store the forward IP and assign a ci. For early bind
|
||||
* the sockets will be created after this.
|
||||
*/
|
||||
for (i = 1; i < number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &endpoints[i];
|
||||
inet_aton(forward_ip, &endp->remote);
|
||||
endp->ci = CI_UNUSED + 23;
|
||||
endp->rtp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port));
|
||||
endp->rtcp = htons(rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port) + 1);
|
||||
}
|
||||
|
||||
if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
DEBUGP(DMGCP, "Configured for Audio Forwarding.\n");
|
||||
} else {
|
||||
bfd.when = BSC_FD_READ;
|
||||
bfd.cb = read_call_agent;
|
||||
bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (bfd.fd < 0) {
|
||||
perror("Gateway failed to listen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(source_port);
|
||||
inet_aton(source_addr, &addr.sin_addr);
|
||||
|
||||
if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (bsc_register_fd(&bfd) != 0) {
|
||||
DEBUGP(DMGCP, "Failed to register the fd\n");
|
||||
return -1;
|
||||
if (bsc_register_fd(&bfd) != 0) {
|
||||
DEBUGP(DMGCP, "Failed to register the fd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUGP(DMGCP, "Configured for MGCP.\n");
|
||||
}
|
||||
|
||||
/* initialisation */
|
||||
|
@@ -459,15 +459,14 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
|
||||
/* we can ask it to connect now */
|
||||
if (lchan->msc_data) {
|
||||
DEBUGP(DMSC, "Connecting BTS to port: %d conn: %d\n",
|
||||
lchan->msc_data->rtp_port, ts->abis_ip.conn_id);
|
||||
lchan->msc_data->rtp_port, lchan->abis_ip.conn_id);
|
||||
|
||||
int rtp_payload = ts->trx->bts->network->rtp_payload;
|
||||
if (rtp_payload == 0)
|
||||
rtp_payload = ts->abis_ip.rtp_payload2;
|
||||
rtp_payload = lchan->abis_ip.rtp_payload2;
|
||||
|
||||
rc = rsl_ipacc_mdcx(lchan, ntohl(local_addr.s_addr),
|
||||
lchan->msc_data->rtp_port,
|
||||
ts->abis_ip.conn_id,
|
||||
rtp_payload);
|
||||
if (rc < 0) {
|
||||
DEBUGP(DMSC, "Failed to send connect: %d\n", rc);
|
||||
@@ -806,6 +805,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_select_main(0);
|
||||
|
@@ -63,6 +63,19 @@ static const struct tlv_definition bss_att_tlvdef = {
|
||||
},
|
||||
};
|
||||
|
||||
static u_int16_t get_network_code_for_msc(struct gsm_network *net)
|
||||
{
|
||||
if (net->core_network_code > 0)
|
||||
return net->core_network_code;
|
||||
return net->network_code;
|
||||
}
|
||||
|
||||
static u_int16_t get_country_code_for_msc(struct gsm_network *net)
|
||||
{
|
||||
if (net->core_country_code > 0)
|
||||
return net->core_country_code;
|
||||
return net->country_code;
|
||||
}
|
||||
|
||||
static int bssmap_paging_cb(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param)
|
||||
{
|
||||
@@ -552,12 +565,22 @@ int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length)
|
||||
memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header));
|
||||
|
||||
/*
|
||||
* patch LAI entries...
|
||||
* This is coming from the network. We need to regenerate the
|
||||
* LAI for the Location Update Accept packet and maybe more
|
||||
* as well.
|
||||
*/
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *)gsm48->l3h;
|
||||
if (gh->msg_type == GSM48_MT_MM_LOC_UPD_ACCEPT) {
|
||||
if (gh->data[2] == 0x80)
|
||||
gh->data[2] = 0x08;
|
||||
if (gsm48->trx->bts->network->core_network_code > 0 ||
|
||||
gsm48->trx->bts->network->core_country_code > 0) {
|
||||
if (msgb_l3len(gsm48) >= sizeof(struct gsm48_loc_area_id) + 1) {
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *)gsm48->l3h;
|
||||
if (gh->msg_type == GSM48_MT_MM_LOC_UPD_ACCEPT) {
|
||||
struct gsm_network *net = gsm48->trx->bts->network;
|
||||
struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) &gh->data[0];
|
||||
gsm0408_generate_lai(lai, net->country_code,
|
||||
net->network_code,
|
||||
gsm48->trx->bts->location_area_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bts_queue_send(gsm48, header->link_id);
|
||||
@@ -572,13 +595,14 @@ struct msgb *bssmap_create_layer3(struct msgb *msg_l3)
|
||||
struct msgb* msg;
|
||||
struct gsm48_loc_area_id *lai;
|
||||
struct gsm_bts *bts = msg_l3->lchan->ts->trx->bts;
|
||||
u_int16_t network_code = get_network_code_for_msc(bts->network);
|
||||
u_int16_t country_code = get_country_code_for_msc(bts->network);
|
||||
|
||||
msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
|
||||
"bssmap cmpl l3");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
|
||||
/* create the bssmap header */
|
||||
msg->l3h = msgb_put(msg, 2);
|
||||
msg->l3h[0] = 0x0;
|
||||
@@ -594,8 +618,8 @@ struct msgb *bssmap_create_layer3(struct msgb *msg_l3)
|
||||
data[2] = CELL_IDENT_WHOLE_GLOBAL;
|
||||
|
||||
lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
|
||||
gsm0408_generate_lai(lai, bts->network->country_code,
|
||||
/*bts->network->network_code - 1*/ 8, bts->location_area_code);
|
||||
gsm0408_generate_lai(lai, country_code,
|
||||
network_code, bts->location_area_code);
|
||||
|
||||
ci = (u_int16_t *) msgb_put(msg, 2);
|
||||
*ci = htons(bts->cell_identity);
|
||||
@@ -724,17 +748,19 @@ struct msgb *bssmap_create_sapi_reject(u_int8_t link_id)
|
||||
return msg;
|
||||
}
|
||||
|
||||
static u_int8_t chan_mode_to_speech(enum gsm48_chan_mode mode)
|
||||
static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan)
|
||||
{
|
||||
switch (mode) {
|
||||
int mode = 0;
|
||||
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
return 1;
|
||||
mode = 1;
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
return 0x11;
|
||||
mode = 0x11;
|
||||
break;
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
return 0x21;
|
||||
mode = 0x21;
|
||||
break;
|
||||
case GSM48_CMODE_SIGN:
|
||||
case GSM48_CMODE_DATA_14k5:
|
||||
@@ -746,6 +772,11 @@ static u_int8_t chan_mode_to_speech(enum gsm48_chan_mode mode)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lchan->type == GSM_LCHAN_TCH_H)
|
||||
mode |= 0x4;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/* 3.2.2.33 */
|
||||
@@ -830,7 +861,7 @@ struct msgb *bssmap_create_assignment_completed(struct gsm_lchan *lchan, u_int8_
|
||||
|
||||
/* write circuit pool 3.2.2.45 */
|
||||
/* write speech version chosen: 3.2.2.51 when BTS picked it */
|
||||
speech_mode = chan_mode_to_speech(lchan->tch_mode);
|
||||
speech_mode = chan_mode_to_speech(lchan);
|
||||
if (speech_mode != 0) {
|
||||
data = msgb_put(msg, 2);
|
||||
data[0] = GSM0808_IE_SPEECH_VERSION;
|
||||
|
@@ -201,9 +201,14 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
break;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H);
|
||||
/* If we don't have TCH/H available, fall-back to TCH/F */
|
||||
if (!lchan) {
|
||||
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
|
||||
type = GSM_LCHAN_TCH_F;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
|
||||
LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
|
||||
}
|
||||
|
||||
if (lchan) {
|
||||
@@ -227,6 +232,8 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
/* Free a logical channel */
|
||||
void lchan_free(struct gsm_lchan *lchan)
|
||||
{
|
||||
int i;
|
||||
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
if (lchan->subscr) {
|
||||
subscr_put(lchan->subscr);
|
||||
@@ -239,6 +246,16 @@ void lchan_free(struct gsm_lchan *lchan)
|
||||
lchan->use_count = 0;
|
||||
}
|
||||
|
||||
bsc_del_timer(&lchan->T3101);
|
||||
|
||||
/* clear cached measuement reports */
|
||||
lchan->meas_rep_idx = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) {
|
||||
lchan->meas_rep[i].flags = 0;
|
||||
lchan->meas_rep[i].nr = 0;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
|
||||
lchan->neigh_meas[i].arfcn = 0;
|
||||
/* FIXME: ts_free() the timeslot, if we're the last logical
|
||||
* channel using it */
|
||||
}
|
||||
@@ -259,11 +276,11 @@ int _lchan_release(struct gsm_lchan *lchan)
|
||||
}
|
||||
|
||||
/* spoofed? message */
|
||||
if (lchan->use_count < 0) {
|
||||
DEBUGP(DRLL, "BUG: channel count is negative: %d\n", lchan->use_count);
|
||||
}
|
||||
if (lchan->use_count < 0)
|
||||
LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
|
||||
lchan->use_count);
|
||||
|
||||
DEBUGP(DRLL, "Releasing the channel with: %d (%x)\n", lchan->nr, lchan->nr);
|
||||
DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
|
||||
rsl_release_request(lchan, 0);
|
||||
return 1;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -60,6 +60,7 @@ static const struct debug_info debug_info[] = {
|
||||
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
|
||||
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
|
||||
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
|
||||
DEBUG_CATEGORY(DHO, "DHO", "", "")
|
||||
};
|
||||
|
||||
static int use_color = 1;
|
||||
|
@@ -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;
|
||||
|
@@ -52,6 +52,7 @@
|
||||
#include <openbsc/trau_frame.h>
|
||||
#include <openbsc/trau_mux.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/misdn.h>
|
||||
|
||||
#define NUM_E1_TS 32
|
||||
@@ -234,7 +235,7 @@ int abis_rsl_sendmsg(struct msgb *msg)
|
||||
msg->l2h = msg->data;
|
||||
|
||||
if (!msg->trx || !msg->trx->rsl_link) {
|
||||
fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
|
||||
LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL\n");
|
||||
talloc_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -263,7 +264,7 @@ int _abis_nm_sendmsg(struct msgb *msg)
|
||||
msg->l2h = msg->data;
|
||||
|
||||
if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
|
||||
fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
|
||||
LOGP(DRSL, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -305,7 +306,7 @@ int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
subch_demux_init(&ts->trau.demux);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unsupported E1 timeslot type %u\n",
|
||||
LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n",
|
||||
ts->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -430,7 +431,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
|
||||
link = e1inp_lookup_sign_link(ts, tei, sapi);
|
||||
if (!link) {
|
||||
fprintf(stderr, "didn't find signalling link for "
|
||||
LOGP(DMI, LOGL_ERROR, "didn't find signalling link for "
|
||||
"tei %d, sapi %d\n", tei, sapi);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -445,7 +446,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
fprintf(stderr, "unknown link type %u\n", link->type);
|
||||
LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -454,7 +455,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
fprintf(stderr, "unknown TS type %u\n", ts->type);
|
||||
LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -491,7 +492,7 @@ struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
|
||||
msgb_put(msg, 40);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
|
||||
LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
|
||||
return NULL;
|
||||
}
|
||||
return msg;
|
||||
@@ -523,8 +524,24 @@ int e1inp_line_update(struct e1inp_line *line)
|
||||
return mi_e1_line_update(line);
|
||||
}
|
||||
|
||||
static int e1i_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
if (subsys != SS_GLOBAL ||
|
||||
signal != S_GLOBAL_SHUTDOWN)
|
||||
return 0;
|
||||
|
||||
if (pcap_fd) {
|
||||
close(pcap_fd);
|
||||
pcap_fd = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load_e1_inp(void)
|
||||
{
|
||||
tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1,
|
||||
"e1inp_sign_link");
|
||||
register_signal_handler(SS_GLOBAL, e1i_sig_cb, NULL);
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <openbsc/db.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/bitvec.h>
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
@@ -56,8 +57,6 @@
|
||||
|
||||
void *tall_locop_ctx;
|
||||
|
||||
extern int ipacc_rtp_direct;
|
||||
|
||||
static const struct tlv_definition rsl_att_tlvdef = {
|
||||
.def = {
|
||||
[GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
|
||||
@@ -166,63 +165,6 @@ static const char *rr_cause_name(u_int8_t cause)
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
|
||||
int len)
|
||||
{
|
||||
memset(rep, 0, sizeof(*rep));
|
||||
|
||||
if (data[0] & 0x80)
|
||||
rep->flags |= MEAS_REP_F_BA1;
|
||||
if (data[0] & 0x40)
|
||||
rep->flags |= MEAS_REP_F_DTX;
|
||||
if ((data[1] & 0x40) == 0x00)
|
||||
rep->flags |= MEAS_REP_F_VALID;
|
||||
|
||||
rep->rxlev_full = data[0] & 0x3f;
|
||||
rep->rxlev_sub = data[1] & 0x3f;
|
||||
rep->rxqual_full = (data[3] >> 4) & 0x7;
|
||||
rep->rxqual_sub = (data[3] >> 1) & 0x7;
|
||||
rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2);
|
||||
if (rep->num_cell < 1)
|
||||
return;
|
||||
|
||||
/* an encoding nightmare in perfection */
|
||||
|
||||
rep->cell[0].rxlev = data[4] & 0x3f;
|
||||
rep->cell[0].bcch_freq = data[5] >> 2;
|
||||
rep->cell[0].bsic = ((data[5] & 0x03) << 3) | (data[6] >> 5);
|
||||
if (rep->num_cell < 2)
|
||||
return;
|
||||
|
||||
rep->cell[1].rxlev = ((data[6] & 0x1f) << 1) | (data[7] >> 7);
|
||||
rep->cell[1].bcch_freq = (data[7] >> 2) & 0x1f;
|
||||
rep->cell[1].bsic = ((data[7] & 0x03) << 4) | (data[8] >> 4);
|
||||
if (rep->num_cell < 3)
|
||||
return;
|
||||
|
||||
rep->cell[2].rxlev = ((data[8] & 0x0f) << 2) | (data[9] >> 6);
|
||||
rep->cell[2].bcch_freq = (data[9] >> 1) & 0x1f;
|
||||
rep->cell[2].bsic = ((data[9] & 0x01) << 6) | (data[10] >> 3);
|
||||
if (rep->num_cell < 4)
|
||||
return;
|
||||
|
||||
rep->cell[3].rxlev = ((data[10] & 0x07) << 3) | (data[11] >> 5);
|
||||
rep->cell[3].bcch_freq = data[11] & 0x1f;
|
||||
rep->cell[3].bsic = data[12] >> 2;
|
||||
if (rep->num_cell < 5)
|
||||
return;
|
||||
|
||||
rep->cell[4].rxlev = ((data[12] & 0x03) << 4) | (data[13] >> 4);
|
||||
rep->cell[4].bcch_freq = ((data[13] & 0xf) << 1) | (data[14] >> 7);
|
||||
rep->cell[4].bsic = (data[14] >> 1) & 0x3f;
|
||||
if (rep->num_cell < 6)
|
||||
return;
|
||||
|
||||
rep->cell[5].rxlev = ((data[14] & 0x01) << 5) | (data[15] >> 3);
|
||||
rep->cell[5].bcch_freq = ((data[15] & 0x07) << 2) | (data[16] >> 6);
|
||||
rep->cell[5].bsic = data[16] & 0x3f;
|
||||
}
|
||||
|
||||
int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
|
||||
static int gsm48_tx_simple(struct gsm_lchan *lchan,
|
||||
u_int8_t pdisc, u_int8_t msg_type);
|
||||
@@ -234,12 +176,6 @@ struct gsm_lai {
|
||||
u_int16_t lac;
|
||||
};
|
||||
|
||||
static int reject_cause = 0;
|
||||
void gsm0408_set_reject_cause(int cause)
|
||||
{
|
||||
reject_cause = cause;
|
||||
}
|
||||
|
||||
static u_int32_t new_callref = 0x80000001;
|
||||
|
||||
static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
|
||||
@@ -297,6 +233,11 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
|
||||
|
||||
db_subscriber_alloc_tmsi(lchan->subscr);
|
||||
rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi);
|
||||
if (lchan->ts->trx->bts->network->send_mm_info) {
|
||||
/* send MM INFO with network name */
|
||||
rc = gsm48_tx_mm_info(msg->lchan);
|
||||
}
|
||||
|
||||
/* call subscr_update after putting the loc_upd_acc
|
||||
* in the transmit queue, since S_SUBSCR_ATTACHED might
|
||||
* trigger further action like SMS delivery */
|
||||
@@ -433,18 +374,29 @@ static int decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap,
|
||||
bcap->coding = (lv[1] & 0x10) >> 4;
|
||||
bcap->radio = (lv[1] & 0x60) >> 5;
|
||||
|
||||
i = 1;
|
||||
s = 0;
|
||||
while(!(lv[i] & 0x80)) {
|
||||
i++; /* octet 3a etc */
|
||||
if (in_len < i)
|
||||
return 0;
|
||||
bcap->speech_ver[s++] = lv[i] & 0x0f;
|
||||
bcap->speech_ver[s] = -1; /* end of list */
|
||||
if (i == 2) /* octet 3a */
|
||||
bcap->speech_ctm = (lv[i] & 0x20) >> 5;
|
||||
if (s == 7) /* maximum speech versions + end of list */
|
||||
return 0;
|
||||
if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
|
||||
i = 1;
|
||||
s = 0;
|
||||
while(!(lv[i] & 0x80)) {
|
||||
i++; /* octet 3a etc */
|
||||
if (in_len < i)
|
||||
return 0;
|
||||
bcap->speech_ver[s++] = lv[i] & 0x0f;
|
||||
bcap->speech_ver[s] = -1; /* end of list */
|
||||
if (i == 2) /* octet 3a */
|
||||
bcap->speech_ctm = (lv[i] & 0x20) >> 5;
|
||||
if (s == 7) /* maximum speech versions + end of list */
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
while (!(lv[i] & 0x80)) {
|
||||
i++; /* octet 3a etc */
|
||||
if (in_len < i)
|
||||
return 0;
|
||||
/* ignore them */
|
||||
}
|
||||
/* FIXME: implement OCTET 4+ parsing */
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -455,21 +407,24 @@ static int encode_bearer_cap(struct msgb *msg, int lv_only,
|
||||
const struct gsm_mncc_bearer_cap *bcap)
|
||||
{
|
||||
u_int8_t lv[32 + 1];
|
||||
int i, s;
|
||||
int i = 1, s;
|
||||
|
||||
lv[1] = bcap->transfer;
|
||||
lv[1] |= bcap->mode << 3;
|
||||
lv[1] |= bcap->coding << 4;
|
||||
lv[1] |= bcap->radio << 5;
|
||||
|
||||
i = 1;
|
||||
for (s = 0; bcap->speech_ver[s] >= 0; s++) {
|
||||
i++; /* octet 3a etc */
|
||||
lv[i] = bcap->speech_ver[s];
|
||||
if (i == 2) /* octet 3a */
|
||||
lv[i] |= bcap->speech_ctm << 5;
|
||||
if (bcap->transfer == GSM_MNCC_BCAP_SPEECH) {
|
||||
for (s = 0; bcap->speech_ver[s] >= 0; s++) {
|
||||
i++; /* octet 3a etc */
|
||||
lv[i] = bcap->speech_ver[s];
|
||||
if (i == 2) /* octet 3a */
|
||||
lv[i] |= bcap->speech_ctm << 5;
|
||||
}
|
||||
lv[i] |= 0x80; /* last IE of octet 3 etc */
|
||||
} else {
|
||||
/* FIXME: implement OCTET 4+ encoding */
|
||||
}
|
||||
lv[i] |= 0x80; /* last IE of octet 3 etc */
|
||||
|
||||
lv[0] = i;
|
||||
if (lv_only)
|
||||
@@ -861,6 +816,7 @@ static int encode_more(struct msgb *msg)
|
||||
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
|
||||
int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
|
||||
{
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
@@ -872,6 +828,8 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
|
||||
gh->data[0] = cause;
|
||||
|
||||
DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
|
||||
|
||||
bts->network->stats.loc_upd_resp.reject++;
|
||||
|
||||
return gsm48_sendmsg(msg, NULL);
|
||||
}
|
||||
@@ -884,7 +842,6 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm48_loc_area_id *lai;
|
||||
u_int8_t *mid;
|
||||
int ret;
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
@@ -901,12 +858,9 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
|
||||
|
||||
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
|
||||
|
||||
ret = gsm48_sendmsg(msg, NULL);
|
||||
bts->network->stats.loc_upd_resp.accept++;
|
||||
|
||||
/* send MM INFO with network name */
|
||||
ret = gsm48_tx_mm_info(lchan);
|
||||
|
||||
return ret;
|
||||
return gsm48_sendmsg(msg, NULL);
|
||||
}
|
||||
|
||||
/* Transmit Chapter 9.2.10 Identity Request */
|
||||
@@ -940,6 +894,8 @@ static int mm_rx_id_resp(struct msgb *msg)
|
||||
DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
|
||||
mi_type, mi_string);
|
||||
|
||||
dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_IMSI:
|
||||
/* look up subscriber based on IMSI, create if not found */
|
||||
@@ -971,8 +927,9 @@ static int mm_rx_id_resp(struct msgb *msg)
|
||||
static void loc_upd_rej_cb(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
|
||||
gsm0408_loc_upd_rej(lchan, reject_cause);
|
||||
gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
|
||||
release_loc_updating_req(lchan);
|
||||
}
|
||||
|
||||
@@ -1018,6 +975,20 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
DEBUGPC(DMM, "mi_type=0x%02x MI(%s) type=%s ", mi_type, mi_string,
|
||||
lupd_name(lu->type));
|
||||
|
||||
dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len);
|
||||
|
||||
switch (lu->type) {
|
||||
case GSM48_LUPD_NORMAL:
|
||||
bts->network->stats.loc_upd_type.normal++;
|
||||
break;
|
||||
case GSM48_LUPD_IMSI_ATT:
|
||||
bts->network->stats.loc_upd_type.attach++;
|
||||
break;
|
||||
case GSM48_LUPD_PERIODIC:
|
||||
bts->network->stats.loc_upd_type.periodic++;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pseudo Spoof detection: Just drop a second/concurrent
|
||||
* location updating request.
|
||||
@@ -1301,6 +1272,8 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
|
||||
DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n",
|
||||
req->cm_service_type, mi_type, mi_string);
|
||||
|
||||
dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len));
|
||||
|
||||
if (is_siemens_bts(bts))
|
||||
send_siemens_mrpci(msg->lchan, classmark2-1);
|
||||
|
||||
@@ -1314,7 +1287,9 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
|
||||
|
||||
if (!msg->lchan->subscr)
|
||||
msg->lchan->subscr = subscr;
|
||||
else if (msg->lchan->subscr != subscr) {
|
||||
else if (msg->lchan->subscr == subscr)
|
||||
subscr_put(subscr); /* lchan already has a ref, don't need another one */
|
||||
else {
|
||||
DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
|
||||
subscr_put(subscr);
|
||||
}
|
||||
@@ -1340,6 +1315,8 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
||||
DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
|
||||
mi_type, mi_string);
|
||||
|
||||
bts->network->stats.loc_upd_type.detach++;
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_TMSI:
|
||||
subscr = subscr_get_by_tmsi(bts->network,
|
||||
@@ -1371,6 +1348,10 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
||||
} else
|
||||
DEBUGP(DMM, "Unknown Subscriber ?!?\n");
|
||||
|
||||
/* FIXME: iterate over all transactions and release them,
|
||||
* imagine an IMSI DETACH happening during an active call! */
|
||||
|
||||
/* subscriber is detached: should we release lchan? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1527,26 +1508,13 @@ static int gsm48_rx_rr_status(struct msgb *msg)
|
||||
|
||||
static int gsm48_rx_rr_meas_rep(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
|
||||
static struct gsm_meas_rep meas_rep;
|
||||
struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan);
|
||||
|
||||
DEBUGP(DMEAS, "MEASUREMENT REPORT ");
|
||||
parse_meas_rep(&meas_rep, gh->data, payload_len);
|
||||
if (meas_rep.flags & MEAS_REP_F_DTX)
|
||||
DEBUGPC(DMEAS, "DTX ");
|
||||
if (meas_rep.flags & MEAS_REP_F_BA1)
|
||||
DEBUGPC(DMEAS, "BA1 ");
|
||||
if (!(meas_rep.flags & MEAS_REP_F_VALID))
|
||||
DEBUGPC(DMEAS, "NOT VALID ");
|
||||
else
|
||||
DEBUGPC(DMEAS, "FULL(lev=%u, qual=%u) SUB(lev=%u, qual=%u) ",
|
||||
meas_rep.rxlev_full, meas_rep.rxqual_full, meas_rep.rxlev_sub,
|
||||
meas_rep.rxqual_sub);
|
||||
|
||||
DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", meas_rep.num_cell);
|
||||
|
||||
/* FIXME: put the results somwhere */
|
||||
/* This shouldn't actually end up here, as RSL treats
|
||||
* L3 Info of 08.58 MEASUREMENT REPORT different by calling
|
||||
* directly into gsm48_parse_meas_rep */
|
||||
DEBUGP(DMEAS, "DIRECT GSM48 MEASUREMENT REPORT ?!? ");
|
||||
gsm48_parse_meas_rep(meas_rep, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1568,6 +1536,33 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
|
||||
return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
|
||||
}
|
||||
|
||||
/* Chapter 9.1.16 Handover complete */
|
||||
static int gsm48_rx_rr_ho_compl(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
|
||||
DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n",
|
||||
rr_cause_name(gh->data[0]));
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, msg->lchan);
|
||||
/* FIXME: release old channel */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Chapter 9.1.17 Handover Failure */
|
||||
static int gsm48_rx_rr_ho_fail(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
|
||||
DEBUGP(DRR, "HANDOVER FAILED cause = %s\n",
|
||||
rr_cause_name(gh->data[0]));
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, msg->lchan);
|
||||
/* FIXME: release allocated new channel */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive a GSM 04.08 Radio Resource (RR) message */
|
||||
static int gsm0408_rcv_rr(struct msgb *msg)
|
||||
@@ -1601,6 +1596,12 @@ static int gsm0408_rcv_rr(struct msgb *msg)
|
||||
DEBUGP(DRR, "CIPHERING MODE COMPLETE\n");
|
||||
/* FIXME: check for MI (if any) */
|
||||
break;
|
||||
case GSM48_MT_RR_HANDO_COMPL:
|
||||
rc = gsm48_rx_rr_ho_compl(msg);
|
||||
break;
|
||||
case GSM48_MT_RR_HANDO_FAIL:
|
||||
rc = gsm48_rx_rr_ho_fail(msg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
|
||||
gh->msg_type);
|
||||
@@ -1811,13 +1812,16 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable);
|
||||
|
||||
/* some other part of the code sends us a signal */
|
||||
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct gsm_lchan *lchan = signal_data;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
int rc;
|
||||
struct gsm_network *net;
|
||||
struct gsm_trans *trans;
|
||||
|
||||
if (subsys != SS_ABISIP)
|
||||
return 0;
|
||||
@@ -1826,56 +1830,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 */
|
||||
@@ -1883,7 +1852,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",
|
||||
@@ -1894,33 +1862,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:
|
||||
@@ -1928,8 +1894,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1951,45 +1916,61 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
|
||||
return tch_map(trans1->lchan, trans2->lchan);
|
||||
}
|
||||
|
||||
/* enable receive of channels to upqueue */
|
||||
static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
|
||||
/* enable receive of channels to MNCC upqueue */
|
||||
static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable)
|
||||
{
|
||||
struct gsm_trans *trans;
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_bts *bts;
|
||||
int rc;
|
||||
|
||||
/* Find callref */
|
||||
trans = trans_find_by_callref(net, data->callref);
|
||||
trans = trans_find_by_callref(net, callref);
|
||||
if (!trans)
|
||||
return -EIO;
|
||||
if (!trans->lchan)
|
||||
return 0;
|
||||
lchan = trans->lchan;
|
||||
bts = lchan->ts->trx->bts;
|
||||
|
||||
// todo IPACCESS
|
||||
if (enable)
|
||||
return trau_recv_lchan(trans->lchan, data->callref);
|
||||
return trau_mux_unmap(NULL, data->callref);
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
// todo IPACCESS
|
||||
return trau_send_lchan(trans->lchan,
|
||||
(struct decoded_trau_frame *)frame->data);
|
||||
}
|
||||
|
||||
|
||||
static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
|
||||
{
|
||||
DEBUGP(DCC, "-> STATUS ENQ\n");
|
||||
@@ -2710,6 +2691,8 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
|
||||
case GSM_CSTATE_RELEASE_REQ:
|
||||
rc = mncc_recvmsg(trans->subscr->net, trans,
|
||||
MNCC_REL_CNF, &rel);
|
||||
/* FIXME: in case of multiple calls, we can't simply
|
||||
* hang up here ! */
|
||||
break;
|
||||
default:
|
||||
rc = mncc_recvmsg(trans->subscr->net, trans,
|
||||
@@ -3216,11 +3199,30 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
|
||||
case MNCC_BRIDGE:
|
||||
return tch_bridge(net, arg);
|
||||
case MNCC_FRAME_DROP:
|
||||
return tch_recv(net, arg, 0);
|
||||
return tch_recv_mncc(net, data->callref, 0);
|
||||
case MNCC_FRAME_RECV:
|
||||
return tch_recv(net, arg, 1);
|
||||
case GSM_TRAU_FRAME:
|
||||
return tch_frame(net, arg);
|
||||
return tch_recv_mncc(net, data->callref, 1);
|
||||
case GSM_TCHF_FRAME:
|
||||
/* Find callref */
|
||||
trans = trans_find_by_callref(net, data->callref);
|
||||
if (!trans)
|
||||
return -EIO;
|
||||
if (!trans->lchan)
|
||||
return 0;
|
||||
if (trans->lchan->type != GSM_LCHAN_TCH_F)
|
||||
return 0;
|
||||
bts = trans->lchan->ts->trx->bts;
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_NANOBTS:
|
||||
if (!trans->lchan->abis_ip.rtp_socket)
|
||||
return 0;
|
||||
return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg);
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
return trau_send_frame(trans->lchan, arg);
|
||||
default:
|
||||
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&rel, 0, sizeof(struct gsm_mncc));
|
||||
|
@@ -258,6 +258,11 @@ static const struct chreq chreq_type_neci1[] = {
|
||||
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 },
|
||||
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
|
||||
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
|
||||
{ 0x67, 0xff, CHREQ_T_LMU },
|
||||
{ 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
|
||||
{ 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
|
||||
{ 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
|
||||
{ 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
|
||||
};
|
||||
|
||||
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
|
||||
@@ -270,6 +275,11 @@ static const struct chreq chreq_type_neci0[] = {
|
||||
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 },
|
||||
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
|
||||
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
|
||||
{ 0x67, 0xff, CHREQ_T_LMU },
|
||||
{ 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH },
|
||||
{ 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH },
|
||||
{ 0x63, 0xff, CHREQ_T_RESERVED_SDCCH },
|
||||
{ 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE },
|
||||
};
|
||||
|
||||
static const enum gsm_chan_t ctype_by_chreq[] = {
|
||||
@@ -286,6 +296,9 @@ static const enum gsm_chan_t ctype_by_chreq[] = {
|
||||
[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_LMU] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
static const enum gsm_chreq_reason_t reason_by_chreq[] = {
|
||||
@@ -295,13 +308,16 @@ 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,
|
||||
[CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER,
|
||||
};
|
||||
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
|
||||
@@ -472,6 +488,8 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
|
||||
sig_data.bts = msg->lchan->ts->trx->bts;
|
||||
sig_data.lchan = msg->lchan;
|
||||
|
||||
bts->network->stats.paging.completed++;
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
|
||||
|
||||
/* Stop paging on the bts we received the paging response */
|
||||
@@ -503,6 +521,51 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
|
||||
return rsl_encryption_cmd(msg);
|
||||
}
|
||||
|
||||
static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
|
||||
const struct gsm_bts *bts)
|
||||
{
|
||||
cd->ncc = (bts->bsic >> 3 & 0x7);
|
||||
cd->bcc = (bts->bsic & 0x7);
|
||||
cd->arfcn_hi = bts->c0->arfcn >> 8;
|
||||
cd->arfcn_lo = bts->c0->arfcn & 0xff;
|
||||
}
|
||||
|
||||
static void gsm48_chan_desc(struct gsm48_chan_desc *cd,
|
||||
const struct gsm_lchan *lchan)
|
||||
{
|
||||
u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
|
||||
|
||||
cd->chan_nr = lchan2chan_nr(lchan);
|
||||
cd->h0.tsc = lchan->ts->trx->bts->tsc;
|
||||
cd->h0.h = 0;
|
||||
cd->h0.arfcn_high = arfcn >> 8;
|
||||
cd->h0.arfcn_low = arfcn & 0xff;
|
||||
}
|
||||
|
||||
/* Chapter 9.1.15: Handover Command */
|
||||
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
|
||||
u_int8_t power_command, u_int8_t ho_ref)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
struct gsm48_ho_cmd *ho =
|
||||
(struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho));
|
||||
|
||||
msg->lchan = old_lchan;
|
||||
gh->proto_discr = GSM48_PDISC_RR;
|
||||
gh->msg_type = GSM48_MT_RR_HANDO_CMD;
|
||||
|
||||
/* mandatory bits */
|
||||
gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
|
||||
gsm48_chan_desc(&ho->chan_desc, new_lchan);
|
||||
ho->ho_ref = ho_ref;
|
||||
ho->power_command = power_command;
|
||||
|
||||
/* FIXME: optional bits for type of synchronization? */
|
||||
|
||||
return gsm48_sendmsg(msg, NULL);
|
||||
}
|
||||
|
||||
/* Chapter 9.1.2: Assignment Command */
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
{
|
||||
@@ -510,7 +573,6 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
struct gsm48_ass_cmd *ass =
|
||||
(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
|
||||
u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
|
||||
|
||||
DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
|
||||
|
||||
@@ -526,11 +588,7 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
* the chan_desc. But as long as multi-slot configurations
|
||||
* are not used we seem to be fine.
|
||||
*/
|
||||
ass->chan_desc.chan_nr = lchan2chan_nr(lchan);
|
||||
ass->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
|
||||
ass->chan_desc.h0.h = 0;
|
||||
ass->chan_desc.h0.arfcn_high = arfcn >> 8;
|
||||
ass->chan_desc.h0.arfcn_low = arfcn & 0xff;
|
||||
gsm48_chan_desc(&ass->chan_desc, lchan);
|
||||
ass->power_command = power_command;
|
||||
|
||||
/* in case of multi rate we need to attach a config */
|
||||
@@ -642,3 +700,72 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
|
||||
rsl_ipacc_crcx(msg->lchan);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
|
||||
u_int8_t *data = gh->data;
|
||||
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
|
||||
struct bitvec *nbv = &bts->si_common.neigh_list;
|
||||
|
||||
if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
|
||||
return -EINVAL;
|
||||
|
||||
if (data[0] & 0x80)
|
||||
rep->flags |= MEAS_REP_F_BA1;
|
||||
if (data[0] & 0x40)
|
||||
rep->flags |= MEAS_REP_F_UL_DTX;
|
||||
if ((data[1] & 0x40) == 0x00)
|
||||
rep->flags |= MEAS_REP_F_DL_VALID;
|
||||
|
||||
rep->dl.full.rx_lev = data[0] & 0x3f;
|
||||
rep->dl.sub.rx_lev = data[1] & 0x3f;
|
||||
rep->dl.full.rx_qual = (data[3] >> 4) & 0x7;
|
||||
rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7;
|
||||
|
||||
rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2);
|
||||
if (rep->num_cell < 1 || rep->num_cell > 6)
|
||||
return 0;
|
||||
|
||||
/* an encoding nightmare in perfection */
|
||||
|
||||
rep->cell[0].rxlev = data[3] & 0x3f;
|
||||
rep->cell[0].arfcn = bitvec_get_nth_set_bit(nbv, data[4] >> 2);
|
||||
rep->cell[0].bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
|
||||
if (rep->num_cell < 2)
|
||||
return 0;
|
||||
|
||||
rep->cell[1].rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
|
||||
rep->cell[1].arfcn = bitvec_get_nth_set_bit(nbv, (data[6] >> 2) & 0x1f);
|
||||
rep->cell[1].bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
|
||||
if (rep->num_cell < 3)
|
||||
return 0;
|
||||
|
||||
rep->cell[2].rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
|
||||
rep->cell[2].arfcn = bitvec_get_nth_set_bit(nbv, (data[8] >> 1) & 0x1f);
|
||||
rep->cell[2].bsic = ((data[8] & 0x01) << 6) | (data[9] >> 3);
|
||||
if (rep->num_cell < 4)
|
||||
return 0;
|
||||
|
||||
rep->cell[3].rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
|
||||
rep->cell[3].arfcn = bitvec_get_nth_set_bit(nbv, data[10] & 0x1f);
|
||||
rep->cell[3].bsic = data[11] >> 2;
|
||||
if (rep->num_cell < 5)
|
||||
return 0;
|
||||
|
||||
rep->cell[4].rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
|
||||
rep->cell[4].arfcn = bitvec_get_nth_set_bit(nbv,
|
||||
((data[12] & 0xf) << 1) | (data[13] >> 7));
|
||||
rep->cell[4].bsic = (data[13] >> 1) & 0x3f;
|
||||
if (rep->num_cell < 6)
|
||||
return 0;
|
||||
|
||||
rep->cell[5].rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
|
||||
rep->cell[5].arfcn = bitvec_get_nth_set_bit(nbv,
|
||||
((data[14] & 0x07) << 2) | (data[15] >> 6));
|
||||
rep->cell[5].bsic = data[15] & 0x3f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -173,7 +173,7 @@ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
|
||||
DEBUGP(DSMS, "TX: CP-ACK ");
|
||||
break;
|
||||
case GSM411_MT_CP_ERROR:
|
||||
DEBUGP(DSMS, "TX: CP-ACK ");
|
||||
DEBUGP(DSMS, "TX: CP-ERROR ");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -198,48 +198,171 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
|
||||
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
|
||||
}
|
||||
|
||||
static time_t gsm340_scts(u_int8_t *scts);
|
||||
/* Turn int into semi-octet representation: 98 => 0x89 */
|
||||
static u_int8_t bcdify(u_int8_t value)
|
||||
{
|
||||
u_int8_t ret;
|
||||
|
||||
static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
|
||||
ret = value / 10;
|
||||
ret |= (value % 10) << 4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Turn semi-octet representation into int: 0x89 => 98 */
|
||||
static u_int8_t unbcdify(u_int8_t value)
|
||||
{
|
||||
u_int8_t ret;
|
||||
|
||||
if ((value & 0x0F) > 9 || (value >> 4) > 9)
|
||||
DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
|
||||
|
||||
ret = (value&0x0F)*10;
|
||||
ret += value>>4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Generate 03.40 TP-SCTS */
|
||||
static void gsm340_gen_scts(u_int8_t *scts, time_t time)
|
||||
{
|
||||
struct tm *tm = localtime(&time);
|
||||
|
||||
*scts++ = bcdify(tm->tm_year % 100);
|
||||
*scts++ = bcdify(tm->tm_mon + 1);
|
||||
*scts++ = bcdify(tm->tm_mday);
|
||||
*scts++ = bcdify(tm->tm_hour);
|
||||
*scts++ = bcdify(tm->tm_min);
|
||||
*scts++ = bcdify(tm->tm_sec);
|
||||
*scts++ = bcdify(tm->tm_gmtoff/(60*15));
|
||||
}
|
||||
|
||||
/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
|
||||
static time_t gsm340_scts(u_int8_t *scts)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
u_int8_t yr = unbcdify(*scts++);
|
||||
|
||||
if (yr <= 80)
|
||||
tm.tm_year = 100 + yr;
|
||||
else
|
||||
tm.tm_year = yr;
|
||||
tm.tm_mon = unbcdify(*scts++) - 1;
|
||||
tm.tm_mday = unbcdify(*scts++);
|
||||
tm.tm_hour = unbcdify(*scts++);
|
||||
tm.tm_min = unbcdify(*scts++);
|
||||
tm.tm_sec = unbcdify(*scts++);
|
||||
/* according to gsm 03.40 time zone is
|
||||
"expressed in quarters of an hour" */
|
||||
tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
|
||||
|
||||
return mktime(&tm);
|
||||
}
|
||||
|
||||
/* Return the default validity period in minutes */
|
||||
static unsigned long gsm340_vp_default(void)
|
||||
{
|
||||
unsigned long minutes;
|
||||
/* Default validity: two days */
|
||||
minutes = 24 * 60 * 2;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'relative' */
|
||||
static unsigned long gsm340_vp_relative(u_int8_t *sms_vp)
|
||||
{
|
||||
/* Chapter 9.2.3.12.1 */
|
||||
u_int8_t vp;
|
||||
unsigned long minutes;
|
||||
|
||||
vp = *(sms_vp);
|
||||
if (vp <= 143)
|
||||
minutes = vp + 1 * 5;
|
||||
else if (vp <= 167)
|
||||
minutes = 12*60 + (vp-143) * 30;
|
||||
else if (vp <= 196)
|
||||
minutes = vp-166 * 60 * 24;
|
||||
else
|
||||
minutes = vp-192 * 60 * 24 * 7;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'absolute' */
|
||||
static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp)
|
||||
{
|
||||
/* Chapter 9.2.3.12.2 */
|
||||
time_t expires, now;
|
||||
unsigned long minutes;
|
||||
|
||||
expires = gsm340_scts(sms_vp);
|
||||
now = mktime(gmtime(NULL));
|
||||
if (expires <= now)
|
||||
minutes = 0;
|
||||
else
|
||||
minutes = (expires-now)/60;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'relative in integer representation' */
|
||||
static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp)
|
||||
{
|
||||
u_int8_t vp;
|
||||
unsigned long minutes;
|
||||
time_t expires;
|
||||
time_t now;
|
||||
vp = *(sms_vp);
|
||||
if (vp == 0) {
|
||||
DEBUGP(DSMS, "reserved relative_integer validity period\n");
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
minutes = vp/60;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'relative in semi-octet representation' */
|
||||
static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp)
|
||||
{
|
||||
unsigned long minutes;
|
||||
minutes = unbcdify(*sms_vp++)*60; /* hours */
|
||||
minutes += unbcdify(*sms_vp++); /* minutes */
|
||||
minutes += unbcdify(*sms_vp++)/60; /* seconds */
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* decode validity period. return minutes */
|
||||
static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
|
||||
{
|
||||
u_int8_t fi; /* functionality indicator */
|
||||
|
||||
switch (sms_vpf) {
|
||||
case GSM340_TP_VPF_RELATIVE:
|
||||
/* Chapter 9.2.3.12.1 */
|
||||
vp = *(sms_vp);
|
||||
if (vp <= 143)
|
||||
minutes = vp + 1 * 5;
|
||||
else if (vp <= 167)
|
||||
minutes = 12*60 + (vp-143) * 30;
|
||||
else if (vp <= 196)
|
||||
minutes = vp-166 * 60 * 24;
|
||||
else
|
||||
minutes = vp-192 * 60 * 24 * 7;
|
||||
break;
|
||||
return gsm340_vp_relative(sms_vp);
|
||||
case GSM340_TP_VPF_ABSOLUTE:
|
||||
/* Chapter 9.2.3.12.2 */
|
||||
expires = gsm340_scts(sms_vp);
|
||||
now = mktime(gmtime(NULL));
|
||||
if (expires <= now)
|
||||
minutes = 0;
|
||||
else
|
||||
minutes = (expires-now)/60;
|
||||
break;
|
||||
return gsm340_vp_absolute(sms_vp);
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
/* Chapter 9.2.3.12.3 */
|
||||
/* FIXME: implementation */
|
||||
DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
|
||||
break;
|
||||
fi = *sms_vp++;
|
||||
/* ignore additional fi */
|
||||
if (fi & (1<<7)) sms_vp++;
|
||||
/* read validity period format */
|
||||
switch (fi & 0b111) {
|
||||
case 0b000:
|
||||
return gsm340_vp_default(); /* no vpf specified */
|
||||
case 0b001:
|
||||
return gsm340_vp_relative(sms_vp);
|
||||
case 0b010:
|
||||
return gsm340_vp_relative_integer(sms_vp);
|
||||
case 0b011:
|
||||
return gsm340_vp_relative_semioctet(sms_vp);
|
||||
default:
|
||||
/* The GSM spec says that the SC should reject any
|
||||
unsupported and/or undefined values. FIXME */
|
||||
DEBUGP(DSMS, "Reserved enhanced validity period format\n");
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
case GSM340_TP_VPF_NONE:
|
||||
/* Default validity: two days */
|
||||
minutes = 24 * 60 * 2;
|
||||
break;
|
||||
default:
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
|
||||
@@ -307,69 +430,6 @@ static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
|
||||
return len_in_bytes;
|
||||
}
|
||||
|
||||
/* Turn int into semi-octet representation: 98 => 0x89 */
|
||||
static u_int8_t bcdify(u_int8_t value)
|
||||
{
|
||||
u_int8_t ret;
|
||||
|
||||
ret = value / 10;
|
||||
ret |= (value % 10) << 4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Turn semi-octet representation into int: 0x89 => 98 */
|
||||
static u_int8_t unbcdify(u_int8_t value)
|
||||
{
|
||||
u_int8_t ret;
|
||||
|
||||
if ((value & 0x0F) > 9 || (value >> 4) > 9)
|
||||
DEBUGP(DSMS, "unbcdify got too big nibble: 0x%02X\n", value);
|
||||
|
||||
ret = (value&0x0F)*10;
|
||||
if (ret > 90)
|
||||
ret += value>>4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Generate 03.40 TP-SCTS */
|
||||
static void gsm340_gen_scts(u_int8_t *scts, time_t time)
|
||||
{
|
||||
struct tm *tm = localtime(&time);
|
||||
|
||||
*scts++ = bcdify(tm->tm_year % 100);
|
||||
*scts++ = bcdify(tm->tm_mon + 1);
|
||||
*scts++ = bcdify(tm->tm_mday);
|
||||
*scts++ = bcdify(tm->tm_hour);
|
||||
*scts++ = bcdify(tm->tm_min);
|
||||
*scts++ = bcdify(tm->tm_sec);
|
||||
*scts++ = bcdify(tm->tm_gmtoff/(60*15));
|
||||
}
|
||||
|
||||
/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
|
||||
static time_t gsm340_scts(u_int8_t *scts)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
u_int8_t yr = unbcdify(*scts++);
|
||||
|
||||
if (yr <= 80)
|
||||
tm.tm_year = 100 + yr;
|
||||
else
|
||||
tm.tm_year = yr;
|
||||
tm.tm_mon = unbcdify(*scts++) - 1;
|
||||
tm.tm_mday = unbcdify(*scts++);
|
||||
tm.tm_hour = unbcdify(*scts++);
|
||||
tm.tm_min = unbcdify(*scts++);
|
||||
tm.tm_sec = unbcdify(*scts++);
|
||||
/* according to gsm 03.40 time zone is
|
||||
"expressed in quarters of an hour" */
|
||||
tm.tm_gmtoff = unbcdify(*scts++) * 15*60;
|
||||
|
||||
return mktime(&tm);
|
||||
}
|
||||
|
||||
/* generate a msgb containing a TPDU derived from struct gsm_sms,
|
||||
* returns total size of TPDU */
|
||||
static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
|
||||
@@ -457,6 +517,8 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
|
||||
int rc = 0;
|
||||
|
||||
bts->network->stats.sms.submitted++;
|
||||
|
||||
gsms = sms_alloc();
|
||||
if (!gsms)
|
||||
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
|
||||
@@ -499,6 +561,9 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
case GSM340_TP_VPF_ABSOLUTE:
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
sms_vp = smsp;
|
||||
/* the additional functionality indicator... */
|
||||
if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7))
|
||||
smsp++;
|
||||
smsp += 7;
|
||||
break;
|
||||
case GSM340_TP_VPF_NONE:
|
||||
@@ -542,6 +607,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
|
||||
if (!gsms->receiver) {
|
||||
rc = 1; /* cause 1: unknown subscriber */
|
||||
bts->network->stats.sms.no_receiver++;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -728,7 +794,9 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
||||
* to store this in our database and wati for a SMMA message */
|
||||
/* FIXME */
|
||||
dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
|
||||
}
|
||||
trans->lchan->ts->trx->bts->network->stats.sms.rp_err_mem++;
|
||||
} else
|
||||
trans->lchan->ts->trx->bts->network->stats.sms.rp_err_other++;
|
||||
|
||||
sms_free(sms);
|
||||
trans->sms.sms = NULL;
|
||||
@@ -844,11 +912,11 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
|
||||
return -EIO;
|
||||
/* FIXME: send some error message */
|
||||
|
||||
DEBUGP(DSMS, "trans_id=%x ", gh->proto_discr >> 4);
|
||||
DEBUGP(DSMS, "trans_id=%x ", transaction_id);
|
||||
trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
|
||||
transaction_id);
|
||||
if (!trans) {
|
||||
DEBUGPC(DSMS, "(unknown) ");
|
||||
DEBUGPC(DSMS, "(new) ");
|
||||
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
|
||||
transaction_id, new_callref++);
|
||||
if (!trans) {
|
||||
@@ -1001,6 +1069,8 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
||||
|
||||
DEBUGP(DSMS, "TX: SMS DELIVER\n");
|
||||
|
||||
lchan->ts->trx->bts->network->stats.sms.delivered++;
|
||||
|
||||
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
|
||||
/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
|
||||
}
|
||||
|
@@ -167,6 +167,14 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
|
||||
bts->num_trx = 0;
|
||||
INIT_LLIST_HEAD(&bts->trx_list);
|
||||
bts->ms_max_power = 15; /* dBm */
|
||||
bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */
|
||||
bts->si_common.cell_sel_par.rxlev_acc_min = 0;
|
||||
bts->si_common.neigh_list.data = bts->si_common.data.neigh_list;
|
||||
bts->si_common.neigh_list.data_len =
|
||||
sizeof(bts->si_common.data.neigh_list);
|
||||
bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc;
|
||||
bts->si_common.cell_alloc.data_len =
|
||||
sizeof(bts->si_common.data.cell_alloc);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
|
||||
bts->gprs.nsvc[i].bts = bts;
|
||||
@@ -198,6 +206,17 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
|
||||
net->country_code = country_code;
|
||||
net->network_code = network_code;
|
||||
net->num_bts = 0;
|
||||
net->T3101 = GSM_T3101_DEFAULT;
|
||||
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);
|
||||
@@ -205,6 +224,9 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
|
||||
|
||||
net->mncc_recv = mncc_recv;
|
||||
|
||||
net->core_country_code = -1;
|
||||
net->core_network_code = -1;
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
@@ -223,6 +245,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;
|
||||
@@ -362,3 +403,39 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
|
||||
return gsm_auth_policy_names[policy];
|
||||
}
|
||||
|
||||
static const char *rrlp_mode_names[] = {
|
||||
[RRLP_MODE_NONE] = "none",
|
||||
[RRLP_MODE_MS_BASED] = "ms-based",
|
||||
[RRLP_MODE_MS_PREF] = "ms-preferred",
|
||||
[RRLP_MODE_ASS_PREF] = "ass-preferred",
|
||||
};
|
||||
|
||||
enum rrlp_mode rrlp_mode_parse(const char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(rrlp_mode_names); i++) {
|
||||
if (!strcmp(arg, rrlp_mode_names[i]))
|
||||
return i;
|
||||
}
|
||||
return RRLP_MODE_NONE;
|
||||
}
|
||||
|
||||
const char *rrlp_mode_name(enum rrlp_mode mode)
|
||||
{
|
||||
if (mode > ARRAY_SIZE(rrlp_mode_names))
|
||||
return "none";
|
||||
return rrlp_mode_names[mode];
|
||||
}
|
||||
|
||||
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_meas_rep *meas_rep;
|
||||
|
||||
meas_rep = &lchan->meas_rep[lchan->meas_rep_idx];
|
||||
memset(meas_rep, 0, sizeof(*meas_rep));
|
||||
meas_rep->lchan = lchan;
|
||||
lchan->meas_rep_idx = (lchan->meas_rep_idx + 1)
|
||||
% ARRAY_SIZE(lchan->meas_rep);
|
||||
|
||||
return meas_rep;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
298
openbsc/src/handover_decision.c
Normal file
298
openbsc/src/handover_decision.c
Normal file
@@ -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);
|
||||
}
|
367
openbsc/src/handover_logic.c
Normal file
367
openbsc/src/handover_logic.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not
|
||||
* actually implement the handover algorithm/decision, but executes a
|
||||
* handover decision */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_utils.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/transaction.h>
|
||||
#include <openbsc/rtp_proxy.h>
|
||||
|
||||
struct bsc_handover {
|
||||
struct llist_head list;
|
||||
|
||||
struct gsm_lchan *old_lchan;
|
||||
struct gsm_lchan *new_lchan;
|
||||
|
||||
struct timer_list T3103;
|
||||
|
||||
u_int8_t ho_ref;
|
||||
};
|
||||
|
||||
static LLIST_HEAD(bsc_handovers);
|
||||
|
||||
static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
|
||||
llist_for_each_entry(ho, &bsc_handovers, list) {
|
||||
if (ho->new_lchan == new_lchan)
|
||||
return ho;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
|
||||
llist_for_each_entry(ho, &bsc_handovers, list) {
|
||||
if (ho->old_lchan == old_lchan)
|
||||
return ho;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Hand over the specified logical channel to the specified new BTS.
|
||||
* This is the main entry point for the actual handover algorithm,
|
||||
* after it has decided it wants to initiate HO to a specific BTS */
|
||||
int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_lchan *new_lchan;
|
||||
struct bsc_handover *ho;
|
||||
static u_int8_t ho_ref;
|
||||
int rc;
|
||||
|
||||
/* don't attempt multiple handovers for the same lchan at
|
||||
* the same time */
|
||||
if (bsc_ho_by_old_lchan(old_lchan))
|
||||
return -EBUSY;
|
||||
|
||||
DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n",
|
||||
old_lchan->ts->trx->bts->nr, bts->nr);
|
||||
|
||||
bts->network->stats.handover.attempted++;
|
||||
|
||||
new_lchan = lchan_alloc(bts, old_lchan->type);
|
||||
if (!new_lchan) {
|
||||
LOGP(DHO, LOGL_NOTICE, "No free channel\n");
|
||||
bts->network->stats.handover.no_channel++;
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
ho = talloc_zero(NULL, struct bsc_handover);
|
||||
if (!ho) {
|
||||
LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
|
||||
lchan_free(new_lchan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ho->old_lchan = old_lchan;
|
||||
ho->new_lchan = new_lchan;
|
||||
ho->ho_ref = ho_ref++;
|
||||
|
||||
/* copy some parameters from old lchan */
|
||||
memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
|
||||
new_lchan->ms_power = old_lchan->ms_power;
|
||||
new_lchan->bs_power = old_lchan->bs_power;
|
||||
new_lchan->rsl_cmode = old_lchan->rsl_cmode;
|
||||
new_lchan->tch_mode = old_lchan->tch_mode;
|
||||
new_lchan->subscr = subscr_get(old_lchan->subscr);
|
||||
|
||||
/* FIXME: do we have a better idea of the timing advance? */
|
||||
rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
|
||||
ho->ho_ref);
|
||||
if (rc < 0) {
|
||||
LOGP(DHO, LOGL_ERROR, "could not activate channel\n");
|
||||
talloc_free(ho);
|
||||
lchan_free(new_lchan);
|
||||
return rc;
|
||||
}
|
||||
|
||||
llist_add(&ho->list, &bsc_handovers);
|
||||
/* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
|
||||
static void ho_T3103_cb(void *_ho)
|
||||
{
|
||||
struct bsc_handover *ho = _ho;
|
||||
|
||||
DEBUGP(DHO, "HO T3103 expired\n");
|
||||
ho->new_lchan->ts->trx->bts->network->stats.handover.timeout++;
|
||||
|
||||
lchan_free(ho->new_lchan);
|
||||
llist_del(&ho->list);
|
||||
talloc_free(ho);
|
||||
}
|
||||
|
||||
/* RSL has acknowledged activation of the new lchan */
|
||||
static int ho_chan_activ_ack(struct gsm_lchan *new_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
int rc;
|
||||
|
||||
/* we need to check if this channel activation is related to
|
||||
* a handover at all (and if, which particular handover) */
|
||||
ho = bsc_ho_by_new_lchan(new_lchan);
|
||||
if (!ho)
|
||||
return -ENODEV;
|
||||
|
||||
DEBUGP(DHO, "handover activate ack, send HO Command\n");
|
||||
|
||||
/* we can now send the 04.08 HANDOVER COMMAND to the MS
|
||||
* using the old lchan */
|
||||
|
||||
rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref);
|
||||
|
||||
/* start T3103. We can continue either with T3103 expiration,
|
||||
* 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */
|
||||
ho->T3103.cb = ho_T3103_cb;
|
||||
ho->T3103.data = ho;
|
||||
bsc_schedule_timer(&ho->T3103, 10, 0);
|
||||
|
||||
/* create a RTP connection */
|
||||
if (is_ipaccess_bts(new_lchan->ts->trx->bts))
|
||||
rsl_ipacc_crcx(new_lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RSL has not acknowledged activation of the new lchan */
|
||||
static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
|
||||
ho = bsc_ho_by_new_lchan(new_lchan);
|
||||
if (!ho) {
|
||||
LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
llist_del(&ho->list);
|
||||
talloc_free(ho);
|
||||
|
||||
/* FIXME: maybe we should try to allocate a new LCHAN here? */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */
|
||||
static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
|
||||
ho = bsc_ho_by_new_lchan(new_lchan);
|
||||
if (!ho) {
|
||||
LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
new_lchan->ts->trx->bts->network->stats.handover.completed++;
|
||||
|
||||
bsc_del_timer(&ho->T3103);
|
||||
|
||||
/* update lchan pointer of transaction */
|
||||
trans_lchan_change(ho->old_lchan, new_lchan);
|
||||
|
||||
ho->old_lchan->state = LCHAN_S_INACTIVE;
|
||||
|
||||
/* do something to re-route the actual speech frames ! */
|
||||
|
||||
llist_del(&ho->list);
|
||||
talloc_free(ho);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GSM 04.08 HANDOVER FAIL has been received */
|
||||
static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
|
||||
ho = bsc_ho_by_old_lchan(old_lchan);
|
||||
if (!ho) {
|
||||
LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
old_lchan->ts->trx->bts->network->stats.handover.failed++;
|
||||
|
||||
bsc_del_timer(&ho->T3103);
|
||||
llist_del(&ho->list);
|
||||
put_lchan(ho->new_lchan);
|
||||
talloc_free(ho);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GSM 08.58 HANDOVER DETECT has been received */
|
||||
static int ho_rsl_detect(struct gsm_lchan *new_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
|
||||
ho = bsc_ho_by_new_lchan(new_lchan);
|
||||
if (!ho) {
|
||||
LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* FIXME: do we actually want to do something here ? */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ho_ipac_crcx_ack(struct gsm_lchan *new_lchan)
|
||||
{
|
||||
struct bsc_handover *ho;
|
||||
struct rtp_socket *old_rs, *new_rs, *other_rs;
|
||||
|
||||
ho = bsc_ho_by_new_lchan(new_lchan);
|
||||
if (!ho) {
|
||||
LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (ipacc_rtp_direct) {
|
||||
LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RTP Proxy mode */
|
||||
new_rs = new_lchan->abis_ip.rtp_socket;
|
||||
old_rs = ho->old_lchan->abis_ip.rtp_socket;
|
||||
|
||||
if (!new_rs) {
|
||||
LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rsl_ipacc_mdcx_to_rtpsock(new_lchan);
|
||||
|
||||
if (!old_rs) {
|
||||
LOGP(DHO, LOGL_ERROR, "no RTP socekt for old_lchan\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* copy rx_action and reference to other sock */
|
||||
new_rs->rx_action = old_rs->rx_action;
|
||||
new_rs->tx_action = old_rs->tx_action;
|
||||
new_rs->transmit = old_rs->transmit;
|
||||
|
||||
switch (ho->old_lchan->abis_ip.rtp_socket->rx_action) {
|
||||
case RTP_PROXY:
|
||||
other_rs = old_rs->proxy.other_sock;
|
||||
rtp_socket_proxy(new_rs, other_rs);
|
||||
/* delete reference to other end socket to prevent
|
||||
* rtp_socket_free() from removing the inverse reference */
|
||||
old_rs->proxy.other_sock = NULL;
|
||||
break;
|
||||
case RTP_RECV_UPSTREAM:
|
||||
new_rs->receive = old_rs->receive;
|
||||
break;
|
||||
case RTP_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
switch (subsys) {
|
||||
case SS_LCHAN:
|
||||
lchan = signal_data;
|
||||
switch (signal) {
|
||||
case S_LCHAN_ACTIVATE_ACK:
|
||||
return ho_chan_activ_ack(lchan);
|
||||
case S_LCHAN_ACTIVATE_NACK:
|
||||
return ho_chan_activ_nack(lchan);
|
||||
case S_LCHAN_HANDOVER_DETECT:
|
||||
return ho_rsl_detect(lchan);
|
||||
case S_LCHAN_HANDOVER_COMPL:
|
||||
return ho_gsm48_ho_compl(lchan);
|
||||
case S_LCHAN_HANDOVER_FAIL:
|
||||
return ho_gsm48_ho_fail(lchan);
|
||||
}
|
||||
break;
|
||||
case SS_ABISIP:
|
||||
lchan = signal_data;
|
||||
switch (signal) {
|
||||
case S_ABISIP_CRCX_ACK:
|
||||
return ho_ipac_crcx_ack(lchan);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load_ho_logic(void)
|
||||
{
|
||||
register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL);
|
||||
register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL);
|
||||
}
|
@@ -238,6 +238,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
|
||||
trx->rsl_tei, 0);
|
||||
/* get rid of our old temporary bfd */
|
||||
memcpy(newbfd, bfd, sizeof(*newbfd));
|
||||
newbfd->priv_nr = 2+trx_id;
|
||||
bsc_unregister_fd(bfd);
|
||||
bsc_register_fd(newbfd);
|
||||
talloc_free(bfd);
|
||||
@@ -247,9 +248,8 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: this is per BTS */
|
||||
static int oml_up = 0;
|
||||
static int rsl_up = 0;
|
||||
#define OML_UP 0x0001
|
||||
#define RSL_UP 0x0002
|
||||
|
||||
/*
|
||||
* read one ipa message from the socket
|
||||
@@ -348,16 +348,16 @@ static int handle_ts1_read(struct bsc_fd *bfd)
|
||||
|
||||
switch (link->type) {
|
||||
case E1INP_SIGN_RSL:
|
||||
if (!rsl_up) {
|
||||
if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) {
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
|
||||
rsl_up = 1;
|
||||
msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr);
|
||||
}
|
||||
ret = abis_rsl_rcvmsg(msg);
|
||||
break;
|
||||
case E1INP_SIGN_OML:
|
||||
if (!oml_up) {
|
||||
if (!(msg->trx->bts->ip_access.flags & OML_UP)) {
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_UP, link->tei, link->sapi);
|
||||
oml_up = 1;
|
||||
msg->trx->bts->ip_access.flags |= OML_UP;
|
||||
}
|
||||
ret = abis_nm_rcvmsg(msg);
|
||||
break;
|
||||
|
114
openbsc/src/meas_rep.c
Normal file
114
openbsc/src/meas_rep.c
Normal file
@@ -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;
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -11,6 +11,8 @@ network
|
||||
mobile network code 1
|
||||
short name OpenBSC
|
||||
long name OpenBSC
|
||||
timer t3101 10
|
||||
timer t3113 60
|
||||
bts 0
|
||||
type bs11
|
||||
band GSM900
|
||||
@@ -29,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
|
||||
|
@@ -11,6 +11,8 @@ network
|
||||
mobile network code 1
|
||||
short name OpenBSC
|
||||
long name OpenBSC
|
||||
timer t3101 10
|
||||
timer t3113 60
|
||||
bts 0
|
||||
type bs11
|
||||
band GSM900
|
||||
|
@@ -11,6 +11,8 @@ network
|
||||
mobile network code 1
|
||||
short name OpenBSC
|
||||
long name OpenBSC
|
||||
timer t3101 10
|
||||
timer t3113 60
|
||||
bts 0
|
||||
type bs11
|
||||
band GSM900
|
||||
@@ -29,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
|
||||
|
@@ -11,6 +11,8 @@ network
|
||||
mobile network code 1
|
||||
short name OpenBSC
|
||||
long name OpenBSC
|
||||
timer t3101 10
|
||||
timer t3113 60
|
||||
bts 0
|
||||
type nanobts
|
||||
ip.access unit_id 1801 0
|
||||
|
@@ -55,7 +55,7 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *
|
||||
int blocks;
|
||||
unsigned int group;
|
||||
|
||||
ccch_conf = bts->chan_desc.ccch_conf;
|
||||
ccch_conf = bts->si_common.chan_desc.ccch_conf;
|
||||
bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf);
|
||||
/* code word + 2, as 2 channels equals 0x0 */
|
||||
blocks = rsl_number_of_paging_subchannels(bts);
|
||||
@@ -212,6 +212,8 @@ static void paging_T3113_expired(void *data)
|
||||
cbfn = req->cbfn;
|
||||
paging_remove_request(&req->bts->paging, req);
|
||||
|
||||
req->bts->network->stats.paging.expired++;
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
|
||||
if (cbfn)
|
||||
cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
|
||||
@@ -239,7 +241,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
req->cbfn_param = data;
|
||||
req->T3113.cb = paging_T3113_expired;
|
||||
req->T3113.data = req;
|
||||
bsc_schedule_timer(&req->T3113, T3113_VALUE);
|
||||
bsc_schedule_timer(&req->T3113, bts->network->T3113, 0);
|
||||
llist_add_tail(&req->entry, &bts_entry->pending_requests);
|
||||
|
||||
if (!bsc_timer_pending(&bts_entry->work_timer))
|
||||
@@ -254,6 +256,8 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
|
||||
struct gsm_bts *bts = NULL;
|
||||
int num_pages = 0;
|
||||
|
||||
network->stats.paging.attempted++;
|
||||
|
||||
/* start paging subscriber on all BTS within Location Area */
|
||||
do {
|
||||
int rc;
|
||||
@@ -269,6 +273,9 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
|
||||
return rc;
|
||||
} while (1);
|
||||
|
||||
if (num_pages == 0)
|
||||
network->stats.paging.detached++;
|
||||
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
|
394
openbsc/src/rest_octets.c
Normal file
394
openbsc/src/rest_octets.c
Normal file
@@ -0,0 +1,394 @@
|
||||
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface,
|
||||
* rest octet handling according to
|
||||
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/bitvec.h>
|
||||
#include <openbsc/rest_octets.h>
|
||||
|
||||
/* generate SI1 rest octets */
|
||||
int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos)
|
||||
{
|
||||
struct bitvec bv;
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 2;
|
||||
|
||||
if (nch_pos) {
|
||||
bitvec_set_bit(&bv, H);
|
||||
bitvec_set_uint(&bv, *nch_pos, 5);
|
||||
} else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
bitvec_spare_padding(&bv, 15);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Append selection parameters to bitvec */
|
||||
static void append_selection_params(struct bitvec *bv,
|
||||
const struct gsm48_si_selection_params *sp)
|
||||
{
|
||||
if (sp->present) {
|
||||
bitvec_set_bit(bv, H);
|
||||
bitvec_set_bit(bv, sp->cbq);
|
||||
bitvec_set_uint(bv, sp->cell_resel_off, 6);
|
||||
bitvec_set_uint(bv, sp->temp_offs, 3);
|
||||
bitvec_set_uint(bv, sp->penalty_time, 5);
|
||||
} else
|
||||
bitvec_set_bit(bv, L);
|
||||
}
|
||||
|
||||
/* Append power offset to bitvec */
|
||||
static void append_power_offset(struct bitvec *bv,
|
||||
const struct gsm48_si_power_offset *po)
|
||||
{
|
||||
if (po->present) {
|
||||
bitvec_set_bit(bv, H);
|
||||
bitvec_set_uint(bv, po->power_offset, 2);
|
||||
} else
|
||||
bitvec_set_bit(bv, L);
|
||||
}
|
||||
|
||||
/* Append GPRS indicator to bitvec */
|
||||
static void append_gprs_ind(struct bitvec *bv,
|
||||
const struct gsm48_si3_gprs_ind *gi)
|
||||
{
|
||||
if (gi->present) {
|
||||
bitvec_set_bit(bv, H);
|
||||
bitvec_set_uint(bv, gi->ra_colour, 3);
|
||||
/* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */
|
||||
bitvec_set_bit(bv, gi->si13_position);
|
||||
} else
|
||||
bitvec_set_bit(bv, L);
|
||||
}
|
||||
|
||||
|
||||
/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
|
||||
int rest_octets_si3(u_int8_t *data, const struct gsm48_si_ro_info *si3)
|
||||
{
|
||||
struct bitvec bv;
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 5;
|
||||
|
||||
/* Optional Selection Parameters */
|
||||
append_selection_params(&bv, &si3->selection_params);
|
||||
|
||||
/* Optional Power Offset */
|
||||
append_power_offset(&bv, &si3->power_offset);
|
||||
|
||||
/* Do we have a SI2ter on the BCCH? */
|
||||
if (si3->si2ter_indicator)
|
||||
bitvec_set_bit(&bv, H);
|
||||
else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
/* Early Classmark Sending Control */
|
||||
if (si3->early_cm_ctrl)
|
||||
bitvec_set_bit(&bv, H);
|
||||
else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
/* Do we have a SI Type 9 on the BCCH? */
|
||||
if (si3->scheduling.present) {
|
||||
bitvec_set_bit(&bv, H);
|
||||
bitvec_set_uint(&bv, si3->scheduling.where, 3);
|
||||
} else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
/* GPRS Indicator */
|
||||
append_gprs_ind(&bv, &si3->gprs_ind);
|
||||
|
||||
return bitvec_spare_padding(&bv, (bv.data_len*8)-1);
|
||||
}
|
||||
|
||||
static int append_lsa_params(struct bitvec *bv,
|
||||
const struct gsm48_lsa_params *lsa_params)
|
||||
{
|
||||
/* FIXME */
|
||||
}
|
||||
|
||||
/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
|
||||
int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4)
|
||||
{
|
||||
struct bitvec bv;
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 11; /* FIXME: up to ? */
|
||||
|
||||
/* SI4 Rest Octets O */
|
||||
append_selection_params(&bv, &si4->selection_params);
|
||||
append_power_offset(&bv, &si4->power_offset);
|
||||
append_gprs_ind(&bv, &si4->gprs_ind);
|
||||
|
||||
if (0 /* FIXME */) {
|
||||
/* H and SI4 Rest Octets S */
|
||||
bitvec_set_bit(&bv, H);
|
||||
|
||||
/* LSA Parameters */
|
||||
if (si4->lsa_params.present) {
|
||||
bitvec_set_bit(&bv, H);
|
||||
append_lsa_params(&bv, &si4->lsa_params);
|
||||
} else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
/* Cell Identity */
|
||||
if (1) {
|
||||
bitvec_set_bit(&bv, H);
|
||||
bitvec_set_uint(&bv, si4->cell_id, 16);
|
||||
} else
|
||||
bitvec_set_bit(&bv, L);
|
||||
|
||||
/* LSA ID Information */
|
||||
if (0) {
|
||||
bitvec_set_bit(&bv, H);
|
||||
/* FIXME */
|
||||
} else
|
||||
bitvec_set_bit(&bv, L);
|
||||
} else {
|
||||
/* L and break indicator */
|
||||
bitvec_set_bit(&bv, L);
|
||||
bitvec_set_bit(&bv, si4->break_ind ? H : L);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a:
|
||||
< GPRS Mobile Allocation IE > ::=
|
||||
< HSN : bit (6) >
|
||||
{ 0 | 1 < RFL number list : < RFL number list struct > > }
|
||||
{ 0 < MA_LENGTH : bit (6) >
|
||||
< MA_BITMAP: bit (val(MA_LENGTH) + 1) >
|
||||
| 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ;
|
||||
|
||||
< RFL number list struct > :: =
|
||||
< RFL_NUMBER : bit (4) >
|
||||
{ 0 | 1 < RFL number list struct > } ;
|
||||
< ARFCN index list struct > ::=
|
||||
< ARFCN_INDEX : bit(6) >
|
||||
{ 0 | 1 < ARFCN index list struct > } ;
|
||||
*/
|
||||
static int append_gprs_mobile_alloc(struct bitvec *bv)
|
||||
{
|
||||
/* Hopping Sequence Number */
|
||||
bitvec_set_uint(bv, 0, 6);
|
||||
|
||||
if (0) {
|
||||
/* We want to use a RFL number list */
|
||||
bitvec_set_bit(bv, 1);
|
||||
/* FIXME: RFL number list */
|
||||
} else
|
||||
bitvec_set_bit(bv, 0);
|
||||
|
||||
if (0) {
|
||||
/* We want to use a MA_BITMAP */
|
||||
bitvec_set_bit(bv, 0);
|
||||
/* FIXME: MA_LENGTH, MA_BITMAP, ... */
|
||||
} else {
|
||||
bitvec_set_bit(bv, 1);
|
||||
if (0) {
|
||||
/* We want to provide an ARFCN index list */
|
||||
bitvec_set_bit(bv, 1);
|
||||
/* FIXME */
|
||||
} else
|
||||
bitvec_set_bit(bv, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encode_t3192(unsigned int t3192)
|
||||
{
|
||||
if (t3192 == 0)
|
||||
return 3;
|
||||
else if (t3192 <= 80)
|
||||
return 4;
|
||||
else if (t3192 <= 120)
|
||||
return 5;
|
||||
else if (t3192 <= 160)
|
||||
return 6;
|
||||
else if (t3192 <= 200)
|
||||
return 7;
|
||||
else if (t3192 <= 500)
|
||||
return 0;
|
||||
else if (t3192 <= 1000)
|
||||
return 1;
|
||||
else if (t3192 <= 1500)
|
||||
return 2;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int encode_drx_timer(unsigned int drx)
|
||||
{
|
||||
if (drx == 0)
|
||||
return 0;
|
||||
else if (drx == 1)
|
||||
return 1;
|
||||
else if (drx == 2)
|
||||
return 2;
|
||||
else if (drx <= 4)
|
||||
return 3;
|
||||
else if (drx <= 8)
|
||||
return 4;
|
||||
else if (drx <= 16)
|
||||
return 5;
|
||||
else if (drx <= 32)
|
||||
return 6;
|
||||
else if (drx <= 64)
|
||||
return 7;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* GPRS Cell Options as per TS 04.60 Chapter 12.24
|
||||
< GPRS Cell Options IE > ::=
|
||||
< NMO : bit(2) >
|
||||
< T3168 : bit(3) >
|
||||
< T3192 : bit(3) >
|
||||
< DRX_TIMER_MAX: bit(3) >
|
||||
< ACCESS_BURST_TYPE: bit >
|
||||
< CONTROL_ACK_TYPE : bit >
|
||||
< BS_CV_MAX: bit(4) >
|
||||
{ 0 | 1 < PAN_DEC : bit(3) >
|
||||
< PAN_INC : bit(3) >
|
||||
< PAN_MAX : bit(3) >
|
||||
{ 0 | 1 < Extension Length : bit(6) >
|
||||
< bit (val(Extension Length) + 1
|
||||
& { < Extension Information > ! { bit ** = <no string> } } ;
|
||||
< Extension Information > ::=
|
||||
{ 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit >
|
||||
< BEP_PERIOD : bit(4) > }
|
||||
< PFC_FEATURE_MODE : bit >
|
||||
< DTM_SUPPORT : bit >
|
||||
<BSS_PAGING_COORDINATION: bit >
|
||||
<spare bit > ** ;
|
||||
*/
|
||||
static int append_gprs_cell_opt(struct bitvec *bv,
|
||||
const struct gprs_cell_options *gco)
|
||||
{
|
||||
int t3192, drx_timer_max;
|
||||
|
||||
t3192 = encode_t3192(gco->t3192);
|
||||
if (t3192 < 0)
|
||||
return t3192;
|
||||
|
||||
drx_timer_max = encode_drx_timer(gco->drx_timer_max);
|
||||
if (drx_timer_max < 0)
|
||||
return drx_timer_max;
|
||||
|
||||
bitvec_set_uint(bv, gco->nmo, 2);
|
||||
bitvec_set_uint(bv, gco->t3168 / 500, 3);
|
||||
bitvec_set_uint(bv, t3192, 3);
|
||||
bitvec_set_uint(bv, drx_timer_max, 3);
|
||||
/* ACCESS_BURST_TYPE: Hard-code 8bit */
|
||||
bitvec_set_bit(bv, 0);
|
||||
/* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */
|
||||
bitvec_set_bit(bv, 1);
|
||||
bitvec_set_uint(bv, gco->bs_cv_max, 4);
|
||||
|
||||
/* hard-code no PAN_{DEC,INC,MAX} */
|
||||
bitvec_set_bit(bv, 0);
|
||||
|
||||
/* no extension information (EDGE) */
|
||||
bitvec_set_bit(bv, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
|
||||
const struct gprs_power_ctrl_pars *pcp)
|
||||
{
|
||||
bitvec_set_uint(bv, pcp->alpha, 4);
|
||||
bitvec_set_uint(bv, pcp->t_avg_w, 5);
|
||||
bitvec_set_uint(bv, pcp->t_avg_t, 5);
|
||||
bitvec_set_uint(bv, pcp->pc_meas_chan, 1);
|
||||
bitvec_set_uint(bv, pcp->n_avg_i, 4);
|
||||
}
|
||||
|
||||
/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
|
||||
int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
|
||||
{
|
||||
struct bitvec bv;
|
||||
|
||||
memset(&bv, 0, sizeof(bv));
|
||||
bv.data = data;
|
||||
bv.data_len = 21;
|
||||
|
||||
if (0) {
|
||||
/* No rest octets */
|
||||
bitvec_set_bit(&bv, L);
|
||||
} else {
|
||||
bitvec_set_bit(&bv, H);
|
||||
bitvec_set_uint(&bv, si13->bcch_change_mark, 3);
|
||||
bitvec_set_uint(&bv, si13->si_change_field, 4);
|
||||
if (0) {
|
||||
bitvec_set_bit(&bv, 0);
|
||||
} else {
|
||||
bitvec_set_bit(&bv, 1);
|
||||
bitvec_set_uint(&bv, si13->bcch_change_mark, 2);
|
||||
append_gprs_mobile_alloc(&bv);
|
||||
}
|
||||
if (!si13->pbcch_present) {
|
||||
/* PBCCH not present in cell */
|
||||
bitvec_set_bit(&bv, 0);
|
||||
bitvec_set_uint(&bv, si13->no_pbcch.rac, 8);
|
||||
bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup);
|
||||
bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3);
|
||||
bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2);
|
||||
append_gprs_cell_opt(&bv, &si13->cell_opts);
|
||||
append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars);
|
||||
} else {
|
||||
/* PBCCH present in cell */
|
||||
bitvec_set_bit(&bv, 1);
|
||||
bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4);
|
||||
/* PBCCH Descripiton */
|
||||
bitvec_set_uint(&bv, si13->pbcch.pb, 4);
|
||||
bitvec_set_uint(&bv, si13->pbcch.tsc, 3);
|
||||
bitvec_set_uint(&bv, si13->pbcch.tn, 3);
|
||||
switch (si13->pbcch.carrier_type) {
|
||||
case PBCCH_BCCH:
|
||||
bitvec_set_bit(&bv, 0);
|
||||
bitvec_set_bit(&bv, 0);
|
||||
break;
|
||||
case PBCCH_ARFCN:
|
||||
bitvec_set_bit(&bv, 0);
|
||||
bitvec_set_bit(&bv, 1);
|
||||
bitvec_set_uint(&bv, si13->pbcch.arfcn, 10);
|
||||
break;
|
||||
case PBCCH_MAIO:
|
||||
bitvec_set_bit(&bv, 1);
|
||||
bitvec_set_uint(&bv, si13->pbcch.maio, 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitvec_spare_padding(&bv, (bv.data_len*8)-1);
|
||||
}
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
444
openbsc/src/system_information.c
Normal file
444
openbsc/src/system_information.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/* GSM 04.08 System Information (SI) encoding and decoding
|
||||
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/rest_octets.h>
|
||||
#include <openbsc/bitvec.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
#define GSM48_CELL_CHAN_DESC_SIZE 16
|
||||
#define GSM_MACBLOCK_LEN 23
|
||||
#define GSM_MACBLOCK_PADDING 0x2b
|
||||
|
||||
/* Frequency Lists as per TS 04.08 10.5.2.13 */
|
||||
|
||||
/* 10.5.2.13.2: Bit map 0 format */
|
||||
static int freq_list_bm0_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
|
||||
{
|
||||
unsigned int byte, bit;
|
||||
|
||||
if (arfcn > 124 || arfcn < 1) {
|
||||
LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* the bitmask is from 1..124, not from 0..123 */
|
||||
arfcn--;
|
||||
|
||||
byte = arfcn / 8;
|
||||
bit = arfcn % 8;
|
||||
|
||||
chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 10.5.2.13.7: Variable bit map format */
|
||||
static int freq_list_bmrel_set_arfcn(u_int8_t *chan_list, unsigned int arfcn)
|
||||
{
|
||||
unsigned int byte, bit;
|
||||
unsigned int min_arfcn;
|
||||
unsigned int bitno;
|
||||
|
||||
min_arfcn = (chan_list[0] & 1) << 9;
|
||||
min_arfcn |= chan_list[1] << 1;
|
||||
min_arfcn |= (chan_list[2] >> 7) & 1;
|
||||
|
||||
/* The lower end of our bitmaks is always implicitly included */
|
||||
if (arfcn == min_arfcn)
|
||||
return 0;
|
||||
|
||||
if (arfcn < min_arfcn) {
|
||||
LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (arfcn > min_arfcn + 111) {
|
||||
LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bitno = (arfcn - min_arfcn);
|
||||
byte = bitno / 8;
|
||||
bit = bitno % 8;
|
||||
|
||||
chan_list[2 + byte] |= 1 << (7 - bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
|
||||
static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv,
|
||||
const struct gsm_bts *bts)
|
||||
{
|
||||
int i, rc, min = 1024, max = 0;
|
||||
|
||||
memset(chan_list, 0, 16);
|
||||
|
||||
/* GSM900-only handsets only support 'bit map 0 format' */
|
||||
if (bts->band == GSM_BAND_900) {
|
||||
chan_list[0] = 0;
|
||||
|
||||
for (i = 0; i < bv->data_len*8; i++) {
|
||||
if (bitvec_get_bit_pos(bv, i)) {
|
||||
rc = freq_list_bm0_set_arfcn(chan_list, i);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We currently only support the 'Variable bitmap format' */
|
||||
chan_list[0] = 0x8e;
|
||||
|
||||
for (i = 0; i < bv->data_len*8; i++) {
|
||||
if (bitvec_get_bit_pos(bv, i)) {
|
||||
if (i < min)
|
||||
min = i;
|
||||
if (i > max)
|
||||
max = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ((max - min) > 111) {
|
||||
LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, "
|
||||
"distance > 111\n", min, max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan_list[0] |= (min >> 9) & 1;
|
||||
chan_list[1] = (min >> 1);
|
||||
chan_list[2] = (min & 1) << 7;
|
||||
|
||||
for (i = 0; i < bv->data_len*8; i++) {
|
||||
if (bitvec_get_bit_pos(bv, i)) {
|
||||
rc = freq_list_bmrel_set_arfcn(chan_list, i);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
|
||||
static int generate_cell_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct bitvec *bv = &bts->si_common.cell_alloc;
|
||||
|
||||
/* first we generate a bitvec of all TRX ARFCN's in our BTS */
|
||||
llist_for_each_entry(trx, &bts->trx_list, list)
|
||||
bitvec_set_bit_pos(bv, trx->arfcn, 1);
|
||||
|
||||
/* then we generate a GSM 04.08 frequency list from the bitvec */
|
||||
return bitvec2freq_list(chan_list, bv, bts);
|
||||
}
|
||||
|
||||
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
|
||||
static int generate_bcch_chan_list(u_int8_t *chan_list, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts *cur_bts;
|
||||
struct bitvec *bv = &bts->si_common.neigh_list;
|
||||
|
||||
/* first we generate a bitvec of the BCCH ARFCN's in our BSC */
|
||||
llist_for_each_entry(cur_bts, &bts->network->bts_list, list) {
|
||||
if (cur_bts == bts)
|
||||
continue;
|
||||
bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1);
|
||||
}
|
||||
|
||||
/* then we generate a GSM 04.08 frequency list from the bitvec */
|
||||
return bitvec2freq_list(chan_list, bv, bts);
|
||||
}
|
||||
|
||||
static int generate_si1(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
int rc;
|
||||
struct gsm48_system_information_type_1 *si1 =
|
||||
(struct gsm48_system_information_type_1 *) output;
|
||||
|
||||
memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
si1->header.l2_plen = (21 << 2) | 1;
|
||||
si1->header.rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si1->header.skip_indicator = 0;
|
||||
si1->header.system_information = GSM48_MT_RR_SYSINFO_1;
|
||||
|
||||
rc = generate_cell_chan_list(si1->cell_channel_description, bts);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
si1->rach_control = bts->si_common.rach_control;
|
||||
|
||||
/* SI1 Rest Octets (10.5.2.32), contains NCH position */
|
||||
rest_octets_si1(si1->rest_octets, NULL);
|
||||
|
||||
return GSM_MACBLOCK_LEN;
|
||||
}
|
||||
|
||||
static int generate_si2(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
int rc;
|
||||
struct gsm48_system_information_type_2 *si2 =
|
||||
(struct gsm48_system_information_type_2 *) output;
|
||||
|
||||
memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
si2->header.l2_plen = (22 << 2) | 1;
|
||||
si2->header.rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si2->header.skip_indicator = 0;
|
||||
si2->header.system_information = GSM48_MT_RR_SYSINFO_2;
|
||||
|
||||
rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
si2->ncc_permitted = bts->si_common.ncc_permitted;
|
||||
si2->rach_control = bts->si_common.rach_control;
|
||||
|
||||
return GSM_MACBLOCK_LEN;
|
||||
}
|
||||
|
||||
struct gsm48_si_ro_info si_info = {
|
||||
.selection_params = {
|
||||
.present = 0,
|
||||
},
|
||||
.power_offset = {
|
||||
.present = 0,
|
||||
},
|
||||
.si2ter_indicator = 0,
|
||||
.early_cm_ctrl = 1,
|
||||
.scheduling = {
|
||||
.present = 0,
|
||||
},
|
||||
.gprs_ind = {
|
||||
.si13_position = 0,
|
||||
.ra_colour = 0,
|
||||
.present = 0,
|
||||
},
|
||||
.lsa_params = {
|
||||
.present = 0,
|
||||
},
|
||||
.cell_id = 0, /* FIXME: doesn't the bts have this? */
|
||||
.break_ind = 0,
|
||||
};
|
||||
|
||||
static int generate_si3(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm48_system_information_type_3 *si3 =
|
||||
(struct gsm48_system_information_type_3 *) output;
|
||||
|
||||
memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
si3->header.l2_plen = (18 << 2) | 1;
|
||||
si3->header.rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si3->header.skip_indicator = 0;
|
||||
si3->header.system_information = GSM48_MT_RR_SYSINFO_3;
|
||||
|
||||
si3->cell_identity = htons(bts->cell_identity);
|
||||
gsm0408_generate_lai(&si3->lai, bts->network->country_code,
|
||||
bts->network->network_code,
|
||||
bts->location_area_code);
|
||||
si3->control_channel_desc = bts->si_common.chan_desc;
|
||||
si3->cell_options = bts->si_common.cell_options;
|
||||
si3->cell_sel_par = bts->si_common.cell_sel_par;
|
||||
si3->rach_control = bts->si_common.rach_control;
|
||||
|
||||
/* SI3 Rest Octets (10.5.2.34), containing
|
||||
CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME
|
||||
Power Offset, 2ter Indicator, Early Classmark Sending,
|
||||
Scheduling if and WHERE, GPRS Indicator, SI13 position */
|
||||
rest_octets_si3(si3->rest_octets, &si_info);
|
||||
|
||||
return GSM_MACBLOCK_LEN;
|
||||
}
|
||||
|
||||
static int generate_si4(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm48_system_information_type_4 *si4 =
|
||||
(struct gsm48_system_information_type_4 *) output;
|
||||
|
||||
/* length of all IEs present except SI4 rest octets and l2_plen */
|
||||
int l2_plen = sizeof(*si4) - 1;
|
||||
|
||||
memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
si4->header.rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si4->header.skip_indicator = 0;
|
||||
si4->header.system_information = GSM48_MT_RR_SYSINFO_4;
|
||||
|
||||
gsm0408_generate_lai(&si4->lai, bts->network->country_code,
|
||||
bts->network->network_code,
|
||||
bts->location_area_code);
|
||||
si4->cell_sel_par = bts->si_common.cell_sel_par;
|
||||
si4->rach_control = bts->si_common.rach_control;
|
||||
|
||||
/* Optional: CBCH Channel Description + CBCH Mobile Allocation */
|
||||
|
||||
si4->header.l2_plen = (l2_plen << 2) | 1;
|
||||
|
||||
/* SI4 Rest Octets (10.5.2.35), containing
|
||||
Optional Power offset, GPRS Indicator,
|
||||
Cell Identity, LSA ID, Selection Parameter */
|
||||
rest_octets_si4(si4->data, &si_info);
|
||||
|
||||
return GSM_MACBLOCK_LEN;
|
||||
}
|
||||
|
||||
static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm48_system_information_type_5 *si5;
|
||||
int rc, l2_plen = 18;
|
||||
|
||||
/* ip.access nanoBTS needs l2_plen!! */
|
||||
if (is_ipaccess_bts(bts)) {
|
||||
*output++ = (l2_plen << 2) | 1;
|
||||
l2_plen++;
|
||||
}
|
||||
|
||||
si5 = (struct gsm48_system_information_type_5 *) output;
|
||||
memset(si5, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* l2 pseudo length, not part of msg: 18 */
|
||||
si5->rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si5->skip_indicator = 0;
|
||||
si5->system_information = GSM48_MT_RR_SYSINFO_5;
|
||||
rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* 04.08 9.1.37: L2 Pseudo Length of 18 */
|
||||
return l2_plen;
|
||||
}
|
||||
|
||||
static int generate_si6(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm48_system_information_type_6 *si6;
|
||||
int l2_plen = 11;
|
||||
|
||||
/* ip.access nanoBTS needs l2_plen!! */
|
||||
if (is_ipaccess_bts(bts)) {
|
||||
*output++ = (l2_plen << 2) | 1;
|
||||
l2_plen++;
|
||||
}
|
||||
|
||||
si6 = (struct gsm48_system_information_type_6 *) output;
|
||||
memset(si6, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* l2 pseudo length, not part of msg: 11 */
|
||||
si6->rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si6->skip_indicator = 0;
|
||||
si6->system_information = GSM48_MT_RR_SYSINFO_6;
|
||||
si6->cell_identity = htons(bts->cell_identity);
|
||||
gsm0408_generate_lai(&si6->lai, bts->network->country_code,
|
||||
bts->network->network_code,
|
||||
bts->location_area_code);
|
||||
si6->cell_options = bts->si_common.cell_options;
|
||||
si6->ncc_permitted = bts->si_common.ncc_permitted;
|
||||
|
||||
/* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */
|
||||
|
||||
return l2_plen;
|
||||
}
|
||||
|
||||
static struct gsm48_si13_info si13_default = {
|
||||
.cell_opts = {
|
||||
.nmo = GPRS_NMO_III,
|
||||
.t3168 = 1000,
|
||||
.t3192 = 1000,
|
||||
.drx_timer_max = 1,
|
||||
.bs_cv_max = 15,
|
||||
},
|
||||
.pwr_ctrl_pars = {
|
||||
.alpha = 10, /* a = 1.0 */
|
||||
.t_avg_w = 25,
|
||||
.t_avg_t = 25,
|
||||
.pc_meas_chan = 0, /* downling measured on CCCH */
|
||||
.n_avg_i = 15,
|
||||
},
|
||||
.bcch_change_mark = 0,
|
||||
.si_change_field = 0,
|
||||
.pbcch_present = 0,
|
||||
{
|
||||
.no_pbcch = {
|
||||
.rac = 0,
|
||||
.spgc_ccch_sup = 0,
|
||||
.net_ctrl_ord = 0,
|
||||
.prio_acc_thr = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int generate_si13(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm48_system_information_type_13 *si13 =
|
||||
(struct gsm48_system_information_type_13 *) output;
|
||||
int ret;
|
||||
|
||||
memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
si13->header.rr_protocol_discriminator = GSM48_PDISC_RR;
|
||||
si13->header.skip_indicator = 0;
|
||||
si13->header.system_information = GSM48_MT_RR_SYSINFO_13;
|
||||
|
||||
ret = rest_octets_si13(si13->rest_octets, &si13_default);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
si13->header.l2_plen = ret & 0xff;
|
||||
|
||||
return GSM_MACBLOCK_LEN;
|
||||
}
|
||||
|
||||
int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type)
|
||||
{
|
||||
switch (type) {
|
||||
case RSL_SYSTEM_INFO_1:
|
||||
return generate_si1(output, bts);
|
||||
case RSL_SYSTEM_INFO_2:
|
||||
return generate_si2(output, bts);
|
||||
case RSL_SYSTEM_INFO_3:
|
||||
return generate_si3(output, bts);
|
||||
case RSL_SYSTEM_INFO_4:
|
||||
return generate_si4(output, bts);
|
||||
case RSL_SYSTEM_INFO_5:
|
||||
return generate_si5(output, bts);
|
||||
case RSL_SYSTEM_INFO_6:
|
||||
return generate_si6(output, bts);
|
||||
case RSL_SYSTEM_INFO_13:
|
||||
return generate_si13(output, bts);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@@ -105,6 +105,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* taken from http://insanecoding.blogspot.com/2007/03/methods-for-safe-string-handling.html */
|
||||
size_t strnlen(const char *s, size_t n)
|
||||
{
|
||||
const char *p = (const char *)memchr(s, 0, n);
|
||||
return(p ? p-s : n);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* this null_context is only used if talloc_enable_leak_report() or
|
||||
talloc_enable_leak_report_full() is called, otherwise it remains
|
||||
NULL
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
#include <vty/buffer.h>
|
||||
|
||||
@@ -71,7 +72,7 @@ void telnet_init(struct gsm_network *network, int port) {
|
||||
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("Telnet interface socket creation failed");
|
||||
LOGP(DNM, LOGL_ERROR, "Telnet interface socket creation failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,12 +84,12 @@ void telnet_init(struct gsm_network *network, int port) {
|
||||
sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
|
||||
perror("Telnet interface failed to bind");
|
||||
LOGP(DNM, LOGL_ERROR, "Telnet interface failed to bind\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(fd, 0) < 0) {
|
||||
perror("Telnet interface failed to listen");
|
||||
LOGP(DNM, LOGL_ERROR, "Telnet interface failed to listen\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,7 +155,7 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
|
||||
int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
|
||||
|
||||
if (new_connection < 0) {
|
||||
perror("telnet accept failed");
|
||||
LOGP(DNM, LOGL_ERROR, "telnet accept failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -171,8 +172,10 @@ static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
|
||||
print_welcome(new_connection);
|
||||
|
||||
connection->vty = vty_create(new_connection, connection);
|
||||
if (!connection->vty)
|
||||
if (!connection->vty) {
|
||||
LOGP(DNM, LOGL_ERROR, "couldn't create VTY\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -134,10 +134,34 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
|
||||
used_tid_bitmask |= (1 << trans->transaction_id);
|
||||
}
|
||||
|
||||
for (i = 0; i <= 7; i++) {
|
||||
for (i = 0; i < 7; i++) {
|
||||
if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0)
|
||||
return i | ti_flag;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* update all transactions to use a different LCHAN, e.g.
|
||||
* after handover has succeeded */
|
||||
int trans_lchan_change(struct gsm_lchan *lchan_old,
|
||||
struct gsm_lchan *lchan_new)
|
||||
{
|
||||
struct gsm_network *net = lchan_old->ts->trx->bts->network;
|
||||
struct gsm_trans *trans;
|
||||
int num = 0;
|
||||
|
||||
llist_for_each_entry(trans, &net->trans_list, entry) {
|
||||
if (trans->lchan == lchan_old) {
|
||||
/* drop old channel use cound */
|
||||
put_lchan(trans->lchan);
|
||||
/* assign new channel */
|
||||
trans->lchan = lchan_new;
|
||||
/* bump new channel use count */
|
||||
use_lchan(trans->lchan);
|
||||
num++;
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -87,10 +87,18 @@ static void net_dump_vty(struct vty *vty, struct gsm_network *net)
|
||||
net->name_short, VTY_NEWLINE);
|
||||
vty_out(vty, " Authentication policy: %s%s",
|
||||
gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
|
||||
vty_out(vty, " Location updating reject cause: %u%s",
|
||||
net->reject_cause, VTY_NEWLINE);
|
||||
vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " NECI (TCH/H): %u%s", net->neci,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off",
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off",
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Allowed Audio Codecs: ");
|
||||
for (i = 0; i < net->audio_length; ++i)
|
||||
vty_out(vty, "hr: %d ver: %d, ",
|
||||
@@ -133,7 +141,13 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
bts->cell_identity,
|
||||
bts->location_area_code, bts->bsic, bts->tsc,
|
||||
bts->num_trx, VTY_NEWLINE);
|
||||
if (bts->cell_barred)
|
||||
vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE);
|
||||
vty_out(vty, "Minimum Rx Level for Access: %i dBm%s",
|
||||
rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s",
|
||||
bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
|
||||
if (bts->si_common.rach_control.cell_bar)
|
||||
vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE);
|
||||
if (is_ipaccess_bts(bts))
|
||||
vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s",
|
||||
@@ -241,13 +255,17 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
|
||||
vty_out(vty, " training_sequence_code %u%s", bts->tsc, VTY_NEWLINE);
|
||||
vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE);
|
||||
vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE);
|
||||
if (bts->chan_desc.t3212)
|
||||
vty_out(vty, " cell reselection hysteresis %u%s",
|
||||
bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE);
|
||||
vty_out(vty, " rxlev access min %u%s",
|
||||
bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE);
|
||||
if (bts->si_common.chan_desc.t3212)
|
||||
vty_out(vty, " periodic location update %u%s",
|
||||
bts->chan_desc.t3212 * 10, VTY_NEWLINE);
|
||||
bts->si_common.chan_desc.t3212 * 10, VTY_NEWLINE);
|
||||
vty_out(vty, " channel allocator %s%s",
|
||||
bts->chan_alloc_reverse ? "descending" : "ascending",
|
||||
VTY_NEWLINE);
|
||||
if (bts->cell_barred)
|
||||
if (bts->si_common.rach_control.cell_bar)
|
||||
vty_out(vty, " cell barred 1%s", VTY_NEWLINE);
|
||||
if (is_ipaccess_bts(bts)) {
|
||||
vty_out(vty, " ip.access unit_id %u %u%s",
|
||||
@@ -276,12 +294,45 @@ static int config_write_net(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "network%s", VTY_NEWLINE);
|
||||
vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE);
|
||||
if (gsmnet->core_country_code > 0)
|
||||
vty_out(vty, " core network country code %u%s", gsmnet->core_country_code, VTY_NEWLINE);
|
||||
vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE);
|
||||
if (gsmnet->core_network_code > 0)
|
||||
vty_out(vty, " core mobile network code %u%s", gsmnet->core_network_code, VTY_NEWLINE);
|
||||
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);
|
||||
vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE);
|
||||
vty_out(vty, " ipacc rtp_payload %u%s", gsmnet->rtp_payload, VTY_NEWLINE);
|
||||
|
||||
if (gsmnet->audio_length != 0) {
|
||||
@@ -379,24 +430,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,
|
||||
@@ -478,13 +520,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
|
||||
@@ -737,6 +790,41 @@ DEFUN(show_paging,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_stats,
|
||||
show_stats_cmd,
|
||||
"show statistics",
|
||||
SHOW_STR "Display network statistics\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
|
||||
vty_out(vty, "Channel Requests: %lu total, %lu no channel%s",
|
||||
net->stats.chreq.total, net->stats.chreq.no_channel,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "Location Update: %lu attach, %lu normal, %lu periodic%s",
|
||||
net->stats.loc_upd_type.attach, net->stats.loc_upd_type.normal,
|
||||
net->stats.loc_upd_type.periodic, VTY_NEWLINE);
|
||||
vty_out(vty, "IMSI Detach Indications: %lu%s\n",
|
||||
net->stats.loc_upd_type.detach, VTY_NEWLINE);
|
||||
vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
|
||||
net->stats.loc_upd_resp.accept,
|
||||
net->stats.loc_upd_resp.reject, VTY_NEWLINE);
|
||||
vty_out(vty, "Paging: %lu attempted, %lu complete, %lu expired%s",
|
||||
net->stats.paging.attempted, net->stats.paging.completed,
|
||||
net->stats.paging.expired, VTY_NEWLINE);
|
||||
vty_out(vty, "Handover: %lu attempted, %lu no_channel, %lu timeout, "
|
||||
"%lu completed, %lu failed%s", net->stats.handover.attempted,
|
||||
net->stats.handover.no_channel, net->stats.handover.timeout,
|
||||
net->stats.handover.completed, net->stats.handover.failed,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "SMS MO: %lu submitted, %lu no receiver%s",
|
||||
net->stats.sms.submitted, net->stats.sms.no_receiver,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "SMS MT: %lu delivered, %lu no memory, %lu other error%s",
|
||||
net->stats.sms.delivered, net->stats.sms.rp_err_mem,
|
||||
net->stats.sms.rp_err_other, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net,
|
||||
cfg_net_cmd,
|
||||
"network",
|
||||
@@ -759,6 +847,16 @@ DEFUN(cfg_net_ncc,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_core_net_ncc,
|
||||
cfg_core_net_ncc_cmd,
|
||||
"core network country code <1-999>",
|
||||
"Set the GSM country code to be used in the MSC connection")
|
||||
{
|
||||
gsmnet->core_country_code = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_mnc,
|
||||
cfg_net_mnc_cmd,
|
||||
"mobile network code <1-999>",
|
||||
@@ -769,6 +867,16 @@ DEFUN(cfg_net_mnc,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_core_net_mnc,
|
||||
cfg_core_net_mnc_cmd,
|
||||
"core mobile network code <1-999>",
|
||||
"Set the GSM mobile network code to be used in the MSC connection")
|
||||
{
|
||||
gsmnet->core_network_code = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_name_short,
|
||||
cfg_net_name_short_cmd,
|
||||
"short name NAME",
|
||||
@@ -807,6 +915,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)",
|
||||
@@ -826,6 +944,87 @@ DEFUN(cfg_net_neci,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd,
|
||||
"rrlp mode (none|ms-based|ms-preferred|ass-preferred)",
|
||||
"Set the Radio Resource Location Protocol Mode")
|
||||
{
|
||||
gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd,
|
||||
"mm info (0|1)",
|
||||
"Whether to send MM INFO after LOC UPD ACCEPT")
|
||||
{
|
||||
gsmnet->send_mm_info = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_handover, cfg_net_handover_cmd,
|
||||
"handover (0|1)",
|
||||
"Whether or not to use in-call handover")
|
||||
{
|
||||
if (ipacc_rtp_direct) {
|
||||
vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode "
|
||||
"is enabled by using the -P command line option%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
gsmnet->handover.active = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
|
||||
"handover window rxlev averaging <1-10>",
|
||||
"How many RxLev measurements are used for averaging")
|
||||
{
|
||||
gsmnet->handover.win_rxlev_avg = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
|
||||
"handover window rxqual averaging <1-10>",
|
||||
"How many RxQual measurements are used for averaging")
|
||||
{
|
||||
gsmnet->handover.win_rxqual_avg = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
|
||||
"handover window rxlev neighbor averaging <1-10>",
|
||||
"How many RxQual measurements are used for averaging")
|
||||
{
|
||||
gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
|
||||
"handover power budget interval <1-99>",
|
||||
"How often to check if we have a better cell (SACCH frames)")
|
||||
{
|
||||
gsmnet->handover.pwr_interval = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
|
||||
"handover power budget hysteresis <0-999>",
|
||||
"How many dB does a neighbor to be stronger to become a HO candidate")
|
||||
{
|
||||
gsmnet->handover.pwr_hysteresis = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
|
||||
"handover maximum distance <0-9999>",
|
||||
"How big is the maximum timing advance before HO is forced")
|
||||
{
|
||||
gsmnet->handover.max_distance = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_supported_codecs,
|
||||
cfg_net_supported_codecs_cmd,
|
||||
"codec_list .LIST",
|
||||
@@ -910,6 +1109,36 @@ DEFUN(cfg_net_rtp_base_port,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DECLARE_TIMER(number) \
|
||||
DEFUN(cfg_net_T##number, \
|
||||
cfg_net_T##number##_cmd, \
|
||||
"timer t" #number " <0-65535>", \
|
||||
"Set the T" #number " value.") \
|
||||
{ \
|
||||
int value = atoi(argv[0]); \
|
||||
\
|
||||
if (value < 0 || value > 65535) { \
|
||||
vty_out(vty, "Timer value %s out of range.%s", \
|
||||
argv[0], VTY_NEWLINE); \
|
||||
return CMD_WARNING; \
|
||||
} \
|
||||
\
|
||||
gsmnet->T##number = value; \
|
||||
return CMD_SUCCESS; \
|
||||
}
|
||||
|
||||
DECLARE_TIMER(3101)
|
||||
DECLARE_TIMER(3103)
|
||||
DECLARE_TIMER(3105)
|
||||
DECLARE_TIMER(3107)
|
||||
DECLARE_TIMER(3109)
|
||||
DECLARE_TIMER(3111)
|
||||
DECLARE_TIMER(3113)
|
||||
DECLARE_TIMER(3115)
|
||||
DECLARE_TIMER(3117)
|
||||
DECLARE_TIMER(3119)
|
||||
DECLARE_TIMER(3141)
|
||||
|
||||
/* per-BTS configuration */
|
||||
DEFUN(cfg_bts,
|
||||
cfg_bts_cmd,
|
||||
@@ -1018,6 +1247,7 @@ DEFUN(cfg_bts_lac,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_bts_tsc,
|
||||
cfg_bts_tsc_cmd,
|
||||
"training_sequence_code <0-255>",
|
||||
@@ -1139,7 +1369,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;
|
||||
}
|
||||
@@ -1155,13 +1385,35 @@ DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd,
|
||||
"cell reselection hysteresis <0-14>",
|
||||
"Cell Re-Selection Hysteresis in dB")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
|
||||
bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd,
|
||||
"rxlev access min <0-63>",
|
||||
"Minimum RxLev needed for cell access (better than -110dBm)")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
|
||||
bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd,
|
||||
"periodic location update <0-1530>",
|
||||
"Periodic Location Updating Interval in Minutes")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
|
||||
bts->chan_desc.t3212 = atoi(argv[0]) / 10;
|
||||
bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 10;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1350,20 +1602,44 @@ int bsc_vty_init(struct gsm_network *net)
|
||||
install_element(VIEW_NODE, &show_e1ts_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &show_paging_cmd);
|
||||
install_element(VIEW_NODE, &show_stats_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_net_cmd);
|
||||
install_node(&net_node, config_write_net);
|
||||
install_default(GSMNET_NODE);
|
||||
install_element(GSMNET_NODE, &cfg_net_ncc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_core_net_ncc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_mnc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_core_net_mnc_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_neci_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_handover_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_supported_codecs_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ipacc_rtp_payload_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_rtp_base_port_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3107_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3109_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3111_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3113_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3115_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3117_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3119_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3141_cmd);
|
||||
|
||||
install_element(GSMNET_NODE, &cfg_bts_cmd);
|
||||
install_node(&bts_node, config_write_bts);
|
||||
@@ -1382,6 +1658,8 @@ int bsc_vty_init(struct gsm_network *net)
|
||||
install_element(BTS_NODE, &cfg_bts_cell_barred_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd);
|
||||
|
||||
|
||||
install_element(BTS_NODE, &cfg_trx_cmd);
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_utils.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
@@ -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 */
|
||||
|
13
wireshark/rsl-system_info.patch
Normal file
13
wireshark/rsl-system_info.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
Index: wireshark/epan/dissectors/packet-rsl.c
|
||||
===================================================================
|
||||
--- wireshark.orig/epan/dissectors/packet-rsl.c
|
||||
+++ wireshark/epan/dissectors/packet-rsl.c
|
||||
@@ -2291,7 +2291,7 @@
|
||||
|
||||
proto_tree_add_text(ie_tree, tvb,offset,length,"Layer 3 message");
|
||||
next_tvb = tvb_new_subset(tvb, offset, length, length);
|
||||
- /* call_dissector(gsm_a_dtap_handle, next_tvb, pinfo, top_tree);*/
|
||||
+ call_dissector(gsm_a_ccch_handle, next_tvb, pinfo, top_tree);
|
||||
|
||||
offset = offset + length;
|
||||
|
Reference in New Issue
Block a user