move openbsc into its own subdirectory
This commit is contained in:
parent
349342864d
commit
59b0468c5b
|
@ -0,0 +1,3 @@
|
|||
SUBDIRS = openbsc vty
|
||||
|
||||
noinst_HEADERS = mISDNif.h
|
|
@ -0,0 +1,39 @@
|
|||
#ifdef MISDN_OLD_AF_COMPATIBILITY
|
||||
#undef AF_ISDN
|
||||
#undef PF_ISDN
|
||||
|
||||
extern int AF_ISDN;
|
||||
#define PF_ISDN AF_ISDN
|
||||
|
||||
int AF_ISDN;
|
||||
|
||||
#endif
|
||||
|
||||
extern void init_af_isdn(void);
|
||||
|
||||
#ifdef AF_COMPATIBILITY_FUNC
|
||||
#ifdef MISDN_OLD_AF_COMPATIBILITY
|
||||
void init_af_isdn(void)
|
||||
{
|
||||
int s;
|
||||
|
||||
/* test for new value */
|
||||
AF_ISDN = 34;
|
||||
s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE);
|
||||
if (s >= 0) {
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
AF_ISDN = 27;
|
||||
s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE);
|
||||
if (s >= 0) {
|
||||
close(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void init_af_isdn(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
|
||||
* version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This code 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 LESSER GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef mISDNIF_H
|
||||
#define mISDNIF_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#ifdef linux
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/socket.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ABI Version 32 bit
|
||||
*
|
||||
* <8 bit> Major version
|
||||
* - changed if any interface become backwards incompatible
|
||||
*
|
||||
* <8 bit> Minor version
|
||||
* - changed if any interface is extended but backwards compatible
|
||||
*
|
||||
* <16 bit> Release number
|
||||
* - should be incremented on every checkin
|
||||
*/
|
||||
#define MISDN_MAJOR_VERSION 1
|
||||
#define MISDN_MINOR_VERSION 1
|
||||
#define MISDN_RELEASE 20
|
||||
|
||||
/* primitives for information exchange
|
||||
* generell format
|
||||
* <16 bit 0 >
|
||||
* <8 bit command>
|
||||
* BIT 8 = 1 LAYER private
|
||||
* BIT 7 = 1 answer
|
||||
* BIT 6 = 1 DATA
|
||||
* <8 bit target layer mask>
|
||||
*
|
||||
* Layer = 00 is reserved for general commands
|
||||
Layer = 01 L2 -> HW
|
||||
Layer = 02 HW -> L2
|
||||
Layer = 04 L3 -> L2
|
||||
Layer = 08 L2 -> L3
|
||||
* Layer = FF is reserved for broadcast commands
|
||||
*/
|
||||
|
||||
#define MISDN_CMDMASK 0xff00
|
||||
#define MISDN_LAYERMASK 0x00ff
|
||||
|
||||
/* generell commands */
|
||||
#define OPEN_CHANNEL 0x0100
|
||||
#define CLOSE_CHANNEL 0x0200
|
||||
#define CONTROL_CHANNEL 0x0300
|
||||
#define CHECK_DATA 0x0400
|
||||
|
||||
/* layer 2 -> layer 1 */
|
||||
#define PH_ACTIVATE_REQ 0x0101
|
||||
#define PH_DEACTIVATE_REQ 0x0201
|
||||
#define PH_DATA_REQ 0x2001
|
||||
#define MPH_ACTIVATE_REQ 0x0501
|
||||
#define MPH_DEACTIVATE_REQ 0x0601
|
||||
#define MPH_INFORMATION_REQ 0x0701
|
||||
#define PH_CONTROL_REQ 0x0801
|
||||
|
||||
/* layer 1 -> layer 2 */
|
||||
#define PH_ACTIVATE_IND 0x0102
|
||||
#define PH_ACTIVATE_CNF 0x4102
|
||||
#define PH_DEACTIVATE_IND 0x0202
|
||||
#define PH_DEACTIVATE_CNF 0x4202
|
||||
#define PH_DATA_IND 0x2002
|
||||
#define PH_DATA_E_IND 0x3002
|
||||
#define MPH_ACTIVATE_IND 0x0502
|
||||
#define MPH_DEACTIVATE_IND 0x0602
|
||||
#define MPH_INFORMATION_IND 0x0702
|
||||
#define PH_DATA_CNF 0x6002
|
||||
#define PH_CONTROL_IND 0x0802
|
||||
#define PH_CONTROL_CNF 0x4802
|
||||
|
||||
/* layer 3 -> layer 2 */
|
||||
#define DL_ESTABLISH_REQ 0x1004
|
||||
#define DL_RELEASE_REQ 0x1104
|
||||
#define DL_DATA_REQ 0x3004
|
||||
#define DL_UNITDATA_REQ 0x3104
|
||||
#define DL_INFORMATION_REQ 0x0004
|
||||
|
||||
/* layer 2 -> layer 3 */
|
||||
#define DL_ESTABLISH_IND 0x1008
|
||||
#define DL_ESTABLISH_CNF 0x5008
|
||||
#define DL_RELEASE_IND 0x1108
|
||||
#define DL_RELEASE_CNF 0x5108
|
||||
#define DL_DATA_IND 0x3008
|
||||
#define DL_UNITDATA_IND 0x3108
|
||||
#define DL_INFORMATION_IND 0x0008
|
||||
|
||||
/* intern layer 2 managment */
|
||||
#define MDL_ASSIGN_REQ 0x1804
|
||||
#define MDL_ASSIGN_IND 0x1904
|
||||
#define MDL_REMOVE_REQ 0x1A04
|
||||
#define MDL_REMOVE_IND 0x1B04
|
||||
#define MDL_STATUS_UP_IND 0x1C04
|
||||
#define MDL_STATUS_DOWN_IND 0x1D04
|
||||
#define MDL_STATUS_UI_IND 0x1E04
|
||||
#define MDL_ERROR_IND 0x1F04
|
||||
#define MDL_ERROR_RSP 0x5F04
|
||||
|
||||
/* DL_INFORMATION_IND types */
|
||||
#define DL_INFO_L2_CONNECT 0x0001
|
||||
#define DL_INFO_L2_REMOVED 0x0002
|
||||
|
||||
/* PH_CONTROL types */
|
||||
/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */
|
||||
#define DTMF_TONE_VAL 0x2000
|
||||
#define DTMF_TONE_MASK 0x007F
|
||||
#define DTMF_TONE_START 0x2100
|
||||
#define DTMF_TONE_STOP 0x2200
|
||||
#define DTMF_HFC_COEF 0x4000
|
||||
#define DSP_CONF_JOIN 0x2403
|
||||
#define DSP_CONF_SPLIT 0x2404
|
||||
#define DSP_RECEIVE_OFF 0x2405
|
||||
#define DSP_RECEIVE_ON 0x2406
|
||||
#define DSP_ECHO_ON 0x2407
|
||||
#define DSP_ECHO_OFF 0x2408
|
||||
#define DSP_MIX_ON 0x2409
|
||||
#define DSP_MIX_OFF 0x240a
|
||||
#define DSP_DELAY 0x240b
|
||||
#define DSP_JITTER 0x240c
|
||||
#define DSP_TXDATA_ON 0x240d
|
||||
#define DSP_TXDATA_OFF 0x240e
|
||||
#define DSP_TX_DEJITTER 0x240f
|
||||
#define DSP_TX_DEJ_OFF 0x2410
|
||||
#define DSP_TONE_PATT_ON 0x2411
|
||||
#define DSP_TONE_PATT_OFF 0x2412
|
||||
#define DSP_VOL_CHANGE_TX 0x2413
|
||||
#define DSP_VOL_CHANGE_RX 0x2414
|
||||
#define DSP_BF_ENABLE_KEY 0x2415
|
||||
#define DSP_BF_DISABLE 0x2416
|
||||
#define DSP_BF_ACCEPT 0x2416
|
||||
#define DSP_BF_REJECT 0x2417
|
||||
#define DSP_PIPELINE_CFG 0x2418
|
||||
#define HFC_VOL_CHANGE_TX 0x2601
|
||||
#define HFC_VOL_CHANGE_RX 0x2602
|
||||
#define HFC_SPL_LOOP_ON 0x2603
|
||||
#define HFC_SPL_LOOP_OFF 0x2604
|
||||
|
||||
/* DSP_TONE_PATT_ON parameter */
|
||||
#define TONE_OFF 0x0000
|
||||
#define TONE_GERMAN_DIALTONE 0x0001
|
||||
#define TONE_GERMAN_OLDDIALTONE 0x0002
|
||||
#define TONE_AMERICAN_DIALTONE 0x0003
|
||||
#define TONE_GERMAN_DIALPBX 0x0004
|
||||
#define TONE_GERMAN_OLDDIALPBX 0x0005
|
||||
#define TONE_AMERICAN_DIALPBX 0x0006
|
||||
#define TONE_GERMAN_RINGING 0x0007
|
||||
#define TONE_GERMAN_OLDRINGING 0x0008
|
||||
#define TONE_AMERICAN_RINGPBX 0x000b
|
||||
#define TONE_GERMAN_RINGPBX 0x000c
|
||||
#define TONE_GERMAN_OLDRINGPBX 0x000d
|
||||
#define TONE_AMERICAN_RINGING 0x000e
|
||||
#define TONE_GERMAN_BUSY 0x000f
|
||||
#define TONE_GERMAN_OLDBUSY 0x0010
|
||||
#define TONE_AMERICAN_BUSY 0x0011
|
||||
#define TONE_GERMAN_HANGUP 0x0012
|
||||
#define TONE_GERMAN_OLDHANGUP 0x0013
|
||||
#define TONE_AMERICAN_HANGUP 0x0014
|
||||
#define TONE_SPECIAL_INFO 0x0015
|
||||
#define TONE_GERMAN_GASSENBESETZT 0x0016
|
||||
#define TONE_GERMAN_AUFSCHALTTON 0x0016
|
||||
|
||||
/* MPH_INFORMATION_IND */
|
||||
#define L1_SIGNAL_LOS_OFF 0x0010
|
||||
#define L1_SIGNAL_LOS_ON 0x0011
|
||||
#define L1_SIGNAL_AIS_OFF 0x0012
|
||||
#define L1_SIGNAL_AIS_ON 0x0013
|
||||
#define L1_SIGNAL_RDI_OFF 0x0014
|
||||
#define L1_SIGNAL_RDI_ON 0x0015
|
||||
#define L1_SIGNAL_SLIP_RX 0x0020
|
||||
#define L1_SIGNAL_SLIP_TX 0x0021
|
||||
|
||||
/*
|
||||
* protocol ids
|
||||
* D channel 1-31
|
||||
* B channel 33 - 63
|
||||
*/
|
||||
|
||||
#define ISDN_P_NONE 0
|
||||
#define ISDN_P_BASE 0
|
||||
#define ISDN_P_TE_S0 0x01
|
||||
#define ISDN_P_NT_S0 0x02
|
||||
#define ISDN_P_TE_E1 0x03
|
||||
#define ISDN_P_NT_E1 0x04
|
||||
#define ISDN_P_TE_UP0 0x05
|
||||
#define ISDN_P_NT_UP0 0x06
|
||||
|
||||
#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
|
||||
(p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
|
||||
#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
|
||||
(p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
|
||||
#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
|
||||
#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
|
||||
#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
|
||||
|
||||
|
||||
#define ISDN_P_LAPD_TE 0x10
|
||||
#define ISDN_P_LAPD_NT 0x11
|
||||
|
||||
#define ISDN_P_B_MASK 0x1f
|
||||
#define ISDN_P_B_START 0x20
|
||||
|
||||
#define ISDN_P_B_RAW 0x21
|
||||
#define ISDN_P_B_HDLC 0x22
|
||||
#define ISDN_P_B_X75SLP 0x23
|
||||
#define ISDN_P_B_L2DTMF 0x24
|
||||
#define ISDN_P_B_L2DSP 0x25
|
||||
#define ISDN_P_B_L2DSPHDLC 0x26
|
||||
|
||||
#define OPTION_L2_PMX 1
|
||||
#define OPTION_L2_PTP 2
|
||||
#define OPTION_L2_FIXEDTEI 3
|
||||
#define OPTION_L2_CLEANUP 4
|
||||
|
||||
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
|
||||
#define MISDN_MAX_IDLEN 20
|
||||
|
||||
struct mISDNhead {
|
||||
unsigned int prim;
|
||||
unsigned int id;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define MISDN_HEADER_LEN sizeof(struct mISDNhead)
|
||||
#define MAX_DATA_SIZE 2048
|
||||
#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN)
|
||||
#define MAX_DFRAME_LEN 260
|
||||
|
||||
#define MISDN_ID_ADDR_MASK 0xFFFF
|
||||
#define MISDN_ID_TEI_MASK 0xFF00
|
||||
#define MISDN_ID_SAPI_MASK 0x00FF
|
||||
#define MISDN_ID_TEI_ANY 0x7F00
|
||||
|
||||
#define MISDN_ID_ANY 0xFFFF
|
||||
#define MISDN_ID_NONE 0xFFFE
|
||||
|
||||
#define GROUP_TEI 127
|
||||
#define TEI_SAPI 63
|
||||
#define CTRL_SAPI 0
|
||||
|
||||
#define MISDN_MAX_CHANNEL 127
|
||||
#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
|
||||
|
||||
#define SOL_MISDN 0
|
||||
|
||||
struct sockaddr_mISDN {
|
||||
sa_family_t family;
|
||||
unsigned char dev;
|
||||
unsigned char channel;
|
||||
unsigned char sapi;
|
||||
unsigned char tei;
|
||||
};
|
||||
|
||||
struct mISDNversion {
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
unsigned short release;
|
||||
};
|
||||
|
||||
#define MAX_DEVICE_ID 63
|
||||
|
||||
struct mISDN_devinfo {
|
||||
u_int id;
|
||||
u_int Dprotocols;
|
||||
u_int Bprotocols;
|
||||
u_int protocol;
|
||||
u_char channelmap[MISDN_CHMAP_SIZE];
|
||||
u_int nrbchan;
|
||||
char name[MISDN_MAX_IDLEN];
|
||||
};
|
||||
|
||||
struct mISDN_devrename {
|
||||
u_int id;
|
||||
char name[MISDN_MAX_IDLEN];
|
||||
};
|
||||
|
||||
struct ph_info_ch {
|
||||
int32_t protocol;
|
||||
int64_t Flags;
|
||||
};
|
||||
|
||||
struct ph_info_dch {
|
||||
struct ph_info_ch ch;
|
||||
int16_t state;
|
||||
int16_t num_bch;
|
||||
};
|
||||
|
||||
struct ph_info {
|
||||
struct ph_info_dch dch;
|
||||
struct ph_info_ch bch[];
|
||||
};
|
||||
|
||||
/* timer device ioctl */
|
||||
#define IMADDTIMER _IOR('I', 64, int)
|
||||
#define IMDELTIMER _IOR('I', 65, int)
|
||||
/* socket ioctls */
|
||||
#define IMGETVERSION _IOR('I', 66, int)
|
||||
#define IMGETCOUNT _IOR('I', 67, int)
|
||||
#define IMGETDEVINFO _IOR('I', 68, int)
|
||||
#define IMCTRLREQ _IOR('I', 69, int)
|
||||
#define IMCLEAR_L2 _IOR('I', 70, int)
|
||||
#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename)
|
||||
|
||||
static inline int
|
||||
test_channelmap(u_int nr, u_char *map)
|
||||
{
|
||||
if (nr <= MISDN_MAX_CHANNEL)
|
||||
return map[nr >> 3] & (1 << (nr & 7));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_channelmap(u_int nr, u_char *map)
|
||||
{
|
||||
map[nr >> 3] |= (1 << (nr & 7));
|
||||
}
|
||||
|
||||
static inline void
|
||||
clear_channelmap(u_int nr, u_char *map)
|
||||
{
|
||||
map[nr >> 3] &= ~(1 << (nr & 7));
|
||||
}
|
||||
|
||||
/* CONTROL_CHANNEL parameters */
|
||||
#define MISDN_CTRL_GETOP 0x0000
|
||||
#define MISDN_CTRL_LOOP 0x0001
|
||||
#define MISDN_CTRL_CONNECT 0x0002
|
||||
#define MISDN_CTRL_DISCONNECT 0x0004
|
||||
#define MISDN_CTRL_PCMCONNECT 0x0010
|
||||
#define MISDN_CTRL_PCMDISCONNECT 0x0020
|
||||
#define MISDN_CTRL_SETPEER 0x0040
|
||||
#define MISDN_CTRL_UNSETPEER 0x0080
|
||||
#define MISDN_CTRL_RX_OFF 0x0100
|
||||
#define MISDN_CTRL_FILL_EMPTY 0x0200
|
||||
#define MISDN_CTRL_GETPEER 0x0400
|
||||
#define MISDN_CTRL_HW_FEATURES_OP 0x2000
|
||||
#define MISDN_CTRL_HW_FEATURES 0x2001
|
||||
#define MISDN_CTRL_HFC_OP 0x4000
|
||||
#define MISDN_CTRL_HFC_PCM_CONN 0x4001
|
||||
#define MISDN_CTRL_HFC_PCM_DISC 0x4002
|
||||
#define MISDN_CTRL_HFC_CONF_JOIN 0x4003
|
||||
#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004
|
||||
#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005
|
||||
#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006
|
||||
#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007
|
||||
#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008
|
||||
|
||||
|
||||
/* socket options */
|
||||
#define MISDN_TIME_STAMP 0x0001
|
||||
|
||||
struct mISDN_ctrl_req {
|
||||
int op;
|
||||
int channel;
|
||||
int p1;
|
||||
int p2;
|
||||
};
|
||||
|
||||
/* muxer options */
|
||||
#define MISDN_OPT_ALL 1
|
||||
#define MISDN_OPT_TEIMGR 2
|
||||
|
||||
#endif /* mISDNIF_H */
|
|
@ -0,0 +1,5 @@
|
|||
noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
|
||||
gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.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
|
|
@ -0,0 +1,635 @@
|
|||
/* GSM Network Management messages on the A-bis interface
|
||||
* 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
|
||||
|
||||
/* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _NM_H
|
||||
#define _NM_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/tlv.h>
|
||||
|
||||
/* PRIVATE */
|
||||
|
||||
/* generic header in front of every OML message according to TS 08.59 */
|
||||
struct abis_om_hdr {
|
||||
u_int8_t mdisc;
|
||||
u_int8_t placement;
|
||||
u_int8_t sequence;
|
||||
u_int8_t length;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define ABIS_OM_MDISC_FOM 0x80
|
||||
#define ABIS_OM_MDISC_MMI 0x40
|
||||
#define ABIS_OM_MDISC_TRAU 0x20
|
||||
#define ABIS_OM_MDISC_MANUF 0x10
|
||||
#define ABIS_OM_PLACEMENT_ONLY 0x80
|
||||
#define ABIS_OM_PLACEMENT_FIRST 0x40
|
||||
#define ABIS_OM_PLACEMENT_MIDDLE 0x20
|
||||
#define ABIS_OM_PLACEMENT_LAST 0x10
|
||||
|
||||
struct abis_om_obj_inst {
|
||||
u_int8_t bts_nr;
|
||||
u_int8_t trx_nr;
|
||||
u_int8_t ts_nr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct abis_om_fom_hdr {
|
||||
u_int8_t msg_type;
|
||||
u_int8_t obj_class;
|
||||
struct abis_om_obj_inst obj_inst;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr))
|
||||
|
||||
/* Section 9.1: Message Types */
|
||||
enum abis_nm_msgtype {
|
||||
/* SW Download Management Messages */
|
||||
NM_MT_LOAD_INIT = 0x01,
|
||||
NM_MT_LOAD_INIT_ACK,
|
||||
NM_MT_LOAD_INIT_NACK,
|
||||
NM_MT_LOAD_SEG,
|
||||
NM_MT_LOAD_SEG_ACK,
|
||||
NM_MT_LOAD_ABORT,
|
||||
NM_MT_LOAD_END,
|
||||
NM_MT_LOAD_END_ACK,
|
||||
NM_MT_LOAD_END_NACK,
|
||||
NM_MT_SW_ACT_REQ, /* BTS->BSC */
|
||||
NM_MT_SW_ACT_REQ_ACK,
|
||||
NM_MT_SW_ACT_REQ_NACK,
|
||||
NM_MT_ACTIVATE_SW, /* BSC->BTS */
|
||||
NM_MT_ACTIVATE_SW_ACK,
|
||||
NM_MT_ACTIVATE_SW_NACK,
|
||||
NM_MT_SW_ACTIVATED_REP, /* 0x10 */
|
||||
/* A-bis Interface Management Messages */
|
||||
NM_MT_ESTABLISH_TEI = 0x21,
|
||||
NM_MT_ESTABLISH_TEI_ACK,
|
||||
NM_MT_ESTABLISH_TEI_NACK,
|
||||
NM_MT_CONN_TERR_SIGN,
|
||||
NM_MT_CONN_TERR_SIGN_ACK,
|
||||
NM_MT_CONN_TERR_SIGN_NACK,
|
||||
NM_MT_DISC_TERR_SIGN,
|
||||
NM_MT_DISC_TERR_SIGN_ACK,
|
||||
NM_MT_DISC_TERR_SIGN_NACK,
|
||||
NM_MT_CONN_TERR_TRAF,
|
||||
NM_MT_CONN_TERR_TRAF_ACK,
|
||||
NM_MT_CONN_TERR_TRAF_NACK,
|
||||
NM_MT_DISC_TERR_TRAF,
|
||||
NM_MT_DISC_TERR_TRAF_ACK,
|
||||
NM_MT_DISC_TERR_TRAF_NACK,
|
||||
/* Transmission Management Messages */
|
||||
NM_MT_CONN_MDROP_LINK = 0x31,
|
||||
NM_MT_CONN_MDROP_LINK_ACK,
|
||||
NM_MT_CONN_MDROP_LINK_NACK,
|
||||
NM_MT_DISC_MDROP_LINK,
|
||||
NM_MT_DISC_MDROP_LINK_ACK,
|
||||
NM_MT_DISC_MDROP_LINK_NACK,
|
||||
/* Air Interface Management Messages */
|
||||
NM_MT_SET_BTS_ATTR = 0x41,
|
||||
NM_MT_SET_BTS_ATTR_ACK,
|
||||
NM_MT_SET_BTS_ATTR_NACK,
|
||||
NM_MT_SET_RADIO_ATTR,
|
||||
NM_MT_SET_RADIO_ATTR_ACK,
|
||||
NM_MT_SET_RADIO_ATTR_NACK,
|
||||
NM_MT_SET_CHAN_ATTR,
|
||||
NM_MT_SET_CHAN_ATTR_ACK,
|
||||
NM_MT_SET_CHAN_ATTR_NACK,
|
||||
/* Test Management Messages */
|
||||
NM_MT_PERF_TEST = 0x51,
|
||||
NM_MT_PERF_TESET_ACK,
|
||||
NM_MT_PERF_TEST_NACK,
|
||||
NM_MT_TEST_REP,
|
||||
NM_MT_SEND_TEST_REP,
|
||||
NM_MT_SEND_TEST_REP_ACK,
|
||||
NM_MT_SEND_TEST_REP_NACK,
|
||||
NM_MT_STOP_TEST,
|
||||
NM_MT_STOP_TEST_ACK,
|
||||
NM_MT_STOP_TEST_NACK,
|
||||
/* State Management and Event Report Messages */
|
||||
NM_MT_STATECHG_EVENT_REP = 0x61,
|
||||
NM_MT_FAILURE_EVENT_REP,
|
||||
NM_MT_STOP_EVENT_REP,
|
||||
NM_MT_STOP_EVENT_REP_ACK,
|
||||
NM_MT_STOP_EVENT_REP_NACK,
|
||||
NM_MT_REST_EVENT_REP,
|
||||
NM_MT_REST_EVENT_REP_ACK,
|
||||
NM_MT_REST_EVENT_REP_NACK,
|
||||
NM_MT_CHG_ADM_STATE,
|
||||
NM_MT_CHG_ADM_STATE_ACK,
|
||||
NM_MT_CHG_ADM_STATE_NACK,
|
||||
NM_MT_CHG_ADM_STATE_REQ,
|
||||
NM_MT_CHG_ADM_STATE_REQ_ACK,
|
||||
NM_MT_CHG_ADM_STATE_REQ_NACK,
|
||||
NM_MT_REP_OUTST_ALARMS = 0x93,
|
||||
NM_MT_REP_OUTST_ALARMS_ACK,
|
||||
NM_MT_REP_OUTST_ALARMS_NACK,
|
||||
/* Equipment Management Messages */
|
||||
NM_MT_CHANGEOVER = 0x71,
|
||||
NM_MT_CHANGEOVER_ACK,
|
||||
NM_MT_CHANGEOVER_NACK,
|
||||
NM_MT_OPSTART,
|
||||
NM_MT_OPSTART_ACK,
|
||||
NM_MT_OPSTART_NACK,
|
||||
NM_MT_REINIT,
|
||||
NM_MT_REINIT_ACK,
|
||||
NM_MT_REINIT_NACK,
|
||||
NM_MT_SET_SITE_OUT, /* BS11: get alarm ?!? */
|
||||
NM_MT_SET_SITE_OUT_ACK,
|
||||
NM_MT_SET_SITE_OUT_NACK,
|
||||
NM_MT_CHG_HW_CONF = 0x90,
|
||||
NM_MT_CHG_HW_CONF_ACK,
|
||||
NM_MT_CHG_HW_CONF_NACK,
|
||||
/* Measurement Management Messages */
|
||||
NM_MT_MEAS_RES_REQ = 0x8a,
|
||||
NM_MT_MEAS_RES_RESP,
|
||||
NM_MT_STOP_MEAS,
|
||||
NM_MT_START_MEAS,
|
||||
/* Other Messages */
|
||||
NM_MT_GET_ATTR = 0x81,
|
||||
NM_MT_GET_ATTR_RESP,
|
||||
NM_MT_GET_ATTR_NACK,
|
||||
NM_MT_SET_ALARM_THRES,
|
||||
NM_MT_SET_ALARM_THRES_ACK,
|
||||
NM_MT_SET_ALARM_THRES_NACK,
|
||||
|
||||
NM_MT_IPACC_RESTART = 0x87,
|
||||
NM_MT_IPACC_RESTART_ACK,
|
||||
};
|
||||
|
||||
enum abis_nm_msgtype_bs11 {
|
||||
NM_MT_BS11_RESET_RESOURCE = 0x74,
|
||||
|
||||
NM_MT_BS11_BEGIN_DB_TX = 0xa3,
|
||||
NM_MT_BS11_BEGIN_DB_TX_ACK,
|
||||
NM_MT_BS11_BEGIN_DB_TX_NACK,
|
||||
NM_MT_BS11_END_DB_TX = 0xa6,
|
||||
NM_MT_BS11_END_DB_TX_ACK,
|
||||
NM_MT_BS11_END_DB_TX_NACK,
|
||||
NM_MT_BS11_CREATE_OBJ = 0xa9,
|
||||
NM_MT_BS11_CREATE_OBJ_ACK,
|
||||
NM_MT_BS11_CREATE_OBJ_NACK,
|
||||
NM_MT_BS11_DELETE_OBJ = 0xac,
|
||||
NM_MT_BS11_DELETE_OBJ_ACK,
|
||||
NM_MT_BS11_DELETE_OBJ_NACK,
|
||||
|
||||
NM_MT_BS11_SET_ATTR = 0xd0,
|
||||
NM_MT_BS11_SET_ATTR_ACK,
|
||||
NM_MT_BS11_SET_ATTR_NACK,
|
||||
NM_MT_BS11_LMT_SESSION = 0xdc,
|
||||
|
||||
NM_MT_BS11_GET_STATE = 0xe3,
|
||||
NM_MT_BS11_GET_STATE_ACK,
|
||||
NM_MT_BS11_LMT_LOGON = 0xe5,
|
||||
NM_MT_BS11_LMT_LOGON_ACK,
|
||||
NM_MT_BS11_RESTART = 0xe7,
|
||||
NM_MT_BS11_RESTART_ACK,
|
||||
NM_MT_BS11_DISCONNECT = 0xe9,
|
||||
NM_MT_BS11_DISCONNECT_ACK,
|
||||
NM_MT_BS11_LMT_LOGOFF = 0xec,
|
||||
NM_MT_BS11_LMT_LOGOFF_ACK,
|
||||
NM_MT_BS11_RECONNECT = 0xf1,
|
||||
NM_MT_BS11_RECONNECT_ACK,
|
||||
};
|
||||
|
||||
enum abis_nm_msgtype_ipacc {
|
||||
NM_MT_IPACC_RSL_CONNECT = 0xe0,
|
||||
NM_MT_IPACC_RSL_CONNECT_ACK,
|
||||
NM_MT_IPACC_RSL_CONNECT_NACK,
|
||||
NM_MT_IPACC_SET_NVATTR = 0xef,
|
||||
NM_MT_IPACC_SET_NVATTR_ACK,
|
||||
NM_MT_IPACC_SET_NVATTR_NACK,
|
||||
NM_MT_IPACC_GET_NVATTR = 0xf2,
|
||||
NM_MT_IPACC_GET_NVATTR_ACK,
|
||||
NM_MT_IPACC_GET_NVATTR_NACK,
|
||||
};
|
||||
|
||||
enum abis_nm_bs11_cell_alloc {
|
||||
NM_BS11_CANR_GSM = 0x00,
|
||||
NM_BS11_CANR_DCS1800 = 0x01,
|
||||
};
|
||||
|
||||
/* Section 9.2: Object Class */
|
||||
enum abis_nm_obj_class {
|
||||
NM_OC_SITE_MANAGER = 0x00,
|
||||
NM_OC_BTS,
|
||||
NM_OC_RADIO_CARRIER,
|
||||
NM_OC_CHANNEL,
|
||||
NM_OC_BASEB_TRANSC,
|
||||
/* RFU: 05-FE */
|
||||
NM_OC_BS11_ADJC = 0xa0,
|
||||
NM_OC_BS11_HANDOVER = 0xa1,
|
||||
NM_OC_BS11_PWR_CTRL = 0xa2,
|
||||
NM_OC_BS11_BTSE = 0xa3, /* LMT? */
|
||||
NM_OC_BS11_RACK = 0xa4,
|
||||
NM_OC_BS11 = 0xa5, /* 01: ALCO */
|
||||
NM_OC_BS11_TEST = 0xa6,
|
||||
NM_OC_BS11_ENVABTSE = 0xa8,
|
||||
NM_OC_BS11_BPORT = 0xa9,
|
||||
|
||||
NM_OC_GPRS_NSE = 0xf0,
|
||||
NM_OC_GPRS_CELL = 0xf1,
|
||||
NM_OC_GPRS_NSVC0 = 0xf2,
|
||||
NM_OC_GPRS_NSVC1 = 0xf3,
|
||||
|
||||
NM_OC_NULL = 0xff,
|
||||
};
|
||||
|
||||
/* Section 9.4: Attributes */
|
||||
enum abis_nm_attr {
|
||||
NM_ATT_ABIS_CHANNEL = 0x01,
|
||||
NM_ATT_ADD_INFO,
|
||||
NM_ATT_ADD_TEXT,
|
||||
NM_ATT_ADM_STATE,
|
||||
NM_ATT_ARFCN_LIST,
|
||||
NM_ATT_AUTON_REPORT,
|
||||
NM_ATT_AVAIL_STATUS,
|
||||
NM_ATT_BCCH_ARFCN,
|
||||
NM_ATT_BSIC,
|
||||
NM_ATT_BTS_AIR_TIMER,
|
||||
NM_ATT_CCCH_L_I_P,
|
||||
NM_ATT_CCCH_L_T,
|
||||
NM_ATT_CHAN_COMB,
|
||||
NM_ATT_CONN_FAIL_CRIT,
|
||||
NM_ATT_DEST,
|
||||
/* res */
|
||||
NM_ATT_EVENT_TYPE = 0x11, /* BS11: file data ?!? */
|
||||
NM_ATT_FILE_ID,
|
||||
NM_ATT_FILE_VERSION,
|
||||
NM_ATT_GSM_TIME,
|
||||
NM_ATT_HSN,
|
||||
NM_ATT_HW_CONFIG,
|
||||
NM_ATT_HW_DESC,
|
||||
NM_ATT_INTAVE_PARAM,
|
||||
NM_ATT_INTERF_BOUND,
|
||||
NM_ATT_LIST_REQ_ATTR,
|
||||
NM_ATT_MAIO,
|
||||
NM_ATT_MANUF_STATE,
|
||||
NM_ATT_MANUF_THRESH,
|
||||
NM_ATT_MANUF_ID,
|
||||
NM_ATT_MAX_TA,
|
||||
NM_ATT_MDROP_LINK, /* 0x20 */
|
||||
NM_ATT_MDROP_NEXT,
|
||||
NM_ATT_NACK_CAUSES,
|
||||
NM_ATT_NY1,
|
||||
NM_ATT_OPER_STATE,
|
||||
NM_ATT_OVERL_PERIOD,
|
||||
NM_ATT_PHYS_CONF,
|
||||
NM_ATT_POWER_CLASS,
|
||||
NM_ATT_POWER_THRESH,
|
||||
NM_ATT_PROB_CAUSE,
|
||||
NM_ATT_RACH_B_THRESH,
|
||||
NM_ATT_LDAVG_SLOTS,
|
||||
NM_ATT_RAD_SUBC,
|
||||
NM_ATT_RF_MAXPOWR_R,
|
||||
NM_ATT_SITE_INPUTS,
|
||||
NM_ATT_SITE_OUTPUTS,
|
||||
NM_ATT_SOURCE, /* 0x30 */
|
||||
NM_ATT_SPEC_PROB,
|
||||
NM_ATT_START_TIME,
|
||||
NM_ATT_T200,
|
||||
NM_ATT_TEI,
|
||||
NM_ATT_TEST_DUR,
|
||||
NM_ATT_TEST_NO,
|
||||
NM_ATT_TEST_REPORT,
|
||||
NM_ATT_VSWR_THRESH,
|
||||
NM_ATT_WINDOW_SIZE,
|
||||
/* Res */
|
||||
NM_ATT_BS11_RSSI_OFFS = 0x3d,
|
||||
NM_ATT_BS11_TXPWR = 0x3e,
|
||||
NM_ATT_BS11_DIVERSITY = 0x3f,
|
||||
/* Res */
|
||||
NM_ATT_TSC = 0x40,
|
||||
NM_ATT_SW_CONFIG,
|
||||
NM_ATT_SW_DESCR,
|
||||
NM_ATT_SEVERITY,
|
||||
NM_ATT_GET_ARI,
|
||||
NM_ATT_HW_CONF_CHG,
|
||||
NM_ATT_OUTST_ALARM,
|
||||
NM_ATT_FILE_DATA,
|
||||
NM_ATT_MEAS_RES,
|
||||
NM_ATT_MEAS_TYPE,
|
||||
|
||||
NM_ATT_BS11_ESN_FW_CODE_NO = 0x4c,
|
||||
NM_ATT_BS11_ESN_HW_CODE_NO = 0x4f,
|
||||
|
||||
NM_ATT_BS11_ESN_PCB_SERIAL = 0x55,
|
||||
NM_ATT_BS11_EXCESSIVE_DISTANCE = 0x58,
|
||||
|
||||
NM_ATT_BS11_ALL_TEST_CATG = 0x60,
|
||||
NM_ATT_BS11_BTSLS_HOPPING,
|
||||
NM_ATT_BS11_CELL_ALLOC_NR,
|
||||
NM_ATT_BS11_CALL_GLOBAL_ID,
|
||||
NM_ATT_BS11_ENA_INTERF_CLASS = 0x66,
|
||||
NM_ATT_BS11_ENA_INT_INTEC_HANDO = 0x67,
|
||||
NM_ATT_BS11_ENA_INT_INTRC_HANDO = 0x68,
|
||||
NM_ATT_BS11_ENA_MS_PWR_CTRL = 0x69,
|
||||
NM_ATT_BS11_ENA_PWR_BDGT_HO = 0x6a,
|
||||
NM_ATT_BS11_ENA_PWR_CTRL_RLFW = 0x6b,
|
||||
NM_ATT_BS11_ENA_RXLEV_HO = 0x6c,
|
||||
NM_ATT_BS11_ENA_RXQUAL_HO = 0x6d,
|
||||
NM_ATT_BS11_FACCH_QUAL = 0x6e,
|
||||
|
||||
NM_ATT_IPACC_RSL_BSC_IP = 0x80,
|
||||
NM_ATT_IPACC_RSL_BSC_PORT = 0x81,
|
||||
NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */
|
||||
NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */
|
||||
NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts-<mac-as-string> */
|
||||
NM_ATT_IPACC_PRIM_OML_IP = 0x95,
|
||||
NM_ATT_IPACC_SEC_OML_IP = 0x96,
|
||||
|
||||
NM_ATT_BS11_RF_RES_IND_PER = 0x8f,
|
||||
|
||||
NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90,
|
||||
NM_ATT_BS11_ABIS_EXT_TIME = 0x91,
|
||||
NM_ATT_BS11_TIMER_HO_REQUEST = 0x92,
|
||||
NM_ATT_BS11_TIMER_NCELL = 0x93,
|
||||
NM_ATT_BS11_TSYNC = 0x94,
|
||||
NM_ATT_BS11_TTRAU = 0x95,
|
||||
NM_ATT_BS11_EMRG_CFG_MEMBER = 0x9b,
|
||||
NM_ATT_BS11_TRX_AREA = 0x9f,
|
||||
|
||||
NM_ATT_BS11_BCCH_RECONF = 0xd7,
|
||||
NM_ATT_BS11_BIT_ERR_THESH = 0xa0,
|
||||
NM_ATT_BS11_BOOT_SW_VERS = 0xa1,
|
||||
NM_ATT_BS11_CCLK_ACCURACY = 0xa3,
|
||||
NM_ATT_BS11_CCLK_TYPE = 0xa4,
|
||||
NM_ATT_BS11_INP_IMPEDANCE = 0xaa,
|
||||
NM_ATT_BS11_L1_PROT_TYPE = 0xab,
|
||||
NM_ATT_BS11_LINE_CFG = 0xac,
|
||||
NM_ATT_BS11_LI_PORT_1 = 0xad,
|
||||
NM_ATT_BS11_LI_PORT_2 = 0xae,
|
||||
|
||||
NM_ATT_BS11_L1_REM_ALM_TYPE = 0xb0,
|
||||
NM_ATT_BS11_SW_LOAD_INTENDED = 0xbb,
|
||||
NM_ATT_BS11_SW_LOAD_SAFETY = 0xbc,
|
||||
NM_ATT_BS11_SW_LOAD_STORED = 0xbd,
|
||||
|
||||
NM_ATT_BS11_VENDOR_NAME = 0xc1,
|
||||
NM_ATT_BS11_HOPPING_MODE = 0xc5,
|
||||
NM_ATT_BS11_LMT_LOGON_SESSION = 0xc6,
|
||||
NM_ATT_BS11_LMT_LOGIN_TIME = 0xc7,
|
||||
NM_ATT_BS11_LMT_USER_ACC_LEV = 0xc8,
|
||||
NM_ATT_BS11_LMT_USER_NAME = 0xc9,
|
||||
|
||||
NM_ATT_BS11_L1_CONTROL_TS = 0xd8,
|
||||
NM_ATT_BS11_RADIO_MEAS_GRAN = 0xdc, /* in SACCH multiframes */
|
||||
NM_ATT_BS11_RADIO_MEAS_REP = 0xdd,
|
||||
|
||||
NM_ATT_BS11_SH_LAPD_INT_TIMER = 0xe8,
|
||||
|
||||
NM_ATT_BS11_BTS_STATE = 0xf0,
|
||||
NM_ATT_BS11_E1_STATE = 0xf1,
|
||||
NM_ATT_BS11_PLL = 0xf2,
|
||||
NM_ATT_BS11_RX_OFFSET = 0xf3,
|
||||
NM_ATT_BS11_ANT_TYPE = 0xf4,
|
||||
NM_ATT_BS11_PLL_MODE = 0xfc,
|
||||
NM_ATT_BS11_PASSWORD = 0xfd,
|
||||
};
|
||||
#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE
|
||||
|
||||
/* Section 9.4.4: Administrative State */
|
||||
enum abis_nm_adm_state {
|
||||
NM_STATE_LOCKED = 0x01,
|
||||
NM_STATE_UNLOCKED = 0x02,
|
||||
NM_STATE_SHUTDOWN = 0x03,
|
||||
NM_STATE_NULL = 0xff,
|
||||
};
|
||||
|
||||
/* Section 9.4.13: Channel Combination */
|
||||
enum abis_nm_chan_comb {
|
||||
NM_CHANC_TCHFull = 0x00,
|
||||
NM_CHANC_TCHHalf = 0x01,
|
||||
NM_CHANC_TCHHalf2 = 0x02,
|
||||
NM_CHANC_SDCCH = 0x03,
|
||||
NM_CHANC_mainBCCH = 0x04,
|
||||
NM_CHANC_BCCCHComb = 0x05,
|
||||
NM_CHANC_BCCH = 0x06,
|
||||
NM_CHANC_BCCH_CBCH = 0x07,
|
||||
NM_CHANC_SDCCH_CBCH = 0x08,
|
||||
};
|
||||
|
||||
/* Section 9.4.16: Event Type */
|
||||
enum abis_nm_event_type {
|
||||
NM_EVT_COMM_FAIL = 0x00,
|
||||
NM_EVT_QOS_FAIL = 0x01,
|
||||
NM_EVT_PROC_FAIL = 0x02,
|
||||
NM_EVT_EQUIP_FAIL = 0x03,
|
||||
NM_EVT_ENV_FAIL = 0x04,
|
||||
};
|
||||
|
||||
/* Section: 9.4.63: Perceived Severity */
|
||||
enum abis_nm_severity {
|
||||
NM_SEVER_CEASED = 0x00,
|
||||
NM_SEVER_CRITICAL = 0x01,
|
||||
NM_SEVER_MAJOR = 0x02,
|
||||
NM_SEVER_MINOR = 0x03,
|
||||
NM_SEVER_WARNING = 0x04,
|
||||
NM_SEVER_INDETERMINATE = 0x05,
|
||||
};
|
||||
|
||||
/* Section 9.4.43: Probable Cause Type */
|
||||
enum abis_nm_pcause_type {
|
||||
NM_PCAUSE_T_X721 = 0x01,
|
||||
NM_PCAUSE_T_GSM = 0x02,
|
||||
NM_PCAUSE_T_MANUF = 0x03,
|
||||
};
|
||||
|
||||
/* Section 9.4.36: NACK Causes */
|
||||
enum abis_nm_nack_cause {
|
||||
/* General Nack Causes */
|
||||
NM_NACK_INCORR_STRUCT = 0x01,
|
||||
NM_NACK_MSGTYPE_INVAL = 0x02,
|
||||
NM_NACK_OBJCLASS_INVAL = 0x05,
|
||||
NM_NACK_OBJCLASS_NOTSUPP = 0x06,
|
||||
NM_NACK_BTSNR_UNKN = 0x07,
|
||||
NM_NACK_TRXNR_UNKN = 0x08,
|
||||
NM_NACK_OBJINST_UNKN = 0x09,
|
||||
NM_NACK_ATTRID_INVAL = 0x0c,
|
||||
NM_NACK_ATTRID_NOTSUPP = 0x0d,
|
||||
NM_NACK_PARAM_RANGE = 0x0e,
|
||||
NM_NACK_ATTRLIST_INCONSISTENT = 0x0f,
|
||||
NM_NACK_SPEC_IMPL_NOTSUPP = 0x10,
|
||||
NM_NACK_CANT_PERFORM = 0x11,
|
||||
/* Specific Nack Causes */
|
||||
NM_NACK_RES_NOTIMPL = 0x19,
|
||||
NM_NACK_RES_NOTAVAIL = 0x1a,
|
||||
NM_NACK_FREQ_NOTAVAIL = 0x1b,
|
||||
NM_NACK_TEST_NOTSUPP = 0x1c,
|
||||
NM_NACK_CAPACITY_RESTR = 0x1d,
|
||||
NM_NACK_PHYSCFG_NOTPERFORM = 0x1e,
|
||||
NM_NACK_TEST_NOTINIT = 0x1f,
|
||||
NM_NACK_PHYSCFG_NOTRESTORE = 0x20,
|
||||
NM_NACK_TEST_NOSUCH = 0x21,
|
||||
NM_NACK_TEST_NOSTOP = 0x22,
|
||||
NM_NACK_MSGINCONSIST_PHYSCFG = 0x23,
|
||||
NM_NACK_FILE_INCOMPLETE = 0x25,
|
||||
NM_NACK_FILE_NOTAVAIL = 0x26,
|
||||
MN_NACK_FILE_NOTACTIVATE = 0x27,
|
||||
NM_NACK_REQ_NOT_GRANT = 0x28,
|
||||
NM_NACK_WAIT = 0x29,
|
||||
NM_NACK_NOTH_REPORT_EXIST = 0x2a,
|
||||
NM_NACK_MEAS_NOTSUPP = 0x2b,
|
||||
NM_NACK_MEAS_NOTSTART = 0x2c,
|
||||
};
|
||||
|
||||
/* Section 9.4.1 */
|
||||
struct abis_nm_channel {
|
||||
u_int8_t attrib;
|
||||
u_int8_t bts_port;
|
||||
u_int8_t timeslot;
|
||||
u_int8_t subslot;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */
|
||||
enum abis_bs11_objtype {
|
||||
BS11_OBJ_ALCO = 0x01,
|
||||
BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */
|
||||
BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */
|
||||
BS11_OBJ_CCLK = 0x04,
|
||||
BS11_OBJ_GPSU = 0x06,
|
||||
BS11_OBJ_LI = 0x07,
|
||||
BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/
|
||||
};
|
||||
|
||||
enum abis_bs11_trx_power {
|
||||
BS11_TRX_POWER_GSM_2W = 0x06,
|
||||
BS11_TRX_POWER_GSM_250mW= 0x07,
|
||||
BS11_TRX_POWER_GSM_80mW = 0x08,
|
||||
BS11_TRX_POWER_GSM_30mW = 0x09,
|
||||
BS11_TRX_POWER_DCS_3W = 0x0a,
|
||||
BS11_TRX_POWER_DCS_1W6 = 0x0b,
|
||||
BS11_TRX_POWER_DCS_500mW= 0x0c,
|
||||
BS11_TRX_POWER_DCS_160mW= 0x0d,
|
||||
};
|
||||
|
||||
enum abis_bs11_li_pll_mode {
|
||||
BS11_LI_PLL_LOCKED = 2,
|
||||
BS11_LI_PLL_STANDALONE = 3,
|
||||
};
|
||||
|
||||
enum abis_bs11_phase {
|
||||
BS11_STATE_SOFTWARE_RQD = 0x01,
|
||||
BS11_STATE_LOAD_SMU_INTENDED = 0x11,
|
||||
BS11_STATE_LOAD_SMU_SAFETY = 0x21,
|
||||
BS11_STATE_LOAD_FAILED = 0x31,
|
||||
BS11_STATE_LOAD_DIAGNOSTIC = 0x41,
|
||||
BS11_STATE_WARM_UP = 0x51,
|
||||
BS11_STATE_WARM_UP_2 = 0x52,
|
||||
BS11_STATE_WAIT_MIN_CFG = 0x62,
|
||||
BS11_STATE_MAINTENANCE = 0x72,
|
||||
BS11_STATE_LOAD_MBCCU = 0x92,
|
||||
BS11_STATE_WAIT_MIN_CFG_2 = 0xA2,
|
||||
BS11_STATE_NORMAL = 0x03,
|
||||
BS11_STATE_ABIS_LOAD = 0x13,
|
||||
};
|
||||
|
||||
|
||||
/* PUBLIC */
|
||||
|
||||
struct msgb;
|
||||
|
||||
struct abis_nm_cfg {
|
||||
/* callback for unidirectional reports */
|
||||
int (*report_cb)(struct msgb *,
|
||||
struct abis_om_fom_hdr *);
|
||||
/* callback for software activate requests from BTS */
|
||||
int (*sw_act_req)(struct msgb *);
|
||||
};
|
||||
|
||||
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_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,
|
||||
u_int8_t i1, u_int8_t i2, u_int8_t adm_state);
|
||||
int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr,
|
||||
u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot,
|
||||
u_int8_t tei);
|
||||
int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx,
|
||||
u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot);
|
||||
int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
|
||||
u_int8_t e1_port, u_int8_t e1_timeslot,
|
||||
u_int8_t e1_subslot);
|
||||
int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len);
|
||||
int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len);
|
||||
int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb);
|
||||
int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
|
||||
u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len);
|
||||
int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg);
|
||||
int abis_nm_event_reports(struct gsm_bts *bts, int on);
|
||||
int abis_nm_reset_resource(struct gsm_bts *bts);
|
||||
int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
|
||||
u_int8_t win_size, int forced,
|
||||
gsm_cbfn *cbfn, void *cb_data);
|
||||
int abis_nm_software_load_status(struct gsm_bts *bts);
|
||||
int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
|
||||
gsm_cbfn *cbfn, void *cb_data);
|
||||
|
||||
/* Siemens / BS-11 specific */
|
||||
int abis_nm_bs11_reset_resource(struct gsm_bts *bts);
|
||||
int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin);
|
||||
int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type,
|
||||
u_int8_t idx, u_int8_t attr_len, const u_int8_t *attr);
|
||||
int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, u_int8_t idx);
|
||||
int abis_nm_bs11_create_bport(struct gsm_bts *bts, u_int8_t idx);
|
||||
int abis_nm_bs11_delete_object(struct gsm_bts *bts,
|
||||
enum abis_bs11_objtype type, u_int8_t idx);
|
||||
int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port,
|
||||
u_int8_t e1_timeslot, u_int8_t e1_subslot, u_int8_t tei);
|
||||
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_factory_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_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,
|
||||
u_int8_t win_size, int forced, gsm_cbfn *cbfn);
|
||||
int abis_nm_bs11_set_ext_time(struct gsm_bts *bts);
|
||||
int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect);
|
||||
int abis_nm_bs11_restart(struct gsm_bts *bts);
|
||||
|
||||
/* ip.access nanoBTS specific commands */
|
||||
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 attr_len);
|
||||
int abis_nm_ipaccess_restart(struct gsm_bts *bts);
|
||||
|
||||
/* Functions calling into other code parts */
|
||||
enum nm_evt {
|
||||
EVT_STATECHG_OPER,
|
||||
EVT_STATECHG_ADM,
|
||||
};
|
||||
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state);
|
||||
|
||||
const char *nm_opstate_name(u_int8_t os);
|
||||
const char *nm_avail_name(u_int8_t avail);
|
||||
#endif /* _NM_H */
|
|
@ -0,0 +1,415 @@
|
|||
/* GSM Radio Signalling Link messages on the A-bis interface
|
||||
* 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
|
||||
|
||||
/* (C) 2008 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _RSL_H
|
||||
#define _RSL_H
|
||||
|
||||
struct abis_rsl_common_hdr {
|
||||
u_int8_t msg_discr;
|
||||
u_int8_t msg_type;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 8.3 */
|
||||
struct abis_rsl_rll_hdr {
|
||||
struct abis_rsl_common_hdr c;
|
||||
u_int8_t ie_chan;
|
||||
u_int8_t chan_nr;
|
||||
u_int8_t ie_link_id;
|
||||
u_int8_t link_id;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 8.3 and 8.4 */
|
||||
struct abis_rsl_dchan_hdr {
|
||||
struct abis_rsl_common_hdr c;
|
||||
u_int8_t ie_chan;
|
||||
u_int8_t chan_nr;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* Chapter 9.1 */
|
||||
#define ABIS_RSL_MDISC_RLL 0x02
|
||||
#define ABIS_RSL_MDISC_DED_CHAN 0x08
|
||||
#define ABIS_RSL_MDISC_COM_CHAN 0x0c
|
||||
#define ABIS_RSL_MDISC_TRX 0x10
|
||||
#define ABIS_RSL_MDISC_LOC 0x20
|
||||
#define ABIS_RSL_MDISC_IPACCESS 0x7e
|
||||
#define ABIS_RSL_MDISC_TRANSP 0x01
|
||||
|
||||
#define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01)
|
||||
|
||||
/* Chapter 9.1 */
|
||||
enum abis_rsl_msgtype {
|
||||
/* Radio Link Layer Management */
|
||||
RSL_MT_DATA_REQ = 0x01,
|
||||
RSL_MT_DATA_IND,
|
||||
RSL_MT_ERROR_IND,
|
||||
RSL_MT_EST_REQ,
|
||||
RSL_MT_EST_CONF,
|
||||
RSL_MT_EST_IND,
|
||||
RSL_MT_REL_REQ,
|
||||
RSL_MT_REL_CONF,
|
||||
RSL_MT_REL_IND,
|
||||
RSL_MT_UNIT_DATA_REQ,
|
||||
RSL_MT_UNIT_DATA_IND, /* 0x0b */
|
||||
|
||||
/* Common Channel Management / TRX Management */
|
||||
RSL_MT_BCCH_INFO = 0x11,
|
||||
RSL_MT_CCCH_LOAD_IND,
|
||||
RSL_MT_CHAN_RQD,
|
||||
RSL_MT_DELETE_IND,
|
||||
RSL_MT_PAGING_CMD,
|
||||
RSL_MT_IMMEDIATE_ASSIGN_CMD,
|
||||
RSL_MT_SMS_BC_REQ,
|
||||
/* empty */
|
||||
RSL_MT_RF_RES_IND = 0x19,
|
||||
RSL_MT_SACCH_FILL,
|
||||
RSL_MT_OVERLOAD,
|
||||
RSL_MT_ERROR_REPORT,
|
||||
RSL_MT_SMS_BC_CMD,
|
||||
RSL_MT_CBCH_LOAD_IND,
|
||||
RSL_MT_NOT_CMD, /* 0x1f */
|
||||
|
||||
/* Dedicate Channel Management */
|
||||
RSL_MT_CHAN_ACTIV = 0x21,
|
||||
RSL_MT_CHAN_ACTIV_ACK,
|
||||
RSL_MT_CHAN_ACTIV_NACK,
|
||||
RSL_MT_CONN_FAIL,
|
||||
RSL_MT_DEACTIVATE_SACCH,
|
||||
RSL_MT_ENCR_CMD,
|
||||
RSL_MT_HANDO_DET,
|
||||
RSL_MT_MEAS_RES,
|
||||
RSL_MT_MODE_MODIFY_REQ,
|
||||
RSL_MT_MODE_MODIFY_ACK,
|
||||
RSL_MT_MODE_MODIFY_NACK,
|
||||
RSL_MT_PHY_CONTEXT_REQ,
|
||||
RSL_MT_PHY_CONTEXT_CONF,
|
||||
RSL_MT_RF_CHAN_REL,
|
||||
RSL_MT_MS_POWER_CONTROL,
|
||||
RSL_MT_BS_POWER_CONTROL, /* 0x30 */
|
||||
RSL_MT_PREPROC_CONFIG,
|
||||
RSL_MT_PREPROC_MEAS_RES,
|
||||
RSL_MT_RF_CHAN_REL_ACK,
|
||||
RSL_MT_SACCH_INFO_MODIFY,
|
||||
RSL_MT_TALKER_DET,
|
||||
RSL_MT_LISTENER_DET,
|
||||
RSL_MT_REMOTE_CODEC_CONF_REP,
|
||||
RSL_MT_RTD_REP,
|
||||
RSL_MT_PRE_HANDO_NOTIF,
|
||||
RSL_MT_MR_CODEC_MOD_REQ,
|
||||
RSL_MT_MR_CODEC_MOD_ACK,
|
||||
RSL_MT_MR_CODEC_MOD_NACK,
|
||||
RSL_MT_MR_CODEC_MOD_PER,
|
||||
RSL_MT_TFO_REP,
|
||||
RSL_MT_TFO_MOD_REQ, /* 0x3f */
|
||||
|
||||
/* ip.access specific RSL message types */
|
||||
RSL_MT_IPAC_BIND = 0x70, /* Bind to local BTS RTP port */
|
||||
RSL_MT_IPAC_BIND_ACK,
|
||||
RSL_MT_IPAC_BIND_NACK,
|
||||
RSL_MT_IPAC_CONNECT = 0x73,
|
||||
RSL_MT_IPAC_CONNECT_ACK,
|
||||
RSL_MT_IPAC_CONNECT_NACK,
|
||||
RSL_MT_IPAC_DISCONNECT_IND = 0x76,
|
||||
|
||||
};
|
||||
|
||||
/* Chapter 9.3 */
|
||||
enum abis_rsl_ie {
|
||||
RSL_IE_CHAN_NR = 0x01,
|
||||
RSL_IE_LINK_IDENT,
|
||||
RSL_IE_ACT_TYPE,
|
||||
RSL_IE_BS_POWER,
|
||||
RSL_IE_CHAN_IDENT,
|
||||
RSL_IE_CHAN_MODE,
|
||||
RSL_IE_ENCR_INFO,
|
||||
RSL_IE_FRAME_NUMBER,
|
||||
RSL_IE_HANDO_REF,
|
||||
RSL_IE_L1_INFO,
|
||||
RSL_IE_L3_INFO,
|
||||
RSL_IE_MS_IDENTITY,
|
||||
RSL_IE_MS_POWER,
|
||||
RSL_IE_PAGING_GROUP,
|
||||
RSL_IE_PAGING_LOAD,
|
||||
RSL_IE_PYHS_CONTEXT = 0x10,
|
||||
RSL_IE_ACCESS_DELAY,
|
||||
RSL_IE_RACH_LOAD,
|
||||
RSL_IE_REQ_REFERENCE,
|
||||
RSL_IE_RELEASE_MODE,
|
||||
RSL_IE_RESOURCE_INFO,
|
||||
RSL_IE_RLM_CAUSE,
|
||||
RSL_IE_STARTNG_TIME,
|
||||
RSL_IE_TIMING_ADVANCE,
|
||||
RSL_IE_UPLINK_MEAS,
|
||||
RSL_IE_CAUSE,
|
||||
RSL_IE_MEAS_RES_NR,
|
||||
RSL_IE_MSG_ID,
|
||||
/* reserved */
|
||||
RSL_IE_SYSINFO_TYPE = 0x1e,
|
||||
RSL_IE_MS_POWER_PARAM,
|
||||
RSL_IE_BS_POWER_PARAM,
|
||||
RSL_IE_PREPROC_PARAM,
|
||||
RSL_IE_PREPROC_MEAS,
|
||||
RSL_IE_IMM_ASS_INFO, /* Phase 1 (3.6.0), later Full below */
|
||||
RSL_IE_SMSCB_INFO = 0x24,
|
||||
RSL_IE_MS_TIMING_OFFSET,
|
||||
RSL_IE_ERR_MSG,
|
||||
RSL_IE_FULL_BCCH_INFO,
|
||||
RSL_IE_CHAN_NEEDED,
|
||||
RSL_IE_CB_CMD_TYPE,
|
||||
RSL_IE_SMSCB_MSG,
|
||||
RSL_IE_FULL_IMM_ASS_INFO,
|
||||
RSL_IE_SACCH_INFO,
|
||||
RSL_IE_CBCH_LOAD_INFO,
|
||||
RSL_IE_SMSCB_CHAN_INDICATOR,
|
||||
RSL_IE_GROUP_CALL_REF,
|
||||
RSL_IE_CHAN_DESC,
|
||||
RSL_IE_NCH_DRX_INFO,
|
||||
RSL_IE_CMD_INDICATOR,
|
||||
RSL_IE_EMLPP_PRIO,
|
||||
RSL_IE_UIC,
|
||||
RSL_IE_MAIN_CHAN_REF,
|
||||
RSL_IE_MR_CONFIG,
|
||||
RSL_IE_MR_CONTROL,
|
||||
RSL_IE_SUP_CODEC_TYPES,
|
||||
RSL_IE_CODEC_CONFIG,
|
||||
RSL_IE_RTD,
|
||||
RSL_IE_TFO_STATUS,
|
||||
RSL_IE_LLP_APDU,
|
||||
|
||||
RSL_IE_IPAC_REMOTE_IP = 0xf0,
|
||||
RSL_IE_IPAC_REMOTE_PORT = 0xf1,
|
||||
RSL_IE_IPAC_LOCAL_PORT = 0xf3,
|
||||
RSL_IE_IPAC_LOCAL_IP = 0xf5,
|
||||
};
|
||||
|
||||
/* Chapter 9.3.1 */
|
||||
#define RSL_CHAN_NR_MASK 0xf8
|
||||
#define RSL_CHAN_Bm_ACCHs 0x08
|
||||
#define RSL_CHAN_Lm_ACCHs 0x10 /* .. 0x18 */
|
||||
#define RSL_CHAN_SDCCH4_ACCH 0x20 /* .. 0x38 */
|
||||
#define RSL_CHAN_SDCCH8_ACCH 0x40 /* ...0x78 */
|
||||
#define RSL_CHAN_BCCH 0x80
|
||||
#define RSL_CHAN_RACH 0x88
|
||||
#define RSL_CHAN_PCH_AGCH 0x90
|
||||
|
||||
/* Chapter 9.3.3 */
|
||||
#define RSL_ACT_TYPE_INITIAL 0x00
|
||||
#define RSL_ACT_TYPE_REACT 0x80
|
||||
#define RSL_ACT_INTRA_IMM_ASS 0x00
|
||||
#define RSL_ACT_INTRA_NORM_ASS 0x01
|
||||
#define RSL_ACT_INTER_ASYNC 0x02
|
||||
#define RSL_ACT_INTER_SYNC 0x03
|
||||
#define RSL_ACT_SECOND_ADD 0x04
|
||||
#define RSL_ACT_SECOND_MULTI 0x05
|
||||
|
||||
/* Chapter 9.3.6 */
|
||||
struct rsl_ie_chan_mode {
|
||||
u_int8_t dtx_dtu;
|
||||
u_int8_t spd_ind;
|
||||
u_int8_t chan_rt;
|
||||
u_int8_t chan_rate;
|
||||
} __attribute__ ((packed));
|
||||
#define RSL_CMOD_DTXu 0x01 /* uplink */
|
||||
#define RSL_CMOD_DTXd 0x02 /* downlink */
|
||||
#define RSL_CMOD_SPD_SPEECH 0x01
|
||||
#define RSL_CMOD_SPD_DATA 0x02
|
||||
#define RSL_CMOD_SPD_SIGN 0x03
|
||||
#define RSL_CMOD_CRT_SDCCH 0x01
|
||||
#define RSL_CMOD_CRT_TCH_Bm 0x08 /* full-rate */
|
||||
#define RSL_CMOD_CRT_TCH_Lm 0x09 /* half-rate */
|
||||
/* FIXME: More CRT types */
|
||||
#define RSL_CMOD_SP_GSM1 0x01
|
||||
#define RSL_CMOD_SP_GSM2 0x11
|
||||
#define RSL_CMOD_SP_GSM3 0x21
|
||||
|
||||
/* Chapter 9.3.5 */
|
||||
struct rsl_ie_chan_ident {
|
||||
/* GSM 04.08 10.5.2.5 */
|
||||
struct {
|
||||
u_int8_t iei;
|
||||
u_int8_t chan_nr; /* enc_chan_nr */
|
||||
u_int8_t oct3;
|
||||
u_int8_t oct4;
|
||||
} chan_desc;
|
||||
#if 0 /* spec says we need this but Abissim doesn't use it */
|
||||
struct {
|
||||
u_int8_t tag;
|
||||
u_int8_t len;
|
||||
} mobile_alloc;
|
||||
#endif
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 9.3.22 */
|
||||
#define RLL_CAUSE_T200_EXPIRED 0x01
|
||||
#define RLL_CAUSE_REEST_REQ 0x02
|
||||
#define RLL_CAUSE_UNSOL_UA_RESP 0x03
|
||||
#define RLL_CAUSE_UNSOL_DM_RESP 0x04
|
||||
#define RLL_CAUSE_UNSOL_DM_RESP_MF 0x05
|
||||
#define RLL_CAUSE_UNSOL_SPRV_RESP 0x06
|
||||
#define RLL_CAUSE_SEQ_ERR 0x07
|
||||
#define RLL_CAUSE_UFRM_INC_PARAM 0x08
|
||||
#define RLL_CAUSE_SFRM_INC_PARAM 0x09
|
||||
#define RLL_CAUSE_IFRM_INC_MBITS 0x0a
|
||||
#define RLL_CAUSE_IFRM_INC_LEN 0x0b
|
||||
#define RLL_CAUSE_FRM_UNIMPL 0x0c
|
||||
#define RLL_CAUSE_SABM_MF 0x0d
|
||||
#define RLL_CAUSE_SABM_INFO_NOTALL 0x0e
|
||||
|
||||
/* Chapter 9.3.26 */
|
||||
#define RSL_ERRCLS_NORMAL 0x00
|
||||
#define RSL_ERRCLS_RESOURCE_UNAVAIL 0x20
|
||||
#define RSL_ERRCLS_SERVICE_UNAVAIL 0x30
|
||||
#define RSL_ERRCLS_SERVICE_UNIMPL 0x40
|
||||
#define RSL_ERRCLS_INVAL_MSG 0x50
|
||||
#define RSL_ERRCLS_PROTO_ERROR 0x60
|
||||
#define RSL_ERRCLS_INTERWORKING 0x70
|
||||
|
||||
#define RSL_ERR_RADIO_IF_FAIL 0x00
|
||||
#define RSL_ERR_RADIO_LINK_FAIL 0x01
|
||||
#define RSL_ERR_HANDOVER_ACC_FAIL 0x02
|
||||
#define RSL_ERR_TALKER_ACC_FAIL 0x03
|
||||
#define RSL_ERR_OM_INTERVENTION 0x07
|
||||
#define RSL_ERR_EQUIPMENT_FAIL 0x20
|
||||
#define RSL_ERR_RR_UNAVAIL 0x21
|
||||
#define RSL_ERR_TERR_CH_FAIL 0x22
|
||||
#define RSL_ERR_CCCH_OVERLOAD 0x23
|
||||
#define RSL_ERR_ACCH_OVERLOAD 0x24
|
||||
#define RSL_ERR_PROCESSOR_OVERLOAD 0x25
|
||||
#define RSL_ERR_RES_UNAVAIL 0x2f
|
||||
#define RSL_ERR_TRANSC_UNAVAIL 0x30
|
||||
#define RSL_ERR_SERV_OPT_UNAVAIL 0x3f
|
||||
#define RSL_ERR_ENCR_UNIMPL 0x40
|
||||
#define RSL_ERR_SEV_OPT_UNIMPL 0x4f
|
||||
#define RSL_ERR_RCH_ALR_ACTV_ALLOC 0x50
|
||||
#define RSL_ERR_INVALID_MESSAGE 0x5f
|
||||
#define RSL_ERR_MSG_DISCR 0x60
|
||||
#define RSL_ERR_MSG_TYPE 0x61
|
||||
#define RSL_ERR_MSG_SEQA 0x62
|
||||
#define RSL_ERR_IE_ERROR 0x63
|
||||
#define RSL_ERR_MAND_IE_ERROR 0x64
|
||||
#define RSL_ERR_OPT_IE_ERROR 0x65
|
||||
#define RSL_ERR_IE_NONEXIST 0x66
|
||||
#define RSL_ERR_IE_LENGTH 0x67
|
||||
#define RSL_ERR_IE_CONTENT 0x68
|
||||
#define RSL_ERR_PROTO 0x6f
|
||||
#define RSL_ERR_INTERWORKING 0x7f
|
||||
|
||||
/* Chapter 9.3.30 */
|
||||
#define RSL_SYSTEM_INFO_8 0x00
|
||||
#define RSL_SYSTEM_INFO_1 0x01
|
||||
#define RSL_SYSTEM_INFO_2 0x02
|
||||
#define RSL_SYSTEM_INFO_3 0x03
|
||||
#define RSL_SYSTEM_INFO_4 0x04
|
||||
#define RSL_SYSTEM_INFO_5 0x05
|
||||
#define RSL_SYSTEM_INFO_6 0x06
|
||||
#define RSL_SYSTEM_INFO_7 0x07
|
||||
#define RSL_SYSTEM_INFO_16 0x08
|
||||
#define RSL_SYSTEM_INFO_17 0x09
|
||||
#define RSL_SYSTEM_INFO_2bis 0x0a
|
||||
#define RSL_SYSTEM_INFO_2ter 0x0b
|
||||
#define RSL_SYSTEM_INFO_5bis 0x0d
|
||||
#define RSL_SYSTEM_INFO_5ter 0x0e
|
||||
#define RSL_SYSTEM_INFO_10 0x0f
|
||||
#define REL_EXT_MEAS_ORDER 0x47
|
||||
#define RSL_MEAS_INFO 0x48
|
||||
#define RSL_SYSTEM_INFO_13 0x28
|
||||
#define RSL_SYSTEM_INFO_2quater 0x29
|
||||
#define RSL_SYSTEM_INFO_9 0x2a
|
||||
#define RSL_SYSTEM_INFO_18 0x2b
|
||||
#define RSL_SYSTEM_INFO_19 0x2c
|
||||
#define RSL_SYSTEM_INFO_20 0x2d
|
||||
|
||||
/* Chapter 9.3.40 */
|
||||
#define RSL_CHANNEED_ANY 0x00
|
||||
#define RSL_CHANNEED_SDCCH 0x01
|
||||
#define RSL_CHANNEED_TCH_F 0x02
|
||||
#define RSL_CHANNEED_TCH_ForH 0x03
|
||||
|
||||
/* Chapter 3.3.2.3 Brocast control channel */
|
||||
/* CCCH-CONF, NC is not combined */
|
||||
#define RSL_BCCH_CCCH_CONF_1_NC 0x00
|
||||
#define RSL_BCCH_CCCH_CONF_1_C 0x01
|
||||
#define RSL_BCCH_CCCH_CONF_2_NC 0x02
|
||||
#define RSL_BCCH_CCCH_CONF_3_NC 0x04
|
||||
#define RSL_BCCH_CCCH_CONF_4_NC 0x06
|
||||
|
||||
/* BS-PA-MFRMS */
|
||||
#define RSL_BS_PA_MFRMS_2 0x00
|
||||
#define RSL_BS_PA_MFRMS_3 0x01
|
||||
#define RSL_BS_PA_MFRMS_4 0x02
|
||||
#define RSL_BS_PA_MFRMS_5 0x03
|
||||
#define RSL_BS_PA_MFRMS_6 0x04
|
||||
#define RSL_BS_PA_MFRMS_7 0x05
|
||||
#define RSL_BS_PA_MFRMS_8 0x06
|
||||
#define RSL_BS_PA_MFRMS_9 0x07
|
||||
|
||||
|
||||
#include "msgb.h"
|
||||
|
||||
int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type,
|
||||
const u_int8_t *data, int len);
|
||||
int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type,
|
||||
const u_int8_t *data, int len);
|
||||
int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
|
||||
u_int8_t act_type,
|
||||
struct rsl_ie_chan_mode *chan_mode,
|
||||
struct rsl_ie_chan_ident *chan_ident,
|
||||
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 mode);
|
||||
int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
|
||||
int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
|
||||
u_int8_t *ms_ident, u_int8_t chan_needed);
|
||||
int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_needed,
|
||||
struct gsm_subscriber *subscr);
|
||||
int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val);
|
||||
|
||||
int rsl_data_request(struct msgb *msg, u_int8_t link_id);
|
||||
|
||||
/* ip.access specfic RSL extensions */
|
||||
int rsl_ipacc_bind(struct gsm_lchan *lchan);
|
||||
int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip,
|
||||
u_int16_t port, u_int16_t f8, u_int8_t fc);
|
||||
|
||||
int abis_rsl_rcvmsg(struct msgb *msg);
|
||||
|
||||
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);
|
||||
|
||||
/* to be provided by external code */
|
||||
int abis_rsl_sendmsg(struct msgb *msg);
|
||||
int rsl_chan_release(struct gsm_lchan *lchan);
|
||||
|
||||
/* BCCH related code */
|
||||
int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
|
||||
int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf);
|
||||
int rsl_number_of_paging_subchannels(struct gsm_bts *bts);
|
||||
|
||||
#endif /* RSL_MT_H */
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* (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 */
|
|
@ -0,0 +1,49 @@
|
|||
/* Management functions to allocate/release struct gsm_lchan */
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
* (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.
|
||||
*
|
||||
*/
|
||||
#ifndef _CHAN_ALLOC_H
|
||||
#define _CHAN_ALLOC_H
|
||||
|
||||
#include "gsm_subscriber.h"
|
||||
|
||||
/* Special allocator for C0 of BTS */
|
||||
struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan);
|
||||
|
||||
/* Regular physical channel allocator */
|
||||
struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan);
|
||||
|
||||
/* Regular physical channel (TS) */
|
||||
void ts_free(struct gsm_bts_trx_ts *ts);
|
||||
|
||||
/* Find an allocated channel */
|
||||
struct gsm_lchan *lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr);
|
||||
|
||||
/* Allocate a logical channel (SDCCH, TCH, ...) */
|
||||
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type);
|
||||
|
||||
/* Free a logical channel (SDCCH, TCH, ...) */
|
||||
void lchan_free(struct gsm_lchan *lchan);
|
||||
|
||||
/* Consider releasing the channel */
|
||||
int lchan_auto_release(struct gsm_lchan *lchan);
|
||||
|
||||
#endif /* _CHAN_ALLOC_H */
|
|
@ -0,0 +1,44 @@
|
|||
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
|
||||
* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DB_H
|
||||
#define _DB_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
|
||||
/* one time initialisation */
|
||||
int db_init(const char *name);
|
||||
int db_prepare();
|
||||
int db_fini();
|
||||
|
||||
/* subscriber management */
|
||||
struct gsm_subscriber* db_create_subscriber(char *imsi);
|
||||
struct gsm_subscriber* db_get_subscriber(enum gsm_subscriber_field field, const char *subscr);
|
||||
int db_sync_subscriber(struct gsm_subscriber* subscriber);
|
||||
int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber);
|
||||
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei);
|
||||
|
||||
/* SMS store-and-forward */
|
||||
int db_sms_store(struct gsm_sms *sms);
|
||||
struct gsm_sms *db_sms_get_unsent(int min_id);
|
||||
int db_sms_mark_sent(struct gsm_sms *sms);
|
||||
#endif /* _DB_H */
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef _DEBUG_H
|
||||
#define _DEBUG_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 DMI 0x1000
|
||||
#define DMIB 0x2000
|
||||
#define DMUX 0x4000
|
||||
#define DINP 0x8000
|
||||
|
||||
#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 DEBUGPC(ss, fmt, args...)
|
||||
#endif
|
||||
|
||||
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
|
||||
|
||||
char *hexdump(unsigned char *buf, int len);
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...);
|
||||
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;
|
||||
|
||||
#endif /* _DEBUG_H */
|
|
@ -0,0 +1,159 @@
|
|||
#ifndef _E1_INPUT_H
|
||||
#define _E1_INPUT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/subchan_demux.h>
|
||||
|
||||
#define NUM_E1_TS 32
|
||||
|
||||
enum e1inp_sign_type {
|
||||
E1INP_SIGN_NONE,
|
||||
E1INP_SIGN_OML,
|
||||
E1INP_SIGN_RSL,
|
||||
};
|
||||
const char *e1inp_signtype_name(enum e1inp_sign_type tp);
|
||||
|
||||
struct e1inp_ts;
|
||||
|
||||
struct e1inp_sign_link {
|
||||
/* list of signalling links */
|
||||
struct llist_head list;
|
||||
|
||||
/* to which timeslot do we belong? */
|
||||
struct e1inp_ts *ts;
|
||||
|
||||
enum e1inp_sign_type type;
|
||||
|
||||
/* trx for msg->trx of received msgs */
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
/* msgb queue of to-be-transmitted msgs */
|
||||
struct llist_head tx_list;
|
||||
|
||||
/* SAPI and TEI on the E1 TS */
|
||||
u_int8_t sapi;
|
||||
u_int8_t tei;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u_int8_t channel;
|
||||
} misdn;
|
||||
} driver;
|
||||
};
|
||||
|
||||
enum e1inp_ts_type {
|
||||
E1INP_TS_TYPE_NONE,
|
||||
E1INP_TS_TYPE_SIGN,
|
||||
E1INP_TS_TYPE_TRAU,
|
||||
};
|
||||
const char *e1inp_tstype_name(enum e1inp_ts_type tp);
|
||||
|
||||
/* A timeslot in the E1 interface */
|
||||
struct e1inp_ts {
|
||||
enum e1inp_ts_type type;
|
||||
int num;
|
||||
|
||||
/* to which line do we belong ? */
|
||||
struct e1inp_line *line;
|
||||
|
||||
union {
|
||||
struct {
|
||||
/* list of all signalling links on this TS */
|
||||
struct llist_head sign_links;
|
||||
/* timer when to dequeue next frame */
|
||||
struct timer_list tx_timer;
|
||||
} sign;
|
||||
struct {
|
||||
/* subchannel demuxer for frames from E1 */
|
||||
struct subch_demux demux;
|
||||
/* subchannel muxer for frames to E1 */
|
||||
struct subch_mux mux;
|
||||
} trau;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
/* mISDN driver has one fd for each ts */
|
||||
struct bsc_fd fd;
|
||||
} misdn;
|
||||
struct {
|
||||
/* ip.access driver has one fd for each ts */
|
||||
struct bsc_fd fd;
|
||||
} ipaccess;
|
||||
|
||||
} driver;
|
||||
};
|
||||
|
||||
struct e1inp_driver {
|
||||
struct llist_head list;
|
||||
const char *name;
|
||||
int (*want_write)(struct e1inp_ts *ts);
|
||||
};
|
||||
|
||||
struct e1inp_line {
|
||||
struct llist_head list;
|
||||
unsigned int num;
|
||||
const char *name;
|
||||
|
||||
/* array of timestlots */
|
||||
struct e1inp_ts ts[NUM_E1_TS];
|
||||
|
||||
struct e1inp_driver *driver;
|
||||
void *driver_data;
|
||||
};
|
||||
|
||||
/* register a driver with the E1 core */
|
||||
int e1inp_driver_register(struct e1inp_driver *drv);
|
||||
|
||||
/* register a line with the E1 core */
|
||||
int e1inp_line_register(struct e1inp_line *line);
|
||||
|
||||
/* find a sign_link for given TEI and SAPI in a TS */
|
||||
struct e1inp_sign_link *
|
||||
e1inp_lookup_sign_link(struct e1inp_ts *ts, u_int8_t tei,
|
||||
u_int8_t sapi);
|
||||
|
||||
/* create a new signalling link in a E1 timeslot */
|
||||
struct e1inp_sign_link *
|
||||
e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
|
||||
struct gsm_bts_trx *trx, u_int8_t tei,
|
||||
u_int8_t sapi);
|
||||
|
||||
/* configure and initialize one e1inp_ts */
|
||||
int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
enum e1inp_ts_type type);
|
||||
|
||||
/* Call from the Stack: configuration of this TS has changed */
|
||||
int e1inp_update_ts(struct e1inp_ts *ts);
|
||||
|
||||
/* Receive a packet from the E1 driver */
|
||||
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
u_int8_t tei, u_int8_t sapi);
|
||||
|
||||
/* called by driver if it wants to transmit on a given TS */
|
||||
struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
|
||||
struct e1inp_sign_link **sign_link);
|
||||
|
||||
/* called by driver in case some kind of link state event */
|
||||
int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi);
|
||||
|
||||
/* Write LAPD frames to the fd. */
|
||||
void e1_set_pcap_fd(int fd);
|
||||
|
||||
/* called by TRAU muxer to obtain the destination mux entity */
|
||||
struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr);
|
||||
|
||||
/* e1_config.c */
|
||||
int e1_config(struct gsm_bts *bts, int cardnr, int release_l2);
|
||||
int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin);
|
||||
int ipaccess_setup(struct gsm_network *gsmnet);
|
||||
|
||||
extern struct llist_head e1inp_driver_list;
|
||||
extern struct llist_head e1inp_line_list;
|
||||
|
||||
#endif /* _E1_INPUT_H */
|
|
@ -0,0 +1,568 @@
|
|||
#ifndef _GSM_04_08_H
|
||||
#define _GSM_04_08_H
|
||||
|
||||
/* GSM TS 04.08 definitions */
|
||||
struct gsm_lchan;
|
||||
|
||||
struct gsm48_classmark1 {
|
||||
u_int8_t spare:1,
|
||||
rev_level:2,
|
||||
es_ind:1,
|
||||
a5_1:1,
|
||||
pwr_lev:3;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 10.5.2.5 */
|
||||
struct gsm48_chan_desc {
|
||||
u_int8_t chan_nr;
|
||||
union {
|
||||
struct {
|
||||
u_int8_t maio_high:4,
|
||||
h:1,
|
||||
tsc:3;
|
||||
u_int8_t hsn:6,
|
||||
maio_low:2;
|
||||
} h1;
|
||||
struct {
|
||||
u_int8_t arfcn_high:2,
|
||||
spare:2,
|
||||
h:1,
|
||||
tsc:3;
|
||||
u_int8_t arfcn_low;
|
||||
} h0;
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 10.5.2.30 */
|
||||
struct gsm48_req_ref {
|
||||
u_int8_t ra;
|
||||
u_int8_t t3_high:3,
|
||||
t1_:5;
|
||||
u_int8_t t2:5,
|
||||
t3_low:3;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 9.1.5 */
|
||||
struct gsm48_chan_mode_modify {
|
||||
struct gsm48_chan_desc chan_desc;
|
||||
u_int8_t mode;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define GSM48_CMODE_SIGN 0x00
|
||||
#define GSM48_CMODE_SPEECH_V1 0x01
|
||||
#define GSM48_CMODE_SPEECH_EFR 0x21
|
||||
#define GSM48_CMODE_SPEECH_AMR 0x41
|
||||
#define GSM48_CMODE_DATA_14k5 0x0f
|
||||
#define GSM48_CMODE_DATA_12k0 0x03
|
||||
#define GSM48_CMODE_DATA_6k0 0x0b
|
||||
#define GSM48_CMODE_DATA_3k6 0x23
|
||||
|
||||
/* Chapter 9.1.18 */
|
||||
struct gsm48_imm_ass {
|
||||
u_int8_t l2_plen;
|
||||
u_int8_t proto_discr;
|
||||
u_int8_t msg_type;
|
||||
u_int8_t page_mode;
|
||||
struct gsm48_chan_desc chan_desc;
|
||||
struct gsm48_req_ref req_ref;
|
||||
u_int8_t timing_advance;
|
||||
u_int8_t mob_alloc_len;
|
||||
u_int8_t mob_alloc[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Chapter 10.5.1.3 */
|
||||
struct gsm48_loc_area_id {
|
||||
u_int8_t digits[3]; /* BCD! */
|
||||
u_int16_t lac;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.2.15 */
|
||||
struct gsm48_loc_upd_req {
|
||||
u_int8_t type:4,
|
||||
key_seq:4;
|
||||
struct gsm48_loc_area_id lai;
|
||||
struct gsm48_classmark1 classmark1;
|
||||
u_int8_t mi_len;
|
||||
u_int8_t mi[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 10.1 */
|
||||
struct gsm48_hdr {
|
||||
u_int8_t proto_discr;
|
||||
u_int8_t msg_type;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.3x System information Type header */
|
||||
struct gsm48_system_information_type_header {
|
||||
u_int8_t l2_plen;
|
||||
u_int8_t rr_protocol_discriminator :4,
|
||||
skip_indicator:4;
|
||||
u_int8_t system_information;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct gsm48_rach_control {
|
||||
u_int8_t re :1,
|
||||
cell_bar :1,
|
||||
tx_integer :4,
|
||||
max_trans :2;
|
||||
u_int8_t t2;
|
||||
u_int8_t t3;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
|
||||
struct gsm48_control_channel_descr {
|
||||
u_int8_t ccch_conf :3,
|
||||
bs_ag_blks_res :3,
|
||||
att :1,
|
||||
spare1 :1;
|
||||
u_int8_t bs_pa_mfrms : 3,
|
||||
spare2 :5;
|
||||
u_int8_t t3212;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.2.9 CM service request */
|
||||
struct gsm48_service_request {
|
||||
u_int8_t cm_service_type : 4,
|
||||
cipher_key_seq : 4;
|
||||
/* length + 3 bytes */
|
||||
u_int32_t classmark;
|
||||
u_int8_t mi_len;
|
||||
u_int8_t mi[0];
|
||||
/* optional priority level */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.31 System information Type 1 */
|
||||
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;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.32 System information Type 2 */
|
||||
struct gsm48_system_information_type_2 {
|
||||
struct gsm48_system_information_type_header header;
|
||||
u_int8_t bcch_frequency_list[16];
|
||||
u_int8_t ncc_permitted;
|
||||
struct gsm48_rach_control rach_control;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.35 System information Type 3 */
|
||||
struct gsm48_system_information_type_3 {
|
||||
struct gsm48_system_information_type_header header;
|
||||
u_int16_t cell_identity;
|
||||
struct gsm48_loc_area_id lai;
|
||||
struct gsm48_control_channel_descr control_channel_desc;
|
||||
u_int8_t cell_options;
|
||||
u_int8_t cell_selection[2];
|
||||
struct gsm48_rach_control rach_control;
|
||||
u_int8_t s3_reset_octets[4];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.36 System information Type 4 */
|
||||
struct gsm48_system_information_type_4 {
|
||||
struct gsm48_system_information_type_header header;
|
||||
struct gsm48_loc_area_id lai;
|
||||
u_int8_t cell_selection[2];
|
||||
struct gsm48_rach_control rach_control;
|
||||
/* optional CBCH conditional CBCH... followed by
|
||||
mandantory SI 4 Reset Octets
|
||||
*/
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.37 System information Type 5 */
|
||||
struct gsm48_system_information_type_5 {
|
||||
u_int8_t rr_protocol_discriminator :4,
|
||||
skip_indicator:4;
|
||||
u_int8_t system_information;
|
||||
u_int8_t bcch_frequency_list[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.1.40 System information Type 6 */
|
||||
struct gsm48_system_information_type_6 {
|
||||
u_int8_t rr_protocol_discriminator :4,
|
||||
skip_indicator:4;
|
||||
u_int8_t system_information;
|
||||
u_int8_t cell_identity[2];
|
||||
struct gsm48_loc_area_id lai;
|
||||
u_int8_t cell_options;
|
||||
u_int8_t ncc_permitted;
|
||||
u_int8_t si_6_reset[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 9.2.12 IMSI Detach Indication */
|
||||
struct gsm48_imsi_detach_ind {
|
||||
struct gsm48_classmark1 classmark1;
|
||||
u_int8_t mi_len;
|
||||
u_int8_t mi[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Section 10.2 + GSM 04.07 12.2.3.1.1 */
|
||||
#define GSM48_PDISC_GROUP_CC 0x00
|
||||
#define GSM48_PDISC_BCAST_CC 0x01
|
||||
#define GSM48_PDISC_PDSS1 0x02
|
||||
#define GSM48_PDISC_CC 0x03
|
||||
#define GSM48_PDISC_PDSS2 0x04
|
||||
#define GSM48_PDISC_MM 0x05
|
||||
#define GSM48_PDISC_RR 0x06
|
||||
#define GSM48_PDISC_MM_GPRS 0x08
|
||||
#define GSM48_PDISC_SMS 0x09
|
||||
#define GSM48_PDISC_SM_GPRS 0x0a
|
||||
#define GSM48_PDISC_NC_SS 0x0b
|
||||
#define GSM48_PDISC_LOC 0x0c
|
||||
#define GSM48_PDISC_MASK 0x0f
|
||||
#define GSM48_PDISC_USSD 0x11
|
||||
|
||||
/* Section 10.4 */
|
||||
#define GSM48_MT_RR_INIT_REQ 0x3c
|
||||
#define GSM48_MT_RR_ADD_ASS 0x3b
|
||||
#define GSM48_MT_RR_IMM_ASS 0x3f
|
||||
#define GSM48_MT_RR_IMM_ASS_EXT 0x39
|
||||
#define GSM48_MT_RR_IMM_ASS_REJ 0x3a
|
||||
|
||||
#define GSM48_MT_RR_CIPH_M_CMD 0x35
|
||||
#define GSM48_MT_RR_CIPH_M_COMPL 0x32
|
||||
|
||||
#define GSM48_MT_RR_CFG_CHG_CMD 0x30
|
||||
#define GSM48_MT_RR_CFG_CHG_ACK 0x31
|
||||
#define GSM48_MT_RR_CFG_CHG_REJ 0x33
|
||||
|
||||
#define GSM48_MT_RR_ASS_CMD 0x2e
|
||||
#define GSM48_MT_RR_ASS_COMPL 0x29
|
||||
#define GSM48_MT_RR_ASS_FAIL 0x2f
|
||||
#define GSM48_MT_RR_HANDO_CMD 0x2b
|
||||
#define GSM48_MT_RR_HANDO_COMPL 0x2c
|
||||
#define GSM48_MT_RR_HANDO_FAIL 0x28
|
||||
#define GSM48_MT_RR_HANDO_INFO 0x2d
|
||||
|
||||
#define GSM48_MT_RR_CELL_CHG_ORDER 0x08
|
||||
#define GSM48_MT_RR_PDCH_ASS_CMD 0x23
|
||||
|
||||
#define GSM48_MT_RR_CHAN_REL 0x0d
|
||||
#define GSM48_MT_RR_PART_REL 0x0a
|
||||
#define GSM48_MT_RR_PART_REL_COMP 0x0f
|
||||
|
||||
#define GSM48_MT_RR_PAG_REQ_1 0x21
|
||||
#define GSM48_MT_RR_PAG_REQ_2 0x22
|
||||
#define GSM48_MT_RR_PAG_REQ_3 0x24
|
||||
#define GSM48_MT_RR_PAG_RESP 0x27
|
||||
#define GSM48_MT_RR_NOTIF_NCH 0x20
|
||||
#define GSM48_MT_RR_NOTIF_FACCH 0x25
|
||||
#define GSM48_MT_RR_NOTIF_RESP 0x26
|
||||
|
||||
#define GSM48_MT_RR_SYSINFO_8 0x18
|
||||
#define GSM48_MT_RR_SYSINFO_1 0x19
|
||||
#define GSM48_MT_RR_SYSINFO_2 0x1a
|
||||
#define GSM48_MT_RR_SYSINFO_3 0x1b
|
||||
#define GSM48_MT_RR_SYSINFO_4 0x1c
|
||||
#define GSM48_MT_RR_SYSINFO_5 0x1d
|
||||
#define GSM48_MT_RR_SYSINFO_6 0x1e
|
||||
#define GSM48_MT_RR_SYSINFO_7 0x1f
|
||||
|
||||
#define GSM48_MT_RR_SYSINFO_2bis 0x02
|
||||
#define GSM48_MT_RR_SYSINFO_2ter 0x03
|
||||
#define GSM48_MT_RR_SYSINFO_5bis 0x05
|
||||
#define GSM48_MT_RR_SYSINFO_5ter 0x06
|
||||
#define GSM48_MT_RR_SYSINFO_9 0x04
|
||||
#define GSM48_MT_RR_SYSINFO_13 0x00
|
||||
|
||||
#define GSM48_MT_RR_SYSINFO_16 0x3d
|
||||
#define GSM48_MT_RR_SYSINFO_17 0x3e
|
||||
|
||||
#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10
|
||||
#define GSM48_MT_RR_STATUS 0x12
|
||||
#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17
|
||||
#define GSM48_MT_RR_FREQ_REDEF 0x14
|
||||
#define GSM48_MT_RR_MEAS_REP 0x15
|
||||
#define GSM48_MT_RR_CLSM_CHG 0x16
|
||||
#define GSM48_MT_RR_CLSM_ENQ 0x13
|
||||
#define GSM48_MT_RR_EXT_MEAS_REP 0x36
|
||||
#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37
|
||||
#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34
|
||||
|
||||
#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08
|
||||
#define GSM48_MT_RR_UPLINK_RELEASE 0x0e
|
||||
#define GSM48_MT_RR_UPLINK_FREE 0x0c
|
||||
#define GSM48_MT_RR_UPLINK_BUSY 0x2a
|
||||
#define GSM48_MT_RR_TALKER_IND 0x11
|
||||
|
||||
#define GSM48_MT_RR_APP_INFO 0x38
|
||||
|
||||
/* Table 10.2/3GPP TS 04.08 */
|
||||
#define GSM48_MT_MM_IMSI_DETACH_IND 0x01
|
||||
#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
|
||||
#define GSM48_MT_MM_LOC_UPD_REJECT 0x04
|
||||
#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08
|
||||
|
||||
#define GSM48_MT_MM_AUTH_REJ 0x11
|
||||
#define GSM48_MT_MM_AUTH_REQ 0x12
|
||||
#define GSM48_MT_MM_AUTH_RESP 0x14
|
||||
#define GSM48_MT_MM_ID_REQ 0x18
|
||||
#define GSM48_MT_MM_ID_RESP 0x19
|
||||
#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a
|
||||
#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b
|
||||
|
||||
#define GSM48_MT_MM_CM_SERV_ACC 0x21
|
||||
#define GSM48_MT_MM_CM_SERV_REJ 0x22
|
||||
#define GSM48_MT_MM_CM_SERV_ABORT 0x23
|
||||
#define GSM48_MT_MM_CM_SERV_REQ 0x24
|
||||
#define GSM48_MT_MM_CM_SERV_PROMPT 0x25
|
||||
#define GSM48_MT_MM_CM_REEST_REQ 0x28
|
||||
#define GSM48_MT_MM_ABORT 0x29
|
||||
|
||||
#define GSM48_MT_MM_NULL 0x30
|
||||
#define GSM48_MT_MM_STATUS 0x31
|
||||
#define GSM48_MT_MM_INFO 0x32
|
||||
|
||||
/* Table 10.3/3GPP TS 04.08 */
|
||||
#define GSM48_MT_CC_ALERTING 0x01
|
||||
#define GSM48_MT_CC_CALL_CONF 0x08
|
||||
#define GSM48_MT_CC_CALL_PROC 0x02
|
||||
#define GSM48_MT_CC_CONNECT 0x07
|
||||
#define GSM48_MT_CC_CONNECT_ACK 0x0f
|
||||
#define GSM48_MT_CC_EMERG_SETUP 0x0e
|
||||
#define GSM48_MT_CC_PROGRESS 0x03
|
||||
#define GSM48_MT_CC_ESTAB 0x04
|
||||
#define GSM48_MT_CC_ESTAB_CONF 0x06
|
||||
#define GSM48_MT_CC_RECALL 0x0b
|
||||
#define GSM48_MT_CC_START_CC 0x09
|
||||
#define GSM48_MT_CC_SETUP 0x05
|
||||
|
||||
#define GSM48_MT_CC_MODIFY 0x17
|
||||
#define GSM48_MT_CC_MODIFY_COMPL 0x1f
|
||||
#define GSM48_MT_CC_MODIFY_REJECT 0x13
|
||||
#define GSM48_MT_CC_USER_INFO 0x10
|
||||
#define GSM48_MT_CC_HOLD 0x18
|
||||
#define GSM48_MT_CC_HOLD_ACK 0x19
|
||||
#define GSM48_MT_CC_HOLD_REJ 0x1a
|
||||
#define GSM48_MT_CC_RETR 0x1c
|
||||
#define GSM48_MT_CC_RETR_ACK 0x1d
|
||||
#define GSM48_MT_CC_RETR_REJ 0x1e
|
||||
|
||||
#define GSM48_MT_CC_DISCONNECT 0x25
|
||||
#define GSM48_MT_CC_RELEASE 0x2d
|
||||
#define GSM48_MT_CC_RELEASE_COMPL 0x2a
|
||||
|
||||
#define GSM48_MT_CC_CONG_CTRL 0x39
|
||||
#define GSM48_MT_CC_NOTIFY 0x3e
|
||||
#define GSM48_MT_CC_STATUS 0x3d
|
||||
#define GSM48_MT_CC_STATUS_ENQ 0x34
|
||||
#define GSM48_MT_CC_START_DTMF 0x35
|
||||
#define GSM48_MT_CC_STOP_DTMF 0x31
|
||||
#define GSM48_MT_CC_STOP_DTMF_ACK 0x32
|
||||
#define GSM48_MT_CC_START_DTMF_ACK 0x36
|
||||
#define GSM48_MT_CC_START_DTMF_REJ 0x37
|
||||
#define GSM48_MT_CC_FACILITY 0x3a
|
||||
|
||||
/* FIXME: Table 10.4 / 10.4a (GPRS) */
|
||||
|
||||
/* Section 10.5.2.26, Table 10.5.64 */
|
||||
#define GSM48_PM_MASK 0x03
|
||||
#define GSM48_PM_NORMAL 0x00
|
||||
#define GSM48_PM_EXTENDED 0x01
|
||||
#define GSM48_PM_REORG 0x02
|
||||
#define GSM48_PM_SAME 0x03
|
||||
|
||||
/* Chapter 10.5.3.5 / Table 10.5.93 */
|
||||
#define GSM48_LUPD_NORMAL 0x0
|
||||
#define GSM48_LUPD_PERIODIC 0x1
|
||||
#define GSM48_LUPD_IMSI_ATT 0x2
|
||||
#define GSM48_LUPD_RESERVED 0x3
|
||||
|
||||
/* Table 10.5.4 */
|
||||
#define GSM_MI_TYPE_MASK 0x07
|
||||
#define GSM_MI_TYPE_NONE 0x00
|
||||
#define GSM_MI_TYPE_IMSI 0x01
|
||||
#define GSM_MI_TYPE_IMEI 0x02
|
||||
#define GSM_MI_TYPE_IMEISV 0x03
|
||||
#define GSM_MI_TYPE_TMSI 0x04
|
||||
#define GSM_MI_ODD 0x08
|
||||
|
||||
#define GSM48_IE_MOBILE_ID 0x17
|
||||
#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */
|
||||
#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */
|
||||
#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */
|
||||
#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */
|
||||
#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */
|
||||
|
||||
#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */
|
||||
#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */
|
||||
#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */
|
||||
#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */
|
||||
#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */
|
||||
#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */
|
||||
#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */
|
||||
#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */
|
||||
#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */
|
||||
#define GSM48_IE_CONN_NUM 0x4c /* 10.5.4.13 */
|
||||
#define GSM48_IE_CONN_SUBADDR 0x4d /* 10.5.4.14 */
|
||||
#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */
|
||||
#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */
|
||||
#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */
|
||||
#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */
|
||||
#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */
|
||||
#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */
|
||||
#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */
|
||||
#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */
|
||||
#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */
|
||||
#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */
|
||||
#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */
|
||||
#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */
|
||||
#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */
|
||||
#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */
|
||||
|
||||
/* Section 10.5.4.11 / Table 10.5.122 */
|
||||
#define GSM48_CAUSE_CS_GSM 0x60
|
||||
|
||||
/* Section 9.1.2 / Table 9.3 */
|
||||
#define GSM48_IE_FRQLIST_AFTER 0x05
|
||||
#define GSM48_IE_CELL_CH_DESC 0x62
|
||||
#define GSM48_IE_MSLOT_DESC 0x10
|
||||
#define GSM48_IE_CHANMODE_1 0x63
|
||||
#define GSM48_IE_CHANMODE_2 0x11
|
||||
#define GSM48_IE_CHANMODE_3 0x13
|
||||
#define GSM48_IE_CHANMODE_4 0x14
|
||||
#define GSM48_IE_CHANMODE_5 0x15
|
||||
#define GSM48_IE_CHANMODE_6 0x16
|
||||
#define GSM48_IE_CHANMODE_7 0x17
|
||||
#define GSM48_IE_CHANMODE_8 0x18
|
||||
#define GSM48_IE_CHANDESC_2 0x64
|
||||
/* FIXME */
|
||||
|
||||
/* Section 10.5.4.23 / Table 10.5.130 */
|
||||
enum gsm48_signal_val {
|
||||
GSM48_SIGNAL_DIALTONE = 0x00,
|
||||
GSM48_SIGNAL_RINGBACK = 0x01,
|
||||
GSM48_SIGNAL_INTERCEPT = 0x02,
|
||||
GSM48_SIGNAL_NET_CONG = 0x03,
|
||||
GSM48_SIGNAL_BUSY = 0x04,
|
||||
GSM48_SIGNAL_CONFIRM = 0x05,
|
||||
GSM48_SIGNAL_ANSWER = 0x06,
|
||||
GSM48_SIGNAL_CALL_WAIT = 0x07,
|
||||
GSM48_SIGNAL_OFF_HOOK = 0x08,
|
||||
GSM48_SIGNAL_OFF = 0x3f,
|
||||
GSM48_SIGNAL_ALERT_OFF = 0x4f,
|
||||
};
|
||||
|
||||
enum gsm48_cause_loc {
|
||||
GSM48_CAUSE_LOC_USER = 0x00,
|
||||
GSM48_CAUSE_LOC_PRN_S_LU = 0x01,
|
||||
GSM48_CAUSE_LOC_PUN_S_LU = 0x02,
|
||||
GSM48_CAUSE_LOC_TRANS_NET = 0x03,
|
||||
GSM48_CAUSE_LOC_PUN_S_RU = 0x04,
|
||||
GSM48_CAUSE_LOC_PRN_S_RU = 0x05,
|
||||
/* not defined */
|
||||
GSM48_CAUSE_LOC_INN_NET = 0x07,
|
||||
GSM48_CAUSE_LOC_NET_BEYOND = 0x0a,
|
||||
};
|
||||
|
||||
/* Section 10.5.2.31 RR Cause / Table 10.5.70 */
|
||||
enum gsm48_rr_cause {
|
||||
GSM48_RR_CAUSE_NORMAL = 0x00,
|
||||
GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01,
|
||||
GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02,
|
||||
GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
|
||||
GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
|
||||
GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
|
||||
GSM48_RR_CAUSE_HNDOVER_IMP = 0x06,
|
||||
GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07,
|
||||
GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08,
|
||||
GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
|
||||
GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
|
||||
GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
|
||||
GSM48_RR_CAUSE_MSG_TYPE_N = 0x61,
|
||||
GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62,
|
||||
GSM48_RR_CAUSE_COND_IE_ERROR = 0x64,
|
||||
GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65,
|
||||
GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
|
||||
};
|
||||
|
||||
/* Annex G, GSM specific cause values for mobility management */
|
||||
enum gsm48_reject_value {
|
||||
GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2,
|
||||
GSM48_REJECT_ILLEGAL_MS = 3,
|
||||
GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4,
|
||||
GSM48_REJECT_IMEI_NOT_ACCEPTED = 5,
|
||||
GSM48_REJECT_ILLEGAL_ME = 6,
|
||||
GSM48_REJECT_PLMN_NOT_ALLOWED = 11,
|
||||
GSM48_REJECT_LOC_NOT_ALLOWED = 12,
|
||||
GSM48_REJECT_ROAMING_NOT_ALLOWED = 13,
|
||||
GSM48_REJECT_NETWORK_FAILURE = 17,
|
||||
GSM48_REJECT_CONGESTION = 22,
|
||||
GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32,
|
||||
GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33,
|
||||
GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34,
|
||||
GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38,
|
||||
GSM48_REJECT_INCORRECT_MESSAGE = 95,
|
||||
GSM48_REJECT_INVALID_MANDANTORY_INF = 96,
|
||||
GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97,
|
||||
GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98,
|
||||
GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99,
|
||||
GSM48_REJECT_CONDTIONAL_IE_ERROR = 100,
|
||||
GSM48_REJECT_MSG_NOT_COMPATIBLE = 101,
|
||||
GSM48_REJECT_PROTOCOL_ERROR = 111,
|
||||
|
||||
/* according to G.6 Additional cause codes for GMM */
|
||||
GSM48_REJECT_GPRS_NOT_ALLOWED = 7,
|
||||
GSM48_REJECT_SERVICES_NOT_ALLOWED = 8,
|
||||
GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9,
|
||||
GSM48_REJECT_IMPLICITLY_DETACHED = 10,
|
||||
GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14,
|
||||
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);
|
||||
|
||||
|
||||
struct msgb;
|
||||
struct gsm_bts;
|
||||
struct gsm_subscriber;
|
||||
|
||||
/* 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);
|
||||
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
|
||||
u_int16_t mnc, u_int16_t lac);
|
||||
int gsm48_cc_tx_setup(struct gsm_lchan *lchan, struct gsm_subscriber *calling);
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra);
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra);
|
||||
|
||||
int gsm48_tx_mm_info(struct gsm_lchan *lchan);
|
||||
struct msgb *gsm48_msgb_alloc(void);
|
||||
int gsm48_sendmsg(struct msgb *msg);
|
||||
int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
|
||||
|
||||
int gsm48_send_rr_release(struct gsm_lchan *lchan);
|
||||
|
||||
/* convert a ASCII phone number to call-control BCD */
|
||||
int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
|
||||
u_int8_t type, const char *input);
|
||||
u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,172 @@
|
|||
#ifndef _GSM_04_11_H
|
||||
#define _GSM_04_11_H
|
||||
|
||||
/* GSM TS 04.11 definitions */
|
||||
|
||||
/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */
|
||||
#define GSM411_PDISC_SMS 0x09
|
||||
|
||||
/* Chapter 8.1.3 */
|
||||
#define GSM411_MT_CP_DATA 0x01
|
||||
#define GSM411_MT_CP_ACK 0x04
|
||||
#define GSM411_MT_CP_ERROR 0x10
|
||||
|
||||
enum gsm411_cp_ie {
|
||||
GSM411_CP_IE_USER_DATA = 0x01, /* 8.1.4.1 */
|
||||
GSM411_CP_IE_CAUSE = 0x02, /* 8.1.4.2. */
|
||||
};
|
||||
|
||||
/* Chapter 8.2.2 */
|
||||
#define GSM411_MT_RP_DATA_MO 0x00
|
||||
#define GSM411_MT_RP_DATA_MT 0x01
|
||||
#define GSM411_MT_RP_ACK_MO 0x02
|
||||
#define GSM411_MT_RP_ACK_MT 0x03
|
||||
#define GSM411_MT_RP_ERROR_MO 0x04
|
||||
#define GSM411_MT_RP_ERROR_MT 0x04
|
||||
#define GSM411_MT_RP_SMMA_MO 0x05
|
||||
|
||||
enum gsm411_rp_ie {
|
||||
GSM411_IE_RP_USER_DATA = 0x41, /* 8.2.5.3 */
|
||||
GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */
|
||||
};
|
||||
|
||||
/* Chapter 8.2.1 */
|
||||
struct gsm411_rp_hdr {
|
||||
u_int8_t len;
|
||||
u_int8_t msg_type;
|
||||
u_int8_t msg_ref;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* our own enum, not related to on-air protocol */
|
||||
enum sms_alphabet {
|
||||
DCS_NONE,
|
||||
DCS_7BIT_DEFAULT,
|
||||
DCS_UCS2,
|
||||
DCS_8BIT_DATA,
|
||||
};
|
||||
|
||||
/* SMS submit PDU */
|
||||
struct sms_submit {
|
||||
u_int8_t *smsc;
|
||||
u_int8_t mti:2;
|
||||
u_int8_t vpf:2;
|
||||
u_int8_t msg_ref;
|
||||
u_int8_t pid;
|
||||
u_int8_t dcs;
|
||||
u_int8_t *vp;
|
||||
u_int8_t ud_len;
|
||||
u_int8_t *user_data;
|
||||
|
||||
/* interpreted */
|
||||
u_int8_t mms:1;
|
||||
u_int8_t sri:1;
|
||||
u_int8_t udhi:1;
|
||||
u_int8_t rp:1;
|
||||
enum sms_alphabet alphabet;
|
||||
char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes BCD == 20 bytes string */
|
||||
unsigned long validity_mins;
|
||||
char decoded[256];
|
||||
};
|
||||
|
||||
/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */
|
||||
#define GSM340_SMS_DELIVER_SC2MS 0x00
|
||||
#define GSM340_SMS_DELIVER_REP_MS2SC 0x00
|
||||
#define GSM340_SMS_STATUS_REP_SC2MS 0x02
|
||||
#define GSM340_SMS_COMMAND_MS2SC 0x02
|
||||
#define GSM340_SMS_SUBMIT_MS2SC 0x01
|
||||
#define GSM340_SMS_SUBMIT_REP_SC2MS 0x01
|
||||
#define GSM340_SMS_RESSERVED 0x03
|
||||
|
||||
/* GSM 03.40 / Chapter 9.2.3.2: TP-More-Messages-to-Send */
|
||||
#define GSM340_TP_MMS_MORE 0
|
||||
#define GSM340_TP_MMS_NO_MORE 1
|
||||
|
||||
/* GSM 03.40 / Chapter 9.2.3.3: TP-Validity-Period-Format */
|
||||
#define GSM340_TP_VPF_NONE 0
|
||||
#define GSM340_TP_VPF_RELATIVE 2
|
||||
#define GSM340_TP_VPF_ENHANCED 1
|
||||
#define GSM340_TP_VPF_ABSOLUTE 3
|
||||
|
||||
/* GSM 03.40 / Chapter 9.2.3.4: TP-Status-Report-Indication */
|
||||
#define GSM340_TP_SRI_NONE 0
|
||||
#define GSM340_TP_SRI_PRESENT 1
|
||||
|
||||
/* GSM 03.40 / Chapter 9.2.3.5: TP-Status-Report-Request */
|
||||
#define GSM340_TP_SRR_NONE 0
|
||||
#define GSM340_TP_SRR_REQUESTED 1
|
||||
|
||||
/* GSM 03.40 / Chapter 9.2.3.9: TP-Protocol-Identifier */
|
||||
/* telematic interworking (001 or 111 in bits 7-5) */
|
||||
#define GSM340_TP_PID_IMPLICIT 0x00
|
||||
#define GSM340_TP_PID_TELEX 0x01
|
||||
#define GSM340_TP_PID_FAX_G3 0x02
|
||||
#define GSM340_TP_PID_FAX_G4 0x03
|
||||
#define GSM340_TP_PID_VOICE 0x04
|
||||
#define GSM430_TP_PID_ERMES 0x05
|
||||
#define GSM430_TP_PID_NATIONAL_PAGING 0x06
|
||||
#define GSM430_TP_PID_VIDEOTEX 0x07
|
||||
#define GSM430_TP_PID_TELETEX_UNSPEC 0x08
|
||||
#define GSM430_TP_PID_TELETEX_PSPDN 0x09
|
||||
#define GSM430_TP_PID_TELETEX_CSPDN 0x0a
|
||||
#define GSM430_TP_PID_TELETEX_PSTN 0x0b
|
||||
#define GSM430_TP_PID_TELETEX_ISDN 0x0c
|
||||
#define GSM430_TP_PID_TELETEX_UCI 0x0d
|
||||
#define GSM430_TP_PID_MSG_HANDLING 0x10
|
||||
#define GSM430_TP_PID_MSG_X400 0x11
|
||||
#define GSM430_TP_PID_EMAIL 0x12
|
||||
#define GSM430_TP_PID_GSM_MS 0x1f
|
||||
/* if bit 7 = 0 and bit 6 = 1 */
|
||||
#define GSM430_TP_PID_SMS_TYPE_0 0
|
||||
#define GSM430_TP_PID_SMS_TYPE_1 1
|
||||
#define GSM430_TP_PID_SMS_TYPE_2 2
|
||||
#define GSM430_TP_PID_SMS_TYPE_3 3
|
||||
#define GSM430_TP_PID_SMS_TYPE_4 4
|
||||
#define GSM430_TP_PID_SMS_TYPE_5 5
|
||||
#define GSM430_TP_PID_SMS_TYPE_6 6
|
||||
#define GSM430_TP_PID_SMS_TYPE_7 7
|
||||
#define GSM430_TP_PID_RETURN_CALL_MSG 0x1f
|
||||
#define GSM430_TP_PID_ME_DATA_DNLOAD 0x3d
|
||||
#define GSM430_TP_PID_ME_DE_PERSONAL 0x3e
|
||||
#define GSM430_TP_PID_ME_SIM_DNLOAD 0x3f
|
||||
|
||||
/* GSM 03.38 Chapter 4: SMS Data Coding Scheme */
|
||||
#define GSM338_DCS_00_
|
||||
|
||||
#define GSM338_DCS_1110_7BIT (0 << 2)
|
||||
#define GSM338_DCS_1111_7BIT (0 << 2)
|
||||
#define GSM338_DCS_1111_8BIT_DATA (1 << 2)
|
||||
#define GSM338_DCS_1111_CLASS0 0
|
||||
#define GSM338_DCS_1111_CLASS1_ME 1
|
||||
#define GSM338_DCS_1111_CLASS2_SIM 2
|
||||
#define GSM338_DCS_1111_CLASS3_TE 3 /* See TS 07.05 */
|
||||
|
||||
|
||||
/* SMS deliver PDU */
|
||||
struct sms_deliver {
|
||||
u_int8_t *smsc;
|
||||
u_int8_t mti:2;
|
||||
u_int8_t rd:1;
|
||||
u_int8_t vpf:2;
|
||||
u_int8_t srr:1;
|
||||
u_int8_t udhi:1;
|
||||
u_int8_t rp:1;
|
||||
u_int8_t msg_ref;
|
||||
u_int8_t *orig_addr;
|
||||
u_int8_t pid;
|
||||
u_int8_t dcs;
|
||||
u_int8_t vp;
|
||||
u_int8_t ud_len;
|
||||
u_int8_t *user_data;
|
||||
};
|
||||
|
||||
struct msgb;
|
||||
|
||||
int gsm0411_rcv_sms(struct msgb *msg);
|
||||
|
||||
int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms);
|
||||
|
||||
struct msgb *gsm411_msgb_alloc(void);
|
||||
int gsm0411_sendmsg(struct msgb *msg);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,406 @@
|
|||
#ifndef _GSM_DATA_H
|
||||
#define _GSM_DATA_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/timer.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define GSM_MAX_BTS 8
|
||||
#define BTS_MAX_TRX 8
|
||||
#define TRX_NR_TS 8
|
||||
#define TS_MAX_LCHAN 8
|
||||
|
||||
#define HARDCODED_ARFCN 123
|
||||
#define HARDCODED_TSC 7
|
||||
#define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */
|
||||
|
||||
enum gsm_hooks {
|
||||
GSM_HOOK_NM_SWLOAD,
|
||||
GSM_HOOK_RR_PAGING,
|
||||
};
|
||||
|
||||
enum gsm_paging_event {
|
||||
GSM_PAGING_SUCCEEDED,
|
||||
GSM_PAGING_EXPIRED,
|
||||
};
|
||||
|
||||
struct msgb;
|
||||
typedef int gsm_cbfn(unsigned int hooknum,
|
||||
unsigned int event,
|
||||
struct msgb *msg,
|
||||
void *data, void *param);
|
||||
|
||||
/*
|
||||
* Use the channel. As side effect the lchannel recycle timer
|
||||
* will be started.
|
||||
*/
|
||||
#define LCHAN_RELEASE_TIMEOUT 4, 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", \
|
||||
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", \
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
|
||||
lchan->nr, lchan->use_count); \
|
||||
} while(0);
|
||||
|
||||
|
||||
/* communications link with a BTS */
|
||||
struct gsm_bts_link {
|
||||
struct gsm_bts *bts;
|
||||
};
|
||||
|
||||
enum gsm_call_type {
|
||||
GSM_CT_NONE,
|
||||
GSM_CT_MO,
|
||||
GSM_CT_MT,
|
||||
};
|
||||
|
||||
enum gsm_call_state {
|
||||
GSM_CSTATE_NULL,
|
||||
GSM_CSTATE_INITIATED,
|
||||
GSM_CSTATE_ACTIVE,
|
||||
GSM_CSTATE_RELEASE_REQ,
|
||||
};
|
||||
|
||||
struct gsm_lchan;
|
||||
struct gsm_subscriber;
|
||||
|
||||
/* One end of a call */
|
||||
struct gsm_call {
|
||||
enum gsm_call_type type;
|
||||
enum gsm_call_state state;
|
||||
u_int8_t transaction_id; /* 10.3.2 */
|
||||
|
||||
/* the 'local' channel */
|
||||
struct gsm_lchan *local_lchan;
|
||||
/* the 'remote' channel */
|
||||
struct gsm_lchan *remote_lchan;
|
||||
|
||||
/* the 'remote' subscriber */
|
||||
struct gsm_subscriber *called_subscr;
|
||||
};
|
||||
|
||||
|
||||
enum gsm_phys_chan_config {
|
||||
GSM_PCHAN_NONE,
|
||||
GSM_PCHAN_CCCH,
|
||||
GSM_PCHAN_CCCH_SDCCH4,
|
||||
GSM_PCHAN_TCH_F,
|
||||
GSM_PCHAN_TCH_H,
|
||||
GSM_PCHAN_SDCCH8_SACCH8C,
|
||||
GSM_PCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
enum gsm_chan_t {
|
||||
GSM_LCHAN_NONE,
|
||||
GSM_LCHAN_SDCCH,
|
||||
GSM_LCHAN_TCH_F,
|
||||
GSM_LCHAN_TCH_H,
|
||||
GSM_LCHAN_UNKNOWN,
|
||||
};
|
||||
|
||||
|
||||
/* Channel Request reason */
|
||||
enum gsm_chreq_reason_t {
|
||||
GSM_CHREQ_REASON_EMERG,
|
||||
GSM_CHREQ_REASON_PAG,
|
||||
GSM_CHREQ_REASON_CALL,
|
||||
GSM_CHREQ_REASON_LOCATION_UPD,
|
||||
GSM_CHREQ_REASON_OTHER,
|
||||
};
|
||||
|
||||
/* Network Management State */
|
||||
struct gsm_nm_state {
|
||||
u_int8_t operational;
|
||||
u_int8_t administrative;
|
||||
u_int8_t availability;
|
||||
};
|
||||
struct gsm_attr {
|
||||
u_int8_t len;
|
||||
u_int8_t data[0];
|
||||
};
|
||||
|
||||
/*
|
||||
* LOCATION UPDATING REQUEST state
|
||||
*
|
||||
* Our current operation is:
|
||||
* - Get imei/tmsi
|
||||
* - Accept/Reject according to global policy
|
||||
*/
|
||||
struct gsm_loc_updating_operation {
|
||||
struct timer_list updating_timer;
|
||||
int waiting_for_imsi : 1;
|
||||
int waiting_for_imei : 1;
|
||||
};
|
||||
|
||||
struct gsm_lchan {
|
||||
/* The TS that we're part of */
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
/* The logical subslot number in the TS */
|
||||
u_int8_t nr;
|
||||
/* The logical channel type */
|
||||
enum gsm_chan_t type;
|
||||
/* If TCH, traffic channel mode */
|
||||
enum gsm_chan_t tch_mode;
|
||||
/* Power levels for MS and BTS */
|
||||
u_int8_t bs_power;
|
||||
u_int8_t ms_power;
|
||||
|
||||
/* To whom we are allocated at the moment */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* Timer started to release the channel */
|
||||
struct timer_list release_timer;
|
||||
|
||||
/* local end of a call, if any */
|
||||
struct gsm_call call;
|
||||
|
||||
/* temporary user data, to be removed... and merged into gsm_call */
|
||||
void *user_data;
|
||||
|
||||
/*
|
||||
* Operations that have a state and might be pending
|
||||
*/
|
||||
struct gsm_loc_updating_operation *loc_operation;
|
||||
|
||||
/* use count. how many users use this channel */
|
||||
unsigned int use_count;
|
||||
};
|
||||
|
||||
struct gsm_e1_subslot {
|
||||
/* Number of E1 link */
|
||||
u_int8_t e1_nr;
|
||||
/* Number of E1 TS inside E1 link */
|
||||
u_int8_t e1_ts;
|
||||
/* Sub-slot within the E1 TS, 0xff if full TS */
|
||||
u_int8_t e1_ts_ss;
|
||||
};
|
||||
|
||||
#define BTS_TRX_F_ACTIVATED 0x0001
|
||||
/* One Timeslot in a TRX */
|
||||
struct gsm_bts_trx_ts {
|
||||
struct gsm_bts_trx *trx;
|
||||
/* number of this timeslot at the TRX */
|
||||
u_int8_t nr;
|
||||
|
||||
enum gsm_phys_chan_config pchan;
|
||||
|
||||
unsigned int flags;
|
||||
struct gsm_nm_state nm_state;
|
||||
struct gsm_attr *nm_attr;
|
||||
|
||||
/* 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 attr_fc;
|
||||
u_int16_t attr_f8;
|
||||
} abis_ip;
|
||||
|
||||
struct gsm_lchan lchan[TS_MAX_LCHAN];
|
||||
};
|
||||
|
||||
/* One TRX in a BTS */
|
||||
struct gsm_bts_trx {
|
||||
struct gsm_bts *bts;
|
||||
/* number of this TRX in the BTS */
|
||||
u_int8_t nr;
|
||||
/* how do we talk RSL with this TRX? */
|
||||
struct e1inp_sign_link *rsl_link;
|
||||
struct gsm_nm_state nm_state;
|
||||
struct gsm_attr *nm_attr;
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
} bb_transc;
|
||||
|
||||
u_int16_t arfcn;
|
||||
|
||||
union {
|
||||
struct {
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
} bbsig;
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
} pa;
|
||||
} bs11;
|
||||
};
|
||||
struct gsm_bts_trx_ts ts[TRX_NR_TS];
|
||||
};
|
||||
|
||||
enum gsm_bts_type {
|
||||
GSM_BTS_TYPE_UNKNOWN,
|
||||
GSM_BTS_TYPE_BS11,
|
||||
GSM_BTS_TYPE_NANOBTS_900,
|
||||
GSM_BTS_TYPE_NANOBTS_1800,
|
||||
};
|
||||
|
||||
/**
|
||||
* A pending paging request
|
||||
*/
|
||||
struct gsm_paging_request {
|
||||
/* list_head for list of all paging requests */
|
||||
struct llist_head entry;
|
||||
/* the subscriber which we're paging. Later gsm_paging_request
|
||||
* should probably become a part of the gsm_subscriber struct? */
|
||||
struct gsm_subscriber *subscr;
|
||||
/* back-pointer to the BTS on which we are paging */
|
||||
struct gsm_bts *bts;
|
||||
/* what kind of channel type do we ask the MS to establish */
|
||||
int chan_type;
|
||||
|
||||
/* Timer 3113: how long do we try to page? */
|
||||
struct timer_list T3113;
|
||||
|
||||
/* callback to be called in case paging completes */
|
||||
gsm_cbfn *cbfn;
|
||||
void *cbfn_param;
|
||||
};
|
||||
#define T3113_VALUE 60, 0
|
||||
|
||||
/*
|
||||
* This keeps track of the paging status of one BTS. It
|
||||
* includes a number of pending requests, a back pointer
|
||||
* to the gsm_bts, a timer and some more state.
|
||||
*/
|
||||
struct gsm_bts_paging_state {
|
||||
/* pending requests */
|
||||
struct llist_head pending_requests;
|
||||
struct gsm_paging_request *last_request;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
struct timer_list work_timer;
|
||||
|
||||
/* load */
|
||||
u_int16_t available_slots;
|
||||
};
|
||||
|
||||
struct gsm_envabtse {
|
||||
struct gsm_nm_state nm_state;
|
||||
};
|
||||
|
||||
/* One BTS */
|
||||
struct gsm_bts {
|
||||
struct gsm_network *network;
|
||||
/* number of ths BTS in network */
|
||||
u_int8_t nr;
|
||||
/* location area code of this BTS */
|
||||
u_int8_t location_area_code;
|
||||
/* Training Sequence Code */
|
||||
u_int8_t tsc;
|
||||
/* Base Station Identification Code (BSIC) */
|
||||
u_int8_t bsic;
|
||||
/* type of BTS */
|
||||
enum gsm_bts_type type;
|
||||
/* how do we talk OML with this TRX? */
|
||||
struct e1inp_sign_link *oml_link;
|
||||
|
||||
/* Abis network management O&M handle */
|
||||
struct abis_nm_h *nmh;
|
||||
struct gsm_nm_state nm_state;
|
||||
struct gsm_attr *nm_attr;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* CCCH is on C0 */
|
||||
struct gsm_bts_trx *c0;
|
||||
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
} site_mgr;
|
||||
|
||||
/* ip.accesss Unit ID's have Site/BTS/TRX layout */
|
||||
union {
|
||||
struct {
|
||||
u_int16_t site_id;
|
||||
u_int16_t bts_id;
|
||||
} ip_access;
|
||||
struct {
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
} cclk;
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
} rack;
|
||||
struct gsm_envabtse envabtse[4];
|
||||
} bs11;
|
||||
};
|
||||
|
||||
/* transceivers */
|
||||
int num_trx;
|
||||
struct gsm_bts_trx trx[BTS_MAX_TRX+1];
|
||||
};
|
||||
|
||||
struct gsm_network {
|
||||
/* global parameters */
|
||||
u_int16_t country_code;
|
||||
u_int16_t network_code;
|
||||
char *name_long;
|
||||
char *name_short;
|
||||
|
||||
unsigned int num_bts;
|
||||
/* private lists */
|
||||
struct gsm_bts bts[GSM_MAX_BTS+1];
|
||||
};
|
||||
|
||||
#define SMS_HDR_SIZE 128
|
||||
#define SMS_TEXT_SIZE 256
|
||||
struct gsm_sms {
|
||||
u_int64_t id;
|
||||
struct gsm_subscriber *sender;
|
||||
struct gsm_subscriber *receiver;
|
||||
|
||||
unsigned char header[SMS_HDR_SIZE];
|
||||
char text[SMS_TEXT_SIZE];
|
||||
};
|
||||
|
||||
struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
|
||||
u_int16_t country_code, u_int16_t network_code);
|
||||
|
||||
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
|
||||
const char *gsm_lchan_name(enum gsm_chan_t c);
|
||||
const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
|
||||
char *gsm_ts_name(struct gsm_bts_trx_ts *ts);
|
||||
|
||||
enum gsm_e1_event {
|
||||
EVT_E1_NONE,
|
||||
EVT_E1_TEI_UP,
|
||||
EVT_E1_TEI_DN,
|
||||
};
|
||||
|
||||
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(char *arg);
|
||||
char *btstype2str(enum gsm_bts_type type);
|
||||
struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
|
||||
struct gsm_bts *start_bts);
|
||||
|
||||
static inline int is_ipaccess_bts(struct gsm_bts *bts)
|
||||
{
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_NANOBTS_900:
|
||||
case GSM_BTS_TYPE_NANOBTS_1800:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef _GSM_SUBSCR_H
|
||||
#define _GSM_SUBSCR_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include "gsm_data.h"
|
||||
#include "linuxlist.h"
|
||||
|
||||
#define GSM_IMEI_LENGTH 17
|
||||
#define GSM_IMSI_LENGTH 17
|
||||
#define GSM_TMSI_LENGTH 17
|
||||
#define GSM_NAME_LENGTH 128
|
||||
#define GSM_EXTENSION_LENGTH 128
|
||||
|
||||
struct gsm_subscriber {
|
||||
long long unsigned int id;
|
||||
char imsi[GSM_IMSI_LENGTH];
|
||||
char tmsi[GSM_TMSI_LENGTH];
|
||||
u_int16_t lac;
|
||||
char name[GSM_NAME_LENGTH];
|
||||
char extension[GSM_EXTENSION_LENGTH];
|
||||
int authorized;
|
||||
|
||||
/* for internal management */
|
||||
int use_count;
|
||||
struct llist_head entry;
|
||||
|
||||
/* those are properties of the equipment, but they
|
||||
* are applicable to the subscriber at the moment */
|
||||
struct gsm48_classmark1 classmark1;
|
||||
u_int8_t classmark2_len;
|
||||
u_int8_t classmark2[3];
|
||||
u_int8_t classmark3_len;
|
||||
u_int8_t classmark3[14];
|
||||
};
|
||||
|
||||
enum gsm_subscriber_field {
|
||||
GSM_SUBSCRIBER_IMSI,
|
||||
GSM_SUBSCRIBER_TMSI,
|
||||
GSM_SUBSCRIBER_EXTENSION,
|
||||
};
|
||||
|
||||
enum gsm_subscriber_update_reason {
|
||||
GSM_SUBSCRIBER_UPDATE_ATTACHED,
|
||||
GSM_SUBSCRIBER_UPDATE_DETACHED,
|
||||
};
|
||||
|
||||
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
|
||||
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr);
|
||||
struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi);
|
||||
struct gsm_subscriber *subscr_get_by_imsi(const char *imsi);
|
||||
struct gsm_subscriber *subscr_get_by_extension(const char *ext);
|
||||
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
|
||||
void subscr_put_channel(struct gsm_lchan *lchan);
|
||||
|
||||
/* internal */
|
||||
struct gsm_subscriber *subscr_alloc(void);
|
||||
|
||||
#endif /* _GSM_SUBSCR_H */
|
|
@ -0,0 +1,33 @@
|
|||
/* GSM utility functions, e.g. coding and decoding */
|
||||
/*
|
||||
* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GSM_UTILS_H
|
||||
#define GSM_UTILS_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int gsm_7bit_decode(char *decoded, const u_int8_t *user_data, u_int8_t length);
|
||||
int gsm_7bit_encode(u_int8_t *result, const char *data);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef _IPACCESS_H
|
||||
#define _IPACCESS_H
|
||||
|
||||
struct ipaccess_head {
|
||||
u_int8_t zero;
|
||||
u_int8_t len;
|
||||
u_int8_t proto;
|
||||
u_int8_t data[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum ipaccess_proto {
|
||||
IPAC_PROTO_RSL = 0x00,
|
||||
IPAC_PROTO_IPACCESS = 0xfe,
|
||||
IPAC_PROTO_OML = 0xff,
|
||||
};
|
||||
|
||||
enum ipaccess_msgtype {
|
||||
IPAC_MSGT_PING = 0x00,
|
||||
IPAC_MSGT_PONG = 0x01,
|
||||
IPAC_MSGT_ID_GET = 0x04,
|
||||
IPAC_MSGT_ID_RESP = 0x05,
|
||||
IPAC_MSGT_ID_ACK = 0x06,
|
||||
};
|
||||
|
||||
enum ipaccess_id_tags {
|
||||
IPAC_IDTAG_SERNR = 0x00,
|
||||
IPAC_IDTAG_UNITNAME = 0x01,
|
||||
IPAC_IDTAG_LOCATION1 = 0x02,
|
||||
IPAC_IDTAG_LOCATION2 = 0x03,
|
||||
IPAC_IDTAG_EQUIPVERS = 0x04,
|
||||
IPAC_IDTAG_SWVERSION = 0x05,
|
||||
IPAC_IDTAG_IPADDR = 0x06,
|
||||
IPAC_IDTAG_MACADDR = 0x07,
|
||||
IPAC_IDTAG_UNIT = 0x08,
|
||||
};
|
||||
|
||||
#endif /* _IPACCESS_H */
|
|
@ -0,0 +1,360 @@
|
|||
#ifndef _LINUX_LLIST_H
|
||||
#define _LINUX_LLIST_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef inline
|
||||
#define inline __inline__
|
||||
#endif
|
||||
|
||||
static inline void prefetch(const void *x) {;}
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
*
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type, member) );})
|
||||
|
||||
|
||||
/*
|
||||
* These are non-NULL pointers that will result in page faults
|
||||
* under normal circumstances, used to verify that nobody uses
|
||||
* non-initialized llist entries.
|
||||
*/
|
||||
#define LLIST_POISON1 ((void *) 0x00100100)
|
||||
#define LLIST_POISON2 ((void *) 0x00200200)
|
||||
|
||||
/*
|
||||
* Simple doubly linked llist implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole llists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
struct llist_head {
|
||||
struct llist_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LLIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LLIST_HEAD(name) \
|
||||
struct llist_head name = LLIST_HEAD_INIT(name)
|
||||
|
||||
#define INIT_LLIST_HEAD(ptr) do { \
|
||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal llist manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __llist_add(struct llist_head *_new,
|
||||
struct llist_head *prev,
|
||||
struct llist_head *next)
|
||||
{
|
||||
next->prev = _new;
|
||||
_new->next = next;
|
||||
_new->prev = prev;
|
||||
prev->next = _new;
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: llist head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void llist_add(struct llist_head *_new, struct llist_head *head)
|
||||
{
|
||||
__llist_add(_new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: llist head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
|
||||
{
|
||||
__llist_add(_new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a llist entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal llist manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_del - deletes entry from llist.
|
||||
* @entry: the element to delete from the llist.
|
||||
* Note: llist_empty on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
static inline void llist_del(struct llist_head *entry)
|
||||
{
|
||||
__llist_del(entry->prev, entry->next);
|
||||
entry->next = (struct llist_head *)LLIST_POISON1;
|
||||
entry->prev = (struct llist_head *)LLIST_POISON2;
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_del_init - deletes entry from llist and reinitialize it.
|
||||
* @entry: the element to delete from the llist.
|
||||
*/
|
||||
static inline void llist_del_init(struct llist_head *entry)
|
||||
{
|
||||
__llist_del(entry->prev, entry->next);
|
||||
INIT_LLIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_move - delete from one llist and add as another's head
|
||||
* @llist: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void llist_move(struct llist_head *llist, struct llist_head *head)
|
||||
{
|
||||
__llist_del(llist->prev, llist->next);
|
||||
llist_add(llist, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_move_tail - delete from one llist and add as another's tail
|
||||
* @llist: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void llist_move_tail(struct llist_head *llist,
|
||||
struct llist_head *head)
|
||||
{
|
||||
__llist_del(llist->prev, llist->next);
|
||||
llist_add_tail(llist, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_empty - tests whether a llist is empty
|
||||
* @head: the llist to test.
|
||||
*/
|
||||
static inline int llist_empty(const struct llist_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
static inline void __llist_splice(struct llist_head *llist,
|
||||
struct llist_head *head)
|
||||
{
|
||||
struct llist_head *first = llist->next;
|
||||
struct llist_head *last = llist->prev;
|
||||
struct llist_head *at = head->next;
|
||||
|
||||
first->prev = head;
|
||||
head->next = first;
|
||||
|
||||
last->next = at;
|
||||
at->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_splice - join two llists
|
||||
* @llist: the new llist to add.
|
||||
* @head: the place to add it in the first llist.
|
||||
*/
|
||||
static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
|
||||
{
|
||||
if (!llist_empty(llist))
|
||||
__llist_splice(llist, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_splice_init - join two llists and reinitialise the emptied llist.
|
||||
* @llist: the new llist to add.
|
||||
* @head: the place to add it in the first llist.
|
||||
*
|
||||
* The llist at @llist is reinitialised
|
||||
*/
|
||||
static inline void llist_splice_init(struct llist_head *llist,
|
||||
struct llist_head *head)
|
||||
{
|
||||
if (!llist_empty(llist)) {
|
||||
__llist_splice(llist, head);
|
||||
INIT_LLIST_HEAD(llist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* llist_entry - get the struct for this entry
|
||||
* @ptr: the &struct llist_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the llist_struct within the struct.
|
||||
*/
|
||||
#define llist_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* llist_for_each - iterate over a llist
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
*/
|
||||
#define llist_for_each(pos, head) \
|
||||
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
|
||||
pos = pos->next, prefetch(pos->next))
|
||||
|
||||
/**
|
||||
* __llist_for_each - iterate over a llist
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
*
|
||||
* This variant differs from llist_for_each() in that it's the
|
||||
* simplest possible llist iteration code, no prefetching is done.
|
||||
* Use this for code that knows the llist to be very short (empty
|
||||
* or 1 entry) most of the time.
|
||||
*/
|
||||
#define __llist_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* llist_for_each_prev - iterate over a llist backwards
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
*/
|
||||
#define llist_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
|
||||
pos = pos->prev, prefetch(pos->prev))
|
||||
|
||||
/**
|
||||
* llist_for_each_safe - iterate over a llist safe against removal of llist entry
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @n: another &struct llist_head to use as temporary storage
|
||||
* @head: the head for your llist.
|
||||
*/
|
||||
#define llist_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* llist_for_each_entry - iterate over llist of given type
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
* @member: the name of the llist_struct within the struct.
|
||||
*/
|
||||
#define llist_for_each_entry(pos, head, member) \
|
||||
for (pos = llist_entry((head)->next, typeof(*pos), member), \
|
||||
prefetch(pos->member.next); \
|
||||
&pos->member != (head); \
|
||||
pos = llist_entry(pos->member.next, typeof(*pos), member), \
|
||||
prefetch(pos->member.next))
|
||||
|
||||
/**
|
||||
* llist_for_each_entry_reverse - iterate backwards over llist of given type.
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
* @member: the name of the llist_struct within the struct.
|
||||
*/
|
||||
#define llist_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = llist_entry((head)->prev, typeof(*pos), member), \
|
||||
prefetch(pos->member.prev); \
|
||||
&pos->member != (head); \
|
||||
pos = llist_entry(pos->member.prev, typeof(*pos), member), \
|
||||
prefetch(pos->member.prev))
|
||||
|
||||
/**
|
||||
* llist_for_each_entry_continue - iterate over llist of given type
|
||||
* continuing after existing point
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
* @member: the name of the llist_struct within the struct.
|
||||
*/
|
||||
#define llist_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
|
||||
prefetch(pos->member.next); \
|
||||
&pos->member != (head); \
|
||||
pos = llist_entry(pos->member.next, typeof(*pos), member), \
|
||||
prefetch(pos->member.next))
|
||||
|
||||
/**
|
||||
* llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your llist.
|
||||
* @member: the name of the llist_struct within the struct.
|
||||
*/
|
||||
#define llist_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = llist_entry((head)->next, typeof(*pos), member), \
|
||||
n = llist_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = llist_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* llist_for_each_rcu - iterate over an rcu-protected llist
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
*/
|
||||
#define llist_for_each_rcu(pos, head) \
|
||||
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
|
||||
pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
|
||||
|
||||
#define __llist_for_each_rcu(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); \
|
||||
pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
|
||||
|
||||
/**
|
||||
* llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
|
||||
* against removal of llist entry
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @n: another &struct llist_head to use as temporary storage
|
||||
* @head: the head for your llist.
|
||||
*/
|
||||
#define llist_for_each_safe_rcu(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
|
||||
|
||||
/**
|
||||
* llist_for_each_entry_rcu - iterate over rcu llist of given type
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
* @member: the name of the llist_struct within the struct.
|
||||
*/
|
||||
#define llist_for_each_entry_rcu(pos, head, member) \
|
||||
for (pos = llist_entry((head)->next, typeof(*pos), member), \
|
||||
prefetch(pos->member.next); \
|
||||
&pos->member != (head); \
|
||||
pos = llist_entry(pos->member.next, typeof(*pos), member), \
|
||||
({ smp_read_barrier_depends(); 0;}), \
|
||||
prefetch(pos->member.next))
|
||||
|
||||
|
||||
/**
|
||||
* llist_for_each_continue_rcu - iterate over an rcu-protected llist
|
||||
* continuing after existing point.
|
||||
* @pos: the &struct llist_head to use as a loop counter.
|
||||
* @head: the head for your llist.
|
||||
*/
|
||||
#define llist_for_each_continue_rcu(pos, head) \
|
||||
for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
|
||||
(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,28 @@
|
|||
/* (C) 2008 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MISDN_H
|
||||
#define MISDN_H
|
||||
|
||||
#include "e1_input.h"
|
||||
|
||||
int mi_setup(int cardnr, struct e1inp_line *line, int release_l2);
|
||||
int _abis_nm_sendmsg(struct msgb *msg);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,111 @@
|
|||
#ifndef _MSGB_H
|
||||
#define _MSGB_H
|
||||
|
||||
/* (C) 2008 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 <openbsc/linuxlist.h>
|
||||
|
||||
struct bts_link;
|
||||
|
||||
struct msgb {
|
||||
struct llist_head list;
|
||||
|
||||
/* ptr to the physical E1 link to the BTS(s) */
|
||||
struct gsm_bts_link *bts_link;
|
||||
|
||||
/* Part of which TRX logical channel we were received / transmitted */
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_lchan *lchan;
|
||||
|
||||
unsigned char *l2h;
|
||||
unsigned char *l3h;
|
||||
unsigned char *smsh;
|
||||
|
||||
u_int16_t data_len;
|
||||
u_int16_t len;
|
||||
|
||||
unsigned char *head;
|
||||
unsigned char *tail;
|
||||
unsigned char *data;
|
||||
unsigned char _data[0];
|
||||
};
|
||||
|
||||
extern struct msgb *msgb_alloc(u_int16_t size);
|
||||
extern void msgb_free(struct msgb *m);
|
||||
extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
|
||||
extern struct msgb *msgb_dequeue(struct llist_head *queue);
|
||||
|
||||
#define msgb_l2(m) ((void *)(m->l2h))
|
||||
#define msgb_l3(m) ((void *)(m->l3h))
|
||||
#define msgb_sms(m) ((void *)(m->smsh))
|
||||
|
||||
static inline unsigned int msgb_l2len(const struct msgb *msgb)
|
||||
{
|
||||
return msgb->tail - (u_int8_t *)msgb_l2(msgb);
|
||||
}
|
||||
|
||||
static inline unsigned int msgb_l3len(const struct msgb *msgb)
|
||||
{
|
||||
return msgb->tail - (u_int8_t *)msgb_l3(msgb);
|
||||
}
|
||||
|
||||
static inline unsigned int msgb_headlen(const struct msgb *msgb)
|
||||
{
|
||||
return msgb->len - msgb->data_len;
|
||||
}
|
||||
static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
|
||||
{
|
||||
unsigned char *tmp = msgb->tail;
|
||||
msgb->tail += len;
|
||||
msgb->len += len;
|
||||
return tmp;
|
||||
}
|
||||
static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
|
||||
{
|
||||
msgb->data -= len;
|
||||
msgb->len += len;
|
||||
return msgb->data;
|
||||
}
|
||||
static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
|
||||
{
|
||||
msgb->len -= len;
|
||||
return msgb->data += len;
|
||||
}
|
||||
static inline int msgb_tailroom(const struct msgb *msgb)
|
||||
{
|
||||
return (msgb->data + msgb->data_len) - msgb->tail;
|
||||
}
|
||||
|
||||
/* increase the headroom of an empty msgb, reducing the tailroom */
|
||||
static inline void msgb_reserve(struct msgb *msg, int len)
|
||||
{
|
||||
msg->data += len;
|
||||
msg->tail += len;
|
||||
}
|
||||
|
||||
static inline struct msgb *msgb_alloc_headroom(int size, int headroom)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(size);
|
||||
if (msg)
|
||||
msgb_reserve(msg, headroom);
|
||||
return msg;
|
||||
}
|
||||
|
||||
#endif /* _MSGB_H */
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OPENBSCDEFINES_H
|
||||
#define OPENBSCDEFINES_H
|
||||
|
||||
#ifdef BUILDING_ON_WINDOWS
|
||||
#ifdef BUILDING_OPENBSC
|
||||
#define BSC_API __declspec(dllexport)
|
||||
#else
|
||||
#define BSC_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define BSC_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,46 @@
|
|||
/* Paging helper and manager.... */
|
||||
/* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PAGING_H
|
||||
#define PAGING_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "linuxlist.h"
|
||||
#include "gsm_data.h"
|
||||
#include "gsm_subscriber.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* call once for every gsm_bts... */
|
||||
void paging_init(struct gsm_bts *bts);
|
||||
|
||||
/* schedule paging request */
|
||||
void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
int type, gsm_cbfn *cbfn, void *data);
|
||||
|
||||
/* stop paging requests */
|
||||
void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
struct gsm_lchan *lchan);
|
||||
|
||||
/* update paging load */
|
||||
void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef _RS232_H
|
||||
#define _RS232_H
|
||||
|
||||
int rs232_setup(const char *serial_port, unsigned int delay_ms,
|
||||
struct gsm_bts *bts);
|
||||
|
||||
int handle_serial_msg(struct msgb *msg);
|
||||
|
||||
#endif /* _RS232_H */
|
|
@ -0,0 +1,22 @@
|
|||
#ifndef _BSC_SELECT_H
|
||||
#define _BSC_SELECT_H
|
||||
|
||||
#include <openbsc/linuxlist.h>
|
||||
|
||||
#define BSC_FD_READ 0x0001
|
||||
#define BSC_FD_WRITE 0x0002
|
||||
#define BSC_FD_EXCEPT 0x0004
|
||||
|
||||
struct bsc_fd {
|
||||
struct llist_head list;
|
||||
int fd;
|
||||
unsigned int when;
|
||||
int (*cb)(struct bsc_fd *fd, unsigned int what);
|
||||
void *data;
|
||||
unsigned int priv_nr;
|
||||
};
|
||||
|
||||
int bsc_register_fd(struct bsc_fd *fd);
|
||||
void bsc_unregister_fd(struct bsc_fd *fd);
|
||||
int bsc_select_main(int polling);
|
||||
#endif /* _BSC_SELECT_H */
|
|
@ -0,0 +1,88 @@
|
|||
/* Generic signalling/notification infrastructure */
|
||||
/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef OPENBSC_SIGNAL_H
|
||||
#define OPENBSC_SIGNAL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
|
||||
|
||||
/*
|
||||
* Signalling subsystems
|
||||
*/
|
||||
enum signal_subsystems {
|
||||
SS_PAGING,
|
||||
SS_SMS,
|
||||
SS_ABISIP,
|
||||
SS_NM,
|
||||
SS_LCHAN,
|
||||
};
|
||||
|
||||
/* SS_PAGING signals */
|
||||
enum signal_paging {
|
||||
S_PAGING_COMPLETED,
|
||||
};
|
||||
|
||||
/* SS_ABISIP signals */
|
||||
enum signal_abisip {
|
||||
S_ABISIP_BIND_ACK,
|
||||
};
|
||||
|
||||
/* SS_NM signals */
|
||||
enum signal_nm {
|
||||
S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */
|
||||
S_NM_FAIL_REP, /* GSM 12.21 failure event report */
|
||||
};
|
||||
|
||||
/* SS_LCHAN signals */
|
||||
enum signal_lchan {
|
||||
/*
|
||||
* The lchan got freed with an use_count != 0 and error
|
||||
* recovery needs to be carried out from within the
|
||||
* signal handler.
|
||||
*/
|
||||
S_LCHAN_UNEXPECTED_RELEASE,
|
||||
};
|
||||
|
||||
typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data);
|
||||
|
||||
struct paging_signal_data {
|
||||
struct gsm_subscriber *subscr;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
/* NULL in case the paging didn't work */
|
||||
struct gsm_lchan *lchan;
|
||||
};
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Dispatch */
|
||||
void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef _SUBCH_DEMUX_H
|
||||
#define _SUBCH_DEMUX_H
|
||||
/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
|
||||
|
||||
/* (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/linuxlist.h>
|
||||
|
||||
#define NR_SUBCH 4
|
||||
#define TRAU_FRAME_SIZE 40
|
||||
#define TRAU_FRAME_BITS (TRAU_FRAME_SIZE*8)
|
||||
|
||||
/***********************************************************************/
|
||||
/* DEMULTIPLEXER */
|
||||
/***********************************************************************/
|
||||
|
||||
struct demux_subch {
|
||||
u_int8_t out_bitbuf[TRAU_FRAME_BITS];
|
||||
u_int16_t out_idx; /* next bit to be written in out_bitbuf */
|
||||
/* number of consecutive zeros that we have received (for sync) */
|
||||
unsigned int consecutive_zeros;
|
||||
/* are we in TRAU frame sync or not? */
|
||||
unsigned int in_sync;
|
||||
};
|
||||
|
||||
struct subch_demux {
|
||||
/* bitmask of currently active subchannels */
|
||||
u_int8_t chan_activ;
|
||||
/* one demux_subch struct for every subchannel */
|
||||
struct demux_subch subch[NR_SUBCH];
|
||||
/* callback to be called once we have received a complete
|
||||
* frame on a given subchannel */
|
||||
int (*out_cb)(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
|
||||
void *);
|
||||
/* user-provided data, transparently passed to out_cb() */
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* initialize one demultiplexer instance */
|
||||
int subch_demux_init(struct subch_demux *dmx);
|
||||
|
||||
/* feed 'len' number of muxed bytes into the demultiplexer */
|
||||
int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len);
|
||||
|
||||
/* activate decoding/processing for one subchannel */
|
||||
int subch_demux_activate(struct subch_demux *dmx, int subch);
|
||||
|
||||
/* deactivate decoding/processing for one subchannel */
|
||||
int subch_demux_deactivate(struct subch_demux *dmx, int subch);
|
||||
|
||||
/***********************************************************************/
|
||||
/* MULTIPLEXER */
|
||||
/***********************************************************************/
|
||||
|
||||
/* one element in the tx_queue of a muxer sub-channel */
|
||||
struct subch_txq_entry {
|
||||
struct llist_head list;
|
||||
|
||||
unsigned int bit_len; /* total number of bits in 'bits' */
|
||||
unsigned int next_bit; /* next bit to be transmitted */
|
||||
|
||||
u_int8_t bits[0]; /* one bit per byte */
|
||||
};
|
||||
|
||||
struct mux_subch {
|
||||
struct llist_head tx_queue;
|
||||
};
|
||||
|
||||
/* structure representing one instance of the subchannel muxer */
|
||||
struct subch_mux {
|
||||
struct mux_subch subch[NR_SUBCH];
|
||||
};
|
||||
|
||||
/* initialize a subchannel muxer instance */
|
||||
int subchan_mux_init(struct subch_mux *mx);
|
||||
|
||||
/* request the output of 'len' multiplexed bytes */
|
||||
int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len);
|
||||
|
||||
/* enqueue some data into one sub-channel of the muxer */
|
||||
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
|
||||
int len);
|
||||
|
||||
#endif /* _SUBCH_DEMUX_H */
|
|
@ -0,0 +1,52 @@
|
|||
/* minimalistic telnet/network interface it might turn into a wire interface */
|
||||
/* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TELNET_INTERFACE_H
|
||||
#define TELNET_INTERFACE_H
|
||||
|
||||
#include "gsm_data.h"
|
||||
#include "linuxlist.h"
|
||||
#include "select.h"
|
||||
|
||||
#include <vty/vty.h>
|
||||
|
||||
#define TELNET_COMMAND_48 1
|
||||
#define TELNET_COMMAND_11 2
|
||||
|
||||
struct telnet_connection {
|
||||
struct llist_head entry;
|
||||
struct gsm_network *network;
|
||||
struct bsc_fd fd;
|
||||
struct vty *vty;
|
||||
|
||||
int bts;
|
||||
|
||||
int command;
|
||||
char *imsi;
|
||||
char commands[1024];
|
||||
int read;
|
||||
};
|
||||
|
||||
|
||||
void telnet_init(struct gsm_network *network, int port);
|
||||
|
||||
int bsc_vty_init(struct gsm_network *net);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* (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
|
||||
* 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 TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "linuxlist.h"
|
||||
|
||||
/**
|
||||
* Timer management:
|
||||
* - Create a struct timer_list
|
||||
* - Fill out timeout and use add_timer or
|
||||
* use schedule_timer to schedule a timer in
|
||||
* x seconds and microseconds from now...
|
||||
* - Use del_timer to remove the timer
|
||||
*
|
||||
* Internally:
|
||||
* - We hook into select.c to give a timeval of the
|
||||
* nearest timer. On already passed timers we give
|
||||
* it a 0 to immediately fire after the select
|
||||
* - update_timers will call the callbacks and remove
|
||||
* the timers.
|
||||
*
|
||||
*/
|
||||
struct timer_list {
|
||||
struct llist_head entry;
|
||||
struct timeval timeout;
|
||||
int active : 1;
|
||||
int handled : 1;
|
||||
int in_list : 1;
|
||||
|
||||
void (*cb)(void*);
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* timer management
|
||||
*/
|
||||
void bsc_add_timer(struct timer_list *timer);
|
||||
void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds);
|
||||
void bsc_del_timer(struct timer_list *timer);
|
||||
int bsc_timer_pending(struct timer_list *timer);
|
||||
|
||||
|
||||
/**
|
||||
* internal timer list management
|
||||
*/
|
||||
struct timeval *bsc_nearest_timer();
|
||||
void bsc_prepare_timers();
|
||||
int bsc_update_timers();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,171 @@
|
|||
#ifndef _TLV_H
|
||||
#define _TLV_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openbsc/msgb.h>
|
||||
|
||||
#define LV_GROSS_LEN(x) (x+1)
|
||||
#define TLV_GROSS_LEN(x) (x+2)
|
||||
#define TLV16_GROSS_LEN(x) ((2*x)+2)
|
||||
#define TL16V_GROSS_LEN(x) (x+3)
|
||||
|
||||
/* TLV generation */
|
||||
|
||||
static inline u_int8_t *lv_put(u_int8_t *buf, u_int8_t len,
|
||||
const u_int8_t *val)
|
||||
{
|
||||
*buf++ = len;
|
||||
memcpy(buf, val, len);
|
||||
return buf + len;
|
||||
}
|
||||
|
||||
static inline u_int8_t *tlv_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
|
||||
const u_int8_t *val)
|
||||
{
|
||||
*buf++ = tag;
|
||||
*buf++ = len;
|
||||
memcpy(buf, val, len);
|
||||
return buf + len;
|
||||
}
|
||||
|
||||
static inline u_int8_t *tlv16_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
|
||||
const u_int16_t *val)
|
||||
{
|
||||
*buf++ = tag;
|
||||
*buf++ = len;
|
||||
memcpy(buf, val, len*2);
|
||||
return buf + len*2;
|
||||
}
|
||||
|
||||
static inline u_int8_t *tl16v_put(u_int8_t *buf, u_int8_t tag, u_int16_t len,
|
||||
const u_int8_t *val)
|
||||
{
|
||||
*buf++ = tag;
|
||||
*buf++ = len >> 8;
|
||||
*buf++ = len & 0xff;
|
||||
memcpy(buf, val, len);
|
||||
return buf + len*2;
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tlv16_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int16_t *val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
|
||||
return tlv16_put(buf, tag, len, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tl16v_put(struct msgb *msg, u_int8_t tag, u_int16_t len,
|
||||
const u_int8_t *val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));
|
||||
return tl16v_put(buf, tag, len, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *v_put(u_int8_t *buf, u_int8_t val)
|
||||
{
|
||||
*buf++ = val;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag,
|
||||
u_int8_t val)
|
||||
{
|
||||
*buf++ = tag;
|
||||
*buf++ = val;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag,
|
||||
u_int16_t val)
|
||||
{
|
||||
*buf++ = tag;
|
||||
*buf++ = val >> 8;
|
||||
*buf++ = val & 0xff;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_lv_put(struct msgb *msg, u_int8_t len, const u_int8_t *val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));
|
||||
return lv_put(buf, len, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tlv_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
|
||||
return tlv_put(buf, tag, len, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tv_put(struct msgb *msg, u_int8_t tag, u_int8_t val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, 2);
|
||||
return tv_put(buf, tag, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_v_put(struct msgb *msg, u_int8_t val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, 1);
|
||||
return v_put(buf, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tv16_put(struct msgb *msg, u_int8_t tag, u_int16_t val)
|
||||
{
|
||||
u_int8_t *buf = msgb_put(msg, 3);
|
||||
return tv16_put(buf, tag, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tlv_push(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
|
||||
{
|
||||
u_int8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
|
||||
return tlv_put(buf, tag, len, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tv_push(struct msgb *msg, u_int8_t tag, u_int8_t val)
|
||||
{
|
||||
u_int8_t *buf = msgb_push(msg, 2);
|
||||
return tv_put(buf, tag, val);
|
||||
}
|
||||
|
||||
static inline u_int8_t *msgb_tv16_push(struct msgb *msg, u_int8_t tag, u_int16_t val)
|
||||
{
|
||||
u_int8_t *buf = msgb_push(msg, 3);
|
||||
return tv16_put(buf, tag, val);
|
||||
}
|
||||
|
||||
/* TLV parsing */
|
||||
|
||||
struct tlv_p_entry {
|
||||
u_int16_t len;
|
||||
const u_int8_t *val;
|
||||
};
|
||||
|
||||
enum tlv_type {
|
||||
TLV_TYPE_FIXED,
|
||||
TLV_TYPE_T,
|
||||
TLV_TYPE_TV,
|
||||
TLV_TYPE_TLV,
|
||||
TLV_TYPE_TL16V,
|
||||
};
|
||||
|
||||
struct tlv_def {
|
||||
enum tlv_type type;
|
||||
u_int8_t fixed_len;
|
||||
};
|
||||
|
||||
struct tlv_definition {
|
||||
struct tlv_def def[0xff];
|
||||
};
|
||||
|
||||
struct tlv_parsed {
|
||||
struct tlv_p_entry lv[0xff];
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
|
||||
#define TLVP_LEN(x, y) (x)->lv[y].len
|
||||
#define TLVP_VAL(x, y) (x)->lv[y].val
|
||||
|
||||
#endif /* _TLV_H */
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef _TRAU_FRAME_H
|
||||
#define _TRAU_FRAME_H
|
||||
/* TRAU frame handling according to GSM TS 08.60 */
|
||||
|
||||
/* (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>
|
||||
|
||||
/* 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */
|
||||
#define MAX_C_BITS 25
|
||||
/* 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */
|
||||
#define MAX_D_BITS 288
|
||||
/* for all speech frames */
|
||||
#define MAX_T_BITS 4
|
||||
/* for OM */
|
||||
#define MAX_S_BITS 6
|
||||
/* for E-data */
|
||||
#define MAX_M_BITS 2
|
||||
|
||||
struct decoded_trau_frame {
|
||||
u_int8_t c_bits[MAX_C_BITS];
|
||||
u_int8_t d_bits[MAX_D_BITS];
|
||||
u_int8_t t_bits[MAX_T_BITS];
|
||||
u_int8_t s_bits[MAX_S_BITS];
|
||||
u_int8_t m_bits[MAX_M_BITS];
|
||||
};
|
||||
|
||||
#define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */
|
||||
#define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */
|
||||
#define TRAU_FT_OM_UP 0x07 /* 0 0 1 0 1 - 3.5.2 */
|
||||
#define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */
|
||||
#define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */
|
||||
#define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */
|
||||
#define TRAU_FT_D145_SYNC 0x14 /* 1 0 1 0 0 - 3.5.3 */
|
||||
#define TRAU_FT_EDATA 0x1f /* 1 1 1 1 1 - 3.5.4 */
|
||||
#define TRAU_FT_IDLE_UP 0x10 /* 1 0 0 0 0 - 3.5.5 */
|
||||
#define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */
|
||||
|
||||
|
||||
int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits);
|
||||
int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr);
|
||||
int trau_frame_up2down(struct decoded_trau_frame *fr);
|
||||
u_int8_t *trau_idle_frame(void);
|
||||
|
||||
|
||||
#endif /* _TRAU_FRAME_H */
|
|
@ -0,0 +1,49 @@
|
|||
/* Simple TRAU frame reflector to route voice calls */
|
||||
|
||||
/* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* The "TRAU mux map" defines which particular 16kbit sub-slot (in which E1
|
||||
* timeslot on which E1 interface) should be directly muxed to which other
|
||||
* sub-slot. Entries in the mux map are always bi-directional.
|
||||
*
|
||||
* The idea of all this is to directly switch voice channels in the BSC
|
||||
* from one phone to another. We do this right now since we don't support
|
||||
* any external interface for voice channels, and in the future as an
|
||||
* optimization to routing them externally.
|
||||
*/
|
||||
|
||||
/* map a TRAU mux map entry */
|
||||
int trau_mux_map(const struct gsm_e1_subslot *src,
|
||||
const struct gsm_e1_subslot *dst);
|
||||
int trau_mux_map_lchan(const struct gsm_lchan *src,
|
||||
const struct gsm_lchan *dst);
|
||||
|
||||
/* unmap a TRAU mux map entry */
|
||||
int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* add a trau receiver */
|
||||
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);
|
|
@ -0,0 +1 @@
|
|||
noinst_HEADERS = buffer.h command.h vector.h vty.h
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Buffering to output and input.
|
||||
* Copyright (C) 1998 Kunihiro Ishiguro
|
||||
*
|
||||
* This file is part of GNU Zebra.
|
||||
*
|
||||
* GNU Zebra 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, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ZEBRA_BUFFER_H
|
||||
#define _ZEBRA_BUFFER_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Create a new buffer. Memory will be allocated in chunks of the given
|
||||
size. If the argument is 0, the library will supply a reasonable
|
||||
default size suitable for buffering socket I/O. */
|
||||
struct buffer *buffer_new(size_t);
|
||||
|
||||
/* Free all data in the buffer. */
|
||||
void buffer_reset(struct buffer *);
|
||||
|
||||
/* This function first calls buffer_reset to release all buffered data.
|
||||
Then it frees the struct buffer itself. */
|
||||
void buffer_free(struct buffer *);
|
||||
|
||||
/* Add the given data to the end of the buffer. */
|
||||
extern void buffer_put(struct buffer *, const void *, size_t);
|
||||
/* Add a single character to the end of the buffer. */
|
||||
extern void buffer_putc(struct buffer *, u_char);
|
||||
/* Add a NUL-terminated string to the end of the buffer. */
|
||||
extern void buffer_putstr(struct buffer *, const char *);
|
||||
|
||||
/* Combine all accumulated (and unflushed) data inside the buffer into a
|
||||
single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
|
||||
that this function does not alter the state of the buffer, so the data
|
||||
is still inside waiting to be flushed. */
|
||||
char *buffer_getstr(struct buffer *);
|
||||
|
||||
/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
|
||||
int buffer_empty(struct buffer *);
|
||||
|
||||
typedef enum {
|
||||
/* An I/O error occurred. The buffer should be destroyed and the
|
||||
file descriptor should be closed. */
|
||||
BUFFER_ERROR = -1,
|
||||
|
||||
/* The data was written successfully, and the buffer is now empty
|
||||
(there is no pending data waiting to be flushed). */
|
||||
BUFFER_EMPTY = 0,
|
||||
|
||||
/* There is pending data in the buffer waiting to be flushed. Please
|
||||
try flushing the buffer when select indicates that the file descriptor
|
||||
is writeable. */
|
||||
BUFFER_PENDING = 1
|
||||
} buffer_status_t;
|
||||
|
||||
/* Try to write this data to the file descriptor. Any data that cannot
|
||||
be written immediately is added to the buffer queue. */
|
||||
extern buffer_status_t buffer_write(struct buffer *, int fd,
|
||||
const void *, size_t);
|
||||
|
||||
/* This function attempts to flush some (but perhaps not all) of
|
||||
the queued data to the given file descriptor. */
|
||||
extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
|
||||
|
||||
/* The following 2 functions (buffer_flush_all and buffer_flush_window)
|
||||
are for use in lib/vty.c only. They should not be used elsewhere. */
|
||||
|
||||
/* Call buffer_flush_available repeatedly until either all data has been
|
||||
flushed, or an I/O error has been encountered, or the operation would
|
||||
block. */
|
||||
extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
|
||||
|
||||
/* Attempt to write enough data to the given fd to fill a window of the
|
||||
given width and height (and remove the data written from the buffer).
|
||||
|
||||
If !no_more, then a message saying " --More-- " is appended.
|
||||
If erase is true, then first overwrite the previous " --More-- " message
|
||||
with spaces.
|
||||
|
||||
Any write error (including EAGAIN or EINTR) will cause this function
|
||||
to return -1 (because the logic for handling the erase and more features
|
||||
is too complicated to retry the write later).
|
||||
*/
|
||||
extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
|
||||
int height, int erase, int no_more);
|
||||
|
||||
#endif /* _ZEBRA_BUFFER_H */
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* Zebra configuration command interface routine
|
||||
* Copyright (C) 1997, 98 Kunihiro Ishiguro
|
||||
*
|
||||
* This file is part of GNU Zebra.
|
||||
*
|
||||
* GNU Zebra 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, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ZEBRA_COMMAND_H
|
||||
#define _ZEBRA_COMMAND_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include "vector.h"
|
||||
#include "vty.h"
|
||||
|
||||
/* Host configuration variable */
|
||||
struct host {
|
||||
/* Host name of this router. */
|
||||
char *name;
|
||||
|
||||
/* Password for vty interface. */
|
||||
char *password;
|
||||
char *password_encrypt;
|
||||
|
||||
/* Enable password */
|
||||
char *enable;
|
||||
char *enable_encrypt;
|
||||
|
||||
/* System wide terminal lines. */
|
||||
int lines;
|
||||
|
||||
/* Log filename. */
|
||||
char *logfile;
|
||||
|
||||
/* config file name of this host */
|
||||
char *config;
|
||||
|
||||
/* Flags for services */
|
||||
int advanced;
|
||||
int encrypt;
|
||||
|
||||
/* Banner configuration. */
|
||||
const char *motd;
|
||||
char *motdfile;
|
||||
};
|
||||
|
||||
/* There are some command levels which called from command node. */
|
||||
enum node_type {
|
||||
BTS_NODE,
|
||||
TRX_NODE,
|
||||
TS_NODE,
|
||||
SUBSCR_NODE,
|
||||
|
||||
AUTH_NODE, /* Authentication mode of vty interface. */
|
||||
VIEW_NODE, /* View node. Default mode of vty interface. */
|
||||
AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
|
||||
ENABLE_NODE, /* Enable node. */
|
||||
CONFIG_NODE, /* Config node. Default mode of config file. */
|
||||
SERVICE_NODE, /* Service node. */
|
||||
DEBUG_NODE, /* Debug node. */
|
||||
AAA_NODE, /* AAA node. */
|
||||
KEYCHAIN_NODE, /* Key-chain node. */
|
||||
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
|
||||
INTERFACE_NODE, /* Interface mode node. */
|
||||
ZEBRA_NODE, /* zebra connection node. */
|
||||
TABLE_NODE, /* rtm_table selection node. */
|
||||
RIP_NODE, /* RIP protocol mode node. */
|
||||
RIPNG_NODE, /* RIPng protocol mode node. */
|
||||
BGP_NODE, /* BGP protocol mode which includes BGP4+ */
|
||||
BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
|
||||
BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
|
||||
BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
|
||||
BGP_IPV6_NODE, /* BGP IPv6 address family */
|
||||
OSPF_NODE, /* OSPF protocol mode */
|
||||
OSPF6_NODE, /* OSPF protocol for IPv6 mode */
|
||||
ISIS_NODE, /* ISIS protocol mode */
|
||||
MASC_NODE, /* MASC for multicast. */
|
||||
IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
|
||||
IP_NODE, /* Static ip route node. */
|
||||
ACCESS_NODE, /* Access list node. */
|
||||
PREFIX_NODE, /* Prefix list node. */
|
||||
ACCESS_IPV6_NODE, /* Access list node. */
|
||||
PREFIX_IPV6_NODE, /* Prefix list node. */
|
||||
AS_LIST_NODE, /* AS list node. */
|
||||
COMMUNITY_LIST_NODE, /* Community list node. */
|
||||
RMAP_NODE, /* Route map node. */
|
||||
SMUX_NODE, /* SNMP configuration node. */
|
||||
DUMP_NODE, /* Packet dump node. */
|
||||
FORWARDING_NODE, /* IP forwarding node. */
|
||||
VTY_NODE /* Vty node. */
|
||||
};
|
||||
|
||||
/* Node which has some commands and prompt string and configuration
|
||||
function pointer . */
|
||||
struct cmd_node {
|
||||
/* Node index. */
|
||||
enum node_type node;
|
||||
|
||||
/* Prompt character at vty interface. */
|
||||
const char *prompt;
|
||||
|
||||
/* Is this node's configuration goes to vtysh ? */
|
||||
int vtysh;
|
||||
|
||||
/* Node's configuration write function */
|
||||
int (*func) (struct vty *);
|
||||
|
||||
/* Vector of this node's command list. */
|
||||
vector cmd_vector;
|
||||
};
|
||||
|
||||
enum {
|
||||
CMD_ATTR_DEPRECATED = 1,
|
||||
CMD_ATTR_HIDDEN,
|
||||
};
|
||||
|
||||
/* Structure of command element. */
|
||||
struct cmd_element {
|
||||
const char *string; /* Command specification by string. */
|
||||
int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
|
||||
const char *doc; /* Documentation of this command. */
|
||||
int daemon; /* Daemon to which this command belong. */
|
||||
vector strvec; /* Pointing out each description vector. */
|
||||
unsigned int cmdsize; /* Command index count. */
|
||||
char *config; /* Configuration string */
|
||||
vector subconfig; /* Sub configuration string */
|
||||
u_char attr; /* Command attributes */
|
||||
};
|
||||
|
||||
/* Command description structure. */
|
||||
struct desc {
|
||||
const char *cmd; /* Command string. */
|
||||
const char *str; /* Command's description. */
|
||||
};
|
||||
|
||||
/* Return value of the commands. */
|
||||
#define CMD_SUCCESS 0
|
||||
#define CMD_WARNING 1
|
||||
#define CMD_ERR_NO_MATCH 2
|
||||
#define CMD_ERR_AMBIGUOUS 3
|
||||
#define CMD_ERR_INCOMPLETE 4
|
||||
#define CMD_ERR_EXEED_ARGC_MAX 5
|
||||
#define CMD_ERR_NOTHING_TODO 6
|
||||
#define CMD_COMPLETE_FULL_MATCH 7
|
||||
#define CMD_COMPLETE_MATCH 8
|
||||
#define CMD_COMPLETE_LIST_MATCH 9
|
||||
#define CMD_SUCCESS_DAEMON 10
|
||||
|
||||
/* Argc max counts. */
|
||||
#define CMD_ARGC_MAX 25
|
||||
|
||||
/* Turn off these macros when uisng cpp with extract.pl */
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
|
||||
/* helper defines for end-user DEFUN* macros */
|
||||
#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
|
||||
struct cmd_element cmdname = \
|
||||
{ \
|
||||
.string = cmdstr, \
|
||||
.func = funcname, \
|
||||
.doc = helpstr, \
|
||||
.attr = attrs, \
|
||||
.daemon = dnum, \
|
||||
};
|
||||
|
||||
#define DEFUN_CMD_FUNC_DECL(funcname) \
|
||||
static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \
|
||||
|
||||
#define DEFUN_CMD_FUNC_TEXT(funcname) \
|
||||
static int funcname \
|
||||
(struct cmd_element *self, struct vty *vty, int argc, const char *argv[])
|
||||
|
||||
/* DEFUN for vty command interafce. Little bit hacky ;-). */
|
||||
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_FUNC_DECL(funcname) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
|
||||
DEFUN_CMD_FUNC_TEXT(funcname)
|
||||
|
||||
#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
|
||||
DEFUN_CMD_FUNC_DECL(funcname) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
|
||||
DEFUN_CMD_FUNC_TEXT(funcname)
|
||||
|
||||
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
|
||||
|
||||
#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
|
||||
|
||||
/* DEFUN_NOSH for commands that vtysh should ignore */
|
||||
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN(funcname, cmdname, cmdstr, helpstr)
|
||||
|
||||
/* DEFSH for vtysh. */
|
||||
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
|
||||
|
||||
/* DEFUN + DEFSH */
|
||||
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_FUNC_DECL(funcname) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
|
||||
DEFUN_CMD_FUNC_TEXT(funcname)
|
||||
|
||||
/* DEFUN + DEFSH with attributes */
|
||||
#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
|
||||
DEFUN_CMD_FUNC_DECL(funcname) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
|
||||
DEFUN_CMD_FUNC_TEXT(funcname)
|
||||
|
||||
#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
|
||||
|
||||
#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
|
||||
|
||||
/* ALIAS macro which define existing command's alias. */
|
||||
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
|
||||
|
||||
#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
|
||||
|
||||
#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
|
||||
|
||||
#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
|
||||
|
||||
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
|
||||
|
||||
#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
|
||||
|
||||
#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
|
||||
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
|
||||
|
||||
#endif /* VTYSH_EXTRACT_PL */
|
||||
|
||||
/* Some macroes */
|
||||
#define CMD_OPTION(S) ((S[0]) == '[')
|
||||
#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
|
||||
#define CMD_VARARG(S) ((S[0]) == '.')
|
||||
#define CMD_RANGE(S) ((S[0] == '<'))
|
||||
|
||||
#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
|
||||
#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
|
||||
#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
|
||||
#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
|
||||
|
||||
/* Common descriptions. */
|
||||
#define SHOW_STR "Show running system information\n"
|
||||
#define IP_STR "IP information\n"
|
||||
#define IPV6_STR "IPv6 information\n"
|
||||
#define NO_STR "Negate a command or set its defaults\n"
|
||||
#define CLEAR_STR "Reset functions\n"
|
||||
#define RIP_STR "RIP information\n"
|
||||
#define BGP_STR "BGP information\n"
|
||||
#define OSPF_STR "OSPF information\n"
|
||||
#define NEIGHBOR_STR "Specify neighbor router\n"
|
||||
#define DEBUG_STR "Debugging functions (see also 'undebug')\n"
|
||||
#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
|
||||
#define ROUTER_STR "Enable a routing process\n"
|
||||
#define AS_STR "AS number\n"
|
||||
#define MBGP_STR "MBGP information\n"
|
||||
#define MATCH_STR "Match values from routing table\n"
|
||||
#define SET_STR "Set values in destination routing protocol\n"
|
||||
#define OUT_STR "Filter outgoing routing updates\n"
|
||||
#define IN_STR "Filter incoming routing updates\n"
|
||||
#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n"
|
||||
#define OSPF6_NUMBER_STR "Specify by number\n"
|
||||
#define INTERFACE_STR "Interface infomation\n"
|
||||
#define IFNAME_STR "Interface name(e.g. ep0)\n"
|
||||
#define IP6_STR "IPv6 Information\n"
|
||||
#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"
|
||||
#define OSPF6_ROUTER_STR "Enable a routing process\n"
|
||||
#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n"
|
||||
#define SECONDS_STR "<1-65535> Seconds\n"
|
||||
#define ROUTE_STR "Routing Table\n"
|
||||
#define PREFIX_LIST_STR "Build a prefix list\n"
|
||||
#define OSPF6_DUMP_TYPE_LIST \
|
||||
"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
|
||||
#define ISIS_STR "IS-IS information\n"
|
||||
#define AREA_TAG_STR "[area tag]\n"
|
||||
|
||||
#define CONF_BACKUP_EXT ".sav"
|
||||
|
||||
/* IPv4 only machine should not accept IPv6 address for peer's IP
|
||||
address. So we replace VTY command string like below. */
|
||||
#ifdef HAVE_IPV6
|
||||
#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) "
|
||||
#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) "
|
||||
#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n"
|
||||
#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) "
|
||||
#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) "
|
||||
#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n"
|
||||
#else
|
||||
#define NEIGHBOR_CMD "neighbor A.B.C.D "
|
||||
#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D "
|
||||
#define NEIGHBOR_ADDR_STR "Neighbor address\n"
|
||||
#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) "
|
||||
#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) "
|
||||
#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"
|
||||
#endif /* HAVE_IPV6 */
|
||||
|
||||
/* Prototypes. */
|
||||
void install_node(struct cmd_node *, int (*)(struct vty *));
|
||||
void install_default(enum node_type);
|
||||
void install_element(enum node_type, struct cmd_element *);
|
||||
void sort_node();
|
||||
|
||||
/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
|
||||
string with a space between each element (allocated using
|
||||
XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
|
||||
char *argv_concat(const char **argv, int argc, int shift);
|
||||
|
||||
vector cmd_make_strvec(const char *);
|
||||
void cmd_free_strvec(vector);
|
||||
vector cmd_describe_command();
|
||||
char **cmd_complete_command();
|
||||
const char *cmd_prompt(enum node_type);
|
||||
int config_from_file(struct vty *, FILE *);
|
||||
enum node_type node_parent(enum node_type);
|
||||
int cmd_execute_command(vector, struct vty *, struct cmd_element **, int);
|
||||
int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **);
|
||||
void config_replace_string(struct cmd_element *, char *, ...);
|
||||
void cmd_init(int);
|
||||
|
||||
/* Export typical functions. */
|
||||
extern struct cmd_element config_end_cmd;
|
||||
extern struct cmd_element config_exit_cmd;
|
||||
extern struct cmd_element config_quit_cmd;
|
||||
extern struct cmd_element config_help_cmd;
|
||||
extern struct cmd_element config_list_cmd;
|
||||
char *host_config_file();
|
||||
void host_config_set(char *);
|
||||
|
||||
void print_version(const char *);
|
||||
|
||||
#endif /* _ZEBRA_COMMAND_H */
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Generic vector interface header.
|
||||
* Copyright (C) 1997, 98 Kunihiro Ishiguro
|
||||
*
|
||||
* This file is part of GNU Zebra.
|
||||
*
|
||||
* GNU Zebra 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, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _ZEBRA_VECTOR_H
|
||||
#define _ZEBRA_VECTOR_H
|
||||
|
||||
/* struct for vector */
|
||||
struct _vector {
|
||||
unsigned int active; /* number of active slots */
|
||||
unsigned int alloced; /* number of allocated slot */
|
||||
void **index; /* index to data */
|
||||
};
|
||||
typedef struct _vector *vector;
|
||||
|
||||
#define VECTOR_MIN_SIZE 1
|
||||
|
||||
/* (Sometimes) usefull macros. This macro convert index expression to
|
||||
array expression. */
|
||||
/* Reference slot at given index, caller must ensure slot is active */
|
||||
#define vector_slot(V,I) ((V)->index[(I)])
|
||||
/* Number of active slots.
|
||||
* Note that this differs from vector_count() as it the count returned
|
||||
* will include any empty slots
|
||||
*/
|
||||
#define vector_active(V) ((V)->active)
|
||||
|
||||
/* Prototypes. */
|
||||
vector vector_init(unsigned int size);
|
||||
void vector_ensure(vector v, unsigned int num);
|
||||
int vector_empty_slot(vector v);
|
||||
int vector_set(vector v, void *val);
|
||||
int vector_set_index(vector v, unsigned int i, void *val);
|
||||
void vector_unset(vector v, unsigned int i);
|
||||
unsigned int vector_count(vector v);
|
||||
void vector_only_wrapper_free(vector v);
|
||||
void vector_only_index_free(void *index);
|
||||
void vector_free(vector v);
|
||||
vector vector_copy(vector v);
|
||||
|
||||
void *vector_lookup(vector, unsigned int);
|
||||
void *vector_lookup_ensure(vector, unsigned int);
|
||||
|
||||
#endif /* _ZEBRA_VECTOR_H */
|
|
@ -0,0 +1,150 @@
|
|||
#ifndef _VTY_H
|
||||
#define _VTY_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* GCC have printf type attribute check. */
|
||||
#ifdef __GNUC__
|
||||
#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
|
||||
#else
|
||||
#define PRINTF_ATTRIBUTE(a,b)
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
/* Does the I/O error indicate that the operation should be retried later? */
|
||||
#define ERRNO_IO_RETRY(EN) \
|
||||
(((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
|
||||
|
||||
/* Vty read buffer size. */
|
||||
#define VTY_READ_BUFSIZ 512
|
||||
|
||||
#define VTY_BUFSIZ 512
|
||||
#define VTY_MAXHIST 20
|
||||
|
||||
/* Vty events */
|
||||
enum event {
|
||||
VTY_SERV,
|
||||
VTY_READ,
|
||||
VTY_WRITE,
|
||||
VTY_TIMEOUT_RESET,
|
||||
#ifdef VTYSH
|
||||
VTYSH_SERV,
|
||||
VTYSH_READ,
|
||||
VTYSH_WRITE
|
||||
#endif /* VTYSH */
|
||||
};
|
||||
|
||||
struct vty {
|
||||
FILE *file;
|
||||
|
||||
/* private data, specified by creator */
|
||||
void *priv;
|
||||
|
||||
/* File descripter of this vty. */
|
||||
int fd;
|
||||
|
||||
/* Is this vty connect to file or not */
|
||||
enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type;
|
||||
|
||||
/* Node status of this vty */
|
||||
int node;
|
||||
|
||||
/* Failure count */
|
||||
int fail;
|
||||
|
||||
/* Output buffer. */
|
||||
struct buffer *obuf;
|
||||
|
||||
/* Command input buffer */
|
||||
char *buf;
|
||||
|
||||
/* Command cursor point */
|
||||
int cp;
|
||||
|
||||
/* Command length */
|
||||
int length;
|
||||
|
||||
/* Command max length. */
|
||||
int max;
|
||||
|
||||
/* Histry of command */
|
||||
char *hist[VTY_MAXHIST];
|
||||
|
||||
/* History lookup current point */
|
||||
int hp;
|
||||
|
||||
/* History insert end point */
|
||||
int hindex;
|
||||
|
||||
/* For current referencing point of interface, route-map,
|
||||
access-list etc... */
|
||||
void *index;
|
||||
|
||||
/* For multiple level index treatment such as key chain and key. */
|
||||
void *index_sub;
|
||||
|
||||
/* For escape character. */
|
||||
unsigned char escape;
|
||||
|
||||
/* Current vty status. */
|
||||
enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
|
||||
|
||||
/* IAC handling: was the last character received the IAC
|
||||
* (interpret-as-command) escape character (and therefore the next
|
||||
* character will be the command code)? Refer to Telnet RFC 854. */
|
||||
unsigned char iac;
|
||||
|
||||
/* IAC SB (option subnegotiation) handling */
|
||||
unsigned char iac_sb_in_progress;
|
||||
/* At the moment, we care only about the NAWS (window size) negotiation,
|
||||
* and that requires just a 5-character buffer (RFC 1073):
|
||||
* <NAWS char> <16-bit width> <16-bit height> */
|
||||
#define TELNET_NAWS_SB_LEN 5
|
||||
unsigned char sb_buf[TELNET_NAWS_SB_LEN];
|
||||
/* How many subnegotiation characters have we received? We just drop
|
||||
* those that do not fit in the buffer. */
|
||||
size_t sb_len;
|
||||
|
||||
/* Window width/height. */
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/* Configure lines. */
|
||||
int lines;
|
||||
|
||||
int monitor;
|
||||
|
||||
/* In configure mode. */
|
||||
int config;
|
||||
};
|
||||
|
||||
/* Small macro to determine newline is newline only or linefeed needed. */
|
||||
#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n")
|
||||
|
||||
static inline char *vty_newline(struct vty *vty)
|
||||
{
|
||||
return VTY_NEWLINE;
|
||||
}
|
||||
|
||||
/* Prototypes. */
|
||||
void vty_init (void);
|
||||
void vty_init_vtysh (void);
|
||||
void vty_reset (void);
|
||||
struct vty *vty_new (void);
|
||||
struct vty *vty_create (int vty_sock, void *priv);
|
||||
int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||
int vty_out_newline(struct vty *);
|
||||
int vty_read(struct vty *vty);
|
||||
void vty_read_config (char *, char *);
|
||||
void vty_time_print (struct vty *, int);
|
||||
void vty_close (struct vty *);
|
||||
char *vty_get_cwd (void);
|
||||
void vty_log (const char *level, const char *proto, const char *fmt, va_list);
|
||||
int vty_config_lock (struct vty *);
|
||||
int vty_config_unlock (struct vty *);
|
||||
int vty_shell (struct vty *);
|
||||
int vty_shell_serv (struct vty *);
|
||||
void vty_hello (struct vty *);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
AM_CFLAGS=-Wall
|
||||
|
||||
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
|
||||
noinst_LIBRARIES = libbsc.a libvty.a
|
||||
noinst_HEADERS = vty/cardshell.h
|
||||
|
||||
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
|
||||
gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
|
||||
gsm_04_11.c telnet_interface.c subchan_demux.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
|
||||
|
||||
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
|
||||
|
||||
bsc_hack_SOURCES = bsc_hack.c vty_interface.c
|
||||
bsc_hack_LDADD = libbsc.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
|
||||
|
||||
ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c
|
||||
|
||||
ipaccess_config_SOURCES = ipaccess-config.c
|
||||
ipaccess_config_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
|
||||
isdnsync_SOURCES = isdnsync.c
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,805 @@
|
|||
/* Siemens BS-11 microBTS configuration tool */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This software is based on ideas (but not code) of BS11Config
|
||||
* (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de>
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/rs232.h>
|
||||
|
||||
/* state of our bs11_config application */
|
||||
enum bs11cfg_state {
|
||||
STATE_NONE,
|
||||
STATE_LOGON_WAIT,
|
||||
STATE_LOGON_ACK,
|
||||
STATE_SWLOAD,
|
||||
STATE_QUERY,
|
||||
};
|
||||
static enum bs11cfg_state bs11cfg_state = STATE_NONE;
|
||||
static char *command;
|
||||
struct timer_list status_timer;
|
||||
|
||||
static const u_int8_t obj_li_attr[] = {
|
||||
NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00,
|
||||
NM_ATT_BS11_L1_PROT_TYPE, 0x00,
|
||||
NM_ATT_BS11_LINE_CFG, 0x00,
|
||||
};
|
||||
static const u_int8_t obj_bbsig0_attr[] = {
|
||||
NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00,
|
||||
NM_ATT_BS11_DIVERSITY, 0x01, 0x00,
|
||||
};
|
||||
static const u_int8_t obj_pa0_attr[] = {
|
||||
NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW,
|
||||
};
|
||||
static const char *trx1_password = "1111111111";
|
||||
#define TEI_OML 25
|
||||
|
||||
static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
|
||||
|
||||
|
||||
int handle_serial_msg(struct msgb *rx_msg);
|
||||
|
||||
/* create all objects for an initial configuration */
|
||||
static int create_objects(struct gsm_bts *bts)
|
||||
{
|
||||
fprintf(stdout, "Crating Objects for minimal config\n");
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr),
|
||||
obj_li_attr);
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL);
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL);
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL);
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0,
|
||||
sizeof(obj_bbsig0_attr), obj_bbsig0_attr);
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0,
|
||||
sizeof(obj_pa0_attr), obj_pa0_attr);
|
||||
abis_nm_bs11_create_envaBTSE(bts, 0);
|
||||
abis_nm_bs11_create_envaBTSE(bts, 1);
|
||||
abis_nm_bs11_create_envaBTSE(bts, 2);
|
||||
abis_nm_bs11_create_envaBTSE(bts, 3);
|
||||
|
||||
abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML);
|
||||
|
||||
abis_nm_bs11_set_trx_power(&bts->trx[0], BS11_TRX_POWER_GSM_30mW);
|
||||
|
||||
sleep(1);
|
||||
|
||||
abis_nm_bs11_set_trx1_pw(bts, trx1_password);
|
||||
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_trx1(struct gsm_bts *bts)
|
||||
{
|
||||
u_int8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12];
|
||||
u_int8_t *cur = bbsig1_attr;
|
||||
|
||||
fprintf(stdout, "Crating Objects for TRX1\n");
|
||||
|
||||
abis_nm_bs11_set_trx1_pw(bts, trx1_password);
|
||||
|
||||
sleep(1);
|
||||
|
||||
cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10,
|
||||
(u_int8_t *)trx1_password);
|
||||
memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr));
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1,
|
||||
sizeof(bbsig1_attr), bbsig1_attr);
|
||||
abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1,
|
||||
sizeof(obj_pa0_attr), obj_pa0_attr);
|
||||
abis_nm_bs11_set_trx_power(&bts->trx[1], BS11_TRX_POWER_GSM_30mW);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *serial_port = "/dev/ttyUSB0";
|
||||
static char *fname_safety = "BTSBMC76.SWI";
|
||||
static char *fname_software = "HS011106.SWL";
|
||||
static int delay_ms = 0;
|
||||
static int win_size = 8;
|
||||
static int param_disconnect = 0;
|
||||
static int param_restart = 0;
|
||||
static int param_forced = 0;
|
||||
static struct gsm_bts *g_bts;
|
||||
|
||||
static int file_is_readable(const char *fname)
|
||||
{
|
||||
int rc;
|
||||
struct stat st;
|
||||
|
||||
rc = stat(fname, &st);
|
||||
if (rc < 0)
|
||||
return 0;
|
||||
|
||||
if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int percent;
|
||||
static int percent_old;
|
||||
|
||||
/* callback function passed to the ABIS OML code */
|
||||
static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg,
|
||||
void *data, void *param)
|
||||
{
|
||||
if (hook != GSM_HOOK_NM_SWLOAD)
|
||||
return 0;
|
||||
|
||||
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:
|
||||
if (data) {
|
||||
/* we did a safety load and must activate it */
|
||||
abis_nm_software_activate(g_bts, fname_safety,
|
||||
swload_cbfn, g_bts);
|
||||
sleep(5);
|
||||
}
|
||||
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:
|
||||
bs11cfg_state = STATE_NONE;
|
||||
|
||||
break;
|
||||
case NM_MT_LOAD_SEG_ACK:
|
||||
percent = abis_nm_software_load_status(g_bts);
|
||||
if (percent > percent_old)
|
||||
printf("Software Download Progress: %d%%\n", percent);
|
||||
percent_old = percent;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *bs11_link_state[] = {
|
||||
[0x00] = "Down",
|
||||
[0x01] = "Up",
|
||||
[0x02] = "Restoring",
|
||||
};
|
||||
|
||||
static const char *linkstate_name(u_int8_t linkstate)
|
||||
{
|
||||
if (linkstate > ARRAY_SIZE(bs11_link_state))
|
||||
return "Unknown";
|
||||
|
||||
return bs11_link_state[linkstate];
|
||||
}
|
||||
|
||||
static const char *mbccu_load[] = {
|
||||
[0] = "No Load",
|
||||
[1] = "Load BTSCAC",
|
||||
[2] = "Load BTSDRX",
|
||||
[3] = "Load BTSBBX",
|
||||
[4] = "Load BTSARC",
|
||||
[5] = "Load",
|
||||
};
|
||||
|
||||
static const char *mbccu_load_name(u_int8_t linkstate)
|
||||
{
|
||||
if (linkstate > ARRAY_SIZE(mbccu_load))
|
||||
return "Unknown";
|
||||
|
||||
return mbccu_load[linkstate];
|
||||
}
|
||||
|
||||
static const char *bts_phase_name(u_int8_t phase)
|
||||
{
|
||||
switch (phase) {
|
||||
case BS11_STATE_WARM_UP:
|
||||
case BS11_STATE_WARM_UP_2:
|
||||
return "Warm Up";
|
||||
break;
|
||||
case BS11_STATE_LOAD_SMU_SAFETY:
|
||||
return "Load SMU Safety";
|
||||
break;
|
||||
case BS11_STATE_LOAD_SMU_INTENDED:
|
||||
return "Load SMU Intended";
|
||||
break;
|
||||
case BS11_STATE_LOAD_MBCCU:
|
||||
return "Load MBCCU";
|
||||
break;
|
||||
case BS11_STATE_SOFTWARE_RQD:
|
||||
return "Software required";
|
||||
break;
|
||||
case BS11_STATE_WAIT_MIN_CFG:
|
||||
case BS11_STATE_WAIT_MIN_CFG_2:
|
||||
return "Wait minimal config";
|
||||
break;
|
||||
case BS11_STATE_MAINTENANCE:
|
||||
return "Maintenance";
|
||||
break;
|
||||
case BS11_STATE_NORMAL:
|
||||
return "Normal";
|
||||
break;
|
||||
case BS11_STATE_ABIS_LOAD:
|
||||
return "Abis load";
|
||||
break;
|
||||
default:
|
||||
return "Unknown";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *trx_power_name(u_int8_t pwr)
|
||||
{
|
||||
switch (pwr) {
|
||||
case BS11_TRX_POWER_GSM_2W:
|
||||
return "2W (GSM)";
|
||||
case BS11_TRX_POWER_GSM_250mW:
|
||||
return "250mW (GSM)";
|
||||
case BS11_TRX_POWER_GSM_80mW:
|
||||
return "80mW (GSM)";
|
||||
case BS11_TRX_POWER_GSM_30mW:
|
||||
return "30mW (GSM)";
|
||||
case BS11_TRX_POWER_DCS_3W:
|
||||
return "3W (DCS)";
|
||||
case BS11_TRX_POWER_DCS_1W6:
|
||||
return "1.6W (DCS)";
|
||||
case BS11_TRX_POWER_DCS_500mW:
|
||||
return "500mW (DCS)";
|
||||
case BS11_TRX_POWER_DCS_160mW:
|
||||
return "160mW (DCS)";
|
||||
default:
|
||||
return "unknown value";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *pll_mode_name(u_int8_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case BS11_LI_PLL_LOCKED:
|
||||
return "E1 Locked";
|
||||
case BS11_LI_PLL_STANDALONE:
|
||||
return "Standalone";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *cclk_acc_name(u_int8_t acc)
|
||||
{
|
||||
switch (acc) {
|
||||
case 0:
|
||||
/* Out of the demanded +/- 0.05ppm */
|
||||
return "Medium";
|
||||
case 1:
|
||||
/* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */
|
||||
return "High";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *obj_name(struct abis_om_fom_hdr *foh)
|
||||
{
|
||||
static char retbuf[256];
|
||||
|
||||
retbuf[0] = 0;
|
||||
|
||||
switch (foh->obj_class) {
|
||||
case NM_OC_BS11:
|
||||
strcat(retbuf, "BS11 ");
|
||||
switch (foh->obj_inst.bts_nr) {
|
||||
case BS11_OBJ_PA:
|
||||
sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ",
|
||||
foh->obj_inst.ts_nr);
|
||||
break;
|
||||
case BS11_OBJ_LI:
|
||||
sprintf(retbuf+strlen(retbuf), "Line Interface ");
|
||||
break;
|
||||
case BS11_OBJ_CCLK:
|
||||
sprintf(retbuf+strlen(retbuf), "CCLK ");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NM_OC_SITE_MANAGER:
|
||||
strcat(retbuf, "SITE MANAGER ");
|
||||
break;
|
||||
}
|
||||
return retbuf;
|
||||
}
|
||||
|
||||
static void print_state(struct tlv_parsed *tp)
|
||||
{
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) {
|
||||
u_int8_t phase, mbccu;
|
||||
if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) {
|
||||
phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE);
|
||||
printf("PHASE: %u %-20s ", phase & 0xf,
|
||||
bts_phase_name(phase));
|
||||
}
|
||||
if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) {
|
||||
mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1);
|
||||
printf("MBCCU0: %-11s MBCCU1: %-11s ",
|
||||
mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4));
|
||||
}
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) &&
|
||||
TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) {
|
||||
u_int8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE);
|
||||
printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int print_attr(struct tlv_parsed *tp)
|
||||
{
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) {
|
||||
printf("\tBS-11 ESN PCB Serial Number: %s\n",
|
||||
TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL));
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) {
|
||||
printf("\tBS-11 ESN Hardware Code Number: %s\n",
|
||||
TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6);
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) {
|
||||
printf("\tBS-11 ESN Firmware Code Number: %s\n",
|
||||
TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6);
|
||||
}
|
||||
#if 0
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) {
|
||||
printf("BS-11 Boot Software Version: %s\n",
|
||||
TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6);
|
||||
}
|
||||
#endif
|
||||
if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) &&
|
||||
TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) {
|
||||
const u_int8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL);
|
||||
printf("\tE1 Channel: Port=%u Timeslot=%u ",
|
||||
chan[0], chan[1]);
|
||||
if (chan[2] == 0xff)
|
||||
printf("(Full Slot)\n");
|
||||
else
|
||||
printf("Subslot=%u\n", chan[2]);
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_TEI))
|
||||
printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI));
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) &&
|
||||
TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) {
|
||||
printf("\tTRX Power: %s\n",
|
||||
trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR)));
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) &&
|
||||
TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) {
|
||||
printf("\tPLL Mode: %s\n",
|
||||
pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE)));
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) &&
|
||||
TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) {
|
||||
const u_int8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL);
|
||||
printf("\tPLL Set Value=%d, Work Value=%d\n",
|
||||
vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]);
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) &&
|
||||
TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) {
|
||||
const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY);
|
||||
printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc);
|
||||
}
|
||||
if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) &&
|
||||
TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) {
|
||||
const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE);
|
||||
printf("\tCCLK Type=%d\n", *acc);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cmd_query(void)
|
||||
{
|
||||
bs11cfg_state = STATE_QUERY;
|
||||
abis_nm_bs11_get_serno(g_bts);
|
||||
abis_nm_bs11_get_oml_tei_ts(g_bts);
|
||||
abis_nm_bs11_get_pll_mode(g_bts);
|
||||
abis_nm_bs11_get_cclk(g_bts);
|
||||
abis_nm_bs11_get_trx_power(&g_bts->trx[0]);
|
||||
abis_nm_bs11_get_trx_power(&g_bts->trx[1]);
|
||||
sleep(1);
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
}
|
||||
|
||||
/* handle a response from the BTS to a GET STATE command */
|
||||
static int handle_state_resp(enum abis_bs11_phase state)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (state) {
|
||||
case BS11_STATE_WARM_UP:
|
||||
case BS11_STATE_LOAD_SMU_SAFETY:
|
||||
case BS11_STATE_LOAD_SMU_INTENDED:
|
||||
case BS11_STATE_LOAD_MBCCU:
|
||||
break;
|
||||
case BS11_STATE_SOFTWARE_RQD:
|
||||
bs11cfg_state = STATE_SWLOAD;
|
||||
/* send safety load. Use g_bts as private 'param'
|
||||
* argument, so our swload_cbfn can distinguish
|
||||
* a safety load from a regular software */
|
||||
if (file_is_readable(fname_safety))
|
||||
rc = abis_nm_software_load(g_bts, fname_safety,
|
||||
win_size, param_forced,
|
||||
swload_cbfn, g_bts);
|
||||
else
|
||||
fprintf(stderr, "No valid Safety Load file \"%s\"\n",
|
||||
fname_safety);
|
||||
break;
|
||||
case BS11_STATE_WAIT_MIN_CFG:
|
||||
case BS11_STATE_WAIT_MIN_CFG_2:
|
||||
bs11cfg_state = STATE_SWLOAD;
|
||||
rc = create_objects(g_bts);
|
||||
break;
|
||||
case BS11_STATE_MAINTENANCE:
|
||||
if (command) {
|
||||
if (!strcmp(command, "disconnect"))
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
else if (!strcmp(command, "reconnect"))
|
||||
rc = abis_nm_bs11_bsc_disconnect(g_bts, 1);
|
||||
else if (!strcmp(command, "software")
|
||||
&& bs11cfg_state != STATE_SWLOAD) {
|
||||
bs11cfg_state = STATE_SWLOAD;
|
||||
/* send software (FIXME: over A-bis?) */
|
||||
if (file_is_readable(fname_software))
|
||||
rc = abis_nm_bs11_load_swl(g_bts, fname_software,
|
||||
win_size, param_forced,
|
||||
swload_cbfn);
|
||||
else
|
||||
fprintf(stderr, "No valid Software file \"%s\"\n",
|
||||
fname_software);
|
||||
} else if (!strcmp(command, "delete-trx1")) {
|
||||
printf("Locing BBSIG and PA objects of TRX1\n");
|
||||
abis_nm_chg_adm_state(g_bts, NM_OC_BS11,
|
||||
BS11_OBJ_BBSIG, 0, 1,
|
||||
NM_STATE_LOCKED);
|
||||
abis_nm_chg_adm_state(g_bts, NM_OC_BS11,
|
||||
BS11_OBJ_PA, 0, 1,
|
||||
NM_STATE_LOCKED);
|
||||
sleep(1);
|
||||
printf("Deleting BBSIG and PA objects of TRX1\n");
|
||||
abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1);
|
||||
abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1);
|
||||
sleep(1);
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "create-trx1")) {
|
||||
create_trx1(g_bts);
|
||||
sleep(1);
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "pll-e1-locked")) {
|
||||
abis_nm_bs11_set_pll_locked(g_bts, 1);
|
||||
sleep(1);
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "pll-standalone")) {
|
||||
abis_nm_bs11_set_pll_locked(g_bts, 0);
|
||||
sleep(1);
|
||||
abis_nm_bs11_factory_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;
|
||||
} else if (!strcmp(command, "restart")) {
|
||||
abis_nm_bs11_restart(g_bts);
|
||||
command = NULL;
|
||||
} else if (!strcmp(command, "query")) {
|
||||
cmd_query();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BS11_STATE_NORMAL:
|
||||
if (command) {
|
||||
if (!strcmp(command, "reconnect"))
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
else if (!strcmp(command, "disconnect"))
|
||||
abis_nm_bs11_bsc_disconnect(g_bts, 0);
|
||||
else if (!strcmp(command, "query")) {
|
||||
cmd_query();
|
||||
}
|
||||
} else if (param_disconnect) {
|
||||
param_disconnect = 0;
|
||||
abis_nm_bs11_bsc_disconnect(g_bts, 0);
|
||||
if (param_restart) {
|
||||
param_restart = 0;
|
||||
abis_nm_bs11_restart(g_bts);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* handle a fully-received message/packet from the RS232 port */
|
||||
int handle_serial_msg(struct msgb *rx_msg)
|
||||
{
|
||||
struct abis_om_hdr *oh;
|
||||
struct abis_om_fom_hdr *foh;
|
||||
struct tlv_parsed tp;
|
||||
int rc = -1;
|
||||
|
||||
#if 0
|
||||
if (rx_msg->len < LAPD_HDR_LEN
|
||||
+ sizeof(struct abis_om_fom_hdr)
|
||||
+ sizeof(struct abis_om_hdr)) {
|
||||
if (!memcmp(rx_msg->data + 2, too_fast,
|
||||
sizeof(too_fast))) {
|
||||
fprintf(stderr, "BS11 tells us we're too "
|
||||
"fast, try --delay bigger than %u\n",
|
||||
delay_ms);
|
||||
return -E2BIG;
|
||||
} else
|
||||
fprintf(stderr, "unknown BS11 message\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
oh = (struct abis_om_hdr *) msgb_l2(rx_msg);
|
||||
foh = (struct abis_om_fom_hdr *) oh->data;
|
||||
switch (foh->msg_type) {
|
||||
case NM_MT_BS11_LMT_LOGON_ACK:
|
||||
printf("LMT LOGON: ACK\n\n");
|
||||
if (bs11cfg_state == STATE_NONE)
|
||||
bs11cfg_state = STATE_LOGON_ACK;
|
||||
rc = abis_nm_bs11_get_state(g_bts);
|
||||
break;
|
||||
case NM_MT_BS11_LMT_LOGOFF_ACK:
|
||||
printf("LMT LOGOFF: ACK\n");
|
||||
exit(0);
|
||||
break;
|
||||
case NM_MT_BS11_GET_STATE_ACK:
|
||||
rc = abis_nm_tlv_parse(&tp, 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)
|
||||
rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE));
|
||||
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));
|
||||
rc = print_attr(&tp);
|
||||
//hexdump(foh->data, oh->length-sizeof(*foh));
|
||||
break;
|
||||
case NM_MT_BS11_SET_ATTR_ACK:
|
||||
printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n",
|
||||
foh->obj_class, foh->obj_inst.bts_nr,
|
||||
foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
|
||||
rc = 0;
|
||||
break;
|
||||
case NM_MT_BS11_SET_ATTR_NACK:
|
||||
printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n",
|
||||
foh->obj_class, foh->obj_inst.bts_nr,
|
||||
foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
|
||||
break;
|
||||
default:
|
||||
rc = abis_nm_rcvmsg(rx_msg);
|
||||
}
|
||||
if (rc < 0) {
|
||||
perror("ERROR in main loop");
|
||||
//break;
|
||||
}
|
||||
if (rc == 1)
|
||||
return rc;
|
||||
|
||||
switch (bs11cfg_state) {
|
||||
case STATE_NONE:
|
||||
abis_nm_bs11_factory_logon(g_bts, 1);
|
||||
break;
|
||||
case STATE_LOGON_ACK:
|
||||
bsc_schedule_timer(&status_timer, 5, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void status_timer_cb(void *data)
|
||||
{
|
||||
abis_nm_bs11_get_state(g_bts);
|
||||
}
|
||||
|
||||
static void print_banner(void)
|
||||
{
|
||||
printf("bs11_config (C) 2009 by Harald Welte and Dieter Spaar\n");
|
||||
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
|
||||
}
|
||||
|
||||
static void print_help(void)
|
||||
{
|
||||
printf("bs11_config [options] [command]\n");
|
||||
printf("\nSupported options:\n");
|
||||
printf("\t-h --help\t\t\tPrint this help text\n");
|
||||
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 --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");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char **argv)
|
||||
{
|
||||
int option_index = 0;
|
||||
print_banner();
|
||||
|
||||
while (1) {
|
||||
int c;
|
||||
static struct option long_options[] = {
|
||||
{ "help", 0, 0, 'h' },
|
||||
{ "port", 1, 0, 'p' },
|
||||
{ "software", 1, 0, 's' },
|
||||
{ "safety", 1, 0, 'S' },
|
||||
{ "delay", 1, 0, 'd' },
|
||||
{ "disconnect", 0, 0, 'D' },
|
||||
{ "win-size", 1, 0, 'w' },
|
||||
{ "forced", 0, 0, 'f' },
|
||||
{ "restart", 0, 0, 'r' },
|
||||
{ "debug", 1, 0, 'b'},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:",
|
||||
long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help();
|
||||
exit(0);
|
||||
case 'p':
|
||||
serial_port = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
debug_parse_category_mask(optarg);
|
||||
break;
|
||||
case 's':
|
||||
fname_software = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
fname_safety = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
delay_ms = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
win_size = atoi(optarg);
|
||||
break;
|
||||
case 'D':
|
||||
param_disconnect = 1;
|
||||
break;
|
||||
case 'f':
|
||||
param_forced = 1;
|
||||
break;
|
||||
case 'r':
|
||||
param_disconnect = 1;
|
||||
param_restart = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (optind < argc)
|
||||
command = argv[optind];
|
||||
}
|
||||
|
||||
static int num_sigint;
|
||||
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
fprintf(stdout, "\nsignal %u received\n", signal);
|
||||
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
num_sigint++;
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
if (num_sigint >= 3)
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct gsm_network *gsmnet;
|
||||
int rc;
|
||||
|
||||
handle_options(argc, argv);
|
||||
|
||||
gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11);
|
||||
if (!gsmnet) {
|
||||
fprintf(stderr, "Unable to allocate gsm network\n");
|
||||
exit(1);
|
||||
}
|
||||
g_bts = &gsmnet->bts[0];
|
||||
|
||||
rc = rs232_setup(serial_port, delay_ms, g_bts);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Problem setting up serial port\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
|
||||
abis_nm_bs11_factory_logon(g_bts, 1);
|
||||
//abis_nm_bs11_get_serno(g_bts);
|
||||
|
||||
status_timer.cb = status_timer_cb;
|
||||
|
||||
while (1) {
|
||||
bsc_select_main(0);
|
||||
}
|
||||
|
||||
abis_nm_bs11_factory_logon(g_bts, 0);
|
||||
|
||||
exit(0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,256 @@
|
|||
/* GSM Channel allocation routines
|
||||
*
|
||||
* (C) 2008 by Harald Welte <laforge@gnumonks.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
|
||||
* 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 <openbsc/gsm_data.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
static void auto_release_channel(void *_lchan);
|
||||
|
||||
struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan)
|
||||
{
|
||||
struct gsm_bts_trx *trx = &bts->trx[0];
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[0];
|
||||
|
||||
if (pchan != GSM_PCHAN_CCCH &&
|
||||
pchan != GSM_PCHAN_CCCH_SDCCH4)
|
||||
return NULL;
|
||||
|
||||
if (ts->pchan != GSM_PCHAN_NONE)
|
||||
return NULL;
|
||||
|
||||
ts->pchan = pchan;
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
static const enum abis_nm_chan_comb chcomb4pchan[] = {
|
||||
[GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH,
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCCHComb,
|
||||
[GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull,
|
||||
[GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH,
|
||||
/* FIXME: bounds check */
|
||||
};
|
||||
|
||||
/* Allocate a logical channel (TS) */
|
||||
struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < bts->num_trx; i++) {
|
||||
struct gsm_bts_trx *trx = &bts->trx[i];
|
||||
int from, to;
|
||||
|
||||
/* the following constraints are pure policy,
|
||||
* no requirement to put this restriction in place */
|
||||
switch (pchan) {
|
||||
case GSM_PCHAN_CCCH:
|
||||
case GSM_PCHAN_CCCH_SDCCH4:
|
||||
from = 0; to = 0;
|
||||
break;
|
||||
case GSM_PCHAN_SDCCH8_SACCH8C:
|
||||
from = 1; to = 1;
|
||||
break;
|
||||
case GSM_PCHAN_TCH_F:
|
||||
case GSM_PCHAN_TCH_H:
|
||||
from = 2; to = 7;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (j = from; j <= to; j++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[j];
|
||||
if (ts->pchan == GSM_PCHAN_NONE) {
|
||||
ts->pchan = pchan;
|
||||
/* set channel attribute on OML */
|
||||
abis_nm_set_channel_attr(ts, chcomb4pchan[pchan]);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free a physical channel (TS) */
|
||||
void ts_free(struct gsm_bts_trx_ts *ts)
|
||||
{
|
||||
ts->pchan = GSM_PCHAN_NONE;
|
||||
}
|
||||
|
||||
static const u_int8_t subslots_per_pchan[] = {
|
||||
[GSM_PCHAN_NONE] = 0,
|
||||
[GSM_PCHAN_CCCH] = 0,
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = 4,
|
||||
[GSM_PCHAN_TCH_F] = 1,
|
||||
[GSM_PCHAN_TCH_H] = 2,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 8.
|
||||
};
|
||||
|
||||
static struct gsm_lchan *
|
||||
_lc_find(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
int i, j, ss;
|
||||
for (i = 0; i < bts->num_trx; i++) {
|
||||
trx = &bts->trx[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
ts = &trx->ts[j];
|
||||
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)
|
||||
return lc;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* we cannot allocate more of these */
|
||||
if (pchan == GSM_PCHAN_CCCH_SDCCH4)
|
||||
return NULL;
|
||||
|
||||
/* if we've reached here, we need to allocate a new physical
|
||||
* channel for the logical channel type requested */
|
||||
ts = ts_alloc(bts, pchan);
|
||||
if (!ts) {
|
||||
/* no more radio resources */
|
||||
return NULL;
|
||||
}
|
||||
return &ts->lchan[0];
|
||||
}
|
||||
|
||||
/* Allocate a logical channel */
|
||||
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
{
|
||||
struct gsm_lchan *lchan = NULL;
|
||||
|
||||
switch (type) {
|
||||
case GSM_LCHAN_SDCCH:
|
||||
lchan = _lc_find(bts, GSM_PCHAN_CCCH_SDCCH4);
|
||||
if (lchan == NULL)
|
||||
lchan = _lc_find(bts, GSM_PCHAN_SDCCH8_SACCH8C);
|
||||
break;
|
||||
case GSM_LCHAN_TCH_F:
|
||||
lchan = _lc_find(bts, GSM_PCHAN_TCH_F);
|
||||
break;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
lchan =_lc_find(bts, GSM_PCHAN_TCH_H);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
|
||||
}
|
||||
|
||||
if (lchan) {
|
||||
lchan->type = type;
|
||||
lchan->use_count = 0;
|
||||
|
||||
/* Configure the time and start it so it will be closed */
|
||||
lchan->release_timer.cb = auto_release_channel;
|
||||
lchan->release_timer.data = lchan;
|
||||
bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
|
||||
}
|
||||
|
||||
return lchan;
|
||||
}
|
||||
|
||||
/* Free a logical channel */
|
||||
void lchan_free(struct gsm_lchan *lchan)
|
||||
{
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
if (lchan->subscr) {
|
||||
subscr_put(lchan->subscr);
|
||||
lchan->subscr = 0;
|
||||
}
|
||||
|
||||
/* We might kill an active channel... */
|
||||
if (lchan->use_count != 0) {
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan);
|
||||
lchan->use_count = 0;
|
||||
}
|
||||
|
||||
/* stop the timer */
|
||||
bsc_del_timer(&lchan->release_timer);
|
||||
|
||||
/* FIXME: ts_free() the timeslot, if we're the last logical
|
||||
* channel using it */
|
||||
}
|
||||
|
||||
/* Consider releasing the channel now */
|
||||
int lchan_auto_release(struct gsm_lchan *lchan)
|
||||
{
|
||||
if (lchan->use_count > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assume we have GSM04.08 running and send a release */
|
||||
if (lchan->subscr) {
|
||||
gsm48_send_rr_release(lchan);
|
||||
}
|
||||
|
||||
/* spoofed? message */
|
||||
if (lchan->use_count < 0) {
|
||||
DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count);
|
||||
}
|
||||
|
||||
DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
|
||||
rsl_chan_release(lchan);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Auto release the channel when the use count is zero */
|
||||
static void auto_release_channel(void *_lchan)
|
||||
{
|
||||
struct gsm_lchan *lchan = _lchan;
|
||||
|
||||
if (!lchan_auto_release(lchan))
|
||||
bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
|
||||
}
|
||||
|
||||
struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) {
|
||||
int trx, ts_no, lchan_no;
|
||||
|
||||
for (trx = 0; trx < bts->num_trx; ++trx) {
|
||||
for (ts_no = 0; ts_no < 8; ++ts_no) {
|
||||
for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) {
|
||||
struct gsm_lchan *lchan =
|
||||
&bts->trx[trx].ts[ts_no].lchan[lchan_no];
|
||||
if (subscr == lchan->subscr)
|
||||
return lchan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,464 @@
|
|||
/* Simple HLR/VLR database backend using dbi */
|
||||
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
|
||||
* (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/gsm_data.h>
|
||||
#include <openbsc/db.h>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <dbi/dbi.h>
|
||||
|
||||
static char *db_basename = NULL;
|
||||
static char *db_dirname = NULL;
|
||||
static dbi_conn conn;
|
||||
|
||||
static char *create_stmts[] = {
|
||||
"CREATE TABLE IF NOT EXISTS Meta ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"key TEXT UNIQUE NOT NULL, "
|
||||
"value TEXT NOT NULL"
|
||||
")",
|
||||
"INSERT OR IGNORE INTO Meta "
|
||||
"(key, value) "
|
||||
"VALUES "
|
||||
"('revision', '1')",
|
||||
"CREATE TABLE IF NOT EXISTS Subscriber ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"created TIMESTAMP NOT NULL, "
|
||||
"updated TIMESTAMP NOT NULL, "
|
||||
"imsi NUMERIC UNIQUE NOT NULL, "
|
||||
"name TEXT, "
|
||||
"extension TEXT UNIQUE, "
|
||||
"authorized INTEGER NOT NULL DEFAULT 0, "
|
||||
"tmsi TEXT UNIQUE, "
|
||||
"lac INTEGER NOT NULL DEFAULT 0"
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS Equipment ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"created TIMESTAMP NOT NULL, "
|
||||
"updated TIMESTAMP NOT NULL, "
|
||||
"name TEXT, "
|
||||
"imei NUMERIC UNIQUE NOT NULL"
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS EquipmentWatch ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"created TIMESTAMP NOT NULL, "
|
||||
"updated TIMESTAMP NOT NULL, "
|
||||
"subscriber_id NUMERIC NOT NULL, "
|
||||
"equipment_id NUMERIC NOT NULL, "
|
||||
"UNIQUE (subscriber_id, equipment_id) "
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS SMS ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"created TIMESTAMP NOT NULL, "
|
||||
"sent TIMESTAMP, "
|
||||
"sender_id NUMERIC NOT NULL, "
|
||||
"receiver_id NUMERIC NOT NULL, "
|
||||
"header NUMERIC, "
|
||||
"text TEXT NOT NULL "
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS VLR ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"created TIMESTAMP NOT NULL, "
|
||||
"updated TIMESTAMP NOT NULL, "
|
||||
"subscriber_id NUMERIC UNIQUE NOT NULL, "
|
||||
"last_bts NUMERIC NOT NULL "
|
||||
")",
|
||||
};
|
||||
|
||||
void db_error_func(dbi_conn conn, void* data) {
|
||||
const char* msg;
|
||||
dbi_conn_error(conn, &msg);
|
||||
printf("DBI: %s\n", msg);
|
||||
}
|
||||
|
||||
int db_init(const char *name) {
|
||||
dbi_initialize(NULL);
|
||||
conn = dbi_conn_new("sqlite3");
|
||||
if (conn==NULL) {
|
||||
printf("DB: Failed to create connection.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbi_conn_error_handler( conn, db_error_func, NULL );
|
||||
|
||||
/* MySQL
|
||||
dbi_conn_set_option(conn, "host", "localhost");
|
||||
dbi_conn_set_option(conn, "username", "your_name");
|
||||
dbi_conn_set_option(conn, "password", "your_password");
|
||||
dbi_conn_set_option(conn, "dbname", "your_dbname");
|
||||
dbi_conn_set_option(conn, "encoding", "UTF-8");
|
||||
*/
|
||||
|
||||
/* SqLite 3 */
|
||||
db_basename = strdup(name);
|
||||
db_dirname = strdup(name);
|
||||
dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
|
||||
dbi_conn_set_option(conn, "dbname", basename(db_basename));
|
||||
|
||||
if (dbi_conn_connect(conn) < 0) {
|
||||
free(db_dirname);
|
||||
free(db_basename);
|
||||
db_dirname = db_basename = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
return 1;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_fini() {
|
||||
dbi_conn_close(conn);
|
||||
dbi_shutdown();
|
||||
|
||||
if (db_dirname)
|
||||
free(db_dirname);
|
||||
if (db_basename)
|
||||
free(db_basename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_subscriber* db_create_subscriber(char *imsi) {
|
||||
dbi_result result;
|
||||
struct gsm_subscriber* subscr;
|
||||
|
||||
/* Is this subscriber known in the db? */
|
||||
subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
|
||||
if (subscr) {
|
||||
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 {
|
||||
dbi_result_free(result);
|
||||
}
|
||||
return subscr;
|
||||
}
|
||||
|
||||
subscr = subscr_alloc();
|
||||
if (!subscr)
|
||||
return NULL;
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO Subscriber "
|
||||
"(imsi, created, updated) "
|
||||
"VALUES "
|
||||
"(%s, datetime('now'), datetime('now')) ",
|
||||
imsi
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to create Subscriber by IMSI.\n");
|
||||
}
|
||||
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);
|
||||
return subscr;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, const char *id) {
|
||||
dbi_result result;
|
||||
const char *string;
|
||||
char *quoted;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
switch (field) {
|
||||
case GSM_SUBSCRIBER_IMSI:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE imsi = %s ",
|
||||
quoted
|
||||
);
|
||||
free(quoted);
|
||||
break;
|
||||
case GSM_SUBSCRIBER_TMSI:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE tmsi = %s ",
|
||||
quoted
|
||||
);
|
||||
free(quoted);
|
||||
break;
|
||||
case GSM_SUBSCRIBER_EXTENSION:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE extension = %s ",
|
||||
quoted
|
||||
);
|
||||
free(quoted);
|
||||
break;
|
||||
default:
|
||||
printf("DB: Unknown query selector for Subscriber.\n");
|
||||
return NULL;
|
||||
}
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber.\n");
|
||||
return NULL;
|
||||
}
|
||||
if (!dbi_result_next_row(result)) {
|
||||
printf("DB: Failed to find the Subscriber. '%u' '%s'\n",
|
||||
field, id);
|
||||
dbi_result_free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
subscr = subscr_alloc();
|
||||
subscr->id = dbi_result_get_ulonglong(result, "id");
|
||||
string = dbi_result_get_string(result, "imsi");
|
||||
if (string)
|
||||
strncpy(subscr->imsi, string, GSM_IMSI_LENGTH);
|
||||
|
||||
string = dbi_result_get_string(result, "tmsi");
|
||||
if (string)
|
||||
strncpy(subscr->tmsi, string, GSM_TMSI_LENGTH);
|
||||
|
||||
string = dbi_result_get_string(result, "name");
|
||||
if (string)
|
||||
strncpy(subscr->name, string, GSM_NAME_LENGTH);
|
||||
|
||||
string = dbi_result_get_string(result, "extension");
|
||||
if (string)
|
||||
strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH);
|
||||
|
||||
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 %s, 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);
|
||||
return subscr;
|
||||
}
|
||||
|
||||
int db_sync_subscriber(struct gsm_subscriber* subscriber) {
|
||||
dbi_result result;
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE Subscriber "
|
||||
"SET updated = datetime('now'), "
|
||||
"tmsi = '%s', "
|
||||
"lac = %i, "
|
||||
"authorized = %i "
|
||||
"WHERE imsi = %s ",
|
||||
subscriber->tmsi, subscriber->lac, subscriber->authorized, subscriber->imsi
|
||||
);
|
||||
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to update Subscriber (by IMSI).\n");
|
||||
return 1;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
|
||||
dbi_result result=NULL;
|
||||
char* tmsi_quoted;
|
||||
for (;;) {
|
||||
sprintf(subscriber->tmsi, "%i", rand());
|
||||
dbi_conn_quote_string_copy(conn, subscriber->tmsi, &tmsi_quoted);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
"WHERE tmsi = %s ",
|
||||
tmsi_quoted
|
||||
);
|
||||
free(tmsi_quoted);
|
||||
if (result==NULL) {
|
||||
printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
|
||||
return 1;
|
||||
}
|
||||
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 %s for IMSI %s.\n", subscriber->tmsi, subscriber->imsi);
|
||||
return db_sync_subscriber(subscriber);
|
||||
}
|
||||
dbi_result_free(result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) {
|
||||
u_int64_t equipment_id, watch_id;
|
||||
dbi_result result;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT OR IGNORE INTO Equipment "
|
||||
"(imei, created, updated) "
|
||||
"VALUES "
|
||||
"(%s, datetime('now'), datetime('now')) ",
|
||||
imei
|
||||
);
|
||||
if (result==NULL) {
|
||||
printf("DB: 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);
|
||||
}
|
||||
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");
|
||||
return 1;
|
||||
}
|
||||
if (!dbi_result_next_row(result)) {
|
||||
printf("DB: Failed to find the Equipment.\n");
|
||||
dbi_result_free(result);
|
||||
return 1;
|
||||
}
|
||||
equipment_id = dbi_result_get_ulonglong(result, "id");
|
||||
dbi_result_free(result);
|
||||
}
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT OR IGNORE INTO EquipmentWatch "
|
||||
"(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");
|
||||
return 1;
|
||||
}
|
||||
watch_id = 0;
|
||||
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);
|
||||
}
|
||||
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");
|
||||
return 1;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* store an [unsent] SMS to the database */
|
||||
int db_sms_store(struct gsm_sms *sms)
|
||||
{
|
||||
dbi_result result;
|
||||
char *q_text;
|
||||
|
||||
dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO SMS "
|
||||
"(created,sender_id,receiver_id,header,text) VALUES "
|
||||
"(datetime('now'),%llu,%llu,%s,%s)",
|
||||
sms->sender->id,
|
||||
sms->receiver ? sms->receiver->id : 0,
|
||||
NULL, q_text);
|
||||
free(q_text);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* retrieve the next unsent SMS with ID >= min_id */
|
||||
struct gsm_sms *db_sms_get_unsent(int min_id)
|
||||
{
|
||||
dbi_result result;
|
||||
struct gsm_sms *sms = malloc(sizeof(*sms));
|
||||
|
||||
if (!sms) {
|
||||
free(sms);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM SMS "
|
||||
"WHERE id >= %llu ORDER BY id", min_id);
|
||||
if (!result) {
|
||||
free(sms);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: fill gsm_sms from database */
|
||||
|
||||
dbi_result_free(result);
|
||||
return sms;
|
||||
}
|
||||
|
||||
/* mark a given SMS as read */
|
||||
int db_sms_mark_sent(struct gsm_sms *sms)
|
||||
{
|
||||
dbi_result result;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE SMS "
|
||||
"SET sent = datetime('now') "
|
||||
"WHERE id = %llu", sms->id);
|
||||
if (!result) {
|
||||
printf("DB: Failed to mark SMS %llu as sent.\n", sms->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/* Debugging/Logging support code */
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2008 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 <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB);
|
||||
|
||||
struct debug_info {
|
||||
const char *name;
|
||||
const char *color;
|
||||
const char *description;
|
||||
int number;
|
||||
};
|
||||
|
||||
#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", "")
|
||||
DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "")
|
||||
DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "")
|
||||
DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "")
|
||||
DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "")
|
||||
DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "")
|
||||
DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "")
|
||||
DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "")
|
||||
DEBUG_CATEGORY(DINP, "DINP", "", "")
|
||||
DEBUG_CATEGORY(DMI, "DMI", "", "")
|
||||
DEBUG_CATEGORY(DMIB, "DMIB", "", "")
|
||||
DEBUG_CATEGORY(DMUX, "DMUX", "", "")
|
||||
};
|
||||
|
||||
static int use_color = 1;
|
||||
|
||||
void debug_use_color(int color)
|
||||
{
|
||||
use_color = color;
|
||||
}
|
||||
|
||||
static int print_timestamp = 0;
|
||||
|
||||
void debug_timestamp(int enable)
|
||||
{
|
||||
print_timestamp = enable;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse the category mask.
|
||||
* category1:category2:category3
|
||||
*/
|
||||
void debug_parse_category_mask(const char *_mask)
|
||||
{
|
||||
unsigned int new_mask = 0;
|
||||
int i = 0;
|
||||
char *mask = strdup(_mask);
|
||||
char *category_token = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
} while ((category_token = strtok(NULL, ":")));
|
||||
|
||||
|
||||
free(mask);
|
||||
debug_mask = new_mask;
|
||||
}
|
||||
|
||||
const char* color(int subsys)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) {
|
||||
if (debug_info[i].number == subsys)
|
||||
return debug_info[i].color;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
FILE *outfd = stderr;
|
||||
|
||||
if (!(debug_mask & subsys))
|
||||
return;
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
fprintf(outfd, "%s", color(subsys));
|
||||
|
||||
if (!cont) {
|
||||
if (print_timestamp) {
|
||||
char *timestr;
|
||||
time_t tm;
|
||||
tm = time(NULL);
|
||||
timestr = ctime(&tm);
|
||||
timestr[strlen(timestr)-1] = '\0';
|
||||
fprintf(outfd, "%s ", timestr);
|
||||
}
|
||||
fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line);
|
||||
}
|
||||
vfprintf(outfd, format, ap);
|
||||
fprintf(outfd, "\033[0;m");
|
||||
|
||||
va_end(ap);
|
||||
|
||||
fflush(outfd);
|
||||
}
|
||||
|
||||
static char hexd_buff[4096];
|
||||
|
||||
char *hexdump(unsigned char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
char *cur = hexd_buff;
|
||||
|
||||
hexd_buff[0] = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
|
||||
int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
|
||||
if (rc <= 0)
|
||||
break;
|
||||
cur += rc;
|
||||
}
|
||||
hexd_buff[sizeof(hexd_buff)-1] = 0;
|
||||
return hexd_buff;
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/trau_frame.h>
|
||||
#include <openbsc/trau_mux.h>
|
||||
#include <openbsc/misdn.h>
|
||||
|
||||
#define SAPI_L2ML 0
|
||||
#define SAPI_OML 62
|
||||
#define SAPI_RSL 0 /* 63 ? */
|
||||
|
||||
#define TEI_L2ML 127
|
||||
#define TEI_OML 25
|
||||
#define TEI_RSL 1
|
||||
|
||||
/* do some compiled-in configuration for our BTS/E1 setup */
|
||||
int e1_config(struct gsm_bts *bts, int cardnr, int release_l2)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *sign_ts;
|
||||
struct e1inp_sign_link *oml_link, *rsl_link;
|
||||
|
||||
line = malloc(sizeof(*line));
|
||||
if (!line)
|
||||
return -ENOMEM;
|
||||
memset(line, 0, sizeof(*line));
|
||||
|
||||
/* create E1 timeslots for signalling and TRAU frames */
|
||||
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
|
||||
e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_TRAU);
|
||||
e1inp_ts_config(&line->ts[3-1], line, E1INP_TS_TYPE_TRAU);
|
||||
|
||||
/* create signalling links for TS1 */
|
||||
sign_ts = &line->ts[1-1];
|
||||
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
|
||||
bts->c0, TEI_OML, SAPI_OML);
|
||||
rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
|
||||
bts->c0, TEI_RSL, SAPI_RSL);
|
||||
|
||||
/* create back-links from bts/trx */
|
||||
bts->oml_link = oml_link;
|
||||
bts->c0->rsl_link = rsl_link;
|
||||
|
||||
/* enable subchannel demuxer on TS2 */
|
||||
subch_demux_activate(&line->ts[2-1].trau.demux, 1);
|
||||
subch_demux_activate(&line->ts[2-1].trau.demux, 2);
|
||||
subch_demux_activate(&line->ts[2-1].trau.demux, 3);
|
||||
|
||||
/* enable subchannel demuxer on TS3 */
|
||||
subch_demux_activate(&line->ts[3-1].trau.demux, 0);
|
||||
subch_demux_activate(&line->ts[3-1].trau.demux, 1);
|
||||
subch_demux_activate(&line->ts[3-1].trau.demux, 2);
|
||||
subch_demux_activate(&line->ts[3-1].trau.demux, 3);
|
||||
|
||||
#ifdef HAVE_TRX1
|
||||
/* create E1 timeslots for TRAU frames of TRX1 */
|
||||
e1inp_ts_config(&line->ts[4-1], line, E1INP_TS_TYPE_TRAU);
|
||||
e1inp_ts_config(&line->ts[5-1], line, E1INP_TS_TYPE_TRAU);
|
||||
|
||||
/* create RSL signalling link for TRX1 */
|
||||
sign_ts = &line->ts[1-1];
|
||||
rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
|
||||
&bts->trx[1], TEI_RSL+1, SAPI_RSL);
|
||||
/* create back-links from trx */
|
||||
bts->trx[1].rsl_link = rsl_link;
|
||||
#endif
|
||||
|
||||
return mi_setup(cardnr, line, release_l2);
|
||||
}
|
||||
|
||||
/* configure pseudo E1 line in ip.access style and connect to BTS */
|
||||
int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *sign_ts, *rsl_ts;
|
||||
struct e1inp_sign_link *oml_link, *rsl_link;
|
||||
|
||||
line = malloc(sizeof(*line));
|
||||
if (!line)
|
||||
return NULL;
|
||||
memset(line, 0, sizeof(*line));
|
||||
|
||||
/* create E1 timeslots for signalling and TRAU frames */
|
||||
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
|
||||
e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
|
||||
|
||||
/* create signalling links for TS1 */
|
||||
sign_ts = &line->ts[1-1];
|
||||
rsl_ts = &line->ts[2-1];
|
||||
oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
|
||||
bts->c0, 0, 0xff);
|
||||
rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
|
||||
bts->c0, 0, 0);
|
||||
|
||||
/* create back-links from bts/trx */
|
||||
bts->oml_link = oml_link;
|
||||
bts->c0->rsl_link = rsl_link;
|
||||
|
||||
/* default port at BTS for incoming connections is 3006 */
|
||||
if (sin->sin_port == 0)
|
||||
sin->sin_port = htons(3006);
|
||||
|
||||
return ipaccess_connect(line, sin);
|
||||
}
|
|
@ -0,0 +1,498 @@
|
|||
/* OpenBSC Abis interface to E1 */
|
||||
|
||||
/* (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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <mISDNif.h>
|
||||
|
||||
//#define AF_COMPATIBILITY_FUNC
|
||||
//#include <compat_af_isdn.h>
|
||||
#ifndef AF_ISDN
|
||||
#define AF_ISDN 34
|
||||
#define PF_ISDN AF_ISDN
|
||||
#endif
|
||||
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/subchan_demux.h>
|
||||
#include <openbsc/trau_frame.h>
|
||||
#include <openbsc/trau_mux.h>
|
||||
|
||||
#define NUM_E1_TS 32
|
||||
|
||||
/* list of all E1 drivers */
|
||||
LLIST_HEAD(e1inp_driver_list);
|
||||
|
||||
/* list of all E1 lines */
|
||||
LLIST_HEAD(e1inp_line_list);
|
||||
|
||||
/* to be implemented, e.g. by bsc_hack.c */
|
||||
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx);
|
||||
|
||||
/*
|
||||
* pcap writing of the misdn load
|
||||
* pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
*/
|
||||
#define DLT_LINUX_LAPD 177
|
||||
#define PCAP_INPUT 0
|
||||
#define PCAP_OUTPUT 1
|
||||
|
||||
struct pcap_hdr {
|
||||
u_int32_t magic_number;
|
||||
u_int16_t version_major;
|
||||
u_int16_t version_minor;
|
||||
int32_t thiszone;
|
||||
u_int32_t sigfigs;
|
||||
u_int32_t snaplen;
|
||||
u_int32_t network;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pcaprec_hdr {
|
||||
u_int32_t ts_sec;
|
||||
u_int32_t ts_usec;
|
||||
u_int32_t incl_len;
|
||||
u_int32_t orig_len;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct fake_linux_lapd_header {
|
||||
u_int16_t pkttype;
|
||||
u_int16_t hatype;
|
||||
u_int16_t halen;
|
||||
u_int64_t addr;
|
||||
int16_t protocol;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct lapd_header {
|
||||
u_int8_t ea1 : 1;
|
||||
u_int8_t cr : 1;
|
||||
u_int8_t sapi : 6;
|
||||
u_int8_t ea2 : 1;
|
||||
u_int8_t tei : 7;
|
||||
u_int8_t control_foo; /* fake UM's ... */
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset);
|
||||
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset);
|
||||
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset);
|
||||
static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset);
|
||||
static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size);
|
||||
|
||||
|
||||
static int pcap_fd = -1;
|
||||
|
||||
void e1_set_pcap_fd(int fd)
|
||||
{
|
||||
int ret;
|
||||
struct pcap_hdr header = {
|
||||
.magic_number = 0xa1b2c3d4,
|
||||
.version_major = 2,
|
||||
.version_minor = 4,
|
||||
.thiszone = 0,
|
||||
.sigfigs = 0,
|
||||
.snaplen = 65535,
|
||||
.network = DLT_LINUX_LAPD,
|
||||
};
|
||||
|
||||
pcap_fd = fd;
|
||||
ret = write(pcap_fd, &header, sizeof(header));
|
||||
}
|
||||
|
||||
/* This currently only works for the D-Channel */
|
||||
static void write_pcap_packet(int direction, int sapi, int tei,
|
||||
struct msgb *msg) {
|
||||
if (pcap_fd < 0)
|
||||
return;
|
||||
|
||||
int ret;
|
||||
time_t cur_time;
|
||||
struct tm *tm;
|
||||
|
||||
struct fake_linux_lapd_header header = {
|
||||
.pkttype = 4,
|
||||
.hatype = 0,
|
||||
.halen = 0,
|
||||
.addr = direction == PCAP_OUTPUT ? 0x0 : 0x1,
|
||||
.protocol = ntohs(48),
|
||||
};
|
||||
|
||||
struct lapd_header lapd_header = {
|
||||
.ea1 = 0,
|
||||
.cr = direction == PCAP_OUTPUT ? 1 : 0,
|
||||
.sapi = sapi & 0x3F,
|
||||
.ea2 = 1,
|
||||
.tei = tei & 0x7F,
|
||||
.control_foo = 0x03 /* UI */,
|
||||
};
|
||||
|
||||
struct pcaprec_hdr payload_header = {
|
||||
.ts_sec = 0,
|
||||
.ts_usec = 0,
|
||||
.incl_len = msg->len + sizeof(struct fake_linux_lapd_header)
|
||||
+ sizeof(struct lapd_header)
|
||||
- MISDN_HEADER_LEN,
|
||||
.orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
|
||||
+ sizeof(struct lapd_header)
|
||||
- MISDN_HEADER_LEN,
|
||||
};
|
||||
|
||||
|
||||
cur_time = time(NULL);
|
||||
tm = localtime(&cur_time);
|
||||
payload_header.ts_sec = mktime(tm);
|
||||
|
||||
ret = write(pcap_fd, &payload_header, sizeof(payload_header));
|
||||
ret = write(pcap_fd, &header, sizeof(header));
|
||||
ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
|
||||
ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
|
||||
msg->len - MISDN_HEADER_LEN);
|
||||
}
|
||||
|
||||
static const char *sign_types[] = {
|
||||
[E1INP_SIGN_NONE] = "None",
|
||||
[E1INP_SIGN_OML] = "OML",
|
||||
[E1INP_SIGN_RSL] = "RSL",
|
||||
};
|
||||
const char *e1inp_signtype_name(enum e1inp_sign_type tp)
|
||||
{
|
||||
if (tp >= ARRAY_SIZE(sign_types))
|
||||
return "undefined";
|
||||
return sign_types[tp];
|
||||
}
|
||||
|
||||
static const char *ts_types[] = {
|
||||
[E1INP_TS_TYPE_NONE] = "None",
|
||||
[E1INP_TS_TYPE_SIGN] = "Signalling",
|
||||
[E1INP_TS_TYPE_TRAU] = "TRAU",
|
||||
};
|
||||
|
||||
const char *e1inp_tstype_name(enum e1inp_ts_type tp)
|
||||
{
|
||||
if (tp >= ARRAY_SIZE(ts_types))
|
||||
return "undefined";
|
||||
return ts_types[tp];
|
||||
}
|
||||
|
||||
/* callback when a TRAU frame was received */
|
||||
static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
|
||||
void *_priv)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = _priv;
|
||||
struct gsm_e1_subslot src_ss;
|
||||
|
||||
src_ss.e1_nr = e1i_ts->line->num;
|
||||
src_ss.e1_ts = e1i_ts->num;
|
||||
src_ss.e1_ts_ss = ch;
|
||||
|
||||
return trau_mux_input(&src_ss, data, len);
|
||||
}
|
||||
|
||||
int abis_rsl_sendmsg(struct msgb *msg)
|
||||
{
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct e1inp_driver *e1inp_driver;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
|
||||
if (!msg->trx || !msg->trx->rsl_link) {
|
||||
fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sign_link = msg->trx->rsl_link;
|
||||
e1i_ts = sign_link->ts;
|
||||
if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) {
|
||||
/* notify the driver we have something to write */
|
||||
e1inp_driver = sign_link->ts->line->driver;
|
||||
e1inp_driver->want_write(e1i_ts);
|
||||
}
|
||||
msgb_enqueue(&sign_link->tx_list, msg);
|
||||
|
||||
/* dump it */
|
||||
write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _abis_nm_sendmsg(struct msgb *msg)
|
||||
{
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct e1inp_driver *e1inp_driver;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
|
||||
if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
|
||||
fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sign_link = msg->trx->bts->oml_link;
|
||||
e1i_ts = sign_link->ts;
|
||||
if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) {
|
||||
/* notify the driver we have something to write */
|
||||
e1inp_driver = sign_link->ts->line->driver;
|
||||
e1inp_driver->want_write(e1i_ts);
|
||||
}
|
||||
msgb_enqueue(&sign_link->tx_list, msg);
|
||||
|
||||
/* dump it */
|
||||
write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Timeslot */
|
||||
|
||||
/* configure and initialize one e1inp_ts */
|
||||
int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
enum e1inp_ts_type type)
|
||||
{
|
||||
ts->type = type;
|
||||
ts->line = line;
|
||||
|
||||
switch (type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
INIT_LLIST_HEAD(&ts->sign.sign_links);
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
subchan_mux_init(&ts->trau.mux);
|
||||
ts->trau.demux.out_cb = subch_cb;
|
||||
ts->trau.demux.data = ts;
|
||||
subch_demux_init(&ts->trau.demux);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unsupported E1 timeslot type %u\n",
|
||||
ts->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr)
|
||||
{
|
||||
struct e1inp_line *e1i_line;
|
||||
|
||||
/* iterate over global list of e1 lines */
|
||||
llist_for_each_entry(e1i_line, &e1inp_line_list, list) {
|
||||
if (e1i_line->num == e1_nr)
|
||||
return e1i_line;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr)
|
||||
{
|
||||
struct e1inp_line *e1i_line;
|
||||
|
||||
e1i_line = e1inp_line_get(e1_nr);
|
||||
if (!e1i_line)
|
||||
return NULL;
|
||||
|
||||
return &e1i_line->ts[ts_nr-1];
|
||||
}
|
||||
|
||||
struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr);
|
||||
|
||||
if (!e1i_ts)
|
||||
return NULL;
|
||||
|
||||
return &e1i_ts->trau.mux;
|
||||
}
|
||||
|
||||
/* Signalling Link */
|
||||
|
||||
struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i,
|
||||
u_int8_t tei, u_int8_t sapi)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
|
||||
llist_for_each_entry(link, &e1i->sign.sign_links, list) {
|
||||
if (link->sapi == sapi && link->tei == tei)
|
||||
return link;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create a new signalling link in a E1 timeslot */
|
||||
|
||||
struct e1inp_sign_link *
|
||||
e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
|
||||
struct gsm_bts_trx *trx, u_int8_t tei,
|
||||
u_int8_t sapi)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
|
||||
if (ts->type != E1INP_TS_TYPE_SIGN)
|
||||
return NULL;
|
||||
|
||||
link = malloc(sizeof(*link));
|
||||
if (!link)
|
||||
return NULL;
|
||||
|
||||
memset(link, 0, sizeof(*link));
|
||||
|
||||
link->ts = ts;
|
||||
link->type = type;
|
||||
INIT_LLIST_HEAD(&link->tx_list);
|
||||
link->trx = trx;
|
||||
link->tei = tei;
|
||||
link->sapi = sapi;
|
||||
|
||||
llist_add_tail(&link->list, &ts->sign.sign_links);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
/* the E1 driver tells us he has received something on a TS */
|
||||
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
u_int8_t tei, u_int8_t sapi)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
int ret;
|
||||
|
||||
switch (ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
/* consult the list of signalling links */
|
||||
write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
|
||||
link = e1inp_lookup_sign_link(ts, tei, sapi);
|
||||
if (!link) {
|
||||
fprintf(stderr, "didn't find singalling link for "
|
||||
"tei %d, sapi %d\n", tei, sapi);
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
msg->trx = link->trx;
|
||||
ret = abis_nm_rcvmsg(msg);
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
msg->trx = link->trx;
|
||||
ret = abis_rsl_rcvmsg(msg);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
fprintf(stderr, "unknown link type %u\n", link->type);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg));
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
fprintf(stderr, "unknown TS type %u\n", ts->type);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TSX_ALLOC_SIZE 4096
|
||||
|
||||
/* called by driver if it wants to transmit on a given TS */
|
||||
struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
|
||||
struct e1inp_sign_link **sign_link)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
struct msgb *msg = NULL;
|
||||
int len;
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
/* FIXME: implement this round robin */
|
||||
llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) {
|
||||
msg = msgb_dequeue(&link->tx_list);
|
||||
if (msg) {
|
||||
if (sign_link)
|
||||
*sign_link = link;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
msg = msgb_alloc(TSX_ALLOC_SIZE);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40);
|
||||
msgb_put(msg, 40);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
|
||||
return NULL;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* called by driver in case some kind of link state event */
|
||||
int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
|
||||
link = e1inp_lookup_sign_link(ts, tei, sapi);
|
||||
if (!link)
|
||||
return -EINVAL;
|
||||
|
||||
/* FIXME: report further upwards */
|
||||
input_event(evt, link->type, link->trx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* register a driver with the E1 core */
|
||||
int e1inp_driver_register(struct e1inp_driver *drv)
|
||||
{
|
||||
llist_add_tail(&drv->list, &e1inp_driver_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* register a line with the E1 core */
|
||||
int e1inp_line_register(struct e1inp_line *line)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_E1_TS; i++) {
|
||||
line->ts[i].num = i+1;
|
||||
line->ts[i].line = line;
|
||||
}
|
||||
|
||||
llist_add_tail(&line->list, &e1inp_line_list);
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,500 @@
|
|||
/* Point-to-Point (PP) Short Message Service (SMS)
|
||||
* Support on Mobile Radio Interface
|
||||
* 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
|
||||
|
||||
/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
|
||||
* (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 <netinet/in.h>
|
||||
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/gsm_04_11.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/gsm_utils.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/db.h>
|
||||
|
||||
#define GSM411_ALLOC_SIZE 1024
|
||||
#define GSM411_ALLOC_HEADROOM 128
|
||||
|
||||
struct msgb *gsm411_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM);
|
||||
}
|
||||
|
||||
int gsm0411_sendmsg(struct msgb *msg)
|
||||
{
|
||||
if (msg->lchan)
|
||||
msg->trx = msg->lchan->ts->trx;
|
||||
|
||||
msg->l3h = msg->data;
|
||||
|
||||
return rsl_data_request(msg, 0);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static u_int8_t gsm0411_tpdu_from_sms(u_int8_t *tpdu, struct sms_deliver *sms)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned long gsm340_validity_period(struct sms_submit *sms)
|
||||
{
|
||||
u_int8_t vp;
|
||||
unsigned long minutes;
|
||||
|
||||
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 */
|
||||
/* FIXME: like service center time stamp */
|
||||
DEBUGP(DSMS, "VPI absolute not implemented yet\n");
|
||||
break;
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
/* Chapter 9.2.3.12.3 */
|
||||
/* FIXME: implementation */
|
||||
DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
|
||||
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 & 3) {
|
||||
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 sms_submit *sms,
|
||||
struct gsm_sms *gsms)
|
||||
{
|
||||
if (db_sms_store(gsms) != 0) {
|
||||
DEBUGP(DSMS, "Failed to store SMS in Database\n");
|
||||
free(sms);
|
||||
free(gsms);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* process an incoming TPDU (called from RP-DATA) */
|
||||
static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
{
|
||||
u_int8_t *smsp = msgb_sms(msg);
|
||||
struct sms_submit *sms;
|
||||
struct gsm_sms *gsms;
|
||||
u_int8_t da_len_bytes;
|
||||
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
|
||||
int rc = 0;
|
||||
|
||||
sms = malloc(sizeof(*sms));
|
||||
if (!sms)
|
||||
return -ENOMEM;
|
||||
memset(sms, 0, sizeof(*sms));
|
||||
|
||||
gsms = malloc(sizeof(*gsms));
|
||||
if (!gsms) {
|
||||
free(sms);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(gsms, 0, sizeof(*gsms));
|
||||
|
||||
/* invert those fields where 0 means active/present */
|
||||
sms->mti = *smsp & 0x03;
|
||||
sms->mms = !!(*smsp & 0x04);
|
||||
sms->vpf = (*smsp & 0x18) >> 3;
|
||||
sms->sri = !!(*smsp & 0x20);
|
||||
sms->udhi= !!(*smsp & 0x40);
|
||||
sms->rp = !!(*smsp & 0x80);
|
||||
|
||||
smsp++;
|
||||
sms->msg_ref = *smsp++;
|
||||
|
||||
/* 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");
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
memcpy(address_lv, smsp, da_len_bytes);
|
||||
/* mangle first byte to reflect length in bytes, not digits */
|
||||
address_lv[0] = da_len_bytes;
|
||||
/* convert to real number */
|
||||
decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv);
|
||||
|
||||
smsp += da_len_bytes;
|
||||
|
||||
sms->pid = *smsp++;
|
||||
|
||||
sms->dcs = *smsp++;
|
||||
sms->alphabet = gsm338_get_sms_alphabet(sms->dcs);
|
||||
|
||||
switch (sms->vpf) {
|
||||
case GSM340_TP_VPF_RELATIVE:
|
||||
sms->vp = smsp++;
|
||||
break;
|
||||
case GSM340_TP_VPF_ABSOLUTE:
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
sms->vp = smsp;
|
||||
smsp += 7;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
|
||||
sms->vpf);
|
||||
}
|
||||
sms->ud_len = *smsp++;
|
||||
if (sms->ud_len)
|
||||
sms->user_data = smsp;
|
||||
else
|
||||
sms->user_data = NULL;
|
||||
|
||||
if (sms->ud_len) {
|
||||
switch (sms->alphabet) {
|
||||
case DCS_7BIT_DEFAULT:
|
||||
gsm_7bit_decode(sms->decoded, smsp, sms->ud_len);
|
||||
break;
|
||||
case DCS_8BIT_DATA:
|
||||
case DCS_UCS2:
|
||||
case DCS_NONE:
|
||||
memcpy(sms->decoded, sms->user_data, sms->ud_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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, sms->msg_ref,
|
||||
sms->pid, sms->dcs, sms->dest_addr, sms->ud_len,
|
||||
sms->alphabet == DCS_7BIT_DEFAULT ? sms->decoded : hexdump(sms->user_data, sms->ud_len));
|
||||
|
||||
dispatch_signal(SS_SMS, 0, sms);
|
||||
|
||||
gsms->sender = msg->lchan->subscr;
|
||||
/* FIXME: sender refcount */
|
||||
|
||||
/* determine gsms->receiver based on dialled number */
|
||||
gsms->receiver = subscr_get_by_extension(sms->dest_addr);
|
||||
if (!gsms->receiver) {
|
||||
rc = 1; /* cause 1: unknown subscriber */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sms->user_data)
|
||||
strncpy(gsms->text, sms->decoded, sizeof(gsms->text));
|
||||
|
||||
switch (sms->mti) {
|
||||
case GSM340_SMS_SUBMIT_MS2SC:
|
||||
/* MS is submitting a SMS */
|
||||
rc = gsm340_rx_sms_submit(msg, sms, gsms);
|
||||
break;
|
||||
case GSM340_SMS_COMMAND_MS2SC:
|
||||
case GSM340_SMS_DELIVER_REP_MS2SC:
|
||||
DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms->mti);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms->mti);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(gsms);
|
||||
free(sms);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int gsm411_send_rp_ack(struct gsm_lchan *lchan, u_int8_t trans_id,
|
||||
u_int8_t msg_ref)
|
||||
{
|
||||
struct msgb *msg = gsm411_msgb_alloc();
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm411_rp_hdr *rp;
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
// Outgoing needs the highest bit set
|
||||
gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
|
||||
gh->msg_type = GSM411_MT_CP_DATA;
|
||||
|
||||
rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
|
||||
rp->len = 2;
|
||||
rp->msg_type = GSM411_MT_RP_ACK_MT;
|
||||
rp->msg_ref = msg_ref;
|
||||
|
||||
DEBUGP(DSMS, "TX: SMS RP ACK\n");
|
||||
|
||||
return gsm0411_sendmsg(msg);
|
||||
}
|
||||
|
||||
static int gsm411_send_rp_error(struct gsm_lchan *lchan, u_int8_t trans_id,
|
||||
u_int8_t msg_ref, u_int8_t cause)
|
||||
{
|
||||
struct msgb *msg = gsm411_msgb_alloc();
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm411_rp_hdr *rp;
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
// Outgoing needs the highest bit set
|
||||
gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
|
||||
gh->msg_type = GSM411_MT_CP_DATA;
|
||||
|
||||
rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
|
||||
rp->msg_type = GSM411_MT_RP_ERROR_MT;
|
||||
rp->msg_ref = msg_ref;
|
||||
msgb_tv_put(msg, 1, cause);
|
||||
|
||||
DEBUGP(DSMS, "TX: SMS RP ERROR (cause %02d)\n", cause);
|
||||
|
||||
return gsm0411_sendmsg(msg);
|
||||
}
|
||||
|
||||
/* Receive a 04.11 TPDU inside RP-DATA / user data */
|
||||
static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm411_rp_hdr *rph,
|
||||
u_int8_t src_len, u_int8_t *src,
|
||||
u_int8_t dst_len, u_int8_t *dst,
|
||||
u_int8_t tpdu_len, u_int8_t *tpdu)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
u_int8_t trans_id = gh->proto_discr >> 4;
|
||||
int rc = 0;
|
||||
|
||||
if (src_len && src)
|
||||
DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
|
||||
|
||||
if (!dst_len || !dst || !tpdu_len || !tpdu) {
|
||||
DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
|
||||
return -EIO;
|
||||
}
|
||||
msg->smsh = tpdu;
|
||||
|
||||
DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
|
||||
//return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
|
||||
|
||||
rc = gsm340_rx_tpdu(msg);
|
||||
if (rc == 0)
|
||||
return gsm411_send_rp_ack(msg->lchan, trans_id, rph->msg_ref);
|
||||
else if (rc > 0)
|
||||
return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
|
||||
else
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */
|
||||
static int gsm411_rx_rp_data(struct msgb *msg, struct gsm411_rp_hdr *rph)
|
||||
{
|
||||
u_int8_t src_len, dst_len, rpud_len;
|
||||
u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL;
|
||||
|
||||
/* in the MO case, this should always be zero length */
|
||||
src_len = rph->data[0];
|
||||
if (src_len)
|
||||
src = &rph->data[1];
|
||||
|
||||
dst_len = rph->data[1+src_len];
|
||||
if (dst_len)
|
||||
dst = &rph->data[1+src_len+1];
|
||||
|
||||
rpud_len = rph->data[1+src_len+1+dst_len];
|
||||
if (rpud_len)
|
||||
rp_ud = &rph->data[1+src_len+1+dst_len+1];
|
||||
|
||||
DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", src_len, dst_len, rpud_len);
|
||||
return gsm411_rx_rp_ud(msg, rph, src_len, src, dst_len, dst,
|
||||
rpud_len, rp_ud);
|
||||
}
|
||||
|
||||
static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh)
|
||||
{
|
||||
struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
|
||||
u_int8_t msg_type = rp_data->msg_type & 0x07;
|
||||
int rc = 0;
|
||||
|
||||
switch (msg_type) {
|
||||
case GSM411_MT_RP_DATA_MO:
|
||||
DEBUGP(DSMS, "SMS RP-DATA (MO)\n");
|
||||
rc = gsm411_rx_rp_data(msg, rp_data);
|
||||
break;
|
||||
case GSM411_MT_RP_ACK_MO:
|
||||
/* Acnkowledgement to MT RP_DATA */
|
||||
case GSM411_MT_RP_ERROR_MO:
|
||||
/* Error in response to MT RP_DATA */
|
||||
case GSM411_MT_RP_SMMA_MO:
|
||||
/* MS tells us that it has memory for more SMS, we need
|
||||
* to check if we have any pending messages for it and then
|
||||
* transfer those */
|
||||
DEBUGP(DSMS, "Unimplemented RP type 0x%02x\n", msg_type);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int gsm0411_rcv_sms(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
u_int8_t msg_type = gh->msg_type;
|
||||
int rc = 0;
|
||||
|
||||
switch(msg_type) {
|
||||
case GSM411_MT_CP_DATA:
|
||||
DEBUGP(DSMS, "SMS CP-DATA\n");
|
||||
rc = gsm411_rx_cp_data(msg, gh);
|
||||
break;
|
||||
case GSM411_MT_CP_ACK:
|
||||
DEBUGP(DSMS, "SMS CP-ACK\n");
|
||||
break;
|
||||
case GSM411_MT_CP_ERROR:
|
||||
DEBUGP(DSMS, "SMS CP-ERROR, cause 0x%02x\n", gh->data[0]);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSMS, "Unimplemented CP msg_type: 0x%02x\n", msg_type);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Test TPDU - 25c3 welcome */
|
||||
#if 0
|
||||
static u_int8_t tpdu_test[] = {
|
||||
0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x92, 0x90, 0x32,
|
||||
0x24, 0x40, 0x4D, 0xB2, 0xDA, 0x70, 0xD6, 0x9A, 0x97, 0xE5, 0xF6, 0xF4,
|
||||
0xB8, 0x0C, 0x0A, 0xBB, 0xDD, 0xEF, 0xBA, 0x7B, 0x5C, 0x6E, 0x97, 0xDD,
|
||||
0x74, 0x1D, 0x08, 0xCA, 0x2E, 0x87, 0xE7, 0x65, 0x50, 0x98, 0x4E, 0x2F,
|
||||
0xBB, 0xC9, 0x20, 0x3A, 0xBA, 0x0C, 0x3A, 0x4E, 0x9B, 0x20, 0x7A, 0x98,
|
||||
0xBD, 0x06, 0x85, 0xE9, 0xA0, 0x58, 0x4C, 0x37, 0x83, 0x81, 0xD2, 0x6E,
|
||||
0xD0, 0x34, 0x1C, 0x66, 0x83, 0x62, 0x21, 0x90, 0xAE, 0x95, 0x02
|
||||
};
|
||||
#else
|
||||
/* Test TPDU - ALL YOUR */
|
||||
static u_int8_t tpdu_test[] = {
|
||||
0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24,
|
||||
0x32, 0x40, 0x1F, 0x41, 0x26, 0x13, 0x94, 0x7D, 0x56, 0xA5, 0x20, 0x28,
|
||||
0xF2, 0xE9, 0x2C, 0x82, 0x82, 0xD2, 0x22, 0x48, 0x58, 0x64, 0x3E, 0x9D,
|
||||
0x47, 0x10, 0xF5, 0x09, 0xAA, 0x4E, 0x01
|
||||
};
|
||||
#endif
|
||||
|
||||
int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms)
|
||||
{
|
||||
struct msgb *msg = gsm411_msgb_alloc();
|
||||
struct gsm48_hdr *gh;
|
||||
struct gsm411_rp_hdr *rp;
|
||||
u_int8_t *data;
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
gh->proto_discr = GSM48_PDISC_SMS;
|
||||
gh->msg_type = GSM411_MT_CP_DATA;
|
||||
|
||||
rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
|
||||
rp->len = sizeof(tpdu_test) + 10;
|
||||
rp->msg_type = GSM411_MT_RP_DATA_MT;
|
||||
rp->msg_ref = 42; /* FIXME: Choose randomly */
|
||||
/* Hardcode OA for now */
|
||||
data = (u_int8_t *)msgb_put(msg, 8);
|
||||
data[0] = 0x07;
|
||||
data[1] = 0x91;
|
||||
data[2] = 0x44;
|
||||
data[3] = 0x77;
|
||||
data[4] = 0x58;
|
||||
data[5] = 0x10;
|
||||
data[6] = 0x06;
|
||||
data[7] = 0x50;
|
||||
data = (u_int8_t *)msgb_put(msg, 1);
|
||||
data[0] = 0;
|
||||
|
||||
/* FIXME: Hardcoded for now */
|
||||
//smslen = gsm0411_tpdu_from_sms(tpdu, sms);
|
||||
|
||||
/* RPDU length */
|
||||
data = (u_int8_t *)msgb_put(msg, 1);
|
||||
data[0] = sizeof(tpdu_test);
|
||||
|
||||
data = (u_int8_t *)msgb_put(msg, sizeof(tpdu_test));
|
||||
|
||||
//memcpy(data, tpdu, smslen);
|
||||
memcpy(data, tpdu_test, sizeof(tpdu_test));
|
||||
|
||||
DEBUGP(DSMS, "TX: SMS SUBMIT\n");
|
||||
|
||||
return gsm0411_sendmsg(msg);
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/* (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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
|
||||
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)
|
||||
{
|
||||
ts->e1_link.e1_nr = e1_nr;
|
||||
ts->e1_link.e1_ts = e1_ts;
|
||||
ts->e1_link.e1_ts_ss = e1_ts_ss;
|
||||
}
|
||||
|
||||
static const char *pchan_names[] = {
|
||||
[GSM_PCHAN_NONE] = "NONE",
|
||||
[GSM_PCHAN_CCCH] = "CCCH",
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = "CCCH+SDCCH4",
|
||||
[GSM_PCHAN_TCH_F] = "TCH/F",
|
||||
[GSM_PCHAN_TCH_H] = "TCH/H",
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8",
|
||||
[GSM_PCHAN_UNKNOWN] = "UNKNOWN",
|
||||
};
|
||||
|
||||
const char *gsm_pchan_name(enum gsm_phys_chan_config c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(pchan_names))
|
||||
return "INVALID";
|
||||
|
||||
return pchan_names[c];
|
||||
}
|
||||
|
||||
static const char *lchan_names[] = {
|
||||
[GSM_LCHAN_NONE] = "NONE",
|
||||
[GSM_LCHAN_SDCCH] = "SDCCH",
|
||||
[GSM_LCHAN_TCH_F] = "TCH/F",
|
||||
[GSM_LCHAN_TCH_H] = "TCH/H",
|
||||
[GSM_LCHAN_UNKNOWN] = "UNKNOWN",
|
||||
};
|
||||
|
||||
const char *gsm_lchan_name(enum gsm_chan_t c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(lchan_names))
|
||||
return "INVALID";
|
||||
|
||||
return lchan_names[c];
|
||||
}
|
||||
|
||||
static const char *chreq_names[] = {
|
||||
[GSM_CHREQ_REASON_EMERG] = "EMERGENCY",
|
||||
[GSM_CHREQ_REASON_PAG] = "PAGING",
|
||||
[GSM_CHREQ_REASON_CALL] = "CALL",
|
||||
[GSM_CHREQ_REASON_LOCATION_UPD] = "LOCATION_UPDATE",
|
||||
[GSM_CHREQ_REASON_OTHER] = "OTHER",
|
||||
};
|
||||
|
||||
const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(chreq_names))
|
||||
return "INVALID";
|
||||
|
||||
return chreq_names[c];
|
||||
}
|
||||
|
||||
struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
|
||||
u_int16_t country_code, u_int16_t network_code)
|
||||
{
|
||||
int i;
|
||||
struct gsm_network *net;
|
||||
|
||||
if (num_bts > GSM_MAX_BTS)
|
||||
return NULL;
|
||||
|
||||
net = malloc(sizeof(*net));
|
||||
if (!net)
|
||||
return NULL;
|
||||
memset(net, 0, sizeof(*net));
|
||||
|
||||
net->country_code = country_code;
|
||||
net->network_code = network_code;
|
||||
net->num_bts = num_bts;
|
||||
|
||||
for (i = 0; i < num_bts; i++) {
|
||||
struct gsm_bts *bts = &net->bts[i];
|
||||
int j;
|
||||
|
||||
bts->network = net;
|
||||
bts->nr = i;
|
||||
bts->type = bts_type;
|
||||
bts->tsc = HARDCODED_TSC;
|
||||
bts->bsic = HARDCODED_BSIC;
|
||||
|
||||
for (j = 0; j < BTS_MAX_TRX; j++) {
|
||||
struct gsm_bts_trx *trx = &bts->trx[j];
|
||||
int k;
|
||||
|
||||
trx->bts = bts;
|
||||
trx->nr = j;
|
||||
|
||||
for (k = 0; k < 8; k++) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[k];
|
||||
int l;
|
||||
|
||||
ts->trx = trx;
|
||||
ts->nr = k;
|
||||
ts->pchan = GSM_PCHAN_NONE;
|
||||
|
||||
for (l = 0; l < TS_MAX_LCHAN; l++) {
|
||||
struct gsm_lchan *lchan;
|
||||
lchan = &ts->lchan[l];
|
||||
|
||||
lchan->ts = ts;
|
||||
lchan->nr = l;
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bts->num_trx = 1; /* FIXME */
|
||||
#ifdef HAVE_TRX1
|
||||
bts->num_trx++;
|
||||
#endif
|
||||
bts->c0 = &bts->trx[0];
|
||||
bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4;
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
static char ts2str[255];
|
||||
|
||||
char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
|
||||
{
|
||||
snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
|
||||
ts->trx->bts->bts_nr, ts->trx->nr, ts->nr);
|
||||
|
||||
return ts2str;
|
||||
}
|
||||
|
||||
static const char *bts_types[] = {
|
||||
[GSM_BTS_TYPE_UNKNOWN] = "unknown",
|
||||
[GSM_BTS_TYPE_BS11] = "bs11",
|
||||
[GSM_BTS_TYPE_NANOBTS_900] = "nanobts900",
|
||||
[GSM_BTS_TYPE_NANOBTS_1800] = "nanobts1800",
|
||||
};
|
||||
|
||||
enum gsm_bts_type parse_btstype(char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(bts_types); i++) {
|
||||
if (!strcmp(arg, bts_types[i]))
|
||||
return i;
|
||||
}
|
||||
return GSM_BTS_TYPE_BS11; /* Default: BS11 */
|
||||
}
|
||||
|
||||
char *btstype2str(enum gsm_bts_type type)
|
||||
{
|
||||
if (type > ARRAY_SIZE(bts_types))
|
||||
return "undefined";
|
||||
return bts_types[type];
|
||||
}
|
||||
|
||||
/* 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,
|
||||
struct gsm_bts *start_bts)
|
||||
{
|
||||
int i;
|
||||
struct gsm_bts *bts;
|
||||
int skip = 0;
|
||||
|
||||
if (start_bts)
|
||||
skip = 1;
|
||||
|
||||
for (i = 0; i < net->num_bts; i++) {
|
||||
bts = &net->bts[i];
|
||||
|
||||
if (skip) {
|
||||
if (start_bts == bts)
|
||||
skip = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bts->location_area_code == lac)
|
||||
return bts;
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */
|
||||
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
* (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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/db.h>
|
||||
|
||||
|
||||
LLIST_HEAD(active_subscribers);
|
||||
|
||||
struct gsm_subscriber *subscr_alloc(void)
|
||||
{
|
||||
struct gsm_subscriber *s;
|
||||
|
||||
s = malloc(sizeof(struct gsm_subscriber));
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
llist_add_tail(&s->entry, &active_subscribers);
|
||||
s->use_count = 1;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void subscr_free(struct gsm_subscriber *subscr)
|
||||
{
|
||||
llist_del(&subscr->entry);
|
||||
free(subscr);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* we might have a record in memory already */
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
if (strcmp(subscr->tmsi, tmsi) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return db_get_subscriber(GSM_SUBSCRIBER_TMSI, tmsi);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get_by_imsi(const char *imsi)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
if (strcmp(subscr->imsi, imsi) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get_by_extension(const char *ext)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
if (strcmp(subscr->extension, ext) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
return db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, ext);
|
||||
}
|
||||
|
||||
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
|
||||
{
|
||||
/* FIXME: Migrate pending requests from one BSC to another */
|
||||
switch (reason) {
|
||||
case GSM_SUBSCRIBER_UPDATE_ATTACHED:
|
||||
/* Indicate "attached to LAC" */
|
||||
s->lac = bts->location_area_code;
|
||||
break;
|
||||
case GSM_SUBSCRIBER_UPDATE_DETACHED:
|
||||
/* Only detach if we are currently in this area */
|
||||
if (bts->location_area_code == s->lac)
|
||||
s->lac = 0;
|
||||
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "subscr_update with unknown reason: %d\n",
|
||||
reason);
|
||||
break;
|
||||
};
|
||||
return db_sync_subscriber(s);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count++;
|
||||
DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
return subscr;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count--;
|
||||
DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
if (subscr->use_count <= 0)
|
||||
subscr_free(subscr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void subscr_put_channel(struct gsm_lchan *lchan)
|
||||
{
|
||||
/*
|
||||
* FIXME: Continue with other requests now... by checking
|
||||
* the gsm_subscriber inside the gsm_lchan. Drop the ref count
|
||||
* of the lchan after having asked the next requestee to handle
|
||||
* the channel.
|
||||
*/
|
||||
put_lchan(lchan);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (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 <openbsc/gsm_utils.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* GSM 03.38 6.2.1 Charachter packing */
|
||||
int gsm_7bit_decode(char *text, const u_int8_t *user_data, u_int8_t length)
|
||||
{
|
||||
u_int8_t d_off = 0, b_off = 0;
|
||||
u_int8_t i;
|
||||
|
||||
for (i=0;i<length;i++) {
|
||||
text[i] = ((user_data[d_off] + (user_data[d_off+1]<<8)) & (0x7f<<b_off))>>b_off;
|
||||
b_off += 7;
|
||||
if (b_off >= 8) {
|
||||
d_off += 1;
|
||||
b_off -= 8;
|
||||
}
|
||||
}
|
||||
text[i] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GSM 03.38 6.2.1 Charachter packing */
|
||||
int gsm_7bit_encode(u_int8_t *result, const char *data)
|
||||
{
|
||||
int i;
|
||||
u_int8_t d_off = 0, b_off = 0;
|
||||
const int length = strlen(data);
|
||||
int out_length = (length * 8)/7;
|
||||
|
||||
memset(result, 0, out_length);
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
u_int8_t first = (data[i] & 0x7f) << b_off;
|
||||
u_int8_t second = (data[i] & 0x7f) >> (8 - b_off);
|
||||
|
||||
result[d_off] |= first;
|
||||
if (second != 0)
|
||||
result[d_off + 1] = second;
|
||||
|
||||
b_off += 7;
|
||||
|
||||
if (b_off >= 8) {
|
||||
d_off += 1;
|
||||
b_off -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
return out_length;
|
||||
}
|
|
@ -0,0 +1,597 @@
|
|||
/* OpenBSC Abis input driver for ip.access */
|
||||
|
||||
/* (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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/tlv.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/subchan_demux.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
|
||||
/* data structure for one E1 interface with A-bis */
|
||||
struct ia_e1_handle {
|
||||
struct bsc_fd listen_fd;
|
||||
struct bsc_fd rsl_listen_fd;
|
||||
struct gsm_network *gsmnet;
|
||||
};
|
||||
|
||||
static struct ia_e1_handle *e1h;
|
||||
|
||||
|
||||
#define TS1_ALLOC_SIZE 300
|
||||
|
||||
static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
|
||||
static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
|
||||
static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_LOCATION1,
|
||||
0x01, IPAC_IDTAG_LOCATION2,
|
||||
0x01, IPAC_IDTAG_EQUIPVERS,
|
||||
0x01, IPAC_IDTAG_SWVERSION,
|
||||
0x01, IPAC_IDTAG_UNITNAME,
|
||||
0x01, IPAC_IDTAG_SERNR,
|
||||
};
|
||||
|
||||
static const char *idtag_names[] = {
|
||||
[IPAC_IDTAG_SERNR] = "Serial_Number",
|
||||
[IPAC_IDTAG_UNITNAME] = "Unit_Name",
|
||||
[IPAC_IDTAG_LOCATION1] = "Location_1",
|
||||
[IPAC_IDTAG_LOCATION2] = "Location_2",
|
||||
[IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
|
||||
[IPAC_IDTAG_SWVERSION] = "Software_Version",
|
||||
[IPAC_IDTAG_IPADDR] = "IP_Address",
|
||||
[IPAC_IDTAG_MACADDR] = "MAC_Address",
|
||||
[IPAC_IDTAG_UNIT] = "Unit_ID",
|
||||
};
|
||||
|
||||
static const char *ipac_idtag_name(int tag)
|
||||
{
|
||||
if (tag >= ARRAY_SIZE(idtag_names))
|
||||
return "unknown";
|
||||
|
||||
return idtag_names[tag];
|
||||
}
|
||||
|
||||
static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
|
||||
{
|
||||
u_int8_t t_len;
|
||||
u_int8_t t_tag;
|
||||
u_int8_t *cur = buf;
|
||||
|
||||
while (cur < buf + len) {
|
||||
t_len = *cur++;
|
||||
t_tag = *cur++;
|
||||
|
||||
DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
|
||||
|
||||
dec->lv[t_tag].len = t_len;
|
||||
dec->lv[t_tag].val = cur;
|
||||
|
||||
cur += t_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_bts *find_bts_by_unitid(struct gsm_network *net,
|
||||
u_int16_t site_id, u_int16_t bts_id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < net->num_bts; i++) {
|
||||
struct gsm_bts *bts = &net->bts[i];
|
||||
|
||||
if (!is_ipaccess_bts(bts))
|
||||
continue;
|
||||
|
||||
if (bts->ip_access.site_id == site_id &&
|
||||
bts->ip_access.bts_id == bts_id)
|
||||
return bts;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
|
||||
u_int16_t *trx_id)
|
||||
{
|
||||
unsigned long ul;
|
||||
char *endptr;
|
||||
const char *nptr;
|
||||
|
||||
nptr = str;
|
||||
ul = strtoul(nptr, &endptr, 10);
|
||||
if (endptr <= nptr)
|
||||
return -EINVAL;
|
||||
if (site_id)
|
||||
*site_id = ul & 0xffff;
|
||||
|
||||
if (*endptr++ != '/')
|
||||
return -EINVAL;
|
||||
|
||||
nptr = endptr;
|
||||
ul = strtoul(nptr, &endptr, 10);
|
||||
if (endptr <= nptr)
|
||||
return -EINVAL;
|
||||
if (bts_id)
|
||||
*bts_id = ul & 0xffff;
|
||||
|
||||
if (*endptr++ != '/')
|
||||
return -EINVAL;
|
||||
|
||||
nptr = endptr;
|
||||
ul = strtoul(nptr, &endptr, 10);
|
||||
if (endptr <= nptr)
|
||||
return -EINVAL;
|
||||
if (trx_id)
|
||||
*trx_id = ul & 0xffff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
|
||||
struct bsc_fd *bfd)
|
||||
{
|
||||
struct tlv_parsed tlvp;
|
||||
u_int8_t msg_type = *(msg->l2h);
|
||||
u_int16_t site_id, bts_id, trx_id;
|
||||
struct gsm_bts *bts;
|
||||
int ret = 0;
|
||||
|
||||
switch (msg_type) {
|
||||
case IPAC_MSGT_PING:
|
||||
ret = write(bfd->fd, pong, sizeof(pong));
|
||||
break;
|
||||
case IPAC_MSGT_PONG:
|
||||
DEBUGP(DMI, "PONG!\n");
|
||||
break;
|
||||
case IPAC_MSGT_ID_RESP:
|
||||
DEBUGP(DMI, "ID_RESP ");
|
||||
/* parse tags, search for Unit ID */
|
||||
ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
|
||||
msgb_l2len(msg)-2);
|
||||
DEBUGP(DMI, "\n");
|
||||
|
||||
if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT))
|
||||
break;
|
||||
|
||||
/* lookup BTS, create sign_link, ... */
|
||||
parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
|
||||
&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 "
|
||||
" %u/%u/%u, disconnecting\n", site_id, bts_id,
|
||||
trx_id);
|
||||
return -EIO;
|
||||
}
|
||||
DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
|
||||
if (bfd->priv_nr == 1) {
|
||||
bts->oml_link = e1inp_sign_link_create(&line->ts[1-1],
|
||||
E1INP_SIGN_OML, bts->c0,
|
||||
0, 0xff);
|
||||
} else if (bfd->priv_nr == 2) {
|
||||
struct e1inp_ts *e1i_ts;
|
||||
struct bsc_fd *newbfd;
|
||||
|
||||
/* FIXME: implement this for non-0 TRX */
|
||||
bfd->data = line = bts->oml_link->ts->line;
|
||||
e1i_ts = &line->ts[2-1];
|
||||
newbfd = &e1i_ts->driver.ipaccess.fd;
|
||||
|
||||
bts->c0->rsl_link =
|
||||
e1inp_sign_link_create(e1i_ts,
|
||||
E1INP_SIGN_RSL, bts->c0,
|
||||
0, 0);
|
||||
/* get rid of our old temporary bfd */
|
||||
memcpy(newbfd, bfd, sizeof(*newbfd));
|
||||
bsc_unregister_fd(bfd);
|
||||
bsc_register_fd(newbfd);
|
||||
free(bfd);
|
||||
}
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
DEBUGP(DMI, "ID_ACK? -> ACK!\n");
|
||||
ret = write(bfd->fd, id_ack, sizeof(id_ack));
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: this is per BTS */
|
||||
static int oml_up = 0;
|
||||
static int rsl_up = 0;
|
||||
|
||||
static int handle_ts1_read(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *link;
|
||||
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
|
||||
struct ipaccess_head *hh;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
/* first read our 3-byte header */
|
||||
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));
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
fprintf(stderr, "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);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
|
||||
/* then read te length as specified in header */
|
||||
msg->l2h = msg->data + sizeof(*hh);
|
||||
ret = recv(bfd->fd, msg->l2h, hh->len, 0);
|
||||
if (ret < hh->len) {
|
||||
fprintf(stderr, "short read!\n");
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msgb_put(msg, ret);
|
||||
DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), ret));
|
||||
|
||||
if (hh->proto == IPAC_PROTO_IPACCESS) {
|
||||
ret = ipaccess_rcvmsg(line, msg, bfd);
|
||||
if (ret < 0) {
|
||||
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);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
/* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
|
||||
* might have free'd it !!! */
|
||||
|
||||
link = e1inp_lookup_sign_link(e1i_ts, 0, hh->proto);
|
||||
if (!link) {
|
||||
printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
msg->trx = link->trx;
|
||||
|
||||
switch (hh->proto) {
|
||||
case IPAC_PROTO_RSL:
|
||||
if (!rsl_up) {
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_RSL);
|
||||
rsl_up = 1;
|
||||
}
|
||||
ret = abis_rsl_rcvmsg(msg);
|
||||
break;
|
||||
case IPAC_PROTO_OML:
|
||||
if (!oml_up) {
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_OML);
|
||||
oml_up = 1;
|
||||
}
|
||||
ret = abis_nm_rcvmsg(msg);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
|
||||
msgb_free(msg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_ts1_write(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct msgb *msg;
|
||||
struct ipaccess_head *hh;
|
||||
u_int8_t *l2_data;
|
||||
int ret;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
if (!msg) {
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
l2_data = msg->data;
|
||||
|
||||
/* prepend the ip.access header */
|
||||
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
|
||||
hh->zero = 0;
|
||||
hh->len = msg->len - sizeof(*hh);
|
||||
|
||||
switch (sign_link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
hh->proto = IPAC_PROTO_OML;
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
hh->proto = IPAC_PROTO_RSL;
|
||||
break;
|
||||
default:
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(l2_data, hh->len));
|
||||
|
||||
ret = send(bfd->fd, msg->data, msg->len, 0);
|
||||
msgb_free(msg);
|
||||
usleep(100000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* callback from select.c in case one of the fd's can be read/written */
|
||||
static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
unsigned int idx = ts_nr-1;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
int rc = 0;
|
||||
|
||||
/* In case of early RSL we might not yet have a line */
|
||||
|
||||
if (line)
|
||||
e1i_ts = &line->ts[idx];
|
||||
|
||||
if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) {
|
||||
if (what & BSC_FD_READ)
|
||||
rc = handle_ts1_read(bfd);
|
||||
if (what & BSC_FD_WRITE)
|
||||
rc = handle_ts1_write(bfd);
|
||||
} else
|
||||
fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int ts_want_write(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct e1inp_driver ipaccess_driver = {
|
||||
.name = "ip.access",
|
||||
.want_write = ts_want_write,
|
||||
};
|
||||
|
||||
/* callback of the OML listening filedescriptor */
|
||||
static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
int idx = 0;
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
struct bsc_fd *bfd;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (!(what & BSC_FD_READ))
|
||||
return 0;
|
||||
|
||||
ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
|
||||
if (ret < 0) {
|
||||
perror("accept");
|
||||
return ret;
|
||||
}
|
||||
DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
|
||||
|
||||
line = malloc(sizeof(*line));
|
||||
if (!line) {
|
||||
close(ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(line, 0, sizeof(*line));
|
||||
line->driver = &ipaccess_driver;
|
||||
//line->driver_data = e1h;
|
||||
/* create virrtual E1 timeslots for signalling */
|
||||
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
|
||||
e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
|
||||
|
||||
e1i_ts = &line->ts[idx];
|
||||
|
||||
bfd = &e1i_ts->driver.ipaccess.fd;
|
||||
bfd->fd = ret;
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = 1;
|
||||
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");
|
||||
close(bfd->fd);
|
||||
free(line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
|
||||
ret = write(bfd->fd, id_req, sizeof(id_req));
|
||||
|
||||
return e1inp_line_register(line);
|
||||
}
|
||||
|
||||
static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
struct bsc_fd *bfd = malloc(sizeof(*bfd));
|
||||
int ret;
|
||||
|
||||
if (!(what & BSC_FD_READ))
|
||||
return 0;
|
||||
|
||||
/* Some BTS has connected to us, but we don't know yet which line
|
||||
* (as created by the OML link) to associate it with. Thus, we
|
||||
* aloocate a temporary bfd until we have received ID from BTS */
|
||||
|
||||
bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
|
||||
if (bfd->fd < 0) {
|
||||
perror("accept");
|
||||
return bfd->fd;
|
||||
}
|
||||
DEBUGP(DINP, "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");
|
||||
close(bfd->fd);
|
||||
free(bfd);
|
||||
return ret;
|
||||
}
|
||||
/* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
|
||||
ret = write(bfd->fd, id_req, sizeof(id_req));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_sock(struct bsc_fd *bfd, u_int16_t port,
|
||||
int (*cb)(struct bsc_fd *fd, unsigned int what))
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int ret, on = 1;
|
||||
|
||||
bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
bfd->cb = cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
//bfd->data = line;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not bind l2 socket %s\n",
|
||||
strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = listen(bfd->fd, 1);
|
||||
if (ret < 0) {
|
||||
perror("listen");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
perror("register_listen_fd");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Actively connect to a BTS. Currently used by ipaccess-config.c */
|
||||
int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = &line->ts[0];
|
||||
struct bsc_fd *bfd = &e1i_ts->driver.ipaccess.fd;
|
||||
int ret, on = 1;
|
||||
|
||||
bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
bfd->cb = ipaccess_fd_cb;
|
||||
bfd->when = BSC_FD_READ | BSC_FD_WRITE;
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = 1;
|
||||
|
||||
setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not connect socket\n");
|
||||
close(bfd->fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
close(bfd->fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
line->driver = &ipaccess_driver;
|
||||
|
||||
return e1inp_line_register(line);
|
||||
}
|
||||
|
||||
int ipaccess_setup(struct gsm_network *gsmnet)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* register the driver with the core */
|
||||
/* FIXME: do this in the plugin initializer function */
|
||||
ret = e1inp_driver_register(&ipaccess_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
e1h = malloc(sizeof(*e1h));
|
||||
memset(e1h, 0, sizeof(*e1h));
|
||||
e1h->gsmnet = gsmnet;
|
||||
|
||||
/* Listen for OML connections */
|
||||
ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Listen for RSL connections */
|
||||
ret = make_sock(&e1h->rsl_listen_fd, 3003, rsl_listen_fd_cb);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,542 @@
|
|||
/* OpenBSC Abis input driver for mISDNuser */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (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 <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <mISDNif.h>
|
||||
|
||||
//#define AF_COMPATIBILITY_FUNC
|
||||
//#include <compat_af_isdn.h>
|
||||
#define AF_ISDN 34
|
||||
#define PF_ISDN AF_ISDN
|
||||
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/subchan_demux.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
|
||||
/* data structure for one E1 interface with A-bis */
|
||||
struct mi_e1_handle {
|
||||
/* The mISDN card number of the card we use */
|
||||
int cardnr;
|
||||
};
|
||||
|
||||
#define TS1_ALLOC_SIZE 300
|
||||
|
||||
struct prim_name {
|
||||
unsigned int prim;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
const struct prim_name prim_names[] = {
|
||||
{ PH_CONTROL_IND, "PH_CONTROL_IND" },
|
||||
{ PH_DATA_IND, "PH_DATA_IND" },
|
||||
{ PH_DATA_CNF, "PH_DATA_CNF" },
|
||||
{ PH_ACTIVATE_IND, "PH_ACTIVATE_IND" },
|
||||
{ DL_ESTABLISH_IND, "DL_ESTABLISH_IND" },
|
||||
{ DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" },
|
||||
{ DL_RELEASE_IND, "DL_RELEASE_IND" },
|
||||
{ DL_RELEASE_CNF, "DL_RELEASE_CNF" },
|
||||
{ DL_DATA_IND, "DL_DATA_IND" },
|
||||
{ DL_UNITDATA_IND, "DL_UNITDATA_IND" },
|
||||
{ DL_INFORMATION_IND, "DL_INFORMATION_IND" },
|
||||
{ MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" },
|
||||
{ MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" },
|
||||
};
|
||||
|
||||
const char *get_prim_name(unsigned int prim)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(prim_names); i++) {
|
||||
if (prim_names[i].prim == prim)
|
||||
return prim_names[i].name;
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static int handle_ts1_read(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *link;
|
||||
struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
|
||||
struct sockaddr_mISDN l2addr;
|
||||
struct mISDNhead *hh;
|
||||
socklen_t alen;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hh = (struct mISDNhead *) msg->data;
|
||||
|
||||
alen = sizeof(l2addr);
|
||||
ret = recvfrom(bfd->fd, msg->data, 300, 0,
|
||||
(struct sockaddr *) &l2addr, &alen);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "recvfrom error %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (alen != sizeof(l2addr)) {
|
||||
fprintf(stderr, "%s error len\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
|
||||
alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);
|
||||
|
||||
DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n",
|
||||
ret, hh->prim, hh->id, get_prim_name(hh->prim));
|
||||
|
||||
switch (hh->prim) {
|
||||
case DL_INFORMATION_IND:
|
||||
/* mISDN tells us which channel number is allocated for this
|
||||
* tuple of (SAPI, TEI). */
|
||||
DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n",
|
||||
l2addr.channel, l2addr.sapi, l2addr.tei);
|
||||
link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
|
||||
if (!link) {
|
||||
DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
|
||||
free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* save the channel number in the driver private struct */
|
||||
link->driver.misdn.channel = l2addr.channel;
|
||||
break;
|
||||
case DL_ESTABLISH_IND:
|
||||
DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n",
|
||||
l2addr.channel, l2addr.sapi, l2addr.tei);
|
||||
ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi);
|
||||
break;
|
||||
case DL_RELEASE_IND:
|
||||
DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n",
|
||||
l2addr.channel, l2addr.sapi, l2addr.tei);
|
||||
ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi);
|
||||
break;
|
||||
case DL_DATA_IND:
|
||||
msg->l2h = msg->data + MISDN_HEADER_LEN;
|
||||
DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
|
||||
break;
|
||||
case PH_ACTIVATE_IND:
|
||||
DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
|
||||
l2addr.channel, l2addr.sapi, l2addr.tei);
|
||||
break;
|
||||
case PH_DEACTIVATE_IND:
|
||||
DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
|
||||
l2addr.channel, l2addr.sapi, l2addr.tei);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ts_want_write(struct e1inp_ts *e1i_ts)
|
||||
{
|
||||
/* We never include the mISDN B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
|
||||
return 0;
|
||||
|
||||
e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timeout_ts1_write(void *data)
|
||||
{
|
||||
struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
|
||||
|
||||
/* trigger write of ts1, due to tx delay timer */
|
||||
ts_want_write(e1i_ts);
|
||||
}
|
||||
|
||||
static int handle_ts1_write(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct e1inp_sign_link *sign_link;
|
||||
struct sockaddr_mISDN sa;
|
||||
struct msgb *msg;
|
||||
struct mISDNhead *hh;
|
||||
u_int8_t *l2_data;
|
||||
int ret;
|
||||
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
|
||||
/* get the next msg for this timeslot */
|
||||
msg = e1inp_tx_ts(e1i_ts, &sign_link);
|
||||
if (!msg) {
|
||||
/* no message after tx delay timer */
|
||||
return 0;
|
||||
}
|
||||
|
||||
l2_data = msg->data;
|
||||
|
||||
/* prepend the mISDNhead */
|
||||
hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
|
||||
hh->prim = DL_DATA_REQ;
|
||||
|
||||
DEBUGP(DMI, "TX TEI(%d) SAPI(%d): %s\n", sign_link->tei,
|
||||
sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN));
|
||||
|
||||
/* construct the sockaddr */
|
||||
sa.family = AF_ISDN;
|
||||
sa.sapi = sign_link->sapi;
|
||||
sa.dev = sign_link->tei;
|
||||
sa.channel = sign_link->driver.misdn.channel;
|
||||
|
||||
ret = sendto(bfd->fd, msg->data, msg->len, 0,
|
||||
(struct sockaddr *)&sa, sizeof(sa));
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "%s sendto failed %d\n", __func__, ret);
|
||||
msgb_free(msg);
|
||||
|
||||
/* set tx delay timer for next event */
|
||||
e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
|
||||
e1i_ts->sign.tx_timer.data = e1i_ts;
|
||||
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define BCHAN_TX_GRAN 160
|
||||
/* write to a B channel TS */
|
||||
static int handle_tsX_write(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct mISDNhead *hh;
|
||||
u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)];
|
||||
struct subch_mux *mx = &e1i_ts->trau.mux;
|
||||
int ret;
|
||||
|
||||
hh = (struct mISDNhead *) tx_buf;
|
||||
hh->prim = PH_DATA_REQ;
|
||||
|
||||
subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN);
|
||||
|
||||
DEBUGP(DMIB, "BCHAN TX: %s\n",
|
||||
hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN));
|
||||
|
||||
ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
|
||||
if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
|
||||
DEBUGP(DMIB, "send returns %d instead of %u\n", ret,
|
||||
sizeof(*hh) + BCHAN_TX_GRAN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TSX_ALLOC_SIZE 4096
|
||||
/* FIXME: read from a B channel TS */
|
||||
static int handle_tsX_read(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
|
||||
struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE);
|
||||
struct mISDNhead *hh;
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hh = (struct mISDNhead *) msg->data;
|
||||
|
||||
ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "recvfrom error %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
if (hh->prim != PH_CONTROL_IND)
|
||||
DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n",
|
||||
ret, hh->prim, hh->id, get_prim_name(hh->prim));
|
||||
|
||||
switch (hh->prim) {
|
||||
case PH_DATA_IND:
|
||||
msg->l2h = msg->data + MISDN_HEADER_LEN;
|
||||
DEBUGP(DMIB, "BCHAN RX: %s\n",
|
||||
hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
|
||||
ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
|
||||
break;
|
||||
case PH_ACTIVATE_IND:
|
||||
case PH_DATA_CNF:
|
||||
/* physical layer indicates that data has been sent,
|
||||
* we thus can send some more data */
|
||||
ret = handle_tsX_write(bfd);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* FIXME: why do we free signalling msgs in the caller, and trau not? */
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* callback from select.c in case one of the fd's can be read/written */
|
||||
static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
unsigned int ts_nr = bfd->priv_nr;
|
||||
unsigned int idx = ts_nr-1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
int rc = 0;
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
if (what & BSC_FD_READ)
|
||||
rc = handle_ts1_read(bfd);
|
||||
if (what & BSC_FD_WRITE)
|
||||
rc = handle_ts1_write(bfd);
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
if (what & BSC_FD_READ)
|
||||
rc = handle_tsX_read(bfd);
|
||||
/* We never include the mISDN B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int activate_bchan(struct e1inp_line *line, int ts, int act)
|
||||
{
|
||||
struct mISDNhead hh;
|
||||
int ret;
|
||||
unsigned int idx = ts-1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
|
||||
|
||||
fprintf(stdout, "activate bchan\n");
|
||||
if (act)
|
||||
hh.prim = PH_ACTIVATE_REQ;
|
||||
else
|
||||
hh.prim = PH_DEACTIVATE_REQ;
|
||||
|
||||
hh.id = MISDN_ID_ANY;
|
||||
ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stdout, "could not send ACTIVATE_RQ %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct e1inp_driver misdn_driver = {
|
||||
.name = "mISDNuser",
|
||||
.want_write = ts_want_write,
|
||||
};
|
||||
|
||||
static int mi_e1_setup(struct e1inp_line *line, int release_l2)
|
||||
{
|
||||
struct mi_e1_handle *e1h = line->driver_data;
|
||||
int ts, ret;
|
||||
|
||||
/* TS0 is CRC4, don't need any fd for it */
|
||||
for (ts = 1; ts < NUM_E1_TS; ts++) {
|
||||
unsigned int idx = ts-1;
|
||||
struct e1inp_ts *e1i_ts = &line->ts[idx];
|
||||
struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
|
||||
struct sockaddr_mISDN addr;
|
||||
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = ts;
|
||||
bfd->cb = misdn_fd_cb;
|
||||
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_NONE:
|
||||
continue;
|
||||
break;
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
|
||||
bfd->when = BSC_FD_READ;
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
|
||||
/* We never include the mISDN B-Channel FD into the
|
||||
* writeset, since it doesn't support poll() based
|
||||
* write flow control */
|
||||
bfd->when = BSC_FD_READ;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bfd->fd < 0) {
|
||||
fprintf(stderr, "%s could not open socket %s\n",
|
||||
__func__, strerror(errno));
|
||||
return bfd->fd;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.family = AF_ISDN;
|
||||
addr.dev = e1h->cardnr;
|
||||
switch (e1i_ts->type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
addr.channel = 0;
|
||||
/* SAPI not supported yet in kernel */
|
||||
//addr.sapi = e1inp_ts->sign.sapi;
|
||||
addr.sapi = 0;
|
||||
addr.tei = GROUP_TEI;
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
addr.channel = ts;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DMI, "unsupported E1 TS type: %u\n",
|
||||
e1i_ts->type);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not bind l2 socket %s\n",
|
||||
strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (e1i_ts->type == E1INP_TS_TYPE_SIGN) {
|
||||
ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: only activate B-Channels once we start to
|
||||
* use them to conserve CPU power */
|
||||
if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
|
||||
activate_bchan(line, ts, 1);
|
||||
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "could not register FD: %s\n",
|
||||
strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mi_setup(int cardnr, struct e1inp_line *line, int release_l2)
|
||||
{
|
||||
struct mi_e1_handle *e1h;
|
||||
int sk, ret, cnt;
|
||||
struct mISDN_devinfo devinfo;
|
||||
|
||||
/* register the driver with the core */
|
||||
/* FIXME: do this in the plugin initializer function */
|
||||
ret = e1inp_driver_register(&misdn_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* create the actual line instance */
|
||||
/* FIXME: do this independent of driver registration */
|
||||
e1h = malloc(sizeof(*e1h));
|
||||
memset(e1h, 0, sizeof(*e1h));
|
||||
|
||||
e1h->cardnr = cardnr;
|
||||
|
||||
line->driver = &misdn_driver;
|
||||
line->driver_data = e1h;
|
||||
|
||||
/* open the ISDN card device */
|
||||
sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
|
||||
if (sk < 0) {
|
||||
fprintf(stderr, "%s could not open socket %s\n",
|
||||
__func__, strerror(errno));
|
||||
return sk;
|
||||
}
|
||||
|
||||
ret = ioctl(sk, IMGETCOUNT, &cnt);
|
||||
if (ret) {
|
||||
fprintf(stderr, "%s error getting interf count: %s\n",
|
||||
__func__, strerror(errno));
|
||||
close(sk);
|
||||
return -ENODEV;
|
||||
}
|
||||
//DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
|
||||
printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
|
||||
#if 1
|
||||
devinfo.id = e1h->cardnr;
|
||||
ret = ioctl(sk, IMGETDEVINFO, &devinfo);
|
||||
if (ret < 0) {
|
||||
fprintf(stdout, "error getting info for device %d: %s\n",
|
||||
e1h->cardnr, strerror(errno));
|
||||
return -ENODEV;
|
||||
}
|
||||
fprintf(stdout, " id: %d\n", devinfo.id);
|
||||
fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols);
|
||||
fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols);
|
||||
fprintf(stdout, " protocol: %d\n", devinfo.protocol);
|
||||
fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan);
|
||||
fprintf(stdout, " name: %s\n", devinfo.name);
|
||||
#endif
|
||||
|
||||
if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) {
|
||||
fprintf(stderr, "error: card is not of type E1 (NT-mode)\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (devinfo.nrbchan != 30) {
|
||||
fprintf(stderr, "error: E1 card has no 30 B-channels\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mi_e1_setup(line, release_l2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return e1inp_line_register(line);
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/* ip.access nanoBTS configuration tool */
|
||||
|
||||
/* (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 <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/timer.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
|
||||
static struct gsm_network *gsmnet;
|
||||
|
||||
static int restart;
|
||||
static char *prim_oml_ip;
|
||||
static char *unit_id;
|
||||
|
||||
/*
|
||||
static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 };
|
||||
static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 };
|
||||
*/
|
||||
|
||||
static void bootstrap_om(struct gsm_bts *bts)
|
||||
{
|
||||
int len;
|
||||
static u_int8_t buf[1024];
|
||||
|
||||
printf("OML link established\n");
|
||||
|
||||
if (unit_id) {
|
||||
len = strlen(unit_id);
|
||||
if (len > sizeof(buf)-10)
|
||||
return;
|
||||
buf[0] = NM_ATT_IPACC_UNIT_ID;
|
||||
buf[1] = (len+1) >> 8;
|
||||
buf[2] = (len+1) & 0xff;
|
||||
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);
|
||||
}
|
||||
if (prim_oml_ip) {
|
||||
struct in_addr ia;
|
||||
u_int8_t *cur = buf;
|
||||
|
||||
if (!inet_aton(prim_oml_ip, &ia)) {
|
||||
fprintf(stderr, "invalid IP address: %s\n",
|
||||
prim_oml_ip);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 0x88 + IP + port */
|
||||
len = 1 + sizeof(ia) + 2;
|
||||
|
||||
*cur++ = NM_ATT_IPACC_PRIM_OML_IP;
|
||||
*cur++ = (len) >> 8;
|
||||
*cur++ = (len) & 0xff;
|
||||
*cur++ = 0x88;
|
||||
memcpy(cur, &ia, sizeof(ia));
|
||||
cur += sizeof(ia);
|
||||
*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);
|
||||
}
|
||||
|
||||
if (restart) {
|
||||
printf("restarting BTS\n");
|
||||
abis_nm_ipaccess_restart(bts);
|
||||
}
|
||||
}
|
||||
|
||||
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
{
|
||||
switch (event) {
|
||||
case EVT_E1_TEI_UP:
|
||||
switch (type) {
|
||||
case E1INP_SIGN_OML:
|
||||
bootstrap_om(trx->bts);
|
||||
break;
|
||||
case E1INP_SIGN_RSL:
|
||||
/* FIXME */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EVT_E1_TEI_DN:
|
||||
fprintf(stderr, "Lost some E1 TEI link\n");
|
||||
/* FIXME: deal with TEI or L1 link loss */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct gsm_bts *bts;
|
||||
struct sockaddr_in sin;
|
||||
int rc, option_index = 0;
|
||||
|
||||
printf("ipaccess-config (C) 2009 by Harald Welte\n");
|
||||
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
|
||||
|
||||
while (1) {
|
||||
int c;
|
||||
static struct option long_options[] = {
|
||||
{ "unit-id", 1, 0, 'u' },
|
||||
{ "oml-ip", 1, 0, 'o' },
|
||||
{ "restart", 0, 0, 'r' },
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "u:o:r", long_options,
|
||||
&option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'u':
|
||||
unit_id = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
prim_oml_ip = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
restart = 1;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "you have to specify the IP address of the BTS\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
gsmnet = gsm_network_init( 1, GSM_BTS_TYPE_NANOBTS_900, 1, 1);
|
||||
if (!gsmnet)
|
||||
exit(1);
|
||||
|
||||
bts = &gsmnet->bts[0];
|
||||
|
||||
printf("Trying to connect to ip.access BTS ...\n");
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
inet_aton(argv[optind], &sin.sin_addr);
|
||||
rc = ia_config_connect(bts, &sin);
|
||||
if (rc < 0) {
|
||||
perror("Error connecting to the BTS");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
rc = bsc_select_main(0);
|
||||
if (rc < 0)
|
||||
exit(3);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/timer.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
|
||||
static const char *idtag_names[] = {
|
||||
[IPAC_IDTAG_SERNR] = "Serial Number",
|
||||
[IPAC_IDTAG_UNITNAME] = "Unit Name",
|
||||
[IPAC_IDTAG_LOCATION1] = "Location 1",
|
||||
[IPAC_IDTAG_LOCATION2] = "Location 2",
|
||||
[IPAC_IDTAG_EQUIPVERS] = "Equipment Version",
|
||||
[IPAC_IDTAG_SWVERSION] = "Software Version",
|
||||
[IPAC_IDTAG_IPADDR] = "IP Address",
|
||||
[IPAC_IDTAG_MACADDR] = "MAC Address",
|
||||
[IPAC_IDTAG_UNIT] = "Unit ID",
|
||||
};
|
||||
|
||||
static const char *ipac_idtag_name(int tag)
|
||||
{
|
||||
if (tag >= ARRAY_SIZE(idtag_names))
|
||||
return "unknown";
|
||||
|
||||
return idtag_names[tag];
|
||||
}
|
||||
|
||||
static int udp_sock(void)
|
||||
{
|
||||
int fd, rc, bc = 1;
|
||||
struct sockaddr_in sa;
|
||||
|
||||
fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(3006);
|
||||
sa.sin_addr.s_addr = INADDR_ANY;
|
||||
inet_aton("192.168.100.11", &sa.sin_addr);
|
||||
|
||||
rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
#if 0
|
||||
rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
#endif
|
||||
return fd;
|
||||
|
||||
err:
|
||||
close(fd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
|
||||
IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_IPADDR,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_LOCATION1,
|
||||
0x01, IPAC_IDTAG_LOCATION2,
|
||||
0x01, IPAC_IDTAG_EQUIPVERS,
|
||||
0x01, IPAC_IDTAG_SWVERSION,
|
||||
0x01, IPAC_IDTAG_UNITNAME,
|
||||
0x01, IPAC_IDTAG_SERNR,
|
||||
};
|
||||
|
||||
|
||||
static int bcast_find(int fd)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(3006);
|
||||
inet_aton("255.255.255.255", &sa.sin_addr);
|
||||
|
||||
return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
|
||||
}
|
||||
|
||||
static int parse_response(unsigned char *buf, int len)
|
||||
{
|
||||
u_int8_t t_len;
|
||||
u_int8_t t_tag;
|
||||
u_int8_t *cur = buf;
|
||||
|
||||
while (cur < buf + len) {
|
||||
t_len = *cur++;
|
||||
t_tag = *cur++;
|
||||
|
||||
printf("%s='%s' ", ipac_idtag_name(t_tag), cur);
|
||||
|
||||
cur += t_len;
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_response(int fd)
|
||||
{
|
||||
unsigned char buf[255];
|
||||
struct sockaddr_in sa;
|
||||
int len;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
return parse_response(buf+6, len-6);
|
||||
}
|
||||
|
||||
static int bfd_cb(struct bsc_fd *bfd, unsigned int flags)
|
||||
{
|
||||
if (flags & BSC_FD_READ)
|
||||
return read_response(bfd->fd);
|
||||
if (flags & BSC_FD_WRITE) {
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
return bcast_find(bfd->fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct timer_list timer;
|
||||
|
||||
static void timer_cb(void *_data)
|
||||
{
|
||||
struct bsc_fd *bfd = _data;
|
||||
|
||||
bfd->when |= BSC_FD_WRITE;
|
||||
|
||||
bsc_schedule_timer(&timer, 5, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bsc_fd bfd;
|
||||
int rc;
|
||||
|
||||
printf("ipaccess-find (C) 2009 by Harald Welte\n");
|
||||
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
|
||||
|
||||
bfd.cb = bfd_cb;
|
||||
bfd.when = BSC_FD_READ | BSC_FD_WRITE;
|
||||
bfd.fd = udp_sock();
|
||||
if (bfd.fd < 0)
|
||||
exit(2);
|
||||
|
||||
bsc_register_fd(&bfd);
|
||||
|
||||
timer.cb = timer_cb;
|
||||
timer.data = &bfd;
|
||||
|
||||
bsc_schedule_timer(&timer, 5, 0);
|
||||
|
||||
printf("Trying to find ip.access BTS by broadcast UDP...\n");
|
||||
|
||||
while (1) {
|
||||
rc = bsc_select_main(0);
|
||||
if (rc < 0)
|
||||
exit(3);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/* isdnsync.c
|
||||
*
|
||||
* Author Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include "mISDNif.h"
|
||||
#define MISDN_OLD_AF_COMPATIBILITY
|
||||
#define AF_COMPATIBILITY_FUNC
|
||||
#include "compat_af_isdn.h"
|
||||
|
||||
int card = 0;
|
||||
int sock = -1;
|
||||
|
||||
int mISDN_open(void)
|
||||
{
|
||||
int fd, ret;
|
||||
struct mISDN_devinfo devinfo;
|
||||
struct sockaddr_mISDN l2addr;
|
||||
|
||||
fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "could not open socket (%s)\n", strerror(errno));
|
||||
return fd;
|
||||
}
|
||||
devinfo.id = card;
|
||||
ret = ioctl(fd, IMGETDEVINFO, &devinfo);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno));
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
close(fd);
|
||||
if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0))
|
||||
&& !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) {
|
||||
fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno));
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno));
|
||||
return fd;
|
||||
}
|
||||
l2addr.family = AF_ISDN;
|
||||
l2addr.dev = card;
|
||||
l2addr.channel = 0;
|
||||
l2addr.sapi = 0;
|
||||
l2addr.tei = 0;
|
||||
ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno));
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
sock = fd;
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
void mISDN_handle(void)
|
||||
{
|
||||
int ret;
|
||||
fd_set rfd;
|
||||
struct timeval tv;
|
||||
struct sockaddr_mISDN addr;
|
||||
socklen_t alen;
|
||||
unsigned char buffer[2048];
|
||||
struct mISDNhead *hh = (struct mISDNhead *)buffer;
|
||||
int l1 = 0, l2 = 0, tei = 0;
|
||||
|
||||
while(1) {
|
||||
again:
|
||||
FD_ZERO(&rfd);
|
||||
FD_SET(sock, &rfd);
|
||||
tv.tv_sec = 2;
|
||||
tv.tv_usec = 0;
|
||||
ret = select(sock+1, &rfd, NULL, NULL, &tv);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (FD_ISSET(sock, &rfd)) {
|
||||
alen = sizeof(addr);
|
||||
ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno));
|
||||
} else if (ret < MISDN_HEADER_LEN) {
|
||||
fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__);
|
||||
} else {
|
||||
switch(hh->prim) {
|
||||
case MPH_ACTIVATE_IND:
|
||||
case PH_ACTIVATE_IND:
|
||||
if (!l1) {
|
||||
printf("PH_ACTIVATE\n");
|
||||
printf("*** Sync available from interface :-)\n");
|
||||
l1 = 1;
|
||||
}
|
||||
goto again;
|
||||
break;
|
||||
case MPH_DEACTIVATE_IND:
|
||||
case PH_DEACTIVATE_IND:
|
||||
if (l1) {
|
||||
printf("PH_DEACTIVATE\n");
|
||||
printf("*** Lost sync on interface :-(\n");
|
||||
l1 = 0;
|
||||
}
|
||||
goto again;
|
||||
break;
|
||||
case DL_ESTABLISH_IND:
|
||||
case DL_ESTABLISH_CNF:
|
||||
printf("DL_ESTABLISH\n");
|
||||
l2 = 1;
|
||||
goto again;
|
||||
break;
|
||||
case DL_RELEASE_IND:
|
||||
case DL_RELEASE_CNF:
|
||||
printf("DL_RELEASE\n");
|
||||
l2 = 0;
|
||||
goto again;
|
||||
break;
|
||||
case DL_INFORMATION_IND:
|
||||
printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi);
|
||||
tei = 1;
|
||||
break;
|
||||
default:
|
||||
// printf("prim %x\n", hh->prim);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tei && !l2) {
|
||||
hh->prim = DL_ESTABLISH_REQ;
|
||||
printf("-> activating layer 2\n");
|
||||
sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (argc <= 1)
|
||||
{
|
||||
printf("Usage: %s <card>\n\n", argv[0]);
|
||||
printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n");
|
||||
printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
card = atoi(argv[1]);
|
||||
|
||||
init_af_isdn();
|
||||
|
||||
if ((ret = mISDN_open() < 0))
|
||||
return(ret);
|
||||
|
||||
mISDN_handle();
|
||||
|
||||
close(sock);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/* (C) 2008 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 <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/msgb.h>
|
||||
|
||||
struct msgb *msgb_alloc(u_int16_t size)
|
||||
{
|
||||
struct msgb *msg = malloc(sizeof(*msg) + size);
|
||||
|
||||
if (!msg)
|
||||
return NULL;
|
||||
memset(msg, 0, sizeof(*msg)+size);
|
||||
|
||||
msg->data_len = size;
|
||||
msg->len = 0;
|
||||
msg->data = msg->_data;
|
||||
|
||||
msg->head = msg->data;
|
||||
msg->data = msg->data;
|
||||
/* reset tail pointer */
|
||||
msg->tail = msg->data;
|
||||
//msg->end = msg->tail + size;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void msgb_free(struct msgb *m)
|
||||
{
|
||||
free(m);
|
||||
}
|
||||
|
||||
void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
|
||||
{
|
||||
llist_add_tail(&msg->list, queue);
|
||||
}
|
||||
|
||||
struct msgb *msgb_dequeue(struct llist_head *queue)
|
||||
{
|
||||
struct llist_head *lh;
|
||||
|
||||
if (llist_empty(queue))
|
||||
return NULL;
|
||||
|
||||
lh = queue->next;
|
||||
llist_del(lh);
|
||||
|
||||
return llist_entry(lh, struct msgb, list);
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/* Paging helper and manager.... */
|
||||
/* (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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Relevant specs:
|
||||
* 12.21:
|
||||
* - 9.4.12 for CCCH Local Threshold
|
||||
*
|
||||
* 05.58:
|
||||
* - 8.5.2 CCCH Load indication
|
||||
* - 9.3.15 Paging Load
|
||||
*
|
||||
* Approach:
|
||||
* - Send paging command to subscriber
|
||||
* - On Channel Request we will remember the reason
|
||||
* - After the ACK we will request the identity
|
||||
* - Then we will send assign the gsm_subscriber and
|
||||
* - and call a callback
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
|
||||
#define PAGING_TIMEOUT 1, 75000
|
||||
#define MAX_PAGING_REQUEST 750
|
||||
|
||||
static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr)
|
||||
{
|
||||
int ccch_conf;
|
||||
int bs_cc_chans;
|
||||
int blocks;
|
||||
unsigned int group;
|
||||
|
||||
ccch_conf = bts->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);
|
||||
group = get_paging_group(str_to_imsi(subscr->imsi),
|
||||
bs_cc_chans, blocks);
|
||||
return group;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kill one paging request update the internal list...
|
||||
*/
|
||||
static void paging_remove_request(struct gsm_bts_paging_state *paging_bts,
|
||||
struct gsm_paging_request *to_be_deleted)
|
||||
{
|
||||
/* Update the last_request if that is necessary */
|
||||
if (to_be_deleted == paging_bts->last_request) {
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->last_request->entry.next;
|
||||
if (&to_be_deleted->entry == &paging_bts->pending_requests)
|
||||
paging_bts->last_request = NULL;
|
||||
}
|
||||
|
||||
bsc_del_timer(&to_be_deleted->T3113);
|
||||
llist_del(&to_be_deleted->entry);
|
||||
subscr_put(to_be_deleted->subscr);
|
||||
free(to_be_deleted);
|
||||
}
|
||||
|
||||
static void page_ms(struct gsm_paging_request *request)
|
||||
{
|
||||
u_int8_t mi[128];
|
||||
unsigned long int tmsi;
|
||||
unsigned int mi_len;
|
||||
unsigned int page_group;
|
||||
|
||||
DEBUGP(DPAG, "Going to send paging commands: '%s'\n",
|
||||
request->subscr->imsi);
|
||||
|
||||
page_group = calculate_group(request->bts, request->subscr);
|
||||
tmsi = strtoul(request->subscr->tmsi, NULL, 10);
|
||||
mi_len = generate_mid_from_tmsi(mi, tmsi);
|
||||
rsl_paging_cmd(request->bts, page_group, mi_len, mi,
|
||||
request->chan_type);
|
||||
}
|
||||
|
||||
static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts)
|
||||
{
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->last_request->entry.next;
|
||||
if (&paging_bts->last_request->entry == &paging_bts->pending_requests)
|
||||
paging_bts->last_request = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is kicked by the periodic PAGING LOAD Indicator
|
||||
* coming from abis_rsl.c
|
||||
*
|
||||
* We attempt to iterate once over the list of items but
|
||||
* only upto available_slots.
|
||||
*/
|
||||
static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts)
|
||||
{
|
||||
struct gsm_paging_request *initial_request = NULL;
|
||||
struct gsm_paging_request *current_request = NULL;
|
||||
|
||||
/*
|
||||
* Determine if the pending_requests list is empty and
|
||||
* return then.
|
||||
*/
|
||||
if (llist_empty(&paging_bts->pending_requests)) {
|
||||
paging_bts->last_request = NULL;
|
||||
/* since the list is empty, no need to reschedule the timer */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!paging_bts->last_request)
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->pending_requests.next;
|
||||
|
||||
assert(paging_bts->last_request);
|
||||
initial_request = paging_bts->last_request;
|
||||
current_request = initial_request;
|
||||
|
||||
do {
|
||||
/* handle the paging request now */
|
||||
page_ms(current_request);
|
||||
paging_bts->available_slots--;
|
||||
|
||||
/*
|
||||
* move to the next item. We might wrap around
|
||||
* this means last_request will be NULL and we just
|
||||
* call paging_page_to_next again. It it guranteed
|
||||
* that the list is not empty.
|
||||
*/
|
||||
paging_move_to_next(paging_bts);
|
||||
if (!paging_bts->last_request)
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->pending_requests.next;
|
||||
current_request = paging_bts->last_request;
|
||||
} while (paging_bts->available_slots > 0
|
||||
&& initial_request != current_request);
|
||||
|
||||
bsc_schedule_timer(&paging_bts->work_timer, 1, 0);
|
||||
}
|
||||
|
||||
static void paging_worker(void *data)
|
||||
{
|
||||
struct gsm_bts_paging_state *paging_bts = data;
|
||||
|
||||
paging_handle_pending_requests(paging_bts);
|
||||
}
|
||||
|
||||
void paging_init(struct gsm_bts *bts)
|
||||
{
|
||||
bts->paging.bts = bts;
|
||||
INIT_LLIST_HEAD(&bts->paging.pending_requests);
|
||||
bts->paging.work_timer.cb = paging_worker;
|
||||
bts->paging.work_timer.data = &bts->paging;
|
||||
|
||||
/* Large number, until we get a proper message */
|
||||
bts->paging.available_slots = 100;
|
||||
}
|
||||
|
||||
static int paging_pending_request(struct gsm_bts_paging_state *bts,
|
||||
struct gsm_subscriber *subscr) {
|
||||
struct gsm_paging_request *req;
|
||||
|
||||
llist_for_each_entry(req, &bts->pending_requests, entry) {
|
||||
if (subscr == req->subscr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void paging_T3113_expired(void *data)
|
||||
{
|
||||
struct gsm_paging_request *req = (struct gsm_paging_request *)data;
|
||||
struct paging_signal_data sig_data;
|
||||
|
||||
DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
|
||||
req, req->subscr->imsi);
|
||||
|
||||
sig_data.subscr = req->subscr,
|
||||
sig_data.bts = req->bts,
|
||||
sig_data.lchan = NULL,
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
|
||||
if (req->cbfn)
|
||||
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
|
||||
req->cbfn_param);
|
||||
paging_remove_request(&req->bts->paging, req);
|
||||
}
|
||||
|
||||
void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
int type, gsm_cbfn *cbfn, void *data)
|
||||
{
|
||||
struct gsm_bts_paging_state *bts_entry = &bts->paging;
|
||||
struct gsm_paging_request *req;
|
||||
|
||||
if (paging_pending_request(bts_entry, subscr)) {
|
||||
DEBUGP(DPAG, "Paging request already pending\n");
|
||||
return;
|
||||
}
|
||||
|
||||
req = (struct gsm_paging_request *)malloc(sizeof(*req));
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->subscr = subscr_get(subscr);
|
||||
req->bts = bts;
|
||||
req->chan_type = type;
|
||||
req->cbfn = cbfn;
|
||||
req->cbfn_param = data;
|
||||
req->T3113.cb = paging_T3113_expired;
|
||||
req->T3113.data = req;
|
||||
bsc_schedule_timer(&req->T3113, T3113_VALUE);
|
||||
llist_add_tail(&req->entry, &bts_entry->pending_requests);
|
||||
|
||||
if (!bsc_timer_pending(&bts_entry->work_timer))
|
||||
bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
|
||||
}
|
||||
|
||||
/* we consciously ignore the type of the request here */
|
||||
void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_bts_paging_state *bts_entry = &bts->paging;
|
||||
struct gsm_paging_request *req, *req2;
|
||||
|
||||
llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
|
||||
entry) {
|
||||
if (req->subscr == subscr) {
|
||||
if (req->cbfn)
|
||||
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
|
||||
NULL, lchan, req->cbfn_param);
|
||||
paging_remove_request(&bts->paging, req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots)
|
||||
{
|
||||
bts->paging.available_slots = free_slots;
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/* OpenBSC BS-11 T-Link interface using POSIX serial port */
|
||||
|
||||
/* (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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/rs232.h>
|
||||
|
||||
/* adaption layer from GSM 08.59 + 12.21 to RS232 */
|
||||
|
||||
struct serial_handle {
|
||||
struct bsc_fd fd;
|
||||
struct llist_head tx_queue;
|
||||
|
||||
struct msgb *rx_msg;
|
||||
unsigned int rxmsg_bytes_missing;
|
||||
|
||||
unsigned int delay_ms;
|
||||
struct gsm_bts *bts;
|
||||
};
|
||||
|
||||
/* FIXME: this needs to go */
|
||||
static struct serial_handle _ser_handle, *ser_handle = &_ser_handle;
|
||||
|
||||
#define LAPD_HDR_LEN 10
|
||||
|
||||
static int handle_ser_write(struct bsc_fd *bfd);
|
||||
|
||||
/* callback from abis_nm */
|
||||
int _abis_nm_sendmsg(struct msgb *msg)
|
||||
{
|
||||
struct serial_handle *sh = ser_handle;
|
||||
u_int8_t *lapd;
|
||||
unsigned int len;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
|
||||
/* prepend LAPD header */
|
||||
lapd = msgb_push(msg, LAPD_HDR_LEN);
|
||||
|
||||
len = msg->len - 2;
|
||||
|
||||
lapd[0] = (len >> 8) & 0xff;
|
||||
lapd[1] = len & 0xff; /* length of bytes startign at lapd[2] */
|
||||
lapd[2] = 0x00;
|
||||
lapd[3] = 0x07;
|
||||
lapd[4] = 0x01;
|
||||
lapd[5] = 0x3e;
|
||||
lapd[6] = 0x00;
|
||||
lapd[7] = 0x00;
|
||||
lapd[8] = msg->len - 10; /* length of bytes starting at lapd[10] */
|
||||
lapd[9] = lapd[8] ^ 0x38;
|
||||
|
||||
msgb_enqueue(&sh->tx_queue, msg);
|
||||
sh->fd.when |= BSC_FD_WRITE;
|
||||
|
||||
/* we try to immediately send */
|
||||
handle_ser_write(&sh->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* select.c callback in case we can write to the RS232 */
|
||||
static int handle_ser_write(struct bsc_fd *bfd)
|
||||
{
|
||||
struct serial_handle *sh = bfd->data;
|
||||
struct msgb *msg;
|
||||
int written;
|
||||
|
||||
msg = msgb_dequeue(&sh->tx_queue);
|
||||
if (!msg) {
|
||||
bfd->when &= ~BSC_FD_WRITE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP(DMI, "RS232 TX: %s\n", hexdump(msg->data, msg->len));
|
||||
|
||||
/* send over serial line */
|
||||
written = write(bfd->fd, msg->data, msg->len);
|
||||
if (written < msg->len) {
|
||||
perror("short write:");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
usleep(sh->delay_ms*1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SERIAL_ALLOC_SIZE 300
|
||||
|
||||
/* select.c callback in case we can read from the RS232 */
|
||||
static int handle_ser_read(struct bsc_fd *bfd)
|
||||
{
|
||||
struct serial_handle *sh = bfd->data;
|
||||
struct msgb *msg;
|
||||
int rc = 0;
|
||||
|
||||
if (!sh->rx_msg) {
|
||||
sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE);
|
||||
sh->rx_msg->l2h = NULL;
|
||||
sh->rx_msg->trx = sh->bts->c0;
|
||||
}
|
||||
msg = sh->rx_msg;
|
||||
|
||||
/* first read two byes to obtain length */
|
||||
if (msg->len < 2) {
|
||||
rc = read(sh->fd.fd, msg->tail, 2 - msg->len);
|
||||
if (rc < 0) {
|
||||
perror("ERROR reading from serial port");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
if (msg->len >= 2) {
|
||||
/* parse LAPD payload length */
|
||||
if (msg->data[0] != 0)
|
||||
fprintf(stderr, "Suspicious header byte 0: 0x%02x\n",
|
||||
msg->data[0]);
|
||||
|
||||
sh->rxmsg_bytes_missing = msg->data[0] << 8;
|
||||
sh->rxmsg_bytes_missing += msg->data[1];
|
||||
|
||||
if (sh->rxmsg_bytes_missing < LAPD_HDR_LEN -2)
|
||||
fprintf(stderr, "Invalid length in hdr: %u\n",
|
||||
sh->rxmsg_bytes_missing);
|
||||
}
|
||||
} else {
|
||||
/* try to read as many of the missing bytes as are available */
|
||||
rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing);
|
||||
if (rc < 0) {
|
||||
perror("ERROR reading from serial port");
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
sh->rxmsg_bytes_missing -= rc;
|
||||
|
||||
if (sh->rxmsg_bytes_missing == 0) {
|
||||
/* we have one complete message now */
|
||||
sh->rx_msg = NULL;
|
||||
|
||||
if (msg->len > LAPD_HDR_LEN)
|
||||
msg->l2h = msg->data + LAPD_HDR_LEN;
|
||||
|
||||
DEBUGP(DMI, "RS232 RX: %s\n", hexdump(msg->data, msg->len));
|
||||
rc = handle_serial_msg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* select.c callback */
|
||||
static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (what & BSC_FD_READ)
|
||||
rc = handle_ser_read(bfd);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (what & BSC_FD_WRITE)
|
||||
rc = handle_ser_write(bfd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int rs232_setup(const char *serial_port, unsigned int delay_ms,
|
||||
struct gsm_bts *bts)
|
||||
{
|
||||
int rc, serial_fd;
|
||||
struct termios tio;
|
||||
|
||||
serial_fd = open(serial_port, O_RDWR);
|
||||
if (serial_fd < 0) {
|
||||
perror("cannot open serial port:");
|
||||
return serial_fd;
|
||||
}
|
||||
|
||||
/* set baudrate */
|
||||
rc = tcgetattr(serial_fd, &tio);
|
||||
if (rc < 0) {
|
||||
perror("tcgetattr()");
|
||||
return rc;
|
||||
}
|
||||
cfsetispeed(&tio, B19200);
|
||||
cfsetospeed(&tio, B19200);
|
||||
tio.c_cflag |= (CREAD | CLOCAL | CS8);
|
||||
tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
|
||||
tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
tio.c_iflag |= (INPCK | ISTRIP);
|
||||
tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
|
||||
tio.c_oflag &= ~(OPOST);
|
||||
rc = tcsetattr(serial_fd, TCSADRAIN, &tio);
|
||||
if (rc < 0) {
|
||||
perror("tcsetattr()");
|
||||
return rc;
|
||||
}
|
||||
|
||||
INIT_LLIST_HEAD(&ser_handle->tx_queue);
|
||||
ser_handle->fd.fd = serial_fd;
|
||||
ser_handle->fd.when = BSC_FD_READ;
|
||||
ser_handle->fd.cb = serial_fd_cb;
|
||||
ser_handle->fd.data = ser_handle;
|
||||
ser_handle->delay_ms = delay_ms;
|
||||
ser_handle->bts = bts;
|
||||
rc = bsc_register_fd(&ser_handle->fd);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "could not register FD: %s\n",
|
||||
strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/* select filedescriptor handling, taken from:
|
||||
* userspace logging daemon for the iptables ULOG target
|
||||
* of the linux 2.4 netfilter subsystem.
|
||||
*
|
||||
* (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <openbsc/select.h>
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/timer.h>
|
||||
|
||||
static int maxfd = 0;
|
||||
static LLIST_HEAD(bsc_fds);
|
||||
|
||||
int bsc_register_fd(struct bsc_fd *fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
/* make FD nonblocking */
|
||||
flags = fcntl(fd->fd, F_GETFL);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
flags |= O_NONBLOCK;
|
||||
flags = fcntl(fd->fd, F_SETFL, flags);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
/* Register FD */
|
||||
if (fd->fd > maxfd)
|
||||
maxfd = fd->fd;
|
||||
|
||||
llist_add_tail(&fd->list, &bsc_fds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bsc_unregister_fd(struct bsc_fd *fd)
|
||||
{
|
||||
llist_del(&fd->list);
|
||||
}
|
||||
|
||||
int bsc_select_main(int polling)
|
||||
{
|
||||
struct bsc_fd *ufd, *tmp;
|
||||
fd_set readset, writeset, exceptset;
|
||||
int work = 0, rc;
|
||||
struct timeval no_time = {0, 0};
|
||||
|
||||
FD_ZERO(&readset);
|
||||
FD_ZERO(&writeset);
|
||||
FD_ZERO(&exceptset);
|
||||
|
||||
/* prepare read and write fdsets */
|
||||
llist_for_each_entry(ufd, &bsc_fds, list) {
|
||||
if (ufd->when & BSC_FD_READ)
|
||||
FD_SET(ufd->fd, &readset);
|
||||
|
||||
if (ufd->when & BSC_FD_WRITE)
|
||||
FD_SET(ufd->fd, &writeset);
|
||||
|
||||
if (ufd->when & BSC_FD_EXCEPT)
|
||||
FD_SET(ufd->fd, &exceptset);
|
||||
}
|
||||
|
||||
if (!polling)
|
||||
bsc_prepare_timers();
|
||||
rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer());
|
||||
if (rc < 0)
|
||||
return 0;
|
||||
|
||||
/* fire timers */
|
||||
bsc_update_timers();
|
||||
|
||||
/* call registered callback functions */
|
||||
llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
|
||||
int flags = 0;
|
||||
|
||||
if (FD_ISSET(ufd->fd, &readset))
|
||||
flags |= BSC_FD_READ;
|
||||
|
||||
if (FD_ISSET(ufd->fd, &writeset))
|
||||
flags |= BSC_FD_WRITE;
|
||||
|
||||
if (FD_ISSET(ufd->fd, &exceptset))
|
||||
flags |= BSC_FD_EXCEPT;
|
||||
|
||||
if (flags) {
|
||||
work = 1;
|
||||
ufd->cb(ufd, flags);
|
||||
}
|
||||
}
|
||||
return work;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* Generic signalling/notification infrastructure */
|
||||
/* (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/signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static LLIST_HEAD(signal_handler_list);
|
||||
|
||||
struct signal_handler {
|
||||
struct llist_head entry;
|
||||
unsigned int subsys;
|
||||
signal_cbfn *cbfn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
|
||||
{
|
||||
struct signal_handler *sig_data = malloc(sizeof(*sig_data));
|
||||
|
||||
if (!sig_data)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(sig_data, 0, sizeof(*sig_data));
|
||||
|
||||
sig_data->subsys = subsys;
|
||||
sig_data->data = data;
|
||||
sig_data->cbfn = cbfn;
|
||||
|
||||
/* FIXME: check if we already have a handler for this subsys/cbfn/data */
|
||||
|
||||
llist_add_tail(&sig_data->entry, &signal_handler_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
|
||||
{
|
||||
struct signal_handler *handler;
|
||||
|
||||
llist_for_each_entry(handler, &signal_handler_list, entry) {
|
||||
if (handler->cbfn == cbfn && handler->data == data
|
||||
&& subsys == handler->subsys) {
|
||||
llist_del(&handler->entry);
|
||||
free(handler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data)
|
||||
{
|
||||
struct signal_handler *handler;
|
||||
|
||||
llist_for_each_entry(handler, &signal_handler_list, entry) {
|
||||
if (handler->subsys != subsys)
|
||||
continue;
|
||||
(*handler->cbfn)(subsys, signal, handler->data, signal_data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
|
||||
|
||||
/* (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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openbsc/subchan_demux.h>
|
||||
#include <openbsc/trau_frame.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
|
||||
{
|
||||
sch->out_bitbuf[sch->out_idx++] = bit;
|
||||
}
|
||||
|
||||
#define SYNC_HDR_BITS 16
|
||||
static const u_int8_t nullbytes[SYNC_HDR_BITS];
|
||||
|
||||
/* check if we have just completed the 16 bit zero sync header,
|
||||
* in accordance with GSM TS 08.60 Chapter 4.8.1 */
|
||||
static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit)
|
||||
{
|
||||
if (bit == 0)
|
||||
sch->consecutive_zeros++;
|
||||
else
|
||||
sch->consecutive_zeros = 0;
|
||||
|
||||
if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
|
||||
sch->consecutive_zeros = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* resynchronize to current location */
|
||||
static void resync_to_here(struct demux_subch *sch)
|
||||
{
|
||||
memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
|
||||
|
||||
/* set index in a way that we can continue receiving bits after
|
||||
* the end of the SYNC header */
|
||||
sch->out_idx = SYNC_HDR_BITS;
|
||||
sch->in_sync = 1;
|
||||
}
|
||||
|
||||
int subch_demux_init(struct subch_demux *dmx)
|
||||
{
|
||||
int i;
|
||||
|
||||
dmx->chan_activ = 0;
|
||||
for (i = 0; i < NR_SUBCH; i++) {
|
||||
struct demux_subch *sch = &dmx->subch[i];
|
||||
sch->out_idx = 0;
|
||||
memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
|
||||
* split it into the 16k subchannels */
|
||||
int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
|
||||
{
|
||||
int i, c;
|
||||
|
||||
/* we avoid partially filled bytes in outbuf */
|
||||
if (len % 4)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
u_int8_t inbyte = data[i];
|
||||
|
||||
for (c = 0; c < NR_SUBCH; c++) {
|
||||
struct demux_subch *sch = &dmx->subch[c];
|
||||
u_int8_t inbits;
|
||||
u_int8_t bit;
|
||||
|
||||
/* ignore inactive subchannels */
|
||||
if (!(dmx->chan_activ & (1 << c)))
|
||||
continue;
|
||||
|
||||
inbits = inbyte >> (c << 1);
|
||||
|
||||
/* two bits for each subchannel */
|
||||
if (inbits & 0x01)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
append_bit(sch, bit);
|
||||
|
||||
if (sync_hdr_complete(sch, bit))
|
||||
resync_to_here(sch);
|
||||
|
||||
if (inbits & 0x02)
|
||||
bit = 1;
|
||||
else
|
||||
bit = 0;
|
||||
append_bit(sch, bit);
|
||||
|
||||
if (sync_hdr_complete(sch, bit))
|
||||
resync_to_here(sch);
|
||||
|
||||
/* FIXME: verify the first bit in octet 2, 4, 6, ...
|
||||
* according to TS 08.60 4.8.1 */
|
||||
|
||||
/* once we have reached TRAU_FRAME_BITS, call
|
||||
* the TRAU frame handler callback function */
|
||||
if (sch->out_idx >= TRAU_FRAME_BITS) {
|
||||
if (sch->in_sync) {
|
||||
dmx->out_cb(dmx, c, sch->out_bitbuf,
|
||||
sch->out_idx, dmx->data);
|
||||
sch->in_sync = 0;
|
||||
}
|
||||
sch->out_idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int subch_demux_activate(struct subch_demux *dmx, int subch)
|
||||
{
|
||||
if (subch >= NR_SUBCH)
|
||||
return -EINVAL;
|
||||
|
||||
dmx->chan_activ |= (1 << subch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int subch_demux_deactivate(struct subch_demux *dmx, int subch)
|
||||
{
|
||||
if (subch >= NR_SUBCH)
|
||||
return -EINVAL;
|
||||
|
||||
dmx->chan_activ &= ~(1 << subch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MULTIPLEXER */
|
||||
|
||||
static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
|
||||
{
|
||||
/* allocate and initialize with idle pattern */
|
||||
return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(),
|
||||
TRAU_FRAME_BITS);
|
||||
}
|
||||
|
||||
/* return the requested number of bits from the specified subchannel */
|
||||
static int get_subch_bits(struct subch_mux *mx, int subch,
|
||||
u_int8_t *bits, int num_requested)
|
||||
{
|
||||
struct mux_subch *sch = &mx->subch[subch];
|
||||
int num_bits = 0;
|
||||
|
||||
while (num_bits < num_requested) {
|
||||
struct subch_txq_entry *txe;
|
||||
int num_bits_left;
|
||||
int num_bits_thistime;
|
||||
|
||||
/* make sure we have a valid entry at top of tx queue.
|
||||
* if not, add an idle frame */
|
||||
if (llist_empty(&sch->tx_queue))
|
||||
alloc_add_idle_frame(mx, subch);
|
||||
|
||||
if (llist_empty(&sch->tx_queue))
|
||||
return -EIO;
|
||||
|
||||
txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
|
||||
num_bits_left = txe->bit_len - txe->next_bit;
|
||||
|
||||
if (num_bits_left < num_requested)
|
||||
num_bits_thistime = num_bits_left;
|
||||
else
|
||||
num_bits_thistime = num_requested;
|
||||
|
||||
/* pull the bits from the txe */
|
||||
memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime);
|
||||
txe->next_bit += num_bits_thistime;
|
||||
|
||||
/* free the tx_queue entry if it is fully consumed */
|
||||
if (txe->next_bit >= txe->bit_len) {
|
||||
llist_del(&txe->list);
|
||||
free(txe);
|
||||
}
|
||||
|
||||
/* increment global number of bits dequeued */
|
||||
num_bits += num_bits_thistime;
|
||||
}
|
||||
|
||||
return num_requested;
|
||||
}
|
||||
|
||||
/* compact an array of 8 single-bit bytes into one byte of 8 bits */
|
||||
static u_int8_t compact_bits(const u_int8_t *bits)
|
||||
{
|
||||
u_int8_t ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
ret |= (bits[i] ? 1 : 0) << i;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* obtain a single output byte from the subchannel muxer */
|
||||
static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte)
|
||||
{
|
||||
u_int8_t bits[8];
|
||||
int rc;
|
||||
|
||||
/* combine two bits of every subchan */
|
||||
rc = get_subch_bits(mx, 0, &bits[0], 2);
|
||||
rc = get_subch_bits(mx, 1, &bits[2], 2);
|
||||
rc = get_subch_bits(mx, 2, &bits[4], 2);
|
||||
rc = get_subch_bits(mx, 3, &bits[6], 2);
|
||||
|
||||
*byte = compact_bits(bits);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Request the output of some muxed bytes from the subchan muxer */
|
||||
int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
int rc;
|
||||
rc = mux_output_byte(mx, &data[i]);
|
||||
if (rc < 0)
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int llist_len(struct llist_head *head)
|
||||
{
|
||||
struct llist_head *entry;
|
||||
int i = 0;
|
||||
|
||||
llist_for_each(entry, head)
|
||||
i++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* evict the 'num_evict' number of oldest entries in the queue */
|
||||
static void tx_queue_evict(struct mux_subch *sch, int num_evict)
|
||||
{
|
||||
struct subch_txq_entry *tqe;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_evict; i++) {
|
||||
if (llist_empty(&sch->tx_queue))
|
||||
return;
|
||||
|
||||
tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
|
||||
llist_del(&tqe->list);
|
||||
free(tqe);
|
||||
}
|
||||
}
|
||||
|
||||
/* enqueue some data into the tx_queue of a given subchannel */
|
||||
int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
|
||||
int len)
|
||||
{
|
||||
struct mux_subch *sch = &mx->subch[s_nr];
|
||||
struct subch_txq_entry *tqe = malloc(sizeof(*tqe) + len);
|
||||
int list_len = llist_len(&sch->tx_queue);
|
||||
|
||||
if (!tqe)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(tqe, 0, sizeof(*tqe));
|
||||
tqe->bit_len = len;
|
||||
memcpy(tqe->bits, data, len);
|
||||
|
||||
if (list_len > 2)
|
||||
tx_queue_evict(sch, list_len-2);
|
||||
|
||||
llist_add_tail(&tqe->list, &sch->tx_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* initialize one subchannel muxer instance */
|
||||
int subchan_mux_init(struct subch_mux *mx)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(mx, 0, sizeof(*mx));
|
||||
for (i = 0; i < NR_SUBCH; i++) {
|
||||
struct mux_subch *sch = &mx->subch[i];
|
||||
INIT_LLIST_HEAD(&sch->tx_queue);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/* minimalistic telnet/network interface it might turn into a wire interface */
|
||||
/* (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 <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openbsc/telnet_interface.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/gsm_04_11.h>
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
#include <vty/buffer.h>
|
||||
|
||||
#define WRITE_CONNECTION(fd, msg...) \
|
||||
int ret; \
|
||||
char buf[4096]; \
|
||||
snprintf(buf, sizeof(buf), msg); \
|
||||
ret = write(fd, buf, strlen(buf));
|
||||
|
||||
|
||||
/* per connection data */
|
||||
LLIST_HEAD(active_connections);
|
||||
|
||||
/* per network data */
|
||||
static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
|
||||
#if 0
|
||||
static int telnet_paging_callback(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data);
|
||||
static int telnet_sms_callback(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data);
|
||||
#endif
|
||||
|
||||
static struct bsc_fd server_socket = {
|
||||
.when = BSC_FD_READ,
|
||||
.cb = telnet_new_connection,
|
||||
.priv_nr = 0,
|
||||
};
|
||||
|
||||
void telnet_init(struct gsm_network *network, int port) {
|
||||
struct sockaddr_in sock_addr;
|
||||
int fd, on = 1;
|
||||
|
||||
bsc_vty_init(network);
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if (fd < 0) {
|
||||
perror("Telnet interface socket creation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
memset(&sock_addr, 0, sizeof(sock_addr));
|
||||
sock_addr.sin_family = AF_INET;
|
||||
sock_addr.sin_port = htons(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");
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(fd, 0) < 0) {
|
||||
perror("Telnet interface failed to listen");
|
||||
return;
|
||||
}
|
||||
|
||||
server_socket.data = network;
|
||||
server_socket.fd = fd;
|
||||
bsc_register_fd(&server_socket);
|
||||
|
||||
/* register callbacks */
|
||||
#if 0
|
||||
register_signal_handler(SS_PAGING, telnet_paging_callback, network);
|
||||
register_signal_handler(SS_SMS, telnet_sms_callback, network);
|
||||
#endif
|
||||
}
|
||||
|
||||
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"
|
||||
"Contributions by Daniel Willmann, Jan Lübbe, "
|
||||
"Stefan Schmidt, Holger Freyther\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 "
|
||||
"and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted "
|
||||
"by law.\nType \"help\" to get a short introduction.\n";
|
||||
|
||||
ret = write(fd, msg, strlen(msg));
|
||||
}
|
||||
|
||||
int telnet_close_client(struct bsc_fd *fd) {
|
||||
struct telnet_connection *conn = (struct telnet_connection*)fd->data;
|
||||
|
||||
close(fd->fd);
|
||||
bsc_unregister_fd(fd);
|
||||
llist_del(&conn->entry);
|
||||
free(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_data(struct bsc_fd *fd, unsigned int what)
|
||||
{
|
||||
struct telnet_connection *conn = fd->data;
|
||||
int rc = 0;
|
||||
|
||||
if (what & BSC_FD_READ) {
|
||||
conn->fd.when &= ~BSC_FD_READ;
|
||||
rc = vty_read(conn->vty);
|
||||
}
|
||||
|
||||
if (what & BSC_FD_WRITE) {
|
||||
rc = buffer_flush_all(conn->vty->obuf, fd->fd);
|
||||
if (rc == BUFFER_EMPTY)
|
||||
conn->fd.when &= ~BSC_FD_WRITE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
|
||||
struct telnet_connection *connection;
|
||||
struct sockaddr_in sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
|
||||
|
||||
if (new_connection < 0) {
|
||||
perror("telnet accept failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
connection = (struct telnet_connection*)malloc(sizeof(*connection));
|
||||
memset(connection, 0, sizeof(*connection));
|
||||
connection->network = (struct gsm_network*)fd->data;
|
||||
connection->fd.data = connection;
|
||||
connection->fd.fd = new_connection;
|
||||
connection->fd.when = BSC_FD_READ;
|
||||
connection->fd.cb = client_data;
|
||||
connection->bts = 0;
|
||||
bsc_register_fd(&connection->fd);
|
||||
llist_add_tail(&connection->entry, &active_connections);
|
||||
|
||||
print_welcome(new_connection);
|
||||
|
||||
connection->vty = vty_create(new_connection, connection);
|
||||
if (!connection->vty)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* callback from VTY code */
|
||||
void vty_event(enum event event, int sock, struct vty *vty)
|
||||
{
|
||||
struct telnet_connection *connection = vty->priv;
|
||||
struct bsc_fd *bfd = &connection->fd;
|
||||
|
||||
switch (event) {
|
||||
case VTY_READ:
|
||||
bfd->when |= BSC_FD_READ;
|
||||
break;
|
||||
case VTY_WRITE:
|
||||
bfd->when |= BSC_FD_WRITE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int telnet_paging_callback(unsigned int subsys, unsigned int singal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct paging_signal_data *paging = signal_data;
|
||||
struct telnet_connection *con;
|
||||
|
||||
llist_for_each_entry(con, &active_connections, entry) {
|
||||
if (paging->lchan) {
|
||||
WRITE_CONNECTION(con->fd.fd, "Paging succeeded\n");
|
||||
show_lchan(con->fd.fd, paging->lchan);
|
||||
} else {
|
||||
WRITE_CONNECTION(con->fd.fd, "Paging failed for subscriber: %s/%s/%s\n",
|
||||
paging->subscr->imsi,
|
||||
paging->subscr->tmsi,
|
||||
paging->subscr->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int telnet_sms_callback(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct sms_submit *sms = signal_data;
|
||||
struct telnet_connection *con;
|
||||
|
||||
llist_for_each_entry(con, &active_connections, entry) {
|
||||
WRITE_CONNECTION(con->fd.fd, "Incoming SMS: %s\n", sms->user_data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* (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
|
||||
* 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 <assert.h>
|
||||
#include <string.h>
|
||||
#include <openbsc/timer.h>
|
||||
|
||||
static LLIST_HEAD(timer_list);
|
||||
static struct timeval s_nearest_time;
|
||||
static struct timeval s_select_time;
|
||||
|
||||
#define MICRO_SECONDS 1000000LL
|
||||
|
||||
#define TIME_SMALLER(left, right) \
|
||||
(left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec)
|
||||
|
||||
void bsc_add_timer(struct timer_list *timer)
|
||||
{
|
||||
struct timer_list *list_timer;
|
||||
|
||||
/* TODO: Optimize and remember the closest item... */
|
||||
timer->active = 1;
|
||||
|
||||
/* this might be called from within update_timers */
|
||||
llist_for_each_entry(list_timer, &timer_list, entry)
|
||||
if (timer == list_timer)
|
||||
return;
|
||||
|
||||
timer->in_list = 1;
|
||||
llist_add(&timer->entry, &timer_list);
|
||||
}
|
||||
|
||||
void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds)
|
||||
{
|
||||
struct timeval current_time;
|
||||
|
||||
gettimeofday(¤t_time, NULL);
|
||||
unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
|
||||
currentTime += seconds * MICRO_SECONDS + microseconds;
|
||||
timer->timeout.tv_sec = currentTime / MICRO_SECONDS;
|
||||
timer->timeout.tv_usec = currentTime % MICRO_SECONDS;
|
||||
bsc_add_timer(timer);
|
||||
}
|
||||
|
||||
void bsc_del_timer(struct timer_list *timer)
|
||||
{
|
||||
if (timer->in_list) {
|
||||
timer->active = 0;
|
||||
timer->in_list = 0;
|
||||
llist_del(&timer->entry);
|
||||
}
|
||||
}
|
||||
|
||||
int bsc_timer_pending(struct timer_list *timer)
|
||||
{
|
||||
return timer->active;
|
||||
}
|
||||
|
||||
/*
|
||||
* if we have a nearest time return the delta between the current
|
||||
* time and the time of the nearest timer.
|
||||
* If the nearest timer timed out return NULL and then we will
|
||||
* dispatch everything after the select
|
||||
*/
|
||||
struct timeval *bsc_nearest_timer()
|
||||
{
|
||||
struct timeval current_time;
|
||||
|
||||
if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
|
||||
return NULL;
|
||||
|
||||
if (gettimeofday(¤t_time, NULL) == -1)
|
||||
return NULL;
|
||||
|
||||
unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec;
|
||||
unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
|
||||
|
||||
if (nearestTime < currentTime) {
|
||||
s_select_time.tv_sec = 0;
|
||||
s_select_time.tv_usec = 0;
|
||||
} else {
|
||||
s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
|
||||
s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
|
||||
}
|
||||
|
||||
return &s_select_time;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the nearest time and update s_nearest_time
|
||||
*/
|
||||
void bsc_prepare_timers()
|
||||
{
|
||||
struct timer_list *timer, *nearest_timer = NULL;
|
||||
llist_for_each_entry(timer, &timer_list, entry) {
|
||||
if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) {
|
||||
nearest_timer = timer;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest_timer) {
|
||||
s_nearest_time = nearest_timer->timeout;
|
||||
} else {
|
||||
memset(&s_nearest_time, 0, sizeof(struct timeval));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fire all timers... and remove them
|
||||
*/
|
||||
int bsc_update_timers()
|
||||
{
|
||||
struct timeval current_time;
|
||||
struct timer_list *timer, *tmp;
|
||||
int work = 0;
|
||||
|
||||
gettimeofday(¤t_time, NULL);
|
||||
|
||||
/*
|
||||
* The callbacks might mess with our list and in this case
|
||||
* even llist_for_each_entry_safe is not safe to use. To allow
|
||||
* del_timer, add_timer, schedule_timer to be called from within
|
||||
* the callback we jump through some loops.
|
||||
*
|
||||
* First we set the handled flag of each active timer to zero,
|
||||
* then we iterate over the list and execute the callbacks. As the
|
||||
* list might have been changed (specially the next) from within
|
||||
* the callback we have to start over again. Once every callback
|
||||
* is dispatched we will remove the non-active from the list.
|
||||
*
|
||||
* TODO: If this is a performance issue we can poison a global
|
||||
* variable in add_timer and del_timer and only then restart.
|
||||
*/
|
||||
llist_for_each_entry(timer, &timer_list, entry) {
|
||||
timer->handled = 0;
|
||||
}
|
||||
|
||||
restart:
|
||||
llist_for_each_entry(timer, &timer_list, entry) {
|
||||
if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) {
|
||||
timer->handled = 1;
|
||||
timer->active = 0;
|
||||
(*timer->cb)(timer->data);
|
||||
work = 1;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
llist_for_each_entry_safe(timer, tmp, &timer_list, entry) {
|
||||
timer->handled = 0;
|
||||
if (!timer->active) {
|
||||
bsc_del_timer(timer);
|
||||
}
|
||||
}
|
||||
|
||||
return work;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#include <stdio.h>
|
||||
#include <openbsc/tlv.h>
|
||||
|
||||
int tlv_dump(struct tlv_parsed *dec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 0xff; i++) {
|
||||
if (!dec->lv[i].val)
|
||||
continue;
|
||||
printf("T=%02x L=%d\n", i, dec->lv[i].len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dec: output: a caller-allocated pointer to a struct tlv_parsed,
|
||||
* def: input: a structure defining the valid TLV tags / configurations
|
||||
* buf: input: the input data buffer to be parsed
|
||||
* buf_len: input: the length of the input data buffer
|
||||
* lv_tag: input: an initial LV tag at the start of the buffer
|
||||
* lv_tag2: input: a second initial LV tag following lv_tag
|
||||
*/
|
||||
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)
|
||||
{
|
||||
u_int8_t tag, len = 1;
|
||||
const u_int8_t *pos = buf;
|
||||
int num_parsed = 0;
|
||||
|
||||
memset(dec, 0, sizeof(*dec));
|
||||
|
||||
if (lv_tag) {
|
||||
if (pos > buf + buf_len)
|
||||
return -1;
|
||||
dec->lv[lv_tag].val = pos+1;
|
||||
dec->lv[lv_tag].len = *pos;
|
||||
len = dec->lv[lv_tag].len + 1;
|
||||
if (pos + len > buf + buf_len)
|
||||
return -2;
|
||||
num_parsed++;
|
||||
pos += len;
|
||||
}
|
||||
if (lv_tag2) {
|
||||
if (pos > buf + buf_len)
|
||||
return -1;
|
||||
dec->lv[lv_tag2].val = pos+1;
|
||||
dec->lv[lv_tag2].len = *pos;
|
||||
len = dec->lv[lv_tag2].len + 1;
|
||||
if (pos + len > buf + buf_len)
|
||||
return -2;
|
||||
num_parsed++;
|
||||
pos += len;
|
||||
}
|
||||
|
||||
for (; pos < buf+buf_len; pos += len) {
|
||||
tag = *pos;
|
||||
/* FIXME: use tables for knwon IEI */
|
||||
switch (def->def[tag].type) {
|
||||
case TLV_TYPE_T:
|
||||
/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
|
||||
dec->lv[tag].val = pos;
|
||||
dec->lv[tag].len = 0;
|
||||
len = 1;
|
||||
num_parsed++;
|
||||
break;
|
||||
case TLV_TYPE_TV:
|
||||
dec->lv[tag].val = pos+1;
|
||||
dec->lv[tag].len = 1;
|
||||
len = 2;
|
||||
num_parsed++;
|
||||
break;
|
||||
case TLV_TYPE_FIXED:
|
||||
dec->lv[tag].val = pos+1;
|
||||
dec->lv[tag].len = def->def[tag].fixed_len;
|
||||
len = def->def[tag].fixed_len + 1;
|
||||
num_parsed++;
|
||||
break;
|
||||
case TLV_TYPE_TLV:
|
||||
/* GSM TS 04.07 11.2.4: Type 4 TLV */
|
||||
if (pos + 1 > buf + buf_len)
|
||||
return -1;
|
||||
dec->lv[tag].val = pos+2;
|
||||
dec->lv[tag].len = *(pos+1);
|
||||
len = dec->lv[tag].len + 2;
|
||||
if (pos + len > buf + buf_len)
|
||||
return -2;
|
||||
num_parsed++;
|
||||
break;
|
||||
case TLV_TYPE_TL16V:
|
||||
if (pos + 2 > buf + buf_len)
|
||||
return -1;
|
||||
dec->lv[tag].val = pos+3;
|
||||
dec->lv[tag].len = *(pos+1) << 8 | *(pos+2);
|
||||
len = dec->lv[tag].len + 3;
|
||||
if (pos + len > buf + buf_len)
|
||||
return -2;
|
||||
num_parsed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//tlv_dump(dec);
|
||||
return num_parsed;
|
||||
}
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
/* TRAU frame handling according to GSM TS 08.60 */
|
||||
|
||||
/* (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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openbsc/trau_frame.h>
|
||||
#include <openbsc/subchan_demux.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num)
|
||||
{
|
||||
int i;
|
||||
u_int32_t ret = 0;
|
||||
|
||||
for (i = offset; i < offset + num; i++) {
|
||||
ret = ret << 1;
|
||||
if (bitbuf[i])
|
||||
ret |= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Decode according to 3.1.1 */
|
||||
static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
|
||||
{
|
||||
int i;
|
||||
int d_idx = 0;
|
||||
|
||||
/* C1 .. C15 */
|
||||
memcpy(fr->c_bits+0, trau_bits+17, 15);
|
||||
/* C16 .. C21 */
|
||||
memcpy(fr->c_bits+15, trau_bits+310, 6);
|
||||
/* T1 .. T4 */
|
||||
memcpy(fr->t_bits+0, trau_bits+316, 4);
|
||||
/* D1 .. D255 */
|
||||
for (i = 32; i < 304; i+= 16) {
|
||||
memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
|
||||
d_idx += 15;
|
||||
}
|
||||
/* D256 .. D260 */
|
||||
memcpy(fr->d_bits + d_idx, trau_bits + 305, 5);
|
||||
}
|
||||
|
||||
/* Decode according to 3.1.2 */
|
||||
static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
|
||||
{
|
||||
int i;
|
||||
int d_idx = 0;
|
||||
|
||||
/* C1 .. C15 */
|
||||
memcpy(fr->c_bits+0, trau_bits+17, 15);
|
||||
/* C16 .. C25 */
|
||||
memcpy(fr->c_bits+15, trau_bits+33, 10);
|
||||
/* T1 .. T4 */
|
||||
memcpy(fr->t_bits+0, trau_bits+316, 4);
|
||||
/* D1 .. D5 */
|
||||
memcpy(fr->d_bits, trau_bits+43, 5);
|
||||
/* D6 .. D245 */
|
||||
for (i = 48; i < 304; i += 16) {
|
||||
memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
|
||||
d_idx += 15;
|
||||
}
|
||||
/* D246 .. D256 */
|
||||
memcpy(fr->d_bits + d_idx, trau_bits + 305, 11);
|
||||
}
|
||||
|
||||
int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
|
||||
{
|
||||
u_int8_t cbits5 = get_bits(trau_bits, 17, 5);
|
||||
|
||||
switch (cbits5) {
|
||||
case TRAU_FT_FR_UP:
|
||||
case TRAU_FT_FR_DOWN:
|
||||
case TRAU_FT_IDLE_UP:
|
||||
case TRAU_FT_IDLE_DOWN:
|
||||
case TRAU_FT_EFR:
|
||||
decode_fr(fr, trau_bits);
|
||||
break;
|
||||
case TRAU_FT_AMR:
|
||||
decode_amr(fr, trau_bits);
|
||||
break;
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
case TRAU_FT_DATA_UP:
|
||||
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);
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DMUX, "can't decode unknown TRAU Frame Type 0x%02x\n", cbits5);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 };
|
||||
const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 };
|
||||
|
||||
/* modify an uplink TRAU frame so we can send it downlink */
|
||||
int trau_frame_up2down(struct decoded_trau_frame *fr)
|
||||
{
|
||||
u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
|
||||
|
||||
switch (cbits5) {
|
||||
case TRAU_FT_FR_UP:
|
||||
memcpy(fr->c_bits, ft_fr_down_bits, 5);
|
||||
/* clear time alignment */
|
||||
memset(fr->c_bits+5, 0, 6);
|
||||
/* FIXME: SP / BFI in case of DTx */
|
||||
/* C12 .. C21 are spare and coded as '1' */
|
||||
memset(fr->c_bits+11, 0x01, 10);
|
||||
break;
|
||||
case TRAU_FT_EFR:
|
||||
/* clear time alignment */
|
||||
memset(fr->c_bits+5, 0, 6);
|
||||
/* FIXME: set UFE appropriately */
|
||||
/* FIXME: SP / BFI in case of DTx */
|
||||
break;
|
||||
case TRAU_FT_IDLE_UP:
|
||||
memcpy(fr->c_bits, ft_idle_down_bits, 5);
|
||||
/* clear time alignment */
|
||||
memset(fr->c_bits+5, 0, 6);
|
||||
/* FIXME: SP / BFI in case of DTx */
|
||||
/* C12 .. C21 are spare and coded as '1' */
|
||||
memset(fr->c_bits+11, 0x01, 10);
|
||||
break;
|
||||
case TRAU_FT_FR_DOWN:
|
||||
case TRAU_FT_IDLE_DOWN:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
case TRAU_FT_DATA_DOWN:
|
||||
/* we cannot convert a downlink to a downlink frame */
|
||||
return -EINVAL;
|
||||
break;
|
||||
case TRAU_FT_AMR:
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_DATA_UP:
|
||||
case TRAU_FT_D145_SYNC:
|
||||
case TRAU_FT_EDATA:
|
||||
DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
{
|
||||
int i;
|
||||
int d_idx = 0;
|
||||
|
||||
trau_bits[16] = 1;
|
||||
/* C1 .. C15 */
|
||||
memcpy(trau_bits+17, fr->c_bits+0, 15);
|
||||
/* D1 .. D255 */
|
||||
for (i = 32; i < 304; i+= 16) {
|
||||
trau_bits[i] = 1;
|
||||
memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15);
|
||||
d_idx += 15;
|
||||
}
|
||||
/* D256 .. D260 */
|
||||
trau_bits[304] = 1;
|
||||
memcpy(trau_bits + 305, fr->d_bits + d_idx, 5);
|
||||
/* C16 .. C21 */
|
||||
memcpy(trau_bits+310, fr->c_bits+15, 6);
|
||||
|
||||
/* FIXME: handle timing adjustment */
|
||||
|
||||
/* T1 .. T4 */
|
||||
memcpy(trau_bits+316, fr->t_bits+0, 4);
|
||||
}
|
||||
|
||||
|
||||
int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
|
||||
{
|
||||
u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
|
||||
|
||||
/* 16 bits of sync header */
|
||||
memset(trau_bits, 0, 16);
|
||||
|
||||
switch (cbits5) {
|
||||
case TRAU_FT_FR_UP:
|
||||
case TRAU_FT_FR_DOWN:
|
||||
case TRAU_FT_IDLE_UP:
|
||||
case TRAU_FT_IDLE_DOWN:
|
||||
case TRAU_FT_EFR:
|
||||
encode_fr(trau_bits, fr);
|
||||
break;
|
||||
case TRAU_FT_AMR:
|
||||
case TRAU_FT_OM_UP:
|
||||
case TRAU_FT_OM_DOWN:
|
||||
case TRAU_FT_DATA_UP:
|
||||
case TRAU_FT_DATA_DOWN:
|
||||
case TRAU_FT_D145_SYNC:
|
||||
case TRAU_FT_EDATA:
|
||||
DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct decoded_trau_frame fr_idle_frame = {
|
||||
.c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */
|
||||
.t_bits = { 1, 1, 1, 1 },
|
||||
};
|
||||
static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS];
|
||||
static int dbits_initted;
|
||||
|
||||
u_int8_t *trau_idle_frame(void)
|
||||
{
|
||||
/* only initialize during the first call */
|
||||
if (!dbits_initted) {
|
||||
/* set all D-bits to 1 */
|
||||
memset(&fr_idle_frame.d_bits, 0x01, 260);
|
||||
encode_fr(encoded_idle_frame, &fr_idle_frame);
|
||||
}
|
||||
return encoded_idle_frame;
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
/* Simple TRAU frame reflector to route voice calls */
|
||||
|
||||
/* (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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/trau_frame.h>
|
||||
#include <openbsc/trau_mux.h>
|
||||
#include <openbsc/subchan_demux.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
struct map_entry {
|
||||
struct llist_head list;
|
||||
struct gsm_e1_subslot src, dst;
|
||||
};
|
||||
|
||||
struct upqueue_entry {
|
||||
struct llist_head list;
|
||||
struct gsm_network *net;
|
||||
struct gsm_e1_subslot src;
|
||||
u_int32_t callref;
|
||||
};
|
||||
|
||||
static LLIST_HEAD(ss_map);
|
||||
static LLIST_HEAD(ss_upqueue);
|
||||
|
||||
/* map one particular subslot to another subslot */
|
||||
int trau_mux_map(const struct gsm_e1_subslot *src,
|
||||
const struct gsm_e1_subslot *dst)
|
||||
{
|
||||
struct map_entry *me = malloc(sizeof(*me));
|
||||
if (!me)
|
||||
return -ENOMEM;
|
||||
|
||||
DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
|
||||
"and (e1=%u,ts=%u,ss=%u)\n",
|
||||
src->e1_nr, src->e1_ts, src->e1_ts_ss,
|
||||
dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
|
||||
|
||||
/* make sure to get rid of any stale old mappings */
|
||||
trau_mux_unmap(src, 0);
|
||||
trau_mux_unmap(dst, 0);
|
||||
|
||||
memcpy(&me->src, src, sizeof(me->src));
|
||||
memcpy(&me->dst, dst, sizeof(me->dst));
|
||||
llist_add(&me->list, &ss_map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trau_mux_map_lchan(const struct gsm_lchan *src,
|
||||
const struct gsm_lchan *dst)
|
||||
{
|
||||
struct gsm_e1_subslot *src_ss, *dst_ss;
|
||||
|
||||
src_ss = &src->ts->e1_link;
|
||||
dst_ss = &dst->ts->e1_link;
|
||||
|
||||
return trau_mux_map(src_ss, dst_ss);
|
||||
}
|
||||
|
||||
|
||||
/* unmap one particular subslot from another subslot */
|
||||
int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref)
|
||||
{
|
||||
struct map_entry *me, *me2;
|
||||
struct upqueue_entry *ue, *ue2;
|
||||
|
||||
if (ss)
|
||||
llist_for_each_entry_safe(me, me2, &ss_map, list) {
|
||||
if (!memcmp(&me->src, ss, sizeof(*ss)) ||
|
||||
!memcmp(&me->dst, ss, sizeof(*ss))) {
|
||||
llist_del(&me->list);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
|
||||
if (ue->callref == callref) {
|
||||
llist_del(&ue->list);
|
||||
return 0;
|
||||
}
|
||||
if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
|
||||
llist_del(&ue->list);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* look-up an enty in the TRAU mux map */
|
||||
static struct gsm_e1_subslot *
|
||||
lookup_trau_mux_map(const struct gsm_e1_subslot *src)
|
||||
{
|
||||
struct map_entry *me;
|
||||
|
||||
llist_for_each_entry(me, &ss_map, list) {
|
||||
if (!memcmp(&me->src, src, sizeof(*src)))
|
||||
return &me->dst;
|
||||
if (!memcmp(&me->dst, src, sizeof(*src)))
|
||||
return &me->src;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up an enty in the TRAU upqueue */
|
||||
struct upqueue_entry *
|
||||
lookup_trau_upqueue(const struct gsm_e1_subslot *src)
|
||||
{
|
||||
struct upqueue_entry *ue;
|
||||
|
||||
llist_for_each_entry(ue, &ss_upqueue, list) {
|
||||
if (!memcmp(&ue->src, src, sizeof(*src)))
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct decoded_trau_frame tf;
|
||||
u_int8_t trau_bits_out[TRAU_FRAME_BITS];
|
||||
struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
|
||||
struct subch_mux *mx;
|
||||
int rc;
|
||||
|
||||
/* decode TRAU, change it to downlink, re-encode */
|
||||
rc = decode_trau_frame(&tf, trau_bits);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!dst_e1_ss)
|
||||
return -EINVAL;
|
||||
|
||||
mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
|
||||
if (!mx)
|
||||
return -EINVAL;
|
||||
|
||||
trau_frame_up2down(&tf);
|
||||
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,
|
||||
TRAU_FRAME_BITS);
|
||||
}
|
||||
|
||||
/* add receiver instance for lchan and callref */
|
||||
int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
|
||||
{
|
||||
struct gsm_e1_subslot *src_ss;
|
||||
struct upqueue_entry *ue = malloc(sizeof(*ue));
|
||||
|
||||
if (!ue)
|
||||
return -ENOMEM;
|
||||
|
||||
src_ss = &lchan->ts->e1_link;
|
||||
|
||||
DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
|
||||
"and (callref 0x%x)\n",
|
||||
src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
|
||||
callref);
|
||||
|
||||
/* make sure to get rid of any stale old mappings */
|
||||
trau_mux_unmap(src_ss, callref);
|
||||
|
||||
memcpy(&ue->src, src_ss, sizeof(ue->src));
|
||||
ue->net = lchan->ts->trx->bts->network;
|
||||
ue->callref = callref;
|
||||
llist_add(&ue->list, &ss_upqueue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
|
||||
{
|
||||
u_int8_t trau_bits_out[TRAU_FRAME_BITS];
|
||||
struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
|
||||
struct subch_mux *mx;
|
||||
|
||||
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);
|
||||
|
||||
/* and send it to the muxer */
|
||||
return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
|
||||
TRAU_FRAME_BITS);
|
||||
}
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* Buffering of output and input.
|
||||
* Copyright (C) 1998 Kunihiro Ishiguro
|
||||
*
|
||||
* This file is part of GNU Zebra.
|
||||
*
|
||||
* GNU Zebra 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, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <vty/buffer.h>
|
||||
#include <vty/vty.h>
|
||||
|
||||
/* Buffer master. */
|
||||
struct buffer {
|
||||
/* Data list. */
|
||||
struct buffer_data *head;
|
||||
struct buffer_data *tail;
|
||||
|
||||
/* Size of each buffer_data chunk. */
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* Data container. */
|
||||
struct buffer_data {
|
||||
struct buffer_data *next;
|
||||
|
||||
/* Location to add new data. */
|
||||
size_t cp;
|
||||
|
||||
/* Pointer to data not yet flushed. */
|
||||
size_t sp;
|
||||
|
||||
/* Actual data stream (variable length). */
|
||||
unsigned char data[0]; /* real dimension is buffer->size */
|
||||
};
|
||||
|
||||
/* It should always be true that: 0 <= sp <= cp <= size */
|
||||
|
||||
/* Default buffer size (used if none specified). It is rounded up to the
|
||||
next page boundery. */
|
||||
#define BUFFER_SIZE_DEFAULT 4096
|
||||
|
||||
#define BUFFER_DATA_FREE(D) free((D))
|
||||
|
||||
/* Make new buffer. */
|
||||
struct buffer *buffer_new(size_t size)
|
||||
{
|
||||
struct buffer *b;
|
||||
|
||||
b = calloc(1, sizeof(struct buffer));
|
||||
|
||||
if (size)
|
||||
b->size = size;
|
||||
else {
|
||||
static size_t default_size;
|
||||
if (!default_size) {
|
||||
long pgsz = sysconf(_SC_PAGESIZE);
|
||||
default_size =
|
||||
((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
|
||||
}
|
||||
b->size = default_size;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Free buffer. */
|
||||
void buffer_free(struct buffer *b)
|
||||
{
|
||||
buffer_reset(b);
|
||||
free(b);
|
||||
}
|
||||
|
||||
/* Make string clone. */
|
||||
char *buffer_getstr(struct buffer *b)
|
||||
{
|
||||
size_t totlen = 0;
|
||||
struct buffer_data *data;
|
||||
char *s;
|
||||
char *p;
|
||||
|
||||
for (data = b->head; data; data = data->next)
|
||||
totlen += data->cp - data->sp;
|
||||
if (!(s = malloc(totlen + 1)))
|
||||
return NULL;
|
||||
p = s;
|
||||
for (data = b->head; data; data = data->next) {
|
||||
memcpy(p, data->data + data->sp, data->cp - data->sp);
|
||||
p += data->cp - data->sp;
|
||||
}
|
||||
*p = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return 1 if buffer is empty. */
|
||||
int buffer_empty(struct buffer *b)
|
||||
{
|
||||
return (b->head == NULL);
|
||||
}
|
||||
|
||||
/* Clear and free all allocated data. */
|
||||
void buffer_reset(struct buffer *b)
|
||||
{
|
||||
struct buffer_data *data;
|
||||
struct buffer_data *next;
|
||||
|
||||
for (data = b->head; data; data = next) {
|
||||
next = data->next;
|
||||
BUFFER_DATA_FREE(data);
|
||||
}
|
||||
b->head = b->tail = NULL;
|
||||
}
|
||||
|
||||
/* Add buffer_data to the end of buffer. */
|
||||
static struct buffer_data *buffer_add(struct buffer *b)
|
||||
{
|
||||
struct buffer_data *d;
|
||||
|
||||
d = malloc(offsetof(struct buffer_data, data[b->size]));
|
||||
if (!d)
|
||||
return NULL;
|
||||
d->cp = d->sp = 0;
|
||||
d->next = NULL;
|
||||
|
||||
if (b->tail)
|
||||
b->tail->next = d;
|
||||
else
|
||||
b->head = d;
|
||||
b->tail = d;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Write data to buffer. */
|
||||
void buffer_put(struct buffer *b, const void *p, size_t size)
|
||||
{
|
||||
struct buffer_data *data = b->tail;
|
||||
const char *ptr = p;
|
||||
|
||||
/* We use even last one byte of data buffer. */
|
||||
while (size) {
|
||||
size_t chunk;
|
||||
|
||||
/* If there is no data buffer add it. */
|
||||
if (data == NULL || data->cp == b->size)
|
||||
data = buffer_add(b);
|
||||
|
||||
chunk =
|
||||
((size <=
|
||||
(b->size - data->cp)) ? size : (b->size - data->cp));
|
||||
memcpy((data->data + data->cp), ptr, chunk);
|
||||
size -= chunk;
|
||||
ptr += chunk;
|
||||
data->cp += chunk;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert character into the buffer. */
|
||||
void buffer_putc(struct buffer *b, u_char c)
|
||||
{
|
||||
buffer_put(b, &c, 1);
|
||||
}
|
||||
|
||||
/* Put string to the buffer. */
|
||||
void buffer_putstr(struct buffer *b, const char *c)
|
||||
{
|
||||
buffer_put(b, c, strlen(c));
|
||||
}
|
||||
|
||||
/* Keep flushing data to the fd until the buffer is empty or an error is
|
||||
encountered or the operation would block. */
|
||||
buffer_status_t buffer_flush_all(struct buffer *b, int fd)
|
||||
{
|
||||
buffer_status_t ret;
|
||||
struct buffer_data *head;
|
||||
size_t head_sp;
|
||||
|
||||
if (!b->head)
|
||||
return BUFFER_EMPTY;
|
||||
head_sp = (head = b->head)->sp;
|
||||
/* Flush all data. */
|
||||
while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
|
||||
if ((b->head == head) && (head_sp == head->sp)
|
||||
&& (errno != EINTR))
|
||||
/* No data was flushed, so kernel buffer must be full. */
|
||||
return ret;
|
||||
head_sp = (head = b->head)->sp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Flush enough data to fill a terminal window of the given scene (used only
|
||||
by vty telnet interface). */
|
||||
buffer_status_t
|
||||
buffer_flush_window(struct buffer * b, int fd, int width, int height,
|
||||
int erase_flag, int no_more_flag)
|
||||
{
|
||||
int nbytes;
|
||||
int iov_alloc;
|
||||
int iov_index;
|
||||
struct iovec *iov;
|
||||
struct iovec small_iov[3];
|
||||
char more[] = " --More-- ";
|
||||
char erase[] =
|
||||
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
|
||||
};
|
||||
struct buffer_data *data;
|
||||
int column;
|
||||
|
||||
if (!b->head)
|
||||
return BUFFER_EMPTY;
|
||||
|
||||
if (height < 1) {
|
||||
zlog_warn
|
||||
("%s called with non-positive window height %d, forcing to 1",
|
||||
__func__, height);
|
||||
height = 1;
|
||||
} else if (height >= 2)
|
||||
height--;
|
||||
if (width < 1) {
|
||||
zlog_warn
|
||||
("%s called with non-positive window width %d, forcing to 1",
|
||||
__func__, width);
|
||||
width = 1;
|
||||
}
|
||||
|
||||
/* For erase and more data add two to b's buffer_data count. */
|
||||
if (b->head->next == NULL) {
|
||||
iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
|
||||
iov = small_iov;
|
||||
} else {
|
||||
iov_alloc = ((height * (width + 2)) / b->size) + 10;
|
||||
iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
|
||||
}
|
||||
iov_index = 0;
|
||||
|
||||
/* Previously print out is performed. */
|
||||
if (erase_flag) {
|
||||
iov[iov_index].iov_base = erase;
|
||||
iov[iov_index].iov_len = sizeof erase;
|
||||
iov_index++;
|
||||
}
|
||||
|
||||
/* Output data. */
|
||||
column = 1; /* Column position of next character displayed. */
|
||||
for (data = b->head; data && (height > 0); data = data->next) {
|
||||
size_t cp;
|
||||
|
||||
cp = data->sp;
|
||||
while ((cp < data->cp) && (height > 0)) {
|
||||
/* Calculate lines remaining and column position after displaying
|
||||
this character. */
|
||||
if (data->data[cp] == '\r')
|
||||
column = 1;
|
||||
else if ((data->data[cp] == '\n') || (column == width)) {
|
||||
column = 1;
|
||||
height--;
|
||||
} else
|
||||
column++;
|
||||
cp++;
|
||||
}
|
||||
iov[iov_index].iov_base = (char *)(data->data + data->sp);
|
||||
iov[iov_index++].iov_len = cp - data->sp;
|
||||
data->sp = cp;
|
||||
|
||||
if (iov_index == iov_alloc)
|
||||
/* This should not ordinarily happen. */
|
||||
{
|
||||
iov_alloc *= 2;
|
||||
if (iov != small_iov) {
|
||||
zlog_warn("%s: growing iov array to %d; "
|
||||
"width %d, height %d, size %lu",
|
||||
__func__, iov_alloc, width, height,
|
||||
(u_long) b->size);
|
||||
iov =
|
||||
XREALLOC(MTYPE_TMP, iov,
|
||||
iov_alloc * sizeof(*iov));
|
||||
} else {
|
||||
/* This should absolutely never occur. */
|
||||
zlog_err
|
||||
("%s: corruption detected: iov_small overflowed; "
|
||||
"head %p, tail %p, head->next %p",
|
||||
__func__, b->head, b->tail, b->head->next);
|
||||
iov =
|
||||
XMALLOC(MTYPE_TMP,
|
||||
iov_alloc * sizeof(*iov));
|
||||
memcpy(iov, small_iov, sizeof(small_iov));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of `more' display need. */
|
||||
if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
|
||||
iov[iov_index].iov_base = more;
|
||||
iov[iov_index].iov_len = sizeof more;
|
||||
iov_index++;
|
||||
}
|
||||
#ifdef IOV_MAX
|
||||
/* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
|
||||
example: Solaris2.6 are defined IOV_MAX size at 16. */
|
||||
{
|
||||
struct iovec *c_iov = iov;
|
||||
nbytes = 0; /* Make sure it's initialized. */
|
||||
|
||||
while (iov_index > 0) {
|
||||
int iov_size;
|
||||
|
||||
iov_size =
|
||||
((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
|
||||
if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
|
||||
zlog_warn("%s: writev to fd %d failed: %s",
|
||||
__func__, fd, safe_strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
/* move pointer io-vector */
|
||||
c_iov += iov_size;
|
||||
iov_index -= iov_size;
|
||||
}
|
||||
}
|
||||
#else /* IOV_MAX */
|
||||
if ((nbytes = writev(fd, iov, iov_index)) < 0)
|
||||
zlog_warn("%s: writev to fd %d failed: %s",
|
||||
__func__, fd, safe_strerror(errno));
|
||||
#endif /* IOV_MAX */
|
||||
|
||||
/* Free printed buffer data. */
|
||||
while (b->head && (b->head->sp == b->head->cp)) {
|
||||
struct buffer_data *del;
|
||||
if (!(b->head = (del = b->head)->next))
|
||||
b->tail = NULL;
|
||||
BUFFER_DATA_FREE(del);
|
||||
}
|
||||
|
||||
if (iov != small_iov)
|
||||
XFREE(MTYPE_TMP, iov);
|
||||
|
||||
return (nbytes < 0) ? BUFFER_ERROR :
|
||||
(b->head ? BUFFER_PENDING : BUFFER_EMPTY);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This function (unlike other buffer_flush* functions above) is designed
|
||||
to work with non-blocking sockets. It does not attempt to write out
|
||||
all of the queued data, just a "big" chunk. It returns 0 if it was
|
||||
able to empty out the buffers completely, 1 if more flushing is
|
||||
required later, or -1 on a fatal write error. */
|
||||
buffer_status_t buffer_flush_available(struct buffer * b, int fd)
|
||||
{
|
||||
|
||||
/* These are just reasonable values to make sure a significant amount of
|
||||
data is written. There's no need to go crazy and try to write it all
|
||||
in one shot. */
|
||||
#ifdef IOV_MAX
|
||||
#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
|
||||
#else
|
||||
#define MAX_CHUNKS 16
|
||||
#endif
|
||||
#define MAX_FLUSH 131072
|
||||
|
||||
struct buffer_data *d;
|
||||
size_t written;
|
||||
struct iovec iov[MAX_CHUNKS];
|
||||
size_t iovcnt = 0;
|
||||
size_t nbyte = 0;
|
||||
|
||||
for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
|
||||
d = d->next, iovcnt++) {
|
||||
iov[iovcnt].iov_base = d->data + d->sp;
|
||||
nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
|
||||
}
|
||||
|
||||
if (!nbyte)
|
||||
/* No data to flush: should we issue a warning message? */
|
||||
return BUFFER_EMPTY;
|
||||
|
||||
/* only place where written should be sign compared */
|
||||
if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
|
||||
if (ERRNO_IO_RETRY(errno))
|
||||
/* Calling code should try again later. */
|
||||
return BUFFER_PENDING;
|
||||
return BUFFER_ERROR;
|
||||
}
|
||||
|
||||
/* Free printed buffer data. */
|
||||
while (written > 0) {
|
||||
struct buffer_data *d;
|
||||
if (!(d = b->head))
|
||||
break;
|
||||
if (written < d->cp - d->sp) {
|
||||
d->sp += written;
|
||||
return BUFFER_PENDING;
|
||||
}
|
||||
|
||||
written -= (d->cp - d->sp);
|
||||
if (!(b->head = d->next))
|
||||
b->tail = NULL;
|
||||
BUFFER_DATA_FREE(d);
|
||||
}
|
||||
|
||||
return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
|
||||
|
||||
#undef MAX_CHUNKS
|
||||
#undef MAX_FLUSH
|
||||
}
|
||||
|
||||
buffer_status_t
|
||||
buffer_write(struct buffer * b, int fd, const void *p, size_t size)
|
||||
{
|
||||
ssize_t nbytes;
|
||||
|
||||
#if 0
|
||||
/* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
|
||||
|
||||
if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
|
||||
return BUFFER_ERROR;
|
||||
#endif
|
||||
if (b->head)
|
||||
/* Buffer is not empty, so do not attempt to write the new data. */
|
||||
nbytes = 0;
|
||||
else if ((nbytes = write(fd, p, size)) < 0) {
|
||||
if (ERRNO_IO_RETRY(errno))
|
||||
nbytes = 0;
|
||||
else
|
||||
return BUFFER_ERROR;
|
||||
}
|
||||
/* Add any remaining data to the buffer. */
|
||||
{
|
||||
size_t written = nbytes;
|
||||
if (written < size)
|
||||
buffer_put(b, ((const char *)p) + written,
|
||||
size - written);
|
||||
}
|
||||
return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#define QUAGGA_PROGNAME "OpenBSC"
|
||||
#define QUAGGA_VERSION "0.01"
|
||||
#define QUAGGA_COPYRIGHT "Harald Welte <laforge@gnumonks.org>"
|
||||
#define CONFIGFILE_MASK 022
|
||||
#define SYSCONFDIR "/usr/local/etc"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,186 @@
|
|||
/* Generic vector interface routine
|
||||
* Copyright (C) 1997 Kunihiro Ishiguro
|
||||
*
|
||||
* This file is part of GNU Zebra.
|
||||
*
|
||||
* GNU Zebra 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, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
|
||||
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vty/vector.h>
|
||||
#include <memory.h>
|
||||
|
||||
/* Initialize vector : allocate memory and return vector. */
|
||||
vector vector_init(unsigned int size)
|
||||
{
|
||||
vector v = calloc(1, sizeof(struct _vector));
|
||||
if (!v)
|
||||
return NULL;
|
||||
|
||||
/* allocate at least one slot */
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
|
||||
v->alloced = size;
|
||||
v->active = 0;
|
||||
v->index = calloc(1, sizeof(void *) * size);
|
||||
if (!v->index) {
|
||||
free(v);
|
||||
return NULL;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void vector_only_wrapper_free(vector v)
|
||||
{
|
||||
free(v);
|
||||
}
|
||||
|
||||
void vector_only_index_free(void *index)
|
||||
{
|
||||
free(index);
|
||||
}
|
||||
|
||||
void vector_free(vector v)
|
||||
{
|
||||
free(v->index);
|
||||
free(v);
|
||||
}
|
||||
|
||||
vector vector_copy(vector v)
|
||||
{
|
||||
unsigned int size;
|
||||
vector new = calloc(1, sizeof(struct _vector));
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
new->active = v->active;
|
||||
new->alloced = v->alloced;
|
||||
|
||||
size = sizeof(void *) * (v->alloced);
|
||||
new->index = calloc(1, size);
|
||||
if (!new->index) {
|
||||
free(new);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(new->index, v->index, size);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/* Check assigned index, and if it runs short double index pointer */
|
||||
void vector_ensure(vector v, unsigned int num)
|
||||
{
|
||||
if (v->alloced > num)
|
||||
return;
|
||||
|
||||
v->index = realloc(v->index, sizeof(void *) * (v->alloced * 2));
|
||||
memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
|
||||
v->alloced *= 2;
|
||||
|
||||
if (v->alloced <= num)
|
||||
vector_ensure(v, num);
|
||||
}
|
||||
|
||||
/* This function only returns next empty slot index. It dose not mean
|
||||
the slot's index memory is assigned, please call vector_ensure()
|
||||
after calling this function. */
|
||||
int vector_empty_slot(vector v)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (v->active == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < v->active; i++)
|
||||
if (v->index[i] == 0)
|
||||
return i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Set value to the smallest empty slot. */
|
||||
int vector_set(vector v, void *val)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
i = vector_empty_slot(v);
|
||||
vector_ensure(v, i);
|
||||
|
||||
v->index[i] = val;
|
||||
|
||||
if (v->active <= i)
|
||||
v->active = i + 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Set value to specified index slot. */
|
||||
int vector_set_index(vector v, unsigned int i, void *val)
|
||||
{
|
||||
vector_ensure(v, i);
|
||||
|
||||
v->index[i] = val;
|
||||
|
||||
if (v->active <= i)
|
||||
v->active = i + 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Look up vector. */
|
||||
void *vector_lookup(vector v, unsigned int i)
|
||||
{
|
||||
if (i >= v->active)
|
||||
return NULL;
|
||||
return v->index[i];
|
||||
}
|
||||
|
||||
/* Lookup vector, ensure it. */
|
||||
void *vector_lookup_ensure(vector v, unsigned int i)
|
||||
{
|
||||
vector_ensure(v, i);
|
||||
return v->index[i];
|
||||
}
|
||||
|
||||
/* Unset value at specified index slot. */
|
||||
void vector_unset(vector v, unsigned int i)
|
||||
{
|
||||
if (i >= v->alloced)
|
||||
return;
|
||||
|
||||
v->index[i] = NULL;
|
||||
|
||||
if (i + 1 == v->active) {
|
||||
v->active--;
|
||||
while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */
|
||||
}
|
||||
}
|
||||
|
||||
/* Count the number of not emplty slot. */
|
||||
unsigned int vector_count(vector v)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned count = 0;
|
||||
|
||||
for (i = 0; i < v->active; i++)
|
||||
if (v->index[i] != NULL)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,905 @@
|
|||
/* OpenBSC interface to quagga VTY */
|
||||
/* (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 <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <vty/command.h>
|
||||
#include <vty/vty.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/db.h>
|
||||
|
||||
static struct gsm_network *gsmnet;
|
||||
|
||||
struct cmd_node bts_node = {
|
||||
BTS_NODE,
|
||||
"%s(bts)#",
|
||||
1,
|
||||
};
|
||||
|
||||
struct cmd_node trx_node = {
|
||||
TRX_NODE,
|
||||
"%s(trx)#",
|
||||
1,
|
||||
};
|
||||
|
||||
struct cmd_node ts_node = {
|
||||
TS_NODE,
|
||||
"%s(ts)#",
|
||||
1,
|
||||
};
|
||||
|
||||
struct cmd_node subscr_node = {
|
||||
SUBSCR_NODE,
|
||||
"%s(subscriber)#",
|
||||
1,
|
||||
};
|
||||
|
||||
static int dummy_config_write(struct vty *v)
|
||||
{
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
|
||||
{
|
||||
vty_out(vty,"Oper '%s', Admin %u, Avail '%s'%s",
|
||||
nm_opstate_name(nms->operational), nms->administrative,
|
||||
nm_avail_name(nms->availability), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void net_dump_vty(struct vty *vty, struct gsm_network *net)
|
||||
{
|
||||
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);
|
||||
vty_out(vty, " Long network name: '%s'%s",
|
||||
net->name_long, VTY_NEWLINE);
|
||||
vty_out(vty, " Short network name: '%s'%s",
|
||||
net->name_short, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_net, show_net_cmd, "show network",
|
||||
SHOW_STR "Display information about a GSM NETWORK\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
net_dump_vty(vty, net);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
|
||||
if (!e1l) {
|
||||
vty_out(vty, " None%s", VTY_NEWLINE);
|
||||
return;
|
||||
}
|
||||
|
||||
line = e1l->ts->line;
|
||||
|
||||
vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s",
|
||||
line->num, line->driver->name, e1l->ts->num,
|
||||
e1inp_signtype_name(e1l->type), VTY_NEWLINE);
|
||||
vty_out(vty, " E1 TEI %u, SAPI %u%s",
|
||||
e1l->tei, e1l->sapi, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
vty_out(vty, "BTS %u is of %s type, has LAC %u, BSIC %u, TSC %u and %u TRX%s",
|
||||
bts->nr, btstype2str(bts->type), bts->location_area_code,
|
||||
bts->bsic, bts->tsc, bts->num_trx, VTY_NEWLINE);
|
||||
if (is_ipaccess_bts(bts))
|
||||
vty_out(vty, " Unit ID: %u/%u/0%s",
|
||||
bts->ip_access.site_id, bts->ip_access.bts_id,
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " NM State: ");
|
||||
net_dump_nmstate(vty, &bts->nm_state);
|
||||
vty_out(vty, " Site Mgr NM State: ");
|
||||
net_dump_nmstate(vty, &bts->site_mgr.nm_state);
|
||||
vty_out(vty, " Paging: FIXME pending requests, %u free slots%s",
|
||||
bts->paging.available_slots, VTY_NEWLINE);
|
||||
vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
|
||||
e1isl_dump_vty(vty, bts->oml_link);
|
||||
/* FIXME: oml_link, chan_desc */
|
||||
}
|
||||
|
||||
DEFUN(show_bts, show_bts_cmd, "show bts [number]",
|
||||
SHOW_STR "Display information about a BTS\n"
|
||||
"BTS number")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
int bts_nr;
|
||||
|
||||
if (argc != 0) {
|
||||
/* use the BTS number that the user has specified */
|
||||
bts_nr = atoi(argv[0]);
|
||||
if (bts_nr > net->num_bts) {
|
||||
vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts_dump_vty(vty, &net->bts[bts_nr]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
/* print all BTS's */
|
||||
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++)
|
||||
bts_dump_vty(vty, &net->bts[bts_nr]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx)
|
||||
{
|
||||
vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
|
||||
trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
|
||||
vty_out(vty, " NM State: ");
|
||||
net_dump_nmstate(vty, &trx->nm_state);
|
||||
vty_out(vty, " Baseband Transceiver NM State: ");
|
||||
net_dump_nmstate(vty, &trx->bb_transc.nm_state);
|
||||
vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
|
||||
e1isl_dump_vty(vty, trx->rsl_link);
|
||||
}
|
||||
|
||||
DEFUN(show_trx,
|
||||
show_trx_cmd,
|
||||
"show trx [bts_nr] [trx_nr]",
|
||||
SHOW_STR "Display information about a TRX\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
struct gsm_bts *bts = NULL;
|
||||
struct gsm_bts_trx *trx;
|
||||
int bts_nr, trx_nr;
|
||||
|
||||
if (argc >= 1) {
|
||||
/* use the BTS number that the user has specified */
|
||||
bts_nr = atoi(argv[0]);
|
||||
if (bts_nr >= net->num_bts) {
|
||||
vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts = &net->bts[bts_nr];
|
||||
}
|
||||
if (argc >= 2) {
|
||||
trx_nr = atoi(argv[1]);
|
||||
if (trx_nr >= bts->num_trx) {
|
||||
vty_out(vty, "%% can't find TRX '%s'%s", argv[1],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
trx = &bts->trx[trx_nr];
|
||||
trx_dump_vty(vty, trx);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
if (bts) {
|
||||
/* print all TRX in this BTS */
|
||||
for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
|
||||
trx = &bts->trx[trx_nr];
|
||||
trx_dump_vty(vty, trx);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
|
||||
bts = &net->bts[bts_nr];
|
||||
for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
|
||||
trx = &bts->trx[trx_nr];
|
||||
trx_dump_vty(vty, trx);
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
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 FC=%u F8=%u%s",
|
||||
inet_ntoa(ia), ts->abis_ip.bound_port,
|
||||
ts->abis_ip.attr_fc, ts->abis_ip.attr_f8,
|
||||
VTY_NEWLINE);
|
||||
} else {
|
||||
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,
|
||||
show_ts_cmd,
|
||||
"show timeslot [bts_nr] [trx_nr] [ts_nr]",
|
||||
SHOW_STR "Display information about a TS\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
struct gsm_bts *bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
int bts_nr, trx_nr, ts_nr;
|
||||
|
||||
if (argc >= 1) {
|
||||
/* use the BTS number that the user has specified */
|
||||
bts_nr = atoi(argv[0]);
|
||||
if (bts_nr >= net->num_bts) {
|
||||
vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts = &net->bts[bts_nr];
|
||||
}
|
||||
if (argc >= 2) {
|
||||
trx_nr = atoi(argv[1]);
|
||||
if (trx_nr >= bts->num_trx) {
|
||||
vty_out(vty, "%% can't find TRX '%s'%s", argv[1],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
trx = &bts->trx[trx_nr];
|
||||
}
|
||||
if (argc >= 3) {
|
||||
ts_nr = atoi(argv[2]);
|
||||
if (ts_nr >= TRX_NR_TS) {
|
||||
vty_out(vty, "%% can't find TS '%s'%s", argv[2],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
ts = &trx->ts[ts_nr];
|
||||
ts_dump_vty(vty, ts);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
|
||||
bts = &net->bts[bts_nr];
|
||||
for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
|
||||
trx = &bts->trx[trx_nr];
|
||||
for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
|
||||
ts = &trx->ts[ts_nr];
|
||||
ts_dump_vty(vty, ts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
||||
{
|
||||
vty_out(vty, " ID: %lu, 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)
|
||||
vty_out(vty, " TMSI: %s%s", subscr->tmsi, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
|
||||
{
|
||||
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),
|
||||
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);
|
||||
}
|
||||
|
||||
static void call_dump_vty(struct vty *vty, struct gsm_call *call)
|
||||
{
|
||||
vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s",
|
||||
call->type, call->state, call->transaction_id, VTY_NEWLINE);
|
||||
|
||||
if (call->local_lchan) {
|
||||
vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE);
|
||||
lchan_dump_vty(vty, call->local_lchan);
|
||||
} else
|
||||
vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE);
|
||||
|
||||
if (call->remote_lchan) {
|
||||
vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE);
|
||||
lchan_dump_vty(vty, call->remote_lchan);
|
||||
} else
|
||||
vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE);
|
||||
|
||||
if (call->called_subscr) {
|
||||
vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE);
|
||||
subscr_dump_vty(vty, call->called_subscr);
|
||||
} else
|
||||
vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_lchan,
|
||||
show_lchan_cmd,
|
||||
"show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
|
||||
SHOW_STR "Display information about a logical channel\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
struct gsm_bts *bts;
|
||||
struct gsm_bts_trx *trx;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
struct gsm_lchan *lchan;
|
||||
int bts_nr, trx_nr, ts_nr, lchan_nr;
|
||||
|
||||
if (argc >= 1) {
|
||||
/* use the BTS number that the user has specified */
|
||||
bts_nr = atoi(argv[0]);
|
||||
if (bts_nr >= net->num_bts) {
|
||||
vty_out(vty, "%% can't find BTS %s%s", argv[0],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts = &net->bts[bts_nr];
|
||||
}
|
||||
if (argc >= 2) {
|
||||
trx_nr = atoi(argv[1]);
|
||||
if (trx_nr >= bts->num_trx) {
|
||||
vty_out(vty, "%% can't find TRX %s%s", argv[1],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
trx = &bts->trx[trx_nr];
|
||||
}
|
||||
if (argc >= 3) {
|
||||
ts_nr = atoi(argv[2]);
|
||||
if (ts_nr >= TRX_NR_TS) {
|
||||
vty_out(vty, "%% can't find TS %s%s", argv[2],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
ts = &trx->ts[ts_nr];
|
||||
}
|
||||
if (argc >= 4) {
|
||||
lchan_nr = atoi(argv[3]);
|
||||
if (lchan_nr >= TS_MAX_LCHAN) {
|
||||
vty_out(vty, "%% can't find LCHAN %s%s", argv[3],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
lchan = &ts->lchan[lchan_nr];
|
||||
lchan_dump_vty(vty, lchan);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
|
||||
bts = &net->bts[bts_nr];
|
||||
for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
|
||||
trx = &bts->trx[trx_nr];
|
||||
for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
|
||||
ts = &trx->ts[ts_nr];
|
||||
for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN;
|
||||
lchan_nr++) {
|
||||
lchan = &ts->lchan[lchan_nr];
|
||||
if (lchan->type == GSM_LCHAN_NONE)
|
||||
continue;
|
||||
lchan_dump_vty(vty, lchan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv)
|
||||
{
|
||||
vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_e1drv,
|
||||
show_e1drv_cmd,
|
||||
"show e1_driver",
|
||||
SHOW_STR "Display information about available E1 drivers\n")
|
||||
{
|
||||
struct e1inp_driver *drv;
|
||||
|
||||
llist_for_each_entry(drv, &e1inp_driver_list, list)
|
||||
e1drv_dump_vty(vty, drv);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line)
|
||||
{
|
||||
vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s",
|
||||
line->num, line->name ? line->name : "",
|
||||
line->driver->name, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_e1line,
|
||||
show_e1line_cmd,
|
||||
"show e1_line [line_nr]",
|
||||
SHOW_STR "Display information about a E1 line\n")
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
|
||||
if (argc >= 1) {
|
||||
int num = atoi(argv[0]);
|
||||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
if (line->num == num) {
|
||||
e1line_dump_vty(vty, line);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
}
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
llist_for_each_entry(line, &e1inp_line_list, list)
|
||||
e1line_dump_vty(vty, line);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts)
|
||||
{
|
||||
vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s",
|
||||
ts->num, ts->line->num, e1inp_tstype_name(ts->type),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
DEFUN(show_e1ts,
|
||||
show_e1ts_cmd,
|
||||
"show e1_timeslot [line_nr] [ts_nr]",
|
||||
SHOW_STR "Display information about a E1 timeslot\n")
|
||||
{
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *ts;
|
||||
int ts_nr;
|
||||
|
||||
if (argc == 0) {
|
||||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
|
||||
ts = &line->ts[ts_nr];
|
||||
e1ts_dump_vty(vty, ts);
|
||||
}
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
if (argc >= 1) {
|
||||
int num = atoi(argv[0]);
|
||||
llist_for_each_entry(line, &e1inp_line_list, list) {
|
||||
if (line->num == num)
|
||||
break;
|
||||
}
|
||||
if (!line || line->num != num) {
|
||||
vty_out(vty, "E1 line %s is invalid%s",
|
||||
argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
}
|
||||
if (argc >= 2) {
|
||||
ts_nr = atoi(argv[1]);
|
||||
if (ts_nr > NUM_E1_TS) {
|
||||
vty_out(vty, "E1 timeslot %s is invalid%s",
|
||||
argv[1], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
ts = &line->ts[ts_nr];
|
||||
e1ts_dump_vty(vty, ts);
|
||||
return CMD_SUCCESS;
|
||||
} else {
|
||||
for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
|
||||
ts = &line->ts[ts_nr];
|
||||
e1ts_dump_vty(vty, ts);
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag)
|
||||
{
|
||||
vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE);
|
||||
subscr_dump_vty(vty, pag->subscr);
|
||||
}
|
||||
|
||||
static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_paging_request *pag;
|
||||
|
||||
llist_for_each_entry(pag, &bts->paging.pending_requests, entry)
|
||||
paging_dump_vty(vty, pag);
|
||||
}
|
||||
|
||||
DEFUN(show_paging,
|
||||
show_paging_cmd,
|
||||
"show paging [bts_nr]",
|
||||
SHOW_STR "Display information about paging reuqests of a BTS\n")
|
||||
{
|
||||
struct gsm_network *net = gsmnet;
|
||||
struct gsm_bts *bts;
|
||||
int bts_nr;
|
||||
|
||||
if (argc >= 1) {
|
||||
/* use the BTS number that the user has specified */
|
||||
bts_nr = atoi(argv[0]);
|
||||
if (bts_nr >= net->num_bts) {
|
||||
vty_out(vty, "%% can't find BTS %s%s", argv[0],
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts = &net->bts[bts_nr];
|
||||
bts_paging_dump_vty(vty, bts);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
|
||||
bts = &net->bts[bts_nr];
|
||||
bts_paging_dump_vty(vty, bts);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per-subscriber configuration */
|
||||
DEFUN(cfg_subscr,
|
||||
cfg_subscr_cmd,
|
||||
"subscriber IMSI",
|
||||
"Select a Subscriber to configure\n")
|
||||
{
|
||||
const char *imsi = argv[0];
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
subscr = subscr_get_by_imsi(imsi);
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% No subscriber for IMSI %s%s",
|
||||
imsi, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->index = subscr;
|
||||
vty->node = SUBSCR_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* per-BTS configuration */
|
||||
DEFUN(cfg_bts,
|
||||
cfg_bts_cmd,
|
||||
"bts BTS_NR",
|
||||
"Select a BTS to configure\n")
|
||||
{
|
||||
int bts_nr = atoi(argv[0]);
|
||||
struct gsm_bts *bts;
|
||||
|
||||
if (bts_nr >= GSM_MAX_BTS) {
|
||||
vty_out(vty, "%% This Version of OpenBSC only supports %u BTS%s",
|
||||
GSM_MAX_BTS, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts = &gsmnet->bts[bts_nr];
|
||||
if (bts_nr >= gsmnet->num_bts)
|
||||
gsmnet->num_bts = bts_nr + 1;
|
||||
|
||||
vty->index = bts;
|
||||
vty->node = BTS_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_type,
|
||||
cfg_bts_type_cmd,
|
||||
"type TYPE",
|
||||
"Set the BTS type\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
|
||||
//bts->type =
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_lac,
|
||||
cfg_bts_lac_cmd,
|
||||
"location_area_code <0-255>",
|
||||
"Set the Location Area Code (LAC) of this BTS\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int lac = atoi(argv[0]);
|
||||
|
||||
if (lac < 0 || lac > 0xff) {
|
||||
vty_out(vty, "%% LAC %d is not in the valid range (0-255)%s",
|
||||
lac, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts->location_area_code = lac;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_tsc,
|
||||
cfg_bts_tsc_cmd,
|
||||
"training_sequence_code <0-255>",
|
||||
"Set the Training Sequence Code (TSC) of this BTS\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int tsc = atoi(argv[0]);
|
||||
|
||||
if (tsc < 0 || tsc > 0xff) {
|
||||
vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s",
|
||||
tsc, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts->tsc = tsc;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bts_bsic,
|
||||
cfg_bts_bsic_cmd,
|
||||
"base_station_id_code <0-63>",
|
||||
"Set the Base Station Identity Code (BSIC) of this BTS\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int bsic = atoi(argv[0]);
|
||||
|
||||
if (bsic < 0 || bsic > 0x3f) {
|
||||
vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s",
|
||||
bsic, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts->bsic = bsic;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_bts_unit_id,
|
||||
cfg_bts_unit_id_cmd,
|
||||
"unit_id <0-65534> <0-255>",
|
||||
"Set the BTS Unit ID of this BTS\n")
|
||||
{
|
||||
struct gsm_bts *bts = vty->index;
|
||||
int site_id = atoi(argv[0]);
|
||||
int bts_id = atoi(argv[1]);
|
||||
|
||||
if (site_id < 0 || site_id > 65534) {
|
||||
vty_out(vty, "%% Site ID %d is not in the valid range%s",
|
||||
site_id, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (site_id < 0 || site_id > 255) {
|
||||
vty_out(vty, "%% BTS ID %d is not in the valid range%s",
|
||||
bts_id, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
bts->ip_access.site_id = site_id;
|
||||
bts->ip_access.bts_id = bts_id;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per TRX configuration */
|
||||
DEFUN(cfg_trx,
|
||||
cfg_trx_cmd,
|
||||
"trx TRX_NR",
|
||||
"Select a TRX to configure")
|
||||
{
|
||||
int trx_nr = atoi(argv[0]);
|
||||
struct gsm_bts *bts = vty->index;
|
||||
struct gsm_bts_trx *trx;
|
||||
|
||||
if (trx_nr > BTS_MAX_TRX) {
|
||||
vty_out(vty, "%% This version of OpenBSC only supports %u TRX%s",
|
||||
BTS_MAX_TRX+1, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (trx_nr >= bts->num_trx)
|
||||
bts->num_trx = trx_nr+1;
|
||||
|
||||
trx = &bts->trx[trx_nr];
|
||||
|
||||
vty->index = trx;
|
||||
vty->node = TRX_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_trx_arfcn,
|
||||
cfg_trx_arfcn_cmd,
|
||||
"arfcn <1-1024>",
|
||||
"Set the ARFCN for this TRX\n")
|
||||
{
|
||||
int arfcn = atoi(argv[0]);
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
|
||||
/* FIXME: check if this ARFCN is supported by this TRX */
|
||||
|
||||
trx->arfcn = arfcn;
|
||||
|
||||
/* FIXME: patch ARFCN into SYSTEM INFORMATION */
|
||||
/* FIXME: use OML layer to update the ARFCN */
|
||||
/* FIXME: use RSL layer to update SYSTEM INFORMATION */
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per TS configuration */
|
||||
DEFUN(cfg_ts,
|
||||
cfg_ts_cmd,
|
||||
"timeslot TS_NR",
|
||||
"Select a Timeslot to configure")
|
||||
{
|
||||
int ts_nr = atoi(argv[0]);
|
||||
struct gsm_bts_trx *trx = vty->index;
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
|
||||
if (ts_nr >= TRX_NR_TS) {
|
||||
vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s",
|
||||
TRX_NR_TS, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
ts = &trx->ts[ts_nr];
|
||||
|
||||
vty->index = ts;
|
||||
vty->node = TS_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* Subscriber */
|
||||
DEFUN(show_subscr,
|
||||
show_subscr_cmd,
|
||||
"show subscriber [IMSI]",
|
||||
SHOW_STR "Display information about a subscriber\n")
|
||||
{
|
||||
const char *imsi;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
if (argc >= 1) {
|
||||
imsi = argv[0];
|
||||
subscr = subscr_get_by_imsi(imsi);
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% unknown subscriber%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
subscr_dump_vty(vty, subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: iterate over all subscribers ? */
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_name,
|
||||
cfg_subscr_name_cmd,
|
||||
"name NAME",
|
||||
"Set the name of the subscriber")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
strncpy(subscr->name, name, sizeof(subscr->name));
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_extension,
|
||||
cfg_subscr_extension_cmd,
|
||||
"extension EXTENSION",
|
||||
"Set the extension of the subscriber")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
strncpy(subscr->extension, name, sizeof(subscr->extension));
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_authorized,
|
||||
cfg_subscr_authorized_cmd,
|
||||
"auth <0-1>",
|
||||
"Set the authorization status of the subscriber")
|
||||
{
|
||||
int auth = atoi(argv[0]);
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
if (auth)
|
||||
subscr->authorized = 1;
|
||||
else
|
||||
subscr->authorized = 0;
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int bsc_vty_init(struct gsm_network *net)
|
||||
{
|
||||
gsmnet = net;
|
||||
|
||||
cmd_init(1);
|
||||
vty_init();
|
||||
|
||||
install_element(VIEW_NODE, &show_net_cmd);
|
||||
install_element(VIEW_NODE, &show_bts_cmd);
|
||||
install_element(VIEW_NODE, &show_trx_cmd);
|
||||
install_element(VIEW_NODE, &show_ts_cmd);
|
||||
install_element(VIEW_NODE, &show_lchan_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &show_e1drv_cmd);
|
||||
install_element(VIEW_NODE, &show_e1line_cmd);
|
||||
install_element(VIEW_NODE, &show_e1ts_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &show_paging_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &show_subscr_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_bts_cmd);
|
||||
install_node(&bts_node, dummy_config_write);
|
||||
install_default(BTS_NODE);
|
||||
install_element(BTS_NODE, &cfg_bts_type_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_lac_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_tsc_cmd);
|
||||
install_element(BTS_NODE, &cfg_bts_unit_id_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_ts_cmd);
|
||||
install_node(&ts_node, dummy_config_write);
|
||||
install_default(TS_NODE);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_subscr_cmd);
|
||||
install_node(&subscr_node, dummy_config_write);
|
||||
install_default(SUBSCR_NODE);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_name_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue