Merge branch 'master' into on-waves/sccp
This commit is contained in:
commit
67f5003caa
|
@ -3,8 +3,12 @@
|
|||
.deps
|
||||
Makefile
|
||||
Makefile.in
|
||||
bscconfig.h
|
||||
bscconfig.h.in
|
||||
openbsc.pc
|
||||
bsc_hack
|
||||
bsc_msc_ip
|
||||
bsc_mgcp
|
||||
*.*~
|
||||
*.sw?
|
||||
|
||||
|
@ -26,6 +30,8 @@ hlr.sqlite3
|
|||
bs11_config
|
||||
ipaccess-config
|
||||
ipaccess-find
|
||||
ipaccess-firmware
|
||||
ipaccess-proxy
|
||||
isdnsync
|
||||
|
||||
#tests
|
||||
|
|
|
@ -3,3 +3,5 @@ Holger Freyther <zecke@selfish.org>
|
|||
Jan Luebbe <jluebbe@debian.org>
|
||||
Stefan Schmidt <stefan@datenfreihafen.org>
|
||||
Daniel Willmann <daniel@totalueberwachung.de>
|
||||
Andreas Eversberg <Andreas.Eversberg@versatel.de>
|
||||
Sylvain Munaut <246tnt@gmail.com>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Convert ETSI documents to an enum
|
||||
#
|
||||
|
||||
import re, sys
|
||||
|
||||
def convert(string):
|
||||
string = string.strip().replace(" ", "").rjust(8, "0")
|
||||
var = 0
|
||||
offset = 7
|
||||
for char in string:
|
||||
assert offset >= 0
|
||||
var = var | (int(char) << offset)
|
||||
offset = offset - 1
|
||||
|
||||
return var
|
||||
|
||||
def string(name):
|
||||
name = name.replace(" ", "_")
|
||||
name = name.replace('"', "")
|
||||
name = name.replace('/', '_')
|
||||
name = name.replace('(', '_')
|
||||
name = name.replace(')', '_')
|
||||
return "%s_%s" % (sys.argv[2], name.upper())
|
||||
|
||||
file = open(sys.argv[1])
|
||||
|
||||
|
||||
for line in file:
|
||||
m = re.match(r"[ \t]*(?P<value>[01 ]+)[ ]+(?P<name>[a-zA-Z /0-9()]+)", line[:-1])
|
||||
|
||||
if m:
|
||||
print "\t%s\t\t= %d," % (string(m.groupdict()["name"]), convert(m.groupdict()["value"]))
|
||||
else:
|
||||
print line[:-1]
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python
|
||||
# Simple server for mgcp... send audit, receive response..
|
||||
|
||||
import socket, time
|
||||
|
||||
MGCP_GATEWAY_PORT = 2427
|
||||
MGCP_CALLAGENT_PORT = 2727
|
||||
|
||||
rsip_resp = """200 321321332\r\n"""
|
||||
audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n"""
|
||||
crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n"""
|
||||
dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n"""
|
||||
mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 4400 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
|
||||
|
||||
def hexdump(src, length=8):
|
||||
"""Recipe is from http://code.activestate.com/recipes/142812/"""
|
||||
result = []
|
||||
digits = 4 if isinstance(src, unicode) else 2
|
||||
for i in xrange(0, len(src), length):
|
||||
s = src[i:i+length]
|
||||
hexa = b' '.join(["%0*X" % (digits, ord(x)) for x in s])
|
||||
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
|
||||
result.append( b"%04X %-*s %s" % (i, length*(digits + 1), hexa, text) )
|
||||
return b'\n'.join(result)
|
||||
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT))
|
||||
server_socket.setblocking(0)
|
||||
|
||||
|
||||
def send_receive(packet):
|
||||
server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT))
|
||||
try:
|
||||
data, addr = server_socket.recvfrom(4096)
|
||||
print hexdump(data), addr
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
def generate_tid():
|
||||
import random
|
||||
return random.randint(0, 65123)
|
||||
|
||||
|
||||
|
||||
i = 1
|
||||
while True:
|
||||
send_receive(rsip_resp)
|
||||
send_receive(audit_packet)
|
||||
send_receive(crcx_packet % generate_tid() )
|
||||
send_receive(mdcx_packet % (generate_tid(), i))
|
||||
send_receive(dlcx_packet % (generate_tid(), i))
|
||||
i = i + 1
|
||||
|
||||
time.sleep(3)
|
|
@ -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
|
||||
|
|
@ -3,4 +3,6 @@ noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
|
|||
timer.h misdn.h chan_alloc.h telnet_interface.h paging.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
|
||||
bsc_rll.h mncc.h talloc.h transaction.h ussd.h gsm_04_80.h \
|
||||
silent_call.h mgcp.h meas_rep.h bitvec.h rest_octets.h \
|
||||
system_information.h handover.h statistics.h
|
||||
|
|
|
@ -211,6 +211,7 @@ enum abis_nm_msgtype_bs11 {
|
|||
enum abis_nm_msgtype_ipacc {
|
||||
NM_MT_IPACC_RESTART = 0x87,
|
||||
NM_MT_IPACC_RESTART_ACK,
|
||||
NM_MT_IPACC_RESTART_NACK,
|
||||
NM_MT_IPACC_RSL_CONNECT = 0xe0,
|
||||
NM_MT_IPACC_RSL_CONNECT_ACK,
|
||||
NM_MT_IPACC_RSL_CONNECT_NACK,
|
||||
|
@ -485,6 +486,12 @@ enum abis_nm_avail_state {
|
|||
NM_AVSTATE_OK = 0xff,
|
||||
};
|
||||
|
||||
enum abis_nm_op_state {
|
||||
NM_OPSTATE_DISABLED = 1,
|
||||
NM_OPSTATE_ENABLED = 2,
|
||||
NM_OPSTATE_NULL = 0xff,
|
||||
};
|
||||
|
||||
/* Section 9.4.13: Channel Combination */
|
||||
enum abis_nm_chan_comb {
|
||||
NM_CHANC_TCHFull = 0x00, /* TCH/F + TCH/H + SACCH/TF */
|
||||
|
@ -712,6 +719,8 @@ struct ipac_bcch_info {
|
|||
u_int8_t ca_list_si1[16];
|
||||
};
|
||||
|
||||
extern const struct tlv_definition nm_att_tlvdef;
|
||||
|
||||
/* PUBLIC */
|
||||
|
||||
struct msgb;
|
||||
|
@ -726,7 +735,7 @@ struct abis_nm_cfg {
|
|||
|
||||
extern int abis_nm_rcvmsg(struct msgb *msg);
|
||||
|
||||
int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len);
|
||||
int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len);
|
||||
int abis_nm_rx(struct msgb *msg);
|
||||
int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2);
|
||||
int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0,
|
||||
|
@ -780,10 +789,13 @@ int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts);
|
|||
int abis_nm_bs11_get_serno(struct gsm_bts *bts);
|
||||
int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, u_int8_t level);
|
||||
int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx);
|
||||
int abis_nm_bs11_logon(struct gsm_bts *bts, u_int8_t level, const char *name, int on);
|
||||
int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on);
|
||||
int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on);
|
||||
int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password);
|
||||
int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked);
|
||||
int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts);
|
||||
int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value);
|
||||
int abis_nm_bs11_get_cclk(struct gsm_bts *bts);
|
||||
int abis_nm_bs11_get_state(struct gsm_bts *bts);
|
||||
int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
|
||||
|
@ -798,7 +810,7 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
|
|||
u_int8_t obj_class, u_int8_t bts_nr,
|
||||
u_int8_t trx_nr, u_int8_t ts_nr,
|
||||
u_int8_t *attr, int attr_len);
|
||||
int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
|
||||
int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
|
||||
int attr_len);
|
||||
int abis_nm_ipaccess_restart(struct gsm_bts *bts);
|
||||
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
|
||||
|
@ -819,4 +831,5 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
|||
|
||||
const char *nm_opstate_name(u_int8_t os);
|
||||
const char *nm_avail_name(u_int8_t avail);
|
||||
int nm_is_running(struct gsm_nm_state *s);
|
||||
#endif /* _NM_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 */
|
||||
|
|
|
@ -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 */
|
|
@ -49,4 +49,18 @@ void lchan_free(struct gsm_lchan *lchan);
|
|||
/* Consider releasing the channel */
|
||||
int lchan_auto_release(struct gsm_lchan *lchan);
|
||||
|
||||
struct load_counter {
|
||||
unsigned int total;
|
||||
unsigned int used;
|
||||
};
|
||||
|
||||
struct pchan_load {
|
||||
struct load_counter pchan[GSM_PCHAN_UNKNOWN];
|
||||
};
|
||||
|
||||
void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts);
|
||||
void network_chan_load(struct pchan_load *pl, struct gsm_network *net);
|
||||
|
||||
int trx_is_usable(struct gsm_bts_trx *trx);
|
||||
|
||||
#endif /* _CHAN_ALLOC_H */
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* COMP128 header
|
||||
*
|
||||
* See comp128.c for details
|
||||
*/
|
||||
|
||||
#ifndef __COMP128_H__
|
||||
#define __COMP128_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* Performs the COMP128 algorithm (used as A3/A8)
|
||||
* ki : u_int8_t [16]
|
||||
* srand : u_int8_t [16]
|
||||
* sres : u_int8_t [4]
|
||||
* kc : u_int8_t [8]
|
||||
*/
|
||||
void comp128(u_int8_t *ki, u_int8_t *srand, u_int8_t *sres, u_int8_t *kc);
|
||||
|
||||
#endif /* __COMP128_H__ */
|
||||
|
|
@ -43,9 +43,20 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
|
|||
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei);
|
||||
int db_sync_equipment(struct gsm_equipment *equip);
|
||||
|
||||
/* auth info */
|
||||
int get_authinfo_by_subscr(struct gsm_auth_info *ainfo,
|
||||
struct gsm_subscriber *subscr);
|
||||
int set_authinfo_for_subscr(struct gsm_auth_info *ainfo,
|
||||
struct gsm_subscriber *subscr);
|
||||
int get_authtuple_by_subscr(struct gsm_auth_tuple *atuple,
|
||||
struct gsm_subscriber *subscr);
|
||||
int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple,
|
||||
struct gsm_subscriber *subscr);
|
||||
|
||||
/* SMS store-and-forward */
|
||||
int db_sms_store(struct gsm_sms *sms);
|
||||
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
|
||||
struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id);
|
||||
struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
|
||||
int db_sms_mark_sent(struct gsm_sms *sms);
|
||||
|
||||
|
@ -53,4 +64,8 @@ int db_sms_mark_sent(struct gsm_sms *sms);
|
|||
int db_apdu_blob_store(struct gsm_subscriber *subscr,
|
||||
u_int8_t apdu_id_flags, u_int8_t len,
|
||||
u_int8_t *apdu);
|
||||
|
||||
/* Statistics counter storage */
|
||||
int db_store_counter(struct counter *ctr);
|
||||
|
||||
#endif /* _DB_H */
|
||||
|
|
|
@ -1,45 +1,131 @@
|
|||
#ifndef _DEBUG_H
|
||||
#define _DEBUG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "linuxlist.h"
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#define DRLL 0x0001
|
||||
#define DCC 0x0002
|
||||
#define DMM 0x0004
|
||||
#define DRR 0x0008
|
||||
#define DRSL 0x0010
|
||||
#define DNM 0x0020
|
||||
|
||||
#define DMNCC 0x0080
|
||||
#define DSMS 0x0100
|
||||
#define DPAG 0x0200
|
||||
#define DMEAS 0x0400
|
||||
|
||||
#define DMI 0x1000
|
||||
#define DMIB 0x2000
|
||||
#define DMUX 0x4000
|
||||
#define DINP 0x8000
|
||||
|
||||
#define DSCCP 0x10000
|
||||
#define DMSC 0x20000
|
||||
|
||||
#define DMGCP 0x40000
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DRLL,
|
||||
DCC,
|
||||
DMM,
|
||||
DRR,
|
||||
DRSL,
|
||||
DNM,
|
||||
DMNCC,
|
||||
DSMS,
|
||||
DPAG,
|
||||
DMEAS,
|
||||
DMI,
|
||||
DMIB,
|
||||
DMUX,
|
||||
DINP,
|
||||
DSCCP,
|
||||
DMSC,
|
||||
DMGCP,
|
||||
DHO,
|
||||
DDB,
|
||||
DREF,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
#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)
|
||||
#else
|
||||
#define DEBUGP(xss, fmt, args...)
|
||||
#define DEBUGP(xss, fmt, args...)
|
||||
#define DEBUGPC(ss, fmt, args...)
|
||||
#endif
|
||||
|
||||
|
||||
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
|
||||
|
||||
char *hexdump(const unsigned char *buf, int len);
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
|
||||
void debug_parse_category_mask(const char* mask);
|
||||
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...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
|
||||
#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __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 */
|
||||
|
||||
/* context */
|
||||
#define BSC_CTX_LCHAN 0
|
||||
#define BSC_CTX_SUBSCR 1
|
||||
#define BSC_CTX_BTS 2
|
||||
#define BSC_CTX_SCCP 3
|
||||
|
||||
/* target */
|
||||
|
||||
enum {
|
||||
DEBUG_FILTER_IMSI = 1 << 0,
|
||||
DEBUG_FILTER_ALL = 1 << 1,
|
||||
};
|
||||
|
||||
struct debug_category {
|
||||
int enabled;
|
||||
int loglevel;
|
||||
};
|
||||
|
||||
struct debug_target {
|
||||
int filter_map;
|
||||
char *imsi_filter;
|
||||
|
||||
|
||||
struct debug_category categories[Debug_LastEntry];
|
||||
int use_color;
|
||||
int print_timestamp;
|
||||
int loglevel;
|
||||
|
||||
union {
|
||||
struct {
|
||||
FILE *out;
|
||||
} tgt_stdout;
|
||||
|
||||
struct {
|
||||
int priority;
|
||||
} tgt_syslog;
|
||||
|
||||
struct {
|
||||
void *vty;
|
||||
} tgt_vty;
|
||||
};
|
||||
|
||||
void (*output) (struct debug_target *target, const char *string);
|
||||
|
||||
struct llist_head entry;
|
||||
};
|
||||
|
||||
/* use the above macros */
|
||||
void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7)));
|
||||
void debug_init(void);
|
||||
|
||||
/* context management */
|
||||
void debug_reset_context(void);
|
||||
void debug_set_context(int ctx, void *value);
|
||||
|
||||
/* filter on the targets */
|
||||
void debug_set_imsi_filter(struct debug_target *target, const char *imsi);
|
||||
void debug_set_all_filter(struct debug_target *target, int);
|
||||
void debug_set_use_color(struct debug_target *target, int);
|
||||
void debug_set_print_timestamp(struct debug_target *target, int);
|
||||
void debug_set_log_level(struct debug_target *target, int log_level);
|
||||
void debug_parse_category_mask(struct debug_target *target, const char* mask);
|
||||
int debug_parse_level(const char *lvl);
|
||||
int debug_parse_category(const char *category);
|
||||
void debug_set_category_filter(struct debug_target *target, int category, int enable, int level);
|
||||
|
||||
|
||||
/* management of the targets */
|
||||
struct debug_target *debug_target_create(void);
|
||||
struct debug_target *debug_target_create_stderr(void);
|
||||
void debug_add_target(struct debug_target *target);
|
||||
void debug_del_target(struct debug_target *target);
|
||||
#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,
|
||||
|
@ -747,7 +756,7 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
|
|||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
|
||||
|
||||
int gsm48_tx_mm_info(struct gsm_lchan *lchan);
|
||||
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand);
|
||||
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq);
|
||||
int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan);
|
||||
struct msgb *gsm48_msgb_alloc(void);
|
||||
int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans);
|
||||
|
@ -759,7 +768,9 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan);
|
|||
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_rr_ass_cmd(struct gsm_lchan *dest_lchan, 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
|
||||
|
|
|
@ -9,6 +9,7 @@ struct value_string {
|
|||
};
|
||||
|
||||
const char *get_value_string(const struct value_string *vs, u_int32_t val);
|
||||
int get_string_value(const struct value_string *vs, const char *str);
|
||||
|
||||
enum gsm_band {
|
||||
GSM_BAND_400,
|
||||
|
@ -38,6 +39,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 +61,8 @@ enum gsm_chreq_reason_t {
|
|||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/mncc.h>
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/bitvec.h>
|
||||
#include <openbsc/statistics.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
|
@ -92,14 +102,14 @@ typedef int gsm_cbfn(unsigned int hooknum,
|
|||
#define LCHAN_RELEASE_TIMEOUT 20, 0
|
||||
#define use_lchan(lchan) \
|
||||
do { lchan->use_count++; \
|
||||
DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
|
||||
DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
|
||||
lchan->nr, lchan->use_count); \
|
||||
bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0);
|
||||
|
||||
#define put_lchan(lchan) \
|
||||
do { lchan->use_count--; \
|
||||
DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
|
||||
DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
|
||||
lchan->nr, lchan->use_count); \
|
||||
} while(0);
|
||||
|
@ -110,6 +120,28 @@ struct gsm_bts_link {
|
|||
struct gsm_bts *bts;
|
||||
};
|
||||
|
||||
/* Real authentication information containing Ki */
|
||||
enum gsm_auth_algo {
|
||||
AUTH_ALGO_NONE,
|
||||
AUTH_ALGO_XOR,
|
||||
AUTH_ALGO_COMP128v1,
|
||||
};
|
||||
|
||||
struct gsm_auth_info {
|
||||
enum gsm_auth_algo auth_algo;
|
||||
unsigned int a3a8_ki_len;
|
||||
u_int8_t a3a8_ki[16];
|
||||
};
|
||||
|
||||
struct gsm_auth_tuple {
|
||||
int use_count;
|
||||
int key_seq;
|
||||
u_int8_t rand[16];
|
||||
u_int8_t sres[4];
|
||||
u_int8_t kc[8];
|
||||
};
|
||||
|
||||
|
||||
struct gsm_lchan;
|
||||
struct gsm_subscriber;
|
||||
struct gsm_mncc;
|
||||
|
@ -135,6 +167,20 @@ struct gsm_loc_updating_operation {
|
|||
unsigned int waiting_for_imei : 1;
|
||||
};
|
||||
|
||||
/* Maximum number of neighbor cells whose average we track */
|
||||
#define MAX_NEIGH_MEAS 10
|
||||
/* Maximum size of the averaging window for neighbor cells */
|
||||
#define MAX_WIN_NEIGH_AVG 10
|
||||
|
||||
/* processed neighbor measurements for one cell */
|
||||
struct neigh_meas_proc {
|
||||
u_int16_t arfcn;
|
||||
u_int8_t bsic;
|
||||
u_int8_t rxlev[MAX_WIN_NEIGH_AVG];
|
||||
unsigned int rxlev_cnt;
|
||||
u_int8_t last_seen_nr;
|
||||
};
|
||||
|
||||
#define MAX_A5_KEY_LEN (128/8)
|
||||
#define RSL_ENC_ALG_A5(x) (x+1)
|
||||
|
||||
|
@ -143,6 +189,15 @@ 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_ACT_REQ, /* channel activatin requested */
|
||||
LCHAN_S_ACTIVE, /* channel is active and operational */
|
||||
LCHAN_S_REL_REQ, /* channel release has been requested */
|
||||
LCHAN_S_INACTIVE, /* channel is set inactive */
|
||||
};
|
||||
|
||||
struct gsm_lchan {
|
||||
/* The TS that we're part of */
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
|
@ -154,6 +209,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;
|
||||
|
@ -163,6 +220,8 @@ struct gsm_lchan {
|
|||
u_int8_t key_len;
|
||||
u_int8_t key[MAX_A5_KEY_LEN];
|
||||
} encr;
|
||||
/* Are we part of a special "silent" call */
|
||||
int silent_call;
|
||||
|
||||
/* AMR bits */
|
||||
struct gsm48_multi_rate_conf mr_conf;
|
||||
|
@ -185,6 +244,24 @@ struct gsm_lchan {
|
|||
|
||||
/* use count. how many users use this channel */
|
||||
unsigned int use_count;
|
||||
|
||||
/* cache of last measurement reports on this lchan */
|
||||
struct gsm_meas_rep meas_rep[6];
|
||||
int meas_rep_idx;
|
||||
|
||||
/* table of neighbor cell measurements */
|
||||
struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS];
|
||||
|
||||
struct {
|
||||
u_int32_t bound_ip;
|
||||
u_int32_t connect_ip;
|
||||
u_int16_t bound_port;
|
||||
u_int16_t connect_port;
|
||||
u_int16_t conn_id;
|
||||
u_int8_t rtp_payload2;
|
||||
u_int8_t speech_mode;
|
||||
struct rtp_socket *rtp_socket;
|
||||
} abis_ip;
|
||||
};
|
||||
|
||||
struct gsm_e1_subslot {
|
||||
|
@ -212,13 +289,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];
|
||||
};
|
||||
|
@ -265,6 +335,15 @@ enum gsm_bts_type {
|
|||
GSM_BTS_TYPE_NANOBTS,
|
||||
};
|
||||
|
||||
struct gsm_bts_model {
|
||||
struct llist_head list;
|
||||
|
||||
enum gsm_bts_type type;
|
||||
const char *name;
|
||||
|
||||
struct tlv_definition nm_att_tlvdef;
|
||||
};
|
||||
|
||||
/**
|
||||
* A pending paging request
|
||||
*/
|
||||
|
@ -286,7 +365,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
|
||||
|
@ -333,11 +411,11 @@ struct gsm_bts {
|
|||
u_int8_t bsic;
|
||||
/* type of BTS */
|
||||
enum gsm_bts_type type;
|
||||
struct gsm_bts_model *model;
|
||||
enum gsm_band band;
|
||||
/* 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;
|
||||
|
||||
|
@ -354,8 +432,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;
|
||||
|
||||
|
@ -366,11 +442,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 {
|
||||
|
@ -399,12 +492,58 @@ struct gsm_bts {
|
|||
struct llist_head trx_list;
|
||||
};
|
||||
|
||||
/* Some statistics of our network */
|
||||
struct gsmnet_stats {
|
||||
struct {
|
||||
struct counter *total;
|
||||
struct counter *no_channel;
|
||||
} chreq;
|
||||
struct {
|
||||
struct counter *attempted;
|
||||
struct counter *no_channel; /* no channel available */
|
||||
struct counter *timeout; /* T3103 timeout */
|
||||
struct counter *completed; /* HO COMPL received */
|
||||
struct counter *failed; /* HO FAIL received */
|
||||
} handover;
|
||||
struct {
|
||||
struct counter *attach;
|
||||
struct counter *normal;
|
||||
struct counter *periodic;
|
||||
struct counter *detach;
|
||||
} loc_upd_type;
|
||||
struct {
|
||||
struct counter *reject;
|
||||
struct counter *accept;
|
||||
} loc_upd_resp;
|
||||
struct {
|
||||
struct counter *attempted;
|
||||
struct counter *detached;
|
||||
struct counter *completed;
|
||||
struct counter *expired;
|
||||
} paging;
|
||||
struct {
|
||||
struct counter *submitted; /* MO SMS submissions */
|
||||
struct counter *no_receiver;
|
||||
struct counter *delivered; /* MT SMS deliveries */
|
||||
struct counter *rp_err_mem;
|
||||
struct counter *rp_err_other;
|
||||
} sms;
|
||||
struct {
|
||||
struct counter *dialled; /* total number of dialled calls */
|
||||
struct counter *alerted; /* we alerted the other end */
|
||||
struct counter *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
|
||||
|
||||
struct gsm_network {
|
||||
/* global parameters */
|
||||
u_int16_t country_code;
|
||||
|
@ -412,8 +551,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;
|
||||
|
||||
/* layer 4 */
|
||||
int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
|
||||
|
@ -422,6 +581,24 @@ 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;
|
||||
};
|
||||
|
||||
#define SMS_HDR_SIZE 128
|
||||
|
@ -446,20 +623,30 @@ struct gsm_sms {
|
|||
char text[SMS_TEXT_SIZE];
|
||||
};
|
||||
|
||||
|
||||
struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_code,
|
||||
int (*mncc_recv)(struct gsm_network *, int, void *));
|
||||
struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
|
||||
u_int8_t tsc, u_int8_t bsic);
|
||||
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
|
||||
int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type);
|
||||
|
||||
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);
|
||||
enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
|
||||
const char *gsm_lchan_name(enum gsm_chan_t c);
|
||||
const char *gsm_lchant_name(enum gsm_chan_t c);
|
||||
const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
|
||||
char *gsm_trx_name(struct gsm_bts_trx *trx);
|
||||
char *gsm_ts_name(struct gsm_bts_trx_ts *ts);
|
||||
char *gsm_lchan_name(struct gsm_lchan *lchan);
|
||||
const char *gsm_lchans_name(enum gsm_lchan_state s);
|
||||
|
||||
enum gsm_e1_event {
|
||||
EVT_E1_NONE,
|
||||
|
@ -471,6 +658,7 @@ void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
|
|||
u_int8_t e1_ts, u_int8_t e1_ts_ss);
|
||||
enum gsm_bts_type parse_btstype(const char *arg);
|
||||
const char *btstype2str(enum gsm_bts_type type);
|
||||
struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr);
|
||||
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
|
||||
struct gsm_bts *start_bts);
|
||||
|
||||
|
@ -478,6 +666,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)
|
||||
{
|
||||
|
@ -506,4 +695,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
|
||||
|
|
|
@ -86,6 +86,8 @@ void subscr_put_channel(struct gsm_lchan *lchan);
|
|||
void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
int type, gsm_cbfn *cbfn, void *param);
|
||||
|
||||
char *subscr_name(struct gsm_subscriber *subscr);
|
||||
|
||||
/* internal */
|
||||
struct gsm_subscriber *subscr_alloc(void);
|
||||
extern struct llist_head active_subscribers;
|
||||
|
|
|
@ -33,5 +33,16 @@ 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);
|
||||
|
||||
/* According to GSM 04.08 Chapter 10.5.2.29 */
|
||||
static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; }
|
||||
static inline int rach_max_trans_raw2val(int raw) {
|
||||
const int tbl[4] = { 1, 2, 4, 7 };
|
||||
return tbl[raw & 3];
|
||||
}
|
||||
|
||||
void generate_backtrace();
|
||||
#endif
|
||||
|
|
|
@ -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 */
|
|
@ -2,6 +2,10 @@
|
|||
#define _IPACCESS_H
|
||||
|
||||
#include "e1_input.h"
|
||||
#include "linuxlist.h"
|
||||
|
||||
#define IPA_TCP_PORT_OML 3002
|
||||
#define IPA_TCP_PORT_RSL 3003
|
||||
|
||||
struct ipaccess_head {
|
||||
u_int16_t len; /* network byte order */
|
||||
|
@ -45,4 +49,54 @@ int ipaccess_rcvmsg_base(struct msgb *msg, struct bsc_fd *bfd);
|
|||
struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error);
|
||||
void ipaccess_prepend_header(struct msgb *msg, int proto);
|
||||
|
||||
/*
|
||||
* Firmware specific header
|
||||
*/
|
||||
struct sdp_firmware {
|
||||
char magic[4];
|
||||
char more_magic[2];
|
||||
u_int16_t more_more_magic;
|
||||
u_int32_t header_length;
|
||||
u_int32_t file_length;
|
||||
char sw_part[20];
|
||||
char text1[64];
|
||||
char time[12];
|
||||
char date[14];
|
||||
char text2[10];
|
||||
char version[20];
|
||||
u_int8_t dummy[2];
|
||||
u_int16_t part_length;
|
||||
/* stuff i don't know */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sdp_header_entry {
|
||||
u_int16_t something1;
|
||||
char text1[64];
|
||||
char time[12];
|
||||
char date[14];
|
||||
char text2[10];
|
||||
char version[20];
|
||||
u_int32_t length;
|
||||
u_int32_t addr1;
|
||||
u_int32_t addr2;
|
||||
u_int32_t start;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sdp_header_item {
|
||||
struct sdp_header_entry header_entry;
|
||||
struct llist_head entry;
|
||||
};
|
||||
|
||||
struct sdp_header {
|
||||
struct sdp_firmware firmware_info;
|
||||
|
||||
/* for more_magic a list of sdp_header_entry_list */
|
||||
struct llist_head header_list;
|
||||
|
||||
/* the entry of the sdp_header */
|
||||
struct llist_head entry;
|
||||
};
|
||||
|
||||
int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned base_offset, struct llist_head *list);
|
||||
|
||||
#endif /* _IPACCESS_H */
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
#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_int8_t neigh_idx;
|
||||
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 */
|
|
@ -0,0 +1,70 @@
|
|||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
|
||||
/*
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009 by On-Waves
|
||||
* 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 OPENBSC_MGCP_H
|
||||
#define OPENBSC_MGCP_H
|
||||
|
||||
#include "msgb.h"
|
||||
|
||||
#define RTP_PORT_DEFAULT 4000
|
||||
extern unsigned int rtp_base_port;
|
||||
|
||||
/**
|
||||
* Calculate the RTP audio port for the given multiplex
|
||||
* and the direction. This allows a semi static endpoint
|
||||
* to port calculation removing the need for the BSC
|
||||
* and the MediaGateway to communicate.
|
||||
*
|
||||
* Port usage explained:
|
||||
* base + (multiplex * 2) + 0 == local port to wait for network packets
|
||||
* base + (multiplex * 2) + 1 == local port for rtcp
|
||||
*
|
||||
* The above port will receive packets from the BTS that need
|
||||
* to be patched and forwarded to the network.
|
||||
* The above port will receive packets from the network that
|
||||
* need to be patched and forwarded to the BTS.
|
||||
*
|
||||
* We assume to have a static BTS IP address so we can differentiate
|
||||
* network and BTS.
|
||||
*
|
||||
*/
|
||||
static inline int rtp_calculate_port(int multiplex, int base)
|
||||
{
|
||||
return base + (multiplex * 2);
|
||||
}
|
||||
|
||||
int mgcp_parse_config(const char *config_file, struct gsm_network *dummy_network);
|
||||
|
||||
struct msgb *mgcp_handle_message(struct msgb *msg);
|
||||
struct msgb *mgcp_create_rsip(void);
|
||||
int mgcp_vty_init(void);
|
||||
|
||||
/* endpoint managed */
|
||||
#define MGCP_ENDP_CRCX 1
|
||||
#define MGCP_ENDP_DLCX 2
|
||||
#define MGCP_ENDP_MDCX 3
|
||||
|
||||
typedef int (*mgcp_change)(int endpoint, int state, int local_rtp, void *);
|
||||
void mgcp_set_change_cb(mgcp_change cb, void *data);
|
||||
|
||||
#endif
|
|
@ -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];
|
||||
|
|
|
@ -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,11 +40,13 @@ enum signal_subsystems {
|
|||
SS_LCHAN,
|
||||
SS_SUBSCR,
|
||||
SS_SCALL,
|
||||
SS_GLOBAL,
|
||||
};
|
||||
|
||||
/* SS_PAGING signals */
|
||||
enum signal_paging {
|
||||
S_PAGING_COMPLETED,
|
||||
S_PAGING_SUCCEEDED,
|
||||
S_PAGING_EXPIRED,
|
||||
};
|
||||
|
||||
/* SS_SMS signals */
|
||||
|
@ -58,6 +60,7 @@ enum signal_sms {
|
|||
/* SS_ABISIP signals */
|
||||
enum signal_abisip {
|
||||
S_ABISIP_CRCX_ACK,
|
||||
S_ABISIP_MDCX_ACK,
|
||||
S_ABISIP_DLCX_IND,
|
||||
};
|
||||
|
||||
|
@ -67,6 +70,9 @@ enum signal_nm {
|
|||
S_NM_FAIL_REP, /* GSM 12.21 failure event report */
|
||||
S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */
|
||||
S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */
|
||||
S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */
|
||||
S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */
|
||||
S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */
|
||||
S_NM_TEST_REP, /* GSM 12.21 Test Report */
|
||||
};
|
||||
|
||||
|
@ -78,12 +84,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 +106,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);
|
||||
|
||||
|
@ -110,6 +127,11 @@ struct scall_signal_data {
|
|||
void *data;
|
||||
};
|
||||
|
||||
struct ipacc_ack_signal_data {
|
||||
struct gsm_bts *bts;
|
||||
u_int8_t msg_type;
|
||||
};
|
||||
|
||||
/* Management */
|
||||
int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
|
||||
void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#ifndef _SILENT_CALL_H
|
||||
#define _SILENT_CALL_H
|
||||
|
||||
extern int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data);
|
||||
extern int gsm_silent_call_start(struct gsm_subscriber *subscr,
|
||||
void *data, int type);
|
||||
extern int gsm_silent_call_stop(struct gsm_subscriber *subscr);
|
||||
extern int silent_call_rx(struct msgb *msg);
|
||||
extern int silent_call_reroute(struct msgb *msg);
|
||||
|
||||
#endif /* _SILENT_CALL_H */
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef _STATISTICS_H
|
||||
#define _STATISTICS_H
|
||||
|
||||
struct counter {
|
||||
struct llist_head list;
|
||||
const char *name;
|
||||
const char *description;
|
||||
unsigned long value;
|
||||
};
|
||||
|
||||
static inline void counter_inc(struct counter *ctr)
|
||||
{
|
||||
ctr->value++;
|
||||
}
|
||||
|
||||
static inline unsigned long counter_get(struct counter *ctr)
|
||||
{
|
||||
return ctr->value;
|
||||
}
|
||||
|
||||
static inline void counter_reset(struct counter *ctr)
|
||||
{
|
||||
ctr->value = 0;
|
||||
}
|
||||
|
||||
struct counter *counter_alloc(const char *name);
|
||||
void counter_free(struct counter *ctr);
|
||||
|
||||
int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data);
|
||||
|
||||
#endif /* _STATISTICS_H */
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef _SYSTEM_INFO_H
|
||||
#define _SYSTEM_INFO_H
|
||||
|
||||
int gsm_generate_si(u_int8_t *output, struct gsm_bts *bts, int type);
|
||||
|
||||
#endif
|
|
@ -22,7 +22,7 @@
|
|||
#define TELNET_INTERFACE_H
|
||||
|
||||
#include "gsm_data.h"
|
||||
#include "linuxlist.h"
|
||||
#include "debug.h"
|
||||
#include "select.h"
|
||||
|
||||
#include <vty/vty.h>
|
||||
|
@ -35,6 +35,7 @@ struct telnet_connection {
|
|||
struct gsm_network *network;
|
||||
struct bsc_fd fd;
|
||||
struct vty *vty;
|
||||
struct debug_target *dbg;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -184,6 +185,7 @@ struct tlv_p_entry {
|
|||
};
|
||||
|
||||
enum tlv_type {
|
||||
TLV_TYPE_NONE,
|
||||
TLV_TYPE_FIXED,
|
||||
TLV_TYPE_T,
|
||||
TLV_TYPE_TV,
|
||||
|
@ -212,6 +214,8 @@ int tlv_parse_one(u_int8_t *o_tag, u_int16_t *o_len, const u_int8_t **o_val,
|
|||
const u_int8_t *buf, int buf_len);
|
||||
int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
|
||||
const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2);
|
||||
/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
|
||||
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);
|
||||
|
||||
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
|
||||
#define TLVP_LEN(x, y) (x)->lv[y].len
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -106,6 +106,7 @@ enum node_type {
|
|||
TRX_NODE,
|
||||
TS_NODE,
|
||||
SUBSCR_NODE,
|
||||
MGCP_NODE,
|
||||
};
|
||||
|
||||
/* Node which has some commands and prompt string and configuration
|
||||
|
@ -355,4 +356,6 @@ void host_config_set(const char *);
|
|||
|
||||
void print_version(const char *);
|
||||
|
||||
extern void *tall_vty_cmd_ctx;
|
||||
|
||||
#endif /* _ZEBRA_COMMAND_H */
|
||||
|
|
|
@ -59,4 +59,6 @@ vector vector_copy(vector v);
|
|||
void *vector_lookup(vector, unsigned int);
|
||||
void *vector_lookup_ensure(vector, unsigned int);
|
||||
|
||||
extern void *tall_vty_vec_ctx;
|
||||
|
||||
#endif /* _ZEBRA_VECTOR_H */
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall
|
||||
|
||||
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
|
||||
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
|
||||
isdnsync bsc_mgcp ipaccess-proxy
|
||||
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a libsccp.a
|
||||
noinst_HEADERS = vty/cardshell.h
|
||||
|
||||
|
@ -10,11 +11,14 @@ 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
|
||||
talloc_ctx.c system_information.c bitvec.c rest_octets.c \
|
||||
rtp_proxy.c statistics.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \
|
||||
bts_unknown.c
|
||||
|
||||
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.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 comp128.c
|
||||
|
||||
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
|
||||
|
||||
|
@ -24,11 +28,17 @@ bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c
|
|||
bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
|
||||
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \
|
||||
select.c timer.c rs232.c tlv_parser.c signal.c talloc.c
|
||||
select.c timer.c rs232.c tlv_parser.c signal.c talloc.c \
|
||||
bts_siemens_bs11.c
|
||||
|
||||
ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c
|
||||
ipaccess_find_SOURCES = ipaccess/ipaccess-find.c select.c timer.c
|
||||
|
||||
ipaccess_config_SOURCES = ipaccess-config.c
|
||||
ipaccess_config_SOURCES = ipaccess/ipaccess-config.c ipaccess/ipaccess-firmware.c
|
||||
ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
|
||||
isdnsync_SOURCES = isdnsync.c
|
||||
|
||||
bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c msgb.c talloc.c debug.c select.c timer.c telnet_interface.c
|
||||
bsc_mgcp_LDADD = libvty.a
|
||||
|
||||
ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c msgb.c select.c talloc.c debug.c timer.c
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
#define OM_ALLOC_SIZE 1024
|
||||
#define OM_HEADROOM_SIZE 128
|
||||
#define IPACC_SEGMENT_SIZE 245
|
||||
|
||||
/* unidirectional messages from BTS to BSC */
|
||||
static const enum abis_nm_msgtype reports[] = {
|
||||
|
@ -263,7 +264,7 @@ static const enum abis_nm_attr nm_att_settable[] = {
|
|||
NM_ATT_MEAS_TYPE,
|
||||
};
|
||||
|
||||
static const struct tlv_definition nm_att_tlvdef = {
|
||||
const struct tlv_definition nm_att_tlvdef = {
|
||||
.def = {
|
||||
[NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
|
||||
[NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
|
||||
|
@ -329,77 +330,6 @@ static const struct tlv_definition nm_att_tlvdef = {
|
|||
[NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
|
||||
[NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
|
||||
/* BS11 specifics */
|
||||
[NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV },
|
||||
[0xd5] = { TLV_TYPE_TLV },
|
||||
[0xa8] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV },
|
||||
[NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
|
||||
[NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_PLL] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV },
|
||||
[NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV },
|
||||
/* ip.access specifics */
|
||||
[NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
|
||||
[NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
|
||||
[NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, },
|
||||
[NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
|
||||
[NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
|
||||
[NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
|
||||
[NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
|
||||
[NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
|
||||
//[0x95] = { TLV_TYPE_FIXED, 2 },
|
||||
[0x85] = { TLV_TYPE_TV },
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -422,9 +352,11 @@ int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len)
|
||||
int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_t *buf, int len)
|
||||
{
|
||||
return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0);
|
||||
if (!bts->model)
|
||||
return -EIO;
|
||||
return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0);
|
||||
}
|
||||
|
||||
static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size)
|
||||
|
@ -540,11 +472,11 @@ static const char *obj_class_name(u_int8_t oc)
|
|||
const char *nm_opstate_name(u_int8_t os)
|
||||
{
|
||||
switch (os) {
|
||||
case 1:
|
||||
case NM_OPSTATE_DISABLED:
|
||||
return "Disabled";
|
||||
case 2:
|
||||
case NM_OPSTATE_ENABLED:
|
||||
return "Enabled";
|
||||
case 0xff:
|
||||
case NM_OPSTATE_NULL:
|
||||
return "NULL";
|
||||
default:
|
||||
return "RFU";
|
||||
|
@ -598,6 +530,13 @@ const char *nm_adm_name(u_int8_t adm)
|
|||
}
|
||||
}
|
||||
|
||||
int nm_is_running(struct gsm_nm_state *s) {
|
||||
return (s->operational == NM_OPSTATE_ENABLED) && (
|
||||
(s->availability == NM_AVSTATE_OK) ||
|
||||
(s->availability == 0xff)
|
||||
);
|
||||
}
|
||||
|
||||
static void debugp_foh(struct abis_om_fom_hdr *foh)
|
||||
{
|
||||
DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
|
||||
|
@ -635,7 +574,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 +610,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 +621,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 +658,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 +677,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;
|
||||
|
@ -793,7 +732,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
|
|||
|
||||
new_state = *nm_state;
|
||||
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh));
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) {
|
||||
new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE);
|
||||
DEBUGPC(DNM, "OP_STATE=%s ", nm_opstate_name(new_state.operational));
|
||||
|
@ -805,19 +744,25 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
|
|||
new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
|
||||
DEBUGPC(DNM, "AVAIL=%s(%02x) ", nm_avail_name(new_state.availability),
|
||||
new_state.availability);
|
||||
}
|
||||
} else
|
||||
new_state.availability = 0xff;
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
|
||||
new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
|
||||
DEBUGPC(DNM, "ADM=%2s ", nm_adm_name(new_state.administrative));
|
||||
}
|
||||
DEBUGPC(DNM, "\n");
|
||||
|
||||
if (memcmp(&new_state, nm_state, sizeof(new_state))) {
|
||||
if ((new_state.administrative != 0 && nm_state->administrative == 0) ||
|
||||
new_state.operational != nm_state->operational ||
|
||||
new_state.availability != nm_state->availability) {
|
||||
/* Update the operational state of a given object in our in-memory data
|
||||
* structures and send an event to the higher layer */
|
||||
void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst);
|
||||
rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state);
|
||||
*nm_state = new_state;
|
||||
nm_state->operational = new_state.operational;
|
||||
nm_state->availability = new_state.availability;
|
||||
if (nm_state->administrative == 0)
|
||||
nm_state->administrative = new_state.administrative;
|
||||
}
|
||||
#if 0
|
||||
if (op_state == 1) {
|
||||
|
@ -839,7 +784,7 @@ static int rx_fail_evt_rep(struct msgb *mb)
|
|||
|
||||
DEBUGPC(DNM, "Failure Event Report ");
|
||||
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE))
|
||||
DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE)));
|
||||
|
@ -933,7 +878,7 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
|
|||
if (nack)
|
||||
return ret;
|
||||
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG);
|
||||
sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG);
|
||||
if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) {
|
||||
|
@ -967,7 +912,7 @@ static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb)
|
|||
struct tlv_parsed tp;
|
||||
u_int8_t adm_state;
|
||||
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -983,7 +928,7 @@ static int abis_nm_rx_lmt_event(struct msgb *mb)
|
|||
struct tlv_parsed tp;
|
||||
|
||||
DEBUGP(DNM, "LMT Event ");
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) &&
|
||||
TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) {
|
||||
u_int8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION);
|
||||
|
@ -1029,7 +974,7 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
|
|||
else
|
||||
DEBUGPC(DNM, "NACK 0x%02x ", mt);
|
||||
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, "CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
|
@ -1043,13 +988,11 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
|
|||
/* check if last message is to be acked */
|
||||
if (is_ack_nack(nmh->last_msgtype)) {
|
||||
if (mt == MT_ACK(nmh->last_msgtype)) {
|
||||
fprintf(stderr, "received ACK (0x%x)\n",
|
||||
foh->msg_type);
|
||||
DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type);
|
||||
/* we got our ACK, continue sending the next msg */
|
||||
} else if (mt == MT_NACK(nmh->last_msgtype)) {
|
||||
/* we got a NACK, signal this to the caller */
|
||||
fprintf(stderr, "received NACK (0x%x)\n",
|
||||
foh->msg_type);
|
||||
DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type);
|
||||
/* FIXME: somehow signal this to the caller */
|
||||
} else {
|
||||
/* really strange things happen */
|
||||
|
@ -1071,6 +1014,12 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
|
|||
case NM_MT_CONN_MDROP_LINK_ACK:
|
||||
DEBUGP(DNM, "CONN MDROP LINK ACK\n");
|
||||
break;
|
||||
case NM_MT_IPACC_RESTART_ACK:
|
||||
dispatch_signal(SS_NM, S_NM_IPACC_RESTART_ACK, NULL);
|
||||
break;
|
||||
case NM_MT_IPACC_RESTART_NACK:
|
||||
dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1088,8 +1037,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 +1055,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 +1068,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 +1086,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;
|
||||
}
|
||||
|
@ -1216,6 +1165,22 @@ struct abis_nm_sw {
|
|||
|
||||
static struct abis_nm_sw g_sw;
|
||||
|
||||
static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg)
|
||||
{
|
||||
if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) {
|
||||
msgb_v_put(msg, NM_ATT_SW_DESCR);
|
||||
msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
|
||||
msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
|
||||
sw->file_version);
|
||||
} else if (sw->bts->type == GSM_BTS_TYPE_BS11) {
|
||||
msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
|
||||
msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
|
||||
sw->file_version);
|
||||
} else {
|
||||
LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* 6.2.1 / 8.3.1: Load Data Initiate */
|
||||
static int sw_load_init(struct abis_nm_sw *sw)
|
||||
{
|
||||
|
@ -1227,11 +1192,8 @@ static int sw_load_init(struct abis_nm_sw *sw)
|
|||
fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class,
|
||||
sw->obj_instance[0], sw->obj_instance[1],
|
||||
sw->obj_instance[2]);
|
||||
|
||||
/* FIXME: this is BS11 specific format */
|
||||
msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
|
||||
msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
|
||||
sw->file_version);
|
||||
|
||||
sw_add_file_id_and_ver(sw, msg);
|
||||
msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size);
|
||||
|
||||
return abis_nm_sendmsg(sw->bts, msg);
|
||||
|
@ -1289,7 +1251,24 @@ static int sw_load_segment(struct abis_nm_sw *sw)
|
|||
/* we only now know the exact length for the OM hdr */
|
||||
len = strlen(line_buf)+2;
|
||||
break;
|
||||
case GSM_BTS_TYPE_NANOBTS: {
|
||||
static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough);
|
||||
len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE);
|
||||
if (len < 0) {
|
||||
perror("read failed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (len != IPACC_SEGMENT_SIZE)
|
||||
sw->last_seg = 1;
|
||||
|
||||
++sw->seg_in_window;
|
||||
msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const u_int8_t *) seg_buf);
|
||||
len += 3;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n");
|
||||
/* FIXME: Other BTS types */
|
||||
return -1;
|
||||
}
|
||||
|
@ -1313,11 +1292,7 @@ static int sw_load_end(struct abis_nm_sw *sw)
|
|||
sw->obj_instance[0], sw->obj_instance[1],
|
||||
sw->obj_instance[2]);
|
||||
|
||||
/* FIXME: this is BS11 specific format */
|
||||
msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
|
||||
msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
|
||||
sw->file_version);
|
||||
|
||||
sw_add_file_id_and_ver(sw, msg);
|
||||
return abis_nm_sendmsg(sw->bts, msg);
|
||||
}
|
||||
|
||||
|
@ -1341,6 +1316,59 @@ static int sw_activate(struct abis_nm_sw *sw)
|
|||
return abis_nm_sendmsg(sw->bts, msg);
|
||||
}
|
||||
|
||||
struct sdp_firmware {
|
||||
char magic[4];
|
||||
char more_magic[4];
|
||||
unsigned int header_length;
|
||||
unsigned int file_length;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static int parse_sdp_header(struct abis_nm_sw *sw)
|
||||
{
|
||||
struct sdp_firmware firmware_header;
|
||||
int rc;
|
||||
struct stat stat;
|
||||
|
||||
rc = read(sw->fd, &firmware_header, sizeof(firmware_header));
|
||||
if (rc != sizeof(firmware_header)) {
|
||||
LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(firmware_header.magic, " SDP", 4) != 0) {
|
||||
LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (firmware_header.more_magic[0] != 0x10 ||
|
||||
firmware_header.more_magic[1] != 0x02 ||
|
||||
firmware_header.more_magic[2] != 0x00 ||
|
||||
firmware_header.more_magic[3] != 0x00) {
|
||||
LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (fstat(sw->fd, &stat) == -1) {
|
||||
LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ntohl(firmware_header.file_length) != stat.st_size) {
|
||||
LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* go back to the start as we checked the whole filesize.. */
|
||||
lseek(sw->fd, 0l, SEEK_SET);
|
||||
LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n"
|
||||
"There might be checksums in the file that are not\n"
|
||||
"verified and incomplete firmware might be flashed.\n"
|
||||
"There is absolutely no WARRANTY that flashing will\n"
|
||||
"work.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
|
||||
{
|
||||
char file_id[12+1];
|
||||
|
@ -1372,6 +1400,19 @@ static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
|
|||
/* rewind to start of file */
|
||||
rewind(sw->stream);
|
||||
break;
|
||||
case GSM_BTS_TYPE_NANOBTS:
|
||||
/* TODO: extract that from the filename or content */
|
||||
rc = parse_sdp_header(sw);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Could not parse the ipaccess SDP header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy((char *)sw->file_id, "id");
|
||||
sw->file_id_len = 3;
|
||||
strcpy((char *)sw->file_version, "version");
|
||||
sw->file_version_len = 8;
|
||||
break;
|
||||
default:
|
||||
/* We don't know how to treat them yet */
|
||||
close(sw->fd);
|
||||
|
@ -1470,6 +1511,12 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
|
|||
rc = sw_load_end(sw);
|
||||
}
|
||||
break;
|
||||
case NM_MT_LOAD_ABORT:
|
||||
if (sw->cbfn)
|
||||
sw->cbfn(GSM_HOOK_NM_SWLOAD,
|
||||
NM_MT_LOAD_ABORT, mb,
|
||||
sw->cb_data, NULL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SW_STATE_WAIT_ENDACK:
|
||||
|
@ -1483,6 +1530,7 @@ static int abis_nm_rcvmsg_sw(struct msgb *mb)
|
|||
sw->cbfn(GSM_HOOK_NM_SWLOAD,
|
||||
NM_MT_LOAD_END_ACK, mb,
|
||||
sw->cb_data, NULL);
|
||||
rc = 0;
|
||||
break;
|
||||
case NM_MT_LOAD_END_NACK:
|
||||
if (sw->forced) {
|
||||
|
@ -1559,10 +1607,26 @@ int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
|
|||
return -EBUSY;
|
||||
|
||||
sw->bts = bts;
|
||||
sw->obj_class = NM_OC_SITE_MANAGER;
|
||||
sw->obj_instance[0] = 0xff;
|
||||
sw->obj_instance[1] = 0xff;
|
||||
sw->obj_instance[2] = 0xff;
|
||||
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
sw->obj_class = NM_OC_SITE_MANAGER;
|
||||
sw->obj_instance[0] = 0xff;
|
||||
sw->obj_instance[1] = 0xff;
|
||||
sw->obj_instance[2] = 0xff;
|
||||
break;
|
||||
case GSM_BTS_TYPE_NANOBTS:
|
||||
sw->obj_class = NM_OC_BASEB_TRANSC;
|
||||
sw->obj_instance[0] = 0x00;
|
||||
sw->obj_instance[1] = 0x00;
|
||||
sw->obj_instance[2] = 0xff;
|
||||
break;
|
||||
case GSM_BTS_TYPE_UNKNOWN:
|
||||
default:
|
||||
LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n");
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
sw->window_size = win_size;
|
||||
sw->state = SW_STATE_WAIT_INITACK;
|
||||
sw->cbfn = cbfn;
|
||||
|
@ -1590,7 +1654,10 @@ int abis_nm_software_load_status(struct gsm_bts *bts)
|
|||
return rc;
|
||||
}
|
||||
|
||||
percent = (ftell(sw->stream) * 100) / st.st_size;
|
||||
if (sw->stream)
|
||||
percent = (ftell(sw->stream) * 100) / st.st_size;
|
||||
else
|
||||
percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size;
|
||||
return percent;
|
||||
}
|
||||
|
||||
|
@ -1755,7 +1822,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 +1869,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;
|
||||
}
|
||||
|
@ -2203,10 +2348,18 @@ int abis_nm_bs11_get_cclk(struct gsm_bts *bts)
|
|||
}
|
||||
|
||||
//static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 };
|
||||
static const u_int8_t bs11_logon_c8[] = { 0x02 };
|
||||
static const u_int8_t bs11_logon_c9[] = "FACTORY";
|
||||
|
||||
int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
|
||||
{
|
||||
return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on);
|
||||
}
|
||||
|
||||
int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on)
|
||||
{
|
||||
return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on);
|
||||
}
|
||||
|
||||
int abis_nm_bs11_logon(struct gsm_bts *bts, u_int8_t level, const char *name, int on)
|
||||
{
|
||||
struct abis_om_hdr *oh;
|
||||
struct msgb *msg = nm_msgb_alloc();
|
||||
|
@ -2217,15 +2370,15 @@ int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
|
|||
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
|
||||
if (on) {
|
||||
u_int8_t len = 3*2 + sizeof(bdt)
|
||||
+ sizeof(bs11_logon_c8) + sizeof(bs11_logon_c9);
|
||||
+ 1 + strlen(name);
|
||||
fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON,
|
||||
NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
|
||||
msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME,
|
||||
sizeof(bdt), (u_int8_t *) &bdt);
|
||||
msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV,
|
||||
sizeof(bs11_logon_c8), bs11_logon_c8);
|
||||
1, &level);
|
||||
msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME,
|
||||
sizeof(bs11_logon_c9), bs11_logon_c9);
|
||||
strlen(name), (u_int8_t *)name);
|
||||
} else {
|
||||
fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF,
|
||||
NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
|
||||
|
@ -2273,6 +2426,27 @@ int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked)
|
|||
return abis_nm_sendmsg(bts, msg);
|
||||
}
|
||||
|
||||
/* Set the calibration value of the PLL (work value/set value)
|
||||
* It depends on the login which one is changed */
|
||||
int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value)
|
||||
{
|
||||
struct abis_om_hdr *oh;
|
||||
struct msgb *msg;
|
||||
u_int8_t tlv_value[2];
|
||||
|
||||
msg = nm_msgb_alloc();
|
||||
oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
|
||||
fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11,
|
||||
BS11_OBJ_TRX1, 0x00, 0x00);
|
||||
|
||||
tlv_value[0] = value>>8;
|
||||
tlv_value[1] = value&0xff;
|
||||
|
||||
msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value);
|
||||
|
||||
return abis_nm_sendmsg(bts, msg);
|
||||
}
|
||||
|
||||
int abis_nm_bs11_get_state(struct gsm_bts *bts)
|
||||
{
|
||||
return __simple_cmd(bts, NM_MT_BS11_GET_STATE);
|
||||
|
@ -2519,14 +2693,15 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
|||
struct abis_om_fom_hdr *foh;
|
||||
u_int8_t idstrlen = oh->data[0];
|
||||
struct tlv_parsed tp;
|
||||
struct ipacc_ack_signal_data signal;
|
||||
|
||||
if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) {
|
||||
DEBUGP(DNM, "id string is not com.ipaccess !?!\n");
|
||||
LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, msg->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
|
||||
debugp_foh(foh);
|
||||
|
||||
|
@ -2549,7 +2724,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
|||
DEBUGPC(DNM, "\n");
|
||||
break;
|
||||
case NM_MT_IPACC_RSL_CONNECT_NACK:
|
||||
DEBUGPC(DNM, "RSL CONNECT NACK ");
|
||||
LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
|
@ -2561,35 +2736,35 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
|||
/* FIXME: decode and show the actual attributes */
|
||||
break;
|
||||
case NM_MT_IPACC_SET_NVATTR_NACK:
|
||||
DEBUGPC(DNM, "SET NVATTR NACK ");
|
||||
LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, " CAUSE=%s\n",
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
DEBUGPC(DNM, "\n");
|
||||
LOGPC(DNM, LOGL_ERROR, "\n");
|
||||
break;
|
||||
case NM_MT_IPACC_GET_NVATTR_ACK:
|
||||
DEBUGPC(DNM, "GET NVATTR ACK\n");
|
||||
/* FIXME: decode and show the actual attributes */
|
||||
break;
|
||||
case NM_MT_IPACC_GET_NVATTR_NACK:
|
||||
DEBUGPC(DNM, "GET NVATTR NACK ");
|
||||
LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, " CAUSE=%s\n",
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
DEBUGPC(DNM, "\n");
|
||||
LOGPC(DNM, LOGL_ERROR, "\n");
|
||||
break;
|
||||
case NM_MT_IPACC_SET_ATTR_ACK:
|
||||
DEBUGPC(DNM, "SET ATTR ACK\n");
|
||||
break;
|
||||
case NM_MT_IPACC_SET_ATTR_NACK:
|
||||
DEBUGPC(DNM, "SET ATTR NACK ");
|
||||
LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, " CAUSE=%s\n",
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
DEBUGPC(DNM, "\n");
|
||||
LOGPC(DNM, LOGL_ERROR, "\n");
|
||||
break;
|
||||
default:
|
||||
DEBUGPC(DNM, "unknown\n");
|
||||
|
@ -2601,7 +2776,14 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
|||
case NM_MT_IPACC_RSL_CONNECT_NACK:
|
||||
case NM_MT_IPACC_SET_NVATTR_NACK:
|
||||
case NM_MT_IPACC_GET_NVATTR_NACK:
|
||||
dispatch_signal(SS_NM, S_NM_IPACC_NACK, &foh->msg_type);
|
||||
signal.bts = msg->trx->bts;
|
||||
signal.msg_type = foh->msg_type;
|
||||
dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal);
|
||||
break;
|
||||
case NM_MT_IPACC_SET_NVATTR_ACK:
|
||||
signal.bts = msg->trx->bts;
|
||||
signal.msg_type = foh->msg_type;
|
||||
dispatch_signal(SS_NM, S_NM_IPACC_ACK, &signal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -2648,11 +2830,11 @@ int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
|
|||
}
|
||||
|
||||
/* set some attributes in NVRAM */
|
||||
int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
|
||||
int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
|
||||
int attr_len)
|
||||
{
|
||||
return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_NVATTR,
|
||||
NM_OC_BASEB_TRANSC, 0, 0, 0xff, attr,
|
||||
return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR,
|
||||
NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr,
|
||||
attr_len);
|
||||
}
|
||||
|
||||
|
@ -2699,6 +2881,19 @@ int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class,
|
|||
attr, attr_len);
|
||||
}
|
||||
|
||||
void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
|
||||
{
|
||||
int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED;
|
||||
|
||||
trx->nm_state.administrative = new_state;
|
||||
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);
|
||||
}
|
||||
|
||||
static const char *ipacc_testres_names[] = {
|
||||
[NM_IPACC_TESTRES_SUCCESS] = "SUCCESS",
|
||||
[NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -51,7 +51,7 @@ enum bs11cfg_state {
|
|||
STATE_QUERY,
|
||||
};
|
||||
static enum bs11cfg_state bs11cfg_state = STATE_NONE;
|
||||
static char *command;
|
||||
static char *command, *value;
|
||||
struct timer_list status_timer;
|
||||
|
||||
static const u_int8_t obj_li_attr[] = {
|
||||
|
@ -71,6 +71,13 @@ static const char *trx1_password = "1111111111";
|
|||
|
||||
static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
|
||||
|
||||
static struct debug_target *stderr_target;
|
||||
|
||||
/* dummy function to keep gsm_data.c happy */
|
||||
struct counter *counter_alloc(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int handle_serial_msg(struct msgb *rx_msg);
|
||||
|
||||
|
@ -533,6 +540,21 @@ static int handle_state_resp(enum abis_bs11_phase state)
|
|||
sleep(1);
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "pll-setvalue")) {
|
||||
abis_nm_bs11_set_pll(g_bts, atoi(value));
|
||||
sleep(1);
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "pll-workvalue")) {
|
||||
/* To set the work value we need to login as FIELD */
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
sleep(1);
|
||||
abis_nm_bs11_infield_logon(g_bts, 1);
|
||||
sleep(1);
|
||||
abis_nm_bs11_set_pll(g_bts, atoi(value));
|
||||
sleep(1);
|
||||
abis_nm_bs11_infield_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "oml-tei")) {
|
||||
abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML);
|
||||
command = NULL;
|
||||
|
@ -627,7 +649,7 @@ int handle_serial_msg(struct msgb *rx_msg)
|
|||
exit(0);
|
||||
break;
|
||||
case NM_MT_BS11_GET_STATE_ACK:
|
||||
rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh));
|
||||
print_state(&tp);
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) &&
|
||||
TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1)
|
||||
|
@ -635,7 +657,7 @@ int handle_serial_msg(struct msgb *rx_msg)
|
|||
break;
|
||||
case NM_MT_GET_ATTR_RESP:
|
||||
printf("\n%sATTRIBUTES:\n", obj_name(foh));
|
||||
abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
|
||||
abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh));
|
||||
rc = print_attr(&tp);
|
||||
//hexdump(foh->data, oh->length-sizeof(*foh));
|
||||
break;
|
||||
|
@ -699,25 +721,27 @@ static void print_help(void)
|
|||
printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n");
|
||||
printf("\t-s --software <file>\t\tSpecify Software file\n");
|
||||
printf("\t-S --safety <file>\t\tSpecify Safety Load file\n");
|
||||
printf("\t-d --delay <ms>\t\tSpecify delay in milliseconds\n");
|
||||
printf("\t-d --delay <ms>\t\t\tSpecify delay in milliseconds\n");
|
||||
printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n");
|
||||
printf("\t-w --win-size <num>\t\tSpecify Window Size\n");
|
||||
printf("\t-f --forced\t\t\tForce Software Load\n");
|
||||
printf("\nSupported commands:\n");
|
||||
printf("\tquery\t\tQuery the BS-11 about serial number and configuration\n");
|
||||
printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n");
|
||||
printf("\tresconnect\tReconnect A-bis link (go into normal state)\n");
|
||||
printf("\trestart\t\tRestart the BTS\n");
|
||||
printf("\tsoftware\tDownload Software (only in administrative state)\n");
|
||||
printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
|
||||
printf("\tdelete-trx1\tDelete objects for TRX1\n");
|
||||
printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n");
|
||||
printf("\tpll-standalone\tSet the PLL to be in standalone mode\n");
|
||||
printf("\toml-tei\tSet OML E1 TS and TEI\n");
|
||||
printf("\tbport0-star\tSet BPORT0 line config to star\n");
|
||||
printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n");
|
||||
printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n");
|
||||
printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n");
|
||||
printf("\trestart\t\t\tRestart the BTS\n");
|
||||
printf("\tsoftware\t\tDownload Software (only in administrative state)\n");
|
||||
printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
|
||||
printf("\tdelete-trx1\t\tDelete objects for TRX1\n");
|
||||
printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n");
|
||||
printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n");
|
||||
printf("\tpll-setvalue <value>\tSet the PLL set value\n");
|
||||
printf("\tpll-workvalue <value>\tSet the PLL work value\n");
|
||||
printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n");
|
||||
printf("\tbport0-star\t\tSet BPORT0 line config to star\n");
|
||||
printf("\tbport0-multiport\tSet BPORT0 line config to multiport\n");
|
||||
printf("\tcreate-bport1\tCreate BPORT1 object\n");
|
||||
printf("\tdelete-bport1\tDelete BPORT1 object\n");
|
||||
printf("\tcreate-bport1\t\tCreate BPORT1 object\n");
|
||||
printf("\tdelete-bport1\t\tDelete BPORT1 object\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
|
@ -754,7 +778,7 @@ static void handle_options(int argc, char **argv)
|
|||
serial_port = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
debug_parse_category_mask(optarg);
|
||||
debug_parse_category_mask(stderr_target, optarg);
|
||||
break;
|
||||
case 's':
|
||||
fname_software = optarg;
|
||||
|
@ -784,6 +808,9 @@ static void handle_options(int argc, char **argv)
|
|||
}
|
||||
if (optind < argc)
|
||||
command = argv[optind];
|
||||
if (optind+1 < argc)
|
||||
value = argv[optind+1];
|
||||
|
||||
}
|
||||
|
||||
static int num_sigint;
|
||||
|
@ -807,7 +834,12 @@ int main(int argc, char **argv)
|
|||
struct gsm_network *gsmnet;
|
||||
int rc;
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
handle_options(argc, argv);
|
||||
bts_model_bs11_init();
|
||||
|
||||
gsmnet = gsm_network_init(1, 1, NULL);
|
||||
if (!gsmnet) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* A hackish minimal BSC (+MSC +HLR) implementation */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
|
@ -35,12 +35,18 @@
|
|||
#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 */
|
||||
static struct debug_target *stderr_target;
|
||||
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;
|
||||
|
||||
|
||||
/* timer to store statistics */
|
||||
#define DB_SYNC_INTERVAL 60, 0
|
||||
static struct timer_list db_sync_timer;
|
||||
|
||||
extern int bsc_bootstrap_network(int (*mmc_rev)(struct gsm_network *, int, void *),
|
||||
const char *cfg_file);
|
||||
|
@ -72,9 +78,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 +94,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'},
|
||||
|
@ -106,10 +111,10 @@ static void handle_options(int argc, char** argv)
|
|||
print_help();
|
||||
exit(0);
|
||||
case 's':
|
||||
debug_use_color(0);
|
||||
debug_set_use_color(stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
debug_parse_category_mask(optarg);
|
||||
debug_parse_category_mask(stderr_target, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
database_name = strdup(optarg);
|
||||
|
@ -117,14 +122,11 @@ 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;
|
||||
case 'T':
|
||||
debug_timestamp(1);
|
||||
debug_set_print_timestamp(stderr_target, 1);
|
||||
break;
|
||||
case 'P':
|
||||
ipacc_rtp_direct = 0;
|
||||
|
@ -136,6 +138,7 @@ static void handle_options(int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
extern void *tall_vty_ctx;
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
fprintf(stdout, "signal %u received\n", signal);
|
||||
|
@ -143,6 +146,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;
|
||||
|
@ -150,21 +154,53 @@ static void signal_handler(int signal)
|
|||
/* in case of abort, we want to obtain a talloc report
|
||||
* and then return to the caller, who will abort the process */
|
||||
case SIGUSR1:
|
||||
talloc_report(tall_vty_ctx, stderr);
|
||||
talloc_report_full(tall_bsc_ctx, stderr);
|
||||
break;
|
||||
case SIGUSR2:
|
||||
talloc_report_full(tall_vty_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* timer handling */
|
||||
static int _db_store_counter(struct counter *counter, void *data)
|
||||
{
|
||||
return db_store_counter(counter);
|
||||
}
|
||||
|
||||
static void db_sync_timer_cb(void *data)
|
||||
{
|
||||
/* store counters to database and re-schedule */
|
||||
counters_for_each(_db_store_counter, NULL);
|
||||
bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
|
||||
}
|
||||
|
||||
extern int bts_model_unknown_init(void);
|
||||
extern int bts_model_bs11_init(void);
|
||||
extern int bts_model_nanobts_init(void);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
debug_init();
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
|
||||
talloc_ctx_init();
|
||||
on_dso_load_token();
|
||||
on_dso_load_rrlp();
|
||||
on_dso_load_ho_dec();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
|
||||
bts_model_unknown_init();
|
||||
bts_model_bs11_init();
|
||||
bts_model_nanobts_init();
|
||||
|
||||
/* enable filters */
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
|
||||
/* parse options */
|
||||
handle_options(argc, argv);
|
||||
|
@ -184,6 +220,11 @@ int main(int argc, char **argv)
|
|||
}
|
||||
printf("DB: Database prepared.\n");
|
||||
|
||||
/* setup the timer */
|
||||
db_sync_timer.cb = db_sync_timer_cb;
|
||||
db_sync_timer.data = NULL;
|
||||
bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL);
|
||||
|
||||
rc = bsc_bootstrap_network(mncc_recv, config_file);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
@ -191,9 +232,12 @@ int main(int argc, char **argv)
|
|||
signal(SIGINT, &signal_handler);
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
while (1) {
|
||||
bsc_upqueue(bsc_gsmnet);
|
||||
debug_reset_context();
|
||||
bsc_select_main(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
@ -356,8 +355,10 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
|||
switch (obj_class) {
|
||||
case NM_OC_SITE_MANAGER:
|
||||
bts = container_of(obj, struct gsm_bts, site_mgr);
|
||||
if (new_state->operational == 2 &&
|
||||
new_state->availability == NM_AVSTATE_OK)
|
||||
if ((new_state->operational == 2 &&
|
||||
new_state->availability == NM_AVSTATE_OK) ||
|
||||
(new_state->operational == 1 &&
|
||||
new_state->availability == NM_AVSTATE_OFF_LINE))
|
||||
abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
|
||||
break;
|
||||
case NM_OC_BTS:
|
||||
|
@ -391,38 +392,11 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
|||
break;
|
||||
case NM_OC_RADIO_CARRIER:
|
||||
trx = obj;
|
||||
if (new_state->operational == 1 &&
|
||||
new_state->availability == NM_AVSTATE_OFF_LINE) {
|
||||
/* Patch ARFCN into radio attribute */
|
||||
nanobts_attr_radio[5] &= 0xf0;
|
||||
nanobts_attr_radio[5] |= trx->arfcn >> 8;
|
||||
nanobts_attr_radio[6] = trx->arfcn & 0xff;
|
||||
abis_nm_set_radio_attr(trx, nanobts_attr_radio,
|
||||
sizeof(nanobts_attr_radio));
|
||||
abis_nm_chg_adm_state(trx->bts, obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff,
|
||||
NM_STATE_UNLOCKED);
|
||||
abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
|
||||
trx->nr, 0xff);
|
||||
}
|
||||
if (new_state->operational == 1 &&
|
||||
new_state->availability == NM_AVSTATE_OK)
|
||||
abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
|
||||
trx->nr, 0xff);
|
||||
break;
|
||||
case NM_OC_BASEB_TRANSC:
|
||||
trx = container_of(obj, struct gsm_bts_trx, bb_transc);
|
||||
if (new_state->operational == 1 &&
|
||||
new_state->availability == NM_AVSTATE_DEPENDENCY) {
|
||||
abis_nm_chg_adm_state(trx->bts, obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff,
|
||||
NM_STATE_UNLOCKED);
|
||||
abis_nm_opstart(trx->bts, obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff);
|
||||
/* TRX software is active, tell it to initiate RSL Link */
|
||||
abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -436,8 +410,43 @@ static int sw_activ_rep(struct msgb *mb)
|
|||
struct gsm_bts *bts = mb->trx->bts;
|
||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr);
|
||||
|
||||
if (!trx)
|
||||
return -EINVAL;
|
||||
|
||||
switch (foh->obj_class) {
|
||||
case NM_OC_BASEB_TRANSC:
|
||||
abis_nm_chg_adm_state(trx->bts, foh->obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff,
|
||||
NM_STATE_UNLOCKED);
|
||||
abis_nm_opstart(trx->bts, foh->obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff);
|
||||
/* TRX software is active, tell it to initiate RSL Link */
|
||||
abis_nm_ipaccess_rsl_connect(trx, 0, 3003, trx->rsl_tei);
|
||||
break;
|
||||
case NM_OC_RADIO_CARRIER: {
|
||||
/*
|
||||
* Locking the radio carrier will make it go
|
||||
* offline again and we would come here. The
|
||||
* framework should determine that there was
|
||||
* no change and avoid recursion.
|
||||
*
|
||||
* This code is here to make sure that on start
|
||||
* a TRX remains locked.
|
||||
*/
|
||||
int rc_state = trx->nm_state.administrative;
|
||||
/* Patch ARFCN into radio attribute */
|
||||
nanobts_attr_radio[5] &= 0xf0;
|
||||
nanobts_attr_radio[5] |= trx->arfcn >> 8;
|
||||
nanobts_attr_radio[6] = trx->arfcn & 0xff;
|
||||
abis_nm_set_radio_attr(trx, nanobts_attr_radio,
|
||||
sizeof(nanobts_attr_radio));
|
||||
abis_nm_chg_adm_state(trx->bts, foh->obj_class,
|
||||
trx->bts->bts_nr, trx->nr, 0xff,
|
||||
rc_state);
|
||||
abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr,
|
||||
trx->nr, 0xff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -446,7 +455,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);
|
||||
}
|
||||
|
@ -549,7 +558,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;
|
||||
}
|
||||
|
@ -568,6 +577,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 */
|
||||
|
@ -613,7 +623,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:
|
||||
|
@ -623,13 +633,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);
|
||||
|
@ -660,219 +670,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -906,81 +752,13 @@ 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) "
|
||||
"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);
|
||||
LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) "
|
||||
"on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n",
|
||||
trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code,
|
||||
bsc_gsmnet->network_code, trx->bts->location_area_code,
|
||||
trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc);
|
||||
set_system_infos(trx);
|
||||
}
|
||||
|
||||
|
@ -1000,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:
|
||||
|
@ -1013,33 +791,50 @@ 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.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;
|
||||
|
@ -1062,7 +857,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* ip.access nanoBTS specific code */
|
||||
|
||||
/* (C) 2009-2010 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/tlv.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
|
||||
static struct gsm_bts_model model_nanobts = {
|
||||
.type = GSM_BTS_TYPE_NANOBTS,
|
||||
.nm_att_tlvdef = {
|
||||
.def = {
|
||||
/* ip.access specifics */
|
||||
[NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 },
|
||||
[NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, },
|
||||
[NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_TV, },
|
||||
[NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 },
|
||||
[NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 },
|
||||
[NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 },
|
||||
[NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 },
|
||||
[NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V },
|
||||
[NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int bts_model_nanobts_init(void)
|
||||
{
|
||||
return gsm_bts_model_register(&model_nanobts);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/* Siemens BS-11 specific code */
|
||||
|
||||
/* (C) 2009-2010 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/tlv.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
|
||||
static struct gsm_bts_model model_bs11 = {
|
||||
.type = GSM_BTS_TYPE_BS11,
|
||||
.nm_att_tlvdef = {
|
||||
.def = {
|
||||
[NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV },
|
||||
/* BS11 specifics */
|
||||
[NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV },
|
||||
[0xd5] = { TLV_TYPE_TLV },
|
||||
[0xa8] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV },
|
||||
[NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
|
||||
[NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
|
||||
[NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_PLL] = { TLV_TYPE_TLV },
|
||||
[NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV },
|
||||
[NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV },
|
||||
[0x95] = { TLV_TYPE_FIXED, 2 },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int bts_model_bs11_init(void)
|
||||
{
|
||||
return gsm_bts_model_register(&model_bs11);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* Generic BTS - VTY code tries to allocate this BTS before type is known */
|
||||
|
||||
/* (C) 2010 by Daniel Willmann <daniel@totalueberwachung.de>
|
||||
*
|
||||
* 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/tlv.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
|
||||
static struct gsm_bts_model model_unknown = {
|
||||
.type = GSM_BTS_TYPE_UNKNOWN,
|
||||
.nm_att_tlvdef = {
|
||||
.def = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int bts_model_unknown_init(void)
|
||||
{
|
||||
return gsm_bts_model_register(&model_unknown);
|
||||
}
|
|
@ -35,6 +35,29 @@
|
|||
|
||||
static void auto_release_channel(void *_lchan);
|
||||
|
||||
static int ts_is_usable(struct gsm_bts_trx_ts *ts)
|
||||
{
|
||||
/* FIXME: How does this behave for BS-11 ? */
|
||||
if (is_ipaccess_bts(ts->trx->bts)) {
|
||||
if (!nm_is_running(&ts->nm_state))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int trx_is_usable(struct gsm_bts_trx *trx)
|
||||
{
|
||||
/* FIXME: How does this behave for BS-11 ? */
|
||||
if (is_ipaccess_bts(trx->bts)) {
|
||||
if (!nm_is_running(&trx->nm_state) ||
|
||||
!nm_is_running(&trx->bb_transc.nm_state))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan)
|
||||
{
|
||||
|
@ -63,6 +86,9 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
|
|||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
int from, to;
|
||||
|
||||
if (!trx_is_usable(trx))
|
||||
continue;
|
||||
|
||||
/* the following constraints are pure policy,
|
||||
* no requirement to put this restriction in place */
|
||||
if (trx == bts->c0) {
|
||||
|
@ -97,6 +123,10 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
|
|||
|
||||
for (j = from; j <= to; j++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[j];
|
||||
|
||||
if (!ts_is_usable(ts))
|
||||
continue;
|
||||
|
||||
if (ts->pchan == GSM_PCHAN_NONE) {
|
||||
ts->pchan = pchan;
|
||||
/* set channel attribute on OML */
|
||||
|
@ -121,6 +151,7 @@ static const u_int8_t subslots_per_pchan[] = {
|
|||
[GSM_PCHAN_TCH_F] = 1,
|
||||
[GSM_PCHAN_TCH_H] = 2,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
|
||||
/* FIXME: what about dynamic TCH_F_TCH_H ? */
|
||||
};
|
||||
|
||||
static struct gsm_lchan *
|
||||
|
@ -129,14 +160,20 @@ _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
|
|||
struct gsm_bts_trx_ts *ts;
|
||||
int j, ss;
|
||||
|
||||
if (!trx_is_usable(trx))
|
||||
return NULL;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
ts = &trx->ts[j];
|
||||
if (!ts_is_usable(ts))
|
||||
continue;
|
||||
if (ts->pchan != pchan)
|
||||
continue;
|
||||
/* check if all sub-slots are allocated yet */
|
||||
for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) {
|
||||
struct gsm_lchan *lc = &ts->lchan[ss];
|
||||
if (lc->type == GSM_LCHAN_NONE)
|
||||
if (lc->type == GSM_LCHAN_NONE &&
|
||||
lc->state == LCHAN_S_NONE)
|
||||
return lc;
|
||||
}
|
||||
}
|
||||
|
@ -203,9 +240,14 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
|||
break;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H);
|
||||
/* If we don't have TCH/H available, fall-back to TCH/F */
|
||||
if (!lchan) {
|
||||
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
|
||||
type = GSM_LCHAN_TCH_F;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
|
||||
LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);
|
||||
}
|
||||
|
||||
if (lchan) {
|
||||
|
@ -230,6 +272,8 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
|||
/* Free a logical channel */
|
||||
void lchan_free(struct gsm_lchan *lchan)
|
||||
{
|
||||
int i;
|
||||
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
if (lchan->subscr) {
|
||||
subscr_put(lchan->subscr);
|
||||
|
@ -244,6 +288,18 @@ void lchan_free(struct gsm_lchan *lchan)
|
|||
|
||||
/* stop the timer */
|
||||
bsc_del_timer(&lchan->release_timer);
|
||||
bsc_del_timer(&lchan->T3101);
|
||||
|
||||
/* clear cached measuement reports */
|
||||
lchan->meas_rep_idx = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) {
|
||||
lchan->meas_rep[i].flags = 0;
|
||||
lchan->meas_rep[i].nr = 0;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
|
||||
lchan->neigh_meas[i].arfcn = 0;
|
||||
|
||||
lchan->silent_call = 0;
|
||||
|
||||
/* FIXME: ts_free() the timeslot, if we're the last logical
|
||||
* channel using it */
|
||||
|
@ -262,11 +318,11 @@ int lchan_auto_release(struct gsm_lchan *lchan)
|
|||
}
|
||||
|
||||
/* spoofed? message */
|
||||
if (lchan->use_count < 0) {
|
||||
DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count);
|
||||
}
|
||||
if (lchan->use_count < 0)
|
||||
LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
|
||||
lchan->use_count);
|
||||
|
||||
DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
|
||||
DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
|
||||
rsl_release_request(lchan, 0);
|
||||
return 1;
|
||||
}
|
||||
|
@ -312,3 +368,51 @@ struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
int i;
|
||||
|
||||
/* skip administratively deactivated tranxsceivers */
|
||||
if (!nm_is_running(&trx->nm_state) ||
|
||||
!nm_is_running(&trx->bb_transc.nm_state))
|
||||
continue;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[i];
|
||||
struct load_counter *pl = &cl->pchan[ts->pchan];
|
||||
int j;
|
||||
|
||||
/* skip administratively deactivated timeslots */
|
||||
if (!nm_is_running(&ts->nm_state))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) {
|
||||
struct gsm_lchan *lchan = &ts->lchan[j];
|
||||
|
||||
pl->total++;
|
||||
|
||||
switch (lchan->state) {
|
||||
case LCHAN_S_NONE:
|
||||
break;
|
||||
default:
|
||||
pl->used++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void network_chan_load(struct pchan_load *pl, struct gsm_network *net)
|
||||
{
|
||||
struct gsm_bts *bts;
|
||||
|
||||
memset(pl, 0, sizeof(*pl));
|
||||
|
||||
llist_for_each_entry(bts, &net->bts_list, list)
|
||||
bts_chan_load(pl, bts);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* COMP128 implementation
|
||||
*
|
||||
*
|
||||
* This code is inspired by original code from :
|
||||
* Marc Briceno <marc@scard.org>, Ian Goldberg <iang@cs.berkeley.edu>,
|
||||
* and David Wagner <daw@cs.berkeley.edu>
|
||||
*
|
||||
* But it has been fully rewritten from various PDFs found online describing
|
||||
* the algorithm because the licence of the code referenced above was unclear.
|
||||
* A comment snippet from the original code is included below, it describes
|
||||
* where the doc came from and how the algorithm was reverse engineered.
|
||||
*
|
||||
*
|
||||
* (C) 2009 by Sylvain Munaut <tnt@246tNt.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* --- SNIP ---
|
||||
*
|
||||
* This code derived from a leaked document from the GSM standards.
|
||||
* Some missing pieces were filled in by reverse-engineering a working SIM.
|
||||
* We have verified that this is the correct COMP128 algorithm.
|
||||
*
|
||||
* The first page of the document identifies it as
|
||||
* _Technical Information: GSM System Security Study_.
|
||||
* 10-1617-01, 10th June 1988.
|
||||
* The bottom of the title page is marked
|
||||
* Racal Research Ltd.
|
||||
* Worton Drive, Worton Grange Industrial Estate,
|
||||
* Reading, Berks. RG2 0SB, England.
|
||||
* Telephone: Reading (0734) 868601 Telex: 847152
|
||||
* The relevant bits are in Part I, Section 20 (pages 66--67). Enjoy!
|
||||
*
|
||||
* Note: There are three typos in the spec (discovered by
|
||||
* reverse-engineering).
|
||||
* First, "z = (2 * x[n] + x[n]) mod 2^(9-j)" should clearly read
|
||||
* "z = (2 * x[m] + x[n]) mod 2^(9-j)".
|
||||
* Second, the "k" loop in the "Form bits from bytes" section is severely
|
||||
* botched: the k index should run only from 0 to 3, and clearly the range
|
||||
* on "the (8-k)th bit of byte j" is also off (should be 0..7, not 1..8,
|
||||
* to be consistent with the subsequent section).
|
||||
* Third, SRES is taken from the first 8 nibbles of x[], not the last 8 as
|
||||
* claimed in the document. (And the document doesn't specify how Kc is
|
||||
* derived, but that was also easily discovered with reverse engineering.)
|
||||
* All of these typos have been corrected in the following code.
|
||||
*
|
||||
* --- /SNIP ---
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* The compression tables (just copied ...) */
|
||||
static const u_int8_t table_0[512] = {
|
||||
102, 177, 186, 162, 2, 156, 112, 75, 55, 25, 8, 12, 251, 193, 246, 188,
|
||||
109, 213, 151, 53, 42, 79, 191, 115, 233, 242, 164, 223, 209, 148, 108, 161,
|
||||
252, 37, 244, 47, 64, 211, 6, 237, 185, 160, 139, 113, 76, 138, 59, 70,
|
||||
67, 26, 13, 157, 63, 179, 221, 30, 214, 36, 166, 69, 152, 124, 207, 116,
|
||||
247, 194, 41, 84, 71, 1, 49, 14, 95, 35, 169, 21, 96, 78, 215, 225,
|
||||
182, 243, 28, 92, 201, 118, 4, 74, 248, 128, 17, 11, 146, 132, 245, 48,
|
||||
149, 90, 120, 39, 87, 230, 106, 232, 175, 19, 126, 190, 202, 141, 137, 176,
|
||||
250, 27, 101, 40, 219, 227, 58, 20, 51, 178, 98, 216, 140, 22, 32, 121,
|
||||
61, 103, 203, 72, 29, 110, 85, 212, 180, 204, 150, 183, 15, 66, 172, 196,
|
||||
56, 197, 158, 0, 100, 45, 153, 7, 144, 222, 163, 167, 60, 135, 210, 231,
|
||||
174, 165, 38, 249, 224, 34, 220, 229, 217, 208, 241, 68, 206, 189, 125, 255,
|
||||
239, 54, 168, 89, 123, 122, 73, 145, 117, 234, 143, 99, 129, 200, 192, 82,
|
||||
104, 170, 136, 235, 93, 81, 205, 173, 236, 94, 105, 52, 46, 228, 198, 5,
|
||||
57, 254, 97, 155, 142, 133, 199, 171, 187, 50, 65, 181, 127, 107, 147, 226,
|
||||
184, 218, 131, 33, 77, 86, 31, 44, 88, 62, 238, 18, 24, 43, 154, 23,
|
||||
80, 159, 134, 111, 9, 114, 3, 91, 16, 130, 83, 10, 195, 240, 253, 119,
|
||||
177, 102, 162, 186, 156, 2, 75, 112, 25, 55, 12, 8, 193, 251, 188, 246,
|
||||
213, 109, 53, 151, 79, 42, 115, 191, 242, 233, 223, 164, 148, 209, 161, 108,
|
||||
37, 252, 47, 244, 211, 64, 237, 6, 160, 185, 113, 139, 138, 76, 70, 59,
|
||||
26, 67, 157, 13, 179, 63, 30, 221, 36, 214, 69, 166, 124, 152, 116, 207,
|
||||
194, 247, 84, 41, 1, 71, 14, 49, 35, 95, 21, 169, 78, 96, 225, 215,
|
||||
243, 182, 92, 28, 118, 201, 74, 4, 128, 248, 11, 17, 132, 146, 48, 245,
|
||||
90, 149, 39, 120, 230, 87, 232, 106, 19, 175, 190, 126, 141, 202, 176, 137,
|
||||
27, 250, 40, 101, 227, 219, 20, 58, 178, 51, 216, 98, 22, 140, 121, 32,
|
||||
103, 61, 72, 203, 110, 29, 212, 85, 204, 180, 183, 150, 66, 15, 196, 172,
|
||||
197, 56, 0, 158, 45, 100, 7, 153, 222, 144, 167, 163, 135, 60, 231, 210,
|
||||
165, 174, 249, 38, 34, 224, 229, 220, 208, 217, 68, 241, 189, 206, 255, 125,
|
||||
54, 239, 89, 168, 122, 123, 145, 73, 234, 117, 99, 143, 200, 129, 82, 192,
|
||||
170, 104, 235, 136, 81, 93, 173, 205, 94, 236, 52, 105, 228, 46, 5, 198,
|
||||
254, 57, 155, 97, 133, 142, 171, 199, 50, 187, 181, 65, 107, 127, 226, 147,
|
||||
218, 184, 33, 131, 86, 77, 44, 31, 62, 88, 18, 238, 43, 24, 23, 154,
|
||||
159, 80, 111, 134, 114, 9, 91, 3, 130, 16, 10, 83, 240, 195, 119, 253,
|
||||
}, table_1[256] = {
|
||||
19, 11, 80, 114, 43, 1, 69, 94, 39, 18, 127, 117, 97, 3, 85, 43,
|
||||
27, 124, 70, 83, 47, 71, 63, 10, 47, 89, 79, 4, 14, 59, 11, 5,
|
||||
35, 107, 103, 68, 21, 86, 36, 91, 85, 126, 32, 50, 109, 94, 120, 6,
|
||||
53, 79, 28, 45, 99, 95, 41, 34, 88, 68, 93, 55, 110, 125, 105, 20,
|
||||
90, 80, 76, 96, 23, 60, 89, 64, 121, 56, 14, 74, 101, 8, 19, 78,
|
||||
76, 66, 104, 46, 111, 50, 32, 3, 39, 0, 58, 25, 92, 22, 18, 51,
|
||||
57, 65, 119, 116, 22, 109, 7, 86, 59, 93, 62, 110, 78, 99, 77, 67,
|
||||
12, 113, 87, 98, 102, 5, 88, 33, 38, 56, 23, 8, 75, 45, 13, 75,
|
||||
95, 63, 28, 49, 123, 120, 20, 112, 44, 30, 15, 98, 106, 2, 103, 29,
|
||||
82, 107, 42, 124, 24, 30, 41, 16, 108, 100, 117, 40, 73, 40, 7, 114,
|
||||
82, 115, 36, 112, 12, 102, 100, 84, 92, 48, 72, 97, 9, 54, 55, 74,
|
||||
113, 123, 17, 26, 53, 58, 4, 9, 69, 122, 21, 118, 42, 60, 27, 73,
|
||||
118, 125, 34, 15, 65, 115, 84, 64, 62, 81, 70, 1, 24, 111, 121, 83,
|
||||
104, 81, 49, 127, 48, 105, 31, 10, 6, 91, 87, 37, 16, 54, 116, 126,
|
||||
31, 38, 13, 0, 72, 106, 77, 61, 26, 67, 46, 29, 96, 37, 61, 52,
|
||||
101, 17, 44, 108, 71, 52, 66, 57, 33, 51, 25, 90, 2, 119, 122, 35,
|
||||
}, table_2[128] = {
|
||||
52, 50, 44, 6, 21, 49, 41, 59, 39, 51, 25, 32, 51, 47, 52, 43,
|
||||
37, 4, 40, 34, 61, 12, 28, 4, 58, 23, 8, 15, 12, 22, 9, 18,
|
||||
55, 10, 33, 35, 50, 1, 43, 3, 57, 13, 62, 14, 7, 42, 44, 59,
|
||||
62, 57, 27, 6, 8, 31, 26, 54, 41, 22, 45, 20, 39, 3, 16, 56,
|
||||
48, 2, 21, 28, 36, 42, 60, 33, 34, 18, 0, 11, 24, 10, 17, 61,
|
||||
29, 14, 45, 26, 55, 46, 11, 17, 54, 46, 9, 24, 30, 60, 32, 0,
|
||||
20, 38, 2, 30, 58, 35, 1, 16, 56, 40, 23, 48, 13, 19, 19, 27,
|
||||
31, 53, 47, 38, 63, 15, 49, 5, 37, 53, 25, 36, 63, 29, 5, 7,
|
||||
}, table_3[64] = {
|
||||
1, 5, 29, 6, 25, 1, 18, 23, 17, 19, 0, 9, 24, 25, 6, 31,
|
||||
28, 20, 24, 30, 4, 27, 3, 13, 15, 16, 14, 18, 4, 3, 8, 9,
|
||||
20, 0, 12, 26, 21, 8, 28, 2, 29, 2, 15, 7, 11, 22, 14, 10,
|
||||
17, 21, 12, 30, 26, 27, 16, 31, 11, 7, 13, 23, 10, 5, 22, 19,
|
||||
}, table_4[32] = {
|
||||
15, 12, 10, 4, 1, 14, 11, 7, 5, 0, 14, 7, 1, 2, 13, 8,
|
||||
10, 3, 4, 9, 6, 0, 3, 2, 5, 6, 8, 9, 11, 13, 15, 12,
|
||||
};
|
||||
|
||||
static const u_int8_t *_comp128_table[5] = { table_0, table_1, table_2, table_3, table_4 };
|
||||
|
||||
|
||||
static inline void
|
||||
_comp128_compression_round(u_int8_t *x, int n, const u_int8_t *tbl)
|
||||
{
|
||||
int i, j, m, a, b, y, z;
|
||||
m = 4 - n;
|
||||
for (i=0; i<(1<<n); i++)
|
||||
for (j=0; j<(1<<m); j++) {
|
||||
a = j + i * (2<<m);
|
||||
b = a + (1<<m);
|
||||
y = (x[a] + (x[b]<<1)) & ((32<<m)-1);
|
||||
z = ((x[a]<<1) + x[b]) & ((32<<m)-1);
|
||||
x[a] = tbl[y];
|
||||
x[b] = tbl[z];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_comp128_compression(u_int8_t *x)
|
||||
{
|
||||
int n;
|
||||
for (n=0; n<5; n++)
|
||||
_comp128_compression_round(x, n, _comp128_table[n]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_comp128_bitsfrombytes(u_int8_t *x, u_int8_t *bits)
|
||||
{
|
||||
int i;
|
||||
memset(bits, 0x00, 128);
|
||||
for (i=0; i<128; i++)
|
||||
if (x[i>>2] & (1<<(3-(i&3))))
|
||||
bits[i] = 1;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_comp128_permutation(u_int8_t *x, u_int8_t *bits)
|
||||
{
|
||||
int i;
|
||||
memset(&x[16], 0x00, 16);
|
||||
for (i=0; i<128; i++)
|
||||
x[(i>>3)+16] |= bits[(i*17) & 127] << (7-(i&7));
|
||||
}
|
||||
|
||||
void
|
||||
comp128(u_int8_t *ki, u_int8_t *rand, u_int8_t *sres, u_int8_t *kc)
|
||||
{
|
||||
int i;
|
||||
u_int8_t x[32], bits[128];
|
||||
|
||||
/* x[16-31] = RAND */
|
||||
memcpy(&x[16], rand, 16);
|
||||
|
||||
/* Round 1-7 */
|
||||
for (i=0; i<7; i++) {
|
||||
/* x[0-15] = Ki */
|
||||
memcpy(x, ki, 16);
|
||||
|
||||
/* Compression */
|
||||
_comp128_compression(x);
|
||||
|
||||
/* FormBitFromBytes */
|
||||
_comp128_bitsfrombytes(x, bits);
|
||||
|
||||
/* Permutation */
|
||||
_comp128_permutation(x, bits);
|
||||
}
|
||||
|
||||
/* Round 8 (final) */
|
||||
/* x[0-15] = Ki */
|
||||
memcpy(x, ki, 16);
|
||||
|
||||
/* Compression */
|
||||
_comp128_compression(x);
|
||||
|
||||
/* Output stage */
|
||||
for (i=0; i<8; i+=2)
|
||||
sres[i>>1] = x[i]<<4 | x[i+1];
|
||||
|
||||
for (i=0; i<12; i+=2)
|
||||
kc[i>>1] = (x[i + 18] << 6) |
|
||||
(x[i + 19] << 2) |
|
||||
(x[i + 20] >> 2);
|
||||
|
||||
kc[6] = (x[30]<<6) | (x[31]<<2);
|
||||
kc[7] = 0;
|
||||
}
|
||||
|
474
openbsc/src/db.c
474
openbsc/src/db.c
|
@ -25,6 +25,7 @@
|
|||
#include <openbsc/db.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/statistics.h>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
|
@ -117,12 +118,35 @@ static char *create_stmts[] = {
|
|||
"subscriber_id INTEGER NOT NULL, "
|
||||
"apdu BLOB "
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS Counters ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"timestamp TIMESTAMP NOT NULL, "
|
||||
"value INTEGER NOT NULL, "
|
||||
"name TEXT NOT NULL "
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS AuthKeys ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"subscriber_id INTEGER UNIQUE NOT NULL, "
|
||||
"algorithm_id INTEGER NOT NULL, "
|
||||
"a3a8_ki BLOB "
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS AuthTuples ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"subscriber_id NUMERIC UNIQUE NOT NULL, "
|
||||
"issued TIMESTAMP NOT NULL, "
|
||||
"use_count INTEGER NOT NULL DEFAULT 0, "
|
||||
"key_seq INTEGER NOT NULL, "
|
||||
"rand BLOB NOT NULL, "
|
||||
"sres BLOB NOT NULL, "
|
||||
"kc BLOB NOT NULL "
|
||||
")",
|
||||
};
|
||||
|
||||
void db_error_func(dbi_conn conn, void* data) {
|
||||
const char* msg;
|
||||
void db_error_func(dbi_conn conn, void *data)
|
||||
{
|
||||
const char *msg;
|
||||
dbi_conn_error(conn, &msg);
|
||||
printf("DBI: %s\n", msg);
|
||||
LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg);
|
||||
}
|
||||
|
||||
static int check_db_revision(void)
|
||||
|
@ -149,11 +173,13 @@ static int check_db_revision(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int db_init(const char *name) {
|
||||
int db_init(const char *name)
|
||||
{
|
||||
dbi_initialize(NULL);
|
||||
|
||||
conn = dbi_conn_new("sqlite3");
|
||||
if (conn==NULL) {
|
||||
printf("DB: Failed to create connection.\n");
|
||||
if (conn == NULL) {
|
||||
LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -186,21 +212,23 @@ out_err:
|
|||
}
|
||||
|
||||
|
||||
int db_prepare() {
|
||||
int db_prepare()
|
||||
{
|
||||
dbi_result result;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
|
||||
result = dbi_conn_query(conn, create_stmts[i]);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to create some table.\n");
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR,
|
||||
"Failed to create some table.\n");
|
||||
return 1;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
}
|
||||
|
||||
if (check_db_revision() < 0) {
|
||||
fprintf(stderr, "Database schema revision invalid, "
|
||||
LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, "
|
||||
"please update your database schema\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -208,7 +236,8 @@ int db_prepare() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int db_fini() {
|
||||
int db_fini()
|
||||
{
|
||||
dbi_conn_close(conn);
|
||||
dbi_shutdown();
|
||||
|
||||
|
@ -219,10 +248,10 @@ int db_fini() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
|
||||
struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi)
|
||||
{
|
||||
dbi_result result;
|
||||
struct gsm_subscriber* subscr;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* Is this subscriber known in the db? */
|
||||
subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
|
||||
|
@ -230,11 +259,10 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
|
|||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE Subscriber set updated = datetime('now') "
|
||||
"WHERE imsi = %s " , imsi);
|
||||
if (result==NULL) {
|
||||
printf("DB: failed to update timestamp\n");
|
||||
} else {
|
||||
if (!result)
|
||||
LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n");
|
||||
else
|
||||
dbi_result_free(result);
|
||||
}
|
||||
return subscr;
|
||||
}
|
||||
|
||||
|
@ -249,14 +277,13 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
|
|||
"(%s, datetime('now'), datetime('now')) ",
|
||||
imsi
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to create Subscriber by IMSI.\n");
|
||||
}
|
||||
if (!result)
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n");
|
||||
subscr->net = net;
|
||||
subscr->id = dbi_conn_sequence_last(conn, NULL);
|
||||
strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1);
|
||||
dbi_result_free(result);
|
||||
printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
|
||||
LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
|
||||
db_subscriber_alloc_exten(subscr);
|
||||
return subscr;
|
||||
}
|
||||
|
@ -265,7 +292,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;
|
||||
|
||||
|
@ -288,7 +315,9 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
|
|||
if (string)
|
||||
strncpy(equip->imei, string, sizeof(equip->imei));
|
||||
|
||||
cm1 = dbi_result_get_uint(result, "classmark1") & 0xff;
|
||||
string = dbi_result_get_string(result, "classmark1");
|
||||
if (string)
|
||||
cm1 = atoi(string) & 0xff;
|
||||
equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
|
||||
|
||||
equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
|
||||
|
@ -307,6 +336,214 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_authinfo_by_subscr(struct gsm_auth_info *ainfo,
|
||||
struct gsm_subscriber *subscr)
|
||||
{
|
||||
dbi_result result;
|
||||
const unsigned char *a3a8_ki;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM AuthKeys WHERE subscriber_id=%u",
|
||||
subscr->id);
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
if (!dbi_result_next_row(result)) {
|
||||
dbi_result_free(result);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id");
|
||||
ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki");
|
||||
a3a8_ki = dbi_result_get_binary(result, "a3a8_ki");
|
||||
if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki))
|
||||
ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki_len);
|
||||
memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len);
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int set_authinfo_for_subscr(struct gsm_auth_info *ainfo,
|
||||
struct gsm_subscriber *subscr)
|
||||
{
|
||||
dbi_result result;
|
||||
struct gsm_auth_info ainfo_old;
|
||||
int rc, upd;
|
||||
unsigned char *ki_str;
|
||||
|
||||
/* Deletion ? */
|
||||
if (ainfo == NULL) {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"DELETE FROM AuthKeys WHERE subscriber_id=%u",
|
||||
subscr->id);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if already existing */
|
||||
rc = get_authinfo_by_subscr(&ainfo_old, subscr);
|
||||
if (rc && rc != -ENOENT)
|
||||
return rc;
|
||||
upd = rc ? 0 : 1;
|
||||
|
||||
/* Update / Insert */
|
||||
dbi_conn_quote_binary_copy(conn,
|
||||
ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str);
|
||||
|
||||
if (!upd) {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO AuthKeys "
|
||||
"(subscriber_id, algorithm_id, a3a8_ki) "
|
||||
"VALUES (%u, %u, %s)",
|
||||
subscr->id, ainfo->auth_algo, ki_str);
|
||||
} else {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE AuthKeys "
|
||||
"SET algorithm_id=%u, a3a8_ki=%s "
|
||||
"WHERE subscriber_id=%u",
|
||||
ainfo->auth_algo, ki_str, subscr->id);
|
||||
}
|
||||
|
||||
free(ki_str);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_authtuple_by_subscr(struct gsm_auth_tuple *atuple,
|
||||
struct gsm_subscriber *subscr)
|
||||
{
|
||||
dbi_result result;
|
||||
int len;
|
||||
const unsigned char *blob;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM AuthTuples WHERE subscriber_id=%u",
|
||||
subscr->id);
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
if (!dbi_result_next_row(result)) {
|
||||
dbi_result_free(result);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
memset(atuple, 0, sizeof(atuple));
|
||||
|
||||
atuple->use_count = dbi_result_get_ulonglong(result, "use_count");
|
||||
atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq");
|
||||
|
||||
len = dbi_result_get_field_length(result, "rand");
|
||||
if (len != sizeof(atuple->rand))
|
||||
goto err_size;
|
||||
|
||||
blob = dbi_result_get_binary(result, "rand");
|
||||
memcpy(atuple->rand, blob, len);
|
||||
|
||||
len = dbi_result_get_field_length(result, "sres");
|
||||
if (len != sizeof(atuple->sres))
|
||||
goto err_size;
|
||||
|
||||
blob = dbi_result_get_binary(result, "sres");
|
||||
memcpy(atuple->sres, blob, len);
|
||||
|
||||
len = dbi_result_get_field_length(result, "kc");
|
||||
if (len != sizeof(atuple->kc))
|
||||
goto err_size;
|
||||
|
||||
blob = dbi_result_get_binary(result, "kc");
|
||||
memcpy(atuple->kc, blob, len);
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
|
||||
err_size:
|
||||
dbi_result_free(result);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple,
|
||||
struct gsm_subscriber *subscr)
|
||||
{
|
||||
dbi_result result;
|
||||
int rc, upd;
|
||||
struct gsm_auth_tuple atuple_old;
|
||||
unsigned char *rand_str, *sres_str, *kc_str;
|
||||
|
||||
/* Deletion ? */
|
||||
if (atuple == NULL) {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"DELETE FROM AuthTuples WHERE subscriber_id=%u",
|
||||
subscr->id);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if already existing */
|
||||
rc = get_authtuple_by_subscr(&atuple_old, subscr);
|
||||
if (rc && rc != -ENOENT)
|
||||
return rc;
|
||||
upd = rc ? 0 : 1;
|
||||
|
||||
/* Update / Insert */
|
||||
dbi_conn_quote_binary_copy(conn,
|
||||
atuple->rand, sizeof(atuple->rand), &rand_str);
|
||||
dbi_conn_quote_binary_copy(conn,
|
||||
atuple->sres, sizeof(atuple->sres), &sres_str);
|
||||
dbi_conn_quote_binary_copy(conn,
|
||||
atuple->kc, sizeof(atuple->kc), &kc_str);
|
||||
|
||||
if (!upd) {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO AuthTuples "
|
||||
"(subscriber_id, issued, use_count, "
|
||||
"key_seq, rand, sres, kc) "
|
||||
"VALUES (%u, datetime('now'), %u, "
|
||||
"%u, %s, %s, %s ) ",
|
||||
subscr->id, atuple->use_count, atuple->key_seq,
|
||||
rand_str, sres_str, kc_str);
|
||||
} else {
|
||||
char *issued = atuple->key_seq == atuple_old.key_seq ?
|
||||
"issued" : "datetime('now')";
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE AuthKeys "
|
||||
"SET issued=%s, use_count=%u, "
|
||||
"key_seq=%u, rand=%s, sres=%s, kc=%s "
|
||||
"WHERE subscriber_id = %u",
|
||||
issued, atuple->use_count, atuple->key_seq,
|
||||
rand_str, sres_str, kc_str, subscr->id);
|
||||
}
|
||||
|
||||
free(rand_str);
|
||||
free(sres_str);
|
||||
free(kc_str);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BASE_QUERY "SELECT * FROM Subscriber "
|
||||
struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
||||
enum gsm_subscriber_field field,
|
||||
|
@ -353,15 +590,15 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
free(quoted);
|
||||
break;
|
||||
default:
|
||||
printf("DB: Unknown query selector for Subscriber.\n");
|
||||
LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n");
|
||||
return NULL;
|
||||
}
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber.\n");
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n");
|
||||
return NULL;
|
||||
}
|
||||
if (!dbi_result_next_row(result)) {
|
||||
printf("DB: Failed to find the Subscriber. '%u' '%s'\n",
|
||||
DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n",
|
||||
field, id);
|
||||
dbi_result_free(result);
|
||||
return NULL;
|
||||
|
@ -388,7 +625,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
|
||||
subscr->lac = dbi_result_get_uint(result, "lac");
|
||||
subscr->authorized = dbi_result_get_uint(result, "authorized");
|
||||
printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n",
|
||||
DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n",
|
||||
subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
|
||||
subscr->lac, subscr->authorized);
|
||||
dbi_result_free(result);
|
||||
|
@ -398,7 +635,8 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
return subscr;
|
||||
}
|
||||
|
||||
int db_sync_subscriber(struct gsm_subscriber* subscriber) {
|
||||
int db_sync_subscriber(struct gsm_subscriber *subscriber)
|
||||
{
|
||||
dbi_result result;
|
||||
char tmsi[14];
|
||||
char *q_tmsi;
|
||||
|
@ -410,6 +648,7 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) {
|
|||
&q_tmsi);
|
||||
} else
|
||||
q_tmsi = strdup("NULL");
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE Subscriber "
|
||||
"SET updated = datetime('now'), "
|
||||
|
@ -424,14 +663,17 @@ int db_sync_subscriber(struct gsm_subscriber* subscriber) {
|
|||
subscriber->authorized,
|
||||
q_tmsi,
|
||||
subscriber->lac,
|
||||
subscriber->imsi
|
||||
);
|
||||
subscriber->imsi);
|
||||
|
||||
free(q_tmsi);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to update Subscriber (by IMSI).\n");
|
||||
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -442,15 +684,15 @@ int db_sync_equipment(struct gsm_equipment *equip)
|
|||
u_int8_t classmark1;
|
||||
|
||||
memcpy(&classmark1, &equip->classmark1, sizeof(classmark1));
|
||||
printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
|
||||
DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x",
|
||||
equip->imei, classmark1);
|
||||
if (equip->classmark2_len)
|
||||
printf(", classmark2=%s",
|
||||
DEBUGPC(DDB, ", classmark2=%s",
|
||||
hexdump(equip->classmark2, equip->classmark2_len));
|
||||
if (equip->classmark3_len)
|
||||
printf(", classmark3=%s",
|
||||
DEBUGPC(DDB, ", classmark3=%s",
|
||||
hexdump(equip->classmark3, equip->classmark3_len));
|
||||
printf("\n");
|
||||
DEBUGPC(DDB, "\n");
|
||||
|
||||
dbi_conn_quote_binary_copy(conn, equip->classmark2,
|
||||
equip->classmark2_len, &cm2);
|
||||
|
@ -470,7 +712,7 @@ int db_sync_equipment(struct gsm_equipment *equip)
|
|||
free(cm3);
|
||||
|
||||
if (!result) {
|
||||
printf("DB: Failed to update Equipment\n");
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -478,10 +720,12 @@ int db_sync_equipment(struct gsm_equipment *equip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
|
||||
dbi_result result=NULL;
|
||||
int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber)
|
||||
{
|
||||
dbi_result result = NULL;
|
||||
char tmsi[14];
|
||||
char* tmsi_quoted;
|
||||
|
||||
for (;;) {
|
||||
subscriber->tmsi = rand();
|
||||
if (subscriber->tmsi == GSM_RESERVED_TMSI)
|
||||
|
@ -492,20 +736,23 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
|
|||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE tmsi = %s ",
|
||||
tmsi_quoted
|
||||
);
|
||||
tmsi_quoted);
|
||||
|
||||
free(tmsi_quoted);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
|
||||
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
|
||||
"while allocating new TMSI.\n");
|
||||
return 1;
|
||||
}
|
||||
if (dbi_result_get_numrows(result)){
|
||||
if (dbi_result_get_numrows(result)) {
|
||||
dbi_result_free(result);
|
||||
continue;
|
||||
}
|
||||
if (!dbi_result_next_row(result)) {
|
||||
dbi_result_free(result);
|
||||
printf("DB: Allocated TMSI %u for IMSI %s.\n", subscriber->tmsi, subscriber->imsi);
|
||||
DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n",
|
||||
subscriber->tmsi, subscriber->imsi);
|
||||
return db_sync_subscriber(subscriber);
|
||||
}
|
||||
dbi_result_free(result);
|
||||
|
@ -513,9 +760,11 @@ int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
|
||||
dbi_result result=NULL;
|
||||
int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber)
|
||||
{
|
||||
dbi_result result = NULL;
|
||||
u_int32_t try;
|
||||
|
||||
for (;;) {
|
||||
try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN);
|
||||
result = dbi_conn_queryf(conn,
|
||||
|
@ -523,8 +772,9 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
|
|||
"WHERE extension = %i",
|
||||
try
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber while allocating new extension.\n");
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber "
|
||||
"while allocating new extension.\n");
|
||||
return 1;
|
||||
}
|
||||
if (dbi_result_get_numrows(result)){
|
||||
|
@ -538,7 +788,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
|
|||
dbi_result_free(result);
|
||||
}
|
||||
sprintf(subscriber->extension, "%i", try);
|
||||
printf("DB: Allocated extension %i for IMSI %s.\n", try, subscriber->imsi);
|
||||
DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi);
|
||||
return db_sync_subscriber(subscriber);
|
||||
}
|
||||
/*
|
||||
|
@ -547,7 +797,7 @@ int db_subscriber_alloc_exten(struct gsm_subscriber* subscriber) {
|
|||
* an error.
|
||||
*/
|
||||
|
||||
int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* token)
|
||||
int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token)
|
||||
{
|
||||
dbi_result result;
|
||||
u_int32_t try;
|
||||
|
@ -561,7 +811,8 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
|
|||
"WHERE subscriber_id = %llu OR token = \"%08X\" ",
|
||||
subscriber->id, try);
|
||||
if (!result) {
|
||||
printf("DB: Failed to query AuthToken while allocating new token.\n");
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken "
|
||||
"while allocating new token.\n");
|
||||
return 1;
|
||||
}
|
||||
if (dbi_result_get_numrows(result)) {
|
||||
|
@ -581,17 +832,19 @@ int db_subscriber_alloc_token(struct gsm_subscriber* subscriber, u_int32_t* toke
|
|||
"(%llu, datetime('now'), \"%08X\") ",
|
||||
subscriber->id, try);
|
||||
if (!result) {
|
||||
printf("DB: Failed to create token %08X for IMSI %s.\n", try, subscriber->imsi);
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for "
|
||||
"IMSI %s.\n", try, subscriber->imsi);
|
||||
return 1;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
*token = try;
|
||||
printf("DB: Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
|
||||
DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) {
|
||||
int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH])
|
||||
{
|
||||
unsigned long long equipment_id, watch_id;
|
||||
dbi_result result;
|
||||
|
||||
|
@ -603,32 +856,32 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
|
|||
"(imei, created, updated) "
|
||||
"VALUES "
|
||||
"(%s, datetime('now'), datetime('now')) ",
|
||||
imei
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to create Equipment by IMEI.\n");
|
||||
imei);
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
equipment_id = 0;
|
||||
if (dbi_result_get_numrows_affected(result)) {
|
||||
equipment_id = dbi_conn_sequence_last(conn, NULL);
|
||||
}
|
||||
dbi_result_free(result);
|
||||
if (equipment_id) {
|
||||
printf("DB: New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
|
||||
}
|
||||
|
||||
if (equipment_id)
|
||||
DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
|
||||
else {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT id FROM Equipment "
|
||||
"WHERE imei = %s ",
|
||||
imei
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Equipment by IMEI.\n");
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n");
|
||||
return 1;
|
||||
}
|
||||
if (!dbi_result_next_row(result)) {
|
||||
printf("DB: Failed to find the Equipment.\n");
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n");
|
||||
dbi_result_free(result);
|
||||
return 1;
|
||||
}
|
||||
|
@ -641,33 +894,33 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
|
|||
"(subscriber_id, equipment_id, created, updated) "
|
||||
"VALUES "
|
||||
"(%llu, %llu, datetime('now'), datetime('now')) ",
|
||||
subscriber->id, equipment_id
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to create EquipmentWatch.\n");
|
||||
subscriber->id, equipment_id);
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
watch_id = 0;
|
||||
if (dbi_result_get_numrows_affected(result)) {
|
||||
if (dbi_result_get_numrows_affected(result))
|
||||
watch_id = dbi_conn_sequence_last(conn, NULL);
|
||||
}
|
||||
|
||||
dbi_result_free(result);
|
||||
if (watch_id) {
|
||||
printf("DB: New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
|
||||
}
|
||||
if (watch_id)
|
||||
DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
|
||||
equipment_id, subscriber->imsi, imei);
|
||||
else {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE EquipmentWatch "
|
||||
"SET updated = datetime('now') "
|
||||
"WHERE subscriber_id = %llu AND equipment_id = %llu ",
|
||||
subscriber->id, equipment_id
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to update EquipmentWatch.\n");
|
||||
subscriber->id, equipment_id);
|
||||
if (!result) {
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n");
|
||||
return 1;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
|
||||
DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n",
|
||||
equipment_id, subscriber->imsi, imei);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -769,8 +1022,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 +1041,34 @@ 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 */
|
||||
struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id)
|
||||
{
|
||||
dbi_result result;
|
||||
struct gsm_sms *sms;
|
||||
|
||||
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 sms.receiver_id, id LIMIT 1",
|
||||
min_subscr_id);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
if (!dbi_result_next_row(result)) {
|
||||
dbi_result_free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sms = sms_from_result(net, result);
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return sms;
|
||||
}
|
||||
|
||||
/* 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 +1077,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;
|
||||
|
@ -824,7 +1106,7 @@ int db_sms_mark_sent(struct gsm_sms *sms)
|
|||
"SET sent = datetime('now') "
|
||||
"WHERE id = %llu", sms->id);
|
||||
if (!result) {
|
||||
printf("DB: Failed to mark SMS %llu as sent.\n", sms->id);
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -842,7 +1124,8 @@ int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
|
|||
"SET deliver_attempts = deliver_attempts + 1 "
|
||||
"WHERE id = %llu", sms->id);
|
||||
if (!result) {
|
||||
printf("DB: Failed to inc deliver attempts for SMS %llu.\n", sms->id);
|
||||
LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for "
|
||||
"SMS %llu.\n", sms->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -873,3 +1156,24 @@ int db_apdu_blob_store(struct gsm_subscriber *subscr,
|
|||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_store_counter(struct counter *ctr)
|
||||
{
|
||||
dbi_result result;
|
||||
char *q_name;
|
||||
|
||||
dbi_conn_quote_string_copy(conn, ctr->name, &q_name);
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO Counters "
|
||||
"(timestamp,name,value) VALUES "
|
||||
"(datetime('now'),%s,%lu)", q_name, ctr->value);
|
||||
|
||||
free(q_name);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -25,23 +25,84 @@
|
|||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
|
||||
unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS);
|
||||
/* default categories */
|
||||
static struct debug_category default_categories[Debug_LastEntry] = {
|
||||
[DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMM] = { .enabled = 1, .loglevel = LOGL_INFO },
|
||||
[DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
[DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
[DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
[DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
};
|
||||
|
||||
const char *get_value_string(const struct value_string *vs, u_int32_t val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
if (vs[i].value == 0 && vs[i].str == NULL)
|
||||
break;
|
||||
if (vs[i].value == val)
|
||||
return vs[i].str;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
int get_string_value(const struct value_string *vs, const char *str)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
if (vs[i].value == 0 && vs[i].str == NULL)
|
||||
break;
|
||||
if (!strcasecmp(vs[i].str, str))
|
||||
return vs[i].value;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct debug_info {
|
||||
const char *name;
|
||||
const char *color;
|
||||
const char *description;
|
||||
int number;
|
||||
int position;
|
||||
};
|
||||
|
||||
struct debug_context {
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_subscriber *subscr;
|
||||
struct gsm_bts *bts;
|
||||
};
|
||||
|
||||
static struct debug_context debug_context;
|
||||
static void *tall_dbg_ctx = NULL;
|
||||
static LLIST_HEAD(target_list);
|
||||
|
||||
#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
|
||||
{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
|
||||
|
||||
static const struct debug_info debug_info[] = {
|
||||
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
|
||||
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
|
||||
|
@ -60,52 +121,83 @@ static const struct debug_info debug_info[] = {
|
|||
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
|
||||
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
|
||||
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
|
||||
DEBUG_CATEGORY(DHO, "DHO", "", "")
|
||||
DEBUG_CATEGORY(DDB, "DDB", "", "")
|
||||
DEBUG_CATEGORY(DDB, "DREF", "", "")
|
||||
};
|
||||
|
||||
static int use_color = 1;
|
||||
static const struct value_string loglevel_strs[] = {
|
||||
{ 0, "EVERYTHING" },
|
||||
{ 1, "DEBUG" },
|
||||
{ 3, "INFO" },
|
||||
{ 5, "NOTICE" },
|
||||
{ 7, "ERROR" },
|
||||
{ 8, "FATAL" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
void debug_use_color(int color)
|
||||
int debug_parse_level(const char *lvl)
|
||||
{
|
||||
use_color = color;
|
||||
return get_string_value(loglevel_strs, lvl);
|
||||
}
|
||||
|
||||
static int print_timestamp = 0;
|
||||
|
||||
void debug_timestamp(int enable)
|
||||
int debug_parse_category(const char *category)
|
||||
{
|
||||
print_timestamp = enable;
|
||||
}
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||
if (!strcasecmp(debug_info[i].name+1, category))
|
||||
return debug_info[i].number;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the category mask.
|
||||
* category1:category2:category3
|
||||
* The format can be this: category1:category2:category3
|
||||
* or category1,2:category2,3:...
|
||||
*/
|
||||
void debug_parse_category_mask(const char *_mask)
|
||||
void debug_parse_category_mask(struct debug_target* target, const char *_mask)
|
||||
{
|
||||
unsigned int new_mask = 0;
|
||||
int i = 0;
|
||||
char *mask = strdup(_mask);
|
||||
char *category_token = NULL;
|
||||
|
||||
/* Disable everything to enable it afterwards */
|
||||
for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
|
||||
target->categories[i].enabled = 0;
|
||||
|
||||
category_token = strtok(mask, ":");
|
||||
do {
|
||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||
if (strcasecmp(debug_info[i].name, category_token) == 0)
|
||||
new_mask |= debug_info[i].number;
|
||||
char* colon = strstr(category_token, ",");
|
||||
int length = strlen(category_token);
|
||||
|
||||
if (colon)
|
||||
length = colon - category_token;
|
||||
|
||||
if (strncasecmp(debug_info[i].name, category_token, length) == 0) {
|
||||
int number = debug_info[i].number;
|
||||
int level = 0;
|
||||
|
||||
if (colon)
|
||||
level = atoi(colon+1);
|
||||
|
||||
target->categories[number].enabled = 1;
|
||||
target->categories[number].loglevel = level;
|
||||
}
|
||||
}
|
||||
} while ((category_token = strtok(NULL, ":")));
|
||||
|
||||
|
||||
free(mask);
|
||||
debug_mask = new_mask;
|
||||
}
|
||||
|
||||
const char* color(int subsys)
|
||||
static const char* color(int subsys)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) {
|
||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||
if (debug_info[i].number == subsys)
|
||||
return debug_info[i].color;
|
||||
}
|
||||
|
@ -113,35 +205,111 @@ const char* color(int subsys)
|
|||
return "";
|
||||
}
|
||||
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
|
||||
static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
|
||||
int cont, const char *format, va_list ap)
|
||||
{
|
||||
va_list ap;
|
||||
FILE *outfd = stderr;
|
||||
char col[30];
|
||||
char sub[30];
|
||||
char tim[30];
|
||||
char buf[4096];
|
||||
char final[4096];
|
||||
|
||||
if (!(debug_mask & subsys))
|
||||
return;
|
||||
/* prepare the data */
|
||||
col[0] = '\0';
|
||||
sub[0] = '\0';
|
||||
tim[0] = '\0';
|
||||
buf[0] = '\0';
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
fprintf(outfd, "%s", color(subsys));
|
||||
/* are we using color */
|
||||
if (target->use_color) {
|
||||
snprintf(col, sizeof(col), "%s", color(subsys));
|
||||
col[sizeof(col)-1] = '\0';
|
||||
}
|
||||
vsnprintf(buf, sizeof(buf), format, ap);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
if (!cont) {
|
||||
if (print_timestamp) {
|
||||
if (target->print_timestamp) {
|
||||
char *timestr;
|
||||
time_t tm;
|
||||
tm = time(NULL);
|
||||
timestr = ctime(&tm);
|
||||
timestr[strlen(timestr)-1] = '\0';
|
||||
fprintf(outfd, "%s ", timestr);
|
||||
snprintf(tim, sizeof(tim), "%s ", timestr);
|
||||
tim[sizeof(tim)-1] = '\0';
|
||||
}
|
||||
fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line);
|
||||
snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
|
||||
sub[sizeof(sub)-1] = '\0';
|
||||
}
|
||||
vfprintf(outfd, format, ap);
|
||||
fprintf(outfd, "\033[0;m");
|
||||
|
||||
snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
|
||||
final[sizeof(final)-1] = '\0';
|
||||
target->output(target, final);
|
||||
}
|
||||
|
||||
|
||||
static void _debugp(unsigned int subsys, int level, char *file, int line,
|
||||
int cont, const char *format, va_list ap)
|
||||
{
|
||||
struct debug_target *tar;
|
||||
|
||||
llist_for_each_entry(tar, &target_list, entry) {
|
||||
struct debug_category *category;
|
||||
int output = 0;
|
||||
|
||||
category = &tar->categories[subsys];
|
||||
/* subsystem is not supposed to be debugged */
|
||||
if (!category->enabled)
|
||||
continue;
|
||||
|
||||
/* Check the global log level */
|
||||
if (tar->loglevel != 0 && level < tar->loglevel)
|
||||
continue;
|
||||
|
||||
/* Check the category log level */
|
||||
if (category->loglevel != 0 && level < category->loglevel)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Apply filters here... if that becomes messy we will need to put
|
||||
* filters in a list and each filter will say stop, continue, output
|
||||
*/
|
||||
if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) {
|
||||
output = 1;
|
||||
} else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0
|
||||
&& debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) {
|
||||
output = 1;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
/* FIXME: copying the va_list is an ugly workaround against a bug
|
||||
* hidden somewhere in _output. If we do not copy here, the first
|
||||
* call to _output() will corrupt the va_list contents, and any
|
||||
* further _output() calls with the same va_list will segfault */
|
||||
va_list bp;
|
||||
va_copy(bp, ap);
|
||||
_output(tar, subsys, file, line, cont, format, bp);
|
||||
va_end(bp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
fflush(outfd);
|
||||
void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_debugp(subsys, level, file, line, cont, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static char hexd_buff[4096];
|
||||
|
@ -163,3 +331,122 @@ char *hexdump(const unsigned char *buf, int len)
|
|||
return hexd_buff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void debug_add_target(struct debug_target *target)
|
||||
{
|
||||
llist_add_tail(&target->entry, &target_list);
|
||||
}
|
||||
|
||||
void debug_del_target(struct debug_target *target)
|
||||
{
|
||||
llist_del(&target->entry);
|
||||
}
|
||||
|
||||
void debug_reset_context(void)
|
||||
{
|
||||
memset(&debug_context, 0, sizeof(debug_context));
|
||||
}
|
||||
|
||||
/* currently we are not reffing these */
|
||||
void debug_set_context(int ctx, void *value)
|
||||
{
|
||||
switch (ctx) {
|
||||
case BSC_CTX_LCHAN:
|
||||
debug_context.lchan = (struct gsm_lchan *) value;
|
||||
break;
|
||||
case BSC_CTX_SUBSCR:
|
||||
debug_context.subscr = (struct gsm_subscriber *) value;
|
||||
break;
|
||||
case BSC_CTX_BTS:
|
||||
debug_context.bts = (struct gsm_bts *) value;
|
||||
break;
|
||||
case BSC_CTX_SCCP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_set_imsi_filter(struct debug_target *target, const char *imsi)
|
||||
{
|
||||
if (imsi) {
|
||||
target->filter_map |= DEBUG_FILTER_IMSI;
|
||||
target->imsi_filter = talloc_strdup(target, imsi);
|
||||
} else if (target->imsi_filter) {
|
||||
target->filter_map &= ~DEBUG_FILTER_IMSI;
|
||||
talloc_free(target->imsi_filter);
|
||||
target->imsi_filter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_set_all_filter(struct debug_target *target, int all)
|
||||
{
|
||||
if (all)
|
||||
target->filter_map |= DEBUG_FILTER_ALL;
|
||||
else
|
||||
target->filter_map &= ~DEBUG_FILTER_ALL;
|
||||
}
|
||||
|
||||
void debug_set_use_color(struct debug_target *target, int use_color)
|
||||
{
|
||||
target->use_color = use_color;
|
||||
}
|
||||
|
||||
void debug_set_print_timestamp(struct debug_target *target, int print_timestamp)
|
||||
{
|
||||
target->print_timestamp = print_timestamp;
|
||||
}
|
||||
|
||||
void debug_set_log_level(struct debug_target *target, int log_level)
|
||||
{
|
||||
target->loglevel = log_level;
|
||||
}
|
||||
|
||||
void debug_set_category_filter(struct debug_target *target, int category, int enable, int level)
|
||||
{
|
||||
if (category >= Debug_LastEntry)
|
||||
return;
|
||||
target->categories[category].enabled = !!enable;
|
||||
target->categories[category].loglevel = level;
|
||||
}
|
||||
|
||||
static void _stderr_output(struct debug_target *target, const char *log)
|
||||
{
|
||||
fprintf(target->tgt_stdout.out, "%s", log);
|
||||
fflush(target->tgt_stdout.out);
|
||||
}
|
||||
|
||||
struct debug_target *debug_target_create(void)
|
||||
{
|
||||
struct debug_target *target;
|
||||
|
||||
target = talloc_zero(tall_dbg_ctx, struct debug_target);
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&target->entry);
|
||||
memcpy(target->categories, default_categories, sizeof(default_categories));
|
||||
target->use_color = 1;
|
||||
target->print_timestamp = 0;
|
||||
target->loglevel = 0;
|
||||
return target;
|
||||
}
|
||||
|
||||
struct debug_target *debug_target_create_stderr(void)
|
||||
{
|
||||
struct debug_target *target;
|
||||
|
||||
target = debug_target_create();
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
target->tgt_stdout.out = stderr;
|
||||
target->output = _stderr_output;
|
||||
return target;
|
||||
}
|
||||
|
||||
void debug_init(void)
|
||||
{
|
||||
tall_dbg_ctx = talloc_named_const(NULL, 1, "debug");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -233,10 +234,16 @@ 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");
|
||||
if (!msg->trx) {
|
||||
LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL: %s\n",
|
||||
hexdump(msg->data, msg->len));
|
||||
talloc_free(msg);
|
||||
return -EINVAL;
|
||||
} else if (!msg->trx->rsl_link) {
|
||||
LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx->rsl_link == NULL: %s\n",
|
||||
hexdump(msg->data, msg->len));
|
||||
talloc_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
sign_link = msg->trx->rsl_link;
|
||||
|
@ -263,7 +270,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 +312,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,10 +437,12 @@ 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;
|
||||
}
|
||||
|
||||
debug_set_context(BSC_CTX_BTS, link->trx->bts);
|
||||
switch (link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
msg->trx = link->trx;
|
||||
|
@ -445,7 +454,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 +463,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 +500,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 +532,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>
|
||||
|
@ -49,6 +50,7 @@
|
|||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/transaction.h>
|
||||
#include <openbsc/ussd.h>
|
||||
#include <openbsc/silent_call.h>
|
||||
|
||||
#define GSM_MAX_FACILITY 128
|
||||
#define GSM_MAX_SSVERSION 128
|
||||
|
@ -56,8 +58,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 +166,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 +177,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,
|
||||
|
@ -298,11 +235,18 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
|
|||
db_subscriber_alloc_tmsi(lchan->subscr);
|
||||
release_loc_updating_req(lchan);
|
||||
rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi);
|
||||
if (lchan->ts->trx->bts->network->send_mm_info) {
|
||||
/* send MM INFO with network name */
|
||||
rc = gsm48_tx_mm_info(msg->lchan);
|
||||
}
|
||||
|
||||
/* call subscr_update after putting the loc_upd_acc
|
||||
* in the transmit queue, since S_SUBSCR_ATTACHED might
|
||||
* trigger further action like SMS delivery */
|
||||
subscr_update(lchan->subscr, msg->trx->bts,
|
||||
GSM_SUBSCRIBER_UPDATE_ATTACHED);
|
||||
/* try to close channel ASAP */
|
||||
lchan_auto_release(lchan);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -433,18 +377,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 +410,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 +819,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;
|
||||
|
||||
|
@ -871,7 +830,12 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
|
|||
gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
|
||||
gh->data[0] = cause;
|
||||
|
||||
DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
|
||||
LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
|
||||
"LAC=%u BTS=%u\n", lchan->subscr ?
|
||||
subscr_name(lchan->subscr) : "unknown",
|
||||
lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr);
|
||||
|
||||
counter_inc(bts->network->stats.loc_upd_resp.reject);
|
||||
|
||||
return gsm48_sendmsg(msg, NULL);
|
||||
}
|
||||
|
@ -884,7 +848,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 +864,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);
|
||||
counter_inc(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 +900,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,9 +933,10 @@ static int mm_rx_id_resp(struct msgb *msg)
|
|||
static void loc_upd_rej_cb(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
|
||||
release_loc_updating_req(lchan);
|
||||
gsm0408_loc_upd_rej(lchan, reject_cause);
|
||||
gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
|
||||
lchan_auto_release(lchan);
|
||||
}
|
||||
|
||||
|
@ -1019,6 +982,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:
|
||||
counter_inc(bts->network->stats.loc_upd_type.normal);
|
||||
break;
|
||||
case GSM48_LUPD_IMSI_ATT:
|
||||
counter_inc(bts->network->stats.loc_upd_type.attach);
|
||||
break;
|
||||
case GSM48_LUPD_PERIODIC:
|
||||
counter_inc(bts->network->stats.loc_upd_type.periodic);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pseudo Spoof detection: Just drop a second/concurrent
|
||||
* location updating request.
|
||||
|
@ -1198,20 +1175,19 @@ int gsm48_tx_mm_info(struct gsm_lchan *lchan)
|
|||
}
|
||||
|
||||
/* Section 9.2.2 */
|
||||
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand)
|
||||
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar));
|
||||
|
||||
DEBUGP(DMM, "-> AUTH REQ\n");
|
||||
DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", hexdump(rand, 16));
|
||||
|
||||
msg->lchan = lchan;
|
||||
gh->proto_discr = GSM48_PDISC_MM;
|
||||
gh->msg_type = GSM48_MT_MM_AUTH_REQ;
|
||||
|
||||
/* Key Sequence: FIXME fixed to 0 */
|
||||
ar->key_seq = 0;
|
||||
ar->key_seq = key_seq;
|
||||
|
||||
/* 16 bytes RAND parameters */
|
||||
if (rand)
|
||||
|
@ -1302,6 +1278,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);
|
||||
|
||||
|
@ -1315,7 +1293,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);
|
||||
}
|
||||
|
@ -1341,6 +1321,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);
|
||||
|
||||
counter_inc(bts->network->stats.loc_upd_type.detach);
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_TMSI:
|
||||
subscr = subscr_get_by_tmsi(bts->network,
|
||||
|
@ -1362,8 +1344,7 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
|||
if (subscr) {
|
||||
subscr_update(subscr, msg->trx->bts,
|
||||
GSM_SUBSCRIBER_UPDATE_DETACHED);
|
||||
DEBUGP(DMM, "Subscriber: %s\n",
|
||||
subscr->name ? subscr->name : subscr->imsi);
|
||||
DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr));
|
||||
|
||||
subscr->equipment.classmark1 = idi->classmark1;
|
||||
db_sync_equipment(&subscr->equipment);
|
||||
|
@ -1372,6 +1353,12 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
|||
} else
|
||||
DEBUGP(DMM, "Unknown Subscriber ?!?\n");
|
||||
|
||||
/* FIXME: iterate over all transactions and release them,
|
||||
* imagine an IMSI DETACH happening during an active call! */
|
||||
|
||||
/* subscriber is detached: should we release lchan? */
|
||||
lchan_auto_release(msg->lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1407,7 +1394,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
|
|||
case GSM48_MT_MM_TMSI_REALL_COMPL:
|
||||
DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
|
||||
msg->lchan->subscr ?
|
||||
msg->lchan->subscr->imsi :
|
||||
subscr_name(msg->lchan->subscr) :
|
||||
"unknown subscriber");
|
||||
break;
|
||||
case GSM48_MT_MM_IMSI_DETACH_IND:
|
||||
|
@ -1420,7 +1407,7 @@ static int gsm0408_rcv_mm(struct msgb *msg)
|
|||
DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n",
|
||||
LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n",
|
||||
gh->msg_type);
|
||||
break;
|
||||
}
|
||||
|
@ -1528,26 +1515,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;
|
||||
}
|
||||
|
@ -1569,6 +1543,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)
|
||||
|
@ -1602,9 +1603,15 @@ 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);
|
||||
LOGP(DRR, LOGL_NOTICE, "Unimplemented "
|
||||
"GSM 04.08 RR msg type 0x%02x\n", gh->msg_type);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1812,13 +1819,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;
|
||||
|
@ -1827,56 +1837,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 */
|
||||
|
@ -1884,7 +1859,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",
|
||||
|
@ -1895,33 +1869,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:
|
||||
|
@ -1929,8 +1901,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;
|
||||
|
@ -1952,45 +1923,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");
|
||||
|
@ -2163,6 +2150,10 @@ static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
|
|||
|
||||
new_cc_state(trans, GSM_CSTATE_INITIATED);
|
||||
|
||||
LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n",
|
||||
subscr_name(trans->subscr), trans->subscr->extension,
|
||||
setup.called.number);
|
||||
|
||||
/* indicate setup to MNCC */
|
||||
mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup);
|
||||
|
||||
|
@ -2711,6 +2702,9 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
|
|||
case GSM_CSTATE_RELEASE_REQ:
|
||||
rc = mncc_recvmsg(trans->subscr->net, trans,
|
||||
MNCC_REL_CNF, &rel);
|
||||
/* FIXME: in case of multiple calls, we can't simply
|
||||
* hang up here ! */
|
||||
lchan_auto_release(msg->lchan);
|
||||
break;
|
||||
default:
|
||||
rc = mncc_recvmsg(trans->subscr->net, trans,
|
||||
|
@ -3217,11 +3211,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));
|
||||
|
@ -3487,6 +3500,9 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
|
|||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
u_int8_t pdisc = gh->proto_discr & 0x0f;
|
||||
int rc = 0;
|
||||
|
||||
if (silent_call_reroute(msg))
|
||||
return silent_call_rx(msg);
|
||||
|
||||
switch (pdisc) {
|
||||
case GSM48_PDISC_CC:
|
||||
|
@ -3503,15 +3519,15 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
|
|||
break;
|
||||
case GSM48_PDISC_MM_GPRS:
|
||||
case GSM48_PDISC_SM_GPRS:
|
||||
fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02x\n",
|
||||
pdisc);
|
||||
LOGP(DRLL, LOGL_NOTICE, "Unimplemented "
|
||||
"GSM 04.08 discriminator 0x%02x\n", pdisc);
|
||||
break;
|
||||
case GSM48_PDISC_NC_SS:
|
||||
rc = handle_rcv_ussd(msg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02x\n",
|
||||
pdisc);
|
||||
LOGP(DRLL, LOGL_NOTICE, "Unknown "
|
||||
"GSM 04.08 discriminator 0x%02x\n", pdisc);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_SDCCH,
|
||||
[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)
|
||||
|
@ -324,7 +340,7 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
|
|||
if ((ra & chr->mask) == chr->val)
|
||||
return ctype_by_chreq[chr->type];
|
||||
}
|
||||
fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
|
||||
LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
|
||||
return GSM_LCHAN_SDCCH;
|
||||
}
|
||||
|
||||
|
@ -347,7 +363,7 @@ enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, in
|
|||
if ((ra & chr->mask) == chr->val)
|
||||
return reason_by_chreq[chr->type];
|
||||
}
|
||||
fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
|
||||
LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
|
||||
return GSM_CHREQ_REASON_OTHER;
|
||||
}
|
||||
|
||||
|
@ -459,7 +475,7 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
|
|||
if (!msg->lchan->subscr) {
|
||||
msg->lchan->subscr = subscr;
|
||||
} else if (msg->lchan->subscr != subscr) {
|
||||
DEBUGP(DRR, "<- Channel already owned by someone else?\n");
|
||||
LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
|
||||
subscr_put(subscr);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
|
@ -472,7 +488,9 @@ 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;
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
|
||||
bts->network->stats.paging.completed++;
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data);
|
||||
|
||||
/* Stop paging on the bts we received the paging response */
|
||||
paging_request_stop(msg->trx->bts, subscr, msg->lchan);
|
||||
|
@ -503,18 +521,62 @@ 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)
|
||||
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_command)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
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);
|
||||
|
||||
msg->lchan = lchan;
|
||||
msg->lchan = dest_lchan;
|
||||
gh->proto_discr = GSM48_PDISC_RR;
|
||||
gh->msg_type = GSM48_MT_RR_ASS_CMD;
|
||||
|
||||
|
@ -526,17 +588,16 @@ 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;
|
||||
|
||||
msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
|
||||
|
||||
/* in case of multi rate we need to attach a config */
|
||||
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
||||
if (lchan->mr_conf.ver == 0) {
|
||||
DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
|
||||
LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec "
|
||||
"without multirate config.\n");
|
||||
} else {
|
||||
u_int8_t *data = msgb_put(msg, 4);
|
||||
data[0] = GSM48_IE_MUL_RATE_CFG;
|
||||
|
@ -576,7 +637,8 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
|
|||
/* in case of multi rate we need to attach a config */
|
||||
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
|
||||
if (lchan->mr_conf.ver == 0) {
|
||||
DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
|
||||
LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec "
|
||||
"without multirate config.\n");
|
||||
} else {
|
||||
u_int8_t *data = msgb_put(msg, 4);
|
||||
data[0] = GSM48_IE_MUL_RATE_CFG;
|
||||
|
@ -609,7 +671,7 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
|
|||
DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
|
||||
|
||||
if (mod->mode != msg->lchan->tch_mode) {
|
||||
DEBUGP(DRR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
|
||||
LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n",
|
||||
msg->lchan->tch_mode, mod->mode);
|
||||
return -1;
|
||||
}
|
||||
|
@ -642,3 +704,82 @@ 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;
|
||||
struct gsm_meas_rep_cell *mrc;
|
||||
|
||||
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 */
|
||||
mrc = &rep->cell[0];
|
||||
mrc->rxlev = data[3] & 0x3f;
|
||||
mrc->neigh_idx = data[4] >> 3;
|
||||
mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
|
||||
mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
|
||||
if (rep->num_cell < 2)
|
||||
return 0;
|
||||
|
||||
mrc = &rep->cell[1];
|
||||
mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
|
||||
mrc->neigh_idx = (data[6] >> 2) & 0x1f;
|
||||
mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
|
||||
mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
|
||||
if (rep->num_cell < 3)
|
||||
return 0;
|
||||
|
||||
mrc = &rep->cell[2];
|
||||
mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
|
||||
mrc->neigh_idx = (data[8] >> 1) & 0x1f;
|
||||
mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
|
||||
mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3);
|
||||
if (rep->num_cell < 4)
|
||||
return 0;
|
||||
|
||||
mrc = &rep->cell[3];
|
||||
mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
|
||||
mrc->neigh_idx = data[10] & 0x1f;
|
||||
mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
|
||||
mrc->bsic = data[11] >> 2;
|
||||
if (rep->num_cell < 5)
|
||||
return 0;
|
||||
|
||||
mrc = &rep->cell[4];
|
||||
mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
|
||||
mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7);
|
||||
mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
|
||||
mrc->bsic = (data[13] >> 1) & 0x3f;
|
||||
if (rep->num_cell < 6)
|
||||
return 0;
|
||||
|
||||
mrc = &rep->cell[5];
|
||||
mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
|
||||
mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6);
|
||||
mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
|
||||
mrc->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,115 +198,6 @@ 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);
|
||||
|
||||
static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
|
||||
{
|
||||
u_int8_t vp;
|
||||
unsigned long minutes;
|
||||
time_t expires;
|
||||
time_t now;
|
||||
|
||||
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;
|
||||
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;
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
/* Chapter 9.2.3.12.3 */
|
||||
/* FIXME: implementation */
|
||||
DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
|
||||
break;
|
||||
case GSM340_TP_VPF_NONE:
|
||||
/* Default validity: two days */
|
||||
minutes = 24 * 60 * 2;
|
||||
break;
|
||||
}
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
|
||||
enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
|
||||
{
|
||||
u_int8_t cgbits = dcs >> 4;
|
||||
enum sms_alphabet alpha = DCS_NONE;
|
||||
|
||||
if ((cgbits & 0xc) == 0) {
|
||||
if (cgbits & 2)
|
||||
DEBUGP(DSMS, "Compressed SMS not supported yet\n");
|
||||
|
||||
switch ((dcs >> 2)&0x03) {
|
||||
case 0:
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
break;
|
||||
case 1:
|
||||
alpha = DCS_8BIT_DATA;
|
||||
break;
|
||||
case 2:
|
||||
alpha = DCS_UCS2;
|
||||
break;
|
||||
}
|
||||
} else if (cgbits == 0xc || cgbits == 0xd)
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
else if (cgbits == 0xe)
|
||||
alpha = DCS_UCS2;
|
||||
else if (cgbits == 0xf) {
|
||||
if (dcs & 4)
|
||||
alpha = DCS_8BIT_DATA;
|
||||
else
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
|
||||
{
|
||||
if (db_sms_store(gsms) != 0) {
|
||||
DEBUGP(DSMS, "Failed to store SMS in Database\n");
|
||||
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
|
||||
}
|
||||
/* dispatch a signal to tell higher level about it */
|
||||
dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
|
||||
/* try delivering the SMS right now */
|
||||
//gsm411_send_sms_subscr(gsms->receiver, gsms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
|
||||
static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
|
||||
struct gsm_subscriber *subscr)
|
||||
{
|
||||
int len_in_bytes;
|
||||
|
||||
oa[1] = 0xb9; /* networks-specific number, private numbering plan */
|
||||
|
||||
len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
|
||||
|
||||
/* GSM 03.40 tells us the length is in 'useful semi-octets' */
|
||||
oa[0] = strlen(subscr->extension) & 0xff;
|
||||
|
||||
return len_in_bytes;
|
||||
}
|
||||
|
||||
/* Turn int into semi-octet representation: 98 => 0x89 */
|
||||
static u_int8_t bcdify(u_int8_t value)
|
||||
{
|
||||
|
@ -324,11 +215,11 @@ 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);
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
"unbcdify got too big nibble: 0x%02X\n", value);
|
||||
|
||||
ret = (value&0x0F)*10;
|
||||
if (ret > 90)
|
||||
ret += value>>4;
|
||||
ret += value>>4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -370,6 +261,179 @@ static time_t gsm340_scts(u_int8_t *scts)
|
|||
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;
|
||||
vp = *(sms_vp);
|
||||
if (vp == 0) {
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
"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:
|
||||
return gsm340_vp_relative(sms_vp);
|
||||
case GSM340_TP_VPF_ABSOLUTE:
|
||||
return gsm340_vp_absolute(sms_vp);
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
/* Chapter 9.2.3.12.3 */
|
||||
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 */
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
"Reserved enhanced validity period format\n");
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
case GSM340_TP_VPF_NONE:
|
||||
default:
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
}
|
||||
|
||||
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
|
||||
enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
|
||||
{
|
||||
u_int8_t cgbits = dcs >> 4;
|
||||
enum sms_alphabet alpha = DCS_NONE;
|
||||
|
||||
if ((cgbits & 0xc) == 0) {
|
||||
if (cgbits & 2)
|
||||
LOGP(DSMS, LOGL_NOTICE,
|
||||
"Compressed SMS not supported yet\n");
|
||||
|
||||
switch ((dcs >> 2)&0x03) {
|
||||
case 0:
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
break;
|
||||
case 1:
|
||||
alpha = DCS_8BIT_DATA;
|
||||
break;
|
||||
case 2:
|
||||
alpha = DCS_UCS2;
|
||||
break;
|
||||
}
|
||||
} else if (cgbits == 0xc || cgbits == 0xd)
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
else if (cgbits == 0xe)
|
||||
alpha = DCS_UCS2;
|
||||
else if (cgbits == 0xf) {
|
||||
if (dcs & 4)
|
||||
alpha = DCS_8BIT_DATA;
|
||||
else
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
|
||||
{
|
||||
if (db_sms_store(gsms) != 0) {
|
||||
LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n");
|
||||
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
|
||||
}
|
||||
/* dispatch a signal to tell higher level about it */
|
||||
dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
|
||||
/* try delivering the SMS right now */
|
||||
//gsm411_send_sms_subscr(gsms->receiver, gsms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
|
||||
static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
|
||||
struct gsm_subscriber *subscr)
|
||||
{
|
||||
int len_in_bytes;
|
||||
|
||||
oa[1] = 0xb9; /* networks-specific number, private numbering plan */
|
||||
|
||||
len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
|
||||
|
||||
/* GSM 03.40 tells us the length is in 'useful semi-octets' */
|
||||
oa[0] = strlen(subscr->extension) & 0xff;
|
||||
|
||||
return len_in_bytes;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
|
@ -437,7 +501,8 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
|
|||
memcpy(smsp, sms->user_data, sms->user_data_len);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "Unhandled Data Coding Scheme: 0x%02X\n", sms->data_coding_scheme);
|
||||
LOGP(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n",
|
||||
sms->data_coding_scheme);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -457,6 +522,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;
|
||||
|
||||
counter_inc(bts->network->stats.sms.submitted);
|
||||
|
||||
gsms = sms_alloc();
|
||||
if (!gsms)
|
||||
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
|
||||
|
@ -475,7 +542,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
|||
/* length in bytes of the destination address */
|
||||
da_len_bytes = 2 + *smsp/2 + *smsp%2;
|
||||
if (da_len_bytes > 12) {
|
||||
DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
|
||||
LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n");
|
||||
rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
|
||||
goto out;
|
||||
}
|
||||
|
@ -499,14 +566,17 @@ 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:
|
||||
sms_vp = 0;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
|
||||
sms_vpf);
|
||||
LOGP(DSMS, LOGL_NOTICE,
|
||||
"SMS Validity period not implemented: 0x%02x\n", sms_vpf);
|
||||
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
|
||||
}
|
||||
gsms->user_data_len = *smsp++;
|
||||
|
@ -524,16 +594,17 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
|||
}
|
||||
}
|
||||
|
||||
DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
|
||||
"PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
|
||||
"UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
|
||||
gsms->protocol_id, gsms->data_coding_scheme,
|
||||
gsms->dest_addr, gsms->user_data_len,
|
||||
gsms->sender = subscr_get(msg->lchan->subscr);
|
||||
|
||||
LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, "
|
||||
"MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, "
|
||||
"UserDataLength: 0x%02x, UserData: \"%s\"\n",
|
||||
subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref,
|
||||
gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr,
|
||||
gsms->user_data_len,
|
||||
sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
|
||||
hexdump(gsms->user_data, gsms->user_data_len));
|
||||
|
||||
gsms->sender = subscr_get(msg->lchan->subscr);
|
||||
|
||||
gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
|
||||
|
||||
dispatch_signal(SS_SMS, 0, gsms);
|
||||
|
@ -542,6 +613,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 */
|
||||
counter_inc(bts->network->stats.sms.no_receiver);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -552,11 +624,11 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
|||
break;
|
||||
case GSM340_SMS_COMMAND_MS2SC:
|
||||
case GSM340_SMS_DELIVER_REP_MS2SC:
|
||||
DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms_mti);
|
||||
LOGP(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti);
|
||||
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
|
||||
LOGP(DSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti);
|
||||
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
|
||||
break;
|
||||
}
|
||||
|
@ -586,7 +658,7 @@ static int gsm411_send_rp_error(struct gsm_trans *trans,
|
|||
|
||||
msgb_tv_put(msg, 1, cause);
|
||||
|
||||
DEBUGP(DSMS, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
|
||||
LOGP(DSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause,
|
||||
get_value_string(rp_cause_strs, cause));
|
||||
|
||||
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref);
|
||||
|
@ -602,10 +674,11 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
|
|||
int rc = 0;
|
||||
|
||||
if (src_len && src)
|
||||
DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
|
||||
LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n");
|
||||
|
||||
if (!dst_len || !dst || !tpdu_len || !tpdu) {
|
||||
DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
"RP-DATA (MO) without DST or TPDU ?!?\n");
|
||||
gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
GSM411_RP_CAUSE_INV_MAND_INF);
|
||||
return -EIO;
|
||||
|
@ -660,13 +733,13 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
|
|||
* transmitted */
|
||||
|
||||
if (!trans->sms.is_mt) {
|
||||
DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n");
|
||||
LOGP(DSMS, LOGL_ERROR, "RX RP-ACK on a MO transfer ?\n");
|
||||
return gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
|
||||
}
|
||||
|
||||
if (!sms) {
|
||||
DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n");
|
||||
LOGP(DSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n");
|
||||
return gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
GSM411_RP_CAUSE_PROTOCOL_ERR);
|
||||
}
|
||||
|
@ -679,14 +752,16 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
|
|||
sms_free(sms);
|
||||
trans->sms.sms = NULL;
|
||||
|
||||
/* free the transaction here */
|
||||
trans_free(trans);
|
||||
|
||||
/* check for more messages for this subscriber */
|
||||
sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
|
||||
if (sms)
|
||||
gsm411_send_sms_lchan(msg->lchan, sms);
|
||||
else
|
||||
|
||||
/* free the transaction here */
|
||||
trans_free(trans);
|
||||
|
||||
/* release channel if done */
|
||||
if (!sms)
|
||||
rsl_release_request(msg->lchan, trans->sms.link_id);
|
||||
|
||||
return 0;
|
||||
|
@ -695,6 +770,7 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
|
|||
static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
||||
struct gsm411_rp_hdr *rph)
|
||||
{
|
||||
struct gsm_network *net = trans->lchan->ts->trx->bts->network;
|
||||
struct gsm_sms *sms = trans->sms.sms;
|
||||
u_int8_t cause_len = rph->data[0];
|
||||
u_int8_t cause = rph->data[1];
|
||||
|
@ -703,11 +779,12 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
|||
* successfully receive the SMS. We need to investigate
|
||||
* the cause and take action depending on it */
|
||||
|
||||
DEBUGP(DSMS, "RX SMS RP-ERROR, cause %d:%d (%s)\n", cause_len, cause,
|
||||
get_value_string(rp_cause_strs, cause));
|
||||
LOGP(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n",
|
||||
subscr_name(msg->lchan->subscr), cause_len, cause,
|
||||
get_value_string(rp_cause_strs, cause));
|
||||
|
||||
if (!trans->sms.is_mt) {
|
||||
DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
|
||||
LOGP(DSMS, LOGL_ERROR, "RX RP-ERR on a MO transfer ?\n");
|
||||
#if 0
|
||||
return gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
|
||||
|
@ -715,7 +792,8 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
|||
}
|
||||
|
||||
if (!sms) {
|
||||
DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
"RX RP-ERR, but no sms in transaction?!?\n");
|
||||
return -EINVAL;
|
||||
#if 0
|
||||
return gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
|
@ -728,7 +806,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);
|
||||
}
|
||||
counter_inc(net->stats.sms.rp_err_mem);
|
||||
} else
|
||||
counter_inc(net->stats.sms.rp_err_other);
|
||||
|
||||
sms_free(sms);
|
||||
trans->sms.sms = NULL;
|
||||
|
@ -790,7 +870,7 @@ static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh,
|
|||
rc = gsm411_rx_rp_error(msg, trans, rp_data);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
|
||||
LOGP(DSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type);
|
||||
rc = gsm411_send_rp_error(trans, rp_data->msg_ref,
|
||||
GSM411_RP_CAUSE_MSGTYPE_NOTEXIST);
|
||||
break;
|
||||
|
@ -821,7 +901,7 @@ static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
|
|||
struct msgb *msg = gsm411_msgb_alloc();
|
||||
u_int8_t *causep;
|
||||
|
||||
DEBUGP(DSMS, "TX CP-ERROR, cause %d (%s)\n", cause,
|
||||
LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause,
|
||||
get_value_string(cp_cause_strs, cause));
|
||||
|
||||
causep = msgb_put(msg, 1);
|
||||
|
@ -844,11 +924,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) {
|
||||
|
@ -868,6 +948,33 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
|
|||
switch(msg_type) {
|
||||
case GSM411_MT_CP_DATA:
|
||||
DEBUGPC(DSMS, "RX SMS CP-DATA\n");
|
||||
|
||||
/* 5.4: For MO, if a CP-DATA is received for a new
|
||||
* transaction, equals reception of an implicit
|
||||
* last CP-ACK for previous transaction */
|
||||
if (trans->sms.cp_state == GSM411_CPS_IDLE) {
|
||||
int i;
|
||||
struct gsm_trans *ptrans;
|
||||
|
||||
/* Scan through all remote initiated transactions */
|
||||
for (i=8; i<15; i++) {
|
||||
if (i == transaction_id)
|
||||
continue;
|
||||
|
||||
ptrans = trans_find_by_id(lchan->subscr,
|
||||
GSM48_PDISC_SMS, i);
|
||||
if (!ptrans)
|
||||
continue;
|
||||
|
||||
DEBUGP(DSMS, "Implicit CP-ACK for trans_id=%x\n", i);
|
||||
|
||||
/* Finish it for good */
|
||||
bsc_del_timer(&ptrans->sms.cp_timer);
|
||||
ptrans->sms.cp_state = GSM411_CPS_IDLE;
|
||||
trans_free(ptrans);
|
||||
}
|
||||
}
|
||||
|
||||
/* 5.2.3.1.3: MO state exists when SMC has received
|
||||
* CP-DATA, including sending of the assoc. CP-ACK */
|
||||
/* 5.2.3.2.4: MT state exists when SMC has received
|
||||
|
@ -940,10 +1047,14 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
|||
struct gsm_trans *trans;
|
||||
u_int8_t *data, *rp_ud_len;
|
||||
u_int8_t msg_ref = 42;
|
||||
u_int8_t transaction_id;
|
||||
int transaction_id;
|
||||
int rc;
|
||||
|
||||
transaction_id = 4; /* FIXME: we always use 4 for now */
|
||||
transaction_id = trans_assign_trans_id(lchan->subscr, GSM48_PDISC_SMS, 0);
|
||||
if (transaction_id == -1) {
|
||||
LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
|
@ -953,7 +1064,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
|||
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
|
||||
transaction_id, new_callref++);
|
||||
if (!trans) {
|
||||
DEBUGP(DSMS, "No memory for trans\n");
|
||||
LOGP(DSMS, LOGL_ERROR, "No memory for trans\n");
|
||||
/* FIXME: send some error message */
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -1001,6 +1112,8 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
|||
|
||||
DEBUGP(DSMS, "TX: SMS DELIVER\n");
|
||||
|
||||
counter_inc(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 */
|
||||
}
|
||||
|
|
|
@ -27,21 +27,12 @@
|
|||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/statistics.h>
|
||||
|
||||
void *tall_bsc_ctx;
|
||||
|
||||
const char *get_value_string(const struct value_string *vs, u_int32_t val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0;; i++) {
|
||||
if (vs[i].value == 0 && vs[i].str == NULL)
|
||||
break;
|
||||
if (vs[i].value == val)
|
||||
return vs[i].str;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
static LLIST_HEAD(bts_models);
|
||||
|
||||
void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
|
||||
u_int8_t e1_ts, u_int8_t e1_ts_ss)
|
||||
|
@ -91,7 +82,7 @@ static const char *lchan_names[] = {
|
|||
[GSM_LCHAN_UNKNOWN] = "UNKNOWN",
|
||||
};
|
||||
|
||||
const char *gsm_lchan_name(enum gsm_chan_t c)
|
||||
const char *gsm_lchant_name(enum gsm_chan_t c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(lchan_names))
|
||||
return "INVALID";
|
||||
|
@ -99,6 +90,20 @@ const char *gsm_lchan_name(enum gsm_chan_t c)
|
|||
return lchan_names[c];
|
||||
}
|
||||
|
||||
static const struct value_string lchan_s_names[] = {
|
||||
{ LCHAN_S_NONE, "NONE" },
|
||||
{ LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
|
||||
{ LCHAN_S_ACTIVE, "ACTIVE" },
|
||||
{ LCHAN_S_INACTIVE, "INACTIVE" },
|
||||
{ LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
const char *gsm_lchans_name(enum gsm_lchan_state s)
|
||||
{
|
||||
return get_value_string(lchan_s_names, s);
|
||||
}
|
||||
|
||||
static const char *chreq_names[] = {
|
||||
[GSM_CHREQ_REASON_EMERG] = "EMERGENCY",
|
||||
[GSM_CHREQ_REASON_PAG] = "PAGING",
|
||||
|
@ -115,6 +120,29 @@ const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
|
|||
return chreq_names[c];
|
||||
}
|
||||
|
||||
static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
|
||||
{
|
||||
struct gsm_bts_model *model;
|
||||
|
||||
llist_for_each_entry(model, &bts_models, list) {
|
||||
if (model->type == type)
|
||||
return model;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int gsm_bts_model_register(struct gsm_bts_model *model)
|
||||
{
|
||||
if (bts_model_find(model->type))
|
||||
return -EEXIST;
|
||||
|
||||
tlv_def_patch(&model->nm_att_tlvdef, &nm_att_tlvdef);
|
||||
llist_add_tail(&model->list, &bts_models);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
|
||||
|
@ -125,6 +153,7 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
|
|||
|
||||
trx->bts = bts;
|
||||
trx->nr = bts->num_trx++;
|
||||
trx->nm_state.administrative = NM_STATE_UNLOCKED;
|
||||
|
||||
for (k = 0; k < TRX_NR_TS; k++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[k];
|
||||
|
@ -144,6 +173,9 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
|
|||
}
|
||||
}
|
||||
|
||||
if (trx->nr != 0)
|
||||
trx->nominal_power = bts->c0->nominal_power;
|
||||
|
||||
llist_add_tail(&trx->list, &bts->trx_list);
|
||||
|
||||
return trx;
|
||||
|
@ -153,19 +185,38 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
|
|||
u_int8_t tsc, u_int8_t bsic)
|
||||
{
|
||||
struct gsm_bts *bts = talloc_zero(net, struct gsm_bts);
|
||||
struct gsm_bts_model *model = bts_model_find(type);
|
||||
int i;
|
||||
|
||||
if (!bts)
|
||||
return NULL;
|
||||
|
||||
if (!model && type != GSM_BTS_TYPE_UNKNOWN) {
|
||||
talloc_free(bts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bts->network = net;
|
||||
bts->nr = net->num_bts++;
|
||||
bts->type = type;
|
||||
bts->model = model;
|
||||
bts->tsc = tsc;
|
||||
bts->bsic = bsic;
|
||||
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);
|
||||
bts->si_common.rach_control.re = 1; /* no re-establishment */
|
||||
bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */
|
||||
bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */
|
||||
bts->si_common.rach_control.t2 = 4; /* no emergency calls */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) {
|
||||
bts->gprs.nsvc[i].bts = bts;
|
||||
|
@ -197,11 +248,49 @@ 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->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED;
|
||||
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);
|
||||
INIT_LLIST_HEAD(&net->bts_list);
|
||||
|
||||
net->stats.chreq.total = counter_alloc("net.chreq.total");
|
||||
net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel");
|
||||
net->stats.handover.attempted = counter_alloc("net.handover.attempted");
|
||||
net->stats.handover.no_channel = counter_alloc("net.handover.no_channel");
|
||||
net->stats.handover.timeout = counter_alloc("net.handover.timeout");
|
||||
net->stats.handover.completed = counter_alloc("net.handover.completed");
|
||||
net->stats.handover.failed = counter_alloc("net.handover.failed");
|
||||
net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach");
|
||||
net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal");
|
||||
net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic");
|
||||
net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count");
|
||||
net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject");
|
||||
net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept");
|
||||
net->stats.paging.attempted = counter_alloc("net.paging.attempted");
|
||||
net->stats.paging.detached = counter_alloc("net.paging.detached");
|
||||
net->stats.paging.completed = counter_alloc("net.paging.completed");
|
||||
net->stats.paging.expired = counter_alloc("net.paging.expired");
|
||||
net->stats.sms.submitted = counter_alloc("net.sms.submitted");
|
||||
net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver");
|
||||
net->stats.sms.delivered = counter_alloc("net.sms.delivered");
|
||||
net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem");
|
||||
net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other");
|
||||
net->stats.call.dialled = counter_alloc("net.call.dialled");
|
||||
net->stats.call.alerted = counter_alloc("net.call.alerted");
|
||||
net->stats.call.connected = counter_alloc("net.call.connected");
|
||||
|
||||
net->mncc_recv = mncc_recv;
|
||||
|
||||
return net;
|
||||
|
@ -222,6 +311,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;
|
||||
|
@ -239,6 +347,15 @@ struct gsm_bts_trx *gsm_bts_trx_num(struct gsm_bts *bts, int num)
|
|||
|
||||
static char ts2str[255];
|
||||
|
||||
char *gsm_trx_name(struct gsm_bts_trx *trx)
|
||||
{
|
||||
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)",
|
||||
trx->bts->nr, trx->nr);
|
||||
|
||||
return ts2str;
|
||||
}
|
||||
|
||||
|
||||
char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
|
||||
{
|
||||
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
|
||||
|
@ -247,6 +364,16 @@ char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
|
|||
return ts2str;
|
||||
}
|
||||
|
||||
char *gsm_lchan_name(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_bts_trx_ts *ts = lchan->ts;
|
||||
|
||||
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)",
|
||||
ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr);
|
||||
|
||||
return ts2str;
|
||||
}
|
||||
|
||||
static const char *bts_types[] = {
|
||||
[GSM_BTS_TYPE_UNKNOWN] = "unknown",
|
||||
[GSM_BTS_TYPE_BS11] = "bs11",
|
||||
|
@ -270,6 +397,17 @@ const char *btstype2str(enum gsm_bts_type type)
|
|||
return bts_types[type];
|
||||
}
|
||||
|
||||
struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
if (trx->nr == nr)
|
||||
return trx;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for a BTS in the given Location Area; optionally start searching
|
||||
* with start_bts (for continuing to search after the first result) */
|
||||
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
|
||||
|
@ -361,3 +499,63 @@ 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;
|
||||
}
|
||||
|
||||
int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type)
|
||||
{
|
||||
struct gsm_bts_model *model;
|
||||
|
||||
model = bts_model_find(type);
|
||||
if (!model)
|
||||
return -EINVAL;
|
||||
|
||||
bts->type = type;
|
||||
bts->model = model;
|
||||
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_NANOBTS:
|
||||
/* Set the default OML Stream ID to 0xff */
|
||||
bts->oml_tei = 0xff;
|
||||
bts->c0->nominal_power = 23;
|
||||
break;
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
|
||||
extern struct llist_head *subscr_bsc_active_subscriber(void);
|
||||
|
||||
char *subscr_name(struct gsm_subscriber *subscr)
|
||||
{
|
||||
if (strlen(subscr->name))
|
||||
return subscr->name;
|
||||
|
||||
return subscr->imsi;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net,
|
||||
u_int32_t tmsi)
|
||||
{
|
||||
|
@ -100,12 +108,15 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
|
|||
s->net = bts->network;
|
||||
/* Indicate "attached to LAC" */
|
||||
s->lac = bts->location_area_code;
|
||||
LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n",
|
||||
subscr_name(s), s->lac);
|
||||
dispatch_signal(SS_SUBSCR, S_SUBSCR_ATTACHED, s);
|
||||
break;
|
||||
case GSM_SUBSCRIBER_UPDATE_DETACHED:
|
||||
/* Only detach if we are currently in this area */
|
||||
if (bts->location_area_code == s->lac)
|
||||
s->lac = GSM_LAC_RESERVED_DETACHED;
|
||||
LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s));
|
||||
dispatch_signal(SS_SUBSCR, S_SUBSCR_DETACHED, s);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -137,7 +137,7 @@ static void subscr_free(struct gsm_subscriber *subscr)
|
|||
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count++;
|
||||
DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
|
||||
DEBUGP(DREF, "subscr %s usage increases usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
return subscr;
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
|
|||
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count--;
|
||||
DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
|
||||
DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
if (subscr->use_count <= 0)
|
||||
subscr_free(subscr);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
/* 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);
|
||||
|
||||
counter_inc(bts->network->stats.handover.attempted);
|
||||
|
||||
new_lchan = lchan_alloc(bts, old_lchan->type);
|
||||
if (!new_lchan) {
|
||||
LOGP(DHO, LOGL_NOTICE, "No free channel\n");
|
||||
counter_inc(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;
|
||||
struct gsm_network *net = ho->new_lchan->ts->trx->bts->network;
|
||||
|
||||
DEBUGP(DHO, "HO T3103 expired\n");
|
||||
counter_inc(net->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 gsm_network *net = new_lchan->ts->trx->bts->network;
|
||||
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;
|
||||
}
|
||||
|
||||
LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
|
||||
"%u->%u\n", subscr_name(ho->old_lchan->subscr),
|
||||
ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
|
||||
ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
|
||||
|
||||
counter_inc(net->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;
|
||||
lchan_auto_release(ho->old_lchan);
|
||||
|
||||
/* 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 gsm_network *net = old_lchan->ts->trx->bts->network;
|
||||
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;
|
||||
}
|
||||
|
||||
counter_inc(net->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) {
|
||||
/* it is perfectly normal, we have CRCX even in non-HO cases */
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
|
@ -213,7 +213,7 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
|
|||
&site_id, &bts_id, &trx_id);
|
||||
bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id);
|
||||
if (!bts) {
|
||||
DEBUGP(DINP, "Unable to find BTS configuration for "
|
||||
LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for "
|
||||
" %u/%u/%u, disconnecting\n", site_id, bts_id,
|
||||
trx_id);
|
||||
return -EIO;
|
||||
|
@ -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
|
||||
|
@ -270,7 +270,8 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
|
|||
hh = (struct ipaccess_head *) msg->data;
|
||||
ret = recv(bfd->fd, msg->data, 3, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "recv error %s\n", strerror(errno));
|
||||
if (errno != EAGAIN)
|
||||
LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
*error = ret;
|
||||
return NULL;
|
||||
|
@ -287,7 +288,7 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
|
|||
len = ntohs(hh->len);
|
||||
ret = recv(bfd->fd, msg->l2h, len, 0);
|
||||
if (ret < len) {
|
||||
fprintf(stderr, "short read!\n");
|
||||
LOGP(DINP, LOGL_ERROR, "short read!\n");
|
||||
msgb_free(msg);
|
||||
*error = -EIO;
|
||||
return NULL;
|
||||
|
@ -310,7 +311,13 @@ static int handle_ts1_read(struct bsc_fd *bfd)
|
|||
msg = ipaccess_read_msg(bfd, &error);
|
||||
if (!msg) {
|
||||
if (error == 0) {
|
||||
fprintf(stderr, "BTS disappeared, dead socket\n");
|
||||
link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0);
|
||||
if (link) {
|
||||
link->trx->bts->ip_access.flags = 0;
|
||||
LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n",
|
||||
link->trx->bts->nr);
|
||||
} else
|
||||
LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n");
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
|
||||
bsc_unregister_fd(bfd);
|
||||
|
@ -340,7 +347,8 @@ static int handle_ts1_read(struct bsc_fd *bfd)
|
|||
|
||||
link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
|
||||
if (!link) {
|
||||
printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto);
|
||||
LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
|
||||
"hh->proto=0x%02x\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -348,21 +356,21 @@ 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;
|
||||
default:
|
||||
DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
|
||||
LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
}
|
||||
|
@ -462,7 +470,7 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
|
|||
if (what & BSC_FD_WRITE)
|
||||
rc = handle_ts1_write(bfd);
|
||||
} else
|
||||
fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -492,7 +500,8 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
|||
perror("accept");
|
||||
return ret;
|
||||
}
|
||||
DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
|
||||
LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n",
|
||||
inet_ntoa(sa.sin_addr));
|
||||
|
||||
line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
|
||||
if (!line) {
|
||||
|
@ -514,7 +523,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
|||
bfd->when = BSC_FD_READ;
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not register FD\n");
|
||||
LOGP(DINP, LOGL_ERROR, "could not register FD\n");
|
||||
close(bfd->fd);
|
||||
talloc_free(line);
|
||||
return ret;
|
||||
|
@ -550,13 +559,13 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
|||
perror("accept");
|
||||
return bfd->fd;
|
||||
}
|
||||
DEBUGP(DINP, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
|
||||
LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
|
||||
bfd->priv_nr = 2;
|
||||
bfd->cb = ipaccess_fd_cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not register FD\n");
|
||||
LOGP(DINP, LOGL_ERROR, "could not register FD\n");
|
||||
close(bfd->fd);
|
||||
talloc_free(bfd);
|
||||
return ret;
|
||||
|
@ -587,7 +596,7 @@ static int make_sock(struct bsc_fd *bfd, u_int16_t port,
|
|||
|
||||
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not bind l2 socket %s\n",
|
||||
LOGP(DINP, LOGL_ERROR, "could not bind l2 socket %s\n",
|
||||
strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -623,7 +632,7 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
|
|||
|
||||
ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not connect socket\n");
|
||||
LOGP(DINP, LOGL_ERROR, "could not connect socket\n");
|
||||
close(bfd->fd);
|
||||
return ret;
|
||||
}
|
||||
|
@ -657,12 +666,12 @@ int ipaccess_setup(struct gsm_network *gsmnet)
|
|||
e1h->gsmnet = gsmnet;
|
||||
|
||||
/* Listen for OML connections */
|
||||
ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb);
|
||||
ret = make_sock(&e1h->listen_fd, IPA_TCP_PORT_OML, listen_fd_cb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Listen for RSL connections */
|
||||
ret = make_sock(&e1h->rsl_listen_fd, 3003, rsl_listen_fd_cb);
|
||||
ret = make_sock(&e1h->rsl_listen_fd, IPA_TCP_PORT_RSL, rsl_listen_fd_cb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* ip.access nanoBTS configuration tool */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther
|
||||
* (C) 2009 by On Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -24,6 +26,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
@ -39,6 +43,7 @@
|
|||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/talloc.h>
|
||||
|
||||
static struct gsm_network *gsmnet;
|
||||
|
||||
|
@ -48,6 +53,21 @@ static char *prim_oml_ip;
|
|||
static char *unit_id;
|
||||
static u_int16_t nv_flags;
|
||||
static u_int16_t nv_mask;
|
||||
static char *software = NULL;
|
||||
static int sw_load_state = 0;
|
||||
static int oml_state = 0;
|
||||
|
||||
struct sw_load {
|
||||
u_int8_t file_id[255];
|
||||
u_int8_t file_id_len;
|
||||
|
||||
u_int8_t file_version[255];
|
||||
u_int8_t file_version_len;
|
||||
};
|
||||
|
||||
static void *tall_ctx_config = NULL;
|
||||
static struct sw_load *sw_load1 = NULL;
|
||||
static struct sw_load *sw_load2 = NULL;
|
||||
|
||||
/*
|
||||
static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 };
|
||||
|
@ -68,6 +88,28 @@ static int ipacc_msg_nack(u_int8_t mt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ipacc_msg_ack(u_int8_t mt, struct gsm_bts *bts)
|
||||
{
|
||||
if (sw_load_state == 1) {
|
||||
fprintf(stderr, "The new software is activaed.\n");
|
||||
|
||||
if (restart) {
|
||||
abis_nm_ipaccess_restart(bts);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
} else if (oml_state == 1) {
|
||||
fprintf(stderr, "Set the primary OML IP.\n");
|
||||
if (restart) {
|
||||
abis_nm_ipaccess_restart(bts);
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipacc_ferr_elem {
|
||||
int16_t freq_err;
|
||||
u_int8_t freq_qual;
|
||||
|
@ -149,14 +191,25 @@ static int test_rep(void *_msg)
|
|||
static int nm_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
u_int8_t *msg_type;
|
||||
struct ipacc_ack_signal_data *ipacc_data;
|
||||
|
||||
switch (signal) {
|
||||
case S_NM_IPACC_NACK:
|
||||
msg_type = signal_data;
|
||||
return ipacc_msg_nack(*msg_type);
|
||||
ipacc_data = signal_data;
|
||||
return ipacc_msg_nack(ipacc_data->msg_type);
|
||||
case S_NM_IPACC_ACK:
|
||||
ipacc_data = signal_data;
|
||||
return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->bts);
|
||||
case S_NM_TEST_REP:
|
||||
return test_rep(signal_data);
|
||||
case S_NM_IPACC_RESTART_ACK:
|
||||
printf("The BTS has acked the restart. Exiting.\n");
|
||||
exit(0);
|
||||
break;
|
||||
case S_NM_IPACC_RESTART_NACK:
|
||||
printf("The BTS has nacked the restart. Exiting.\n");
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -164,6 +217,84 @@ static int nm_sig_cb(unsigned int subsys, unsigned int signal,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* callback function passed to the ABIS OML code */
|
||||
static int percent;
|
||||
static int percent_old;
|
||||
static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
|
||||
void *data, void *param)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
if (hook != GSM_HOOK_NM_SWLOAD)
|
||||
return 0;
|
||||
|
||||
bts = (struct gsm_bts *) data;
|
||||
|
||||
switch (event) {
|
||||
case NM_MT_LOAD_INIT_ACK:
|
||||
fprintf(stdout, "Software Load Initiate ACK\n");
|
||||
break;
|
||||
case NM_MT_LOAD_INIT_NACK:
|
||||
fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
|
||||
exit(5);
|
||||
break;
|
||||
case NM_MT_LOAD_END_ACK:
|
||||
fprintf(stderr, "LOAD END ACK...");
|
||||
/* now make it the default */
|
||||
sw_load_state = 1;
|
||||
|
||||
msg = msgb_alloc(1024, "sw: nvattr");
|
||||
msg->l2h = msgb_put(msg, 3);
|
||||
msg->l3h = &msg->l2h[3];
|
||||
|
||||
/* activate software */
|
||||
if (sw_load1) {
|
||||
msgb_v_put(msg, NM_ATT_SW_DESCR);
|
||||
msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load1->file_id_len, sw_load1->file_id);
|
||||
msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load1->file_version_len,
|
||||
sw_load1->file_version);
|
||||
}
|
||||
|
||||
if (sw_load2) {
|
||||
msgb_v_put(msg, NM_ATT_SW_DESCR);
|
||||
msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw_load2->file_id_len, sw_load2->file_id);
|
||||
msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw_load2->file_version_len,
|
||||
sw_load2->file_version);
|
||||
}
|
||||
|
||||
/* fill in the data */
|
||||
msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG;
|
||||
msg->l2h[1] = msgb_l3len(msg) >> 8;
|
||||
msg->l2h[2] = msgb_l3len(msg) & 0xff;
|
||||
printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg));
|
||||
abis_nm_ipaccess_set_nvattr(bts->c0, msg->l2h, msgb_l2len(msg));
|
||||
msgb_free(msg);
|
||||
break;
|
||||
case NM_MT_LOAD_END_NACK:
|
||||
fprintf(stderr, "ERROR: Software Load End NACK\n");
|
||||
exit(3);
|
||||
break;
|
||||
case NM_MT_ACTIVATE_SW_NACK:
|
||||
fprintf(stderr, "ERROR: Activate Software NACK\n");
|
||||
exit(4);
|
||||
break;
|
||||
case NM_MT_ACTIVATE_SW_ACK:
|
||||
break;
|
||||
case NM_MT_LOAD_SEG_ACK:
|
||||
percent = abis_nm_software_load_status(bts);
|
||||
if (percent > percent_old)
|
||||
printf("Software Download Progress: %d%%\n", percent);
|
||||
percent_old = percent;
|
||||
break;
|
||||
case NM_MT_LOAD_ABORT:
|
||||
fprintf(stderr, "ERROR: Load aborted by the BTS.\n");
|
||||
exit(6);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bootstrap_om(struct gsm_bts *bts)
|
||||
{
|
||||
int len;
|
||||
|
@ -182,7 +313,7 @@ static void bootstrap_om(struct gsm_bts *bts)
|
|||
memcpy(buf+3, unit_id, len);
|
||||
buf[3+len] = 0;
|
||||
printf("setting Unit ID to '%s'\n", unit_id);
|
||||
abis_nm_ipaccess_set_nvattr(bts, buf, 3+len+1);
|
||||
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1);
|
||||
}
|
||||
if (prim_oml_ip) {
|
||||
struct in_addr ia;
|
||||
|
@ -205,7 +336,8 @@ static void bootstrap_om(struct gsm_bts *bts)
|
|||
*cur++ = 0;
|
||||
*cur++ = 0;
|
||||
printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
|
||||
abis_nm_ipaccess_set_nvattr(bts, buf, 3+len);
|
||||
oml_state = 1;
|
||||
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
|
||||
}
|
||||
if (nv_mask) {
|
||||
len = 4;
|
||||
|
@ -219,13 +351,14 @@ static void bootstrap_om(struct gsm_bts *bts)
|
|||
*cur++ = nv_mask >> 8;
|
||||
printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
|
||||
nv_flags, nv_mask);
|
||||
abis_nm_ipaccess_set_nvattr(bts, buf, 3+len);
|
||||
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
|
||||
}
|
||||
|
||||
if (restart) {
|
||||
if (restart && !prim_oml_ip && !software) {
|
||||
printf("restarting BTS\n");
|
||||
abis_nm_ipaccess_restart(bts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
|
@ -257,17 +390,146 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
|||
{
|
||||
if (evt == EVT_STATECHG_OPER &&
|
||||
obj_class == NM_OC_RADIO_CARRIER &&
|
||||
new_state->availability == 3 &&
|
||||
net_listen_testnr) {
|
||||
new_state->availability == 3) {
|
||||
struct gsm_bts_trx *trx = obj;
|
||||
u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
|
||||
abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
|
||||
net_listen_testnr, 1,
|
||||
phys_config, sizeof(phys_config));
|
||||
|
||||
if (net_listen_testnr) {
|
||||
u_int8_t phys_config[] = { 0x02, 0x0a, 0x00, 0x01, 0x02 };
|
||||
abis_nm_perform_test(trx->bts, 2, 0, 0, 0xff,
|
||||
net_listen_testnr, 1,
|
||||
phys_config, sizeof(phys_config));
|
||||
} else if (software) {
|
||||
int rc;
|
||||
printf("Attempting software upload with '%s'\n", software);
|
||||
rc = abis_nm_software_load(trx->bts, software, 19, 0, swload_cbfn, trx->bts);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to start software load\n");
|
||||
exit(-3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sw_load *create_swload(struct sdp_header *header)
|
||||
{
|
||||
struct sw_load *load;
|
||||
|
||||
load = talloc_zero(tall_ctx_config, struct sw_load);
|
||||
|
||||
strncpy((char *)load->file_id, header->firmware_info.sw_part, 20);
|
||||
load->file_id_len = strlen(header->firmware_info.sw_part) + 1;
|
||||
|
||||
strncpy((char *)load->file_version, header->firmware_info.version, 20);
|
||||
load->file_version_len = strlen(header->firmware_info.version) + 1;
|
||||
|
||||
return load;
|
||||
}
|
||||
|
||||
static void find_sw_load_params(const char *filename)
|
||||
{
|
||||
struct stat stat;
|
||||
struct sdp_header *header;
|
||||
struct llist_head *entry;
|
||||
int fd;
|
||||
void *tall_firm_ctx = 0;
|
||||
|
||||
entry = talloc_zero(tall_firm_ctx, struct llist_head);
|
||||
INIT_LLIST_HEAD(entry);
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (!fd) {
|
||||
perror("nada");
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify the file */
|
||||
if (fstat(fd, &stat) == -1) {
|
||||
perror("Can not stat the file");
|
||||
return;
|
||||
}
|
||||
|
||||
ipaccess_analyze_file(fd, stat.st_size, 0, entry);
|
||||
if (close(fd) != 0) {
|
||||
perror("Close failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* try to find what we are looking for */
|
||||
llist_for_each_entry(header, entry, entry) {
|
||||
if (ntohs(header->firmware_info.more_more_magic) == 0x1000) {
|
||||
sw_load1 = create_swload(header);
|
||||
} else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) {
|
||||
sw_load2 = create_swload(header);
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(tall_firm_ctx);
|
||||
}
|
||||
|
||||
static void analyze_firmware(const char *filename)
|
||||
{
|
||||
struct stat stat;
|
||||
struct sdp_header *header;
|
||||
struct sdp_header_item *sub_entry;
|
||||
struct llist_head *entry;
|
||||
int fd;
|
||||
void *tall_firm_ctx = 0;
|
||||
|
||||
entry = talloc_zero(tall_firm_ctx, struct llist_head);
|
||||
INIT_LLIST_HEAD(entry);
|
||||
|
||||
printf("Opening possible firmware '%s'\n", filename);
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (!fd) {
|
||||
perror("nada");
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify the file */
|
||||
if (fstat(fd, &stat) == -1) {
|
||||
perror("Can not stat the file");
|
||||
return;
|
||||
}
|
||||
|
||||
ipaccess_analyze_file(fd, stat.st_size, 0, entry);
|
||||
if (close(fd) != 0) {
|
||||
perror("Close failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
llist_for_each_entry(header, entry, entry) {
|
||||
printf("Printing header information:\n");
|
||||
printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic));
|
||||
printf("header_length: %u\n", ntohl(header->firmware_info.header_length));
|
||||
printf("file_length: %u\n", ntohl(header->firmware_info.file_length));
|
||||
printf("sw_part: %.20s\n", header->firmware_info.sw_part);
|
||||
printf("text1: %.64s\n", header->firmware_info.text1);
|
||||
printf("time: %.12s\n", header->firmware_info.time);
|
||||
printf("date: %.14s\n", header->firmware_info.date);
|
||||
printf("text2: %.10s\n", header->firmware_info.text2);
|
||||
printf("version: %.20s\n", header->firmware_info.version);
|
||||
printf("subitems...\n");
|
||||
|
||||
llist_for_each_entry(sub_entry, &header->header_list, entry) {
|
||||
printf("\tsomething1: %u\n", sub_entry->header_entry.something1);
|
||||
printf("\ttext1: %.64s\n", sub_entry->header_entry.text1);
|
||||
printf("\ttime: %.12s\n", sub_entry->header_entry.time);
|
||||
printf("\tdate: %.14s\n", sub_entry->header_entry.date);
|
||||
printf("\ttext2: %.10s\n", sub_entry->header_entry.text2);
|
||||
printf("\tversion: %.20s\n", sub_entry->header_entry.version);
|
||||
printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length));
|
||||
printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1));
|
||||
printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2));
|
||||
printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start));
|
||||
printf("\n\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
talloc_free(tall_firm_ctx);
|
||||
}
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
printf("Usage: ipaccess-config\n");
|
||||
|
@ -282,6 +544,8 @@ static void print_help(void)
|
|||
printf(" -l --listen testnr \tPerform specified test number\n");
|
||||
printf(" -h --help this text\n");
|
||||
printf(" -s --stream-id ID\n");
|
||||
printf(" -d --software firmware\n");
|
||||
printf(" -f --firmware firmware Provide firmware information\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
@ -289,6 +553,15 @@ int main(int argc, char **argv)
|
|||
struct gsm_bts *bts;
|
||||
struct sockaddr_in sin;
|
||||
int rc, option_index = 0, stream_id = 0xff;
|
||||
struct debug_target *stderr_target;
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
debug_set_log_level(stderr_target, 0);
|
||||
debug_parse_category_mask(stderr_target, "DNM,0");
|
||||
bts_model_nanobts_init();
|
||||
|
||||
printf("ipaccess-config (C) 2009 by Harald Welte\n");
|
||||
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
|
||||
|
@ -304,9 +577,11 @@ int main(int argc, char **argv)
|
|||
{ "help", 0, 0, 'h' },
|
||||
{ "listen", 1, 0, 'l' },
|
||||
{ "stream-id", 1, 0, 's' },
|
||||
{ "software", 1, 0, 'd' },
|
||||
{ "firmware", 1, 0, 'f' },
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "u:o:rn:l:hs:", long_options,
|
||||
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:", long_options,
|
||||
&option_index);
|
||||
|
||||
if (c == -1)
|
||||
|
@ -336,8 +611,14 @@ int main(int argc, char **argv)
|
|||
break;
|
||||
case 's':
|
||||
stream_id = atoi(optarg);
|
||||
printf("foo: %d\n", stream_id);
|
||||
break;
|
||||
case 'd':
|
||||
software = strdup(optarg);
|
||||
find_sw_load_params(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
analyze_firmware(optarg);
|
||||
exit(0);
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
|
@ -356,6 +637,10 @@ int main(int argc, char **argv)
|
|||
|
||||
bts = gsm_bts_alloc(gsmnet, GSM_BTS_TYPE_NANOBTS, HARDCODED_TSC,
|
||||
HARDCODED_BSIC);
|
||||
/* ip.access supports up to 4 chained TRX */
|
||||
gsm_bts_trx_alloc(bts);
|
||||
gsm_bts_trx_alloc(bts);
|
||||
gsm_bts_trx_alloc(bts);
|
||||
bts->oml_tei = stream_id;
|
||||
|
||||
register_signal_handler(SS_NM, nm_sig_cb, NULL);
|
|
@ -0,0 +1,123 @@
|
|||
/* Routines for parsing an ipacces SDP firmware file */
|
||||
|
||||
/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <openbsc/debug.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/talloc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define PART_LENGTH 138
|
||||
|
||||
static_assert(sizeof(struct sdp_header_entry) == 138, right_entry);
|
||||
static_assert(sizeof(struct sdp_firmware) == 160, _right_header_length);
|
||||
|
||||
/* more magic, the second "int" in the header */
|
||||
static char more_magic[] = { 0x10, 0x02 };
|
||||
|
||||
int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list)
|
||||
{
|
||||
struct sdp_firmware *firmware_header = 0;
|
||||
struct sdp_header *header;
|
||||
char buf[4096];
|
||||
int rc, i;
|
||||
|
||||
rc = read(fd, buf, sizeof(*firmware_header));
|
||||
if (rc < 0) {
|
||||
perror("Can not read header start.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
firmware_header = (struct sdp_firmware *) &buf[0];
|
||||
if (strncmp(firmware_header->magic, " SDP", 4) != 0) {
|
||||
fprintf(stderr, "Wrong magic.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) {
|
||||
fprintf(stderr, "Wrong more magic. Got: 0x%x %x %x %x\n",
|
||||
firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff,
|
||||
firmware_header->more_magic[2] & 0xff, firmware_header->more_magic[3] & 0xff);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!firmware_header)
|
||||
return -1;
|
||||
|
||||
if (ntohl(firmware_header->file_length) != st_size) {
|
||||
fprintf(stderr, "The filesize and the header do not match.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* add the firmware */
|
||||
header = talloc_zero(list, struct sdp_header);
|
||||
header->firmware_info = *firmware_header;
|
||||
INIT_LLIST_HEAD(&header->header_list);
|
||||
llist_add(&header->entry, list);
|
||||
|
||||
/* this semantic appears to be only the case for 0x0000 */
|
||||
if (firmware_header->more_more_magic != 0)
|
||||
return -1;
|
||||
|
||||
if (ntohs(firmware_header->part_length) % PART_LENGTH != 0) {
|
||||
fprintf(stderr, "The part length seems to be wrong.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* look into each firmware now */
|
||||
for (i = 0; i < ntohs(firmware_header->part_length) / PART_LENGTH; ++i) {
|
||||
struct sdp_header_entry entry;
|
||||
struct sdp_header_item *header_entry;
|
||||
unsigned int offset = base_offset + sizeof(struct sdp_firmware);
|
||||
offset += i * 138;
|
||||
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
fprintf(stderr, "Can not seek to the offset: %u.\n", offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = read(fd, &entry, sizeof(entry));
|
||||
if (rc != sizeof(entry)) {
|
||||
fprintf(stderr, "Can not read the header entry.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* now we need to find the SDP file... */
|
||||
offset = ntohl(entry.start) + 4 + base_offset;
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
perror("can't seek to sdp");
|
||||
return -1;
|
||||
}
|
||||
|
||||
header_entry = talloc_zero(header, struct sdp_header_item);
|
||||
header_entry->header_entry = entry;
|
||||
llist_add(&header_entry->entry, &header->header_list);
|
||||
|
||||
ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
!
|
||||
! MGCP configuration hand edited
|
||||
! !
|
||||
password foo
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
mgcp
|
||||
! local ip 213.167.134.14
|
||||
bts ip 172.16.252.43
|
||||
bind ip 213.167.134.141
|
||||
bind port 2427
|
||||
bind early 1
|
||||
rtp base 4000
|
||||
sdp audio payload number 98
|
||||
sdp audio payload name AMR/8000
|
||||
number endpoints 31
|
||||
loop 1
|
|
@ -0,0 +1,228 @@
|
|||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
||||
/* The main method to drive it as a standalone process */
|
||||
|
||||
/*
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2009 by On-Waves
|
||||
* 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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/mgcp.h>
|
||||
#include <openbsc/telnet_interface.h>
|
||||
|
||||
#include <vty/command.h>
|
||||
#include <vty/vty.h>
|
||||
|
||||
/* this is here for the vty... it will never be called */
|
||||
void subscr_put() { abort(); }
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
#warning "Make use of the rtp proxy code"
|
||||
|
||||
static int source_port = 2427;
|
||||
static const char *source_addr = "0.0.0.0";
|
||||
static struct bsc_fd bfd;
|
||||
static int first_request = 1;
|
||||
|
||||
static char *config_file = "mgcp.cfg";
|
||||
|
||||
/* used by msgb and mgcp */
|
||||
void *tall_bsc_ctx = NULL;
|
||||
|
||||
unsigned int rtp_base_port = RTP_PORT_DEFAULT;
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf("Some useful help...\n");
|
||||
printf(" -h --help is printing this text.\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char** argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch(c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
break;
|
||||
case 'c':
|
||||
config_file = talloc_strdup(tall_bsc_ctx, optarg);
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static int read_call_agent(struct bsc_fd *fd, unsigned int what)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t slen = sizeof(addr);
|
||||
struct msgb *msg;
|
||||
struct msgb *resp;
|
||||
|
||||
msg = (struct msgb *) fd->data;
|
||||
|
||||
/* read one less so we can use it as a \0 */
|
||||
int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0,
|
||||
(struct sockaddr *) &addr, &slen);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to read");
|
||||
return -1;
|
||||
} else if (slen > sizeof(addr)) {
|
||||
fprintf(stderr, "Gateway received message from outerspace: %d %d\n",
|
||||
slen, sizeof(addr));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first_request) {
|
||||
first_request = 0;
|
||||
resp = mgcp_create_rsip();
|
||||
|
||||
if (resp) {
|
||||
sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0,
|
||||
(struct sockaddr *) &addr, sizeof(addr));
|
||||
msgb_free(resp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle message now */
|
||||
msg->l2h = msgb_put(msg, rc);
|
||||
resp = mgcp_handle_message(msg);
|
||||
msgb_reset(msg);
|
||||
|
||||
if (resp) {
|
||||
sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
|
||||
msgb_free(resp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int bsc_vty_init(struct gsm_network *dummy)
|
||||
{
|
||||
cmd_init(1);
|
||||
vty_init();
|
||||
|
||||
mgcp_vty_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
struct gsm_network dummy_network;
|
||||
struct sockaddr_in addr;
|
||||
int on = 1, rc;
|
||||
struct debug_target *stderr_target;
|
||||
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
telnet_init(&dummy_network, 4243);
|
||||
rc = mgcp_parse_config(config_file, &dummy_network);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
|
||||
/* we need to bind a socket */
|
||||
if (rc == 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DEBUGP(DMGCP, "Configured for MGCP.\n");
|
||||
}
|
||||
|
||||
/* initialisation */
|
||||
srand(time(NULL));
|
||||
|
||||
/* main loop */
|
||||
while (1) {
|
||||
bsc_select_main(0);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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,8 +26,9 @@
|
|||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
static void *tall_msgb_ctx;
|
||||
void *tall_msgb_ctx;
|
||||
|
||||
struct msgb *msgb_alloc(u_int16_t size, const char *name)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -93,8 +96,3 @@ void msgb_reset(struct msgb *msg)
|
|||
msg->l3h = NULL;
|
||||
msg->smsh = NULL;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load_trau_msgb(void)
|
||||
{
|
||||
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb");
|
||||
}
|
||||
|
|
|
@ -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,7 +212,9 @@ static void paging_T3113_expired(void *data)
|
|||
cbfn = req->cbfn;
|
||||
paging_remove_request(&req->bts->paging, req);
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
|
||||
counter_inc(req->bts->network->stats.paging.expired);
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data);
|
||||
if (cbfn)
|
||||
cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
|
||||
cbfn_param);
|
||||
|
@ -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;
|
||||
|
||||
counter_inc(network->stats.paging.attempted);
|
||||
|
||||
/* start paging subscriber on all BTS within Location Area */
|
||||
do {
|
||||
int rc;
|
||||
|
@ -261,6 +265,11 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
|
|||
bts = gsm_bts_by_lac(network, subscr->lac, bts);
|
||||
if (!bts)
|
||||
break;
|
||||
|
||||
/* skip all currently inactive TRX */
|
||||
if (!trx_is_usable(bts->c0))
|
||||
continue;
|
||||
|
||||
num_pages++;
|
||||
|
||||
/* Trigger paging, pass any error to caller */
|
||||
|
@ -269,6 +278,9 @@ int paging_request(struct gsm_network *network, struct gsm_subscriber *subscr,
|
|||
return rc;
|
||||
} while (1);
|
||||
|
||||
if (num_pages == 0)
|
||||
counter_inc(network->stats.paging.detached);
|
||||
|
||||
return num_pages;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
/* 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 = 1;
|
||||
|
||||
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, 7);
|
||||
return bv.data_len;
|
||||
}
|
||||
|
||||
/* 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 = 4;
|
||||
|
||||
/* 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);
|
||||
|
||||
bitvec_spare_padding(&bv, (bv.data_len*8)-1);
|
||||
return bv.data_len;
|
||||
}
|
||||
|
||||
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 = 10; /* 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 bv.data_len;
|
||||
}
|
||||
|
||||
/* 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 = 20;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
bitvec_spare_padding(&bv, (bv.data_len*8)-1);
|
||||
return bv.data_len;
|
||||
}
|
|
@ -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;
|
||||
|
@ -57,11 +89,11 @@ static int paging_sig_cb(unsigned int subsys, unsigned int signal,
|
|||
struct paging_signal_data *psig_data = signal_data;
|
||||
|
||||
switch (signal) {
|
||||
case S_PAGING_COMPLETED:
|
||||
case S_PAGING_SUCCEEDED:
|
||||
/* 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;
|
||||
case S_PAGING_EXPIRED:
|
||||
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,214 @@ 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;
|
||||
|
||||
LOGP(DMUX, LOGL_NOTICE,
|
||||
"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 +335,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 +366,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 +403,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 +457,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 +666,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;
|
||||
|
|
|
@ -95,14 +95,20 @@ restart:
|
|||
llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
|
||||
int flags = 0;
|
||||
|
||||
if (FD_ISSET(ufd->fd, &readset))
|
||||
if (FD_ISSET(ufd->fd, &readset)) {
|
||||
flags |= BSC_FD_READ;
|
||||
FD_CLR(ufd->fd, &readset);
|
||||
}
|
||||
|
||||
if (FD_ISSET(ufd->fd, &writeset))
|
||||
if (FD_ISSET(ufd->fd, &writeset)) {
|
||||
flags |= BSC_FD_WRITE;
|
||||
FD_CLR(ufd->fd, &writeset);
|
||||
}
|
||||
|
||||
if (FD_ISSET(ufd->fd, &exceptset))
|
||||
if (FD_ISSET(ufd->fd, &exceptset)) {
|
||||
flags |= BSC_FD_EXCEPT;
|
||||
FD_CLR(ufd->fd, &exceptset);
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
work = 1;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
|
||||
/* paging of the requested subscriber has completed */
|
||||
static int paging_cb_silent(unsigned int hooknum, unsigned int event,
|
||||
struct msgb *msg, void *_lchan, void *_data)
|
||||
{
|
||||
|
@ -53,6 +54,7 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
|
|||
case GSM_PAGING_SUCCEEDED:
|
||||
DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n",
|
||||
lchan->ts->nr, lchan->ts->trx->arfcn);
|
||||
lchan->silent_call = 1;
|
||||
/* increment lchan reference count */
|
||||
dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
|
||||
use_lchan(lchan);
|
||||
|
@ -69,15 +71,58 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
|
|||
return rc;
|
||||
}
|
||||
|
||||
int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data)
|
||||
/* receive a layer 3 message from a silent call */
|
||||
int silent_call_rx(struct msgb *msg)
|
||||
{
|
||||
/* FIXME: do something like sending it through a UDP port */
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msg_match {
|
||||
u_int8_t pdisc;
|
||||
u_int8_t msg_type;
|
||||
};
|
||||
|
||||
/* list of messages that are handled inside OpenBSC, even in a silent call */
|
||||
static const struct msg_match silent_call_accept[] = {
|
||||
{ GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST },
|
||||
{ GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ },
|
||||
};
|
||||
|
||||
/* decide if we need to reroute a message as part of a silent call */
|
||||
int silent_call_reroute(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
u_int8_t pdisc = gh->proto_discr & 0x0f;
|
||||
int i;
|
||||
|
||||
/* if we're not part of a silent call, never reroute */
|
||||
if (!msg->lchan->silent_call)
|
||||
return 0;
|
||||
|
||||
/* check if we are a special message that is handled in openbsc */
|
||||
for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) {
|
||||
if (silent_call_accept[i].pdisc == pdisc &&
|
||||
silent_call_accept[i].msg_type == gh->msg_type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* otherwise, reroute */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* initiate a silent call with a given subscriber */
|
||||
int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = paging_request(subscr->net, subscr, RSL_CHANNEED_TCH_F,
|
||||
rc = paging_request(subscr->net, subscr, type,
|
||||
paging_cb_silent, data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* end a silent call with a given subscriber */
|
||||
int gsm_silent_call_stop(struct gsm_subscriber *subscr)
|
||||
{
|
||||
struct gsm_lchan *lchan;
|
||||
|
@ -86,7 +131,10 @@ int gsm_silent_call_stop(struct gsm_subscriber *subscr)
|
|||
if (!lchan)
|
||||
return -EINVAL;
|
||||
|
||||
/* FIXME: did we actually establish a silent call for this guy? */
|
||||
/* did we actually establish a silent call for this guy? */
|
||||
if (!lchan->silent_call)
|
||||
return -EINVAL;
|
||||
|
||||
put_lchan(lchan);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* utility routines for keeping some statistics */
|
||||
|
||||
/* (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/signal.h>
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/statistics.h>
|
||||
#include <openbsc/db.h>
|
||||
#include <openbsc/timer.h>
|
||||
|
||||
static LLIST_HEAD(counters);
|
||||
|
||||
void *tall_ctr_ctx;
|
||||
|
||||
struct counter *counter_alloc(const char *name)
|
||||
{
|
||||
struct counter *ctr = talloc_zero(tall_ctr_ctx, struct counter);
|
||||
|
||||
if (!ctr)
|
||||
return NULL;
|
||||
|
||||
ctr->name = name;
|
||||
llist_add_tail(&ctr->list, &counters);
|
||||
|
||||
return ctr;
|
||||
}
|
||||
|
||||
void counter_free(struct counter *ctr)
|
||||
{
|
||||
llist_del(&ctr->list);
|
||||
talloc_free(ctr);
|
||||
}
|
||||
|
||||
int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data)
|
||||
{
|
||||
struct counter *ctr;
|
||||
int rc = 0;
|
||||
|
||||
llist_for_each_entry(ctr, &counters, list) {
|
||||
rc = handle_counter(ctr, data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,469 @@
|
|||
/* 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
|
||||
|
||||
/* verify the sizes of the system information type structs */
|
||||
|
||||
/* rest octets are not part of the struct */
|
||||
static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size);
|
||||
static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control);
|
||||
static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size);
|
||||
static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size);
|
||||
static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size);
|
||||
static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size);
|
||||
|
||||
/* bs11 forgot the l2 len, 0-6 rest octets */
|
||||
static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size);
|
||||
static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size);
|
||||
|
||||
static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size);
|
||||
|
||||
/* 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 = -1;
|
||||
|
||||
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 == -1) {
|
||||
/* Empty set, use 'bit map 0 format' */
|
||||
chan_list[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 */
|
||||
rc = rest_octets_si1(si1->rest_octets, NULL);
|
||||
return sizeof(*si1) + rc;
|
||||
}
|
||||
|
||||
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 sizeof(*si2);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int rc;
|
||||
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 */
|
||||
rc = rest_octets_si3(si3->rest_octets, &si_info);
|
||||
|
||||
return sizeof(*si3) + rc;
|
||||
}
|
||||
|
||||
static int generate_si4(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
int rc;
|
||||
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 */
|
||||
rc = rest_octets_si4(si4->data, &si_info);
|
||||
|
||||
return sizeof(*si4) + rc;
|
||||
}
|
||||
|
||||
static int generate_si5(u_int8_t *output, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm48_system_information_type_5 *si5;
|
||||
int rc, l2_plen = 18;
|
||||
|
||||
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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;
|
||||
|
||||
memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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 sizeof (*si13) + ret;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
|
||||
extern void *tall_msgb_ctx;
|
||||
extern void *tall_fle_ctx;
|
||||
extern void *tall_locop_ctx;
|
||||
extern void *tall_gsms_ctx;
|
||||
|
@ -13,9 +14,11 @@ extern void *tall_tqe_ctx;
|
|||
extern void *tall_trans_ctx;
|
||||
extern void *tall_map_ctx;
|
||||
extern void *tall_upq_ctx;
|
||||
extern void *tall_ctr_ctx;
|
||||
|
||||
void talloc_ctx_init(void)
|
||||
{
|
||||
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
|
||||
tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0,
|
||||
"bs11_file_list_entry");
|
||||
tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper");
|
||||
|
@ -29,4 +32,5 @@ void talloc_ctx_init(void)
|
|||
tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction");
|
||||
tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry");
|
||||
tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry");
|
||||
tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -101,9 +102,9 @@ static void print_welcome(int fd) {
|
|||
int ret;
|
||||
static char *msg =
|
||||
"Welcome to the OpenBSC Control interface\n"
|
||||
"Copyright (C) 2008, 2009 Harald Welte\n"
|
||||
"Copyright (C) 2008-2010 Harald Welte\n"
|
||||
"Contributions by Daniel Willmann, Jan Lübbe, "
|
||||
"Stefan Schmidt, Holger Freyther\n\n"
|
||||
"Stefan Schmidt, Holger Freyther, Andreas Eversberg\n\n"
|
||||
"License GPLv2+: GNU GPL version 2 or later "
|
||||
"<http://gnu.org/licenses/gpl.html>\n"
|
||||
"This is free software: you are free to change "
|
||||
|
@ -119,6 +120,12 @@ int telnet_close_client(struct bsc_fd *fd) {
|
|||
|
||||
close(fd->fd);
|
||||
bsc_unregister_fd(fd);
|
||||
|
||||
if (conn->dbg) {
|
||||
debug_del_target(conn->dbg);
|
||||
talloc_free(conn->dbg);
|
||||
}
|
||||
|
||||
llist_del(&conn->entry);
|
||||
talloc_free(conn);
|
||||
return 0;
|
||||
|
@ -154,7 +161,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 +178,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;
|
||||
}
|
||||
|
|
|
@ -149,6 +149,19 @@ int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
|
|||
return num_parsed;
|
||||
}
|
||||
|
||||
/* take a master (src) tlvdev and fill up all empty slots in 'dst' */
|
||||
void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dst->def); i++) {
|
||||
if (src->def[i].type == TLV_TYPE_NONE)
|
||||
continue;
|
||||
if (dst->def[i].type == TLV_TYPE_NONE)
|
||||
dst->def[i] = src->def[i];
|
||||
}
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void on_dso_load_tlv(void)
|
||||
{
|
||||
int i;
|
||||
|
|
|
@ -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) {
|
||||
|
@ -103,7 +103,7 @@ unauth:
|
|||
if (lchan) {
|
||||
u_int8_t auth_rand[16];
|
||||
/* kick the subscriber off the network */
|
||||
gsm48_tx_mm_auth_req(lchan, auth_rand);
|
||||
gsm48_tx_mm_auth_req(lchan, auth_rand, 0);
|
||||
gsm48_tx_mm_auth_rej(lchan);
|
||||
/* FIXME: close the channel early ?*/
|
||||
//gsm48_send_rr_Release(lchan);
|
||||
|
@ -139,7 +139,7 @@ static int token_sms_cb(unsigned int subsys, unsigned int signal,
|
|||
lchan = lchan_for_subscr(sms->receiver);
|
||||
if (lchan) {
|
||||
/* kick the subscriber off the network */
|
||||
gsm48_tx_mm_auth_req(lchan, auth_rand);
|
||||
gsm48_tx_mm_auth_req(lchan, auth_rand, 0);
|
||||
gsm48_tx_mm_auth_rej(lchan);
|
||||
/* FIXME: close the channel early ?*/
|
||||
//gsm48_send_rr_Release(lchan);
|
||||
|
|
|
@ -119,7 +119,7 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
|
|||
struct gsm_network *net = subscr->net;
|
||||
struct gsm_trans *trans;
|
||||
unsigned int used_tid_bitmask = 0;
|
||||
int i;
|
||||
int i, j, h;
|
||||
|
||||
if (ti_flag)
|
||||
ti_flag = 0x8;
|
||||
|
@ -133,10 +133,39 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
|
|||
used_tid_bitmask |= (1 << trans->transaction_id);
|
||||
}
|
||||
|
||||
for (i = 0; i <= 7; i++) {
|
||||
if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0)
|
||||
return i | ti_flag;
|
||||
/* find a new one, trying to go in a 'circular' pattern */
|
||||
for (h = 6; h > 0; h--)
|
||||
if (used_tid_bitmask & (1 << (h | ti_flag)))
|
||||
break;
|
||||
for (i = 0; i < 7; i++) {
|
||||
j = ((h + i) % 7) | ti_flag;
|
||||
if ((used_tid_bitmask & (1 << j)) == 0)
|
||||
return j;
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -49,6 +49,8 @@ Boston, MA 02111-1307, USA. */
|
|||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/talloc.h>
|
||||
|
||||
void *tall_vty_cmd_ctx;
|
||||
|
||||
/* Command vector which includes some level of command lists. Normally
|
||||
each daemon maintains each own cmdvec. */
|
||||
vector cmdvec;
|
||||
|
@ -173,7 +175,7 @@ char *argv_concat(const char **argv, int argc, int shift)
|
|||
len += strlen(argv[i]) + 1;
|
||||
if (!len)
|
||||
return NULL;
|
||||
p = str = _talloc_zero(tall_vty_ctx, len, "arvg_concat");
|
||||
p = str = _talloc_zero(tall_vty_cmd_ctx, len, "arvg_concat");
|
||||
for (i = shift; i < argc; i++) {
|
||||
size_t arglen;
|
||||
memcpy(p, argv[i], (arglen = strlen(argv[i])));
|
||||
|
@ -275,7 +277,7 @@ vector cmd_make_strvec(const char *string)
|
|||
*cp != '\0')
|
||||
cp++;
|
||||
strlen = cp - start;
|
||||
token = _talloc_zero(tall_vty_ctx, strlen + 1, "make_strvec");
|
||||
token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "make_strvec");
|
||||
memcpy(token, start, strlen);
|
||||
*(token + strlen) = '\0';
|
||||
vector_set(strvec, token);
|
||||
|
@ -331,7 +333,7 @@ static char *cmd_desc_str(const char **string)
|
|||
cp++;
|
||||
|
||||
strlen = cp - start;
|
||||
token = _talloc_zero(tall_vty_ctx, strlen + 1, "cmd_desc_str");
|
||||
token = _talloc_zero(tall_vty_cmd_ctx, strlen + 1, "cmd_desc_str");
|
||||
memcpy(token, start, strlen);
|
||||
*(token + strlen) = '\0';
|
||||
|
||||
|
@ -402,11 +404,11 @@ static vector cmd_make_descvec(const char *string, const char *descstr)
|
|||
|
||||
len = cp - sp;
|
||||
|
||||
token = _talloc_zero(tall_vty_ctx, len + 1, "cmd_make_descvec");
|
||||
token = _talloc_zero(tall_vty_cmd_ctx, len + 1, "cmd_make_descvec");
|
||||
memcpy(token, sp, len);
|
||||
*(token + len) = '\0';
|
||||
|
||||
desc = talloc_zero(tall_vty_ctx, struct desc);
|
||||
desc = talloc_zero(tall_vty_cmd_ctx, struct desc);
|
||||
desc->cmd = token;
|
||||
desc->str = cmd_desc_str(&dp);
|
||||
|
||||
|
@ -1804,7 +1806,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
|
|||
if ((desc = vector_slot(descvec, j))) {
|
||||
if ((string = cmd_entry_function(vector_slot(vline, index), desc->cmd)))
|
||||
if (cmd_unique_string (matchvec, string))
|
||||
vector_set (matchvec, talloc_strdup(tall_vty_ctx, string));
|
||||
vector_set (matchvec, talloc_strdup(tall_vty_cmd_ctx, string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1845,7 +1847,7 @@ static char **cmd_complete_command_real(vector vline, struct vty *vty,
|
|||
if (len < lcd) {
|
||||
char *lcdstr;
|
||||
|
||||
lcdstr = _talloc_zero(tall_vty_ctx, lcd + 1,
|
||||
lcdstr = _talloc_zero(tall_vty_cmd_ctx, lcd + 1,
|
||||
"complete-lcdstr");
|
||||
memcpy(lcdstr, matchvec->index[0], lcd);
|
||||
lcdstr[lcd] = '\0';
|
||||
|
@ -2463,13 +2465,13 @@ DEFUN(config_write_file,
|
|||
config_file = host.config;
|
||||
|
||||
config_file_sav =
|
||||
_talloc_zero(tall_vty_ctx,
|
||||
_talloc_zero(tall_vty_cmd_ctx,
|
||||
strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1,
|
||||
"config_file_sav");
|
||||
strcpy(config_file_sav, config_file);
|
||||
strcat(config_file_sav, CONF_BACKUP_EXT);
|
||||
|
||||
config_file_tmp = _talloc_zero(tall_vty_ctx, strlen(config_file) + 8,
|
||||
config_file_tmp = _talloc_zero(tall_vty_cmd_ctx, strlen(config_file) + 8,
|
||||
"config_file_tmp");
|
||||
sprintf(config_file_tmp, "%s.XXXXXX", config_file);
|
||||
|
||||
|
@ -2650,7 +2652,7 @@ DEFUN(config_hostname,
|
|||
if (host.name)
|
||||
talloc_free(host.name);
|
||||
|
||||
host.name = talloc_strdup(tall_vty_ctx, argv[0]);
|
||||
host.name = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -2685,7 +2687,7 @@ DEFUN(config_password, password_cmd,
|
|||
host.password = NULL;
|
||||
if (host.password_encrypt)
|
||||
talloc_free(host.password_encrypt);
|
||||
host.password_encrypt = talloc_strdup(tall_vty_ctx, argv[1]);
|
||||
host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
} else {
|
||||
vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
|
||||
|
@ -2708,10 +2710,10 @@ DEFUN(config_password, password_cmd,
|
|||
if (host.encrypt) {
|
||||
if (host.password_encrypt)
|
||||
talloc_free(host.password_encrypt);
|
||||
host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0]));
|
||||
host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
|
||||
} else
|
||||
#endif
|
||||
host.password = talloc_strdup(tall_vty_ctx, argv[0]);
|
||||
host.password = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -2744,7 +2746,7 @@ ALIAS(config_password, password_text_cmd,
|
|||
|
||||
if (host.enable_encrypt)
|
||||
talloc_free(host.enable_encrypt);
|
||||
host.enable_encrypt = talloc_strdup(tall_vty_ctx, argv[1]);
|
||||
host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, argv[1]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
} else {
|
||||
|
@ -2769,10 +2771,10 @@ ALIAS(config_password, password_text_cmd,
|
|||
if (host.encrypt) {
|
||||
if (host.enable_encrypt)
|
||||
talloc_free(host.enable_encrypt);
|
||||
host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(argv[0]));
|
||||
host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(argv[0]));
|
||||
} else
|
||||
#endif
|
||||
host.enable = talloc_strdup(tall_vty_ctx, argv[0]);
|
||||
host.enable = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -2816,12 +2818,12 @@ DEFUN(service_password_encrypt,
|
|||
if (host.password) {
|
||||
if (host.password_encrypt)
|
||||
talloc_free(host.password_encrypt);
|
||||
host.password_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.password));
|
||||
host.password_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.password));
|
||||
}
|
||||
if (host.enable) {
|
||||
if (host.enable_encrypt)
|
||||
talloc_free(host.enable_encrypt);
|
||||
host.enable_encrypt = talloc_strdup(tall_vty_ctx, zencrypt(host.enable));
|
||||
host.enable_encrypt = talloc_strdup(tall_vty_cmd_ctx, zencrypt(host.enable));
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
|
@ -3095,7 +3097,7 @@ static int set_log_file(struct vty *vty, const char *fname, int loglevel)
|
|||
if (host.logfile)
|
||||
talloc_free(host.logfile);
|
||||
|
||||
host.logfile = talloc_strdup(tall_vty_ctx, fname);
|
||||
host.logfile = talloc_strdup(tall_vty_cmd_ctx, fname);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -3285,7 +3287,7 @@ DEFUN(banner_motd_file,
|
|||
{
|
||||
if (host.motdfile)
|
||||
talloc_free(host.motdfile);
|
||||
host.motdfile = talloc_strdup(tall_vty_ctx, argv[0]);
|
||||
host.motdfile = talloc_strdup(tall_vty_cmd_ctx, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -3313,7 +3315,7 @@ DEFUN(no_banner_motd,
|
|||
/* Set config filename. Called from vty.c */
|
||||
void host_config_set(const char *filename)
|
||||
{
|
||||
host.config = talloc_strdup(tall_vty_ctx, filename);
|
||||
host.config = talloc_strdup(tall_vty_cmd_ctx, filename);
|
||||
}
|
||||
|
||||
void install_default(enum node_type node)
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
#include <openbsc/talloc.h>
|
||||
#include <memory.h>
|
||||
|
||||
void *tall_vty_vec_ctx;
|
||||
|
||||
/* Initialize vector : allocate memory and return vector. */
|
||||
vector vector_init(unsigned int size)
|
||||
{
|
||||
vector v = talloc_zero(tall_vty_ctx, struct _vector);
|
||||
vector v = talloc_zero(tall_vty_vec_ctx, struct _vector);
|
||||
if (!v)
|
||||
return NULL;
|
||||
|
||||
|
@ -40,7 +42,7 @@ vector vector_init(unsigned int size)
|
|||
|
||||
v->alloced = size;
|
||||
v->active = 0;
|
||||
v->index = _talloc_zero(tall_vty_ctx, sizeof(void *) * size,
|
||||
v->index = _talloc_zero(tall_vty_vec_ctx, sizeof(void *) * size,
|
||||
"vector_init:index");
|
||||
if (!v->index) {
|
||||
talloc_free(v);
|
||||
|
@ -68,7 +70,7 @@ void vector_free(vector v)
|
|||
vector vector_copy(vector v)
|
||||
{
|
||||
unsigned int size;
|
||||
vector new = talloc_zero(tall_vty_ctx, struct _vector);
|
||||
vector new = talloc_zero(tall_vty_vec_ctx, struct _vector);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
|
@ -76,7 +78,7 @@ vector vector_copy(vector v)
|
|||
new->alloced = v->alloced;
|
||||
|
||||
size = sizeof(void *) * (v->alloced);
|
||||
new->index = _talloc_zero(tall_vty_ctx, size, "vector_copy:index");
|
||||
new->index = _talloc_zero(tall_vty_vec_ctx, size, "vector_copy:index");
|
||||
if (!new->index) {
|
||||
talloc_free(new);
|
||||
return NULL;
|
||||
|
@ -92,7 +94,7 @@ void vector_ensure(vector v, unsigned int num)
|
|||
if (v->alloced > num)
|
||||
return;
|
||||
|
||||
v->index = talloc_realloc_size(tall_vty_ctx, v->index,
|
||||
v->index = talloc_realloc_size(tall_vty_vec_ctx, v->index,
|
||||
sizeof(void *) * (v->alloced * 2));
|
||||
memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
|
||||
v->alloced *= 2;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1631,6 +1633,8 @@ extern void *tall_bsc_ctx;
|
|||
void vty_init()
|
||||
{
|
||||
tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
|
||||
tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
|
||||
tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
|
||||
|
||||
/* For further configuration read, preserve current directory. */
|
||||
vty_save_cwd();
|
||||
|
|
|
@ -33,8 +33,11 @@
|
|||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/gsm_utils.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
#include <openbsc/meas_rep.h>
|
||||
#include <openbsc/db.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/telnet_interface.h>
|
||||
|
||||
static struct gsm_network *gsmnet;
|
||||
|
||||
|
@ -74,8 +77,30 @@ static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
|
|||
nm_avail_name(nms->availability), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void dump_pchan_load_vty(struct vty *vty, char *prefix,
|
||||
const struct pchan_load *pl)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) {
|
||||
const struct load_counter *lc = &pl->pchan[i];
|
||||
unsigned int percent;
|
||||
|
||||
if (lc->total == 0)
|
||||
continue;
|
||||
|
||||
percent = (lc->used * 100) / lc->total;
|
||||
|
||||
vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix,
|
||||
gsm_pchan_name(i), percent, lc->used, lc->total,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
static void net_dump_vty(struct vty *vty, struct gsm_network *net)
|
||||
{
|
||||
struct pchan_load pl;
|
||||
|
||||
vty_out(vty, "BSC is on Country Code %u, Network Code %u "
|
||||
"and has %u BTS%s", net->country_code, net->network_code,
|
||||
net->num_bts, VTY_NEWLINE);
|
||||
|
@ -85,10 +110,21 @@ 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);
|
||||
network_chan_load(&pl, net);
|
||||
vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
|
||||
dump_pchan_load_vty(vty, " ", &pl);
|
||||
}
|
||||
|
||||
DEFUN(show_net, show_net_cmd, "show network",
|
||||
|
@ -120,13 +156,26 @@ static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
|
|||
|
||||
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
struct pchan_load pl;
|
||||
|
||||
vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
|
||||
"BSIC %u, TSC %u and %u TRX%s",
|
||||
bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
|
||||
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);
|
||||
vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, "RACH Max transmissions: %u%s",
|
||||
rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
|
||||
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",
|
||||
|
@ -143,6 +192,10 @@ static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
|||
e1isl_dump_vty(vty, bts->oml_link);
|
||||
}
|
||||
/* FIXME: oml_link, chan_desc */
|
||||
memset(&pl, 0, sizeof(pl));
|
||||
bts_chan_load(&pl, bts);
|
||||
vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE);
|
||||
dump_pchan_load_vty(vty, " ", &pl);
|
||||
}
|
||||
|
||||
DEFUN(show_bts, show_bts_cmd, "show bts [number]",
|
||||
|
@ -213,6 +266,7 @@ static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx)
|
|||
|
||||
vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE);
|
||||
vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE);
|
||||
vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE);
|
||||
config_write_e1_link(vty, &trx->rsl_e1_link, " rsl ");
|
||||
vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE);
|
||||
|
@ -234,13 +288,22 @@ 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)
|
||||
vty_out(vty, " rach tx integer %u%s",
|
||||
bts->si_common.rach_control.tx_integer, VTY_NEWLINE);
|
||||
vty_out(vty, " rach max transmission %u%s",
|
||||
rach_max_trans_raw2val(bts->si_common.rach_control.max_trans),
|
||||
VTY_NEWLINE);
|
||||
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",
|
||||
|
@ -273,8 +336,37 @@ static int config_write_net(struct vty *vty)
|
|||
vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE);
|
||||
vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE);
|
||||
vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE);
|
||||
vty_out(vty, " location updating reject cause %u%s",
|
||||
gsmnet->reject_cause, VTY_NEWLINE);
|
||||
vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE);
|
||||
vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE);
|
||||
vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE);
|
||||
vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE);
|
||||
vty_out(vty, " handover window rxlev averaging %u%s",
|
||||
gsmnet->handover.win_rxlev_avg, VTY_NEWLINE);
|
||||
vty_out(vty, " handover window rxqual averaging %u%s",
|
||||
gsmnet->handover.win_rxqual_avg, VTY_NEWLINE);
|
||||
vty_out(vty, " handover window rxlev neighbor averaging %u%s",
|
||||
gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE);
|
||||
vty_out(vty, " handover power budget interval %u%s",
|
||||
gsmnet->handover.pwr_interval, VTY_NEWLINE);
|
||||
vty_out(vty, " handover power budget hysteresis %u%s",
|
||||
gsmnet->handover.pwr_hysteresis, VTY_NEWLINE);
|
||||
vty_out(vty, " handover maximum distance %u%s",
|
||||
gsmnet->handover.max_distance, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE);
|
||||
vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE);
|
||||
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);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -354,24 +446,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,
|
||||
|
@ -429,8 +512,12 @@ DEFUN(show_ts,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
||||
static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
||||
{
|
||||
int rc;
|
||||
struct gsm_auth_info ainfo;
|
||||
struct gsm_auth_tuple atuple;
|
||||
|
||||
vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
|
||||
subscr->authorized, VTY_NEWLINE);
|
||||
if (subscr->name)
|
||||
|
@ -443,23 +530,77 @@ void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
|||
if (subscr->tmsi != GSM_RESERVED_TMSI)
|
||||
vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
|
||||
VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void meas_rep_dump_uni_vty(struct vty *vty,
|
||||
struct gsm_meas_rep_unidir *mru,
|
||||
const char *prefix,
|
||||
const char *dir)
|
||||
{
|
||||
vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ",
|
||||
prefix, dir, rxlev2dbm(mru->full.rx_lev),
|
||||
dir, rxlev2dbm(mru->sub.rx_lev));
|
||||
vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s",
|
||||
dir, mru->full.rx_qual, dir, mru->sub.rx_qual,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr,
|
||||
const char *prefix)
|
||||
{
|
||||
vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE);
|
||||
vty_out(vty, "%s Flags: %s%s%s%s%s", prefix,
|
||||
mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "",
|
||||
mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "",
|
||||
mr->flags & MEAS_REP_F_FPC ? "FPC " : "",
|
||||
mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ",
|
||||
VTY_NEWLINE);
|
||||
if (mr->flags & MEAS_REP_F_MS_TO)
|
||||
vty_out(vty, "%s MS Timing Offset: %u%s", prefix,
|
||||
mr->ms_timing_offset, VTY_NEWLINE);
|
||||
if (mr->flags & MEAS_REP_F_MS_L1)
|
||||
vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s",
|
||||
prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE);
|
||||
if (mr->flags & MEAS_REP_F_DL_VALID)
|
||||
meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl");
|
||||
meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul");
|
||||
}
|
||||
|
||||
static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
|
||||
{
|
||||
int idx;
|
||||
|
||||
vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s",
|
||||
lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
|
||||
lchan->ts->trx->bts->nr, gsm_lchan_name(lchan->type),
|
||||
lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Use Count: %u, State: %s%s", lchan->use_count,
|
||||
gsm_lchans_name(lchan->state), 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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
/* we want to report the last measurement report */
|
||||
idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep),
|
||||
lchan->meas_rep_idx, 1);
|
||||
meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " ");
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -712,6 +853,244 @@ DEFUN(show_paging,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void _vty_output(struct debug_target *tgt, const char *line)
|
||||
{
|
||||
struct vty *vty = tgt->tgt_vty.vty;
|
||||
vty_out(vty, "%s", line);
|
||||
/* This is an ugly hack, but there is no easy way... */
|
||||
if (strchr(line, '\n'))
|
||||
vty_out(vty, "\r");
|
||||
}
|
||||
|
||||
struct debug_target *debug_target_create_vty(struct vty *vty)
|
||||
{
|
||||
struct debug_target *target;
|
||||
|
||||
target = debug_target_create();
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
target->tgt_vty.vty = vty;
|
||||
target->output = _vty_output;
|
||||
return target;
|
||||
}
|
||||
|
||||
DEFUN(enable_logging,
|
||||
enable_logging_cmd,
|
||||
"logging enable",
|
||||
"Enables logging to this vty\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (conn->dbg) {
|
||||
vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
conn->dbg = debug_target_create_vty(vty);
|
||||
if (!conn->dbg)
|
||||
return CMD_WARNING;
|
||||
|
||||
debug_add_target(conn->dbg);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(logging_fltr_imsi,
|
||||
logging_fltr_imsi_cmd,
|
||||
"logging filter imsi IMSI",
|
||||
"Print all messages related to a IMSI\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_set_imsi_filter(conn->dbg, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(logging_fltr_all,
|
||||
logging_fltr_all_cmd,
|
||||
"logging filter all <0-1>",
|
||||
"Print all messages to the console\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_set_all_filter(conn->dbg, atoi(argv[0]));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(logging_use_clr,
|
||||
logging_use_clr_cmd,
|
||||
"logging color <0-1>",
|
||||
"Use color for printing messages\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_set_use_color(conn->dbg, atoi(argv[0]));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(logging_prnt_timestamp,
|
||||
logging_prnt_timestamp_cmd,
|
||||
"logging timestamp <0-1>",
|
||||
"Print the timestamp of each message\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_set_print_timestamp(conn->dbg, atoi(argv[0]));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: those have to be kept in sync with the log levels and categories */
|
||||
#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref)"
|
||||
#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
|
||||
DEFUN(logging_level,
|
||||
logging_level_cmd,
|
||||
"logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
|
||||
"Set the log level for a specified category\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
int category = debug_parse_category(argv[0]);
|
||||
int level = debug_parse_level(argv[1]);
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (category < 0) {
|
||||
vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (level < 0) {
|
||||
vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
conn->dbg->categories[category].enabled = 1;
|
||||
conn->dbg->categories[category].loglevel = level;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(logging_set_category_mask,
|
||||
logging_set_category_mask_cmd,
|
||||
"logging set debug mask MASK",
|
||||
"Decide which categories to output.\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_parse_category_mask(conn->dbg, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(logging_set_log_level,
|
||||
logging_set_log_level_cmd,
|
||||
"logging set log level <0-8>",
|
||||
"Set the global log level. The value 0 implies no filtering.\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_set_log_level(conn->dbg, atoi(argv[0]));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(diable_logging,
|
||||
disable_logging_cmd,
|
||||
"logging disable",
|
||||
"Disables logging to this vty\n")
|
||||
{
|
||||
struct telnet_connection *conn;
|
||||
|
||||
conn = (struct telnet_connection *) vty->priv;
|
||||
if (!conn->dbg) {
|
||||
vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
debug_del_target(conn->dbg);
|
||||
talloc_free(conn->dbg);
|
||||
conn->dbg = NULL;
|
||||
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",
|
||||
counter_get(net->stats.chreq.total),
|
||||
counter_get(net->stats.chreq.no_channel), VTY_NEWLINE);
|
||||
vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s",
|
||||
counter_get(net->stats.loc_upd_type.attach),
|
||||
counter_get(net->stats.loc_upd_type.normal),
|
||||
counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE);
|
||||
vty_out(vty, "IMSI Detach Indications : %lu%s",
|
||||
counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE);
|
||||
vty_out(vty, "Location Update Response: %lu accept, %lu reject%s",
|
||||
counter_get(net->stats.loc_upd_resp.accept),
|
||||
counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE);
|
||||
vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s",
|
||||
counter_get(net->stats.paging.attempted),
|
||||
counter_get(net->stats.paging.completed),
|
||||
counter_get(net->stats.paging.expired), VTY_NEWLINE);
|
||||
vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, "
|
||||
"%lu completed, %lu failed%s",
|
||||
counter_get(net->stats.handover.attempted),
|
||||
counter_get(net->stats.handover.no_channel),
|
||||
counter_get(net->stats.handover.timeout),
|
||||
counter_get(net->stats.handover.completed),
|
||||
counter_get(net->stats.handover.failed), VTY_NEWLINE);
|
||||
vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s",
|
||||
counter_get(net->stats.sms.submitted),
|
||||
counter_get(net->stats.sms.no_receiver), VTY_NEWLINE);
|
||||
vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s",
|
||||
counter_get(net->stats.sms.delivered),
|
||||
counter_get(net->stats.sms.rp_err_mem),
|
||||
counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net,
|
||||
cfg_net_cmd,
|
||||
"network",
|
||||
|
@ -782,6 +1161,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)",
|
||||
|
@ -801,6 +1190,120 @@ 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")
|
||||
{
|
||||
int enable = atoi(argv[0]);
|
||||
|
||||
if (enable && 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 = enable;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd,
|
||||
"handover window rxlev averaging <1-10>",
|
||||
"How many RxLev measurements are used for averaging")
|
||||
{
|
||||
gsmnet->handover.win_rxlev_avg = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd,
|
||||
"handover window rxqual averaging <1-10>",
|
||||
"How many RxQual measurements are used for averaging")
|
||||
{
|
||||
gsmnet->handover.win_rxqual_avg = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd,
|
||||
"handover window rxlev neighbor averaging <1-10>",
|
||||
"How many RxQual measurements are used for averaging")
|
||||
{
|
||||
gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd,
|
||||
"handover power budget interval <1-99>",
|
||||
"How often to check if we have a better cell (SACCH frames)")
|
||||
{
|
||||
gsmnet->handover.pwr_interval = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd,
|
||||
"handover power budget hysteresis <0-999>",
|
||||
"How many dB does a neighbor to be stronger to become a HO candidate")
|
||||
{
|
||||
gsmnet->handover.pwr_hysteresis = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd,
|
||||
"handover maximum distance <0-9999>",
|
||||
"How big is the maximum timing advance before HO is forced")
|
||||
{
|
||||
gsmnet->handover.max_distance = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DECLARE_TIMER(number, doc) \
|
||||
DEFUN(cfg_net_T##number, \
|
||||
cfg_net_T##number##_cmd, \
|
||||
"timer t" #number " <0-65535>", \
|
||||
doc) \
|
||||
{ \
|
||||
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, "Set the timeout value for IMMEDIATE ASSIGNMENT.")
|
||||
DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.")
|
||||
DECLARE_TIMER(3105, "Currently not used.")
|
||||
DECLARE_TIMER(3107, "Currently not used.")
|
||||
DECLARE_TIMER(3109, "Currently not used.")
|
||||
DECLARE_TIMER(3111, "Currently not used.")
|
||||
DECLARE_TIMER(3113, "Set the time to try paging a subscriber.")
|
||||
DECLARE_TIMER(3115, "Currently not used.")
|
||||
DECLARE_TIMER(3117, "Currently not used.")
|
||||
DECLARE_TIMER(3119, "Currently not used.")
|
||||
DECLARE_TIMER(3141, "Currently not used.")
|
||||
|
||||
|
||||
/* per-BTS configuration */
|
||||
DEFUN(cfg_bts,
|
||||
cfg_bts_cmd,
|
||||
|
@ -821,8 +1324,11 @@ DEFUN(cfg_bts,
|
|||
} else
|
||||
bts = gsm_bts_num(gsmnet, bts_nr);
|
||||
|
||||
if (!bts)
|
||||
if (!bts) {
|
||||
vty_out(vty, "%% Unable to allocate BTS %u%s",
|
||||
gsmnet->num_bts, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->index = bts;
|
||||
vty->node = BTS_NODE;
|
||||
|
@ -836,13 +1342,11 @@ DEFUN(cfg_bts_type,
|
|||
"Set the BTS type\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int rc;
|
||||
|
||||
bts->type = parse_btstype(argv[0]);
|
||||
|
||||
if (is_ipaccess_bts(bts)) {
|
||||
/* Set the default OML Stream ID to 0xff */
|
||||
bts->oml_tei = 0xff;
|
||||
}
|
||||
rc = gsm_set_bts_type(bts, parse_btstype(argv[0]));
|
||||
if (rc < 0)
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -909,6 +1413,7 @@ DEFUN(cfg_bts_lac,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_bts_tsc,
|
||||
cfg_bts_tsc_cmd,
|
||||
"training_sequence_code <0-255>",
|
||||
|
@ -1024,13 +1529,33 @@ DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_rach_tx_integer,
|
||||
cfg_bts_rach_tx_integer_cmd,
|
||||
"rach tx integer <0-15>",
|
||||
"Set the raw tx integer value in RACH Control parameters IE")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_rach_max_trans,
|
||||
cfg_bts_rach_max_trans_cmd,
|
||||
"rach max transmission (1|2|4|7)",
|
||||
"Set the maximum number of RACH burst transmissions")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0]));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd,
|
||||
"cell barred (0|1)",
|
||||
"Should this cell be barred from access?")
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -1046,13 +1571,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;
|
||||
}
|
||||
|
@ -1106,6 +1653,18 @@ DEFUN(cfg_trx_arfcn,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_nominal_power,
|
||||
cfg_trx_nominal_power_cmd,
|
||||
"nominal power <0-100>",
|
||||
"Nominal TRX RF Power in dB\n")
|
||||
{
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
trx->nominal_power = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_max_power_red,
|
||||
cfg_trx_max_power_red_cmd,
|
||||
"max_power_red <0-100>",
|
||||
|
@ -1158,6 +1717,17 @@ DEFUN(cfg_trx_rsl_e1_tei,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_rf_locked,
|
||||
cfg_trx_rf_locked_cmd,
|
||||
"rf_locked (0|1)",
|
||||
"Turn off RF of the TRX.\n")
|
||||
{
|
||||
int locked = atoi(argv[0]);
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
gsm_trx_lock_rf(trx, locked);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per TS configuration */
|
||||
DEFUN(cfg_ts,
|
||||
|
@ -1230,6 +1800,17 @@ 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(VIEW_NODE, &enable_logging_cmd);
|
||||
install_element(VIEW_NODE, &disable_logging_cmd);
|
||||
install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
|
||||
install_element(VIEW_NODE, &logging_fltr_all_cmd);
|
||||
install_element(VIEW_NODE, &logging_use_clr_cmd);
|
||||
install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
|
||||
install_element(VIEW_NODE, &logging_set_category_mask_cmd);
|
||||
install_element(VIEW_NODE, &logging_level_cmd);
|
||||
install_element(VIEW_NODE, &logging_set_log_level_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_net_cmd);
|
||||
install_node(&net_node, config_write_net);
|
||||
|
@ -1239,8 +1820,29 @@ int bsc_vty_init(struct gsm_network *net)
|
|||
install_element(GSMNET_NODE, &cfg_net_name_short_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_neci_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_mm_info_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_handover_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
|
||||
install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
|
||||
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);
|
||||
|
@ -1256,18 +1858,24 @@ int bsc_vty_init(struct gsm_network *net)
|
|||
install_element(BTS_NODE, &cfg_bts_oml_e1_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_challoc_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd);
|
||||
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);
|
||||
install_node(&trx_node, dummy_config_write);
|
||||
install_default(TRX_NODE);
|
||||
install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_nominal_power_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_max_power_red_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd);
|
||||
install_element(TRX_NODE, &cfg_trx_rf_locked_cmd);
|
||||
|
||||
install_element(TRX_NODE, &cfg_ts_cmd);
|
||||
install_node(&ts_node, dummy_config_write);
|
||||
|
|
|
@ -40,9 +40,7 @@
|
|||
#include <openbsc/db.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
/* forward declarations */
|
||||
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr);
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
static struct gsm_network *gsmnet;
|
||||
|
||||
|
@ -74,6 +72,33 @@ static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
|
|||
return b;
|
||||
}
|
||||
|
||||
static int hexparse(const char *str, u_int8_t *b, int max_len)
|
||||
|
||||
{
|
||||
int i, l, v;
|
||||
|
||||
l = strlen(str);
|
||||
if ((l&1) || ((l>>1) > max_len))
|
||||
return -1;
|
||||
|
||||
memset(b, 0x00, max_len);
|
||||
|
||||
for (i=0; i<l; i++) {
|
||||
char c = str[i];
|
||||
if (c >= '0' && c <= '9')
|
||||
v = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
v = 10 + (c - 'a');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
v = 10 + (c - 'a');
|
||||
else
|
||||
return -1;
|
||||
b[i>>1] |= v << (i&1 ? 0 : 4);
|
||||
}
|
||||
|
||||
return i>>1;
|
||||
}
|
||||
|
||||
/* per-subscriber configuration */
|
||||
DEFUN(cfg_subscr,
|
||||
cfg_subscr_cmd,
|
||||
|
@ -97,6 +122,55 @@ DEFUN(cfg_subscr,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
||||
{
|
||||
int rc;
|
||||
struct gsm_auth_info ainfo;
|
||||
struct gsm_auth_tuple atuple;
|
||||
|
||||
vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
|
||||
subscr->authorized, VTY_NEWLINE);
|
||||
if (subscr->name)
|
||||
vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
|
||||
if (subscr->extension)
|
||||
vty_out(vty, " Extension: %s%s", subscr->extension,
|
||||
VTY_NEWLINE);
|
||||
if (subscr->imsi)
|
||||
vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
|
||||
if (subscr->tmsi != GSM_RESERVED_TMSI)
|
||||
vty_out(vty, " TMSI: %08X%s", subscr->tmsi,
|
||||
VTY_NEWLINE);
|
||||
|
||||
rc = get_authinfo_by_subscr(&ainfo, subscr);
|
||||
if (!rc) {
|
||||
vty_out(vty, " A3A8 algorithm id: %d%s",
|
||||
ainfo.auth_algo, VTY_NEWLINE);
|
||||
vty_out(vty, " A3A8 Ki: %s%s",
|
||||
hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
rc = get_authtuple_by_subscr(&atuple, subscr);
|
||||
if (!rc) {
|
||||
vty_out(vty, " A3A8 last tuple (used %d times):%s",
|
||||
atuple.use_count, VTY_NEWLINE);
|
||||
vty_out(vty, " seq # : %d%s",
|
||||
atuple.key_seq, VTY_NEWLINE);
|
||||
vty_out(vty, " RAND : %s%s",
|
||||
hexdump(atuple.rand, sizeof(atuple.rand)),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " SRES : %s%s",
|
||||
hexdump(atuple.sres, sizeof(atuple.sres)),
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " Kc : %s%s",
|
||||
hexdump(atuple.kc, sizeof(atuple.kc)),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
|
||||
/* Subscriber */
|
||||
DEFUN(show_subscr,
|
||||
show_subscr_cmd,
|
||||
|
@ -114,7 +188,7 @@ DEFUN(show_subscr,
|
|||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
subscr_dump_vty(vty, subscr);
|
||||
subscr_dump_full_vty(vty, subscr);
|
||||
subscr_put(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
|
@ -135,7 +209,7 @@ DEFUN(show_subscr_cache,
|
|||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
|
||||
subscr_dump_vty(vty, subscr);
|
||||
subscr_dump_full_vty(vty, subscr);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
|
@ -143,23 +217,20 @@ DEFUN(show_subscr_cache,
|
|||
|
||||
DEFUN(sms_send_pend,
|
||||
sms_send_pend_cmd,
|
||||
"sms send pending MIN_ID",
|
||||
"Send all pending SMS starting from MIN_ID")
|
||||
"sms send pending",
|
||||
"Send all pending SMS")
|
||||
{
|
||||
struct gsm_sms *sms;
|
||||
int id = atoi(argv[0]);
|
||||
int id = 0;
|
||||
|
||||
while (1) {
|
||||
sms = db_sms_get_unsent(gsmnet, id++);
|
||||
sms = db_sms_get_unsent_by_subscr(gsmnet, id);
|
||||
if (!sms)
|
||||
return CMD_WARNING;
|
||||
|
||||
if (!sms->receiver) {
|
||||
sms_free(sms);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
gsm411_send_sms_subscr(sms->receiver, sms);
|
||||
|
||||
id = sms->receiver->id + 1;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
|
@ -269,10 +340,46 @@ DEFUN(subscriber_silent_sms,
|
|||
return rc;
|
||||
}
|
||||
|
||||
DEFUN(subscriber_silent_call,
|
||||
subscriber_silent_call_cmd,
|
||||
"subscriber " SUBSCR_TYPES " EXTEN silent call (start|stop)",
|
||||
"Send a silent call to a subscriber")
|
||||
DEFUN(subscriber_silent_call_start,
|
||||
subscriber_silent_call_start_cmd,
|
||||
"subscriber " SUBSCR_TYPES " EXTEN silent call start (any|tch/f|tch/any|sdcch)",
|
||||
"Start a silent call to a subscriber")
|
||||
{
|
||||
struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
|
||||
int rc, type;
|
||||
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% No subscriber found for %s %s%s",
|
||||
argv[0], argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[2], "tch/f"))
|
||||
type = RSL_CHANNEED_TCH_F;
|
||||
else if (!strcmp(argv[2], "tch/any"))
|
||||
type = RSL_CHANNEED_TCH_ForH;
|
||||
else if (!strcmp(argv[2], "sdcch"))
|
||||
type = RSL_CHANNEED_SDCCH;
|
||||
else
|
||||
type = RSL_CHANNEED_ANY; /* Defaults to ANY */
|
||||
|
||||
rc = gsm_silent_call_start(subscr, vty, type);
|
||||
if (rc <= 0) {
|
||||
vty_out(vty, "%% Subscriber not attached%s",
|
||||
VTY_NEWLINE);
|
||||
subscr_put(subscr);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
subscr_put(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(subscriber_silent_call_stop,
|
||||
subscriber_silent_call_stop_cmd,
|
||||
"subscriber " SUBSCR_TYPES " EXTEN silent call stop",
|
||||
"Stop a silent call to a subscriber")
|
||||
{
|
||||
struct gsm_subscriber *subscr = get_subscr_by_argv(argv[0], argv[1]);
|
||||
int rc;
|
||||
|
@ -283,17 +390,10 @@ DEFUN(subscriber_silent_call,
|
|||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[2], "start")) {
|
||||
rc = gsm_silent_call_start(subscr, vty);
|
||||
if (rc <= 0) {
|
||||
vty_out(vty, "%% Subscriber not attached%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
} else {
|
||||
rc = gsm_silent_call_stop(subscr);
|
||||
if (rc < 0)
|
||||
return CMD_WARNING;
|
||||
rc = gsm_silent_call_stop(subscr);
|
||||
if (rc < 0) {
|
||||
subscr_put(subscr);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
subscr_put(subscr);
|
||||
|
@ -349,6 +449,40 @@ DEFUN(cfg_subscr_authorized,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define A3A8_ALG_TYPES "(none|comp128v1)"
|
||||
|
||||
DEFUN(cfg_subscr_a3a8,
|
||||
cfg_subscr_a3a8_cmd,
|
||||
"a3a8 " A3A8_ALG_TYPES " [KI]",
|
||||
"Set a3a8 parameters for the subscriber")
|
||||
{
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
const char *alg_str = argv[0];
|
||||
const char *ki_str = argv[1];
|
||||
struct gsm_auth_info ainfo;
|
||||
int rc;
|
||||
|
||||
if (!strcasecmp(alg_str, "none")) {
|
||||
/* Just erase */
|
||||
rc = set_authinfo_for_subscr(NULL, subscr);
|
||||
} else if (!strcasecmp(alg_str, "comp128v1")) {
|
||||
/* Parse hex string Ki */
|
||||
rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
|
||||
if (rc != 16)
|
||||
return CMD_WARNING;
|
||||
|
||||
/* Set the infos */
|
||||
ainfo.auth_algo = AUTH_ALGO_COMP128v1;
|
||||
ainfo.a3a8_ki_len = rc;
|
||||
rc = set_authinfo_for_subscr(&ainfo, subscr);
|
||||
} else {
|
||||
/* Unknown method */
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
return rc ? CMD_WARNING : CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int scall_cbfn(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
|
@ -381,7 +515,8 @@ int bsc_vty_init_extra(struct gsm_network *net)
|
|||
|
||||
install_element(VIEW_NODE, &subscriber_send_sms_cmd);
|
||||
install_element(VIEW_NODE, &subscriber_silent_sms_cmd);
|
||||
install_element(VIEW_NODE, &subscriber_silent_call_cmd);
|
||||
install_element(VIEW_NODE, &subscriber_silent_call_start_cmd);
|
||||
install_element(VIEW_NODE, &subscriber_silent_call_stop_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_subscr_cmd);
|
||||
install_node(&subscr_node, dummy_config_write);
|
||||
|
@ -390,6 +525,7 @@ int bsc_vty_init_extra(struct gsm_network *net)
|
|||
install_element(SUBSCR_NODE, &cfg_subscr_name_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_a3a8_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ channel_test_SOURCES = channel_test.c \
|
|||
$(top_srcdir)/src/select.c \
|
||||
$(top_srcdir)/src/talloc.c \
|
||||
$(top_srcdir)/src/gsm_data.c \
|
||||
$(top_srcdir)/src/signal.c
|
||||
$(top_srcdir)/src/signal.c \
|
||||
$(top_srcdir)/src/statistics.c \
|
||||
$(top_srcdir)/src/bts_ipaccess_nanobts.c \
|
||||
$(top_srcdir)/src/bts_siemens_bs11.c \
|
||||
$(top_srcdir)/src/tlv_parser.c
|
||||
channel_test_LDADD = -ldl -ldbi
|
||||
|
||||
|
|
|
@ -77,3 +77,5 @@ void nm_state_event() {}
|
|||
void input_event() {}
|
||||
void sms_alloc() {}
|
||||
|
||||
struct tlv_definition nm_att_tlvdef;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
noinst_PROGRAMS = debug_test
|
||||
|
||||
debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c
|
||||
debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c $(top_srcdir)/src/talloc.c
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* simple test for the debug interface */
|
||||
/*
|
||||
* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -24,11 +24,18 @@
|
|||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
debug_parse_category_mask("DRLL");
|
||||
DEBUGP(DCC, "You should not see this\n");
|
||||
struct debug_target *stderr_target;
|
||||
|
||||
debug_parse_category_mask("DRLL:DCC");
|
||||
DEBUGP(DRLL, "You should see this\n");
|
||||
DEBUGP(DCC, "You should see this\n");
|
||||
DEBUGP(DMM, "You should not see this\n");
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
|
||||
debug_parse_category_mask(stderr_target, "DRLL");
|
||||
DEBUGP(DCC, "You should not see this\n");
|
||||
|
||||
debug_parse_category_mask(stderr_target, "DRLL:DCC");
|
||||
DEBUGP(DRLL, "You should see this\n");
|
||||
DEBUGP(DCC, "You should see this\n");
|
||||
DEBUGP(DMM, "You should not see this\n");
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue