mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-10-22 23:32:00 +00:00
Putting the actual OpenBTS P2.8 source code into the public SVN branch.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2242 19bc5d8c-e614-43d4-8b26-e1612bc8e597
This commit is contained in:
192
AUTHORS
Normal file
192
AUTHORS
Normal file
@@ -0,0 +1,192 @@
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GNU Radio
|
||||
#
|
||||
# GNU Radio 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 3, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# GNU Radio 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.
|
||||
#
|
||||
|
||||
David A. Burgess, dburgess@kestrelsp.com:
|
||||
CLI/CLI.cpp
|
||||
CLI/CLI.h
|
||||
CommonLibs/Assert.h
|
||||
CommonLibs/BitVector.cpp
|
||||
CommonLibs/BitVectorTest.cpp
|
||||
CommonLibs/Configuration.cpp
|
||||
CommonLibs/Configuration.h
|
||||
CommonLibs/ConfigurationTest.cpp
|
||||
CommonLibs/Interthread.h
|
||||
CommonLibs/InterthreadTest.cpp
|
||||
CommonLibs/LinkedLists.cpp
|
||||
CommonLibs/LinkedLists.h
|
||||
CommonLibs/Regexp.h
|
||||
CommonLibs/RegexpTest.cpp
|
||||
CommonLibs/Sockets.cpp
|
||||
CommonLibs/Sockets.h
|
||||
CommonLibs/SocketsTest.cpp
|
||||
CommonLibs/Threads.cpp
|
||||
CommonLibs/Threads.h
|
||||
CommonLibs/Timeval.cpp
|
||||
CommonLibs/Timeval.h
|
||||
CommonLibs/TimevalTest.cpp
|
||||
CommonLibs/Vector.h
|
||||
CommonLibs/VectorTest.cpp
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
Control/SDCCHDispatch.cpp
|
||||
GSM/GSM610Tables.cpp
|
||||
GSM/GSM610Tables.h
|
||||
GSM/GSMCommon.cpp
|
||||
GSM/GSMCommon.h
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSML1FEC.cpp
|
||||
GSM/GSML1FEC.h
|
||||
GSM/GSML2LAPDm.cpp
|
||||
GSM/GSML2LAPDm.h
|
||||
GSM/GSML3CCElements.cpp
|
||||
GSM/GSML3CCElements.h
|
||||
GSM/GSML3CCMessages.cpp
|
||||
GSM/GSML3CCMessages.h
|
||||
GSM/GSML3CommonElements.cpp
|
||||
GSM/GSML3CommonElements.h
|
||||
GSM/GSML3MMElements.cpp
|
||||
GSM/GSML3MMElements.h
|
||||
GSM/GSML3MMMessages.cpp
|
||||
GSM/GSML3MMMessages.h
|
||||
GSM/GSML3Message.cpp
|
||||
GSM/GSML3Message.h
|
||||
GSM/GSML3RRElements.cpp
|
||||
GSM/GSML3RRElements.h
|
||||
GSM/GSML3RRMessages.cpp
|
||||
GSM/GSML3RRMessages.h
|
||||
GSM/GSMLogicalChannel.h
|
||||
GSM/GSMTDMA.cpp
|
||||
GSM/GSMTDMA.h
|
||||
GSM/GSMTransfer.cpp
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.h
|
||||
SIP/SIPInterface.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
TRXManager/TRXManager.cpp
|
||||
Transceiver/Complex.h
|
||||
apps/OpenBTS900.cpp
|
||||
apps/OpenBTS850.cpp
|
||||
apps/OpenBTS25c3.cpp
|
||||
tests/AGCHTest.cpp
|
||||
tests/BeaconTest.cpp
|
||||
tests/CallTest.cpp
|
||||
tests/CallTest2.cpp
|
||||
tests/LAPDmTest.cpp
|
||||
tests/LoopbackTest.cpp
|
||||
tests/RegistrationTest.cpp
|
||||
tests/TRXSimulator.cpp
|
||||
|
||||
Harvind S. Samra, hssamra@kestrelsp.com:
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
Transceiver/ComplexTest.cpp
|
||||
Transceiver/Transceiver.cpp
|
||||
Transceiver/Transceiver.h
|
||||
Transceiver/USRPDevice.cpp
|
||||
Transceiver/USRPDevice.h
|
||||
Transceiver/USRPping.cpp
|
||||
Transceiver/radioInterface.cpp
|
||||
Transceiver/radioInterface.h
|
||||
Transceiver/rcvLPF_651.h
|
||||
Transceiver/runTransceiver.cpp
|
||||
Transceiver/sendLPF_961.h
|
||||
Transceiver/sigProcLib.cpp
|
||||
Transceiver/sigProcLib.h
|
||||
Transceiver/sigProcLibTest.cpp
|
||||
Transceiver/sweepGenerator.cpp
|
||||
Transceiver/testRadio.cpp
|
||||
|
||||
Raffi Sevlian, raffisev@gmail.com:
|
||||
Control/CallControl.cpp
|
||||
Control/ControlCommon.cpp
|
||||
Control/ControlCommon.h
|
||||
Control/FACCHDispatch.cpp
|
||||
Control/MobilityManagement.cpp
|
||||
Control/PagerTest.cpp
|
||||
Control/RadioResource.cpp
|
||||
GSM/GSMCommon.h
|
||||
GSM/GSMConfig.h
|
||||
GSM/GSML1FEC.h
|
||||
GSM/GSML3CCElements.cpp
|
||||
GSM/GSML3CCElements.h
|
||||
GSM/GSML3CCMessages.cpp
|
||||
GSM/GSML3CCMessages.h
|
||||
GSM/GSML3CommonElements.cpp
|
||||
GSM/GSML3CommonElements.h
|
||||
GSM/GSML3MMElements.cpp
|
||||
GSM/GSML3MMElements.h
|
||||
GSM/GSML3MMMessages.cpp
|
||||
GSM/GSML3MMMessages.h
|
||||
GSM/GSML3Message.cpp
|
||||
GSM/GSML3Message.h
|
||||
GSM/GSML3RRElements.cpp
|
||||
GSM/GSML3RRElements.h
|
||||
GSM/GSML3RRMessages.cpp
|
||||
GSM/GSML3RRMessages.h
|
||||
GSM/GSMLogicalChannel.h
|
||||
GSM/GSMSAPMux.cpp
|
||||
GSM/GSMSAPMux.h
|
||||
GSM/GSMTransfer.h
|
||||
LICENSEBLOCK
|
||||
SIP/SIPEngine.cpp
|
||||
SIP/SIPInterface.cpp
|
||||
SIP/SIPInterface.h
|
||||
SIP/SIPMessage.cpp
|
||||
SIP/SIPMessage.h
|
||||
SIP/SIPUtility.cpp
|
||||
SIP/SIPUtility.h
|
||||
SMS/CMMessage.cpp
|
||||
SMS/CMMessage.h
|
||||
SMS/CMProcessor.cpp
|
||||
SMS/CMProcessor.h
|
||||
SMS/CMTest.cpp
|
||||
SMS/RLMessage.cpp
|
||||
SMS/RLMessage.h
|
||||
SMS/RLProcessor.cpp
|
||||
SMS/RLProcessor.h
|
||||
SMS/SMSMessages.cpp
|
||||
SMS/SMSMessages.h
|
||||
SMS/SMSProcessors.cpp
|
||||
SMS/SMSProcessors.h
|
||||
SMS/SMSTransfer.cpp
|
||||
SMS/SMSTransfer.h
|
||||
SMS/TLMessage.cpp
|
||||
SMS/TLMessage.h
|
||||
SMS/TLProcessor.cpp
|
||||
SMS/TLProcessor.h
|
||||
TRXManager/TRXManager.h
|
||||
|
||||
Alon Levy, alonlevy1@gmail.com
|
||||
RRLPMessages.cpp
|
||||
RRLPMessages.h
|
||||
RRLPTest.cpp
|
||||
|
||||
33
AsteriskConfig/Makefile.am
Normal file
33
AsteriskConfig/Makefile.am
Normal file
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
# Install files in this directory
|
||||
ourdatadir = $(datadir)/OpenBTS/Asterisk
|
||||
|
||||
dist_ourdata_DATA = \
|
||||
cdr.conf \
|
||||
extensions.conf \
|
||||
indications.conf \
|
||||
logger.conf \
|
||||
modules.conf \
|
||||
README.AsteriskConf \
|
||||
sip.conf
|
||||
450
AsteriskConfig/Makefile.in
Normal file
450
AsteriskConfig/Makefile.in
Normal file
@@ -0,0 +1,450 @@
|
||||
# Makefile.in generated by automake 1.9.4 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004 Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = ..
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
DIST_COMMON = $(dist_ourdata_DATA) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(top_srcdir)/Makefile.common
|
||||
subdir = AsteriskConfig
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
SOURCES =
|
||||
DIST_SOURCES =
|
||||
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
||||
am__vpath_adj = case $$p in \
|
||||
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
*) f=$$p;; \
|
||||
esac;
|
||||
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
|
||||
am__installdirs = "$(DESTDIR)$(ourdatadir)"
|
||||
dist_ourdataDATA_INSTALL = $(INSTALL_DATA)
|
||||
DATA = $(dist_ourdata_DATA)
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
ECHO = @ECHO@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
F77 = @F77@
|
||||
FFLAGS = @FFLAGS@
|
||||
GREP = @GREP@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIBUSB_CFLAGS = @LIBUSB_CFLAGS@
|
||||
LIBUSB_LIBS = @LIBUSB_LIBS@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
ORTP_CFLAGS = @ORTP_CFLAGS@
|
||||
ORTP_LIBS = @ORTP_LIBS@
|
||||
OSIP_CFLAGS = @OSIP_CFLAGS@
|
||||
OSIP_LIBS = @OSIP_LIBS@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
RANLIB = @RANLIB@
|
||||
RM_PROG = @RM_PROG@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_F77 = @ac_ct_F77@
|
||||
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
|
||||
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
CONTROL_INCLUDEDIR = $(top_srcdir)/Control
|
||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||
SIP_INCLUDEDIR = $(top_srcdir)/SIP
|
||||
SMS_INCLUDEDIR = $(top_srcdir)/SMS
|
||||
TRX_INCLUDEDIR = $(top_srcdir)/TRXManager
|
||||
GLOBALS_INCLUDEDIR = $(top_srcdir)/Globals
|
||||
CLI_INCLUDEDIR = $(top_srcdir)/CLI
|
||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||
SR_INCLUDEDIR = $(top_srcdir)/SR
|
||||
STD_DEFINES_AND_INCLUDES = \
|
||||
-I$(COMMON_INCLUDEDIR) \
|
||||
-I$(CONTROL_INCLUDEDIR) \
|
||||
-I$(GSM_INCLUDEDIR) \
|
||||
-I$(SIP_INCLUDEDIR) \
|
||||
-I$(SMS_INCLUDEDIR) \
|
||||
-I$(TRX_INCLUDEDIR) \
|
||||
-I$(GLOBALS_INCLUDEDIR) \
|
||||
-I$(CLI_INCLUDEDIR) \
|
||||
-I$(SR_INCLUDEDIR) \
|
||||
-I$(SQLITE_INCLUDEDIR)
|
||||
|
||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||
SIP_LA = $(top_builddir)/SIP/libSIP.la
|
||||
SMS_LA = $(top_builddir)/SMS/libSMS.la
|
||||
TRX_LA = $(top_builddir)/TRXManager/libtrxmanager.la
|
||||
CONTROL_LA = $(top_builddir)/Control/libcontrol.la
|
||||
GLOBALS_LA = $(top_builddir)/Globals/libglobals.la
|
||||
CLI_LA = $(top_builddir)/CLI/libcli.la
|
||||
SR_LA = $(top_builddir)/SR/libSR.la
|
||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la
|
||||
MOSTLYCLEANFILES = *~
|
||||
|
||||
# Install files in this directory
|
||||
ourdatadir = $(datadir)/OpenBTS/Asterisk
|
||||
dist_ourdata_DATA = \
|
||||
cdr.conf \
|
||||
extensions.conf \
|
||||
indications.conf \
|
||||
logger.conf \
|
||||
modules.conf \
|
||||
README.AsteriskConf \
|
||||
sip.conf
|
||||
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Makefile.common $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
|
||||
&& exit 0; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu AsteriskConfig/Makefile'; \
|
||||
cd $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu AsteriskConfig/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
distclean-libtool:
|
||||
-rm -f libtool
|
||||
uninstall-info-am:
|
||||
install-dist_ourdataDATA: $(dist_ourdata_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(ourdatadir)" || $(mkdir_p) "$(DESTDIR)$(ourdatadir)"
|
||||
@list='$(dist_ourdata_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_ourdataDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(ourdatadir)/$$f'"; \
|
||||
$(dist_ourdataDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(ourdatadir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_ourdataDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_ourdata_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(ourdatadir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(ourdatadir)/$$f"; \
|
||||
done
|
||||
tags: TAGS
|
||||
TAGS:
|
||||
|
||||
ctags: CTAGS
|
||||
CTAGS:
|
||||
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
$(mkdir_p) $(distdir)/..
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
|
||||
else \
|
||||
test -f $(distdir)/$$file \
|
||||
|| cp -p $$d/$$file $(distdir)/$$file \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(DATA)
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(ourdatadir)"; do \
|
||||
test -z "$$dir" || $(mkdir_p) "$$dir"; \
|
||||
done
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
`test -z '$(STRIP)' || \
|
||||
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
|
||||
mostlyclean-generic:
|
||||
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-generic distclean-libtool
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am: install-dist_ourdataDATA
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-dist_ourdataDATA uninstall-info-am
|
||||
|
||||
.PHONY: all all-am check check-am clean clean-generic clean-libtool \
|
||||
distclean distclean-generic distclean-libtool distdir dvi \
|
||||
dvi-am html html-am info info-am install install-am \
|
||||
install-data install-data-am install-dist_ourdataDATA \
|
||||
install-exec install-exec-am install-info install-info-am \
|
||||
install-man install-strip installcheck installcheck-am \
|
||||
installdirs maintainer-clean maintainer-clean-generic \
|
||||
mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
|
||||
ps ps-am uninstall uninstall-am uninstall-dist_ourdataDATA \
|
||||
uninstall-info-am
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
4
AsteriskConfig/README.AsteriskConf
Normal file
4
AsteriskConfig/README.AsteriskConf
Normal file
@@ -0,0 +1,4 @@
|
||||
This file contains example Asterisk configuration files for the OpenBTS Asterisk server.
|
||||
|
||||
This file does not contain REAL configuration files, since those would include real IMSIs.
|
||||
|
||||
148
AsteriskConfig/cdr.conf
Normal file
148
AsteriskConfig/cdr.conf
Normal file
@@ -0,0 +1,148 @@
|
||||
;
|
||||
; Asterisk Call Detail Record engine configuration
|
||||
;
|
||||
; CDR is Call Detail Record, which provides logging services via a variety of
|
||||
; pluggable backend modules. Detailed call information can be recorded to
|
||||
; databases, files, etc. Useful for billing, fraud prevention, compliance with
|
||||
; Sarbanes-Oxley aka The Enron Act, QOS evaluations, and more.
|
||||
;
|
||||
|
||||
[general]
|
||||
|
||||
; Define whether or not to use CDR logging. Setting this to "no" will override
|
||||
; any loading of backend CDR modules. Default is "yes".
|
||||
;enable=yes
|
||||
|
||||
; Define whether or not to log unanswered calls. Setting this to "yes" will
|
||||
; report every attempt to ring a phone in dialing attempts, when it was not
|
||||
; answered. For example, if you try to dial 3 extensions, and this option is "yes",
|
||||
; you will get 3 CDR's, one for each phone that was rung. Default is "no". Some
|
||||
; find this information horribly useless. Others find it very valuable. Note, in "yes"
|
||||
; mode, you will see one CDR, with one of the call targets on one side, and the originating
|
||||
; channel on the other, and then one CDR for each channel attempted. This may seem
|
||||
; redundant, but cannot be helped.
|
||||
;unanswered = no
|
||||
|
||||
; Define the CDR batch mode, where instead of posting the CDR at the end of
|
||||
; every call, the data will be stored in a buffer to help alleviate load on the
|
||||
; asterisk server. Default is "no".
|
||||
;
|
||||
; WARNING WARNING WARNING
|
||||
; Use of batch mode may result in data loss after unsafe asterisk termination
|
||||
; ie. software crash, power failure, kill -9, etc.
|
||||
; WARNING WARNING WARNING
|
||||
;
|
||||
;batch=no
|
||||
|
||||
; Define the maximum number of CDRs to accumulate in the buffer before posting
|
||||
; them to the backend engines. 'batch' must be set to 'yes'. Default is 100.
|
||||
;size=100
|
||||
|
||||
; Define the maximum time to accumulate CDRs in the buffer before posting them
|
||||
; to the backend engines. If this time limit is reached, then it will post the
|
||||
; records, regardless of the value defined for 'size'. 'batch' must be set to
|
||||
; 'yes'. Note that time is in seconds. Default is 300 (5 minutes).
|
||||
;time=300
|
||||
|
||||
; The CDR engine uses the internal asterisk scheduler to determine when to post
|
||||
; records. Posting can either occur inside the scheduler thread, or a new
|
||||
; thread can be spawned for the submission of every batch. For small batches,
|
||||
; it might be acceptable to just use the scheduler thread, so set this to "yes".
|
||||
; For large batches, say anything over size=10, a new thread is recommended, so
|
||||
; set this to "no". Default is "no".
|
||||
;scheduleronly=no
|
||||
|
||||
; When shutting down asterisk, you can block until the CDRs are submitted. If
|
||||
; you don't, then data will likely be lost. You can always check the size of
|
||||
; the CDR batch buffer with the CLI "cdr status" command. To enable blocking on
|
||||
; submission of CDR data during asterisk shutdown, set this to "yes". Default
|
||||
; is "yes".
|
||||
;safeshutdown=yes
|
||||
|
||||
; Normally, CDR's are not closed out until after all extensions are finished
|
||||
; executing. By enabling this option, the CDR will be ended before executing
|
||||
; the "h" extension so that CDR values such as "end" and "billsec" may be
|
||||
; retrieved inside of of this extension.
|
||||
;endbeforehexten=no
|
||||
|
||||
;
|
||||
;
|
||||
; CHOOSING A CDR "BACKEND" (what kind of output to generate)
|
||||
;
|
||||
; To choose a backend, you have to make sure either the right category is
|
||||
; defined in this file, or that the appropriate config file exists, and has the
|
||||
; proper definitions in it. If there are any problems, usually, the entry will
|
||||
; silently ignored, and you get no output.
|
||||
;
|
||||
; Also, please note that you can generate CDR records in as many formats as you
|
||||
; wish. If you configure 5 different CDR formats, then each event will be logged
|
||||
; in 5 different places! In the example config files, all formats are commented
|
||||
; out except for the cdr-csv format.
|
||||
;
|
||||
; Here are all the possible back ends:
|
||||
;
|
||||
; csv, custom, manager, odbc, pgsql, radius, sqlite, tds
|
||||
; (also, mysql is available via the asterisk-addons, due to licensing
|
||||
; requirements)
|
||||
; (please note, also, that other backends can be created, by creating
|
||||
; a new backend module in the source cdr/ directory!)
|
||||
;
|
||||
; Some of the modules required to provide these backends will not build or install
|
||||
; unless some dependency requirements are met. Examples of this are pgsql, odbc,
|
||||
; etc. If you are not getting output as you would expect, the first thing to do
|
||||
; is to run the command "make menuselect", and check what modules are available,
|
||||
; by looking in the "2. Call Detail Recording" option in the main menu. If your
|
||||
; backend is marked with XXX, you know that the "configure" command could not find
|
||||
; the required libraries for that option.
|
||||
;
|
||||
; To get CDRs to be logged to the plain-jane /var/log/asterisk/cdr-csv/Master.csv
|
||||
; file, define the [csv] category in this file. No database necessary. The example
|
||||
; config files are set up to provide this kind of output by default.
|
||||
;
|
||||
; To get custom csv CDR records, make sure the cdr_custom.conf file
|
||||
; is present, and contains the proper [mappings] section. The advantage to
|
||||
; using this backend, is that you can define which fields to output, and in
|
||||
; what order. By default, the example configs are set up to mimic the cdr-csv
|
||||
; output. If you don't make any changes to the mappings, you are basically generating
|
||||
; the same thing as cdr-csv, but expending more CPU cycles to do so!
|
||||
;
|
||||
; To get manager events generated, make sure the cdr_manager.conf file exists,
|
||||
; and the [general] section is defined, with the single variable 'enabled = yes'.
|
||||
;
|
||||
; For odbc, make sure all the proper libs are installed, that "make menuselect"
|
||||
; shows that the modules are available, and the cdr_odbc.conf file exists, and
|
||||
; has a [global] section with the proper variables defined.
|
||||
;
|
||||
; For pgsql, make sure all the proper libs are installed, that "make menuselect"
|
||||
; shows that the modules are available, and the cdr_pgsql.conf file exists, and
|
||||
; has a [global] section with the proper variables defined.
|
||||
;
|
||||
; For logging to radius databases, make sure all the proper libs are installed, that
|
||||
; "make menuselect" shows that the modules are available, and the [radius]
|
||||
; category is defined in this file, and in that section, make sure the 'radiuscfg'
|
||||
; variable is properly pointing to an existing radiusclient.conf file.
|
||||
;
|
||||
; For logging to sqlite databases, make sure the 'cdr.db' file exists in the log directory,
|
||||
; which is usually /var/log/asterisk. Of course, the proper libraries should be available
|
||||
; during the 'configure' operation.
|
||||
;
|
||||
; For tds logging, make sure the proper libraries are available during the 'configure'
|
||||
; phase, and that cdr_tds.conf exists and is properly set up with a [global] category.
|
||||
;
|
||||
; Also, remember, that if you wish to log CDR info to a database, you will have to define
|
||||
; a specific table in that databse to make things work! See the doc directory for more details
|
||||
; on how to create this table in each database.
|
||||
;
|
||||
|
||||
[csv]
|
||||
usegmtime=yes ; log date/time in GMT. Default is "no"
|
||||
loguniqueid=yes ; log uniqueid. Default is "no
|
||||
loguserfield=yes ; log user field. Default is "no
|
||||
|
||||
;[radius]
|
||||
;usegmtime=yes ; log date/time in GMT
|
||||
;loguniqueid=yes ; log uniqueid
|
||||
;loguserfield=yes ; log user field
|
||||
; Set this to the location of the radiusclient-ng configuration file
|
||||
; The default is /etc/radiusclient-ng/radiusclient.conf
|
||||
;radiuscfg => /usr/local/etc/radiusclient-ng/radiusclient.conf
|
||||
57
AsteriskConfig/extensions.conf
Normal file
57
AsteriskConfig/extensions.conf
Normal file
@@ -0,0 +1,57 @@
|
||||
[globals]
|
||||
|
||||
|
||||
[default]
|
||||
; This is the context for handsets that are allowed to attached via open registration.
|
||||
; Normally, this context is only used for testing.
|
||||
|
||||
; These are test extensions that you might want to disable after installation.
|
||||
|
||||
; Create an extension, 2600, for evaluating echo latency.
|
||||
exten => 2600,1,Answer() ; Do the echo test
|
||||
exten => 2600,n,Echo ; Do the echo test
|
||||
exten => 2600,n,Hangup
|
||||
|
||||
; The 2101 extension is used for factory testing with zoiper.
|
||||
exten => 2101,1,Dial(SIP/zoiper)
|
||||
|
||||
; The 2100 extension is for factory testing with the test SIM.
|
||||
exten => 2100,1,Dial(SIP/IMSI001010000000000)
|
||||
|
||||
|
||||
|
||||
[outbound-trunk]
|
||||
; If you had an external trunk, you would dial it here.
|
||||
exten => _N.,1,Answer()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[phones]
|
||||
; This is the context for handsets provisioned through the realtime database.
|
||||
; This assumes that OpenBTS units all are running their SIP interfaces on port 5062.
|
||||
exten => _N.,1,Set(Name=${ODBC_SQL(select dial from dialdata_table where exten = \"${EXTEN}\")})
|
||||
exten => _N.,n,GotoIf($["${Name}" = ""] ?outbound-trunk,${EXTEN},1)
|
||||
exten => _N.,n,Set(IPAddr=${ODBC_SQL(select ipaddr from sip_buddies where name = \"${Name}\")})
|
||||
exten => _N.,n,GotoIf($["${IPAddr}" = ""] ?outbound-trunk,${EXTEN},1)
|
||||
exten => _N.,n,Dial(SIP/${Name}@${IPAddr}:5062)
|
||||
|
||||
|
||||
|
||||
[sip-local]
|
||||
; This context is the union of all of the in-network contexts.
|
||||
include => default
|
||||
include => phones
|
||||
|
||||
|
||||
|
||||
|
||||
[sip-external]
|
||||
; This is the top-level context that gives access to out-of-network calling.
|
||||
; also includes the in-network calling.
|
||||
include => sip-local
|
||||
include => outbound-trunk
|
||||
|
||||
|
||||
|
||||
733
AsteriskConfig/indications.conf
Normal file
733
AsteriskConfig/indications.conf
Normal file
@@ -0,0 +1,733 @@
|
||||
; indications.conf
|
||||
; Configuration file for location specific tone indications
|
||||
; used by the pbx_indications module.
|
||||
;
|
||||
; NOTE:
|
||||
; When adding countries to this file, please keep them in alphabetical
|
||||
; order according to the 2-character country codes!
|
||||
;
|
||||
; The [general] category is for certain global variables.
|
||||
; All other categories are interpreted as location specific indications
|
||||
;
|
||||
;
|
||||
[general]
|
||||
country=us ; default location
|
||||
|
||||
|
||||
; [example]
|
||||
; description = string
|
||||
; The full name of your country, in English.
|
||||
; alias = iso[,iso]*
|
||||
; List of other countries 2-letter iso codes, which have the same
|
||||
; tone indications.
|
||||
; ringcadence = num[,num]*
|
||||
; List of durations the physical bell rings.
|
||||
; dial = tonelist
|
||||
; Set of tones to be played when one picks up the hook.
|
||||
; busy = tonelist
|
||||
; Set of tones played when the receiving end is busy.
|
||||
; congestion = tonelist
|
||||
; Set of tones played when there is some congestion (on the network?)
|
||||
; callwaiting = tonelist
|
||||
; Set of tones played when there is a call waiting in the background.
|
||||
; dialrecall = tonelist
|
||||
; Not well defined; many phone systems play a recall dial tone after hook
|
||||
; flash.
|
||||
; record = tonelist
|
||||
; Set of tones played when call recording is in progress.
|
||||
; info = tonelist
|
||||
; Set of tones played with special information messages (e.g., "number is
|
||||
; out of service")
|
||||
; 'name' = tonelist
|
||||
; Every other variable will be available as a shortcut for the "PlayList" command
|
||||
; but will not be used automatically by Asterisk.
|
||||
;
|
||||
;
|
||||
; The tonelist itself is defined by a comma-separated sequence of elements.
|
||||
; Each element consist of a frequency (f) with an optional duration (in ms)
|
||||
; attached to it (f/duration). The frequency component may be a mixture of two
|
||||
; frequencies (f1+f2) or a frequency modulated by another frequency (f1*f2).
|
||||
; The implicit modulation depth is fixed at 90%, though.
|
||||
; If the list element starts with a !, that element is NOT repeated,
|
||||
; therefore, only if all elements start with !, the tonelist is time-limited,
|
||||
; all others will repeat indefinitely.
|
||||
;
|
||||
; concisely:
|
||||
; element = [!]freq[+|*freq2][/duration]
|
||||
; tonelist = element[,element]*
|
||||
;
|
||||
; Please note that SPACES ARE NOT ALLOWED in tone lists!
|
||||
;
|
||||
|
||||
[at]
|
||||
description = Austria
|
||||
ringcadence = 1000,5000
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
dial = 420
|
||||
busy = 420/400,0/400
|
||||
ring = 420/1000,0/5000
|
||||
congestion = 420/200,0/200
|
||||
callwaiting = 420/40,0/1960
|
||||
dialrecall = 420
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/80,0/14920
|
||||
info = 950/330,1450/330,1850/330,0/1000
|
||||
stutter = 380+420
|
||||
|
||||
[au]
|
||||
description = Australia
|
||||
; Reference http://www.acif.org.au/__data/page/3303/S002_2001.pdf
|
||||
; Normal Ring
|
||||
ringcadence = 400,200,400,2000
|
||||
; Distinctive Ring 1 - Forwarded Calls
|
||||
; 400,400,200,200,400,1400
|
||||
; Distinctive Ring 2 - Selective Ring 2 + Operator + Recall
|
||||
; 400,400,200,2000
|
||||
; Distinctive Ring 3 - Multiple Subscriber Number 1
|
||||
; 200,200,400,2200
|
||||
; Distinctive Ring 4 - Selective Ring 1 + Centrex
|
||||
; 400,2600
|
||||
; Distinctive Ring 5 - Selective Ring 3
|
||||
; 400,400,200,400,200,1400
|
||||
; Distinctive Ring 6 - Multiple Subscriber Number 2
|
||||
; 200,400,200,200,400,1600
|
||||
; Distinctive Ring 7 - Multiple Subscriber Number 3 + Data Privacy
|
||||
; 200,400,200,400,200,1600
|
||||
; Tones
|
||||
dial = 413+438
|
||||
busy = 425/375,0/375
|
||||
ring = 413+438/400,0/200,413+438/400,0/2000
|
||||
; XXX Congestion: Should reduce by 10 db every other cadence XXX
|
||||
congestion = 425/375,0/375,420/375,0/375
|
||||
callwaiting = 425/200,0/200,425/200,0/4400
|
||||
dialrecall = 413+438
|
||||
; Record tone used for Call Intrusion/Recording or Conference
|
||||
record = !425/1000,!0/15000,425/360,0/15000
|
||||
info = 425/2500,0/500
|
||||
; Other Australian Tones
|
||||
; The STD "pips" indicate the call is not an untimed local call
|
||||
std = !525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100
|
||||
; Facility confirmation tone (eg. Call Forward Activated)
|
||||
facility = 425
|
||||
; Message Waiting "stutter" dialtone
|
||||
stutter = 413+438/100,0/40
|
||||
; Ringtone for calls to Telstra mobiles
|
||||
ringmobile = 400+450/400,0/200,400+450/400,0/2000
|
||||
|
||||
[bg]
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
description = Bulgaria
|
||||
ringdance = 1000,4000
|
||||
;
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/250,0/250
|
||||
callwaiting = 425/150,0/150,425/150,0/4000
|
||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
record = 1400/425,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
stutter = 425/1500,0/100
|
||||
|
||||
[br]
|
||||
description = Brazil
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/250,0/250
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/250,0/250,425/750,0/250
|
||||
callwaiting = 425/50,0/1000
|
||||
; Dialrecall not used in Brazil standard (using UK standard)
|
||||
dialrecall = 350+440
|
||||
; Record tone is not used in Brazil, use busy tone
|
||||
record = 425/250,0/250
|
||||
; Info not used in Brazil standard (using UK standard)
|
||||
info = 950/330,1400/330,1800/330
|
||||
stutter = 350+440
|
||||
|
||||
[be]
|
||||
description = Belgium
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,3000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/3000
|
||||
congestion = 425/167,0/167
|
||||
callwaiting = 1400/175,0/175,1400/175,0/3500
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440"
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
info = 900/330,1400/330,1800/330,0/1000
|
||||
stutter = 425/1000,0/250
|
||||
|
||||
[ch]
|
||||
description = Switzerland
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 425/200,0/200,425/200,0/4000
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/80,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
stutter = 425+340/1100,0/1100
|
||||
|
||||
[cl]
|
||||
description = Chile
|
||||
; According to specs from Telefonica CTC Chile
|
||||
ringcadence = 1000,3000
|
||||
dial = 400
|
||||
busy = 400/500,0/500
|
||||
ring = 400/1000,0/3000
|
||||
congestion = 400/200,0/200
|
||||
callwaiting = 400/250,0/8750
|
||||
dialrecall = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
|
||||
record = 1400/500,0/15000
|
||||
info = 950/333,1400/333,1800/333,0/1000
|
||||
stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
|
||||
|
||||
[cn]
|
||||
description = China
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 450
|
||||
busy = 450/350,0/350
|
||||
ring = 450/1000,0/4000
|
||||
congestion = 450/700,0/700
|
||||
callwaiting = 450/400,0/4000
|
||||
dialrecall = 450
|
||||
record = 950/400,0/10000
|
||||
info = 450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400
|
||||
; STUTTER - not specified
|
||||
stutter = 450+425
|
||||
|
||||
[cz]
|
||||
description = Czech Republic
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 425/330,0/330,425/660,0/660
|
||||
busy = 425/330,0/330
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/165,0/165
|
||||
callwaiting = 425/330,0/9000
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/14000
|
||||
info = 950/330,0/30,1400/330,0/30,1800/330,0/1000
|
||||
; STUTTER - not specified
|
||||
stutter = 425/450,0/50
|
||||
|
||||
[de]
|
||||
description = Germany
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/480,0/480
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/240,0/240
|
||||
callwaiting = !425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/80,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
stutter = 425+400
|
||||
|
||||
[dk]
|
||||
description = Denmark
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = !425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/80,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
; STUTTER - not specified
|
||||
stutter = 425/450,0/50
|
||||
|
||||
[ee]
|
||||
description = Estonia
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/300,0/300
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
; CALLWAIT not in accordance to ITU
|
||||
callwaiting = 950/650,0/325,950/325,0/30,1400/1300,0/2600
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = 425/650,0/25
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
; INFO not in accordance to ITU
|
||||
info = 950/650,0/325,950/325,0/30,1400/1300,0/2600
|
||||
; STUTTER not specified
|
||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
|
||||
[es]
|
||||
description = Spain
|
||||
ringcadence = 1500,3000
|
||||
dial = 425
|
||||
busy = 425/200,0/200
|
||||
ring = 425/1500,0/3000
|
||||
congestion = 425/200,0/200,425/200,0/200,425/200,0/600
|
||||
callwaiting = 425/175,0/175,425/175,0/3500
|
||||
dialrecall = !425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425
|
||||
record = 1400/500,0/15000
|
||||
info = 950/330,0/1000
|
||||
dialout = 500
|
||||
|
||||
|
||||
[fi]
|
||||
description = Finland
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/300,0/300
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 425/150,0/150,425/150,0/8000
|
||||
dialrecall = 425/650,0/25
|
||||
record = 1400/500,0/15000
|
||||
info = 950/650,0/325,950/325,0/30,1400/1300,0/2600
|
||||
stutter = 425/650,0/25
|
||||
|
||||
[fr]
|
||||
description = France
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1500,3500
|
||||
; Dialtone can also be 440+330
|
||||
dial = 440
|
||||
busy = 440/500,0/500
|
||||
ring = 440/1500,0/3500
|
||||
; CONGESTION - not specified
|
||||
congestion = 440/250,0/250
|
||||
callwait = 440/300,0/10000
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330
|
||||
stutter = !440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440
|
||||
|
||||
[gr]
|
||||
description = Greece
|
||||
ringcadence = 1000,4000
|
||||
dial = 425/200,0/300,425/700,0/800
|
||||
busy = 425/300,0/300
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 425/150,0/150,425/150,0/8000
|
||||
dialrecall = 425/650,0/25
|
||||
record = 1400/400,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
|
||||
stutter = 425/650,0/25
|
||||
|
||||
[hu]
|
||||
description = Hungary
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1250,3750
|
||||
dial = 425
|
||||
busy = 425/300,0/300
|
||||
ring = 425/1250,0/3750
|
||||
congestion = 425/300,0/300
|
||||
callwaiting = 425/40,0/1960
|
||||
dialrecall = 425+450
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/400,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
|
||||
stutter = 350+375+400
|
||||
|
||||
[il]
|
||||
description = Israel
|
||||
ringcadence = 1000,3000
|
||||
dial = 414
|
||||
busy = 414/500,0/500
|
||||
ring = 414/1000,0/3000
|
||||
congestion = 414/250,0/250
|
||||
callwaiting = 414/100,0/100,414/100,0/100,414/600,0/3000
|
||||
dialrecall = !414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414
|
||||
record = 1400/500,0/15000
|
||||
info = 1000/330,1400/330,1800/330,0/1000
|
||||
stutter = !414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414
|
||||
|
||||
|
||||
[in]
|
||||
description = India
|
||||
ringcadence = 400,200,400,2000
|
||||
dial = 400*25
|
||||
busy = 400/750,0/750
|
||||
ring = 400*25/400,0/200,400*25/400,0/2000
|
||||
congestion = 400/250,0/250
|
||||
callwaiting = 400/200,0/100,400/200,0/7500
|
||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,0/1000
|
||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
|
||||
[it]
|
||||
description = Italy
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
dial = 425/200,0/200,425/600,0/1000
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 425/400,0/100,425/250,0/100,425/150,0/14000
|
||||
dialrecall = 470/400,425/400
|
||||
record = 1400/400,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
|
||||
stutter = 470/400,425/400
|
||||
|
||||
[lt]
|
||||
description = Lithuania
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/350,0/350
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 425/150,0/150,425/150,0/4000
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = 425/500,0/50
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
|
||||
; STUTTER - not specified
|
||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
|
||||
[jp]
|
||||
description = Japan
|
||||
ringcadence = 1000,2000
|
||||
dial = 400
|
||||
busy = 400/500,0/500
|
||||
ring = 400+15/1000,0/2000
|
||||
congestion = 400/500,0/500
|
||||
callwaiting = 400+16/500,0/8000
|
||||
dialrecall = !400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,0
|
||||
stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
|
||||
|
||||
[mx]
|
||||
description = Mexico
|
||||
ringcadence = 2000,4000
|
||||
dial = 425
|
||||
busy = 425/250,0/250
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/250,0/250
|
||||
callwaiting = 425/200,0/600,425/200,0/10000
|
||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
record = 1400/500,0/15000
|
||||
info = 950/330,0/30,1400/330,0/30,1800/330,0/1000
|
||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
|
||||
[my]
|
||||
description = Malaysia
|
||||
ringcadence = 2000,4000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/400,0/200
|
||||
congestion = 425/500,0/500
|
||||
|
||||
[nl]
|
||||
description = Netherlands
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
ringcadence = 1000,4000
|
||||
; Most of these 425's can also be 450's
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/250,0/250
|
||||
callwaiting = 425/500,0/9500
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = 425/500,0/50
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
stutter = 425/500,0/50
|
||||
|
||||
[no]
|
||||
description = Norway
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 425/200,0/600,425/200,0/10000
|
||||
dialrecall = 470/400,425/400
|
||||
record = 1400/400,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
|
||||
stutter = 470/400,425/400
|
||||
|
||||
[nz]
|
||||
description = New Zealand
|
||||
;NOTE - the ITU has different tonesets for NZ, but according to some residents there,
|
||||
; this is, indeed, the correct way to do it.
|
||||
ringcadence = 400,200,400,2000
|
||||
dial = 400
|
||||
busy = 400/250,0/250
|
||||
ring = 400+450/400,0/200,400+450/400,0/2000
|
||||
congestion = 400/375,0/375
|
||||
callwaiting = !400/200,!0/3000,!400/200,!0/3000,!400/200,!0/3000,!400/200
|
||||
dialrecall = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,400
|
||||
record = 1400/425,0/15000
|
||||
info = 400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400
|
||||
stutter = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400
|
||||
unobtainable = 400/75,0/100,400/75,0/100,400/75,0/100,400/75,0/400
|
||||
|
||||
[ph]
|
||||
|
||||
; reference http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
|
||||
description = Philippines
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 480+620/500,0/500
|
||||
ring = 425+480/1000,0/4000
|
||||
congestion = 480+620/250,0/250
|
||||
callwaiting = 440/300,0/10000
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
; INFO - not specified
|
||||
info = !950/330,!1400/330,!1800/330,0
|
||||
; STUTTER - not specified
|
||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
|
||||
|
||||
[pl]
|
||||
description = Poland
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/500,0/500
|
||||
callwaiting = 425/150,0/150,425/150,0/4000
|
||||
; DIALRECALL - not specified
|
||||
dialrecall = 425/500,0/50
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
; 950/1400/1800 3x0.33 on 1.0 off repeated 3 times
|
||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000
|
||||
; STUTTER - not specified
|
||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
|
||||
[pt]
|
||||
description = Portugal
|
||||
ringcadence = 1000,5000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/5000
|
||||
congestion = 425/200,0/200
|
||||
callwaiting = 440/300,0/10000
|
||||
dialrecall = 425/1000,0/200
|
||||
record = 1400/500,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
|
||||
[ru]
|
||||
; References:
|
||||
; http://www.minsvyaz.ru/site.shtml?id=1806
|
||||
; http://www.aboutphone.info/lib/gost/45-223-2001.html
|
||||
description = Russian Federation / ex Soviet Union
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/350,0/350
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/175,0/175
|
||||
callwaiting = 425/200,0/5000
|
||||
record = 1400/400,0/15000
|
||||
info = 950/330,1400/330,1800/330,0/1000
|
||||
dialrecall = 425/400,0/40
|
||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
|
||||
[se]
|
||||
description = Sweden
|
||||
ringcadence = 1000,5000
|
||||
dial = 425
|
||||
busy = 425/250,0/250
|
||||
ring = 425/1000,0/5000
|
||||
congestion = 425/250,0/750
|
||||
callwaiting = 425/200,0/500,425/200,0/9100
|
||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
record = 1400/500,0/15000
|
||||
info = !950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,0
|
||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
|
||||
; stutter = 425/320,0/20 ; Real swedish standard, not used for now
|
||||
|
||||
[sg]
|
||||
description = Singapore
|
||||
; Singapore
|
||||
; Reference: http://www.ida.gov.sg/idaweb/doc/download/I397/ida_ts_pstn1_i4r2.pdf
|
||||
; Frequency specs are: 425 Hz +/- 20Hz; 24 Hz +/- 2Hz; modulation depth 100%; SIT +/- 50Hz
|
||||
ringcadence = 400,200,400,2000
|
||||
dial = 425
|
||||
ring = 425*24/400,0/200,425*24/400,0/2000 ; modulation should be 100%, not 90%
|
||||
busy = 425/750,0/750
|
||||
congestion = 425/250,0/250
|
||||
callwaiting = 425*24/300,0/200,425*24/300,0/3200
|
||||
stutter = !425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425
|
||||
info = 950/330,1400/330,1800/330,0/1000 ; not currently in use acc. to reference
|
||||
dialrecall = 425*24/500,0/500,425/500,0/2500 ; unspecified in IDA reference, use repeating Holding Tone A,B
|
||||
record = 1400/500,0/15000 ; unspecified in IDA reference, use 0.5s tone every 15s
|
||||
; additionally defined in reference
|
||||
nutone = 425/2500,0/500
|
||||
intrusion = 425/250,0/2000
|
||||
warning = 425/624,0/4376 ; end of period tone, warning
|
||||
acceptance = 425/125,0/125
|
||||
holdinga = !425*24/500,!0/500 ; followed by holdingb
|
||||
holdingb = !425/500,!0/2500
|
||||
|
||||
[th]
|
||||
description = Thailand
|
||||
ringcadence = 1000,4000
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
dial = 400*50
|
||||
busy = 400/500,0/500
|
||||
ring = 420/1000,0/5000
|
||||
congestion = 400/300,0/300
|
||||
callwaiting = 1000/400,10000/400,1000/400
|
||||
; DIALRECALL - not specified - use special dial tone instead.
|
||||
dialrecall = 400*50/400,0/100,400*50/400,0/100
|
||||
; RECORDTONE - not specified
|
||||
record = 1400/500,0/15000
|
||||
; INFO - specified as an announcement - use special information tones instead
|
||||
info = 950/330,1400/330,1800/330
|
||||
; STUTTER - not specified
|
||||
stutter = !400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,400
|
||||
|
||||
[uk]
|
||||
description = United Kingdom
|
||||
ringcadence = 400,200,400,2000
|
||||
; These are the official tones taken from BT SIN350. The actual tones
|
||||
; used by BT include some volume differences so sound slightly different
|
||||
; from Asterisk-generated ones.
|
||||
dial = 350+440
|
||||
; Special dial is the intermittent dial tone heard when, for example,
|
||||
; you have a divert active on the line
|
||||
specialdial = 350+440/750,440/750
|
||||
; Busy is also called "Engaged"
|
||||
busy = 400/375,0/375
|
||||
; "Congestion" is the Beep-bip engaged tone
|
||||
congestion = 400/400,0/350,400/225,0/525
|
||||
; "Special Congestion" is not used by BT very often if at all
|
||||
specialcongestion = 400/200,1004/300
|
||||
unobtainable = 400
|
||||
ring = 400+450/400,0/200,400+450/400,0/2000
|
||||
callwaiting = 400/100,0/4000
|
||||
; BT seem to use "Special Call Waiting" rather than just "Call Waiting" tones
|
||||
specialcallwaiting = 400/250,0/250,400/250,0/250,400/250,0/5000
|
||||
; "Pips" used by BT on payphones. (Sounds wrong, but this is what BT claim it
|
||||
; is and I've not used a payphone for years)
|
||||
creditexpired = 400/125,0/125
|
||||
; These two are used to confirm/reject service requests on exchanges that
|
||||
; don't do voice announcements.
|
||||
confirm = 1400
|
||||
switching = 400/200,0/400,400/2000,0/400
|
||||
; This is the three rising tones Doo-dah-dee "Special Information Tone",
|
||||
; usually followed by the BT woman saying an appropriate message.
|
||||
info = 950/330,0/15,1400/330,0/15,1800/330,0/1000
|
||||
; Not listed in SIN350
|
||||
record = 1400/500,0/60000
|
||||
stutter = 350+440/750,440/750
|
||||
|
||||
[us]
|
||||
description = United States / North America
|
||||
ringcadence = 2000,4000
|
||||
dial = 350+440
|
||||
busy = 480+620/500,0/500
|
||||
ring = 440+480/2000,0/4000
|
||||
congestion = 480+620/250,0/250
|
||||
callwaiting = 440/300,0/10000
|
||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,0
|
||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
|
||||
[us-old]
|
||||
description = United States Circa 1950/ North America
|
||||
ringcadence = 2000,4000
|
||||
dial = 600*120
|
||||
busy = 500*100/500,0/500
|
||||
ring = 420*40/2000,0/4000
|
||||
congestion = 500*100/250,0/250
|
||||
callwaiting = 440/300,0/10000
|
||||
dialrecall = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,0
|
||||
stutter = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120
|
||||
|
||||
[tw]
|
||||
description = Taiwan
|
||||
; http://nemesis.lonestar.org/reference/telecom/signaling/dialtone.html
|
||||
; http://nemesis.lonestar.org/reference/telecom/signaling/busy.html
|
||||
; http://www.iproducts.com.tw/ee/kylink/06ky-1000a.htm
|
||||
; http://www.pbx-manufacturer.com/ky120dx.htm
|
||||
; http://www.nettwerked.net/tones.txt
|
||||
; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/taiw_sup/taiw2.htm
|
||||
;
|
||||
; busy tone 480+620Hz 0.5 sec. on ,0.5 sec. off
|
||||
; reorder tone 480+620Hz 0.25 sec. on,0.25 sec. off
|
||||
; ringing tone 440+480Hz 1 sec. on ,2 sec. off
|
||||
;
|
||||
ringcadence = 1000,4000
|
||||
dial = 350+440
|
||||
busy = 480+620/500,0/500
|
||||
ring = 440+480/1000,0/2000
|
||||
congestion = 480+620/250,0/250
|
||||
callwaiting = 350+440/250,0/250,350+440/250,0/3250
|
||||
dialrecall = 300/1500,0/500
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1400/330,!1800/330,0
|
||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
|
||||
|
||||
[ve]
|
||||
; Tone definition source for ve found on
|
||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
|
||||
description = Venezuela / South America
|
||||
ringcadence = 1000,4000
|
||||
dial = 425
|
||||
busy = 425/500,0/500
|
||||
ring = 425/1000,0/4000
|
||||
congestion = 425/250,0/250
|
||||
callwaiting = 400+450/300,0/6000
|
||||
dialrecall = 425
|
||||
record = 1400/500,0/15000
|
||||
info = !950/330,!1440/330,!1800/330,0/1000
|
||||
|
||||
|
||||
[za]
|
||||
description = South Africa
|
||||
; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/safr_sup/saf02.htm
|
||||
; (definitions for other countries can also be found there)
|
||||
; Note, though, that South Africa uses two switch types in their network --
|
||||
; Alcatel switches -- mainly in the Western Cape, and Siemens elsewhere.
|
||||
; The former use 383+417 in dial, ringback etc. The latter use 400*33
|
||||
; I've provided both, uncomment the ones you prefer
|
||||
ringcadence = 400,200,400,2000
|
||||
; dial/ring/callwaiting for the Siemens switches:
|
||||
dial = 400*33
|
||||
ring = 400*33/400,0/200,400*33/400,0/2000
|
||||
callwaiting = 400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250
|
||||
; dial/ring/callwaiting for the Alcatel switches:
|
||||
; dial = 383+417
|
||||
; ring = 383+417/400,0/200,383+417/400,0/2000
|
||||
; callwaiting = 383+417/250,0/250,383+417/250,0/250,383+417/250,0/250,383+417/250,0/250
|
||||
congestion = 400/250,0/250
|
||||
busy = 400/500,0/500
|
||||
dialrecall = 350+440
|
||||
; XXX Not sure about the RECORDTONE
|
||||
record = 1400/500,0/10000
|
||||
info = 950/330,1400/330,1800/330,0/330
|
||||
stutter = !400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33
|
||||
69
AsteriskConfig/logger.conf
Normal file
69
AsteriskConfig/logger.conf
Normal file
@@ -0,0 +1,69 @@
|
||||
;
|
||||
; Logging Configuration
|
||||
;
|
||||
; In this file, you configure logging to files or to
|
||||
; the syslog system.
|
||||
;
|
||||
; "logger reload" at the CLI will reload configuration
|
||||
; of the logging system.
|
||||
|
||||
[general]
|
||||
; Customize the display of debug message time stamps
|
||||
; this example is the ISO 8601 date format (yyyy-mm-dd HH:MM:SS)
|
||||
; see strftime(3) Linux manual for format specifiers
|
||||
;dateformat=%F %T
|
||||
;
|
||||
; This appends the hostname to the name of the log files.
|
||||
;appendhostname = yes
|
||||
;
|
||||
; This determines whether or not we log queue events to a file
|
||||
; (defaults to yes).
|
||||
;queue_log = no
|
||||
;
|
||||
; This determines whether or not we log generic events to a file
|
||||
; (defaults to yes).
|
||||
;event_log = no
|
||||
;
|
||||
;
|
||||
; For each file, specify what to log.
|
||||
;
|
||||
; For console logging, you set options at start of
|
||||
; Asterisk with -v for verbose and -d for debug
|
||||
; See 'asterisk -h' for more information.
|
||||
;
|
||||
; Directory for log files is configures in asterisk.conf
|
||||
; option astlogdir
|
||||
;
|
||||
[logfiles]
|
||||
;
|
||||
; Format is "filename" and then "levels" of debugging to be included:
|
||||
; debug
|
||||
; notice
|
||||
; warning
|
||||
; error
|
||||
; verbose
|
||||
; dtmf
|
||||
;
|
||||
; Special filename "console" represents the system console
|
||||
;
|
||||
; We highly recommend that you DO NOT turn on debug mode if you are simply
|
||||
; running a production system. Debug mode turns on a LOT of extra messages,
|
||||
; most of which you are unlikely to understand without an understanding of
|
||||
; the underlying code. Do NOT report debug messages as code issues, unless
|
||||
; you have a specific issue that you are attempting to debug. They are
|
||||
; messages for just that -- debugging -- and do not rise to the level of
|
||||
; something that merit your attention as an Asterisk administrator. Debug
|
||||
; messages are also very verbose and can and do fill up logfiles quickly;
|
||||
; this is another reason not to have debug mode on a production system unless
|
||||
; you are in the process of debugging a specific issue.
|
||||
;
|
||||
;debug => debug
|
||||
console => notice,warning,error
|
||||
;console => notice,warning,error,debug
|
||||
;messages => notice,warning,error
|
||||
;full => notice,warning,error,debug,verbose
|
||||
|
||||
;syslog keyword : This special keyword logs to syslog facility
|
||||
;
|
||||
;syslog.local0 => notice,warning,error
|
||||
;
|
||||
36
AsteriskConfig/modules.conf
Normal file
36
AsteriskConfig/modules.conf
Normal file
@@ -0,0 +1,36 @@
|
||||
;
|
||||
; Asterisk configuration file
|
||||
;
|
||||
; Module Loader configuration file
|
||||
;
|
||||
|
||||
[modules]
|
||||
autoload=yes
|
||||
;
|
||||
; Any modules that need to be loaded before the Asterisk core has been
|
||||
; initialized (just after the logger has been initialized) can be loaded
|
||||
; using 'preload'. This will frequently be needed if you wish to map all
|
||||
; module configuration files into Realtime storage, since the Realtime
|
||||
; driver will need to be loaded before the modules using those configuration
|
||||
; files are initialized.
|
||||
;
|
||||
; An example of loading ODBC support would be:
|
||||
;preload => res_odbc.so
|
||||
;preload => res_config_odbc.so
|
||||
;
|
||||
; Uncomment the following if you wish to use the Speech Recognition API
|
||||
;preload => res_speech.so
|
||||
;
|
||||
; If you want, load the GTK console right away.
|
||||
;
|
||||
noload => pbx_gtkconsole.so
|
||||
;load => pbx_gtkconsole.so
|
||||
;
|
||||
noload => res_musiconhold.so
|
||||
;load => res_musiconhold.so
|
||||
;
|
||||
; Load either OSS or ALSA, not both
|
||||
; By default, load OSS only (automatically) and do not load ALSA
|
||||
;
|
||||
noload => chan_alsa.so
|
||||
;noload => chan_oss.so
|
||||
91
AsteriskConfig/sip.conf
Normal file
91
AsteriskConfig/sip.conf
Normal file
@@ -0,0 +1,91 @@
|
||||
[general]
|
||||
bindport=5060 ; asterisk 1.6
|
||||
; UDP Port to bind to (SIP standard port for unencrypted UDP
|
||||
; and TCP sessions is 5060)
|
||||
; bindport is the local UDP port that Asterisk will listen on
|
||||
bindaddr=0.0.0.0 ; asterisk 1.6
|
||||
; IP address to bind UDP listen socket to (0.0.0.0 binds to all)
|
||||
; You can specify port here too, like 123.123.123.123:5080
|
||||
udpbindaddr=0.0.0.0 ; asterisk 1.8
|
||||
; IP address to bind UDP listen socket to (0.0.0.0 binds to all)
|
||||
; Optionally add a port number, 192.168.1.1:5062 (default is port 5060)
|
||||
|
||||
tos_sip=cs3 ; Sets TOS for SIP packets.
|
||||
tos_audio=ef ; Sets TOS for RTP audio packets.
|
||||
tos_video=af41 ; Sets TOS for RTP video packets.
|
||||
tos_text=af41 ; Sets TOS for RTP text packets.
|
||||
|
||||
cos_sip=3 ; Sets 802.1p priority for SIP packets.
|
||||
cos_audio=5 ; Sets 802.1p priority for RTP audio packets.
|
||||
cos_video=4 ; Sets 802.1p priority for RTP video packets.
|
||||
cos_text=3 ; Sets 802.1p priority for RTP text packets.
|
||||
|
||||
maxexpiry=3600 ; Maximum allowed time of incoming registrations
|
||||
; and subscriptions (seconds)
|
||||
minexpiry=60 ; Minimum length of registrations/subscriptions (default 60)
|
||||
defaultexpiry=3600 ; Default length of incoming/outgoing registration
|
||||
dynamic_exclude_static=yes ; Disallow all dynamic hosts from registering
|
||||
; as any IP address used for staticly defined
|
||||
; hosts. This helps avoid the configuration
|
||||
; error of allowing your users to register at
|
||||
; the same address as a SIP provider.
|
||||
use_q850_reason=yes ; Set to yes add Reason header and use Reason header if it is available.
|
||||
|
||||
;t1min=100 ; Minimum roundtrip time for messages to monitored hosts
|
||||
; Defaults to 100 ms
|
||||
;timert1=500 ; Default T1 timer
|
||||
; Defaults to 500 ms or the measured round-trip
|
||||
; time to a peer (qualify=yes).
|
||||
;timerb=32000 ; Call setup timer. If a provisional response is not received
|
||||
; in this amount of time, the call will autocongest
|
||||
; Defaults to 64*timert1
|
||||
rtptimeout=60 ; Terminate call if 60 seconds of no RTP or RTCP activity
|
||||
; on the audio channel
|
||||
; when we're not on hold. This is to be able to hangup
|
||||
; a call in the case of a phone disappearing from the net,
|
||||
; like a powerloss or grandma tripping over a cable.
|
||||
rtpholdtimeout=300 ; Terminate call if 300 seconds of no RTP or RTCP activity
|
||||
; on the audio channel
|
||||
; when we're on hold (must be > rtptimeout)
|
||||
|
||||
;allowguest=no ; Allow or reject guest calls (default is yes)
|
||||
autocreatepeer=yes ; The Autocreatepeer option allows,
|
||||
; if set to Yes, any SIP ua to register with your Asterisk PBX as a peer.
|
||||
; This peer's settings will be based on global options.
|
||||
; The peer's name will be based on the user part of the Contact: header field's URL.
|
||||
|
||||
context=phones ; Default context for incoming calls
|
||||
allowoverlap=no ; Disable overlap dialing support. (Default is yes)
|
||||
|
||||
disallow=all ; need to disallow=all before we can use allow=
|
||||
allow=gsm ; GSM
|
||||
allow=ulaw ; ISDN US
|
||||
allow=alaw ; ISDN EU
|
||||
|
||||
relaxdtmf=yes ; Relax dtmf handling
|
||||
dtmfmode=auto ; Set default dtmfmode for sending DTMF. Default: rfc2833
|
||||
; Other options:
|
||||
; info : SIP INFO messages (application/dtmf-relay)
|
||||
; shortinfo : SIP INFO messages (application/dtmf)
|
||||
; inband : Inband audio (requires 64 kbit codec -alaw, ulaw)
|
||||
; auto : Use rfc2833 if offered, inband otherwise
|
||||
|
||||
|
||||
; Zoiper is used as a fixture for factory testing.
|
||||
[zoiper]
|
||||
secret=3078923984
|
||||
callerid=2101
|
||||
canreinvite=no
|
||||
type=friend
|
||||
context=sip-local
|
||||
host=dynamic
|
||||
dtmfmode=auto
|
||||
|
||||
; This is a test SIM provided with the BTS.
|
||||
[IMSI001010000000000]
|
||||
callerid=2100
|
||||
canreinvite=no
|
||||
type=friend
|
||||
context=sip-local
|
||||
host=dynamic
|
||||
|
||||
768
CLI/CLI.cpp
Normal file
768
CLI/CLI.cpp
Normal file
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <config.h>
|
||||
|
||||
// #include <Config.h>
|
||||
#include <Logger.h>
|
||||
|
||||
#include <GSMConfig.h>
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <ControlCommon.h>
|
||||
#include <TransactionTable.h>
|
||||
#include <TRXManager.h>
|
||||
#include <PowerManager.h>
|
||||
#include <TMSITable.h>
|
||||
#include <RadioResource.h>
|
||||
#include <CallControl.h>
|
||||
|
||||
#include <Globals.h>
|
||||
|
||||
#include "CLI.h"
|
||||
|
||||
#undef WARNING
|
||||
|
||||
using namespace std;
|
||||
using namespace CommandLine;
|
||||
|
||||
#define SUCCESS 0
|
||||
#define BAD_NUM_ARGS 1
|
||||
#define BAD_VALUE 2
|
||||
#define NOT_FOUND 3
|
||||
#define TOO_MANY_ARGS 4
|
||||
#define FAILURE 5
|
||||
|
||||
extern TransceiverManager gTRX;
|
||||
|
||||
/** Standard responses in the CLI, much mach erorrCode enum. */
|
||||
static const char* standardResponses[] = {
|
||||
"success", // 0
|
||||
"wrong number of arguments", // 1
|
||||
"bad argument(s)", // 2
|
||||
"command not found", // 3
|
||||
"too many arguments for parser", // 4
|
||||
"command failed", // 5
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int Parser::execute(char* line, ostream& os, istream& is) const
|
||||
{
|
||||
// escape to the shell?
|
||||
if (line[0]=='!') {
|
||||
os << endl;
|
||||
int retVal = system(line+1);
|
||||
os << endl << "External call returned " << retVal << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
// tokenize
|
||||
char *argv[mMaxArgs];
|
||||
int argc = 0;
|
||||
char **ap;
|
||||
// This is (almost) straight from the man page for strsep.
|
||||
for (ap=argv; (*ap=strsep(&line," ")) != NULL; ) {
|
||||
if (**ap != '\0') {
|
||||
if (++ap >= &argv[mMaxArgs]) break;
|
||||
else argc++;
|
||||
}
|
||||
}
|
||||
// Blank line?
|
||||
if (!argc) return SUCCESS;
|
||||
// Find the command.
|
||||
ParseTable::const_iterator cfp = mParseTable.find(argv[0]);
|
||||
if (cfp == mParseTable.end()) {
|
||||
return NOT_FOUND;
|
||||
}
|
||||
int (*func)(int,char**,ostream&,istream&);
|
||||
func = cfp->second;
|
||||
// Do it.
|
||||
int retVal = (*func)(argc,argv,os,is);
|
||||
// Give hint on bad # args.
|
||||
if (retVal==BAD_NUM_ARGS) os << help(argv[0]) << endl;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
int Parser::process(const char* line, ostream& os, istream& is) const
|
||||
{
|
||||
char *newLine = strdup(line);
|
||||
int retVal = execute(newLine,os,is);
|
||||
free(newLine);
|
||||
if (retVal>0) os << standardResponses[retVal] << endl;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
const char * Parser::help(const string& cmd) const
|
||||
{
|
||||
HelpTable::const_iterator hp = mHelpTable.find(cmd);
|
||||
if (hp==mHelpTable.end()) return "no help available";
|
||||
return hp->second.c_str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**@name Commands for the CLI. */
|
||||
//@{
|
||||
|
||||
// forward refs
|
||||
int printStats(int argc, char** argv, ostream& os, istream& is);
|
||||
|
||||
/*
|
||||
A CLI command takes the argument in an array.
|
||||
It returns 0 on success.
|
||||
*/
|
||||
|
||||
/** Display system uptime and current GSM frame number. */
|
||||
int uptime(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
os.precision(2);
|
||||
os << "Unix time " << time(NULL) << endl;
|
||||
int seconds = gBTS.uptime();
|
||||
if (seconds<120) {
|
||||
os << "uptime " << seconds << " seconds, frame " << gBTS.time() << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
float minutes = seconds / 60.0F;
|
||||
if (minutes<120) {
|
||||
os << "uptime " << minutes << " minutes, frame " << gBTS.time() << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
float hours = minutes / 60.0F;
|
||||
if (hours<48) {
|
||||
os << "uptime " << hours << " hours, frame " << gBTS.time() << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
float days = hours / 24.0F;
|
||||
os << "uptime " << days << " days, frame " << gBTS.time() << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Give a list of available commands or describe a specific command. */
|
||||
int showHelp(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc==2) {
|
||||
os << argv[1] << " " << gParser.help(argv[1]) << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
ParseTable::const_iterator cp = gParser.begin();
|
||||
os << endl << "Type \"help\" followed by the command name for help on that command." << endl << endl;
|
||||
int c=0;
|
||||
const int cols = 3;
|
||||
while (cp != gParser.end()) {
|
||||
const string& wd = cp->first;
|
||||
os << wd << '\t';
|
||||
if (wd.size()<8) os << '\t';
|
||||
++cp;
|
||||
c++;
|
||||
if (c%cols==0) os << endl;
|
||||
}
|
||||
if (c%cols!=0) os << endl;
|
||||
os << endl << "Lines starting with '!' are escaped to the shell." << endl;
|
||||
os << endl << "Use <cntrl-A>, <D> to detach from \"screen\", *not* <cntrl-C>." << endl << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** A function to return -1, the exit code for the caller. */
|
||||
int exit_function(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
unsigned wait =0;
|
||||
if (argc>2) return BAD_NUM_ARGS;
|
||||
if (argc==2) wait = atoi(argv[1]);
|
||||
|
||||
if (wait!=0)
|
||||
os << "waiting up to " << wait << " seconds for clearing of "
|
||||
<< gBTS.TCHActive() << " active calls" << endl;
|
||||
|
||||
// Block creation of new channels.
|
||||
gBTS.hold(true);
|
||||
// Wait up to the timeout for active channels to release.
|
||||
time_t finish = time(NULL) + wait;
|
||||
while (time(NULL)<finish) {
|
||||
unsigned load = gBTS.SDCCHActive() + gBTS.TCHActive();
|
||||
if (load==0) break;
|
||||
sleep(1);
|
||||
}
|
||||
bool loads = false;
|
||||
if (gBTS.SDCCHActive()>0) {
|
||||
LOG(WARNING) << "dropping " << gBTS.SDCCHActive() << " control transactions on exit";
|
||||
loads = true;
|
||||
}
|
||||
if (gBTS.TCHActive()>0) {
|
||||
LOG(WARNING) << "dropping " << gBTS.TCHActive() << " calls on exit";
|
||||
loads = true;
|
||||
}
|
||||
if (loads) {
|
||||
os << endl << "exiting with loads:" << endl;
|
||||
printStats(1,NULL,os,is);
|
||||
}
|
||||
os << endl << "exiting..." << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Forward ref.
|
||||
int tmsis(int argc, char** argv, ostream& os, istream& is);
|
||||
|
||||
/** Dump TMSI table to a text file. */
|
||||
int dumpTMSIs(const char* filename, istream& is)
|
||||
{
|
||||
ofstream fileout;
|
||||
fileout.open(filename, ios::out); // erases existing!
|
||||
// FIXME -- Check that the file really opened.
|
||||
// Fake an argument list to call printTMSIs.
|
||||
char* subargv[] = {"tmsis", NULL};
|
||||
int subargc = 1;
|
||||
return tmsis(subargc, subargv, fileout, is);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Print or clear the TMSI table. */
|
||||
int tmsis(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc>=2) {
|
||||
// Clear?
|
||||
if (strcmp(argv[1],"clear")==0) {
|
||||
if (argc!=2) return BAD_NUM_ARGS;
|
||||
os << "clearing TMSI table" << endl;
|
||||
gTMSITable.clear();
|
||||
return SUCCESS;
|
||||
}
|
||||
// Dump?
|
||||
if (strcmp(argv[1],"dump")==0) {
|
||||
if (argc!=3) return BAD_NUM_ARGS;
|
||||
os << "dumping TMSI table to " << argv[2] << endl;
|
||||
return dumpTMSIs(argv[2],is);
|
||||
}
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
os << "TMSI IMSI age used" << endl;
|
||||
gTMSITable.dump(os);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Submit an SMS for delivery to an IMSI. */
|
||||
int sendsimple(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=3) return BAD_NUM_ARGS;
|
||||
|
||||
os << "enter text to send: ";
|
||||
char txtBuf[161];
|
||||
cin.getline(txtBuf,160,'\n');
|
||||
char *IMSI = argv[1];
|
||||
char *srcAddr = argv[2];
|
||||
|
||||
static UDPSocket sock(0,"127.0.0.1",gConfig.getNum("SIP.Local.Port"));
|
||||
|
||||
static const char form[] =
|
||||
"MESSAGE sip:IMSI%s@127.0.0.1 SIP/2.0\n"
|
||||
"Via: SIP/2.0/TCP 127.0.0.1;branch=%x\n"
|
||||
"Max-Forwards: 2\n"
|
||||
"From: %s <sip:%s@127.0.0.1:%d>;tag=%d\n"
|
||||
"To: sip:IMSI%s@127.0.0.1\n"
|
||||
"Call-ID: %x@127.0.0.1:%d\n"
|
||||
"CSeq: 1 MESSAGE\n"
|
||||
"Content-Type: text/plain\nContent-Length: %u\n"
|
||||
"\n%s\n";
|
||||
static char buffer[1500];
|
||||
snprintf(buffer,1499,form,
|
||||
IMSI, (unsigned)random(), srcAddr,srcAddr,sock.port(),(unsigned)random(), IMSI, (unsigned)random(),sock.port(), strlen(txtBuf), txtBuf);
|
||||
sock.write(buffer);
|
||||
|
||||
|
||||
#if 0
|
||||
int numRead = sock.read(buffer,10000);
|
||||
if (numRead>=0) {
|
||||
buffer[numRead]='\0';
|
||||
os << "response: " << buffer << endl;
|
||||
} else {
|
||||
os << "timed out waiting for response";
|
||||
}
|
||||
#endif
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Submit an SMS for delivery to an IMSI. */
|
||||
int sendsms(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=3) return BAD_NUM_ARGS;
|
||||
|
||||
os << "enter text to send: ";
|
||||
char txtBuf[161];
|
||||
cin.getline(txtBuf,160,'\n');
|
||||
char *IMSI = argv[1];
|
||||
char *srcAddr = argv[2];
|
||||
|
||||
Control::TransactionEntry *transaction = new Control::TransactionEntry(
|
||||
gConfig.getStr("SIP.Proxy.SMS").c_str(),
|
||||
GSM::L3MobileIdentity(IMSI),
|
||||
NULL,
|
||||
GSM::L3CMServiceType::MobileTerminatedShortMessage,
|
||||
GSM::L3CallingPartyBCDNumber(srcAddr),
|
||||
GSM::Paging,
|
||||
txtBuf);
|
||||
transaction->messageType("text/plain");
|
||||
Control::initiateMTTransaction(transaction,GSM::SDCCHType,30000);
|
||||
os << "message submitted for delivery" << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/** DEBUGGING: Sends a special sms that triggers a RRLP message to an IMSI. */
|
||||
int sendrrlp(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=3) return BAD_NUM_ARGS;
|
||||
|
||||
char *IMSI = argv[1];
|
||||
|
||||
UDPSocket sock(0,"127.0.0.1",gConfig.getNum("SIP.Local.Port"));
|
||||
unsigned port = sock.port();
|
||||
unsigned callID = random();
|
||||
|
||||
// Just fake out a SIP message.
|
||||
const char form[] = "MESSAGE sip:IMSI%s@localhost SIP/2.0\nVia: SIP/2.0/TCP localhost;branch=z9hG4bK776sgdkse\nMax-Forwards: 2\nFrom: RRLP@localhost:%d;tag=49583\nTo: sip:IMSI%s@localhost\nCall-ID: %d@127.0.0.1:5063\nCSeq: 1 MESSAGE\nContent-Type: text/plain\nContent-Length: %lu\n\n%s\n";
|
||||
|
||||
char txtBuf[161];
|
||||
snprintf(txtBuf,160,"RRLP%s",argv[2]);
|
||||
char outbuf[1500];
|
||||
snprintf(outbuf,1499,form,IMSI,port,IMSI,callID,strlen(txtBuf),txtBuf);
|
||||
sock.write(outbuf);
|
||||
sleep(2);
|
||||
sock.write(outbuf);
|
||||
sock.close();
|
||||
os << "RRLP Triggering message submitted for delivery" << endl;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Print current usage loads. */
|
||||
int printStats(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
os << "SDCCH load: " << gBTS.SDCCHActive() << '/' << gBTS.SDCCHTotal() << endl;
|
||||
os << "TCH/F load: " << gBTS.TCHActive() << '/' << gBTS.TCHTotal() << endl;
|
||||
os << "AGCH/PCH load: " << gBTS.AGCHLoad() << ',' << gBTS.PCHLoad() << endl;
|
||||
// paging table size
|
||||
os << "Paging table size: " << gBTS.pager().pagingEntryListSize() << endl;
|
||||
os << "Transactions: " << gTransactionTable.size() << endl;
|
||||
// 3122 timer current value (the number of seconds an MS should hold off the next RACH)
|
||||
os << "T3122: " << gBTS.T3122() << " ms" << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Get/Set MCC, MNC, LAC, CI. */
|
||||
int cellID(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc==1) {
|
||||
os << "MCC=" << gConfig.getStr("GSM.Identity.MCC")
|
||||
<< " MNC=" << gConfig.getStr("GSM.Identity.MNC")
|
||||
<< " LAC=" << gConfig.getNum("GSM.Identity.LAC")
|
||||
<< " CI=" << gConfig.getNum("GSM.Identity.CI")
|
||||
<< endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (argc!=5) return BAD_NUM_ARGS;
|
||||
|
||||
// Safety check the args!!
|
||||
char* MCC = argv[1];
|
||||
char* MNC = argv[2];
|
||||
if (strlen(MCC)!=3) {
|
||||
os << "MCC must be three digits" << endl;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
int MNCLen = strlen(MNC);
|
||||
if ((MNCLen<2)||(MNCLen>3)) {
|
||||
os << "MNC must be two or three digits" << endl;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
gTMSITable.clear();
|
||||
gConfig.set("GSM.Identity.MCC",MCC);
|
||||
gConfig.set("GSM.Identity.MNC",MNC);
|
||||
gConfig.set("GSM.Identity.LAC",argv[3]);
|
||||
gConfig.set("GSM.Identity.CI",argv[4]);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Print table of current transactions. */
|
||||
int calls(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
size_t count = gTransactionTable.dump(os);
|
||||
os << endl << count << " transactions in table" << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Print or modify the global configuration table. */
|
||||
int config(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
// no args, just print
|
||||
if (argc==1) {
|
||||
gConfig.find("",os);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// one arg, pattern match and print
|
||||
if (argc==2) {
|
||||
gConfig.find(argv[1],os);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// >1 args: set new value
|
||||
string val;
|
||||
for (int i=2; i<argc; i++) {
|
||||
val.append(argv[i]);
|
||||
if (i!=(argc-1)) val.append(" ");
|
||||
}
|
||||
bool existing = gConfig.defines(argv[1]);
|
||||
if (gConfig.isStatic(argv[1])) {
|
||||
os << argv[1] << " is static; change takes effect on restart" << endl;
|
||||
}
|
||||
if (!gConfig.set(argv[1],val)) {
|
||||
os << argv[1] << " change failed" << endl;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (!existing) {
|
||||
os << "defined new config " << argv[1] << " as \"" << val << "\"" << endl;
|
||||
// Anything created by the CLI is optional.
|
||||
//gConfig.makeOptional(argv[1]);
|
||||
} else {
|
||||
os << "changed " << argv[1] << " to \"" << val << "\"" << endl;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/** Remove a configiuration value. */
|
||||
int unconfig(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=2) return BAD_NUM_ARGS;
|
||||
|
||||
if (gConfig.unset(argv[1])) {
|
||||
os << "\"" << argv[1] << "\" removed from the configuration table" << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
if (gConfig.defines(argv[1])) {
|
||||
os << "\"" << argv[1] << "\" could not be removed" << endl;
|
||||
} else {
|
||||
os << "\"" << argv[1] << "\" was not in the table" << endl;
|
||||
}
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
/** Dump current configuration to a file. */
|
||||
int configsave(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
os << "obsolete" << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Change the registration timers. */
|
||||
int regperiod(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc==1) {
|
||||
os << "T3212 is " << gConfig.getNum("GSM.Timer.T3212") << " minutes" << endl;
|
||||
os << "SIP registration period is " << gConfig.getNum("SIP.RegistrationPeriod")/60 << " minutes" << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (argc>3) return BAD_NUM_ARGS;
|
||||
|
||||
unsigned newT3212 = strtol(argv[1],NULL,10);
|
||||
if ((newT3212<6)||(newT3212>1530)) {
|
||||
os << "valid T3212 range is 6..1530 minutes" << endl;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// By defuault, make SIP registration period 1.5x the GSM registration period.
|
||||
unsigned SIPRegPeriod = newT3212*90;
|
||||
if (argc==3) {
|
||||
SIPRegPeriod = 60*strtol(argv[2],NULL,10);
|
||||
}
|
||||
|
||||
// Set the values in the table and on the GSM beacon.
|
||||
gConfig.set("SIP.RegistrationPeriod",SIPRegPeriod);
|
||||
gConfig.set("GSM.Timer.T3212",newT3212);
|
||||
// Done.
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Print the list of alarms kept by the logger, i.e. the last LOG(ALARM) << <text> */
|
||||
int alarms(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
std::ostream_iterator<std::string> output( os, "\n" );
|
||||
std::list<std::string> alarms = gGetLoggerAlarms();
|
||||
std::copy( alarms.begin(), alarms.end(), output );
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/** Version string. */
|
||||
int version(int argc, char **argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
os << "release " VERSION " built " __DATE__ << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/** Show start-up notices. */
|
||||
int notices(int argc, char **argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
os << endl << gOpenBTSWelcome << endl;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int page(int argc, char **argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc==1) {
|
||||
gBTS.pager().dump(os);
|
||||
return SUCCESS;
|
||||
}
|
||||
if (argc!=3) return BAD_NUM_ARGS;
|
||||
char *IMSI = argv[1];
|
||||
if (strlen(IMSI)>15) {
|
||||
os << IMSI << " is not a valid IMSI" << endl;
|
||||
return BAD_VALUE;
|
||||
}
|
||||
Control::TransactionEntry dummy(
|
||||
gConfig.getStr("SIP.Proxy.SMS").c_str(),
|
||||
GSM::L3MobileIdentity(IMSI),
|
||||
NULL,
|
||||
GSM::L3CMServiceType::UndefinedType,
|
||||
GSM::L3CallingPartyBCDNumber("0"),
|
||||
GSM::Paging);
|
||||
gBTS.pager().addID(GSM::L3MobileIdentity(IMSI),GSM::SDCCHType,dummy,1000*atoi(argv[2]));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int endcall(int argc, char **argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=2) return BAD_NUM_ARGS;
|
||||
unsigned transID = atoi(argv[1]);
|
||||
Control::TransactionEntry* target = gTransactionTable.find(transID);
|
||||
if (!target) {
|
||||
os << transID << " not found in table";
|
||||
return BAD_VALUE;
|
||||
}
|
||||
target->terminate();
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void printChanInfo(unsigned transID, const GSM::LogicalChannel* chan, ostream& os)
|
||||
{
|
||||
os << setw(2) << chan->CN() << " " << chan->TN();
|
||||
os << " " << setw(8) << chan->typeAndOffset();
|
||||
os << " " << setw(12) << transID;
|
||||
char buffer[200];
|
||||
snprintf(buffer,199,"%5.2f %4d %5d %4d",
|
||||
100.0*chan->FER(), (int)round(chan->RSSI()),
|
||||
chan->actualMSPower(), chan->actualMSTiming());
|
||||
os << " " << buffer;
|
||||
const GSM::L3MeasurementResults& meas = chan->SACCH()->measurementResults();
|
||||
if (!meas.MEAS_VALID()) {
|
||||
snprintf(buffer,199,"%5d %5.2f",
|
||||
meas.RXLEV_FULL_SERVING_CELL_dBm(),
|
||||
100.0*meas.RXQUAL_FULL_SERVING_CELL_BER());
|
||||
os << " " << buffer;
|
||||
} else {
|
||||
os << " ----- ------";
|
||||
}
|
||||
os << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int chans(int argc, char **argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
|
||||
os << "CN TN chan transaction UPFER RSSI TXPWR TXTA DNLEV DNBER" << endl;
|
||||
os << "CN TN type id pct dB dBm sym dBm pct" << endl;
|
||||
|
||||
//gPhysStatus.dump(os);
|
||||
//os << endl << "Old data reporting: " << endl;
|
||||
|
||||
// SDCCHs
|
||||
GSM::SDCCHList::const_iterator sChanItr = gBTS.SDCCHPool().begin();
|
||||
while (sChanItr != gBTS.SDCCHPool().end()) {
|
||||
const GSM::SDCCHLogicalChannel* sChan = *sChanItr;
|
||||
if (sChan->active()) {
|
||||
Control::TransactionEntry *trans = gTransactionTable.find(sChan);
|
||||
if (trans) printChanInfo(trans->ID(),sChan,os);
|
||||
else printChanInfo(0,sChan,os);
|
||||
}
|
||||
++sChanItr;
|
||||
}
|
||||
|
||||
// TCHs
|
||||
GSM::TCHList::const_iterator tChanItr = gBTS.TCHPool().begin();
|
||||
while (tChanItr != gBTS.TCHPool().end()) {
|
||||
const GSM::TCHFACCHLogicalChannel* tChan = *tChanItr;
|
||||
if (tChan->active()) {
|
||||
Control::TransactionEntry *trans = gTransactionTable.find(tChan);
|
||||
if (trans) printChanInfo(trans->ID(),tChan,os);
|
||||
else printChanInfo(0,tChan,os);
|
||||
}
|
||||
++tChanItr;
|
||||
}
|
||||
|
||||
os << endl;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int power(int argc, char **argv, ostream& os, istream& is)
|
||||
{
|
||||
os << "current downlink power " << gBTS.powerManager().power() << " dB wrt full scale" << endl;
|
||||
os << "current attenuation bounds "
|
||||
<< gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")
|
||||
<< " to "
|
||||
<< gConfig.getNum("GSM.Radio.PowerManager.MaxAttenDB")
|
||||
<< " dB" << endl;
|
||||
|
||||
if (argc==1) return SUCCESS;
|
||||
if (argc!=3) return BAD_NUM_ARGS;
|
||||
|
||||
int min = atoi(argv[1]);
|
||||
int max = atoi(argv[2]);
|
||||
if (min>max) return BAD_VALUE;
|
||||
|
||||
gConfig.set("GSM.Radio.PowerManager.MinAttenDB",argv[1]);
|
||||
gConfig.set("GSM.Radio.PowerManager.MaxAttenDB",argv[2]);
|
||||
|
||||
os << "new attenuation bounds "
|
||||
<< gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")
|
||||
<< " to "
|
||||
<< gConfig.getNum("GSM.Radio.PowerManager.MaxAttenDB")
|
||||
<< " dB" << endl;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int rxgain(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
os << "current RX gain is " << gConfig.getNum("GSM.Radio.RxGain") << " dB" << endl;
|
||||
if (argc==1) return SUCCESS;
|
||||
if (argc!=2) return BAD_NUM_ARGS;
|
||||
|
||||
int newGain = gTRX.ARFCN()->setRxGain(atoi(argv[1]));
|
||||
os << "new RX gain is " << newGain << " dB" << endl;
|
||||
|
||||
gConfig.set("GSM.Radio.RxGain",newGain);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int noise(int argc, char** argv, ostream& os, istream& is)
|
||||
{
|
||||
if (argc!=1) return BAD_NUM_ARGS;
|
||||
|
||||
int noise = gTRX.ARFCN()->getNoiseLevel();
|
||||
os << "noise RSSI is -" << noise << " dB wrt full scale" << endl;
|
||||
os << "MS RSSI target is " << gConfig.getNum("GSM.Radio.RSSITarget") << " dB wrt full scale" << endl;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
//@} // CLI commands
|
||||
|
||||
|
||||
|
||||
Parser::Parser()
|
||||
{
|
||||
// The constructor adds the commands.
|
||||
addCommand("uptime", uptime, "-- show BTS uptime and BTS frame number.");
|
||||
addCommand("help", showHelp, "[command] -- list available commands or gets help on a specific command.");
|
||||
addCommand("exit", exit_function, "[wait] -- exit the application, either immediately, or waiting for existing calls to clear with a timeout in seconds");
|
||||
addCommand("tmsis", tmsis, "[\"clear\"] or [\"dump\" filename] -- print/clear the TMSI table or dump it to a file.");
|
||||
addCommand("sendsms", sendsms, "<IMSI> <src> -- send direct SMS to <IMSI>, addressed from <src>, after prompting.");
|
||||
addCommand("sendsimple", sendsimple, "<IMSI> <src> -- send SMS to <IMSI> via SIP interface, addressed from <src>, after prompting.");
|
||||
addCommand("sendrrlp", sendrrlp, "<IMSI> <hexstring> -- send RRLP message <hexstring> to <IMSI>.");
|
||||
addCommand("load", printStats, "-- print the current activity loads.");
|
||||
addCommand("cellid", cellID, "[MCC MNC LAC CI] -- get/set location area identity (MCC, MNC, LAC) and cell ID (CI)");
|
||||
addCommand("calls", calls, "-- print the transaction table");
|
||||
addCommand("config", config, "[] OR [patt] OR [key val(s)] -- print the current configuration, print configuration values matching a pattern, or set/change a configuration value");
|
||||
addCommand("configsave", configsave, "<path> -- write the current configuration to a file");
|
||||
addCommand("regperiod", regperiod, "[GSM] [SIP] -- get/set the registration period (GSM T3212), in MINUTES");
|
||||
addCommand("alarms", alarms, "-- show latest alarms");
|
||||
addCommand("version", version,"-- print the version string");
|
||||
addCommand("page", page, "[IMSI time] -- dump the paging table or page the given IMSI for the given period");
|
||||
addCommand("chans", chans, "-- report PHY status for active channels");
|
||||
addCommand("power", power, "[minAtten maxAtten] -- report current attentuation or set min/max bounds");
|
||||
addCommand("rxgain", rxgain, "[newRxgain] -- get/set the RX gain in dB");
|
||||
addCommand("noise", noise, "-- report receive noise level in RSSI dB");
|
||||
addCommand("unconfig", unconfig, "key -- remove a config value");
|
||||
addCommand("notices", notices, "-- show startup copyright and legal notices");
|
||||
addCommand("endcall", endcall,"trans# -- terminate the given transaction");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
76
CLI/CLI.h
Normal file
76
CLI/CLI.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPENBTSCLI_H
|
||||
#define OPENBTSCLI_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace CommandLine {
|
||||
|
||||
|
||||
/** A table for matching strings to actions. */
|
||||
typedef std::map<std::string,int (*)(int,char**,std::ostream&,std::istream&)> ParseTable;
|
||||
|
||||
/** The help table. */
|
||||
typedef std::map<std::string,std::string> HelpTable;
|
||||
|
||||
class Parser {
|
||||
|
||||
private:
|
||||
|
||||
ParseTable mParseTable;
|
||||
HelpTable mHelpTable;
|
||||
static const int mMaxArgs = 10;
|
||||
|
||||
public:
|
||||
|
||||
Parser();
|
||||
|
||||
/**
|
||||
Process a command line.
|
||||
@return 0 on sucess, -1 on exit request, error codes otherwise
|
||||
*/
|
||||
int process(const char* line, std::ostream& os, std::istream& is) const;
|
||||
|
||||
/** Add a command to the parsing table. */
|
||||
void addCommand(const char* name, int (*func)(int,char**,std::ostream&,std::istream&), const char* helpString)
|
||||
{ mParseTable[name] = func; mHelpTable[name]=helpString; }
|
||||
|
||||
ParseTable::const_iterator begin() const { return mParseTable.begin(); }
|
||||
ParseTable::const_iterator end() const { return mParseTable.end(); }
|
||||
|
||||
/** Return a help string. */
|
||||
const char *help(const std::string& cmd) const;
|
||||
|
||||
private:
|
||||
|
||||
/** Parse and execute a command string. */
|
||||
int execute(char* line, std::ostream& os, std::istream& is) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // CLI
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
35
CLI/Makefile.am
Normal file
35
CLI/Makefile.am
Normal file
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# Copyright 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
EXTRA_DIST = \
|
||||
README.CLI
|
||||
|
||||
AM_CXXFLAGS = -Wall
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
|
||||
noinst_LTLIBRARIES = libcli.la
|
||||
|
||||
libcli_la_SOURCES = \
|
||||
CLI.cpp
|
||||
|
||||
noinst_HEADERS = \
|
||||
CLI.h
|
||||
531
CLI/Makefile.in
Normal file
531
CLI/Makefile.in
Normal file
@@ -0,0 +1,531 @@
|
||||
# Makefile.in generated by automake 1.9.4 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004 Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
# Copyright 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
SOURCES = $(libcli_la_SOURCES)
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = ..
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(top_srcdir)/Makefile.common
|
||||
subdir = CLI
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||
libcli_la_LIBADD =
|
||||
am_libcli_la_OBJECTS = CLI.lo
|
||||
libcli_la_OBJECTS = $(am_libcli_la_OBJECTS)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
CXXLD = $(CXX)
|
||||
CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
|
||||
$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(libcli_la_SOURCES)
|
||||
DIST_SOURCES = $(libcli_la_SOURCES)
|
||||
HEADERS = $(noinst_HEADERS)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
ECHO = @ECHO@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
F77 = @F77@
|
||||
FFLAGS = @FFLAGS@
|
||||
GREP = @GREP@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIBUSB_CFLAGS = @LIBUSB_CFLAGS@
|
||||
LIBUSB_LIBS = @LIBUSB_LIBS@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
ORTP_CFLAGS = @ORTP_CFLAGS@
|
||||
ORTP_LIBS = @ORTP_LIBS@
|
||||
OSIP_CFLAGS = @OSIP_CFLAGS@
|
||||
OSIP_LIBS = @OSIP_LIBS@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
RANLIB = @RANLIB@
|
||||
RM_PROG = @RM_PROG@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_F77 = @ac_ct_F77@
|
||||
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
|
||||
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
CONTROL_INCLUDEDIR = $(top_srcdir)/Control
|
||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||
SIP_INCLUDEDIR = $(top_srcdir)/SIP
|
||||
SMS_INCLUDEDIR = $(top_srcdir)/SMS
|
||||
TRX_INCLUDEDIR = $(top_srcdir)/TRXManager
|
||||
GLOBALS_INCLUDEDIR = $(top_srcdir)/Globals
|
||||
CLI_INCLUDEDIR = $(top_srcdir)/CLI
|
||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||
SR_INCLUDEDIR = $(top_srcdir)/SR
|
||||
STD_DEFINES_AND_INCLUDES = \
|
||||
-I$(COMMON_INCLUDEDIR) \
|
||||
-I$(CONTROL_INCLUDEDIR) \
|
||||
-I$(GSM_INCLUDEDIR) \
|
||||
-I$(SIP_INCLUDEDIR) \
|
||||
-I$(SMS_INCLUDEDIR) \
|
||||
-I$(TRX_INCLUDEDIR) \
|
||||
-I$(GLOBALS_INCLUDEDIR) \
|
||||
-I$(CLI_INCLUDEDIR) \
|
||||
-I$(SR_INCLUDEDIR) \
|
||||
-I$(SQLITE_INCLUDEDIR)
|
||||
|
||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||
SIP_LA = $(top_builddir)/SIP/libSIP.la
|
||||
SMS_LA = $(top_builddir)/SMS/libSMS.la
|
||||
TRX_LA = $(top_builddir)/TRXManager/libtrxmanager.la
|
||||
CONTROL_LA = $(top_builddir)/Control/libcontrol.la
|
||||
GLOBALS_LA = $(top_builddir)/Globals/libglobals.la
|
||||
CLI_LA = $(top_builddir)/CLI/libcli.la
|
||||
SR_LA = $(top_builddir)/SR/libSR.la
|
||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la
|
||||
MOSTLYCLEANFILES = *~
|
||||
EXTRA_DIST = \
|
||||
README.CLI
|
||||
|
||||
AM_CXXFLAGS = -Wall
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
noinst_LTLIBRARIES = libcli.la
|
||||
libcli_la_SOURCES = \
|
||||
CLI.cpp
|
||||
|
||||
noinst_HEADERS = \
|
||||
CLI.h
|
||||
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .cpp .lo .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Makefile.common $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
|
||||
&& exit 0; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu CLI/Makefile'; \
|
||||
cd $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu CLI/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
clean-noinstLTLIBRARIES:
|
||||
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
|
||||
@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
|
||||
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
|
||||
test "$$dir" != "$$p" || dir=.; \
|
||||
echo "rm -f \"$${dir}/so_locations\""; \
|
||||
rm -f "$${dir}/so_locations"; \
|
||||
done
|
||||
libcli.la: $(libcli_la_OBJECTS) $(libcli_la_DEPENDENCIES)
|
||||
$(CXXLINK) $(libcli_la_LDFLAGS) $(libcli_la_OBJECTS) $(libcli_la_LIBADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CLI.Plo@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.cpp.lo:
|
||||
@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
distclean-libtool:
|
||||
-rm -f libtool
|
||||
uninstall-info-am:
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
mkid -fID $$unique
|
||||
tags: TAGS
|
||||
|
||||
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
tags=; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$tags $$unique; \
|
||||
fi
|
||||
ctags: CTAGS
|
||||
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
tags=; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
test -z "$(CTAGS_ARGS)$$tags$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$tags $$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& cd $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) $$here
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
$(mkdir_p) $(distdir)/..
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
|
||||
else \
|
||||
test -f $(distdir)/$$file \
|
||||
|| cp -p $$d/$$file $(distdir)/$$file \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
`test -z '$(STRIP)' || \
|
||||
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
|
||||
mostlyclean-generic:
|
||||
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
|
||||
mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-libtool distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-info-am
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libtool clean-noinstLTLIBRARIES ctags distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-exec \
|
||||
install-exec-am install-info install-info-am install-man \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags uninstall uninstall-am \
|
||||
uninstall-info-am
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
2
CLI/README.CLI
Normal file
2
CLI/README.CLI
Normal file
@@ -0,0 +1,2 @@
|
||||
This is the directory for the OpenBTS command line interface.
|
||||
|
||||
724
COPYING
Normal file
724
COPYING
Normal file
@@ -0,0 +1,724 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
|
||||
=========================================================================
|
||||
|
||||
This marks the end of the AGPLv3 text. The following text is appended to the
|
||||
same file for convience but constituting a distinct document, not part of the
|
||||
actual AGPL text and not part of an attempt to create a deriviative work based
|
||||
on the AGPLv3 text.
|
||||
|
||||
=========================================================================
|
||||
|
||||
|
||||
ADDITIONAL TERMS TO THE AGPLv3 LICENSE FOR OPENBTS
|
||||
|
||||
|
||||
Permissive Terms Supplementing the License
|
||||
|
||||
1. Remote Interaction Through IP Networks.
|
||||
|
||||
OpenBTS includes an implementation of the GSM network cellular air interface,
|
||||
as well as other interfaces to IP networks. The interaction of cellular
|
||||
handsets with the OpenBTS software is considered "remote network interaction"
|
||||
for the purposes of the Affero General Public License and cellular users are
|
||||
subject to the source code access requirements of Section 13 of AGPLv3 ("Remote
|
||||
Network Interaction; Use with the GNU General Public License").
|
||||
|
||||
Remote interactions through interfaces other than the GSM air interface are, at
|
||||
your option, exempted from the requirements of Section 13 ("Remote Network
|
||||
Interaction; Use with the GNU General Public License"). This exemption of
|
||||
interfaces other than the GSM air interface from the requirements of Section 13
|
||||
is an additional permission granted to you.
|
||||
|
||||
|
||||
Non-Permissive Terms Supplementing The License
|
||||
|
||||
1. Trademarks.
|
||||
|
||||
"OpenBTS" is a trademark of Range Networks, Inc., registered with
|
||||
the US Patent and Trademark Office. Your use of OpenBTS software under a GPL
|
||||
license does not include the right to use the OpenBTS trademark in commerce.
|
||||
This additional non-permissive term is consistent with Section 7 of the AGPLv3
|
||||
license.
|
||||
|
||||
|
||||
END OF ADDITIONAL TERMS
|
||||
|
||||
|
||||
|
||||
How to comply with Section 13 of the AGPLv3 license.
|
||||
|
||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
|
||||
to deliver a text message to each handset that attaches to the OpenBTS cellular
|
||||
network. At a minimum, that text message should include the string "OpenBTS
|
||||
AGPLv3" and a URL that can be used to access the OpenBTS source code. This
|
||||
message need not be delivered to handsets that are denied registration with the
|
||||
network, since those handsets have been denied service.
|
||||
|
||||
In OpenBTS 2.6, such text messages can be delivered with the "Welcome Message"
|
||||
feature. See the OpenBTS.config.example file for more information on the use of
|
||||
this feature for AGPLv3 compliance.
|
||||
|
||||
603
CommonLibs/BitVector.cpp
Normal file
603
CommonLibs/BitVector.cpp
Normal file
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "BitVector.h"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/**
|
||||
Apply a Galois polymonial to a binary seqeunce.
|
||||
@param val The input sequence.
|
||||
@param poly The polynomial.
|
||||
@param order The order of the polynomial.
|
||||
@return Single-bit result.
|
||||
*/
|
||||
unsigned applyPoly(uint64_t val, uint64_t poly, unsigned order)
|
||||
{
|
||||
uint64_t prod = val & poly;
|
||||
unsigned sum = prod;
|
||||
for (unsigned i=1; i<order; i++) sum ^= prod>>i;
|
||||
return sum & 0x01;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BitVector::BitVector(const char *valString)
|
||||
:Vector<char>(strlen(valString))
|
||||
{
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
accum <<= 1;
|
||||
if (valString[i]=='1') accum |= 0x01;
|
||||
mStart[i] = accum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
uint64_t BitVector::peekField(size_t readIndex, unsigned length) const
|
||||
{
|
||||
uint64_t accum = 0;
|
||||
char *dp = mStart + readIndex;
|
||||
assert(dp+length <= mEnd);
|
||||
for (unsigned i=0; i<length; i++) {
|
||||
accum = (accum<<1) | ((*dp++) & 0x01);
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint64_t BitVector::peekFieldReversed(size_t readIndex, unsigned length) const
|
||||
{
|
||||
uint64_t accum = 0;
|
||||
char *dp = mStart + readIndex + length - 1;
|
||||
assert(dp<mEnd);
|
||||
for (int i=(length-1); i>=0; i--) {
|
||||
accum = (accum<<1) | ((*dp--) & 0x01);
|
||||
}
|
||||
return accum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint64_t BitVector::readField(size_t& readIndex, unsigned length) const
|
||||
{
|
||||
const uint64_t retVal = peekField(readIndex,length);
|
||||
readIndex += length;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
uint64_t BitVector::readFieldReversed(size_t& readIndex, unsigned length) const
|
||||
{
|
||||
const uint64_t retVal = peekFieldReversed(readIndex,length);
|
||||
readIndex += length;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void BitVector::fillField(size_t writeIndex, uint64_t value, unsigned length)
|
||||
{
|
||||
char *dpBase = mStart + writeIndex;
|
||||
char *dp = dpBase + length - 1;
|
||||
assert(dp < mEnd);
|
||||
while (dp>=dpBase) {
|
||||
*dp-- = value & 0x01;
|
||||
value >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BitVector::fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length)
|
||||
{
|
||||
char *dp = mStart + writeIndex;
|
||||
char *dpEnd = dp + length - 1;
|
||||
assert(dpEnd < mEnd);
|
||||
while (dp<=dpEnd) {
|
||||
*dp++ = value & 0x01;
|
||||
value >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void BitVector::writeField(size_t& writeIndex, uint64_t value, unsigned length)
|
||||
{
|
||||
fillField(writeIndex,value,length);
|
||||
writeIndex += length;
|
||||
}
|
||||
|
||||
|
||||
void BitVector::writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length)
|
||||
{
|
||||
fillFieldReversed(writeIndex,value,length);
|
||||
writeIndex += length;
|
||||
}
|
||||
|
||||
|
||||
void BitVector::invert()
|
||||
{
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
mStart[i] = ~mStart[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void BitVector::reverse8()
|
||||
{
|
||||
assert(size()>=8);
|
||||
|
||||
char tmp0 = mStart[0];
|
||||
mStart[0] = mStart[7];
|
||||
mStart[7] = tmp0;
|
||||
|
||||
char tmp1 = mStart[1];
|
||||
mStart[1] = mStart[6];
|
||||
mStart[6] = tmp1;
|
||||
|
||||
char tmp2 = mStart[2];
|
||||
mStart[2] = mStart[5];
|
||||
mStart[5] = tmp2;
|
||||
|
||||
char tmp3 = mStart[3];
|
||||
mStart[3] = mStart[4];
|
||||
mStart[4] = tmp3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BitVector::LSB8MSB()
|
||||
{
|
||||
if (size()<8) return;
|
||||
size_t size8 = 8*(size()/8);
|
||||
size_t iTop = size8 - 8;
|
||||
for (size_t i=0; i<=iTop; i+=8) segment(i,8).reverse8();
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint64_t BitVector::syndrome(Generator& gen) const
|
||||
{
|
||||
gen.clear();
|
||||
const char *dp = mStart;
|
||||
while (dp<mEnd) gen.syndromeShift(*dp++);
|
||||
return gen.state();
|
||||
}
|
||||
|
||||
|
||||
uint64_t BitVector::parity(Generator& gen) const
|
||||
{
|
||||
gen.clear();
|
||||
const char *dp = mStart;
|
||||
while (dp<mEnd) gen.encoderShift(*dp++);
|
||||
return gen.state();
|
||||
}
|
||||
|
||||
|
||||
void BitVector::encode(const ViterbiR2O4& coder, BitVector& target)
|
||||
{
|
||||
size_t sz = size();
|
||||
assert(sz*coder.iRate() == target.size());
|
||||
|
||||
// Build a "history" array where each element contains the full history.
|
||||
uint32_t history[sz];
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
accum = (accum<<1) | bit(i);
|
||||
history[i] = accum;
|
||||
}
|
||||
|
||||
// Look up histories in the pre-generated state table.
|
||||
char *op = target.begin();
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
unsigned index = coder.cMask() & history[i];
|
||||
for (unsigned g=0; g<coder.iRate(); g++) {
|
||||
*op++ = coder.stateTable(g,index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned BitVector::sum() const
|
||||
{
|
||||
unsigned sum = 0;
|
||||
for (size_t i=0; i<size(); i++) sum += mStart[i] & 0x01;
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void BitVector::map(const unsigned *map, size_t mapSize, BitVector& dest) const
|
||||
{
|
||||
for (unsigned i=0; i<mapSize; i++) {
|
||||
dest.mStart[i] = mStart[map[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void BitVector::unmap(const unsigned *map, size_t mapSize, BitVector& dest) const
|
||||
{
|
||||
for (unsigned i=0; i<mapSize; i++) {
|
||||
dest.mStart[map[i]] = mStart[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ostream& operator<<(ostream& os, const BitVector& hv)
|
||||
{
|
||||
for (size_t i=0; i<hv.size(); i++) {
|
||||
if (hv.bit(i)) os << '1';
|
||||
else os << '0';
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ViterbiR2O4::ViterbiR2O4()
|
||||
{
|
||||
assert(mDeferral < 32);
|
||||
mCoeffs[0] = 0x019;
|
||||
mCoeffs[1] = 0x01b;
|
||||
computeStateTables(0);
|
||||
computeStateTables(1);
|
||||
computeGeneratorTable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::initializeStates()
|
||||
{
|
||||
for (unsigned i=0; i<mIStates; i++) clear(mSurvivors[i]);
|
||||
for (unsigned i=0; i<mNumCands; i++) clear(mCandidates[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::computeStateTables(unsigned g)
|
||||
{
|
||||
assert(g<mIRate);
|
||||
for (unsigned state=0; state<mIStates; state++) {
|
||||
// 0 input
|
||||
uint32_t inputVal = state<<1;
|
||||
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
|
||||
// 1 input
|
||||
inputVal |= 1;
|
||||
mStateTable[g][inputVal] = applyPoly(inputVal, mCoeffs[g], mOrder+1);
|
||||
}
|
||||
}
|
||||
|
||||
void ViterbiR2O4::computeGeneratorTable()
|
||||
{
|
||||
for (unsigned index=0; index<mIStates*2; index++) {
|
||||
mGeneratorTable[index] = (mStateTable[0][index]<<1) | mStateTable[1][index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ViterbiR2O4::branchCandidates()
|
||||
{
|
||||
// Branch to generate new input states.
|
||||
const vCand *sp = mSurvivors;
|
||||
for (unsigned i=0; i<mNumCands; i+=2) {
|
||||
// extend and suffix
|
||||
const uint32_t iState0 = (sp->iState) << 1; // input state for 0
|
||||
const uint32_t iState1 = iState0 | 0x01; // input state for 1
|
||||
const uint32_t oStateShifted = (sp->oState) << mIRate; // shifted output
|
||||
const float cost = sp->cost;
|
||||
sp++;
|
||||
// 0 input extension
|
||||
mCandidates[i].cost = cost;
|
||||
mCandidates[i].oState = oStateShifted | mGeneratorTable[iState0 & mCMask];
|
||||
mCandidates[i].iState = iState0;
|
||||
// 1 input extension
|
||||
mCandidates[i+1].cost = cost;
|
||||
mCandidates[i+1].oState = oStateShifted | mGeneratorTable[iState1 & mCMask];
|
||||
mCandidates[i+1].iState = iState1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViterbiR2O4::getSoftCostMetrics(const uint32_t inSample, const float *matchCost, const float *mismatchCost)
|
||||
{
|
||||
const float *cTab[2] = {matchCost,mismatchCost};
|
||||
for (unsigned i=0; i<mNumCands; i++) {
|
||||
vCand& thisCand = mCandidates[i];
|
||||
// We examine input bits 2 at a time for a rate 1/2 coder.
|
||||
const unsigned mismatched = inSample ^ (thisCand.oState);
|
||||
thisCand.cost += cTab[mismatched&0x01][1] + cTab[(mismatched>>1)&0x01][0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViterbiR2O4::pruneCandidates()
|
||||
{
|
||||
const vCand* c1 = mCandidates; // 0-prefix
|
||||
const vCand* c2 = mCandidates + mIStates; // 1-prefix
|
||||
for (unsigned i=0; i<mIStates; i++) {
|
||||
if (c1[i].cost < c2[i].cost) mSurvivors[i] = c1[i];
|
||||
else mSurvivors[i] = c2[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const ViterbiR2O4::vCand& ViterbiR2O4::minCost() const
|
||||
{
|
||||
int minIndex = 0;
|
||||
float minCost = mSurvivors[0].cost;
|
||||
for (unsigned i=1; i<mIStates; i++) {
|
||||
const float thisCost = mSurvivors[i].cost;
|
||||
if (thisCost>=minCost) continue;
|
||||
minCost = thisCost;
|
||||
minIndex=i;
|
||||
}
|
||||
return mSurvivors[minIndex];
|
||||
}
|
||||
|
||||
|
||||
const ViterbiR2O4::vCand& ViterbiR2O4::step(uint32_t inSample, const float *probs, const float *iprobs)
|
||||
{
|
||||
branchCandidates();
|
||||
getSoftCostMetrics(inSample,probs,iprobs);
|
||||
pruneCandidates();
|
||||
return minCost();
|
||||
}
|
||||
|
||||
|
||||
uint64_t Parity::syndrome(const BitVector& receivedCodeword)
|
||||
{
|
||||
return receivedCodeword.syndrome(*this);
|
||||
}
|
||||
|
||||
|
||||
void Parity::writeParityWord(const BitVector& data, BitVector& parityTarget, bool invert)
|
||||
{
|
||||
uint64_t pWord = data.parity(*this);
|
||||
if (invert) pWord = ~pWord;
|
||||
parityTarget.fillField(0,pWord,size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SoftVector::SoftVector(const BitVector& source)
|
||||
{
|
||||
resize(source.size());
|
||||
for (size_t i=0; i<size(); i++) {
|
||||
if (source.bit(i)) mStart[i]=1.0F;
|
||||
else mStart[i]=0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BitVector SoftVector::sliced() const
|
||||
{
|
||||
size_t sz = size();
|
||||
BitVector newSig(sz);
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
if (mStart[i]>0.5F) newSig[i]=1;
|
||||
else newSig[i] = 0;
|
||||
}
|
||||
return newSig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoftVector::decode(ViterbiR2O4 &decoder, BitVector& target) const
|
||||
{
|
||||
const size_t sz = size();
|
||||
const unsigned deferral = decoder.deferral();
|
||||
const size_t ctsz = sz + deferral*decoder.iRate();
|
||||
assert(sz <= decoder.iRate()*target.size());
|
||||
|
||||
// Build a "history" array where each element contains the full history.
|
||||
uint32_t history[ctsz];
|
||||
{
|
||||
BitVector bits = sliced();
|
||||
uint32_t accum = 0;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
accum = (accum<<1) | bits.bit(i);
|
||||
history[i] = accum;
|
||||
}
|
||||
// Repeat last bit at the end.
|
||||
for (size_t i=sz; i<ctsz; i++) {
|
||||
accum = (accum<<1) | (accum & 0x01);
|
||||
history[i] = accum;
|
||||
}
|
||||
}
|
||||
|
||||
// Precompute metric tables.
|
||||
float matchCostTable[ctsz];
|
||||
float mismatchCostTable[ctsz];
|
||||
{
|
||||
const float *dp = mStart;
|
||||
for (size_t i=0; i<sz; i++) {
|
||||
// pVal is the probability that a bit is correct.
|
||||
// ipVal is the probability that a bit is incorrect.
|
||||
float pVal = dp[i];
|
||||
if (pVal>0.5F) pVal = 1.0F-pVal;
|
||||
float ipVal = 1.0F-pVal;
|
||||
// This is a cheap approximation to an ideal cost function.
|
||||
if (pVal<0.01F) pVal = 0.01;
|
||||
if (ipVal<0.01F) ipVal = 0.01;
|
||||
matchCostTable[i] = 0.25F/ipVal;
|
||||
mismatchCostTable[i] = 0.25F/pVal;
|
||||
}
|
||||
|
||||
// pad end of table with unknowns
|
||||
for (size_t i=sz; i<ctsz; i++) {
|
||||
matchCostTable[i] = 0.5F;
|
||||
mismatchCostTable[i] = 0.5F;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
decoder.initializeStates();
|
||||
// Each sample of history[] carries its history.
|
||||
// So we only have to process every iRate-th sample.
|
||||
const unsigned step = decoder.iRate();
|
||||
// input pointer
|
||||
const uint32_t *ip = history + step - 1;
|
||||
// output pointers
|
||||
char *op = target.begin();
|
||||
const char *const opt = target.end();
|
||||
// table pointers
|
||||
const float* match = matchCostTable;
|
||||
const float* mismatch = mismatchCostTable;
|
||||
size_t oCount = 0;
|
||||
while (op<opt) {
|
||||
// Viterbi algorithm
|
||||
assert(match-matchCostTable<sizeof(matchCostTable)/sizeof(matchCostTable[0])-1);
|
||||
assert(mismatch-mismatchCostTable<sizeof(mismatchCostTable)/sizeof(mismatchCostTable[0])-1);
|
||||
const ViterbiR2O4::vCand &minCost = decoder.step(*ip, match, mismatch);
|
||||
ip += step;
|
||||
match += step;
|
||||
mismatch += step;
|
||||
// output
|
||||
if (oCount>=deferral) *op++ = (minCost.iState >> deferral)&0x01;
|
||||
oCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ostream& operator<<(ostream& os, const SoftVector& sv)
|
||||
{
|
||||
for (size_t i=0; i<sv.size(); i++) {
|
||||
if (sv[i]<0.25) os << "0";
|
||||
else if (sv[i]>0.75) os << "1";
|
||||
else os << "-";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void BitVector::pack(unsigned char* targ) const
|
||||
{
|
||||
// Assumes MSB-first packing.
|
||||
unsigned bytes = size()/8;
|
||||
for (unsigned i=0; i<bytes; i++) {
|
||||
targ[i] = peekField(i*8,8);
|
||||
}
|
||||
unsigned whole = bytes*8;
|
||||
unsigned rem = size() - whole;
|
||||
if (rem==0) return;
|
||||
targ[bytes] = peekField(whole,rem) << (8-rem);
|
||||
}
|
||||
|
||||
|
||||
void BitVector::unpack(const unsigned char* src)
|
||||
{
|
||||
// Assumes MSB-first packing.
|
||||
unsigned bytes = size()/8;
|
||||
for (unsigned i=0; i<bytes; i++) {
|
||||
fillField(i*8,src[i],8);
|
||||
}
|
||||
unsigned whole = bytes*8;
|
||||
unsigned rem = size() - whole;
|
||||
if (rem==0) return;
|
||||
fillField(whole,src[bytes],rem);
|
||||
}
|
||||
|
||||
void BitVector::hex(ostream& os) const
|
||||
{
|
||||
os << std::hex;
|
||||
unsigned digits = size()/4;
|
||||
size_t wp=0;
|
||||
for (unsigned i=0; i<digits; i++) {
|
||||
os << readField(wp,4);
|
||||
}
|
||||
os << std::dec;
|
||||
}
|
||||
|
||||
bool BitVector::unhex(const char* src)
|
||||
{
|
||||
// Assumes MSB-first packing.
|
||||
unsigned int val;
|
||||
unsigned digits = size()/4;
|
||||
for (unsigned i=0; i<digits; i++) {
|
||||
if (sscanf(src+i, "%1x", &val) < 1) {
|
||||
return false;
|
||||
}
|
||||
fillField(i*4,val,4);
|
||||
}
|
||||
unsigned whole = digits*4;
|
||||
unsigned rem = size() - whole;
|
||||
if (rem>0) {
|
||||
if (sscanf(src+digits, "%1x", &val) < 1) {
|
||||
return false;
|
||||
}
|
||||
fillField(whole,val,rem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
441
CommonLibs/BitVector.h
Normal file
441
CommonLibs/BitVector.h
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FECVECTORS_H
|
||||
#define FECVECTORS_H
|
||||
|
||||
#include "Vector.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
class BitVector;
|
||||
class SoftVector;
|
||||
|
||||
|
||||
|
||||
/** Shift-register (LFSR) generator. */
|
||||
class Generator {
|
||||
|
||||
private:
|
||||
|
||||
uint64_t mCoeff; ///< polynomial coefficients. LSB is zero exponent.
|
||||
uint64_t mState; ///< shift register state. LSB is most recent.
|
||||
uint64_t mMask; ///< mask for reading state
|
||||
unsigned mLen; ///< number of bits used in shift register
|
||||
unsigned mLen_1; ///< mLen - 1
|
||||
|
||||
public:
|
||||
|
||||
Generator(uint64_t wCoeff, unsigned wLen)
|
||||
:mCoeff(wCoeff),mState(0),
|
||||
mMask((1ULL<<wLen)-1),
|
||||
mLen(wLen),mLen_1(wLen-1)
|
||||
{ assert(wLen<64); }
|
||||
|
||||
void clear() { mState=0; }
|
||||
|
||||
/**@name Accessors */
|
||||
//@{
|
||||
uint64_t state() const { return mState & mMask; }
|
||||
unsigned size() const { return mLen; }
|
||||
//@}
|
||||
|
||||
/**
|
||||
Calculate one bit of a syndrome.
|
||||
This is in the .h for inlining.
|
||||
*/
|
||||
void syndromeShift(unsigned inBit)
|
||||
{
|
||||
const unsigned fb = (mState>>(mLen_1)) & 0x01;
|
||||
mState = (mState<<1) ^ (inBit & 0x01);
|
||||
if (fb) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
/**
|
||||
Update the generator state by one cycle.
|
||||
This is in the .h for inlining.
|
||||
*/
|
||||
void encoderShift(unsigned inBit)
|
||||
{
|
||||
const unsigned fb = ((mState>>(mLen_1)) ^ inBit) & 0x01;
|
||||
mState <<= 1;
|
||||
if (fb) mState ^= mCoeff;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Parity (CRC-type) generator and checker based on a Generator. */
|
||||
class Parity : public Generator {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mCodewordSize;
|
||||
|
||||
public:
|
||||
|
||||
Parity(uint64_t wCoefficients, unsigned wParitySize, unsigned wCodewordSize)
|
||||
:Generator(wCoefficients, wParitySize),
|
||||
mCodewordSize(wCodewordSize)
|
||||
{ }
|
||||
|
||||
/** Compute the parity word and write it into the target segment. */
|
||||
void writeParityWord(const BitVector& data, BitVector& parityWordTarget, bool invert=true);
|
||||
|
||||
/** Compute the syndrome of a received sequence. */
|
||||
uint64_t syndrome(const BitVector& receivedCodeword);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Class to represent convolutional coders/decoders of rate 1/2, memory length 4.
|
||||
This is the "workhorse" coder for most GSM channels.
|
||||
*/
|
||||
class ViterbiR2O4 {
|
||||
|
||||
private:
|
||||
/**name Lots of precomputed elements so the compiler can optimize like hell. */
|
||||
//@{
|
||||
/**@name Core values. */
|
||||
//@{
|
||||
static const unsigned mIRate = 2; ///< reciprocal of rate
|
||||
static const unsigned mOrder = 4; ///< memory length of generators
|
||||
//@}
|
||||
/**@name Derived values. */
|
||||
//@{
|
||||
static const unsigned mIStates = 0x01 << mOrder; ///< number of states, number of survivors
|
||||
static const uint32_t mSMask = mIStates-1; ///< survivor mask
|
||||
static const uint32_t mCMask = (mSMask<<1) | 0x01; ///< candidate mask
|
||||
static const uint32_t mOMask = (0x01<<mIRate)-1; ///< ouput mask, all iRate low bits set
|
||||
static const unsigned mNumCands = mIStates*2; ///< number of candidates to generate during branching
|
||||
static const unsigned mDeferral = 6*mOrder; ///< deferral to be used
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/** Precomputed tables. */
|
||||
//@{
|
||||
uint32_t mCoeffs[mIRate]; ///< polynomial for each generator
|
||||
uint32_t mStateTable[mIRate][2*mIStates]; ///< precomputed generator output tables
|
||||
uint32_t mGeneratorTable[2*mIStates]; ///< precomputed coder output table
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
A candidate sequence in a Viterbi decoder.
|
||||
The 32-bit state register can support a deferral of 6 with a 4th-order coder.
|
||||
*/
|
||||
typedef struct candStruct {
|
||||
uint32_t iState; ///< encoder input associated with this candidate
|
||||
uint32_t oState; ///< encoder output associated with this candidate
|
||||
float cost; ///< cost (metric value), float to support soft inputs
|
||||
} vCand;
|
||||
|
||||
/** Clear a structure. */
|
||||
void clear(vCand& v)
|
||||
{
|
||||
v.iState=0;
|
||||
v.oState=0;
|
||||
v.cost=0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**@name Survivors and candidates. */
|
||||
//@{
|
||||
vCand mSurvivors[mIStates]; ///< current survivor pool
|
||||
vCand mCandidates[2*mIStates]; ///< current candidate pool
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
unsigned iRate() const { return mIRate; }
|
||||
uint32_t cMask() const { return mCMask; }
|
||||
uint32_t stateTable(unsigned g, unsigned i) const { return mStateTable[g][i]; }
|
||||
unsigned deferral() const { return mDeferral; }
|
||||
|
||||
|
||||
ViterbiR2O4();
|
||||
|
||||
/** Set all cost metrics to zero. */
|
||||
void initializeStates();
|
||||
|
||||
/**
|
||||
Full cycle of the Viterbi algorithm: branch, metrics, prune, select.
|
||||
@return reference to minimum-cost candidate.
|
||||
*/
|
||||
const vCand& step(uint32_t inSample, const float *probs, const float *iprobs);
|
||||
|
||||
private:
|
||||
|
||||
/** Branch survivors into new candidates. */
|
||||
void branchCandidates();
|
||||
|
||||
/** Compute cost metrics for soft-inputs. */
|
||||
void getSoftCostMetrics(uint32_t inSample, const float *probs, const float *iprobs);
|
||||
|
||||
/** Select survivors from the candidate set. */
|
||||
void pruneCandidates();
|
||||
|
||||
/** Find the minimum cost survivor. */
|
||||
const vCand& minCost() const;
|
||||
|
||||
/**
|
||||
Precompute the state tables.
|
||||
@param g Generator index 0..((1/rate)-1)
|
||||
*/
|
||||
void computeStateTables(unsigned g);
|
||||
|
||||
/**
|
||||
Precompute the generator outputs.
|
||||
mCoeffs must be defined first.
|
||||
*/
|
||||
void computeGeneratorTable();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class BitVector : public Vector<char> {
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**@name Constructors. */
|
||||
//@{
|
||||
|
||||
/**@name Casts of Vector constructors. */
|
||||
//@{
|
||||
BitVector(char* wData, char* wStart, char* wEnd)
|
||||
:Vector<char>(wData,wStart,wEnd)
|
||||
{ }
|
||||
BitVector(size_t len=0):Vector<char>(len) {}
|
||||
BitVector(const Vector<char>& source):Vector<char>(source) {}
|
||||
BitVector(Vector<char>& source):Vector<char>(source) {}
|
||||
BitVector(const Vector<char>& source1, const Vector<char> source2):Vector<char>(source1,source2) {}
|
||||
//@}
|
||||
|
||||
/** Construct from a string of "0" and "1". */
|
||||
BitVector(const char* valString);
|
||||
//@}
|
||||
|
||||
/** Index a single bit. */
|
||||
bool bit(size_t index) const
|
||||
{
|
||||
// We put this code in .h for fast inlining.
|
||||
const char *dp = mStart+index;
|
||||
assert(dp<mEnd);
|
||||
return (*dp) & 0x01;
|
||||
}
|
||||
|
||||
/**@name Casts and overrides of Vector operators. */
|
||||
//@{
|
||||
BitVector segment(size_t start, size_t span)
|
||||
{
|
||||
char* wStart = mStart + start;
|
||||
char* wEnd = wStart + span;
|
||||
assert(wEnd<=mEnd);
|
||||
return BitVector(NULL,wStart,wEnd);
|
||||
}
|
||||
|
||||
BitVector alias()
|
||||
{ return segment(0,size()); }
|
||||
|
||||
const BitVector segment(size_t start, size_t span) const
|
||||
{ return (BitVector)(Vector<char>::segment(start,span)); }
|
||||
|
||||
BitVector head(size_t span) { return segment(0,span); }
|
||||
const BitVector head(size_t span) const { return segment(0,span); }
|
||||
BitVector tail(size_t start) { return segment(start,size()-start); }
|
||||
const BitVector tail(size_t start) const { return segment(start,size()-start); }
|
||||
//@}
|
||||
|
||||
|
||||
void zero() { fill(0); }
|
||||
|
||||
/**@name FEC operations. */
|
||||
//@{
|
||||
/** Calculate the syndrome of the vector with the given Generator. */
|
||||
uint64_t syndrome(Generator& gen) const;
|
||||
/** Calculate the parity word for the vector with the given Generator. */
|
||||
uint64_t parity(Generator& gen) const;
|
||||
/** Encode the signal with the GSM rate 1/2 convolutional encoder. */
|
||||
void encode(const ViterbiR2O4& encoder, BitVector& target);
|
||||
//@}
|
||||
|
||||
|
||||
/** Invert 0<->1. */
|
||||
void invert();
|
||||
|
||||
/**@name Byte-wise operations. */
|
||||
//@{
|
||||
/** Reverse an 8-bit vector. */
|
||||
void reverse8();
|
||||
/** Reverse groups of 8 within the vector (byte reversal). */
|
||||
void LSB8MSB();
|
||||
//@}
|
||||
|
||||
/**@name Serialization and deserialization. */
|
||||
//@{
|
||||
uint64_t peekField(size_t readIndex, unsigned length) const;
|
||||
uint64_t peekFieldReversed(size_t readIndex, unsigned length) const;
|
||||
uint64_t readField(size_t& readIndex, unsigned length) const;
|
||||
uint64_t readFieldReversed(size_t& readIndex, unsigned length) const;
|
||||
void fillField(size_t writeIndex, uint64_t value, unsigned length);
|
||||
void fillFieldReversed(size_t writeIndex, uint64_t value, unsigned length);
|
||||
void writeField(size_t& writeIndex, uint64_t value, unsigned length);
|
||||
void writeFieldReversed(size_t& writeIndex, uint64_t value, unsigned length);
|
||||
//@}
|
||||
|
||||
/** Sum of bits. */
|
||||
unsigned sum() const;
|
||||
|
||||
/** Reorder bits, dest[i] = this[map[i]]. */
|
||||
void map(const unsigned *map, size_t mapSize, BitVector& dest) const;
|
||||
|
||||
/** Reorder bits, dest[map[i]] = this[i]. */
|
||||
void unmap(const unsigned *map, size_t mapSize, BitVector& dest) const;
|
||||
|
||||
/** Pack into a char array. */
|
||||
void pack(unsigned char*) const;
|
||||
|
||||
/** Unpack from a char array. */
|
||||
void unpack(const unsigned char*);
|
||||
|
||||
/** Make a hexdump string. */
|
||||
void hex(std::ostream&) const;
|
||||
|
||||
/** Unpack from a hexdump string.
|
||||
* @returns true on success, false on error. */
|
||||
bool unhex(const char*);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const BitVector&);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The SoftVector class is used to represent a soft-decision signal.
|
||||
Values 0..1 represent probabilities that a bit is "true".
|
||||
*/
|
||||
class SoftVector: public Vector<float> {
|
||||
|
||||
public:
|
||||
|
||||
/** Build a SoftVector of a given length. */
|
||||
SoftVector(size_t wSize=0):Vector<float>(wSize) {}
|
||||
|
||||
/** Construct a SoftVector from a C string of "0", "1", and "X". */
|
||||
SoftVector(const char* valString);
|
||||
|
||||
/** Construct a SoftVector from a BitVector. */
|
||||
SoftVector(const BitVector& source);
|
||||
|
||||
/**
|
||||
Wrap a SoftVector around a block of floats.
|
||||
The block will be delete[]ed upon desctuction.
|
||||
*/
|
||||
SoftVector(float *wData, unsigned length)
|
||||
:Vector<float>(wData,length)
|
||||
{}
|
||||
|
||||
SoftVector(float* wData, float* wStart, float* wEnd)
|
||||
:Vector<float>(wData,wStart,wEnd)
|
||||
{ }
|
||||
|
||||
/**
|
||||
Casting from a Vector<float>.
|
||||
Note that this is NOT pass-by-reference.
|
||||
*/
|
||||
SoftVector(Vector<float> source)
|
||||
:Vector<float>(source)
|
||||
{}
|
||||
|
||||
|
||||
/**@name Casts and overrides of Vector operators. */
|
||||
//@{
|
||||
SoftVector segment(size_t start, size_t span)
|
||||
{
|
||||
float* wStart = mStart + start;
|
||||
float* wEnd = wStart + span;
|
||||
assert(wEnd<=mEnd);
|
||||
return SoftVector(NULL,wStart,wEnd);
|
||||
}
|
||||
|
||||
SoftVector alias()
|
||||
{ return segment(0,size()); }
|
||||
|
||||
const SoftVector segment(size_t start, size_t span) const
|
||||
{ return (SoftVector)(Vector<float>::segment(start,span)); }
|
||||
|
||||
SoftVector head(size_t span) { return segment(0,span); }
|
||||
const SoftVector head(size_t span) const { return segment(0,span); }
|
||||
SoftVector tail(size_t start) { return segment(start,size()-start); }
|
||||
const SoftVector tail(size_t start) const { return segment(start,size()-start); }
|
||||
//@}
|
||||
|
||||
/** Decode soft symbols with the GSM rate-1/2 Viterbi decoder. */
|
||||
void decode(ViterbiR2O4 &decoder, BitVector& target) const;
|
||||
|
||||
/** Fill with "unknown" values. */
|
||||
void unknown() { fill(0.5F); }
|
||||
|
||||
/** Return a hard bit value from a given index by slicing. */
|
||||
bool bit(size_t index) const
|
||||
{
|
||||
const float *dp = mStart+index;
|
||||
assert(dp<mEnd);
|
||||
return (*dp)>0.5F;
|
||||
}
|
||||
|
||||
/** Slice the whole signal into bits. */
|
||||
BitVector sliced() const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const SoftVector&);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
88
CommonLibs/BitVectorTest.cpp
Normal file
88
CommonLibs/BitVectorTest.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "BitVector.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BitVector v1("0000111100111100101011110000");
|
||||
cout << v1 << endl;
|
||||
v1.LSB8MSB();
|
||||
cout << v1 << endl;
|
||||
ViterbiR2O4 vCoder;
|
||||
BitVector v2(v1.size()*2);
|
||||
v1.encode(vCoder,v2);
|
||||
cout << v2 << endl;
|
||||
SoftVector sv2(v2);
|
||||
cout << sv2 << endl;
|
||||
for (unsigned i=0; i<sv2.size()/4; i++) sv2[random()%sv2.size()]=0.5;
|
||||
cout << sv2 << endl;
|
||||
BitVector v3(v1.size());
|
||||
sv2.decode(vCoder,v3);
|
||||
cout << v3 << endl;
|
||||
|
||||
cout << v3.segment(3,4) << endl;
|
||||
|
||||
BitVector v4(v3.segment(0,4),v3.segment(8,4));
|
||||
cout << v4 << endl;
|
||||
|
||||
BitVector v5("000011110000");
|
||||
int r1 = v5.peekField(0,8);
|
||||
int r2 = v5.peekField(4,4);
|
||||
int r3 = v5.peekField(4,8);
|
||||
cout << r1 << ' ' << r2 << ' ' << r3 << endl;
|
||||
cout << v5 << endl;
|
||||
v5.fillField(0,0xa,4);
|
||||
int r4 = v5.peekField(0,8);
|
||||
cout << v5 << endl;
|
||||
cout << r4 << endl;
|
||||
|
||||
v5.reverse8();
|
||||
cout << v5 << endl;
|
||||
|
||||
BitVector mC = "000000000000111100000000000001110000011100001101000011000000000000000111000011110000100100001010000010100000101000001010000010100000010000000000000000000000000000000000000000000000001100001111000000000000000000000000000000000000000000000000000010010000101000001010000010100000101000001010000001000000000000000000000000110000111100000000000001110000101000001100000001000000000000";
|
||||
SoftVector mCS(mC);
|
||||
BitVector mU(mC.size()/2);
|
||||
mCS.decode(vCoder,mU);
|
||||
cout << "c=" << mCS << endl;
|
||||
cout << "u=" << mU << endl;
|
||||
|
||||
|
||||
unsigned char ts[9] = "abcdefgh";
|
||||
BitVector tp(70);
|
||||
cout << "ts=" << ts << endl;
|
||||
tp.unpack(ts);
|
||||
cout << "tp=" << tp << endl;
|
||||
tp.pack(ts);
|
||||
cout << "ts=" << ts << endl;
|
||||
}
|
||||
339
CommonLibs/Configuration.cpp
Normal file
339
CommonLibs/Configuration.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "Configuration.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
static const char* createConfigTable = {
|
||||
"CREATE TABLE IF NOT EXISTS CONFIG ("
|
||||
"KEYSTRING TEXT UNIQUE NOT NULL, "
|
||||
"VALUESTRING TEXT, "
|
||||
"STATIC INTEGER DEFAULT 0, "
|
||||
"OPTIONAL INTEGER DEFAULT 0, "
|
||||
"COMMENTS TEXT DEFAULT ''"
|
||||
")"
|
||||
};
|
||||
|
||||
|
||||
ConfigurationTable::ConfigurationTable(const char* filename)
|
||||
{
|
||||
// Connect to the database.
|
||||
int rc = sqlite3_open(filename,&mDB);
|
||||
if (rc) {
|
||||
cerr << "Cannot open configuration database: " << sqlite3_errmsg(mDB);
|
||||
sqlite3_close(mDB);
|
||||
mDB = NULL;
|
||||
return;
|
||||
}
|
||||
// Create the table, if needed.
|
||||
if (!sqlite3_command(mDB,createConfigTable)) {
|
||||
cerr << "Cannot create configuration table:" << sqlite3_errmsg(mDB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ConfigurationTable::defines(const string& key)
|
||||
{
|
||||
assert(mDB);
|
||||
ScopedLock lock(mLock);
|
||||
|
||||
// Check the cache.
|
||||
checkCacheAge();
|
||||
ConfigurationMap::const_iterator where = mCache.find(key);
|
||||
if (where!=mCache.end()) return where->second.defined();
|
||||
|
||||
// Check the database.
|
||||
char *value = NULL;
|
||||
sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",value);
|
||||
|
||||
// Cache the result.
|
||||
if (value) {
|
||||
mCache[key] = ConfigurationRecord(value);
|
||||
free(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
mCache[key] = ConfigurationRecord(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
|
||||
{
|
||||
assert(mDB);
|
||||
checkCacheAge();
|
||||
// We assume the caller holds mLock.
|
||||
// So it is OK to return a reference into the cache.
|
||||
|
||||
// Check the cache.
|
||||
// This is cheap.
|
||||
ConfigurationMap::const_iterator where = mCache.find(key);
|
||||
if (where!=mCache.end()) {
|
||||
if (where->second.defined()) return where->second;
|
||||
// Unlock the mutex before throwing the exception.
|
||||
mLock.unlock();
|
||||
syslog(LOG_ALERT, "configuration key %s not found", key.c_str());
|
||||
throw ConfigurationTableKeyNotFound(key);
|
||||
}
|
||||
|
||||
// Check the database.
|
||||
// This is more expensive.
|
||||
char *value = NULL;
|
||||
sqlite3_single_lookup(mDB,"CONFIG",
|
||||
"KEYSTRING",key.c_str(),"VALUESTRING",value);
|
||||
|
||||
// Nothing defined?
|
||||
if (!value) {
|
||||
// Cache the failure.
|
||||
mCache[key] = ConfigurationRecord(false);
|
||||
// Unlock the mutex before throwing the exception.
|
||||
mLock.unlock();
|
||||
throw ConfigurationTableKeyNotFound(key);
|
||||
}
|
||||
|
||||
// Cache the result.
|
||||
mCache[key] = ConfigurationRecord(value);
|
||||
free(value);
|
||||
|
||||
// Leave mLock locked. The caller holds it still.
|
||||
return mCache[key];
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ConfigurationTable::isStatic(const string& key) const
|
||||
{
|
||||
assert(mDB);
|
||||
unsigned stat;
|
||||
bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"STATIC",stat);
|
||||
if (success) return (bool)stat;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigurationTable::isRequired(const string& key) const
|
||||
{
|
||||
assert(mDB);
|
||||
unsigned optional;
|
||||
bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"OPTIONAL",optional);
|
||||
if (success) return !((bool)optional);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
string ConfigurationTable::getStr(const string& key)
|
||||
{
|
||||
// We need the lock because rec is a reference into the cache.
|
||||
ScopedLock lock(mLock);
|
||||
return lookup(key).value();
|
||||
}
|
||||
|
||||
string ConfigurationTable::getStr(const string& key, const char* defaultValue)
|
||||
{
|
||||
try {
|
||||
return getStr(key);
|
||||
} catch (ConfigurationTableKeyNotFound) {
|
||||
set(key,defaultValue);
|
||||
return string(defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long ConfigurationTable::getNum(const string& key)
|
||||
{
|
||||
// We need the lock because rec is a reference into the cache.
|
||||
ScopedLock lock(mLock);
|
||||
return lookup(key).number();
|
||||
}
|
||||
|
||||
|
||||
long ConfigurationTable::getNum(const string& key, long defaultValue)
|
||||
{
|
||||
try {
|
||||
return getNum(key);
|
||||
} catch (ConfigurationTableKeyNotFound) {
|
||||
set(key,defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<unsigned> ConfigurationTable::getVector(const string& key)
|
||||
{
|
||||
// Look up the string.
|
||||
mLock.lock();
|
||||
const ConfigurationRecord& rec = lookup(key);
|
||||
char* line = strdup(rec.value().c_str());
|
||||
mLock.unlock();
|
||||
// Parse the string.
|
||||
std::vector<unsigned> retVal;
|
||||
char *lp=line;
|
||||
while (lp) {
|
||||
// Watch for multiple or trailing spaces.
|
||||
while (*lp==' ') lp++;
|
||||
if (*lp=='\0') break;
|
||||
retVal.push_back(strtol(lp,NULL,0));
|
||||
strsep(&lp," ");
|
||||
}
|
||||
free(line);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
bool ConfigurationTable::unset(const string& key)
|
||||
{
|
||||
assert(mDB);
|
||||
if (!defines(key)) return true;
|
||||
if (isRequired(key)) return false;
|
||||
|
||||
ScopedLock lock(mLock);
|
||||
// Clear the cache entry and the database.
|
||||
ConfigurationMap::iterator where = mCache.find(key);
|
||||
if (where!=mCache.end()) mCache.erase(where);
|
||||
// Don't delete it; just set VALUESTRING to NULL.
|
||||
string cmd = "UPDATE CONFIG SET VALUESTRING=NULL WHERE KEYSTRING=='"+key+"'";
|
||||
return sqlite3_command(mDB,cmd.c_str());
|
||||
}
|
||||
|
||||
|
||||
void ConfigurationTable::find(const string& pat, ostream& os) const
|
||||
{
|
||||
// Prepare the statement.
|
||||
string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG WHERE KEYSTRING LIKE \"%" + pat + "%\"";
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return;
|
||||
// Read the result.
|
||||
int src = sqlite3_run_query(mDB,stmt);
|
||||
while (src==SQLITE_ROW) {
|
||||
const char* value = (const char*)sqlite3_column_text(stmt,1);
|
||||
os << sqlite3_column_text(stmt,0) << " ";
|
||||
if (value) os << value << endl;
|
||||
else os << "(null)" << endl;
|
||||
src = sqlite3_run_query(mDB,stmt);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
|
||||
bool ConfigurationTable::set(const string& key, const string& value)
|
||||
{
|
||||
assert(mDB);
|
||||
ScopedLock lock(mLock);
|
||||
// Is it there already?
|
||||
char * oldValue = NULL;
|
||||
bool exists = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",oldValue);
|
||||
// Update or insert as appropriate.
|
||||
string cmd;
|
||||
if (exists) cmd = "UPDATE CONFIG SET VALUESTRING=\""+value+"\" WHERE KEYSTRING==\""+key+"\"";
|
||||
else cmd = "INSERT INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)";
|
||||
bool success = sqlite3_command(mDB,cmd.c_str());
|
||||
// Cache the result.
|
||||
if (success) mCache[key] = ConfigurationRecord(value);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ConfigurationTable::set(const string& key, long value)
|
||||
{
|
||||
char buffer[30];
|
||||
sprintf(buffer,"%ld",value);
|
||||
return set(key,buffer);
|
||||
}
|
||||
|
||||
|
||||
bool ConfigurationTable::set(const string& key)
|
||||
{
|
||||
assert(mDB);
|
||||
ScopedLock lock(mLock);
|
||||
string cmd = "INSERT INTO CONFIG (KEYSTRING) VALUES (\"" + key + "\")";
|
||||
bool success = sqlite3_command(mDB,cmd.c_str());
|
||||
if (success) mCache[key] = ConfigurationRecord(true);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
void ConfigurationTable::checkCacheAge()
|
||||
{
|
||||
// mLock is set by caller
|
||||
static time_t timeOfLastPurge = 0;
|
||||
time_t now = time(NULL);
|
||||
// purge every 3 seconds
|
||||
// purge period cannot be configuration parameter
|
||||
if (now - timeOfLastPurge < 3) return;
|
||||
timeOfLastPurge = now;
|
||||
// this is purge() without the lock
|
||||
ConfigurationMap::iterator mp = mCache.begin();
|
||||
while (mp != mCache.end()) {
|
||||
ConfigurationMap::iterator prev = mp;
|
||||
mp++;
|
||||
mCache.erase(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConfigurationTable::purge()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
ConfigurationMap::iterator mp = mCache.begin();
|
||||
while (mp != mCache.end()) {
|
||||
ConfigurationMap::iterator prev = mp;
|
||||
mp++;
|
||||
mCache.erase(prev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConfigurationTable::setUpdateHook(void(*func)(void *,int ,char const *,char const *,sqlite3_int64))
|
||||
{
|
||||
assert(mDB);
|
||||
sqlite3_update_hook(mDB,func,NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HashString::computeHash()
|
||||
{
|
||||
// FIXME -- Someone needs to review this hash function.
|
||||
const char* cstr = c_str();
|
||||
mHash = 0;
|
||||
for (unsigned i=0; i<size(); i++) {
|
||||
mHash = mHash ^ (mHash >> 32);
|
||||
mHash = mHash*127 + cstr[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
275
CommonLibs/Configuration.h
Normal file
275
CommonLibs/Configuration.h
Normal file
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONFIGURATION_H
|
||||
#define CONFIGURATION_H
|
||||
|
||||
|
||||
#include "sqlite3util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <Threads.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/** A class for configuration file errors. */
|
||||
class ConfigurationTableError {};
|
||||
|
||||
/** An exception thrown when a given config key isn't found. */
|
||||
class ConfigurationTableKeyNotFound : public ConfigurationTableError {
|
||||
|
||||
private:
|
||||
|
||||
std::string mKey;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationTableKeyNotFound(const std::string& wKey)
|
||||
:mKey(wKey)
|
||||
{ std::cerr << "cannot find configuration value " << mKey << std::endl; }
|
||||
|
||||
const std::string& key() const { return mKey; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ConfigurationRecord {
|
||||
|
||||
private:
|
||||
|
||||
std::string mValue;
|
||||
long mNumber;
|
||||
bool mDefined;
|
||||
|
||||
public:
|
||||
|
||||
ConfigurationRecord(bool wDefined=true):
|
||||
mDefined(wDefined)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const std::string& wValue):
|
||||
mValue(wValue),
|
||||
mNumber(strtol(wValue.c_str(),NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
ConfigurationRecord(const char* wValue):
|
||||
mValue(std::string(wValue)),
|
||||
mNumber(strtol(wValue,NULL,0)),
|
||||
mDefined(true)
|
||||
{ }
|
||||
|
||||
|
||||
const std::string& value() const { return mValue; }
|
||||
long number() const { return mNumber; }
|
||||
bool defined() const { return mDefined; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** A string class that uses a hash function for comparison. */
|
||||
class HashString : public std::string {
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t mHash;
|
||||
|
||||
void computeHash();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
HashString(const char* src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString(const std::string& src)
|
||||
:std::string(src)
|
||||
{
|
||||
computeHash();
|
||||
}
|
||||
|
||||
HashString()
|
||||
{
|
||||
mHash=0;
|
||||
}
|
||||
|
||||
HashString& operator=(std::string& src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
HashString& operator=(const char* src)
|
||||
{
|
||||
std::string::operator=(src);
|
||||
computeHash();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const HashString& other)
|
||||
{
|
||||
return mHash==other.mHash;
|
||||
}
|
||||
|
||||
bool operator<(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
bool operator>(const HashString& other)
|
||||
{
|
||||
return mHash<other.mHash;
|
||||
}
|
||||
|
||||
uint64_t hash() const { return mHash; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::map<HashString, ConfigurationRecord> ConfigurationMap;
|
||||
|
||||
|
||||
/**
|
||||
A class for maintaining a configuration key-value table,
|
||||
based on sqlite3 and a local map-based cache.
|
||||
Thread-safe, too.
|
||||
*/
|
||||
class ConfigurationTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3* mDB; ///< database connection
|
||||
ConfigurationMap mCache; ///< cache of recently access configuration values
|
||||
mutable Mutex mLock; ///< control for multithreaded access to the cache
|
||||
|
||||
public:
|
||||
|
||||
|
||||
ConfigurationTable(const char* filename = ":memory:");
|
||||
|
||||
/** Return true if the key is used in the table. */
|
||||
bool defines(const std::string& key);
|
||||
|
||||
/** Return true if this key is identified as static. */
|
||||
bool isStatic(const std::string& key) const;
|
||||
|
||||
/** Return true if this key is identified as required (!optional). */
|
||||
bool isRequired(const std::string& key) const;
|
||||
|
||||
/**
|
||||
Get a string parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
std::string getStr(const std::string& key);
|
||||
|
||||
|
||||
/**
|
||||
Get a string parameter from the table.
|
||||
Define the parameter to the default value if not found.
|
||||
*/
|
||||
std::string getStr(const std::string& key, const char* defaultValue);
|
||||
|
||||
|
||||
/**
|
||||
Get a numeric parameter from the table.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
*/
|
||||
long getNum(const std::string& key);
|
||||
|
||||
/**
|
||||
Get a numeric parameter from the table.
|
||||
Define the parameter to the default value if not found.
|
||||
*/
|
||||
long getNum(const std::string& key, long defaultValue);
|
||||
|
||||
/**
|
||||
Get a numeric vector from the table.
|
||||
*/
|
||||
std::vector<unsigned> getVector(const std::string& key);
|
||||
|
||||
/** Get length of a vector */
|
||||
unsigned getVectorLength(const std::string &key)
|
||||
{ return getVector(key).size(); }
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, const std::string& value);
|
||||
|
||||
/** Set or change a value in the table. */
|
||||
bool set(const std::string& key, long value);
|
||||
|
||||
/** Create an entry in the table, no value though. */
|
||||
bool set(const std::string& key);
|
||||
|
||||
/**
|
||||
Remove a key from the table.
|
||||
Will not remove static or required values.
|
||||
@param key The key of the item to be deleted.
|
||||
@return true if anything was actually removed.
|
||||
*/
|
||||
bool unset(const std::string& key);
|
||||
|
||||
/** Search the table, dumping to a stream. */
|
||||
void find(const std::string& pattern, std::ostream&) const;
|
||||
|
||||
/** Define the callback to purge the cache whenever the database changes. */
|
||||
void setUpdateHook(void(*)(void *,int ,char const *,char const *,sqlite3_int64));
|
||||
|
||||
/** purege cache if it exceeds a certain age */
|
||||
void checkCacheAge();
|
||||
|
||||
/** Delete all records from the cache. */
|
||||
void purge();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
Attempt to lookup a record, cache if needed.
|
||||
Throw ConfigurationTableKeyNotFound if not found.
|
||||
Caller should hold mLock because the returned reference points into the cache.
|
||||
*/
|
||||
const ConfigurationRecord& lookup(const std::string& key);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
69
CommonLibs/ConfigurationTest.cpp
Normal file
69
CommonLibs/ConfigurationTest.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Configuration.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ConfigurationTable gConfig("exampleconfig.db");
|
||||
|
||||
void purgeConfig(void*,int,char const*, char const*, sqlite3_int64)
|
||||
{
|
||||
//cout << "update hook" << endl;
|
||||
gConfig.purge();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
gConfig.setUpdateHook(purgeConfig);
|
||||
|
||||
char *keys[5] = {"key1", "key2", "key3", "key4", "key5"};
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
gConfig.set(keys[i],i);
|
||||
}
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
cout << "table[" << keys[i] << "]=" << gConfig.getStr(keys[i]) << endl;
|
||||
cout << "table[" << keys[i] << "]=" << gConfig.getNum(keys[i]) << endl;
|
||||
}
|
||||
|
||||
gConfig.unset("key1");
|
||||
for (int i=0; i<5; i++) {
|
||||
cout << "defined table[" << keys[i] << "]=" << gConfig.defines(keys[i]) << endl;
|
||||
}
|
||||
|
||||
gConfig.set("key5","100 200 300 400");
|
||||
std::vector<unsigned> vect = gConfig.getVector("key5");
|
||||
cout << "vect length " << vect.size() << ": ";
|
||||
for (unsigned i=0; i<vect.size(); i++) cout << " " << vect[i];
|
||||
cout << endl;
|
||||
}
|
||||
210
CommonLibs/F16.h
Normal file
210
CommonLibs/F16.h
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef F16_H
|
||||
#define F16_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
|
||||
/** Round a float to the appropriate F16 value. */
|
||||
inline int32_t _f16_round(float f)
|
||||
{
|
||||
if (f>0.0F) return (int32_t)(f+0.5F);
|
||||
if (f<0.0F) return (int32_t)(f-0.5F);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** A class for F15.16 fixed point arithmetic with saturation. */
|
||||
class F16 {
|
||||
|
||||
|
||||
private:
|
||||
|
||||
int32_t mV;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
F16() {}
|
||||
|
||||
F16(int i) { mV = i<<16; }
|
||||
F16(float f) { mV = _f16_round(f*65536.0F); }
|
||||
F16(double f) { mV = _f16_round((float)f*65536.0F); }
|
||||
|
||||
int32_t& raw() { return mV; }
|
||||
const int32_t& raw() const { return mV; }
|
||||
|
||||
float f() const { return mV/65536.0F; }
|
||||
|
||||
//operator float() const { return mV/65536.0F; }
|
||||
//operator int() const { return mV>>16; }
|
||||
|
||||
F16 operator=(float f)
|
||||
{
|
||||
mV = _f16_round(f*65536.0F);
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator=(int i)
|
||||
{
|
||||
mV = i<<16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator=(const F16& other)
|
||||
{
|
||||
mV = other.mV;
|
||||
return mV;
|
||||
}
|
||||
|
||||
F16 operator+(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV + other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator+=(const F16& other)
|
||||
{
|
||||
mV += other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator-(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV - other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator-=(const F16& other)
|
||||
{
|
||||
mV -= other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator*(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
int64_t p = (int64_t)mV * (int64_t)other.mV;
|
||||
retVal.mV = p>>16;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator*=(const F16& other)
|
||||
{
|
||||
int64_t p = (int64_t)mV * (int64_t)other.mV;
|
||||
mV = p>>16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator*(float f) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV * f;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator*=(float f)
|
||||
{
|
||||
mV *= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator/(const F16& other) const
|
||||
{
|
||||
F16 retVal;
|
||||
int64_t pV = (int64_t)mV << 16;
|
||||
retVal.mV = pV / other.mV;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator/=(const F16& other)
|
||||
{
|
||||
int64_t pV = (int64_t)mV << 16;
|
||||
mV = pV / other.mV;
|
||||
return *this;
|
||||
}
|
||||
|
||||
F16 operator/(float f) const
|
||||
{
|
||||
F16 retVal;
|
||||
retVal.mV = mV / f;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
F16& operator/=(float f)
|
||||
{
|
||||
mV /= f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator>(const F16& other) const
|
||||
{
|
||||
return mV>other.mV;
|
||||
}
|
||||
|
||||
bool operator<(const F16& other) const
|
||||
{
|
||||
return mV<other.mV;
|
||||
}
|
||||
|
||||
bool operator==(const F16& other) const
|
||||
{
|
||||
return mV==other.mV;
|
||||
}
|
||||
|
||||
bool operator>(float f) const
|
||||
{
|
||||
return (mV/65536.0F) > f;
|
||||
}
|
||||
|
||||
bool operator<(float f) const
|
||||
{
|
||||
return (mV/65536.0F) < f;
|
||||
}
|
||||
|
||||
bool operator==(float f) const
|
||||
{
|
||||
return (mV/65536.0F) == f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const F16& v)
|
||||
{
|
||||
os << v.f();
|
||||
return os;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
55
CommonLibs/F16Test.cpp
Normal file
55
CommonLibs/F16Test.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include "F16.h"
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
F16 a = 2.5;
|
||||
F16 b = 1.5;
|
||||
F16 c = 2.5 * 1.5;
|
||||
F16 d = c + a;
|
||||
F16 e = 10;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e << endl;
|
||||
|
||||
a *= 3;
|
||||
b *= 0.3;
|
||||
c *= e;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||
|
||||
a /= 3;
|
||||
b /= 0.3;
|
||||
c = d * 0.05;
|
||||
cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
|
||||
|
||||
F16 f = a/d;
|
||||
cout << f << ' ' << f+0.5 << endl;
|
||||
}
|
||||
546
CommonLibs/Interthread.h
Normal file
546
CommonLibs/Interthread.h
Normal file
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright 2008, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef INTERTHREAD_H
|
||||
#define INTERTHREAD_H
|
||||
|
||||
#include "Timeval.h"
|
||||
#include "Threads.h"
|
||||
#include "LinkedLists.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**@defgroup Templates for interthread mechanisms. */
|
||||
//@{
|
||||
|
||||
|
||||
/** Pointer FIFO for interthread operations. */
|
||||
template <class T> class InterthreadQueue {
|
||||
|
||||
protected:
|
||||
|
||||
PointerFIFO mQ;
|
||||
mutable Mutex mLock;
|
||||
mutable Signal mWriteSignal;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** Delete contents. */
|
||||
void clear()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
while (mQ.size()>0) delete (T*)mQ.get();
|
||||
}
|
||||
|
||||
/** Empty the queue, but don't delete. */
|
||||
void flushNoDelete()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
while (mQ.size()>0) mQ.get();
|
||||
}
|
||||
|
||||
|
||||
~InterthreadQueue()
|
||||
{ clear(); }
|
||||
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mQ.size();
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read.
|
||||
@return Pointer to object (will not be NULL).
|
||||
*/
|
||||
T* read()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
T* retVal = (T*)mQ.get();
|
||||
while (retVal==NULL) {
|
||||
mWriteSignal.wait(mLock);
|
||||
retVal = (T*)mQ.get();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read with a timeout.
|
||||
@param timeout The read timeout in ms.
|
||||
@return Pointer to object or NULL on timeout.
|
||||
*/
|
||||
T* read(unsigned timeout)
|
||||
{
|
||||
if (timeout==0) return readNoBlock();
|
||||
Timeval waitTime(timeout);
|
||||
ScopedLock lock(mLock);
|
||||
while ((mQ.size()==0) && (!waitTime.passed()))
|
||||
mWriteSignal.wait(mLock,waitTime.remaining());
|
||||
T* retVal = (T*)mQ.get();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Non-blocking read.
|
||||
@return Pointer to object or NULL if FIFO is empty.
|
||||
*/
|
||||
T* readNoBlock()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return (T*)mQ.get();
|
||||
}
|
||||
|
||||
/** Non-blocking write. */
|
||||
void write(T* val)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mQ.put(val);
|
||||
mWriteSignal.signal();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Pointer FIFO for interthread operations. */
|
||||
template <class T> class InterthreadQueueWithWait {
|
||||
|
||||
protected:
|
||||
|
||||
PointerFIFO mQ;
|
||||
mutable Mutex mLock;
|
||||
mutable Signal mWriteSignal;
|
||||
mutable Signal mReadSignal;
|
||||
|
||||
virtual void freeElement(T* element) const { delete element; };
|
||||
|
||||
public:
|
||||
|
||||
/** Delete contents. */
|
||||
void clear()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
while (mQ.size()>0) freeElement((T*)mQ.get());
|
||||
mReadSignal.signal();
|
||||
}
|
||||
|
||||
|
||||
|
||||
virtual ~InterthreadQueueWithWait()
|
||||
{ clear(); }
|
||||
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mQ.size();
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read.
|
||||
@return Pointer to object (will not be NULL).
|
||||
*/
|
||||
T* read()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
T* retVal = (T*)mQ.get();
|
||||
while (retVal==NULL) {
|
||||
mWriteSignal.wait(mLock);
|
||||
retVal = (T*)mQ.get();
|
||||
}
|
||||
mReadSignal.signal();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read with a timeout.
|
||||
@param timeout The read timeout in ms.
|
||||
@return Pointer to object or NULL on timeout.
|
||||
*/
|
||||
T* read(unsigned timeout)
|
||||
{
|
||||
if (timeout==0) return readNoBlock();
|
||||
Timeval waitTime(timeout);
|
||||
ScopedLock lock(mLock);
|
||||
while ((mQ.size()==0) && (!waitTime.passed()))
|
||||
mWriteSignal.wait(mLock,waitTime.remaining());
|
||||
T* retVal = (T*)mQ.get();
|
||||
if (retVal!=NULL) mReadSignal.signal();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Non-blocking read.
|
||||
@return Pointer to object or NULL if FIFO is empty.
|
||||
*/
|
||||
T* readNoBlock()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
T* retVal = (T*)mQ.get();
|
||||
if (retVal!=NULL) mReadSignal.signal();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/** Non-blocking write. */
|
||||
void write(T* val)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mQ.put(val);
|
||||
mWriteSignal.signal();
|
||||
}
|
||||
|
||||
/** Wait until the queue falls below a low water mark. */
|
||||
void wait(size_t sz=0)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
while (mQ.size()>sz) mReadSignal.wait(mLock);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Thread-safe map of pointers to class D, keyed by class K. */
|
||||
template <class K, class D > class InterthreadMap {
|
||||
|
||||
protected:
|
||||
|
||||
typedef std::map<K,D*> Map;
|
||||
Map mMap;
|
||||
mutable Mutex mLock;
|
||||
Signal mWriteSignal;
|
||||
|
||||
public:
|
||||
|
||||
void clear()
|
||||
{
|
||||
// Delete everything in the map.
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::iterator iter = mMap.begin();
|
||||
while (iter != mMap.end()) {
|
||||
delete iter->second;
|
||||
++iter;
|
||||
}
|
||||
mMap.clear();
|
||||
}
|
||||
|
||||
~InterthreadMap() { clear(); }
|
||||
|
||||
/**
|
||||
Non-blocking write.
|
||||
@param key The index to write to.
|
||||
@param wData Pointer to data, not to be deleted until removed from the map.
|
||||
*/
|
||||
void write(const K &key, D * wData)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::iterator iter = mMap.find(key);
|
||||
if (iter!=mMap.end()) {
|
||||
delete iter->second;
|
||||
iter->second = wData;
|
||||
} else {
|
||||
mMap[key] = wData;
|
||||
}
|
||||
mWriteSignal.broadcast();
|
||||
}
|
||||
|
||||
/**
|
||||
Non-blocking read with element removal.
|
||||
@param key Key to read from.
|
||||
@return Pointer at key or NULL if key not found, to be deleted by caller.
|
||||
*/
|
||||
D* getNoBlock(const K& key)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::iterator iter = mMap.find(key);
|
||||
if (iter==mMap.end()) return NULL;
|
||||
D* retVal = iter->second;
|
||||
mMap.erase(iter);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read with a timeout and element removal.
|
||||
@param key The key to read from.
|
||||
@param timeout The blocking timeout in ms.
|
||||
@return Pointer at key or NULL on timeout, to be deleted by caller.
|
||||
*/
|
||||
D* get(const K &key, unsigned timeout)
|
||||
{
|
||||
if (timeout==0) return getNoBlock(key);
|
||||
Timeval waitTime(timeout);
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::iterator iter = mMap.find(key);
|
||||
while ((iter==mMap.end()) && (!waitTime.passed())) {
|
||||
mWriteSignal.wait(mLock,waitTime.remaining());
|
||||
iter = mMap.find(key);
|
||||
}
|
||||
if (iter==mMap.end()) return NULL;
|
||||
D* retVal = iter->second;
|
||||
mMap.erase(iter);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read with and element removal.
|
||||
@param key The key to read from.
|
||||
@return Pointer at key, to be deleted by caller.
|
||||
*/
|
||||
D* get(const K &key)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::iterator iter = mMap.find(key);
|
||||
while (iter==mMap.end()) {
|
||||
mWriteSignal.wait(mLock);
|
||||
iter = mMap.find(key);
|
||||
}
|
||||
D* retVal = iter->second;
|
||||
mMap.erase(iter);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Remove an entry and delete it.
|
||||
@param key The key of the entry to delete.
|
||||
@return True if it was actually found and deleted.
|
||||
*/
|
||||
bool remove(const K &key )
|
||||
{
|
||||
D* val = getNoBlock(key);
|
||||
if (!val) return false;
|
||||
delete val;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Non-blocking read.
|
||||
@param key Key to read from.
|
||||
@return Pointer at key or NULL if key not found.
|
||||
*/
|
||||
D* readNoBlock(const K& key) const
|
||||
{
|
||||
D* retVal=NULL;
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::const_iterator iter = mMap.find(key);
|
||||
if (iter!=mMap.end()) retVal = iter->second;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read with a timeout.
|
||||
@param key The key to read from.
|
||||
@param timeout The blocking timeout in ms.
|
||||
@return Pointer at key or NULL on timeout.
|
||||
*/
|
||||
D* read(const K &key, unsigned timeout) const
|
||||
{
|
||||
if (timeout==0) return readNoBlock(key);
|
||||
ScopedLock lock(mLock);
|
||||
Timeval waitTime(timeout);
|
||||
typename Map::const_iterator iter = mMap.find(key);
|
||||
while ((iter==mMap.end()) && (!waitTime.passed())) {
|
||||
mWriteSignal.wait(mLock,waitTime.remaining());
|
||||
iter = mMap.find(key);
|
||||
}
|
||||
if (iter==mMap.end()) return NULL;
|
||||
D* retVal = iter->second;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
Blocking read.
|
||||
@param key The key to read from.
|
||||
@return Pointer at key.
|
||||
*/
|
||||
D* read(const K &key) const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
typename Map::const_iterator iter = mMap.find(key);
|
||||
while (iter==mMap.end()) {
|
||||
mWriteSignal.wait(mLock);
|
||||
iter = mMap.find(key);
|
||||
}
|
||||
D* retVal = iter->second;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** This class is used to provide pointer-based comparison in priority_queues. */
|
||||
template <class T> class PointerCompare {
|
||||
|
||||
public:
|
||||
|
||||
/** Compare the objects pointed to, not the pointers themselves. */
|
||||
bool operator()(const T *v1, const T *v2)
|
||||
{ return (*v1)>(*v2); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Priority queue for interthread operations.
|
||||
Passes pointers to objects.
|
||||
*/
|
||||
template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > class InterthreadPriorityQueue {
|
||||
|
||||
protected:
|
||||
|
||||
std::priority_queue<T*,C,Cmp> mQ;
|
||||
mutable Mutex mLock;
|
||||
mutable Signal mWriteSignal;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/** Clear the FIFO. */
|
||||
void clear()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
while (mQ.size()>0) {
|
||||
T* ptr = mQ.top();
|
||||
mQ.pop();
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~InterthreadPriorityQueue()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mQ.size();
|
||||
}
|
||||
|
||||
|
||||
/** Non-blocking read. */
|
||||
T* readNoBlock()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
T* retVal = NULL;
|
||||
if (mQ.size()!=0) {
|
||||
retVal = mQ.top();
|
||||
mQ.pop();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/** Blocking read. */
|
||||
T* read()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
T* retVal;
|
||||
while (mQ.size()==0) mWriteSignal.wait(mLock);
|
||||
retVal = mQ.top();
|
||||
mQ.pop();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/** Non-blocking write. */
|
||||
void write(T* val)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mQ.push(val);
|
||||
mWriteSignal.signal();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Semaphore {
|
||||
|
||||
private:
|
||||
|
||||
bool mFlag;
|
||||
Signal mSignal;
|
||||
mutable Mutex mLock;
|
||||
|
||||
public:
|
||||
|
||||
Semaphore()
|
||||
:mFlag(false)
|
||||
{ }
|
||||
|
||||
void post()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mFlag=true;
|
||||
mSignal.signal();
|
||||
}
|
||||
|
||||
void get()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
while (!mFlag) mSignal.wait(mLock);
|
||||
mFlag=false;
|
||||
}
|
||||
|
||||
bool semtry()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
bool retVal = mFlag;
|
||||
mFlag = false;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
114
CommonLibs/InterthreadTest.cpp
Normal file
114
CommonLibs/InterthreadTest.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Threads.h"
|
||||
#include "Interthread.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
InterthreadQueue<int> gQ;
|
||||
InterthreadMap<int,int> gMap;
|
||||
|
||||
void* qWriter(void*)
|
||||
{
|
||||
int *p;
|
||||
for (int i=0; i<20; i++) {
|
||||
p = new int;
|
||||
*p = i;
|
||||
COUT("queue write " << *p);
|
||||
gQ.write(p);
|
||||
if (random()%2) sleep(1);
|
||||
}
|
||||
p = new int;
|
||||
*p = -1;
|
||||
gQ.write(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* qReader(void*)
|
||||
{
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
int *p = gQ.read();
|
||||
COUT("queue read " << *p);
|
||||
if (*p<0) done=true;
|
||||
delete p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void* mapWriter(void*)
|
||||
{
|
||||
int *p;
|
||||
for (int i=0; i<20; i++) {
|
||||
p = new int;
|
||||
*p = i;
|
||||
COUT("map write " << *p);
|
||||
gMap.write(i,p);
|
||||
if (random()%2) sleep(1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* mapReader(void*)
|
||||
{
|
||||
for (int i=0; i<20; i++) {
|
||||
int *p = gMap.read(i);
|
||||
COUT("map read " << *p);
|
||||
delete p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Thread qReaderThread;
|
||||
qReaderThread.start(qReader,NULL);
|
||||
Thread mapReaderThread;
|
||||
mapReaderThread.start(mapReader,NULL);
|
||||
|
||||
Thread qWriterThread;
|
||||
qWriterThread.start(qWriter,NULL);
|
||||
Thread mapWriterThread;
|
||||
mapWriterThread.start(mapWriter,NULL);
|
||||
|
||||
qReaderThread.join();
|
||||
qWriterThread.join();
|
||||
mapReaderThread.join();
|
||||
mapWriterThread.join();
|
||||
}
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
77
CommonLibs/LinkedLists.cpp
Normal file
77
CommonLibs/LinkedLists.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "LinkedLists.h"
|
||||
|
||||
|
||||
|
||||
|
||||
void PointerFIFO::put(void* val)
|
||||
{
|
||||
ListNode *node = allocate();
|
||||
node->data(val);
|
||||
node->next(NULL);
|
||||
if (mTail!=NULL) mTail->next(node);
|
||||
mTail=node;
|
||||
if (mHead==NULL) mHead=node;
|
||||
mSize++;
|
||||
}
|
||||
|
||||
/** Take an item from the FIFO. */
|
||||
void* PointerFIFO::get()
|
||||
{
|
||||
// empty list?
|
||||
if (mHead==NULL) return NULL;
|
||||
// normal case
|
||||
ListNode* next = mHead->next();
|
||||
void* retVal = mHead->data();
|
||||
release(mHead);
|
||||
mHead = next;
|
||||
if (next==NULL) mTail=NULL;
|
||||
mSize--;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ListNode *PointerFIFO::allocate()
|
||||
{
|
||||
if (mFreeList==NULL) return new ListNode;
|
||||
ListNode* retVal = mFreeList;
|
||||
mFreeList = mFreeList->next();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void PointerFIFO::release(ListNode* wNode)
|
||||
{
|
||||
wNode->next(mFreeList);
|
||||
mFreeList = wNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
100
CommonLibs/LinkedLists.h
Normal file
100
CommonLibs/LinkedLists.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef LINKEDLISTS_H
|
||||
#define LINKEDLISTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
/** This node class is used to build singly-linked lists. */
|
||||
class ListNode {
|
||||
|
||||
private:
|
||||
|
||||
ListNode* mNext;
|
||||
void* mData;
|
||||
|
||||
public:
|
||||
|
||||
ListNode* next() { return mNext; }
|
||||
void next(ListNode* wNext) { mNext=wNext; }
|
||||
|
||||
void* data() { return mData; }
|
||||
void data(void* wData) { mData=wData; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** A fast FIFO for pointer-based storage. */
|
||||
class PointerFIFO {
|
||||
|
||||
private:
|
||||
|
||||
ListNode* mHead; ///< points to next item out
|
||||
ListNode* mTail; ///< points to last item in
|
||||
ListNode* mFreeList; ///< pool of previously-allocated nodes
|
||||
unsigned mSize; ///< number of items in the FIFO
|
||||
|
||||
public:
|
||||
|
||||
PointerFIFO()
|
||||
:mHead(NULL),mTail(NULL),mFreeList(NULL),
|
||||
mSize(0)
|
||||
{}
|
||||
|
||||
unsigned size() const { return mSize; }
|
||||
|
||||
/** Put an item into the FIFO. */
|
||||
void put(void* val);
|
||||
|
||||
/**
|
||||
Take an item from the FIFO.
|
||||
Returns NULL for empty list.
|
||||
*/
|
||||
void* get();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** Allocate a new node to extend the FIFO. */
|
||||
ListNode *allocate();
|
||||
|
||||
/** Release a node to the free pool after removal from the FIFO. */
|
||||
void release(ListNode* wNode);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
70
CommonLibs/LogTest.cpp
Normal file
70
CommonLibs/LogTest.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2009 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Configuration.h"
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
//ConfigurationTable gConfig("example.config");
|
||||
|
||||
void printAlarms()
|
||||
{
|
||||
std::ostream_iterator<std::string> output( std::cout, "\n" );
|
||||
std::list<std::string> alarms = gGetLoggerAlarms();
|
||||
std::cout << "#alarms = " << alarms.size() << std::endl;
|
||||
std::copy( alarms.begin(), alarms.end(), output );
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
gLogInit("LogTest","NOTICE",LOG_LOCAL7);
|
||||
|
||||
LOG(EMERG) << " testing the logger.";
|
||||
LOG(ALERT) << " testing the logger.";
|
||||
LOG(CRIT) << " testing the logger.";
|
||||
LOG(ERR) << " testing the logger.";
|
||||
LOG(WARNING) << " testing the logger.";
|
||||
LOG(NOTICE) << " testing the logger.";
|
||||
LOG(INFO) << " testing the logger.";
|
||||
LOG(DEBUG) << " testing the logger.";
|
||||
std::cout << "\n\n\n";
|
||||
std::cout << "testing Alarms\n";
|
||||
LOG(ALERT) << " testing the logger alarm.";
|
||||
std::cout << "you should see three lines:" << std::endl;
|
||||
printAlarms();
|
||||
std::cout << "----------- generating 20 alarms ----------" << std::endl;
|
||||
for (int i = 0 ; i < 20 ; ++i) {
|
||||
LOG(ALERT) << i;
|
||||
}
|
||||
std::cout << "you should see ten lines with the numbers 10..19:" << std::endl;
|
||||
printAlarms();
|
||||
}
|
||||
|
||||
|
||||
|
||||
197
CommonLibs/Logger.cpp
Normal file
197
CommonLibs/Logger.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "Configuration.h"
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Reference to a global config table, used all over the system.
|
||||
extern ConfigurationTable gConfig;
|
||||
|
||||
|
||||
/**@ The global alarms table. */
|
||||
//@{
|
||||
Mutex alarmsLock;
|
||||
list<string> alarmsList;
|
||||
void addAlarm(const string&);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Names of the logging levels. */
|
||||
const char *levelNames[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||
};
|
||||
int numLevels = 8;
|
||||
|
||||
|
||||
/** Given a string, return the corresponding level name. */
|
||||
int lookupLevel(const string& name)
|
||||
{
|
||||
// Reverse search, since the numerically larger levels are more common.
|
||||
for (int i=numLevels-1; i>=0; i--) {
|
||||
if (name == levelNames[i]) return i;
|
||||
}
|
||||
// This should never be called with a bogus name.
|
||||
LOG(ERR) << "undefined logging level " << name << "defaulting to ERR";
|
||||
return LOG_ERR;
|
||||
}
|
||||
|
||||
|
||||
int getLoggingLevel(const char* filename)
|
||||
{
|
||||
// Default level?
|
||||
if (!filename) return lookupLevel(gConfig.getStr("Log.Level"));
|
||||
|
||||
// This can afford to be inefficient since it is not called that often.
|
||||
const string keyName = string("Log.Level.") + string(filename);
|
||||
if (gConfig.defines(keyName)) return lookupLevel(gConfig.getStr(keyName));
|
||||
return lookupLevel(gConfig.getStr("Log.Level"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
int gGetLoggingLevel(const char* filename)
|
||||
{
|
||||
// This is called a lot and needs to be efficient.
|
||||
|
||||
static Mutex sLogCacheLock;
|
||||
static map<uint64_t,int> sLogCache;
|
||||
static unsigned sCacheCount;
|
||||
static const unsigned sCacheRefreshCount = 1000;
|
||||
|
||||
if (filename==NULL) return gGetLoggingLevel("");
|
||||
|
||||
HashString hs(filename);
|
||||
uint64_t key = hs.hash();
|
||||
|
||||
sLogCacheLock.lock();
|
||||
// Time for a cache flush?
|
||||
if (sCacheCount>sCacheRefreshCount) {
|
||||
sLogCache.clear();
|
||||
sCacheCount=0;
|
||||
}
|
||||
// Is it cached already?
|
||||
map<uint64_t,int>::const_iterator where = sLogCache.find(key);
|
||||
sCacheCount++;
|
||||
if (where!=sLogCache.end()) {
|
||||
int retVal = where->second;
|
||||
sLogCacheLock.unlock();
|
||||
return retVal;
|
||||
}
|
||||
// Look it up in the config table and cache it.
|
||||
// FIXME: Figure out why unlock and lock below fix the config table deadlock.
|
||||
sLogCacheLock.unlock();
|
||||
int level = getLoggingLevel(filename);
|
||||
sLogCacheLock.lock();
|
||||
sLogCache.insert(pair<uint64_t,int>(key,level));
|
||||
sLogCacheLock.unlock();
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// copies the alarm list and returns it. list supposed to be small.
|
||||
list<string> gGetLoggerAlarms()
|
||||
{
|
||||
alarmsLock.lock();
|
||||
list<string> ret;
|
||||
// excuse the "complexity", but to use std::copy with a list you need
|
||||
// an insert_iterator - copy technically overwrites, doesn't insert.
|
||||
insert_iterator< list<string> > ii(ret, ret.begin());
|
||||
copy(alarmsList.begin(), alarmsList.end(), ii);
|
||||
alarmsLock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Add an alarm to the alarm list. */
|
||||
void addAlarm(const string& s)
|
||||
{
|
||||
alarmsLock.lock();
|
||||
alarmsList.push_back(s);
|
||||
unsigned maxAlarms = gConfig.getNum("Log.Alarms.Max");
|
||||
while (alarmsList.size() > maxAlarms) alarmsList.pop_front();
|
||||
alarmsLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
// Anything at or above LOG_CRIT is an "alarm".
|
||||
// Save alarms in the local list and echo them to stderr.
|
||||
if (mPriority <= LOG_CRIT) {
|
||||
addAlarm(mStream.str().c_str());
|
||||
cerr << mStream.str() << endl;
|
||||
}
|
||||
// Current logging level was already checked by the macro.
|
||||
// So just log.
|
||||
syslog(mPriority, "%s", mStream.str().c_str());
|
||||
}
|
||||
|
||||
|
||||
ostringstream& Log::get()
|
||||
{
|
||||
assert(mPriority<numLevels);
|
||||
mStream << levelNames[mPriority] << ' ';
|
||||
return mStream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void gLogInit(const char* name, const char* level, int facility)
|
||||
{
|
||||
// Set the level.
|
||||
if (level) {
|
||||
gConfig.set("Log.Level",level);
|
||||
} else {
|
||||
if (!gConfig.defines("Log.Level")) {
|
||||
gConfig.set("Log.Level","WARNING");
|
||||
}
|
||||
}
|
||||
|
||||
// Define other logging parameters in the global config.
|
||||
if (!gConfig.defines("Log.Alarms.Max")) {
|
||||
gConfig.set("Log.Alarms.Max",DEFAULT_MAX_ALARMS);
|
||||
}
|
||||
|
||||
// Open the log connection.
|
||||
openlog(name,0,facility);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
99
CommonLibs/Logger.h
Normal file
99
CommonLibs/Logger.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <syslog.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "Threads.h"
|
||||
|
||||
|
||||
#define _LOG(level) \
|
||||
Log(LOG_##level).get() << pthread_self() \
|
||||
<< " " __FILE__ ":" << __LINE__ << ":" << __FUNCTION__ << ": "
|
||||
#define LOG(wLevel) \
|
||||
if (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel)
|
||||
#define OBJLOG(wLevel) \
|
||||
if (gGetLoggingLevel(__FILE__)>=LOG_##wLevel) _LOG(wLevel) << "obj: " << this << ' '
|
||||
|
||||
#define LOG_ASSERT(x) { if (!(x)) LOG(EMERG) << "assertion " #x " failed"; } assert(x);
|
||||
|
||||
|
||||
#define DEFAULT_MAX_ALARMS 10
|
||||
|
||||
|
||||
/**
|
||||
A C++ stream-based thread-safe logger.
|
||||
Derived from Dr. Dobb's Sept. 2007 issue.
|
||||
Updated to use syslog.
|
||||
This object is NOT the global logger;
|
||||
every log record is an object of this class.
|
||||
*/
|
||||
class Log {
|
||||
|
||||
public:
|
||||
|
||||
protected:
|
||||
|
||||
std::ostringstream mStream; ///< This is where we buffer up the log entry.
|
||||
int mPriority; ///< Priority of current repot.
|
||||
|
||||
public:
|
||||
|
||||
Log(int wPriority)
|
||||
:mPriority(wPriority)
|
||||
{ }
|
||||
|
||||
// Most of the work is in the desctructor.
|
||||
/** The destructor actually generates the log entry. */
|
||||
~Log();
|
||||
|
||||
std::ostringstream& get();
|
||||
};
|
||||
|
||||
|
||||
|
||||
std::list<std::string> gGetLoggerAlarms(); ///< Get a copy of the recent alarm list.
|
||||
|
||||
|
||||
/**@ Global control and initialization of the logging system. */
|
||||
//@{
|
||||
/** Initialize the global logging system. */
|
||||
void gLogInit(const char* name, const char* level=NULL, int facility=LOG_USER);
|
||||
/** Get the logging level associated with a given file. */
|
||||
int gGetLoggingLevel(const char *filename=NULL);
|
||||
//@}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
93
CommonLibs/Makefile.am
Normal file
93
CommonLibs/Makefile.am
Normal file
@@ -0,0 +1,93 @@
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -ldl -O3 -g -lpthread
|
||||
|
||||
noinst_LTLIBRARIES = libcommon.la
|
||||
|
||||
libcommon_la_SOURCES = \
|
||||
BitVector.cpp \
|
||||
LinkedLists.cpp \
|
||||
Sockets.cpp \
|
||||
Threads.cpp \
|
||||
Timeval.cpp \
|
||||
Logger.cpp \
|
||||
URLEncode.cpp \
|
||||
Configuration.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
BitVectorTest \
|
||||
InterthreadTest \
|
||||
SocketsTest \
|
||||
TimevalTest \
|
||||
RegexpTest \
|
||||
VectorTest \
|
||||
ConfigurationTest \
|
||||
LogTest \
|
||||
F16Test
|
||||
|
||||
noinst_HEADERS = \
|
||||
BitVector.h \
|
||||
Interthread.h \
|
||||
LinkedLists.h \
|
||||
Sockets.h \
|
||||
Threads.h \
|
||||
Timeval.h \
|
||||
Regexp.h \
|
||||
Vector.h \
|
||||
URLEncode.h \
|
||||
Configuration.h \
|
||||
F16.h \
|
||||
Logger.h
|
||||
|
||||
BitVectorTest_SOURCES = BitVectorTest.cpp
|
||||
BitVectorTest_LDADD = libcommon.la
|
||||
|
||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||
InterthreadTest_LDADD = libcommon.la
|
||||
InterthreadTest_LDFLAGS = -lpthread
|
||||
|
||||
SocketsTest_SOURCES = SocketsTest.cpp
|
||||
SocketsTest_LDADD = libcommon.la
|
||||
SocketsTest_LDFLAGS = -lpthread
|
||||
|
||||
TimevalTest_SOURCES = TimevalTest.cpp
|
||||
TimevalTest_LDADD = libcommon.la
|
||||
|
||||
VectorTest_SOURCES = VectorTest.cpp
|
||||
VectorTest_LDADD = libcommon.la
|
||||
|
||||
RegexpTest_SOURCES = RegexpTest.cpp
|
||||
RegexpTest_LDADD = libcommon.la
|
||||
|
||||
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
LogTest_SOURCES = LogTest.cpp
|
||||
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
|
||||
F16Test_SOURCES = F16Test.cpp
|
||||
|
||||
MOSTLYCLEANFILES += testSource testDestination
|
||||
|
||||
|
||||
658
CommonLibs/Makefile.in
Normal file
658
CommonLibs/Makefile.in
Normal file
@@ -0,0 +1,658 @@
|
||||
# Makefile.in generated by automake 1.9.4 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004 Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
# Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
|
||||
SOURCES = $(libcommon_la_SOURCES) $(BitVectorTest_SOURCES) $(ConfigurationTest_SOURCES) $(F16Test_SOURCES) $(InterthreadTest_SOURCES) $(LogTest_SOURCES) $(RegexpTest_SOURCES) $(SocketsTest_SOURCES) $(TimevalTest_SOURCES) $(VectorTest_SOURCES)
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = ..
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(top_srcdir)/Makefile.common
|
||||
noinst_PROGRAMS = BitVectorTest$(EXEEXT) InterthreadTest$(EXEEXT) \
|
||||
SocketsTest$(EXEEXT) TimevalTest$(EXEEXT) RegexpTest$(EXEEXT) \
|
||||
VectorTest$(EXEEXT) ConfigurationTest$(EXEEXT) \
|
||||
LogTest$(EXEEXT) F16Test$(EXEEXT)
|
||||
subdir = CommonLibs
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||
libcommon_la_LIBADD =
|
||||
am_libcommon_la_OBJECTS = BitVector.lo LinkedLists.lo Sockets.lo \
|
||||
Threads.lo Timeval.lo Logger.lo URLEncode.lo Configuration.lo
|
||||
libcommon_la_OBJECTS = $(am_libcommon_la_OBJECTS)
|
||||
PROGRAMS = $(noinst_PROGRAMS)
|
||||
am_BitVectorTest_OBJECTS = BitVectorTest.$(OBJEXT)
|
||||
BitVectorTest_OBJECTS = $(am_BitVectorTest_OBJECTS)
|
||||
BitVectorTest_DEPENDENCIES = libcommon.la
|
||||
am_ConfigurationTest_OBJECTS = ConfigurationTest.$(OBJEXT)
|
||||
ConfigurationTest_OBJECTS = $(am_ConfigurationTest_OBJECTS)
|
||||
am__DEPENDENCIES_1 = $(top_builddir)/sqlite3/libsqlite.la
|
||||
ConfigurationTest_DEPENDENCIES = libcommon.la $(am__DEPENDENCIES_1)
|
||||
am_F16Test_OBJECTS = F16Test.$(OBJEXT)
|
||||
F16Test_OBJECTS = $(am_F16Test_OBJECTS)
|
||||
F16Test_LDADD = $(LDADD)
|
||||
am_InterthreadTest_OBJECTS = InterthreadTest.$(OBJEXT)
|
||||
InterthreadTest_OBJECTS = $(am_InterthreadTest_OBJECTS)
|
||||
InterthreadTest_DEPENDENCIES = libcommon.la
|
||||
am_LogTest_OBJECTS = LogTest.$(OBJEXT)
|
||||
LogTest_OBJECTS = $(am_LogTest_OBJECTS)
|
||||
LogTest_DEPENDENCIES = libcommon.la $(am__DEPENDENCIES_1)
|
||||
am_RegexpTest_OBJECTS = RegexpTest.$(OBJEXT)
|
||||
RegexpTest_OBJECTS = $(am_RegexpTest_OBJECTS)
|
||||
RegexpTest_DEPENDENCIES = libcommon.la
|
||||
am_SocketsTest_OBJECTS = SocketsTest.$(OBJEXT)
|
||||
SocketsTest_OBJECTS = $(am_SocketsTest_OBJECTS)
|
||||
SocketsTest_DEPENDENCIES = libcommon.la
|
||||
am_TimevalTest_OBJECTS = TimevalTest.$(OBJEXT)
|
||||
TimevalTest_OBJECTS = $(am_TimevalTest_OBJECTS)
|
||||
TimevalTest_DEPENDENCIES = libcommon.la
|
||||
am_VectorTest_OBJECTS = VectorTest.$(OBJEXT)
|
||||
VectorTest_OBJECTS = $(am_VectorTest_OBJECTS)
|
||||
VectorTest_DEPENDENCIES = libcommon.la
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
CXXLD = $(CXX)
|
||||
CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
|
||||
$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(libcommon_la_SOURCES) $(BitVectorTest_SOURCES) \
|
||||
$(ConfigurationTest_SOURCES) $(F16Test_SOURCES) \
|
||||
$(InterthreadTest_SOURCES) $(LogTest_SOURCES) \
|
||||
$(RegexpTest_SOURCES) $(SocketsTest_SOURCES) \
|
||||
$(TimevalTest_SOURCES) $(VectorTest_SOURCES)
|
||||
DIST_SOURCES = $(libcommon_la_SOURCES) $(BitVectorTest_SOURCES) \
|
||||
$(ConfigurationTest_SOURCES) $(F16Test_SOURCES) \
|
||||
$(InterthreadTest_SOURCES) $(LogTest_SOURCES) \
|
||||
$(RegexpTest_SOURCES) $(SocketsTest_SOURCES) \
|
||||
$(TimevalTest_SOURCES) $(VectorTest_SOURCES)
|
||||
HEADERS = $(noinst_HEADERS)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
ECHO = @ECHO@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
F77 = @F77@
|
||||
FFLAGS = @FFLAGS@
|
||||
GREP = @GREP@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIBUSB_CFLAGS = @LIBUSB_CFLAGS@
|
||||
LIBUSB_LIBS = @LIBUSB_LIBS@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
ORTP_CFLAGS = @ORTP_CFLAGS@
|
||||
ORTP_LIBS = @ORTP_LIBS@
|
||||
OSIP_CFLAGS = @OSIP_CFLAGS@
|
||||
OSIP_LIBS = @OSIP_LIBS@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
RANLIB = @RANLIB@
|
||||
RM_PROG = @RM_PROG@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_F77 = @ac_ct_F77@
|
||||
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
|
||||
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
CONTROL_INCLUDEDIR = $(top_srcdir)/Control
|
||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||
SIP_INCLUDEDIR = $(top_srcdir)/SIP
|
||||
SMS_INCLUDEDIR = $(top_srcdir)/SMS
|
||||
TRX_INCLUDEDIR = $(top_srcdir)/TRXManager
|
||||
GLOBALS_INCLUDEDIR = $(top_srcdir)/Globals
|
||||
CLI_INCLUDEDIR = $(top_srcdir)/CLI
|
||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||
SR_INCLUDEDIR = $(top_srcdir)/SR
|
||||
STD_DEFINES_AND_INCLUDES = \
|
||||
-I$(COMMON_INCLUDEDIR) \
|
||||
-I$(CONTROL_INCLUDEDIR) \
|
||||
-I$(GSM_INCLUDEDIR) \
|
||||
-I$(SIP_INCLUDEDIR) \
|
||||
-I$(SMS_INCLUDEDIR) \
|
||||
-I$(TRX_INCLUDEDIR) \
|
||||
-I$(GLOBALS_INCLUDEDIR) \
|
||||
-I$(CLI_INCLUDEDIR) \
|
||||
-I$(SR_INCLUDEDIR) \
|
||||
-I$(SQLITE_INCLUDEDIR)
|
||||
|
||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||
SIP_LA = $(top_builddir)/SIP/libSIP.la
|
||||
SMS_LA = $(top_builddir)/SMS/libSMS.la
|
||||
TRX_LA = $(top_builddir)/TRXManager/libtrxmanager.la
|
||||
CONTROL_LA = $(top_builddir)/Control/libcontrol.la
|
||||
GLOBALS_LA = $(top_builddir)/Globals/libglobals.la
|
||||
CLI_LA = $(top_builddir)/CLI/libcli.la
|
||||
SR_LA = $(top_builddir)/SR/libSR.la
|
||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la
|
||||
MOSTLYCLEANFILES = *~ testSource testDestination
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -ldl -O3 -g -lpthread
|
||||
noinst_LTLIBRARIES = libcommon.la
|
||||
libcommon_la_SOURCES = \
|
||||
BitVector.cpp \
|
||||
LinkedLists.cpp \
|
||||
Sockets.cpp \
|
||||
Threads.cpp \
|
||||
Timeval.cpp \
|
||||
Logger.cpp \
|
||||
URLEncode.cpp \
|
||||
Configuration.cpp
|
||||
|
||||
noinst_HEADERS = \
|
||||
BitVector.h \
|
||||
Interthread.h \
|
||||
LinkedLists.h \
|
||||
Sockets.h \
|
||||
Threads.h \
|
||||
Timeval.h \
|
||||
Regexp.h \
|
||||
Vector.h \
|
||||
URLEncode.h \
|
||||
Configuration.h \
|
||||
F16.h \
|
||||
Logger.h
|
||||
|
||||
BitVectorTest_SOURCES = BitVectorTest.cpp
|
||||
BitVectorTest_LDADD = libcommon.la
|
||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||
InterthreadTest_LDADD = libcommon.la
|
||||
InterthreadTest_LDFLAGS = -lpthread
|
||||
SocketsTest_SOURCES = SocketsTest.cpp
|
||||
SocketsTest_LDADD = libcommon.la
|
||||
SocketsTest_LDFLAGS = -lpthread
|
||||
TimevalTest_SOURCES = TimevalTest.cpp
|
||||
TimevalTest_LDADD = libcommon.la
|
||||
VectorTest_SOURCES = VectorTest.cpp
|
||||
VectorTest_LDADD = libcommon.la
|
||||
RegexpTest_SOURCES = RegexpTest.cpp
|
||||
RegexpTest_LDADD = libcommon.la
|
||||
ConfigurationTest_SOURCES = ConfigurationTest.cpp
|
||||
ConfigurationTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
LogTest_SOURCES = LogTest.cpp
|
||||
LogTest_LDADD = libcommon.la $(SQLITE_LA)
|
||||
F16Test_SOURCES = F16Test.cpp
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .cpp .lo .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Makefile.common $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
|
||||
&& exit 0; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu CommonLibs/Makefile'; \
|
||||
cd $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu CommonLibs/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
clean-noinstLTLIBRARIES:
|
||||
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
|
||||
@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
|
||||
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
|
||||
test "$$dir" != "$$p" || dir=.; \
|
||||
echo "rm -f \"$${dir}/so_locations\""; \
|
||||
rm -f "$${dir}/so_locations"; \
|
||||
done
|
||||
libcommon.la: $(libcommon_la_OBJECTS) $(libcommon_la_DEPENDENCIES)
|
||||
$(CXXLINK) $(libcommon_la_LDFLAGS) $(libcommon_la_OBJECTS) $(libcommon_la_LIBADD) $(LIBS)
|
||||
|
||||
clean-noinstPROGRAMS:
|
||||
@list='$(noinst_PROGRAMS)'; for p in $$list; do \
|
||||
f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
|
||||
echo " rm -f $$p $$f"; \
|
||||
rm -f $$p $$f ; \
|
||||
done
|
||||
BitVectorTest$(EXEEXT): $(BitVectorTest_OBJECTS) $(BitVectorTest_DEPENDENCIES)
|
||||
@rm -f BitVectorTest$(EXEEXT)
|
||||
$(CXXLINK) $(BitVectorTest_LDFLAGS) $(BitVectorTest_OBJECTS) $(BitVectorTest_LDADD) $(LIBS)
|
||||
ConfigurationTest$(EXEEXT): $(ConfigurationTest_OBJECTS) $(ConfigurationTest_DEPENDENCIES)
|
||||
@rm -f ConfigurationTest$(EXEEXT)
|
||||
$(CXXLINK) $(ConfigurationTest_LDFLAGS) $(ConfigurationTest_OBJECTS) $(ConfigurationTest_LDADD) $(LIBS)
|
||||
F16Test$(EXEEXT): $(F16Test_OBJECTS) $(F16Test_DEPENDENCIES)
|
||||
@rm -f F16Test$(EXEEXT)
|
||||
$(CXXLINK) $(F16Test_LDFLAGS) $(F16Test_OBJECTS) $(F16Test_LDADD) $(LIBS)
|
||||
InterthreadTest$(EXEEXT): $(InterthreadTest_OBJECTS) $(InterthreadTest_DEPENDENCIES)
|
||||
@rm -f InterthreadTest$(EXEEXT)
|
||||
$(CXXLINK) $(InterthreadTest_LDFLAGS) $(InterthreadTest_OBJECTS) $(InterthreadTest_LDADD) $(LIBS)
|
||||
LogTest$(EXEEXT): $(LogTest_OBJECTS) $(LogTest_DEPENDENCIES)
|
||||
@rm -f LogTest$(EXEEXT)
|
||||
$(CXXLINK) $(LogTest_LDFLAGS) $(LogTest_OBJECTS) $(LogTest_LDADD) $(LIBS)
|
||||
RegexpTest$(EXEEXT): $(RegexpTest_OBJECTS) $(RegexpTest_DEPENDENCIES)
|
||||
@rm -f RegexpTest$(EXEEXT)
|
||||
$(CXXLINK) $(RegexpTest_LDFLAGS) $(RegexpTest_OBJECTS) $(RegexpTest_LDADD) $(LIBS)
|
||||
SocketsTest$(EXEEXT): $(SocketsTest_OBJECTS) $(SocketsTest_DEPENDENCIES)
|
||||
@rm -f SocketsTest$(EXEEXT)
|
||||
$(CXXLINK) $(SocketsTest_LDFLAGS) $(SocketsTest_OBJECTS) $(SocketsTest_LDADD) $(LIBS)
|
||||
TimevalTest$(EXEEXT): $(TimevalTest_OBJECTS) $(TimevalTest_DEPENDENCIES)
|
||||
@rm -f TimevalTest$(EXEEXT)
|
||||
$(CXXLINK) $(TimevalTest_LDFLAGS) $(TimevalTest_OBJECTS) $(TimevalTest_LDADD) $(LIBS)
|
||||
VectorTest$(EXEEXT): $(VectorTest_OBJECTS) $(VectorTest_DEPENDENCIES)
|
||||
@rm -f VectorTest$(EXEEXT)
|
||||
$(CXXLINK) $(VectorTest_LDFLAGS) $(VectorTest_OBJECTS) $(VectorTest_LDADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitVector.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BitVectorTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Configuration.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ConfigurationTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/F16Test.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InterthreadTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LinkedLists.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LogTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Logger.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RegexpTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Sockets.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SocketsTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Threads.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Timeval.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TimevalTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/URLEncode.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/VectorTest.Po@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.cpp.lo:
|
||||
@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
distclean-libtool:
|
||||
-rm -f libtool
|
||||
uninstall-info-am:
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
mkid -fID $$unique
|
||||
tags: TAGS
|
||||
|
||||
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
tags=; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$tags $$unique; \
|
||||
fi
|
||||
ctags: CTAGS
|
||||
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
tags=; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
test -z "$(CTAGS_ARGS)$$tags$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$tags $$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& cd $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) $$here
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
$(mkdir_p) $(distdir)/..
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
|
||||
else \
|
||||
test -f $(distdir)/$$file \
|
||||
|| cp -p $$d/$$file $(distdir)/$$file \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
`test -z '$(STRIP)' || \
|
||||
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
|
||||
mostlyclean-generic:
|
||||
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
|
||||
clean-noinstPROGRAMS mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-libtool distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-info-am
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
|
||||
ctags distclean distclean-compile distclean-generic \
|
||||
distclean-libtool distclean-tags distdir dvi dvi-am html \
|
||||
html-am info info-am install install-am install-data \
|
||||
install-data-am install-exec install-exec-am install-info \
|
||||
install-info-am install-man install-strip installcheck \
|
||||
installcheck-am installdirs maintainer-clean \
|
||||
maintainer-clean-generic mostlyclean mostlyclean-compile \
|
||||
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
|
||||
tags uninstall uninstall-am uninstall-info-am
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
64
CommonLibs/Regexp.h
Normal file
64
CommonLibs/Regexp.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REGEXPW_H
|
||||
#define REGEXPW_H
|
||||
|
||||
#include <regex.h>
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
class Regexp {
|
||||
|
||||
private:
|
||||
|
||||
regex_t mRegex;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
Regexp(const char* regexp, int flags=REG_EXTENDED)
|
||||
{
|
||||
int result = regcomp(&mRegex, regexp, flags);
|
||||
if (result) {
|
||||
char msg[256];
|
||||
regerror(result,&mRegex,msg,255);
|
||||
std::cerr << "Regexp compilation of " << regexp << " failed: " << msg << std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
~Regexp()
|
||||
{ regfree(&mRegex); }
|
||||
|
||||
bool match(const char *text, int flags=0) const
|
||||
{ return regexec(&mRegex, text, 0, NULL, flags)==0; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
48
CommonLibs/RegexpTest.cpp
Normal file
48
CommonLibs/RegexpTest.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Regexp.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
Regexp email("^[[:graph:]]+@[[:graph:]]+ ");
|
||||
Regexp simple("^dburgess@");
|
||||
|
||||
const char text1[] = "dburgess@jcis.net test message";
|
||||
const char text2[] = "no address text message";
|
||||
|
||||
cout << email.match(text1) << " " << text1 << endl;
|
||||
cout << email.match(text2) << " " << text2 << endl;
|
||||
|
||||
cout << simple.match(text1) << " " << text1 << endl;
|
||||
cout << simple.match(text2) << " " << text2 << endl;
|
||||
}
|
||||
302
CommonLibs/Sockets.cpp
Normal file
302
CommonLibs/Sockets.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdio>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "Threads.h"
|
||||
#include "Sockets.h"
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort)
|
||||
{
|
||||
assert(address);
|
||||
assert(hostAndPort);
|
||||
char *copy = strdup(hostAndPort);
|
||||
char *colon = strchr(copy,':');
|
||||
if (!colon) return false;
|
||||
*colon = '\0';
|
||||
char *host = copy;
|
||||
unsigned port = strtol(colon+1,NULL,10);
|
||||
bool retVal = resolveAddress(address,host,port);
|
||||
free(copy);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port)
|
||||
{
|
||||
assert(address);
|
||||
assert(host);
|
||||
// FIXME -- Need to ignore leading/trailing spaces in hostname.
|
||||
struct hostent *hp = gethostbyname(host);
|
||||
if (hp==NULL) {
|
||||
CERR("WARNING -- gethostbyname() failed for " << host << ", " << hstrerror(h_errno));
|
||||
return false;
|
||||
}
|
||||
address->sin_family = AF_INET;
|
||||
bcopy(hp->h_addr, &(address->sin_addr), hp->h_length);
|
||||
address->sin_port = htons(port);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DatagramSocket::DatagramSocket()
|
||||
{
|
||||
bzero(mDestination,sizeof(mDestination));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void DatagramSocket::nonblocking()
|
||||
{
|
||||
fcntl(mSocketFD,F_SETFL,O_NONBLOCK);
|
||||
}
|
||||
|
||||
void DatagramSocket::blocking()
|
||||
{
|
||||
fcntl(mSocketFD,F_SETFL,0);
|
||||
}
|
||||
|
||||
void DatagramSocket::close()
|
||||
{
|
||||
::close(mSocketFD);
|
||||
}
|
||||
|
||||
|
||||
DatagramSocket::~DatagramSocket()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int DatagramSocket::write( const char * message, size_t length )
|
||||
{
|
||||
assert(length<=MAX_UDP_LENGTH);
|
||||
int retVal = sendto(mSocketFD, message, length, 0,
|
||||
(struct sockaddr *)mDestination, addressSize());
|
||||
if (retVal == -1 ) perror("DatagramSocket::write() failed");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int DatagramSocket::writeBack( const char * message, size_t length )
|
||||
{
|
||||
assert(length<=MAX_UDP_LENGTH);
|
||||
int retVal = sendto(mSocketFD, message, length, 0,
|
||||
(struct sockaddr *)mSource, addressSize());
|
||||
if (retVal == -1 ) perror("DatagramSocket::write() failed");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int DatagramSocket::write( const char * message)
|
||||
{
|
||||
size_t length=strlen(message)+1;
|
||||
return write(message,length);
|
||||
}
|
||||
|
||||
int DatagramSocket::writeBack( const char * message)
|
||||
{
|
||||
size_t length=strlen(message)+1;
|
||||
return writeBack(message,length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int DatagramSocket::send(const struct sockaddr* dest, const char * message, size_t length )
|
||||
{
|
||||
assert(length<=MAX_UDP_LENGTH);
|
||||
int retVal = sendto(mSocketFD, message, length, 0, dest, addressSize());
|
||||
if (retVal == -1 ) perror("DatagramSocket::send() failed");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int DatagramSocket::send(const struct sockaddr* dest, const char * message)
|
||||
{
|
||||
size_t length=strlen(message)+1;
|
||||
return send(dest,message,length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int DatagramSocket::read(char* buffer)
|
||||
{
|
||||
socklen_t temp_len = sizeof(mSource);
|
||||
int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
|
||||
(struct sockaddr*)&mSource,&temp_len);
|
||||
if ((length==-1) && (errno!=EAGAIN)) {
|
||||
perror("DatagramSocket::read() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
int DatagramSocket::read(char* buffer, unsigned timeout)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_SET(mSocketFD,&fds);
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout/1000;
|
||||
tv.tv_usec = (timeout%1000)*1000;
|
||||
int sel = select(mSocketFD+1,&fds,NULL,NULL,&tv);
|
||||
if (sel<0) {
|
||||
perror("DatagramSocket::read() select() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
if (sel==0) return -1;
|
||||
return read(buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort)
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
}
|
||||
|
||||
|
||||
UDPSocket::UDPSocket(unsigned short wSrcPort,
|
||||
const char * wDestIP, unsigned short wDestPort )
|
||||
:DatagramSocket()
|
||||
{
|
||||
open(wSrcPort);
|
||||
destination(wDestPort, wDestIP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDPSocket::destination( unsigned short wDestPort, const char * wDestIP )
|
||||
{
|
||||
resolveAddress((sockaddr_in*)mDestination, wDestIP, wDestPort );
|
||||
}
|
||||
|
||||
|
||||
void UDPSocket::open(unsigned short localPort)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_INET,SOCK_DGRAM,0);
|
||||
if (mSocketFD<0) {
|
||||
perror("socket() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
|
||||
// bind
|
||||
struct sockaddr_in address;
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(localPort);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned short UDPSocket::port() const
|
||||
{
|
||||
struct sockaddr_in name;
|
||||
socklen_t nameSize = sizeof(name);
|
||||
int retVal = getsockname(mSocketFD, (struct sockaddr*)&name, &nameSize);
|
||||
if (retVal==-1) throw SocketError();
|
||||
return ntohs(name.sin_port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
UDDSocket::UDDSocket(const char* localPath, const char* remotePath)
|
||||
:DatagramSocket()
|
||||
{
|
||||
if (localPath!=NULL) open(localPath);
|
||||
if (remotePath!=NULL) destination(remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::open(const char* localPath)
|
||||
{
|
||||
// create
|
||||
mSocketFD = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (mSocketFD<0) {
|
||||
perror("socket() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
|
||||
// bind
|
||||
struct sockaddr_un address;
|
||||
size_t length = sizeof(address);
|
||||
bzero(&address,length);
|
||||
address.sun_family = AF_UNIX;
|
||||
strcpy(address.sun_path,localPath);
|
||||
unlink(localPath);
|
||||
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
|
||||
perror("bind() failed");
|
||||
throw SocketError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UDDSocket::destination(const char* remotePath)
|
||||
{
|
||||
struct sockaddr_un* unAddr = (struct sockaddr_un*)mDestination;
|
||||
strcpy(unAddr->sun_path,remotePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim:ts=4:sw=4
|
||||
193
CommonLibs/Sockets.h
Normal file
193
CommonLibs/Sockets.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SOCKETS_H
|
||||
#define SOCKETS_H
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <list>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MAX_UDP_LENGTH 1500
|
||||
|
||||
/** A function to resolve IP host names. */
|
||||
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port);
|
||||
|
||||
/** Resolve an address of the form "<host>:<port>". */
|
||||
bool resolveAddress(struct sockaddr_in *address, const char *hostAndPort);
|
||||
|
||||
/** An exception to throw when a critical socket operation fails. */
|
||||
class SocketError {};
|
||||
#define SOCKET_ERROR {throw SocketError(); }
|
||||
|
||||
/** Abstract class for connectionless sockets. */
|
||||
class DatagramSocket {
|
||||
|
||||
protected:
|
||||
|
||||
int mSocketFD; ///< underlying file descriptor
|
||||
char mDestination[256]; ///< address to which packets are sent
|
||||
char mSource[256]; ///< return address of most recent received packet
|
||||
|
||||
public:
|
||||
|
||||
/** An almost-does-nothing constructor. */
|
||||
DatagramSocket();
|
||||
|
||||
virtual ~DatagramSocket();
|
||||
|
||||
/** Return the address structure size for this socket type. */
|
||||
virtual size_t addressSize() const = 0;
|
||||
|
||||
/**
|
||||
Send a binary packet.
|
||||
@param buffer The data bytes to send to mDestination.
|
||||
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
|
||||
@return number of bytes written, or -1 on error.
|
||||
*/
|
||||
int write( const char * buffer, size_t length);
|
||||
|
||||
/**
|
||||
Send a C-style string packet.
|
||||
@param buffer The data bytes to send to mDestination.
|
||||
@return number of bytes written, or -1 on error.
|
||||
*/
|
||||
int write( const char * buffer);
|
||||
|
||||
/**
|
||||
Send a binary packet.
|
||||
@param buffer The data bytes to send to mSource.
|
||||
@param length Number of bytes to send, or strlen(buffer) if defaulted to -1.
|
||||
@return number of bytes written, or -1 on error.
|
||||
*/
|
||||
int writeBack(const char * buffer, size_t length);
|
||||
|
||||
/**
|
||||
Send a C-style string packet.
|
||||
@param buffer The data bytes to send to mSource.
|
||||
@return number of bytes written, or -1 on error.
|
||||
*/
|
||||
int writeBack(const char * buffer);
|
||||
|
||||
|
||||
/**
|
||||
Receive a packet.
|
||||
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
||||
@return The number of bytes received or -1 on non-blocking pass.
|
||||
*/
|
||||
int read(char* buffer);
|
||||
|
||||
/**
|
||||
Receive a packet with a timeout.
|
||||
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
|
||||
@param maximum wait time in milliseconds
|
||||
@return The number of bytes received or -1 on timeout.
|
||||
*/
|
||||
int read(char* buffer, unsigned timeout);
|
||||
|
||||
|
||||
/** Send a packet to a given destination, other than the default. */
|
||||
int send(const struct sockaddr *dest, const char * buffer, size_t length);
|
||||
|
||||
/** Send a C-style string to a given destination, other than the default. */
|
||||
int send(const struct sockaddr *dest, const char * buffer);
|
||||
|
||||
/** Make the socket non-blocking. */
|
||||
void nonblocking();
|
||||
|
||||
/** Make the socket blocking (the default). */
|
||||
void blocking();
|
||||
|
||||
/** Close the socket. */
|
||||
void close();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** UDP/IP User Datagram Socket */
|
||||
class UDPSocket : public DatagramSocket {
|
||||
|
||||
public:
|
||||
|
||||
/** Open a USP socket with an OS-assigned port and no default destination. */
|
||||
UDPSocket( unsigned short localPort=0);
|
||||
|
||||
/** Given a full specification, open the socket and set the dest address. */
|
||||
UDPSocket( unsigned short localPort,
|
||||
const char * remoteIP, unsigned short remotePort);
|
||||
|
||||
/** Set the destination port. */
|
||||
void destination( unsigned short wDestPort, const char * wDestIP );
|
||||
|
||||
/** Return the actual port number in use. */
|
||||
unsigned short port() const;
|
||||
|
||||
/** Open and bind the UDP socket to a local port. */
|
||||
void open(unsigned short localPort=0);
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_in* source() const { return (const struct sockaddr_in*)mSource; }
|
||||
|
||||
size_t addressSize() const { return sizeof(struct sockaddr_in); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** Unix Domain Datagram Socket */
|
||||
class UDDSocket : public DatagramSocket {
|
||||
|
||||
public:
|
||||
|
||||
UDDSocket(const char* localPath=NULL, const char* remotePath=NULL);
|
||||
|
||||
void destination(const char* remotePath);
|
||||
|
||||
void open(const char* localPath);
|
||||
|
||||
/** Give the return address of the most recently received packet. */
|
||||
const struct sockaddr_un* source() const { return (const struct sockaddr_un*)mSource; }
|
||||
|
||||
size_t addressSize() const { return sizeof(struct sockaddr_un); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// vim:ts=4:sw=4
|
||||
103
CommonLibs/SocketsTest.cpp
Normal file
103
CommonLibs/SocketsTest.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Sockets.h"
|
||||
#include "Threads.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
static const int gNumToSend = 10;
|
||||
|
||||
|
||||
void *testReaderIP(void *)
|
||||
{
|
||||
UDPSocket readSocket(5934, "localhost", 5061);
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf);
|
||||
if (count>0) {
|
||||
COUT("read: " << buf);
|
||||
rc++;
|
||||
} else {
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *testReaderUnix(void *)
|
||||
{
|
||||
UDDSocket readSocket("testDestination");
|
||||
readSocket.nonblocking();
|
||||
int rc = 0;
|
||||
while (rc<gNumToSend) {
|
||||
char buf[MAX_UDP_LENGTH];
|
||||
int count = readSocket.read(buf);
|
||||
if (count>0) {
|
||||
COUT("read: " << buf);
|
||||
rc++;
|
||||
} else {
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * argv[] )
|
||||
{
|
||||
|
||||
Thread readerThreadIP;
|
||||
readerThreadIP.start(testReaderIP,NULL);
|
||||
Thread readerThreadUnix;
|
||||
readerThreadUnix.start(testReaderUnix,NULL);
|
||||
|
||||
UDPSocket socket1(5061, "127.0.0.1",5934);
|
||||
UDDSocket socket1U("testSource","testDestination");
|
||||
|
||||
COUT("socket1: " << socket1.port());
|
||||
|
||||
// give the readers time to open
|
||||
sleep(1);
|
||||
|
||||
for (int i=0; i<gNumToSend; i++) {
|
||||
socket1.write("Hello IP land");
|
||||
socket1U.write("Hello Unix domain");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
readerThreadIP.join();
|
||||
readerThreadUnix.join();
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
120
CommonLibs/Threads.cpp
Normal file
120
CommonLibs/Threads.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Threads.h"
|
||||
#include "Timeval.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
|
||||
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
|
||||
|
||||
void lockCout()
|
||||
{
|
||||
gStreamLock.lock();
|
||||
Timeval entryTime;
|
||||
cout << entryTime << " " << pthread_self() << ": ";
|
||||
}
|
||||
|
||||
|
||||
void unlockCout()
|
||||
{
|
||||
cout << dec << endl << flush;
|
||||
gStreamLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
void lockCerr()
|
||||
{
|
||||
gStreamLock.lock();
|
||||
Timeval entryTime;
|
||||
cerr << entryTime << " " << pthread_self() << ": ";
|
||||
}
|
||||
|
||||
void unlockCerr()
|
||||
{
|
||||
cerr << dec << endl << flush;
|
||||
gStreamLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
bool res;
|
||||
res = pthread_mutexattr_init(&mAttribs);
|
||||
assert(!res);
|
||||
res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE);
|
||||
assert(!res);
|
||||
res = pthread_mutex_init(&mMutex,&mAttribs);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
pthread_mutex_destroy(&mMutex);
|
||||
bool res = pthread_mutexattr_destroy(&mAttribs);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Block for the signal up to the cancellation timeout. */
|
||||
void Signal::wait(Mutex& wMutex, unsigned timeout) const
|
||||
{
|
||||
Timeval then(timeout);
|
||||
struct timespec waitTime = then.timespec();
|
||||
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
||||
}
|
||||
|
||||
|
||||
void Thread::start(void *(*task)(void*), void *arg)
|
||||
{
|
||||
assert(mThread==((pthread_t)0));
|
||||
bool res;
|
||||
res = pthread_attr_init(&mAttrib);
|
||||
assert(!res);
|
||||
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
|
||||
assert(!res);
|
||||
res = pthread_create(&mThread, &mAttrib, task, arg);
|
||||
assert(!res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
176
CommonLibs/Threads.h
Normal file
176
CommonLibs/Threads.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright 2008, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef THREADS_H
|
||||
#define THREADS_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
|
||||
class Mutex;
|
||||
|
||||
|
||||
/**@name Multithreaded access for standard streams. */
|
||||
//@{
|
||||
|
||||
/**@name Functions for gStreamLock. */
|
||||
//@{
|
||||
extern Mutex gStreamLock; ///< global lock for cout and cerr
|
||||
void lockCerr(); ///< call prior to writing cerr
|
||||
void unlockCerr(); ///< call after writing cerr
|
||||
void lockCout(); ///< call prior to writing cout
|
||||
void unlockCout(); ///< call after writing cout
|
||||
//@}
|
||||
|
||||
/**@name Macros for standard messages. */
|
||||
//@{
|
||||
#define COUT(text) { lockCout(); std::cout << text; unlockCout(); }
|
||||
#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); }
|
||||
#ifdef NDEBUG
|
||||
#define DCOUT(text) {}
|
||||
#define OBJDCOUT(text) {}
|
||||
#else
|
||||
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
|
||||
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
|
||||
#endif
|
||||
//@}
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/**@defgroup C++ wrappers for pthread mechanisms. */
|
||||
//@{
|
||||
|
||||
/** A class for recursive mutexes based on pthread_mutex. */
|
||||
class Mutex {
|
||||
|
||||
private:
|
||||
|
||||
pthread_mutex_t mMutex;
|
||||
pthread_mutexattr_t mAttribs;
|
||||
|
||||
public:
|
||||
|
||||
Mutex();
|
||||
|
||||
~Mutex();
|
||||
|
||||
void lock() { pthread_mutex_lock(&mMutex); }
|
||||
|
||||
void unlock() { pthread_mutex_unlock(&mMutex); }
|
||||
|
||||
friend class Signal;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ScopedLock {
|
||||
|
||||
private:
|
||||
Mutex& mMutex;
|
||||
|
||||
public:
|
||||
ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
|
||||
~ScopedLock() { mMutex.unlock(); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** A C++ interthread signal based on pthread condition variables. */
|
||||
class Signal {
|
||||
|
||||
private:
|
||||
|
||||
mutable pthread_cond_t mSignal;
|
||||
|
||||
public:
|
||||
|
||||
Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
|
||||
|
||||
~Signal() { pthread_cond_destroy(&mSignal); }
|
||||
|
||||
/**
|
||||
Block for the signal up to the cancellation timeout.
|
||||
Under Linux, spurious returns are possible.
|
||||
*/
|
||||
void wait(Mutex& wMutex, unsigned timeout) const;
|
||||
|
||||
/**
|
||||
Block for the signal.
|
||||
Under Linux, spurious returns are possible.
|
||||
*/
|
||||
void wait(Mutex& wMutex) const
|
||||
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
|
||||
|
||||
void signal() { pthread_cond_signal(&mSignal); }
|
||||
|
||||
void broadcast() { pthread_cond_broadcast(&mSignal); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define START_THREAD(thread,function,argument) \
|
||||
thread.start((void *(*)(void*))function, (void*)argument);
|
||||
|
||||
/** A C++ wrapper for pthread threads. */
|
||||
class Thread {
|
||||
|
||||
private:
|
||||
|
||||
pthread_t mThread;
|
||||
pthread_attr_t mAttrib;
|
||||
// FIXME -- Can this be reduced now?
|
||||
size_t mStackSize;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** Create a thread in a non-running state. */
|
||||
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) { mStackSize=wStackSize;}
|
||||
|
||||
/**
|
||||
Destroy the Thread.
|
||||
It should be stopped and joined.
|
||||
*/
|
||||
~Thread() { pthread_attr_destroy(&mAttrib); }
|
||||
|
||||
|
||||
/** Start the thread on a task. */
|
||||
void start(void *(*task)(void*), void *arg);
|
||||
|
||||
/** Join a thread that will stop on its own. */
|
||||
void join() { int s = pthread_join(mThread,NULL); assert(!s); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
98
CommonLibs/Timeval.cpp
Normal file
98
CommonLibs/Timeval.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Timeval.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Timeval::future(unsigned offset)
|
||||
{
|
||||
now();
|
||||
unsigned sec = offset/1000;
|
||||
unsigned msec = offset%1000;
|
||||
mTimeval.tv_usec += msec*1000;
|
||||
mTimeval.tv_sec += sec;
|
||||
if (mTimeval.tv_usec>1000000) {
|
||||
mTimeval.tv_usec -= 1000000;
|
||||
mTimeval.tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct timespec Timeval::timespec() const
|
||||
{
|
||||
struct timespec retVal;
|
||||
retVal.tv_sec = mTimeval.tv_sec;
|
||||
retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
bool Timeval::passed() const
|
||||
{
|
||||
Timeval nowTime;
|
||||
if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
|
||||
if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
|
||||
if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
double Timeval::seconds() const
|
||||
{
|
||||
return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long Timeval::delta(const Timeval& other) const
|
||||
{
|
||||
// 2^31 milliseconds is just over 4 years.
|
||||
long deltaS = other.sec() - sec();
|
||||
long deltaUs = other.usec() - usec();
|
||||
return 1000*deltaS + deltaUs/1000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ostream& operator<<(ostream& os, const Timeval& tv)
|
||||
{
|
||||
os.setf( ios::fixed, ios::floatfield );
|
||||
os << tv.seconds();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
ostream& operator<<(ostream& os, const struct timespec& ts)
|
||||
{
|
||||
os << ts.tv_sec << "," << ts.tv_nsec;
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
104
CommonLibs/Timeval.h
Normal file
104
CommonLibs/Timeval.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TIMEVAL_H
|
||||
#define TIMEVAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sys/time.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
|
||||
/** A wrapper on usleep to sleep for milliseconds. */
|
||||
inline void msleep(long v) { usleep(v*1000); }
|
||||
|
||||
|
||||
/** A C++ wrapper for struct timeval. */
|
||||
class Timeval {
|
||||
|
||||
private:
|
||||
|
||||
struct timeval mTimeval;
|
||||
|
||||
public:
|
||||
|
||||
/** Set the value to gettimeofday. */
|
||||
void now() { gettimeofday(&mTimeval,NULL); }
|
||||
|
||||
/** Set the value to gettimeofday plus an offset. */
|
||||
void future(unsigned ms);
|
||||
|
||||
//@{
|
||||
Timeval(unsigned sec, unsigned usec)
|
||||
{
|
||||
mTimeval.tv_sec = sec;
|
||||
mTimeval.tv_usec = usec;
|
||||
}
|
||||
|
||||
Timeval(const struct timeval& wTimeval)
|
||||
:mTimeval(wTimeval)
|
||||
{}
|
||||
|
||||
/**
|
||||
Create a Timeval offset into the future.
|
||||
@param offset milliseconds
|
||||
*/
|
||||
Timeval(unsigned offset=0) { future(offset); }
|
||||
//@}
|
||||
|
||||
/** Convert to a struct timespec. */
|
||||
struct timespec timespec() const;
|
||||
|
||||
/** Return total seconds. */
|
||||
double seconds() const;
|
||||
|
||||
uint32_t sec() const { return mTimeval.tv_sec; }
|
||||
uint32_t usec() const { return mTimeval.tv_usec; }
|
||||
|
||||
/** Return differnce from other (other-self), in ms. */
|
||||
long delta(const Timeval& other) const;
|
||||
|
||||
/** Elapsed time in ms. */
|
||||
long elapsed() const { return delta(Timeval()); }
|
||||
|
||||
/** Remaining time in ms. */
|
||||
long remaining() const { return -elapsed(); }
|
||||
|
||||
/** Return true if the time has passed, as per gettimeofday. */
|
||||
bool passed() const;
|
||||
|
||||
/** Add a given number of minutes to the time. */
|
||||
void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; }
|
||||
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Timeval&);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const struct timespec&);
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
45
CommonLibs/TimevalTest.cpp
Normal file
45
CommonLibs/TimevalTest.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "Timeval.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
Timeval then(10000);
|
||||
cout << then.elapsed() << endl;
|
||||
|
||||
while (!then.passed()) {
|
||||
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
||||
usleep(500000);
|
||||
}
|
||||
cout << "now: " << Timeval() << " then: " << then << " remaining: " << then.remaining() << endl;
|
||||
}
|
||||
51
CommonLibs/URLEncode.cpp
Normal file
51
CommonLibs/URLEncode.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <URLEncode.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//based on javascript encodeURIComponent()
|
||||
string URLEncode(const string &c)
|
||||
{
|
||||
static const char *digits = "01234567890ABCDEF";
|
||||
string retVal="";
|
||||
for (int i=0; i<c.length(); i++)
|
||||
{
|
||||
const char ch = c[i];
|
||||
if (isalnum(ch) || strchr("-_.!~'()",ch)) {
|
||||
retVal += ch;
|
||||
} else {
|
||||
retVal += '%';
|
||||
retVal += digits[(ch>>4) & 0x0f];
|
||||
retVal += digits[ch & 0x0f];
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
30
CommonLibs/URLEncode.h
Normal file
30
CommonLibs/URLEncode.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
# include <string>
|
||||
|
||||
std::string URLEncode(const std::string&);
|
||||
268
CommonLibs/Vector.h
Normal file
268
CommonLibs/Vector.h
Normal file
@@ -0,0 +1,268 @@
|
||||
/**@file Simplified Vector template with aliases. */
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef VECTOR_H
|
||||
#define VECTOR_H
|
||||
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
/**
|
||||
A simplified Vector template with aliases.
|
||||
Unlike std::vector, this class does not support dynamic resizing.
|
||||
Unlike std::vector, this class does support "aliases" and subvectors.
|
||||
*/
|
||||
template <class T> class Vector {
|
||||
|
||||
// TODO -- Replace memcpy calls with for-loops.
|
||||
|
||||
public:
|
||||
|
||||
/**@name Iterator types. */
|
||||
//@{
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
//@}
|
||||
|
||||
protected:
|
||||
|
||||
T* mData; ///< allocated data block, if any
|
||||
T* mStart; ///< start of useful data
|
||||
T* mEnd; ///< end of useful data + 1
|
||||
|
||||
public:
|
||||
|
||||
/** Return the size of the Vector. */
|
||||
size_t size() const
|
||||
{
|
||||
assert(mStart>=mData);
|
||||
assert(mEnd>=mStart);
|
||||
return mEnd - mStart;
|
||||
}
|
||||
|
||||
/** Return size in bytes. */
|
||||
size_t bytes() const { return size()*sizeof(T); }
|
||||
|
||||
/** Change the size of the Vector, discarding content. */
|
||||
void resize(size_t newSize)
|
||||
{
|
||||
if (mData!=NULL) delete[] mData;
|
||||
if (newSize==0) mData=NULL;
|
||||
else mData = new T[newSize];
|
||||
mStart = mData;
|
||||
mEnd = mStart + newSize;
|
||||
}
|
||||
|
||||
/** Release memory and clear pointers. */
|
||||
void clear() { resize(0); }
|
||||
|
||||
|
||||
/** Copy data from another vector. */
|
||||
void clone(const Vector<T>& other)
|
||||
{
|
||||
resize(other.size());
|
||||
memcpy(mData,other.mStart,other.bytes());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//@{
|
||||
|
||||
/** Build an empty Vector of a given size. */
|
||||
Vector(size_t wSize=0):mData(NULL) { resize(wSize); }
|
||||
|
||||
/** Build a Vector by shifting the data block. */
|
||||
Vector(Vector<T>& other)
|
||||
:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)
|
||||
{ other.mData=NULL; }
|
||||
|
||||
/** Build a Vector by copying another. */
|
||||
Vector(const Vector<T>& other):mData(NULL) { clone(other); }
|
||||
|
||||
/** Build a Vector with explicit values. */
|
||||
Vector(T* wData, T* wStart, T* wEnd)
|
||||
:mData(wData),mStart(wStart),mEnd(wEnd)
|
||||
{ }
|
||||
|
||||
/** Build a vector from an existing block, NOT to be deleted upon destruction. */
|
||||
Vector(T* wStart, size_t span)
|
||||
:mData(NULL),mStart(wStart),mEnd(wStart+span)
|
||||
{ }
|
||||
|
||||
/** Build a Vector by concatenation. */
|
||||
Vector(const Vector<T>& other1, const Vector<T>& other2)
|
||||
:mData(NULL)
|
||||
{
|
||||
resize(other1.size()+other2.size());
|
||||
memcpy(mStart, other1.mStart, other1.bytes());
|
||||
memcpy(mStart+other1.size(), other2.mStart, other2.bytes());
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/** Destroy a Vector, deleting held memory. */
|
||||
~Vector() { clear(); }
|
||||
|
||||
|
||||
|
||||
|
||||
//@{
|
||||
|
||||
/** Assign from another Vector, shifting ownership. */
|
||||
void operator=(Vector<T>& other)
|
||||
{
|
||||
clear();
|
||||
mData=other.mData;
|
||||
mStart=other.mStart;
|
||||
mEnd=other.mEnd;
|
||||
other.mData=NULL;
|
||||
}
|
||||
|
||||
/** Assign from another Vector, copying. */
|
||||
void operator=(const Vector<T>& other) { clone(other); }
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
//@{
|
||||
|
||||
/** Return an alias to a segment of this Vector. */
|
||||
Vector<T> segment(size_t start, size_t span)
|
||||
{
|
||||
T* wStart = mStart + start;
|
||||
T* wEnd = wStart + span;
|
||||
assert(wEnd<=mEnd);
|
||||
return Vector<T>(NULL,wStart,wEnd);
|
||||
}
|
||||
|
||||
/** Return an alias to a segment of this Vector. */
|
||||
const Vector<T> segment(size_t start, size_t span) const
|
||||
{
|
||||
T* wStart = mStart + start;
|
||||
T* wEnd = wStart + span;
|
||||
assert(wEnd<=mEnd);
|
||||
return Vector<T>(NULL,wStart,wEnd);
|
||||
}
|
||||
|
||||
Vector<T> head(size_t span) { return segment(0,span); }
|
||||
const Vector<T> head(size_t span) const { return segment(0,span); }
|
||||
Vector<T> tail(size_t start) { return segment(start,size()-start); }
|
||||
const Vector<T> tail(size_t start) const { return segment(start,size()-start); }
|
||||
|
||||
/**
|
||||
Copy part of this Vector to a segment of another Vector.
|
||||
@param other The other vector.
|
||||
@param start The start point in the other vector.
|
||||
@param span The number of elements to copy.
|
||||
*/
|
||||
void copyToSegment(Vector<T>& other, size_t start, size_t span) const
|
||||
{
|
||||
T* base = other.mStart + start;
|
||||
assert(base+span<=other.mEnd);
|
||||
assert(mStart+span<=mEnd);
|
||||
memcpy(base,mStart,span*sizeof(T));
|
||||
}
|
||||
|
||||
/** Copy all of this Vector to a segment of another Vector. */
|
||||
void copyToSegment(Vector<T>& other, size_t start=0) const { copyToSegment(other,start,size()); }
|
||||
|
||||
void copyTo(Vector<T>& other) const { copyToSegment(other,0,size()); }
|
||||
|
||||
/**
|
||||
Copy a segment of this vector into another.
|
||||
@param other The other vector (to copt into starting at 0.)
|
||||
@param start The start point in this vector.
|
||||
@param span The number of elements to copy.
|
||||
*/
|
||||
void segmentCopyTo(Vector<T>& other, size_t start, size_t span) const
|
||||
{
|
||||
const T* base = mStart + start;
|
||||
assert(base+span<=mEnd);
|
||||
assert(other.mStart+span<=other.mEnd);
|
||||
memcpy(other.mStart,base,span*sizeof(T));
|
||||
}
|
||||
|
||||
void fill(const T& val)
|
||||
{
|
||||
T* dp=mStart;
|
||||
while (dp<mEnd) *dp++=val;
|
||||
}
|
||||
|
||||
void fill(const T& val, unsigned start, unsigned length)
|
||||
{
|
||||
T* dp=mStart+start;
|
||||
T* end=dp+length;
|
||||
assert(end<=mEnd);
|
||||
while (dp<end) *dp++=val;
|
||||
}
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
//@{
|
||||
|
||||
T& operator[](size_t index)
|
||||
{
|
||||
assert(mStart+index<mEnd);
|
||||
return mStart[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const
|
||||
{
|
||||
assert(mStart+index<mEnd);
|
||||
return mStart[index];
|
||||
}
|
||||
|
||||
const T* begin() const { return mStart; }
|
||||
T* begin() { return mStart; }
|
||||
const T* end() const { return mEnd; }
|
||||
T* end() { return mEnd; }
|
||||
//@}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Basic print operator for Vector objects. */
|
||||
template <class T>
|
||||
std::ostream& operator<<(std::ostream& os, const Vector<T>& v)
|
||||
{
|
||||
for (unsigned i=0; i<v.size(); i++) os << v[i] << " ";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
63
CommonLibs/VectorTest.cpp
Normal file
63
CommonLibs/VectorTest.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "Vector.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef Vector<int> TestVector;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TestVector test1(5);
|
||||
for (int i=0; i<5; i++) test1[i]=i;
|
||||
TestVector test2(5);
|
||||
for (int i=0; i<5; i++) test2[i]=10+i;
|
||||
|
||||
cout << test1 << endl;
|
||||
cout << test2 << endl;
|
||||
|
||||
{
|
||||
TestVector testC(test1,test2);
|
||||
cout << testC << endl;
|
||||
cout << testC.head(3) << endl;
|
||||
cout << testC.tail(3) << endl;
|
||||
testC.fill(8);
|
||||
cout << testC << endl;
|
||||
test1.copyToSegment(testC,3);
|
||||
cout << testC << endl;
|
||||
|
||||
TestVector testD(testC.segment(4,3));
|
||||
cout << testD << endl;
|
||||
testD.fill(9);
|
||||
cout << testC << endl;
|
||||
cout << testD << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
1054
Control/CallControl.cpp
Normal file
1054
Control/CallControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
67
Control/CallControl.h
Normal file
67
Control/CallControl.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**@file GSM/SIP Call Control -- GSM 04.08, ISDN ITU-T Q.931, SIP IETF RFC-3261, RTP IETF RFC-3550. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef CALLCONTROL_H
|
||||
#define CALLCONTROL_H
|
||||
|
||||
|
||||
namespace GSM {
|
||||
class LogicalChannel;
|
||||
class TCHFACCHLogicalChannel;
|
||||
class L3CMServiceRequest;
|
||||
};
|
||||
|
||||
namespace Control {
|
||||
|
||||
class TransactionEntry;
|
||||
|
||||
|
||||
|
||||
/**@name MOC */
|
||||
//@{
|
||||
/** Run the MOC to the point of alerting, doing early assignment if needed. */
|
||||
void MOCStarter(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
|
||||
/** Complete the MOC connection. */
|
||||
void MOCController(TransactionEntry*, GSM::TCHFACCHLogicalChannel*);
|
||||
/** Set up an emergency call, assuming very early assignment. */
|
||||
void EmergencyCall(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
|
||||
//@}
|
||||
|
||||
|
||||
/**@name MTC */
|
||||
//@{
|
||||
/** Run the MTC to the point of alerting, doing early assignment if needed. */
|
||||
void MTCStarter(TransactionEntry*, GSM::LogicalChannel*);
|
||||
/** Complete the MTC connection. */
|
||||
void MTCController(TransactionEntry*, GSM::TCHFACCHLogicalChannel*);
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Test Call */
|
||||
//@{
|
||||
/** Run the test call. */
|
||||
void TestCall(TransactionEntry*, GSM::LogicalChannel*);
|
||||
//@}
|
||||
|
||||
/** Create a new transaction entry and start paging. */
|
||||
void initiateMTTransaction(TransactionEntry* transaction,
|
||||
GSM::ChannelType chanType, unsigned pageTime);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
180
Control/ControlCommon.cpp
Normal file
180
Control/ControlCommon.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
/**@file Common-use functions for the control layer. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "ControlCommon.h"
|
||||
#include "TransactionTable.h"
|
||||
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSML3Message.h>
|
||||
#include <GSML3CCMessages.h>
|
||||
#include <GSML3RRMessages.h>
|
||||
#include <GSML3MMMessages.h>
|
||||
#include <GSMConfig.h>
|
||||
|
||||
#include <SIPEngine.h>
|
||||
#include <SIPInterface.h>
|
||||
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
using namespace Control;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME -- getMessage should return an L3Frame, not an L3Message.
|
||||
// This will mean moving all of the parsing into the control layer.
|
||||
// FIXME -- This needs an adjustable timeout.
|
||||
|
||||
L3Message* Control::getMessage(LogicalChannel *LCH, unsigned SAPI)
|
||||
{
|
||||
unsigned timeout_ms = LCH->N200() * T200ms;
|
||||
L3Frame *rcv = LCH->recv(timeout_ms,SAPI);
|
||||
if (rcv==NULL) {
|
||||
LOG(NOTICE) << "timeout";
|
||||
throw ChannelReadTimeout();
|
||||
}
|
||||
LOG(DEBUG) << "received " << *rcv;
|
||||
Primitive primitive = rcv->primitive();
|
||||
if (primitive!=DATA) {
|
||||
LOG(NOTICE) << "unexpected primitive " << primitive;
|
||||
delete rcv;
|
||||
throw UnexpectedPrimitive();
|
||||
}
|
||||
L3Message *msg = parseL3(*rcv);
|
||||
delete rcv;
|
||||
if (msg==NULL) {
|
||||
LOG(NOTICE) << "unparsed message";
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Resolve a mobile ID to an IMSI and return TMSI if it is assigned. */
|
||||
unsigned Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobileID, LogicalChannel* LCH)
|
||||
{
|
||||
// Returns known or assigned TMSI.
|
||||
assert(LCH);
|
||||
LOG(DEBUG) << "resolving mobile ID " << mobileID << ", sameLAI: " << sameLAI;
|
||||
|
||||
// IMSI already? See if there's a TMSI already, too.
|
||||
if (mobileID.type()==IMSIType) return gTMSITable.TMSI(mobileID.digits());
|
||||
|
||||
// IMEI? WTF?!
|
||||
// FIXME -- Should send MM Reject, cause 0x60, "invalid mandatory information".
|
||||
if (mobileID.type()==IMEIType) throw UnexpectedMessage();
|
||||
|
||||
// Must be a TMSI.
|
||||
// Look in the table to see if it's one we assigned.
|
||||
unsigned TMSI = mobileID.TMSI();
|
||||
char* IMSI = NULL;
|
||||
if (sameLAI) IMSI = gTMSITable.IMSI(TMSI);
|
||||
if (IMSI) {
|
||||
// We assigned this TMSI already; the TMSI/IMSI pair is already in the table.
|
||||
mobileID = L3MobileIdentity(IMSI);
|
||||
LOG(DEBUG) << "resolving mobile ID (table): " << mobileID;
|
||||
free(IMSI);
|
||||
return TMSI;
|
||||
}
|
||||
// Not our TMSI.
|
||||
// Phones are not supposed to do this, but many will.
|
||||
// If the IMSI's not in the table, ASK for it.
|
||||
LCH->send(L3IdentityRequest(IMSIType));
|
||||
// FIXME -- This request times out on T3260, 12 sec. See GSM 04.08 Table 11.2.
|
||||
L3Message* msg = getMessage(LCH);
|
||||
L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg);
|
||||
if (!resp) {
|
||||
if (msg) delete msg;
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
mobileID = resp->mobileID();
|
||||
LOG(INFO) << resp;
|
||||
delete msg;
|
||||
LOG(DEBUG) << "resolving mobile ID (requested): " << mobileID;
|
||||
// FIXME -- Should send MM Reject, cause 0x60, "invalid mandatory information".
|
||||
if (mobileID.type()!=IMSIType) throw UnexpectedMessage();
|
||||
// Return 0 to indicate that we have not yet assigned our own TMSI for this phone.
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Resolve a mobile ID to an IMSI. */
|
||||
void Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH)
|
||||
{
|
||||
// Are we done already?
|
||||
if (mobileIdentity.type()==IMSIType) return;
|
||||
|
||||
// If we got a TMSI, find the IMSI.
|
||||
if (mobileIdentity.type()==TMSIType) {
|
||||
char *IMSI = gTMSITable.IMSI(mobileIdentity.TMSI());
|
||||
if (IMSI) mobileIdentity = L3MobileIdentity(IMSI);
|
||||
free(IMSI);
|
||||
}
|
||||
|
||||
// Still no IMSI? Ask for one.
|
||||
if (mobileIdentity.type()!=IMSIType) {
|
||||
LOG(NOTICE) << "MOC with no IMSI or valid TMSI. Reqesting IMSI.";
|
||||
LCH->send(L3IdentityRequest(IMSIType));
|
||||
// FIXME -- This request times out on T3260, 12 sec. See GSM 04.08 Table 11.2.
|
||||
L3Message* msg = getMessage(LCH);
|
||||
L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg);
|
||||
if (!resp) {
|
||||
if (msg) delete msg;
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
mobileIdentity = resp->mobileID();
|
||||
delete msg;
|
||||
}
|
||||
|
||||
// Still no IMSI??
|
||||
if (mobileIdentity.type()!=IMSIType) {
|
||||
// FIXME -- This is quick-and-dirty, not following GSM 04.08 5.
|
||||
LOG(WARNING) << "MOC setup with no IMSI";
|
||||
// Cause 0x60 "Invalid mandatory information"
|
||||
LCH->send(L3CMServiceReject(L3RejectCause(0x60)));
|
||||
LCH->send(L3ChannelRelease());
|
||||
// The SIP side and transaction record don't exist yet.
|
||||
// So we're done.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
219
Control/ControlCommon.h
Normal file
219
Control/ControlCommon.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/**@file Declarations for common-use control-layer functions. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef CONTROLCOMMON_H
|
||||
#define CONTROLCOMMON_H
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <list>
|
||||
|
||||
#include <Logger.h>
|
||||
#include <Interthread.h>
|
||||
#include <Timeval.h>
|
||||
|
||||
|
||||
#include <GSML3CommonElements.h>
|
||||
#include <GSML3MMElements.h>
|
||||
#include <GSML3CCElements.h>
|
||||
#include <GSML3RRMessages.h>
|
||||
#include <SIPEngine.h>
|
||||
|
||||
#include "TMSITable.h"
|
||||
|
||||
|
||||
// Enough forward refs to prevent "kitchen sick" includes and circularity.
|
||||
|
||||
namespace GSM {
|
||||
class L3Message;
|
||||
class LogicalChannel;
|
||||
class SDCCHLogicalChannel;
|
||||
class SACCHLogicalChannel;
|
||||
class TCHFACCHLogicalChannel;
|
||||
class L3CMServiceRequest;
|
||||
};
|
||||
|
||||
|
||||
/**@namespace Control This namepace is for use by the control layer. */
|
||||
namespace Control {
|
||||
|
||||
class TransactionEntry;
|
||||
class TransactionTable;
|
||||
|
||||
/**@name Call control time-out values (in ms) from ITU-T Q.931 Table 9-1 and GSM 04.08 Table 11.4. */
|
||||
//@{
|
||||
#ifndef RACETEST
|
||||
const unsigned T301ms=60000; ///< recv ALERT --> recv CONN
|
||||
const unsigned T302ms=12000; ///< send SETUP ACK --> any progress
|
||||
const unsigned T303ms=10000; ///< send SETUP --> recv CALL CONF or REL COMP
|
||||
const unsigned T304ms=20000; ///< recv SETUP ACK --> any progress
|
||||
const unsigned T305ms=30000; ///< send DISC --> recv REL or DISC
|
||||
const unsigned T308ms=30000; ///< send REL --> rev REL or REL COMP
|
||||
const unsigned T310ms=30000; ///< recv CALL CONF --> recv ALERT, CONN, or DISC
|
||||
const unsigned T313ms=30000; ///< send CONNECT --> recv CONNECT ACK
|
||||
#else
|
||||
// These are reduced values to force testing of poor network behavior.
|
||||
const unsigned T301ms=18000; ///< recv ALERT --> recv CONN
|
||||
const unsigned T302ms=1200; ///< send SETUP ACK --> any progress
|
||||
const unsigned T303ms=400; ///< send SETUP --> recv CALL CONF or REL COMP
|
||||
const unsigned T304ms=2000; ///< recv SETUP ACK --> any progress
|
||||
const unsigned T305ms=3000; ///< send DISC --> recv REL or DISC
|
||||
const unsigned T308ms=3000; ///< send REL --> rev REL or REL COMP
|
||||
const unsigned T310ms=3000; ///< recv CALL CONF --> recv ALERT, CONN, or DISC
|
||||
const unsigned T313ms=3000; ///< send CONNECT --> recv CONNECT ACK
|
||||
#endif
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
/**@name Common-use functions from the control layer. */
|
||||
//@{
|
||||
|
||||
/**
|
||||
Get a message from a LogicalChannel.
|
||||
Close the channel with abnormal release on timeout.
|
||||
Caller must delete the returned pointer.
|
||||
Throws ChannelReadTimeout, UnexpecedPrimitive or UnsupportedMessage on timeout.
|
||||
@param LCH The channel to receive on.
|
||||
@param SAPI The service access point.
|
||||
@return Pointer to message.
|
||||
*/
|
||||
// FIXME -- This needs an adjustable timeout.
|
||||
GSM::L3Message* getMessage(GSM::LogicalChannel* LCH, unsigned SAPI=0);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Dispatch controllers for specific channel types. */
|
||||
//@{
|
||||
void FACCHDispatcher(GSM::TCHFACCHLogicalChannel *TCHFACCH);
|
||||
void SDCCHDispatcher(GSM::SDCCHLogicalChannel *SDCCH);
|
||||
void DCCHDispatcher(GSM::LogicalChannel *DCCH);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Resolve a mobile ID to an IMSI.
|
||||
Returns TMSI, if it is already in the TMSITable.
|
||||
@param sameLAI True if the mobileID is known to have come from this LAI.
|
||||
@param mobID A mobile ID, that may be modified by the function.
|
||||
@param LCH The Dm channel to the mobile.
|
||||
@return A TMSI value from the TMSITable or zero if non found.
|
||||
*/
|
||||
unsigned resolveIMSI(bool sameLAI, GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
|
||||
|
||||
/**
|
||||
Resolve a mobile ID to an IMSI.
|
||||
@param mobID A mobile ID, that may be modified by the function.
|
||||
@param LCH The Dm channel to the mobile.
|
||||
*/
|
||||
void resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**@name Control-layer exceptions. */
|
||||
//@{
|
||||
|
||||
/**
|
||||
A control layer excpection includes a pointer to a transaction.
|
||||
The transaction might require some clean-up action, depending on the exception.
|
||||
*/
|
||||
class ControlLayerException {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mTransactionID;
|
||||
|
||||
public:
|
||||
|
||||
ControlLayerException(unsigned wTransactionID=0)
|
||||
:mTransactionID(wTransactionID)
|
||||
{}
|
||||
|
||||
unsigned transactionID() { return mTransactionID; }
|
||||
};
|
||||
|
||||
/** Thrown when the control layer gets the wrong message */
|
||||
class UnexpectedMessage : public ControlLayerException {
|
||||
public:
|
||||
UnexpectedMessage(unsigned wTransactionID=0)
|
||||
:ControlLayerException(wTransactionID)
|
||||
{}
|
||||
};
|
||||
|
||||
/** Thrown when recvL3 returns NULL */
|
||||
class ChannelReadTimeout : public ControlLayerException {
|
||||
public:
|
||||
ChannelReadTimeout(unsigned wTransactionID=0)
|
||||
:ControlLayerException(wTransactionID)
|
||||
{}
|
||||
};
|
||||
|
||||
/** Thrown when L3 can't parse an incoming message */
|
||||
class UnsupportedMessage : public ControlLayerException {
|
||||
public:
|
||||
UnsupportedMessage(unsigned wTransactionID=0)
|
||||
:ControlLayerException(wTransactionID)
|
||||
{}
|
||||
};
|
||||
|
||||
/** Thrown when the control layer gets the wrong primitive */
|
||||
class UnexpectedPrimitive : public ControlLayerException {
|
||||
public:
|
||||
UnexpectedPrimitive(unsigned wTransactionID=0)
|
||||
:ControlLayerException(wTransactionID)
|
||||
{}
|
||||
};
|
||||
|
||||
/** Thrown when a T3xx expires */
|
||||
class Q931TimerExpired : public ControlLayerException {
|
||||
public:
|
||||
Q931TimerExpired(unsigned wTransactionID=0)
|
||||
:ControlLayerException(wTransactionID)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
} //Control
|
||||
|
||||
|
||||
|
||||
/**@addtogroup Globals */
|
||||
//@{
|
||||
/** A single global transaction table in the global namespace. */
|
||||
extern Control::TransactionTable gTransactionTable;
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
192
Control/DCCHDispatch.cpp
Normal file
192
Control/DCCHDispatch.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/**@file Idle-mode dispatcher for dedicated control channels. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "ControlCommon.h"
|
||||
#include "TransactionTable.h"
|
||||
#include "RadioResource.h"
|
||||
#include "MobilityManagement.h"
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSML3MMMessages.h>
|
||||
#include <GSML3RRMessages.h>
|
||||
#include <SIPUtility.h>
|
||||
#include <SIPInterface.h>
|
||||
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
using namespace Control;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Dispatch the appropriate controller for a Mobility Management message.
|
||||
@param req A pointer to the initial message.
|
||||
@param DCCH A pointer to the logical channel for the transaction.
|
||||
*/
|
||||
void DCCHDispatchMM(const L3MMMessage* req, LogicalChannel *DCCH)
|
||||
{
|
||||
assert(req);
|
||||
L3MMMessage::MessageType MTI = (L3MMMessage::MessageType)req->MTI();
|
||||
switch (MTI) {
|
||||
case L3MMMessage::LocationUpdatingRequest:
|
||||
LocationUpdatingController(dynamic_cast<const L3LocationUpdatingRequest*>(req),DCCH);
|
||||
break;
|
||||
case L3MMMessage::IMSIDetachIndication:
|
||||
IMSIDetachController(dynamic_cast<const L3IMSIDetachIndication*>(req),DCCH);
|
||||
break;
|
||||
case L3MMMessage::CMServiceRequest:
|
||||
CMServiceResponder(dynamic_cast<const L3CMServiceRequest*>(req),DCCH);
|
||||
break;
|
||||
default:
|
||||
LOG(NOTICE) << "unhandled MM message " << MTI << " on " << *DCCH;
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Dispatch the appropriate controller for a Radio Resource message.
|
||||
@param req A pointer to the initial message.
|
||||
@param DCCH A pointer to the logical channel for the transaction.
|
||||
*/
|
||||
void DCCHDispatchRR(const L3RRMessage* req, LogicalChannel *DCCH)
|
||||
{
|
||||
LOG(DEBUG) << "checking MTI"<< (L3RRMessage::MessageType)req->MTI();
|
||||
|
||||
// TODO SMS -- This needs to handle SACCH Measurement Reports.
|
||||
|
||||
assert(req);
|
||||
L3RRMessage::MessageType MTI = (L3RRMessage::MessageType)req->MTI();
|
||||
switch (MTI) {
|
||||
case L3RRMessage::PagingResponse:
|
||||
PagingResponseHandler(dynamic_cast<const L3PagingResponse*>(req),DCCH);
|
||||
break;
|
||||
case L3RRMessage::AssignmentComplete:
|
||||
AssignmentCompleteHandler(dynamic_cast<const L3AssignmentComplete*>(req),
|
||||
dynamic_cast<TCHFACCHLogicalChannel*>(DCCH));
|
||||
break;
|
||||
default:
|
||||
LOG(NOTICE) << "unhandled RR message " << MTI << " on " << *DCCH;
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DCCHDispatchMessage(const L3Message* msg, LogicalChannel* DCCH)
|
||||
{
|
||||
// Each protocol has it's own sub-dispatcher.
|
||||
switch (msg->PD()) {
|
||||
case L3MobilityManagementPD:
|
||||
DCCHDispatchMM(dynamic_cast<const L3MMMessage*>(msg),DCCH);
|
||||
break;
|
||||
case L3RadioResourcePD:
|
||||
DCCHDispatchRR(dynamic_cast<const L3RRMessage*>(msg),DCCH);
|
||||
break;
|
||||
default:
|
||||
LOG(NOTICE) << "unhandled protocol " << msg->PD() << " on " << *DCCH;
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Example of a closed-loop, persistent-thread control function for the DCCH. */
|
||||
void Control::DCCHDispatcher(LogicalChannel *DCCH)
|
||||
{
|
||||
while (1) {
|
||||
try {
|
||||
// Wait for a transaction to start.
|
||||
LOG(DEBUG) << "waiting for " << *DCCH << " ESTABLISH";
|
||||
DCCH->waitForPrimitive(ESTABLISH);
|
||||
// Pull the first message and dispatch a new transaction.
|
||||
const L3Message *message = getMessage(DCCH);
|
||||
LOG(DEBUG) << *DCCH << " received " << *message;
|
||||
DCCHDispatchMessage(message,DCCH);
|
||||
delete message;
|
||||
}
|
||||
|
||||
// Catch the various error cases.
|
||||
|
||||
catch (ChannelReadTimeout except) {
|
||||
LOG(NOTICE) << "ChannelReadTimeout";
|
||||
// Cause 0x03 means "abnormal release, timer expired".
|
||||
DCCH->send(L3ChannelRelease(0x03));
|
||||
gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
catch (UnexpectedPrimitive except) {
|
||||
LOG(NOTICE) << "UnexpectedPrimitive";
|
||||
// Cause 0x62 means "message type not not compatible with protocol state".
|
||||
DCCH->send(L3ChannelRelease(0x62));
|
||||
if (except.transactionID()) gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
catch (UnexpectedMessage except) {
|
||||
LOG(NOTICE) << "UnexpectedMessage";
|
||||
// Cause 0x62 means "message type not not compatible with protocol state".
|
||||
DCCH->send(L3ChannelRelease(0x62));
|
||||
if (except.transactionID()) gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
catch (UnsupportedMessage except) {
|
||||
LOG(NOTICE) << "UnsupportedMessage";
|
||||
// Cause 0x61 means "message type not implemented".
|
||||
DCCH->send(L3ChannelRelease(0x61));
|
||||
if (except.transactionID()) gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
catch (Q931TimerExpired except) {
|
||||
LOG(NOTICE) << "Q.931 T3xx timer expired";
|
||||
// Cause 0x03 means "abnormal release, timer expired".
|
||||
// TODO -- Send diagnostics.
|
||||
DCCH->send(L3ChannelRelease(0x03));
|
||||
if (except.transactionID()) gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
catch (SIP::SIPTimeout except) {
|
||||
// FIXME -- The transaction ID should be an argument here.
|
||||
LOG(WARNING) << "Uncaught SIPTimeout, will leave a stray transcation";
|
||||
// Cause 0x03 means "abnormal release, timer expired".
|
||||
DCCH->send(L3ChannelRelease(0x03));
|
||||
if (except.transactionID()) gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
catch (SIP::SIPError except) {
|
||||
// FIXME -- The transaction ID should be an argument here.
|
||||
LOG(WARNING) << "Uncaught SIPError, will leave a stray transcation";
|
||||
// Cause 0x01 means "abnormal release, unspecified".
|
||||
DCCH->send(L3ChannelRelease(0x01));
|
||||
if (except.transactionID()) gTransactionTable.remove(except.transactionID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
51
Control/Makefile.am
Normal file
51
Control/Makefile.am
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
# Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
# Copyright 2011 Range Networks, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall
|
||||
|
||||
EXTRA_DIST = README.Control
|
||||
|
||||
noinst_LTLIBRARIES = libcontrol.la
|
||||
|
||||
libcontrol_la_SOURCES = \
|
||||
TransactionTable.cpp \
|
||||
TMSITable.cpp \
|
||||
CallControl.cpp \
|
||||
SMSControl.cpp \
|
||||
ControlCommon.cpp \
|
||||
MobilityManagement.cpp \
|
||||
RadioResource.cpp \
|
||||
DCCHDispatch.cpp
|
||||
|
||||
|
||||
noinst_HEADERS = \
|
||||
ControlCommon.h \
|
||||
SMSControl.h \
|
||||
TransactionTable.h \
|
||||
TMSITable.h \
|
||||
RadioResource.h \
|
||||
MobilityManagement.h \
|
||||
CallControl.h \
|
||||
TMSITable.h
|
||||
554
Control/Makefile.in
Normal file
554
Control/Makefile.in
Normal file
@@ -0,0 +1,554 @@
|
||||
# Makefile.in generated by automake 1.9.4 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004 Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
# Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
# Copyright 2011 Range Networks, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
|
||||
SOURCES = $(libcontrol_la_SOURCES)
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = ..
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
INSTALL_HEADER = $(INSTALL_DATA)
|
||||
transform = $(program_transform_name)
|
||||
NORMAL_INSTALL = :
|
||||
PRE_INSTALL = :
|
||||
POST_INSTALL = :
|
||||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(top_srcdir)/Makefile.common
|
||||
subdir = Control
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||
libcontrol_la_LIBADD =
|
||||
am_libcontrol_la_OBJECTS = TransactionTable.lo TMSITable.lo \
|
||||
CallControl.lo SMSControl.lo ControlCommon.lo \
|
||||
MobilityManagement.lo RadioResource.lo DCCHDispatch.lo
|
||||
libcontrol_la_OBJECTS = $(am_libcontrol_la_OBJECTS)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
|
||||
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
|
||||
$(AM_CXXFLAGS) $(CXXFLAGS)
|
||||
CXXLD = $(CXX)
|
||||
CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
|
||||
$(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(libcontrol_la_SOURCES)
|
||||
DIST_SOURCES = $(libcontrol_la_SOURCES)
|
||||
HEADERS = $(noinst_HEADERS)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AS = @AS@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCAS = @CCAS@
|
||||
CCASFLAGS = @CCASFLAGS@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
CXXDEPMODE = @CXXDEPMODE@
|
||||
CXXFLAGS = @CXXFLAGS@
|
||||
CYGPATH_W = @CYGPATH_W@
|
||||
DEFS = @DEFS@
|
||||
DEPDIR = @DEPDIR@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
ECHO = @ECHO@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
F77 = @F77@
|
||||
FFLAGS = @FFLAGS@
|
||||
GREP = @GREP@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIBUSB_CFLAGS = @LIBUSB_CFLAGS@
|
||||
LIBUSB_LIBS = @LIBUSB_LIBS@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
ORTP_CFLAGS = @ORTP_CFLAGS@
|
||||
ORTP_LIBS = @ORTP_LIBS@
|
||||
OSIP_CFLAGS = @OSIP_CFLAGS@
|
||||
OSIP_LIBS = @OSIP_LIBS@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
RANLIB = @RANLIB@
|
||||
RM_PROG = @RM_PROG@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
VERSION = @VERSION@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_F77 = @ac_ct_F77@
|
||||
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
|
||||
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
|
||||
CONTROL_INCLUDEDIR = $(top_srcdir)/Control
|
||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
|
||||
SIP_INCLUDEDIR = $(top_srcdir)/SIP
|
||||
SMS_INCLUDEDIR = $(top_srcdir)/SMS
|
||||
TRX_INCLUDEDIR = $(top_srcdir)/TRXManager
|
||||
GLOBALS_INCLUDEDIR = $(top_srcdir)/Globals
|
||||
CLI_INCLUDEDIR = $(top_srcdir)/CLI
|
||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
|
||||
SR_INCLUDEDIR = $(top_srcdir)/SR
|
||||
STD_DEFINES_AND_INCLUDES = \
|
||||
-I$(COMMON_INCLUDEDIR) \
|
||||
-I$(CONTROL_INCLUDEDIR) \
|
||||
-I$(GSM_INCLUDEDIR) \
|
||||
-I$(SIP_INCLUDEDIR) \
|
||||
-I$(SMS_INCLUDEDIR) \
|
||||
-I$(TRX_INCLUDEDIR) \
|
||||
-I$(GLOBALS_INCLUDEDIR) \
|
||||
-I$(CLI_INCLUDEDIR) \
|
||||
-I$(SR_INCLUDEDIR) \
|
||||
-I$(SQLITE_INCLUDEDIR)
|
||||
|
||||
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
|
||||
GSM_LA = $(top_builddir)/GSM/libGSM.la
|
||||
SIP_LA = $(top_builddir)/SIP/libSIP.la
|
||||
SMS_LA = $(top_builddir)/SMS/libSMS.la
|
||||
TRX_LA = $(top_builddir)/TRXManager/libtrxmanager.la
|
||||
CONTROL_LA = $(top_builddir)/Control/libcontrol.la
|
||||
GLOBALS_LA = $(top_builddir)/Globals/libglobals.la
|
||||
CLI_LA = $(top_builddir)/CLI/libcli.la
|
||||
SR_LA = $(top_builddir)/SR/libSR.la
|
||||
SQLITE_LA = $(top_builddir)/sqlite3/libsqlite.la
|
||||
MOSTLYCLEANFILES = *~
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall
|
||||
EXTRA_DIST = README.Control
|
||||
noinst_LTLIBRARIES = libcontrol.la
|
||||
libcontrol_la_SOURCES = \
|
||||
TransactionTable.cpp \
|
||||
TMSITable.cpp \
|
||||
CallControl.cpp \
|
||||
SMSControl.cpp \
|
||||
ControlCommon.cpp \
|
||||
MobilityManagement.cpp \
|
||||
RadioResource.cpp \
|
||||
DCCHDispatch.cpp
|
||||
|
||||
noinst_HEADERS = \
|
||||
ControlCommon.h \
|
||||
SMSControl.h \
|
||||
TransactionTable.h \
|
||||
TMSITable.h \
|
||||
RadioResource.h \
|
||||
MobilityManagement.h \
|
||||
CallControl.h \
|
||||
TMSITable.h
|
||||
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .cpp .lo .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Makefile.common $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
*$$dep*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
|
||||
&& exit 0; \
|
||||
exit 1;; \
|
||||
esac; \
|
||||
done; \
|
||||
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Control/Makefile'; \
|
||||
cd $(top_srcdir) && \
|
||||
$(AUTOMAKE) --gnu Control/Makefile
|
||||
.PRECIOUS: Makefile
|
||||
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
|
||||
@case '$?' in \
|
||||
*config.status*) \
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
|
||||
*) \
|
||||
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
|
||||
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
|
||||
esac;
|
||||
|
||||
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
$(top_srcdir)/configure: $(am__configure_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
|
||||
clean-noinstLTLIBRARIES:
|
||||
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
|
||||
@list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
|
||||
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
|
||||
test "$$dir" != "$$p" || dir=.; \
|
||||
echo "rm -f \"$${dir}/so_locations\""; \
|
||||
rm -f "$${dir}/so_locations"; \
|
||||
done
|
||||
libcontrol.la: $(libcontrol_la_OBJECTS) $(libcontrol_la_DEPENDENCIES)
|
||||
$(CXXLINK) $(libcontrol_la_LDFLAGS) $(libcontrol_la_OBJECTS) $(libcontrol_la_LIBADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
||||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CallControl.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ControlCommon.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DCCHDispatch.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MobilityManagement.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RadioResource.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SMSControl.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TMSITable.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransactionTable.Plo@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
|
||||
.cpp.lo:
|
||||
@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
distclean-libtool:
|
||||
-rm -f libtool
|
||||
uninstall-info-am:
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
mkid -fID $$unique
|
||||
tags: TAGS
|
||||
|
||||
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
tags=; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
|
||||
test -n "$$unique" || unique=$$empty_fix; \
|
||||
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
|
||||
$$tags $$unique; \
|
||||
fi
|
||||
ctags: CTAGS
|
||||
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
|
||||
$(TAGS_FILES) $(LISP)
|
||||
tags=; \
|
||||
here=`pwd`; \
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
|
||||
done | \
|
||||
$(AWK) ' { files[$$0] = 1; } \
|
||||
END { for (i in files) print i; }'`; \
|
||||
test -z "$(CTAGS_ARGS)$$tags$$unique" \
|
||||
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
|
||||
$$tags $$unique
|
||||
|
||||
GTAGS:
|
||||
here=`$(am__cd) $(top_builddir) && pwd` \
|
||||
&& cd $(top_srcdir) \
|
||||
&& gtags -i $(GTAGS_ARGS) $$here
|
||||
|
||||
distclean-tags:
|
||||
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
$(mkdir_p) $(distdir)/..
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
|
||||
else \
|
||||
test -f $(distdir)/$$file \
|
||||
|| cp -p $$d/$$file $(distdir)/$$file \
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
|
||||
installdirs:
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
install-data: install-data-am
|
||||
uninstall: uninstall-am
|
||||
|
||||
install-am: all-am
|
||||
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
|
||||
|
||||
installcheck: installcheck-am
|
||||
install-strip:
|
||||
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
|
||||
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
|
||||
`test -z '$(STRIP)' || \
|
||||
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
|
||||
mostlyclean-generic:
|
||||
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
|
||||
|
||||
clean-generic:
|
||||
|
||||
distclean-generic:
|
||||
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
|
||||
|
||||
maintainer-clean-generic:
|
||||
@echo "This command is intended for maintainers to use"
|
||||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
|
||||
mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-libtool distclean-tags
|
||||
|
||||
dvi: dvi-am
|
||||
|
||||
dvi-am:
|
||||
|
||||
html: html-am
|
||||
|
||||
info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-exec-am:
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
pdf-am:
|
||||
|
||||
ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-info-am
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libtool clean-noinstLTLIBRARIES ctags distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-exec \
|
||||
install-exec-am install-info install-info-am install-man \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
|
||||
pdf pdf-am ps ps-am tags uninstall uninstall-am \
|
||||
uninstall-info-am
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
558
Control/MobilityManagement.cpp
Normal file
558
Control/MobilityManagement.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
/**@file GSM/SIP Mobility Management, GSM 04.08. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <Timeval.h>
|
||||
|
||||
#include "ControlCommon.h"
|
||||
#include "MobilityManagement.h"
|
||||
#include "SMSControl.h"
|
||||
#include "CallControl.h"
|
||||
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSML3RRMessages.h>
|
||||
#include <GSML3MMMessages.h>
|
||||
#include <GSML3CCMessages.h>
|
||||
#include <GSMConfig.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <SIPInterface.h>
|
||||
#include <SIPUtility.h>
|
||||
#include <SIPMessage.h>
|
||||
#include <SIPEngine.h>
|
||||
#include <SubscriberRegistry.h>
|
||||
|
||||
using namespace SIP;
|
||||
|
||||
#include <Regexp.h>
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
|
||||
using namespace GSM;
|
||||
using namespace Control;
|
||||
|
||||
|
||||
/** Controller for CM Service requests, dispatches out to multiple possible transaction controllers. */
|
||||
void Control::CMServiceResponder(const L3CMServiceRequest* cmsrq, LogicalChannel* DCCH)
|
||||
{
|
||||
assert(cmsrq);
|
||||
assert(DCCH);
|
||||
LOG(INFO) << *cmsrq;
|
||||
switch (cmsrq->serviceType().type()) {
|
||||
case L3CMServiceType::MobileOriginatedCall:
|
||||
MOCStarter(cmsrq,DCCH);
|
||||
break;
|
||||
case L3CMServiceType::ShortMessage:
|
||||
MOSMSController(cmsrq,DCCH);
|
||||
break;
|
||||
default:
|
||||
LOG(NOTICE) << "service not supported for " << *cmsrq;
|
||||
// Cause 0x20 means "serivce not supported".
|
||||
DCCH->send(L3CMServiceReject(0x20));
|
||||
DCCH->send(L3ChannelRelease());
|
||||
}
|
||||
// The transaction may or may not be cleared,
|
||||
// depending on the assignment type.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** Controller for the IMSI Detach transaction, GSM 04.08 4.3.4. */
|
||||
void Control::IMSIDetachController(const L3IMSIDetachIndication* idi, LogicalChannel* DCCH)
|
||||
{
|
||||
assert(idi);
|
||||
assert(DCCH);
|
||||
LOG(INFO) << *idi;
|
||||
|
||||
// The IMSI detach maps to a SIP unregister with the local Asterisk server.
|
||||
try {
|
||||
// FIXME -- Resolve TMSIs to IMSIs.
|
||||
if (idi->mobileID().type()==IMSIType) {
|
||||
SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(), idi->mobileID().digits());
|
||||
engine.unregister();
|
||||
}
|
||||
}
|
||||
catch(SIPTimeout) {
|
||||
LOG(ALERT) "SIP registration timed out. Is Asterisk running?";
|
||||
}
|
||||
// No reponse required, so just close the channel.
|
||||
DCCH->send(L3ChannelRelease());
|
||||
// Many handsets never complete the transaction.
|
||||
// So force a shutdown of the channel.
|
||||
DCCH->send(HARDRELEASE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Send a given welcome message from a given short code.
|
||||
@return true if it was sent
|
||||
*/
|
||||
bool sendWelcomeMessage(const char* messageName, const char* shortCodeName, const char *IMSI, LogicalChannel* DCCH, const char *whiteListCode = NULL)
|
||||
{
|
||||
if (!gConfig.defines(messageName)) return false;
|
||||
LOG(INFO) << "sending " << messageName << " message to handset";
|
||||
ostringstream message;
|
||||
message << gConfig.getStr(messageName) << " IMSI:" << IMSI;
|
||||
if (whiteListCode) {
|
||||
message << ", white-list code: " << whiteListCode;
|
||||
}
|
||||
// This returns when delivery is acked in L3.
|
||||
deliverSMSToMS(
|
||||
gConfig.getStr(shortCodeName).c_str(),
|
||||
message.str().c_str(), "text/plain",
|
||||
random()%7,DCCH);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class RRLPServer
|
||||
{
|
||||
public:
|
||||
RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH);
|
||||
// tell server to send location assistance to mobile
|
||||
bool assist();
|
||||
// tell server to ask mobile for location
|
||||
bool locate();
|
||||
private:
|
||||
RRLPServer(); // not allowed
|
||||
string url;
|
||||
L3MobileIdentity mobileID;
|
||||
LogicalChannel *DCCH;
|
||||
string query;
|
||||
string name;
|
||||
bool transact();
|
||||
bool trouble;
|
||||
};
|
||||
|
||||
RRLPServer::RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH)
|
||||
{
|
||||
trouble = false;
|
||||
url = gConfig.getStr("GSM.RRLP.SERVER.URL", "");
|
||||
if (url.length() == 0) {
|
||||
LOG(INFO) << "RRLP not configured";
|
||||
trouble = true;
|
||||
return;
|
||||
}
|
||||
mobileID = wMobileID;
|
||||
DCCH = wDCCH;
|
||||
// name of subscriber
|
||||
name = string("IMSI") + mobileID.digits();
|
||||
// don't continue if IMSI has a RRLP support flag and it's false
|
||||
unsigned int supported = 0;
|
||||
if (sqlite3_single_lookup(gSubscriberRegistry.db(), "sip_buddies", "name", name.c_str(),
|
||||
"RRLPSupported", supported) && !supported) {
|
||||
LOG(INFO) << "RRLP not supported for " << name;
|
||||
trouble = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool RRLPServer::assist()
|
||||
{
|
||||
if (trouble) return false;
|
||||
query = "query=assist";
|
||||
return transact();
|
||||
}
|
||||
|
||||
bool RRLPServer::locate()
|
||||
{
|
||||
if (trouble) return false;
|
||||
query = "query=loc";
|
||||
return transact();
|
||||
}
|
||||
|
||||
void clean(char *line)
|
||||
{
|
||||
char *p = line + strlen(line) - 1;
|
||||
while (p > line && *p <= ' ') *p-- = 0;
|
||||
}
|
||||
|
||||
string getConfig()
|
||||
{
|
||||
const char *configs[] = {
|
||||
"GSM.RRLP.ACCURACY",
|
||||
"GSM.RRLP.RESPONSETIME",
|
||||
"GSM.RRLP.ALMANAC.URL",
|
||||
"GSM.RRLP.EPHEMERIS.URL",
|
||||
"GSM.RRLP.ALMANAC.REFRESH.TIME",
|
||||
"GSM.RRLP.EPHEMERIS.REFRESH.TIME",
|
||||
"GSM.RRLP.SEED.LATITUDE",
|
||||
"GSM.RRLP.SEED.LONGITUDE",
|
||||
"GSM.RRLP.SEED.ALTITUDE",
|
||||
"GSM.RRLP.EPHEMERIS.ASSIST.COUNT",
|
||||
"GSM.RRLP.ALMANAC.ASSIST.PRESENT",
|
||||
0
|
||||
};
|
||||
string config = "";
|
||||
for (const char **p = configs; *p; p++) {
|
||||
string configValue = gConfig.getStr(*p, "");
|
||||
if (configValue.length() == 0) return "";
|
||||
config.append("&");
|
||||
config.append(*p);
|
||||
config.append("=");
|
||||
if (0 == strcmp((*p) + strlen(*p) - 3, "URL")) {
|
||||
// TODO - better to have urlencode and decode, but then I'd have to look for them
|
||||
char buf[3];
|
||||
buf[2] = 0;
|
||||
for (const char *q = configValue.c_str(); *q; q++) {
|
||||
sprintf(buf, "%02x", *q);
|
||||
config.append(buf);
|
||||
}
|
||||
} else {
|
||||
config.append(configValue);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
bool RRLPServer::transact()
|
||||
{
|
||||
vector<string> apdus;
|
||||
while (true) {
|
||||
// bounce off server
|
||||
string esc = "'";
|
||||
string config = getConfig();
|
||||
if (config.length() == 0) return false;
|
||||
string cmd = "wget -qO- " + esc + url + "?" + query + config + esc;
|
||||
LOG(INFO) << "*************** " << cmd;
|
||||
FILE *result = popen(cmd.c_str(), "r");
|
||||
if (!result) {
|
||||
LOG(CRIT) << "popen call \"" << cmd << "\" failed";
|
||||
return NULL;
|
||||
}
|
||||
// build map of responses, and list of apdus
|
||||
map<string,string> response;
|
||||
size_t nbytes = 1500;
|
||||
char *line = (char*)malloc(nbytes+1);
|
||||
while (!feof(result)) {
|
||||
if (!fgets(line, nbytes, result)) break;
|
||||
clean(line);
|
||||
LOG(INFO) << "server return: " << line;
|
||||
char *p = strchr(line, '=');
|
||||
if (!p) continue;
|
||||
string lhs = string(line, 0, p-line);
|
||||
string rhs = string(line, p+1-line, string::npos);
|
||||
if (lhs == "apdu") {
|
||||
apdus.push_back(rhs);
|
||||
} else {
|
||||
response[lhs] = rhs;
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
pclose(result);
|
||||
|
||||
// quit if error
|
||||
if (response.find("error") != response.end()) {
|
||||
LOG(INFO) << "error from server: " << response["error"];
|
||||
return false;
|
||||
}
|
||||
|
||||
// quit if ack from assist unless there are more apdu messages
|
||||
if (response.find("ack") != response.end()) {
|
||||
LOG(INFO) << "ack from mobile, decoded by server";
|
||||
if (apdus.size() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
LOG(INFO) << "more apdu messages";
|
||||
}
|
||||
}
|
||||
|
||||
// quit if location decoded
|
||||
if (response.find("latitude") != response.end() && response.find("longitude") != response.end() && response.find("positionError") != response.end()) {
|
||||
ostringstream os;
|
||||
os << "insert into RRLP (name, latitude, longitude, error, time) values (" <<
|
||||
'"' << name << '"' << "," <<
|
||||
response["latitude"] << "," <<
|
||||
response["longitude"] << "," <<
|
||||
response["positionError"] << "," <<
|
||||
"datetime('now')"
|
||||
")";
|
||||
LOG(INFO) << os.str();
|
||||
if (!sqlite3_command(gSubscriberRegistry.db(), os.str().c_str())) {
|
||||
LOG(INFO) << "sqlite3_command problem";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// bounce off mobile
|
||||
if (apdus.size() == 0) {
|
||||
LOG(INFO) << "missing apdu for mobile";
|
||||
return false;
|
||||
}
|
||||
string apdu = apdus[0];
|
||||
apdus.erase(apdus.begin());
|
||||
BitVector bv(apdu.size()*4);
|
||||
if (!bv.unhex(apdu.c_str())) {
|
||||
LOG(INFO) << "BitVector::unhex problem";
|
||||
return false;
|
||||
}
|
||||
|
||||
DCCH->send(L3ApplicationInformation(bv));
|
||||
// Receive an L3 frame with a timeout. Timeout loc req response time max + 2 seconds.
|
||||
L3Frame* resp = DCCH->recv(130000);
|
||||
if (!resp) {
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << "RRLPQuery returned " << *resp;
|
||||
if (resp->primitive() != DATA) {
|
||||
LOG(INFO) << "didn't receive data";
|
||||
switch (resp->primitive()) {
|
||||
case ESTABLISH: LOG(INFO) << "channel establihsment"; break;
|
||||
case RELEASE: LOG(INFO) << "normal channel release"; break;
|
||||
case DATA: LOG(INFO) << "multiframe data transfer"; break;
|
||||
case UNIT_DATA: LOG(INFO) << "datagram-type data transfer"; break;
|
||||
case ERROR: LOG(INFO) << "channel error"; break;
|
||||
case HARDRELEASE: LOG(INFO) << "forced release after an assignment"; break;
|
||||
default: LOG(INFO) << "unrecognized primitive response"; break;
|
||||
}
|
||||
delete resp;
|
||||
return false;
|
||||
}
|
||||
const unsigned PD_RR = 6;
|
||||
LOG(INFO) << "resp.pd = " << resp->PD();
|
||||
if (resp->PD() != PD_RR) {
|
||||
LOG(INFO) << "didn't receive an RR message";
|
||||
delete resp;
|
||||
return false;
|
||||
}
|
||||
const unsigned MTI_RR_STATUS = 18;
|
||||
if (resp->MTI() == MTI_RR_STATUS) {
|
||||
ostringstream os2;
|
||||
int cause = resp->peekField(16, 8);
|
||||
delete resp;
|
||||
switch (cause) {
|
||||
case 97:
|
||||
LOG(INFO) << "MS says: message not implemented";
|
||||
// flag unsupported in SR so we don't waste time on it again
|
||||
os2 << "update sip_buddies set RRLPSupported = \"0\" where name = \"" << name << "\"";
|
||||
if (!sqlite3_command(gSubscriberRegistry.db(), os2.str().c_str())) {
|
||||
LOG(INFO) << "sqlite3_command problem";
|
||||
}
|
||||
return false;
|
||||
case 98:
|
||||
LOG(INFO) << "MS says: message type not compatible with protocol state";
|
||||
return false;
|
||||
default:
|
||||
LOG(INFO) << "unknown RR_STATUS response, cause = " << cause;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const unsigned MTI_RR_APDU = 56;
|
||||
if (resp->MTI() != MTI_RR_APDU) {
|
||||
LOG(INFO) << "received unexpected RR Message " << resp->MTI();
|
||||
delete resp;
|
||||
return false;
|
||||
}
|
||||
|
||||
// looks like a good APDU
|
||||
BitVector *bv2 = (BitVector*)resp;
|
||||
BitVector bv3 = bv2->tail(32);
|
||||
ostringstream os;
|
||||
bv3.hex(os);
|
||||
apdu = os.str();
|
||||
delete resp;
|
||||
|
||||
// next query for server
|
||||
query = "query=apdu&apdu=" + apdu;
|
||||
}
|
||||
// not reached
|
||||
}
|
||||
|
||||
/**
|
||||
Controller for the Location Updating transaction, GSM 04.08 4.4.4.
|
||||
@param lur The location updating request.
|
||||
@param DCCH The Dm channel to the MS, which will be released by the function.
|
||||
*/
|
||||
void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, LogicalChannel* DCCH)
|
||||
{
|
||||
assert(DCCH);
|
||||
assert(lur);
|
||||
LOG(INFO) << *lur;
|
||||
|
||||
// The location updating request gets mapped to a SIP
|
||||
// registration with the Asterisk server.
|
||||
|
||||
// We also allocate a new TMSI for every handset we encounter.
|
||||
// If the handset is allow to register it may receive a TMSI reassignment.
|
||||
|
||||
// Resolve an IMSI and see if there's a pre-existing IMSI-TMSI mapping.
|
||||
// This operation will throw an exception, caught in a higher scope,
|
||||
// if it fails in the GSM domain.
|
||||
L3MobileIdentity mobileID = lur->mobileID();
|
||||
bool sameLAI = (lur->LAI() == gBTS.LAI());
|
||||
unsigned preexistingTMSI = resolveIMSI(sameLAI,mobileID,DCCH);
|
||||
const char *IMSI = mobileID.digits();
|
||||
// IMSIAttach set to true if this is a new registration.
|
||||
bool IMSIAttach = (preexistingTMSI==0);
|
||||
|
||||
// We assign generate a TMSI for every new phone we see,
|
||||
// even if we don't actually assign it.
|
||||
unsigned newTMSI = 0;
|
||||
if (!preexistingTMSI) newTMSI = gTMSITable.assign(IMSI,lur);
|
||||
|
||||
// Try to register the IMSI.
|
||||
// This will be set true if registration succeeded in the SIP world.
|
||||
bool success = false;
|
||||
try {
|
||||
SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(),IMSI);
|
||||
LOG(DEBUG) << "waiting for registration of " << IMSI << " on " << gConfig.getStr("SIP.Proxy.Registration");
|
||||
success = engine.Register(SIPEngine::SIPRegister);
|
||||
}
|
||||
catch(SIPTimeout) {
|
||||
LOG(ALERT) "SIP registration timed out. Is the proxy running at " << gConfig.getStr("SIP.Proxy.Registration");
|
||||
// Reject with a "network failure" cause code, 0x11.
|
||||
DCCH->send(L3LocationUpdatingReject(0x11));
|
||||
// HACK -- wait long enough for a response
|
||||
// FIXME -- Why are we doing this?
|
||||
sleep(4);
|
||||
// Release the channel and return.
|
||||
DCCH->send(L3ChannelRelease());
|
||||
return;
|
||||
}
|
||||
|
||||
if (gConfig.defines("Control.LUR.QueryRRLP")) {
|
||||
// Query for RRLP
|
||||
RRLPServer wRRLPServer(mobileID, DCCH);
|
||||
if (!wRRLPServer.assist()) {
|
||||
LOG(INFO) << "RRLPServer::assist problem";
|
||||
}
|
||||
// can still try to check location even if assist didn't work
|
||||
if (!wRRLPServer.locate()) {
|
||||
LOG(INFO) << "RRLPServer::locate problem";
|
||||
}
|
||||
}
|
||||
|
||||
// This allows us to configure Open Registration
|
||||
bool openRegistration = gConfig.defines("Control.LUR.OpenRegistration");
|
||||
|
||||
// Query for IMEI?
|
||||
if (IMSIAttach && gConfig.defines("Control.LUR.QueryIMEI")) {
|
||||
DCCH->send(L3IdentityRequest(IMEIType));
|
||||
L3Message* msg = getMessage(DCCH);
|
||||
L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg);
|
||||
if (!resp) {
|
||||
if (msg) {
|
||||
LOG(WARNING) << "Unexpected message " << *msg;
|
||||
delete msg;
|
||||
}
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
LOG(INFO) << *resp;
|
||||
if (!gTMSITable.IMEI(IMSI,resp->mobileID().digits()))
|
||||
LOG(WARNING) << "failed access to TMSITable";
|
||||
delete msg;
|
||||
}
|
||||
|
||||
// Query for classmark?
|
||||
if (IMSIAttach && gConfig.defines("Control.LUR.QueryClassmark")) {
|
||||
DCCH->send(L3ClassmarkEnquiry());
|
||||
L3Message* msg = getMessage(DCCH);
|
||||
L3ClassmarkChange *resp = dynamic_cast<L3ClassmarkChange*>(msg);
|
||||
if (!resp) {
|
||||
if (msg) {
|
||||
LOG(WARNING) << "Unexpected message " << *msg;
|
||||
delete msg;
|
||||
}
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
LOG(INFO) << *resp;
|
||||
const L3MobileStationClassmark2& classmark = resp->classmark();
|
||||
if (!gTMSITable.classmark(IMSI,classmark))
|
||||
LOG(WARNING) << "failed access to TMSITable";
|
||||
delete msg;
|
||||
}
|
||||
|
||||
// We fail closed unless we're configured otherwise
|
||||
if (!success && !openRegistration) {
|
||||
LOG(INFO) << "registration FAILED: " << mobileID;
|
||||
DCCH->send(L3LocationUpdatingReject(gConfig.getNum("Control.LUR.UnprovisionedRejectCause")));
|
||||
if (!preexistingTMSI) {
|
||||
sendWelcomeMessage( "Control.LUR.FailedRegistration.Message",
|
||||
"Control.LUR.FailedRegistration.ShortCode", IMSI,DCCH);
|
||||
}
|
||||
// Release the channel and return.
|
||||
DCCH->send(L3ChannelRelease());
|
||||
return;
|
||||
}
|
||||
|
||||
// If success is true, we had a normal registration.
|
||||
// Otherwise, we are here because of open registration.
|
||||
// Either way, we're going to register a phone if we arrive here.
|
||||
|
||||
if (success) {
|
||||
LOG(INFO) << "registration SUCCESS: " << mobileID;
|
||||
} else {
|
||||
LOG(INFO) << "registration ALLOWED: " << mobileID;
|
||||
}
|
||||
|
||||
|
||||
// Send the "short name" and time-of-day.
|
||||
if (IMSIAttach && gConfig.defines("GSM.Identity.ShortName")) {
|
||||
DCCH->send(L3MMInformation(gConfig.getStr("GSM.Identity.ShortName").c_str()));
|
||||
}
|
||||
// Accept. Make a TMSI assignment, too, if needed.
|
||||
if (preexistingTMSI || !gConfig.defines("Control.LUR.SendTMSIs")) {
|
||||
DCCH->send(L3LocationUpdatingAccept(gBTS.LAI()));
|
||||
} else {
|
||||
assert(newTMSI);
|
||||
DCCH->send(L3LocationUpdatingAccept(gBTS.LAI(),newTMSI));
|
||||
// Wait for MM TMSI REALLOCATION COMPLETE (0x055b).
|
||||
L3Frame* resp = DCCH->recv(1000);
|
||||
// FIXME -- Actually check the response type.
|
||||
if (!resp) {
|
||||
LOG(NOTICE) << "no response to TMSI assignment";
|
||||
} else {
|
||||
LOG(INFO) << *resp;
|
||||
}
|
||||
delete resp;
|
||||
}
|
||||
|
||||
// If this is an IMSI attach, send a welcome message.
|
||||
if (IMSIAttach) {
|
||||
if (success) {
|
||||
sendWelcomeMessage( "Control.LUR.NormalRegistration.Message",
|
||||
"Control.LUR.NormalRegistration.ShortCode", IMSI, DCCH);
|
||||
} else {
|
||||
sendWelcomeMessage( "Control.LUR.OpenRegistration.Message",
|
||||
"Control.LUR.OpenRegistration.ShortCode", IMSI, DCCH);
|
||||
}
|
||||
}
|
||||
|
||||
// Release the channel and return.
|
||||
DCCH->send(L3ChannelRelease());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
39
Control/MobilityManagement.h
Normal file
39
Control/MobilityManagement.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**@file GSM/SIP Mobility Management, GSM 04.08. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MOBILITYMANAGEMENT_H
|
||||
#define MOBILITYMANAGEMENT_H
|
||||
|
||||
|
||||
namespace GSM {
|
||||
class LogicalChannel;
|
||||
class L3CMServiceRequest;
|
||||
class L3LocationUpdatingRequest;
|
||||
class L3IMSIDetachIndication;
|
||||
};
|
||||
|
||||
namespace Control {
|
||||
|
||||
void CMServiceResponder(const GSM::L3CMServiceRequest* cmsrq, GSM::LogicalChannel* DCCH);
|
||||
|
||||
void IMSIDetachController(const GSM::L3IMSIDetachIndication* idi, GSM::LogicalChannel* DCCH);
|
||||
|
||||
void LocationUpdatingController(const GSM::L3LocationUpdatingRequest* lur, GSM::LogicalChannel* DCCH);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
25
Control/README.Control
Normal file
25
Control/README.Control
Normal file
@@ -0,0 +1,25 @@
|
||||
This directory contains control-layer functions for the access point.
|
||||
|
||||
Most GSM L3 and VoIP messages terminate here.
|
||||
|
||||
Everything in this directory should be in the Control namespace.
|
||||
|
||||
Components:
|
||||
|
||||
RadioResource -- Functions for RR procedures (paging, access grant)
|
||||
MobilityManagement -- Functions for MM procedures (CM service, location updating)
|
||||
CallControl -- Functions for CC (mobile originated, mobile terminated)
|
||||
SMS -- Funcitons for text messaging.
|
||||
|
||||
|
||||
|
||||
SIP and RTP UDP/IP Port Assignments
|
||||
|
||||
Component Protocol Port(s)
|
||||
Asterisk SIP 5060
|
||||
Zoiper SIP 5061
|
||||
AP/BTS SIP 5062
|
||||
Zoiper RTP 16384-16385
|
||||
Asterisk RTP 16386-16483
|
||||
AP/BTS RTP 16484-16583
|
||||
|
||||
509
Control/RadioResource.cpp
Normal file
509
Control/RadioResource.cpp
Normal file
@@ -0,0 +1,509 @@
|
||||
/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <list>
|
||||
|
||||
#include "ControlCommon.h"
|
||||
#include "TransactionTable.h"
|
||||
#include "RadioResource.h"
|
||||
#include "SMSControl.h"
|
||||
#include "CallControl.h"
|
||||
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSMConfig.h>
|
||||
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
using namespace Control;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Determine the channel type needed.
|
||||
This is based on GSM 04.08 9.1.8, Table 9.3 and 9.3a.
|
||||
The following is assumed about the global BTS capabilities:
|
||||
- We do not support call reestablishment.
|
||||
- We do not support GPRS.
|
||||
@param RA The request reference from the channel request message.
|
||||
@return channel type code, undefined if not a supported service
|
||||
*/
|
||||
ChannelType decodeChannelNeeded(unsigned RA)
|
||||
{
|
||||
// This code is based on GSM 04.08 Table 9.9.
|
||||
|
||||
unsigned RA4 = RA>>4;
|
||||
unsigned RA5 = RA>>5;
|
||||
|
||||
// Answer to paging, Table 9.9a.
|
||||
// We don't support TCH/H, so it's wither SDCCH or TCH/F.
|
||||
// The spec allows for "SDCCH-only" MS. We won't support that here.
|
||||
// FIXME -- So we probably should not use "any channel" in the paging indications.
|
||||
if (RA5 == 0x04) return TCHFType; // any channel or any TCH.
|
||||
if (RA4 == 0x01) return SDCCHType; // SDCCH
|
||||
if (RA4 == 0x02) return TCHFType; // TCH/F
|
||||
if (RA4 == 0x03) return TCHFType; // TCH/F
|
||||
|
||||
int NECI = gConfig.getNum("GSM.CellSelection.NECI");
|
||||
if (NECI==0) {
|
||||
if (RA5 == 0x07) return SDCCHType; // MOC or SDCCH procedures
|
||||
if (RA5 == 0x00) return SDCCHType; // location updating
|
||||
} else {
|
||||
assert(NECI==1);
|
||||
if (gConfig.defines("Control.VEA")) {
|
||||
// Very Early Assignment
|
||||
if (RA5 == 0x07) return TCHFType; // MOC for TCH/F
|
||||
if (RA4 == 0x04) return TCHFType; // MOC, TCH/H sufficient
|
||||
} else {
|
||||
// Early Assignment
|
||||
if (RA5 == 0x07) return SDCCHType; // MOC for TCH/F
|
||||
if (RA4 == 0x04) return SDCCHType; // MOC, TCH/H sufficient
|
||||
}
|
||||
if (RA4 == 0x00) return SDCCHType; // location updating
|
||||
if (RA4 == 0x01) return SDCCHType; // other procedures on SDCCH
|
||||
}
|
||||
|
||||
// Anything else falls through to here.
|
||||
// We are still ignoring data calls, GPRS, LMU.
|
||||
return UndefinedCHType;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if RA indicates LUR. */
|
||||
bool requestingLUR(unsigned RA)
|
||||
{
|
||||
int NECI = gConfig.getNum("GSM.CellSelection.NECI");
|
||||
if (NECI==0) return ((RA>>5) == 0x00);
|
||||
else return ((RA>>4) == 0x00);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Decode RACH bits and send an immediate assignment; may block waiting for a channel. */
|
||||
void AccessGrantResponder(
|
||||
unsigned RA, const GSM::Time& when,
|
||||
float RSSI, float timingError)
|
||||
{
|
||||
// RR Establishment.
|
||||
// Immediate Assignment procedure, "Answer from the Network"
|
||||
// GSM 04.08 3.3.1.1.3.
|
||||
// Given a request reference, try to allocate a channel
|
||||
// and send the assignment to the handset on the CCCH.
|
||||
// This GSM's version of medium access control.
|
||||
// Papa Legba, open that door...
|
||||
|
||||
// Are we holding off new allocations?
|
||||
if (gBTS.hold()) {
|
||||
LOG(NOTICE) << "ignoring RACH due to BTS hold-off";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check "when" against current clock to see if we're too late.
|
||||
// Calculate maximum number of frames of delay.
|
||||
// See GSM 04.08 3.3.1.1.2 for the logic here.
|
||||
static const unsigned txInteger = gConfig.getNum("GSM.RACH.TxInteger");
|
||||
static const int maxAge = GSM::RACHSpreadSlots[txInteger] + GSM::RACHWaitSParam[txInteger];
|
||||
// Check burst age.
|
||||
int age = gBTS.time() - when;
|
||||
LOG(INFO) << "RA=0x" << hex << RA << dec
|
||||
<< " when=" << when << " age=" << age
|
||||
<< " delay=" << timingError << " RSSI=" << RSSI;
|
||||
if (age>maxAge) {
|
||||
LOG(WARNING) << "ignoring RACH bust with age " << age;
|
||||
gBTS.growT3122()/1000;
|
||||
return;
|
||||
}
|
||||
|
||||
// Screen for delay.
|
||||
if (timingError>gConfig.getNum("GSM.MS.TA.Max")) {
|
||||
LOG(WARNING) << "ignoring RACH burst with delay " << timingError;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get an AGCH to send on.
|
||||
CCCHLogicalChannel *AGCH = gBTS.getAGCH();
|
||||
// Someone had better have created a least one AGCH.
|
||||
assert(AGCH);
|
||||
// Check AGCH load now.
|
||||
if (AGCH->load()>gConfig.getNum("GSM.CCCH.AGCH.QMax")) {
|
||||
LOG(WARNING) "AGCH congestion";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for location update.
|
||||
// This gives LUR a lower priority than other services.
|
||||
if (requestingLUR(RA)) {
|
||||
if (gBTS.SDCCHAvailable()<=gConfig.getNum("GSM.CCCH.PCH.Reserve")) {
|
||||
unsigned waitTime = gBTS.growT3122()/1000;
|
||||
LOG(WARNING) << "LUR congestion, RA=" << RA << " T3122=" << waitTime;
|
||||
const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime);
|
||||
LOG(DEBUG) << "LUR rejection, sending " << reject;
|
||||
AGCH->send(reject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the channel according to the needed type indicated by RA.
|
||||
// The returned channel is already open and ready for the transaction.
|
||||
LogicalChannel *LCH = NULL;
|
||||
switch (decodeChannelNeeded(RA)) {
|
||||
case TCHFType: LCH = gBTS.getTCH(); break;
|
||||
case SDCCHType: LCH = gBTS.getSDCCH(); break;
|
||||
// If we don't support the service, assign to an SDCCH and we can reject it in L3.
|
||||
case UndefinedCHType:
|
||||
LOG(NOTICE) << "RACH burst for unsupported service RA=" << RA;
|
||||
LCH = gBTS.getSDCCH();
|
||||
break;
|
||||
// We should never be here.
|
||||
default: assert(0);
|
||||
}
|
||||
|
||||
// Nothing available?
|
||||
if (!LCH) {
|
||||
// Rejection, GSM 04.08 3.3.1.1.3.2.
|
||||
// But since we recognize SOS calls already,
|
||||
// we might as well save some AGCH bandwidth.
|
||||
unsigned waitTime = gBTS.growT3122()/1000;
|
||||
LOG(WARNING) << "congestion, RA=" << RA << " T3122=" << waitTime;
|
||||
const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime);
|
||||
LOG(DEBUG) << "rejection, sending " << reject;
|
||||
AGCH->send(reject);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the channel physical parameters from the RACH burst.
|
||||
LCH->setPhy(RSSI,timingError);
|
||||
|
||||
// Assignment, GSM 04.08 3.3.1.1.3.1.
|
||||
// Create the ImmediateAssignment message.
|
||||
// Woot!! We got a channel! Thanks to Legba!
|
||||
int initialTA = (int)(timingError + 0.5F);
|
||||
if (initialTA<0) initialTA=0;
|
||||
if (initialTA>62) initialTA=62;
|
||||
const L3ImmediateAssignment assign(
|
||||
L3RequestReference(RA,when),
|
||||
LCH->channelDescription(),
|
||||
L3TimingAdvance(initialTA)
|
||||
);
|
||||
LOG(INFO) << "sending " << assign;
|
||||
AGCH->send(assign);
|
||||
|
||||
// On successful allocation, shrink T3122.
|
||||
gBTS.shrinkT3122();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* Control::AccessGrantServiceLoop(void*)
|
||||
{
|
||||
while (true) {
|
||||
ChannelRequestRecord *req = gBTS.nextChannelRequest();
|
||||
if (!req) continue;
|
||||
AccessGrantResponder(
|
||||
req->RA(), req->frame(),
|
||||
req->RSSI(), req->timingError()
|
||||
);
|
||||
delete req;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel* DCCH)
|
||||
{
|
||||
assert(resp);
|
||||
assert(DCCH);
|
||||
LOG(INFO) << *resp;
|
||||
|
||||
// If we got a TMSI, find the IMSI.
|
||||
L3MobileIdentity mobileID = resp->mobileID();
|
||||
if (mobileID.type()==TMSIType) {
|
||||
char *IMSI = gTMSITable.IMSI(mobileID.TMSI());
|
||||
if (IMSI) {
|
||||
mobileID = L3MobileIdentity(IMSI);
|
||||
free(IMSI);
|
||||
} else {
|
||||
// Don't try too hard to resolve.
|
||||
// The handset is supposed to respond with the same ID type as in the request.
|
||||
// This could be the sign of some kind of DOS attack.
|
||||
LOG(CRIT) << "Paging Reponse with non-valid TMSI";
|
||||
// Cause 0x60 "Invalid mandatory information"
|
||||
DCCH->send(L3ChannelRelease(0x60));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the Mobile ID from the paging list to free up CCCH bandwidth.
|
||||
// ... if it was not deleted by a timer already ...
|
||||
gBTS.pager().removeID(mobileID);
|
||||
|
||||
// Find the transction table entry that was created when the phone was paged.
|
||||
// We have to look up by mobile ID since the paging entry may have been
|
||||
// erased before this handler was called. That's too bad.
|
||||
// HACK -- We also flush stray transactions until we find what we
|
||||
// are looking for.
|
||||
TransactionEntry* transaction = gTransactionTable.answeredPaging(mobileID);
|
||||
if (!transaction) {
|
||||
LOG(WARNING) << "Paging Reponse with no transaction record for " << mobileID;
|
||||
// Cause 0x41 means "call already cleared".
|
||||
DCCH->send(L3ChannelRelease(0x41));
|
||||
return;
|
||||
}
|
||||
// Set the transaction channel.
|
||||
transaction->channel(DCCH);
|
||||
// We are looking for a mobile-terminated transaction.
|
||||
// The transaction controller will take it from here.
|
||||
switch (transaction->service().type()) {
|
||||
case L3CMServiceType::MobileTerminatedCall:
|
||||
MTCStarter(transaction, DCCH);
|
||||
return;
|
||||
case L3CMServiceType::MobileTerminatedShortMessage:
|
||||
MTSMSController(transaction, DCCH);
|
||||
return;
|
||||
default:
|
||||
// Flush stray MOC entries.
|
||||
// There should not be any, but...
|
||||
LOG(ERR) << "non-valid paging-state transaction: " << *transaction;
|
||||
gTransactionTable.remove(transaction);
|
||||
// FIXME -- Send a channel release on the DCCH.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Control::AssignmentCompleteHandler(const L3AssignmentComplete *confirm, TCHFACCHLogicalChannel *TCH)
|
||||
{
|
||||
// The assignment complete handler is used to
|
||||
// tie together split transactions across a TCH assignment
|
||||
// in non-VEA call setup.
|
||||
|
||||
assert(TCH);
|
||||
assert(confirm);
|
||||
LOG(DEBUG) << *confirm;
|
||||
|
||||
// Check the transaction table to know what to do next.
|
||||
TransactionEntry* transaction = gTransactionTable.find(TCH);
|
||||
if (!transaction) {
|
||||
LOG(WARNING) << "No transaction matching channel " << *TCH << " (" << TCH << ").";
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
LOG(INFO) << "service="<<transaction->service().type();
|
||||
|
||||
// These "controller" functions don't return until the call is cleared.
|
||||
switch (transaction->service().type()) {
|
||||
case L3CMServiceType::MobileOriginatedCall:
|
||||
MOCController(transaction,TCH);
|
||||
break;
|
||||
case L3CMServiceType::MobileTerminatedCall:
|
||||
MTCController(transaction,TCH);
|
||||
break;
|
||||
default:
|
||||
LOG(WARNING) << "unsupported service " << transaction->service();
|
||||
throw UnsupportedMessage(transaction->ID());
|
||||
}
|
||||
// If we got here, the call is cleared.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Pager::addID(const L3MobileIdentity& newID, ChannelType chanType,
|
||||
TransactionEntry& transaction, unsigned wLife)
|
||||
{
|
||||
transaction.GSMState(GSM::Paging);
|
||||
transaction.setTimer("3113",wLife);
|
||||
// Add a mobile ID to the paging list for a given lifetime.
|
||||
ScopedLock lock(mLock);
|
||||
// If this ID is already in the list, just reset its timer.
|
||||
// Uhg, another linear time search.
|
||||
// This would be faster if the paging list were ordered by ID.
|
||||
// But the list should usually be short, so it may not be worth the effort.
|
||||
for (PagingEntryList::iterator lp = mPageIDs.begin(); lp != mPageIDs.end(); ++lp) {
|
||||
if (lp->ID()==newID) {
|
||||
LOG(DEBUG) << newID << " already in table";
|
||||
lp->renew(wLife);
|
||||
mPageSignal.signal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If this ID is new, put it in the list.
|
||||
mPageIDs.push_back(PagingEntry(newID,chanType,transaction.ID(),wLife));
|
||||
LOG(INFO) << newID << " added to table";
|
||||
mPageSignal.signal();
|
||||
}
|
||||
|
||||
|
||||
unsigned Pager::removeID(const L3MobileIdentity& delID)
|
||||
{
|
||||
// Return the associated transaction ID, or 0 if none found.
|
||||
LOG(INFO) << delID;
|
||||
ScopedLock lock(mLock);
|
||||
for (PagingEntryList::iterator lp = mPageIDs.begin(); lp != mPageIDs.end(); ++lp) {
|
||||
if (lp->ID()==delID) {
|
||||
unsigned retVal = lp->transactionID();
|
||||
mPageIDs.erase(lp);
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned Pager::pageAll()
|
||||
{
|
||||
// Traverse the full list and page all IDs.
|
||||
// Remove expired IDs.
|
||||
// Return the number of IDs paged.
|
||||
// This is a linear time operation.
|
||||
|
||||
ScopedLock lock(mLock);
|
||||
|
||||
// Clear expired entries.
|
||||
PagingEntryList::iterator lp = mPageIDs.begin();
|
||||
while (lp != mPageIDs.end()) {
|
||||
if (!lp->expired()) ++lp;
|
||||
else {
|
||||
LOG(INFO) << "erasing " << lp->ID();
|
||||
// Non-responsive, dead transaction?
|
||||
gTransactionTable.removePaging(lp->transactionID());
|
||||
// remove from the list
|
||||
lp=mPageIDs.erase(lp);
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "paging " << mPageIDs.size() << " mobile(s)";
|
||||
|
||||
// Page remaining entries, two at a time if possible.
|
||||
// These PCH send operations are non-blocking.
|
||||
lp = mPageIDs.begin();
|
||||
while (lp != mPageIDs.end()) {
|
||||
// FIXME -- This completely ignores the paging groups.
|
||||
// HACK -- So we send every page twice.
|
||||
// That will probably mean a different Pager for each subchannel.
|
||||
// See GSM 04.08 10.5.2.11 and GSM 05.02 6.5.2.
|
||||
const L3MobileIdentity& id1 = lp->ID();
|
||||
ChannelType type1 = lp->type();
|
||||
++lp;
|
||||
if (lp==mPageIDs.end()) {
|
||||
// Just one ID left?
|
||||
LOG(DEBUG) << "paging " << id1;
|
||||
gBTS.getPCH(0)->send(L3PagingRequestType1(id1,type1));
|
||||
gBTS.getPCH(0)->send(L3PagingRequestType1(id1,type1));
|
||||
break;
|
||||
}
|
||||
// Page by pairs when possible.
|
||||
const L3MobileIdentity& id2 = lp->ID();
|
||||
ChannelType type2 = lp->type();
|
||||
++lp;
|
||||
LOG(DEBUG) << "paging " << id1 << " and " << id2;
|
||||
gBTS.getPCH(0)->send(L3PagingRequestType1(id1,type1,id2,type2));
|
||||
gBTS.getPCH(0)->send(L3PagingRequestType1(id1,type1,id2,type2));
|
||||
}
|
||||
|
||||
return mPageIDs.size();
|
||||
}
|
||||
|
||||
size_t Pager::pagingEntryListSize()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mPageIDs.size();
|
||||
}
|
||||
|
||||
void Pager::start()
|
||||
{
|
||||
if (mRunning) return;
|
||||
mRunning=true;
|
||||
mPagingThread.start((void* (*)(void*))PagerServiceLoopAdapter, (void*)this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* Control::PagerServiceLoopAdapter(Pager *pager)
|
||||
{
|
||||
pager->serviceLoop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Pager::serviceLoop()
|
||||
{
|
||||
while (mRunning) {
|
||||
|
||||
LOG(DEBUG) << "Pager blocking for signal";
|
||||
mLock.lock();
|
||||
while (mPageIDs.size()==0) mPageSignal.wait(mLock);
|
||||
mLock.unlock();
|
||||
|
||||
// page everything
|
||||
pageAll();
|
||||
|
||||
// Wait for pending activity to clear the channel.
|
||||
// This wait is what causes PCH to have lower priority than AGCH.
|
||||
unsigned load = gBTS.getPCH()->load();
|
||||
LOG(DEBUG) << "Pager waiting for " << load << " multiframes";
|
||||
if (load) sleepFrames(51*load);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Pager::dump(ostream& os) const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
PagingEntryList::const_iterator lp = mPageIDs.begin();
|
||||
while (lp != mPageIDs.end()) {
|
||||
os << lp->ID() << " " << lp->type() << " " << lp->expired() << endl;
|
||||
++lp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
214
Control/RadioResource.h
Normal file
214
Control/RadioResource.h
Normal file
@@ -0,0 +1,214 @@
|
||||
/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RADIORESOURCE_H
|
||||
#define RADIORESOURCE_H
|
||||
|
||||
#include <list>
|
||||
#include <GSML3CommonElements.h>
|
||||
|
||||
|
||||
namespace GSM {
|
||||
class Time;
|
||||
class TCHFACCHLogicalChannel;
|
||||
class L3PagingResponse;
|
||||
class L3AssignmentComplete;
|
||||
};
|
||||
|
||||
namespace Control {
|
||||
|
||||
class TransactionEntry;
|
||||
|
||||
|
||||
|
||||
|
||||
/** Find and compelte the in-process transaction associated with a paging repsonse. */
|
||||
void PagingResponseHandler(const GSM::L3PagingResponse*, GSM::LogicalChannel*);
|
||||
|
||||
/** Find and compelte the in-process transaction associated with a completed assignment. */
|
||||
void AssignmentCompleteHandler(const GSM::L3AssignmentComplete*, GSM::TCHFACCHLogicalChannel*);
|
||||
|
||||
|
||||
/**@ Access Grant mechanisms */
|
||||
//@{
|
||||
|
||||
|
||||
/** Decode RACH bits and send an immediate assignment; may block waiting for a channel. */
|
||||
//void AccessGrantResponder(
|
||||
// unsigned requestReference, const GSM::Time& when,
|
||||
// float RSSI, float timingError);
|
||||
|
||||
|
||||
/** This record carries all of the parameters associated with a RACH burst. */
|
||||
class ChannelRequestRecord {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mRA; ///< request reference
|
||||
GSM::Time mFrame; ///< receive timestamp
|
||||
float mRSSI; ///< dB wrt full scale
|
||||
float mTimingError; ///< correlator timing error in symbol periods
|
||||
|
||||
public:
|
||||
|
||||
ChannelRequestRecord(
|
||||
unsigned wRA, const GSM::Time& wFrame,
|
||||
float wRSSI, float wTimingError)
|
||||
:mRA(wRA), mFrame(wFrame),
|
||||
mRSSI(wRSSI), mTimingError(wTimingError)
|
||||
{ }
|
||||
|
||||
unsigned RA() const { return mRA; }
|
||||
const GSM::Time& frame() const { return mFrame; }
|
||||
float RSSI() const { return mRSSI; }
|
||||
float timingError() const { return mTimingError; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** A thread to process contents of the channel request queue. */
|
||||
void* AccessGrantServiceLoop(void*);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
/**@ Paging mechanisms */
|
||||
//@{
|
||||
|
||||
/** An entry in the paging list. */
|
||||
class PagingEntry {
|
||||
|
||||
private:
|
||||
|
||||
GSM::L3MobileIdentity mID; ///< The mobile ID.
|
||||
GSM::ChannelType mType; ///< The needed channel type.
|
||||
unsigned mTransactionID; ///< The associated transaction ID.
|
||||
Timeval mExpiration; ///< The expiration time for this entry.
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Create a new entry, with current timestamp.
|
||||
@param wID The ID to be paged.
|
||||
@param wLife The number of milliseconds to keep paging.
|
||||
*/
|
||||
PagingEntry(const GSM::L3MobileIdentity& wID, GSM::ChannelType wType,
|
||||
unsigned wTransactionID, unsigned wLife)
|
||||
:mID(wID),mType(wType),mTransactionID(wTransactionID),mExpiration(wLife)
|
||||
{}
|
||||
|
||||
/** Access the ID. */
|
||||
const GSM::L3MobileIdentity& ID() const { return mID; }
|
||||
|
||||
/** Access the channel type needed. */
|
||||
GSM::ChannelType type() const { return mType; }
|
||||
|
||||
unsigned transactionID() const { return mTransactionID; }
|
||||
|
||||
/** Renew the timer. */
|
||||
void renew(unsigned wLife) { mExpiration = Timeval(wLife); }
|
||||
|
||||
/** Returns true if the entry is expired. */
|
||||
bool expired() const { return mExpiration.passed(); }
|
||||
|
||||
};
|
||||
|
||||
typedef std::list<PagingEntry> PagingEntryList;
|
||||
|
||||
|
||||
/**
|
||||
The pager is a global object that generates paging messages on the CCCH.
|
||||
To page a mobile, add the mobile ID to the pager.
|
||||
The entry will be deleted automatically when it expires.
|
||||
All pager operations are linear time.
|
||||
Not much point in optimizing since the main operation is inherently linear.
|
||||
*/
|
||||
class Pager {
|
||||
|
||||
private:
|
||||
|
||||
PagingEntryList mPageIDs; ///< List of ID's to be paged.
|
||||
mutable Mutex mLock; ///< Lock for thread-safe access.
|
||||
Signal mPageSignal; ///< signal to wake the paging loop
|
||||
Thread mPagingThread; ///< Thread for the paging loop.
|
||||
volatile bool mRunning;
|
||||
|
||||
public:
|
||||
|
||||
Pager()
|
||||
:mRunning(false)
|
||||
{}
|
||||
|
||||
/** Set the output FIFO and start the paging loop. */
|
||||
void start();
|
||||
|
||||
/**
|
||||
Add a mobile ID to the paging list.
|
||||
@param addID The mobile ID to be paged.
|
||||
@param chanType The channel type to be requested.
|
||||
@param transaction The transaction record, which will be modified.
|
||||
@param wLife The paging duration in ms, default is SIP Timer B.
|
||||
*/
|
||||
void addID(
|
||||
const GSM::L3MobileIdentity& addID,
|
||||
GSM::ChannelType chanType,
|
||||
TransactionEntry& transaction,
|
||||
unsigned wLife=gConfig.getNum("SIP.Timer.B")
|
||||
);
|
||||
|
||||
/**
|
||||
Remove a mobile ID.
|
||||
This is used to stop the paging when a phone responds.
|
||||
@return The transaction ID associated with this entry.
|
||||
*/
|
||||
unsigned removeID(const GSM::L3MobileIdentity&);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
Traverse the paging list, paging all IDs.
|
||||
@return Number of IDs paged.
|
||||
*/
|
||||
unsigned pageAll();
|
||||
|
||||
/** A loop that repeatedly calls pageAll. */
|
||||
void serviceLoop();
|
||||
|
||||
/** C-style adapter. */
|
||||
friend void *PagerServiceLoopAdapter(Pager*);
|
||||
|
||||
public:
|
||||
|
||||
/** return size of PagingEntryList */
|
||||
size_t pagingEntryListSize();
|
||||
|
||||
/** Dump the paging list to an ostream. */
|
||||
void dump(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
void *PagerServiceLoopAdapter(Pager*);
|
||||
|
||||
|
||||
//@} // paging mech
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
584
Control/SMSControl.cpp
Normal file
584
Control/SMSControl.cpp
Normal file
@@ -0,0 +1,584 @@
|
||||
/**@file SMS Control (L3), GSM 03.40, 04.11. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Abbreviations:
|
||||
MOSMS -- Mobile Originated Short Message Service
|
||||
MTSMS -- Mobile Terminated Short Message Service
|
||||
|
||||
Verbs:
|
||||
"send" -- Transfer to the network.
|
||||
"receive" -- Transfer from the network.
|
||||
"submit" -- Transfer from the MS.
|
||||
"deliver" -- Transfer to the MS.
|
||||
|
||||
MOSMS: The MS "submits" a message that OpenBTS then "sends" to the network.
|
||||
MTSMS: OpenBTS "receives" a message from the network that it then "delivers" to the MS.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSML3MMMessages.h>
|
||||
#include "SMSControl.h"
|
||||
#include "ControlCommon.h"
|
||||
#include "TransactionTable.h"
|
||||
#include <Regexp.h>
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace Control;
|
||||
|
||||
#include "SMSMessages.h"
|
||||
using namespace SMS;
|
||||
|
||||
#include "SIPInterface.h"
|
||||
#include "SIPUtility.h"
|
||||
#include "SIPMessage.h"
|
||||
#include "SIPEngine.h"
|
||||
using namespace SIP;
|
||||
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
/**
|
||||
Read an L3Frame from SAP3.
|
||||
Throw exception on failure. Will NOT return a NULL pointer.
|
||||
*/
|
||||
GSM::L3Frame* getFrameSMS(GSM::LogicalChannel *LCH, GSM::Primitive primitive=GSM::DATA)
|
||||
{
|
||||
GSM::L3Frame *retVal = LCH->recv(LCH->N200()*LCH->T200(),3);
|
||||
if (!retVal) {
|
||||
LOG(NOTICE) << "channel read time out on " << *LCH << " SAP3";
|
||||
throw ChannelReadTimeout();
|
||||
}
|
||||
LOG(DEBUG) << "getFrameSMS on " << *LCH << " in frame " << *retVal;
|
||||
if (retVal->primitive() != primitive) {
|
||||
LOG(NOTICE) << "unexpected primitive on " << *LCH << ", expecting " << primitive << ", got " << *retVal;
|
||||
throw UnexpectedPrimitive();
|
||||
}
|
||||
if ((retVal->primitive() == GSM::DATA) && (retVal->PD() != GSM::L3SMSPD)) {
|
||||
LOG(NOTICE) << "unexpected (non-SMS) protocol on " << *LCH << " in frame " << *retVal;
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
bool sendSIP(TransactionEntry *transaction, const char* address, const char* body, const char* contentType)
|
||||
{
|
||||
// Steps:
|
||||
// 1 -- Complete transaction record.
|
||||
// 2 -- Send TL-SUBMIT to the server.
|
||||
// 3 -- Wait for response or timeout.
|
||||
// 4 -- Return true for OK or ACCEPTED, false otherwise.
|
||||
|
||||
// Step 1 -- Complete the transaction record.
|
||||
// Form the TLAddress into a CalledPartyNumber for the transaction.
|
||||
GSM::L3CalledPartyBCDNumber calledParty(address);
|
||||
// Attach calledParty and message body to the transaction.
|
||||
transaction->called(calledParty);
|
||||
transaction->message(body,strlen(body));
|
||||
|
||||
// Step 2 -- Send the message to the server.
|
||||
transaction->MOSMSSendMESSAGE(address,gConfig.getStr("SIP.Local.IP").c_str(),contentType);
|
||||
|
||||
// Step 3 -- Wait for OK or ACCEPTED.
|
||||
SIPState state = transaction->MOSMSWaitForSubmit();
|
||||
|
||||
// Step 4 -- Done
|
||||
return state==SIP::Cleared;
|
||||
}
|
||||
|
||||
/**
|
||||
Process the RPDU.
|
||||
@param mobileID The sender's IMSI.
|
||||
@param RPDU The RPDU to process.
|
||||
@return true if successful.
|
||||
*/
|
||||
bool handleRPDU(TransactionEntry *transaction, const RLFrame& RPDU)
|
||||
{
|
||||
LOG(DEBUG) << "SMS: handleRPDU MTI=" << RPDU.MTI();
|
||||
switch ((RPMessage::MessageType)RPDU.MTI()) {
|
||||
case RPMessage::Data: {
|
||||
string contentType = gConfig.getStr("SMS.MIMEType");
|
||||
ostringstream body;
|
||||
|
||||
if (contentType == "text/plain") {
|
||||
// TODO: Clean this mess up!
|
||||
RPData data;
|
||||
data.parse(RPDU);
|
||||
TLSubmit submit;
|
||||
submit.parse(data.TPDU());
|
||||
|
||||
body << submit.UD().decode();
|
||||
} else if (contentType == "application/vnd.3gpp.sms") {
|
||||
RPDU.hex(body);
|
||||
} else {
|
||||
LOG(ALERT) << "\"" << contentType << "\" is not a valid SMS payload type";
|
||||
}
|
||||
const char* address = NULL;
|
||||
if (gConfig.defines("SIP.SMSC")) address = gConfig.getStr("SIP.SMSC").c_str();
|
||||
|
||||
/* The SMSC is not defined, we are using an older version */
|
||||
if (address == NULL) {
|
||||
RPData data;
|
||||
data.parse(RPDU);
|
||||
TLSubmit submit;
|
||||
submit.parse(data.TPDU());
|
||||
|
||||
address = submit.DA().digits();
|
||||
}
|
||||
return sendSIP(transaction, address, body.str().data(),contentType.c_str());
|
||||
}
|
||||
case RPMessage::Ack:
|
||||
case RPMessage::SMMA:
|
||||
return true;
|
||||
case RPMessage::Error:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Control::MOSMSController(const GSM::L3CMServiceRequest *req, GSM::LogicalChannel *LCH)
|
||||
{
|
||||
assert(req);
|
||||
assert(req->serviceType().type() == GSM::L3CMServiceType::ShortMessage);
|
||||
assert(LCH);
|
||||
assert(LCH->type() != GSM::SACCHType);
|
||||
|
||||
LOG(INFO) << "MOSMS, req " << *req;
|
||||
|
||||
// If we got a TMSI, find the IMSI.
|
||||
// Note that this is a copy, not a reference.
|
||||
GSM::L3MobileIdentity mobileID = req->mobileID();
|
||||
resolveIMSI(mobileID,LCH);
|
||||
|
||||
// Create a transaction record.
|
||||
TransactionEntry *transaction = new TransactionEntry(gConfig.getStr("SIP.Proxy.SMS").c_str(),mobileID,LCH);
|
||||
gTransactionTable.add(transaction);
|
||||
LOG(DEBUG) << "MOSMS: transaction: " << *transaction;
|
||||
|
||||
// See GSM 04.11 Arrow Diagram A5 for the transaction
|
||||
// Step 1 MS->Network CP-DATA containing RP-DATA
|
||||
// Step 2 Network->MS CP-ACK
|
||||
// Step 3 Network->MS CP-DATA containing RP-ACK
|
||||
// Step 4 MS->Network CP-ACK
|
||||
|
||||
// LAPDm operation, from GSM 04.11, Annex F:
|
||||
// """
|
||||
// Case A: Mobile originating short message transfer, no parallel call:
|
||||
// The mobile station side will initiate SAPI 3 establishment by a SABM command
|
||||
// on the DCCH after the cipher mode has been set. If no hand over occurs, the
|
||||
// SAPI 3 link will stay up until the last CP-ACK is received by the MSC, and
|
||||
// the clearing procedure is invoked.
|
||||
// """
|
||||
|
||||
// FIXME: check provisioning
|
||||
|
||||
// Let the phone know we're going ahead with the transaction.
|
||||
LOG(INFO) << "sending CMServiceAccept";
|
||||
LCH->send(GSM::L3CMServiceAccept());
|
||||
// Wait for SAP3 to connect.
|
||||
// The first read on SAP3 is the ESTABLISH primitive.
|
||||
delete getFrameSMS(LCH,GSM::ESTABLISH);
|
||||
|
||||
// Step 1
|
||||
// Now get the first message.
|
||||
// Should be CP-DATA, containing RP-DATA.
|
||||
GSM::L3Frame *CM = getFrameSMS(LCH);
|
||||
LOG(DEBUG) << "data from MS " << *CM;
|
||||
if (CM->MTI()!=CPMessage::DATA) {
|
||||
LOG(NOTICE) << "unexpected SMS CP message with TI=" << CM->MTI();
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
unsigned L3TI = CM->TI() | 0x08;
|
||||
transaction->L3TI(L3TI);
|
||||
|
||||
// Step 2
|
||||
// Respond with CP-ACK.
|
||||
// This just means that we got the message.
|
||||
LOG(INFO) << "sending CPAck";
|
||||
LCH->send(CPAck(L3TI),3);
|
||||
|
||||
// Parse the message in CM and process RP part.
|
||||
// This is where we actually parse the message and send it out.
|
||||
// FIXME -- We need to set the message ref correctly,
|
||||
// even if the parsing fails.
|
||||
// The compiler gives a warning here. Let it. It will remind someone to fix it.
|
||||
unsigned ref;
|
||||
bool success = false;
|
||||
try {
|
||||
CPData data;
|
||||
data.parse(*CM);
|
||||
delete CM;
|
||||
LOG(INFO) << "CPData " << data;
|
||||
// Transfer out the RPDU -> TPDU -> delivery.
|
||||
ref = data.RPDU().reference();
|
||||
// This handler invokes higher-layer parsers, too.
|
||||
success = handleRPDU(transaction,data.RPDU());
|
||||
}
|
||||
catch (SMSReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (above L3)";
|
||||
// Cause 95, "semantically incorrect message".
|
||||
LCH->send(CPData(L3TI,RPError(95,ref)),3);
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
catch (GSM::L3ReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (in L3)";
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
|
||||
// Step 3
|
||||
// Send CP-DATA containing RP-ACK and message reference.
|
||||
if (success) {
|
||||
LOG(INFO) << "sending RPAck in CPData";
|
||||
LCH->send(CPData(L3TI,RPAck(ref)),3);
|
||||
} else {
|
||||
LOG(INFO) << "sending RPError in CPData";
|
||||
// Cause 127 is "internetworking error, unspecified".
|
||||
// See GSM 04.11 Table 8.4.
|
||||
LCH->send(CPData(L3TI,RPError(127,ref)),3);
|
||||
}
|
||||
|
||||
// Step 4
|
||||
// Get CP-ACK from the MS.
|
||||
CM = getFrameSMS(LCH);
|
||||
if (CM->MTI()!=CPMessage::ACK) {
|
||||
LOG(NOTICE) << "unexpected SMS CP message with TI=" << CM->MTI();
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
LOG(DEBUG) << "ack from MS: " << *CM;
|
||||
CPAck ack;
|
||||
ack.parse(*CM);
|
||||
LOG(INFO) << "CPAck " << ack;
|
||||
|
||||
// Done.
|
||||
LCH->send(GSM::L3ChannelRelease());
|
||||
gTransactionTable.remove(transaction);
|
||||
LOG(INFO) << "closing the Um channel";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Control::deliverSMSToMS(const char *callingPartyDigits, const char* message, const char* contentType, unsigned L3TI, GSM::LogicalChannel *LCH)
|
||||
{
|
||||
if (!LCH->multiframeMode(3)) {
|
||||
// Start ABM in SAP3.
|
||||
LCH->send(GSM::ESTABLISH,3);
|
||||
// Wait for SAP3 ABM to connect.
|
||||
// The next read on SAP3 should the ESTABLISH primitive.
|
||||
// This won't return NULL. It will throw an exception if it fails.
|
||||
delete getFrameSMS(LCH,GSM::ESTABLISH);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// HACK -- Check for "Easter Eggs"
|
||||
// TL-PID
|
||||
unsigned TLPID=0;
|
||||
if (strncmp(message,"#!TLPID",7)==0) sscanf(message,"#!TLPID%d",&TLPID);
|
||||
|
||||
// Step 1
|
||||
// Send the first message.
|
||||
// CP-DATA, containing RP-DATA.
|
||||
unsigned reference = random() % 255;
|
||||
CPData deliver(L3TI,
|
||||
RPData(reference,
|
||||
RPAddress(gConfig.getStr("SMS.FakeSrcSMSC").c_str()),
|
||||
TLDeliver(callingPartyDigits,message,TLPID)));
|
||||
#else
|
||||
// TODO: Read MIME Type from smqueue!!
|
||||
unsigned reference = random() % 255;
|
||||
RPData rp_data;
|
||||
|
||||
if (strncmp(contentType,"text/plain",10)==0) {
|
||||
rp_data = RPData(reference,
|
||||
RPAddress(gConfig.getStr("SMS.FakeSrcSMSC").c_str()),
|
||||
TLDeliver(callingPartyDigits,message,0));
|
||||
} else if (strncmp(contentType,"application/vnd.3gpp.sms",24)==0) {
|
||||
BitVector RPDUbits(strlen(message)*4);
|
||||
if (!RPDUbits.unhex(message)) {
|
||||
LOG(WARNING) << "Hex string parsing failed (in incoming SIP MESSAGE)";
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
|
||||
try {
|
||||
RLFrame RPDU(RPDUbits);
|
||||
LOG(DEBUG) << "SMS RPDU: " << RPDU;
|
||||
|
||||
rp_data.parse(RPDU);
|
||||
LOG(DEBUG) << "SMS RP-DATA " << rp_data;
|
||||
}
|
||||
catch (SMSReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (above L3)";
|
||||
// Cause 95, "semantically incorrect message".
|
||||
LCH->send(CPData(L3TI,RPError(95,reference)),3);
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
catch (GSM::L3ReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (in L3)";
|
||||
// TODO:: send error back to the phone
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "Unsupported content type (in incoming SIP MESSAGE) -- type: " << contentType;
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
|
||||
CPData deliver(L3TI,rp_data);
|
||||
|
||||
#endif
|
||||
|
||||
// Start ABM in SAP3.
|
||||
//LCH->send(GSM::ESTABLISH,3);
|
||||
// Wait for SAP3 ABM to connect.
|
||||
// The next read on SAP3 should the ESTABLISH primitive.
|
||||
// This won't return NULL. It will throw an exception if it fails.
|
||||
//delete getFrameSMS(LCH,GSM::ESTABLISH);
|
||||
|
||||
LOG(INFO) << "sending " << deliver;
|
||||
LCH->send(deliver,3);
|
||||
|
||||
// Step 2
|
||||
// Get the CP-ACK.
|
||||
// FIXME -- Check TI.
|
||||
LOG(DEBUG) << "MTSMS: waiting for CP-ACK";
|
||||
GSM::L3Frame *CM = getFrameSMS(LCH);
|
||||
LOG(DEBUG) << "MTSMS: ack from MS " << *CM;
|
||||
if (CM->MTI()!=CPMessage::ACK) {
|
||||
LOG(WARNING) << "MS rejected our RP-DATA with CP message with TI=" << CM->MTI();
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
|
||||
// Step 3
|
||||
// Get CP-DATA containing RP-ACK and message reference.
|
||||
LOG(DEBUG) << "MTSMS: waiting for RP-ACK";
|
||||
CM = getFrameSMS(LCH);
|
||||
LOG(DEBUG) << "MTSMS: data from MS " << *CM;
|
||||
if (CM->MTI()!=CPMessage::DATA) {
|
||||
LOG(NOTICE) << "Unexpected SMS CP message with TI=" << CM->MTI();
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
|
||||
// FIXME -- Check L3 TI.
|
||||
|
||||
// Parse to check for RP-ACK.
|
||||
CPData data;
|
||||
try {
|
||||
data.parse(*CM);
|
||||
delete CM;
|
||||
LOG(DEBUG) << "CPData " << data;
|
||||
}
|
||||
catch (SMSReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (above L3)";
|
||||
// Cause 95, "semantically incorrect message".
|
||||
LCH->send(CPError(L3TI,95),3);
|
||||
throw UnexpectedMessage();
|
||||
}
|
||||
catch (GSM::L3ReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (in L3)";
|
||||
throw UnsupportedMessage();
|
||||
}
|
||||
|
||||
// FIXME -- Check SMS reference.
|
||||
|
||||
bool success = true;
|
||||
if (data.RPDU().MTI()!=RPMessage::Ack) {
|
||||
LOG(WARNING) << "unexpected RPDU " << data.RPDU();
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Step 4
|
||||
// Send CP-ACK to the MS.
|
||||
LOG(INFO) << "MTSMS: sending CPAck";
|
||||
LCH->send(CPAck(L3TI),3);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Control::MTSMSController(TransactionEntry *transaction, GSM::LogicalChannel *LCH)
|
||||
{
|
||||
assert(LCH);
|
||||
assert(transaction);
|
||||
|
||||
// See GSM 04.11 Arrow Diagram A5 for the transaction
|
||||
// Step 1 Network->MS CP-DATA containing RP-DATA
|
||||
// Step 2 MS->Network CP-ACK
|
||||
// Step 3 MS->Network CP-DATA containing RP-ACK
|
||||
// Step 4 Network->MS CP-ACK
|
||||
|
||||
// LAPDm operation, from GSM 04.11, Annex F:
|
||||
// """
|
||||
// Case B: Mobile terminating short message transfer, no parallel call:
|
||||
// The network side, i.e. the BSS will initiate SAPI3 establishment by a
|
||||
// SABM command on the DCCH when the first CP-Data message is received
|
||||
// from the MSC. If no hand over occurs, the link will stay up until the
|
||||
// MSC has given the last CP-ack and invokes the clearing procedure.
|
||||
// """
|
||||
|
||||
|
||||
// Attach the channel to the transaction and update the state.
|
||||
LOG(DEBUG) << "transaction: "<< *transaction;
|
||||
transaction->channel(LCH);
|
||||
transaction->GSMState(GSM::SMSDelivering);
|
||||
LOG(INFO) << "transaction: "<< *transaction;
|
||||
|
||||
bool success = deliverSMSToMS(transaction->calling().digits(),transaction->message(),
|
||||
transaction->messageType(),transaction->L3TI(),LCH);
|
||||
|
||||
// Close the Dm channel?
|
||||
if (LCH->type()!=GSM::SACCHType) {
|
||||
LCH->send(GSM::L3ChannelRelease());
|
||||
LOG(INFO) << "closing the Um channel";
|
||||
}
|
||||
|
||||
// Ack in SIP domain.
|
||||
if (success) transaction->MTSMSSendOK();
|
||||
|
||||
// Done.
|
||||
gTransactionTable.remove(transaction);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Control::InCallMOSMSStarter(TransactionEntry *parallelCall)
|
||||
{
|
||||
GSM::LogicalChannel *hostChan = parallelCall->channel();
|
||||
assert(hostChan);
|
||||
GSM::LogicalChannel *SACCH = hostChan->SACCH();
|
||||
assert(SACCH);
|
||||
|
||||
// Create a partial transaction record.
|
||||
TransactionEntry *newTransaction = new TransactionEntry(
|
||||
gConfig.getStr("SIP.Proxy.SMS").c_str(),
|
||||
parallelCall->subscriber(),
|
||||
SACCH);
|
||||
gTransactionTable.add(newTransaction);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Control::InCallMOSMSController(const CPData *cpData, TransactionEntry* transaction, GSM::SACCHLogicalChannel *LCH)
|
||||
{
|
||||
LOG(INFO) << *cpData;
|
||||
|
||||
// See GSM 04.11 Arrow Diagram A5 for the transaction
|
||||
// Step 1 MS->Network CP-DATA containing RP-DATA
|
||||
// Step 2 Network->MS CP-ACK
|
||||
// Step 3 Network->MS CP-DATA containing RP-ACK
|
||||
// Step 4 MS->Network CP-ACK
|
||||
|
||||
// LAPDm operation, from GSM 04.11, Annex F:
|
||||
// """
|
||||
// Case C: Mobile originating short message transfer, parallel call.
|
||||
// The mobile station will send a SABM command on the SACCH when a CM_SERV_ACC
|
||||
// message has been received from the network, allowing the short message
|
||||
// transfer to start. If no hand over occurs the link will stay up until the
|
||||
// MSC orders a explicit release, or the clearing procedure is invoked. If the
|
||||
// parallel call is cleared before the short message transfer is finalized, the
|
||||
// MSC will delay the clearing procedure toward the BSS, i.e. the channel
|
||||
// release procedure is delayed.
|
||||
// """
|
||||
|
||||
// Since there's a parallel call, we will assume correct provisioning.
|
||||
// And we know that CM and SABM are established.
|
||||
|
||||
// Step 1 already happened in the SACCH service loop.
|
||||
// Just get the L3 TI and set the high bit since it originated in the MS.
|
||||
unsigned L3TI = cpData->TI() | 0x08;
|
||||
transaction->L3TI(L3TI);
|
||||
|
||||
// Step 2
|
||||
// Respond with CP-ACK.
|
||||
// This just means that we got the message.
|
||||
LOG(INFO) << "sending CPAck";
|
||||
LCH->send(CPAck(L3TI),3);
|
||||
|
||||
// Parse the message in CM and process RP part.
|
||||
// This is where we actually parse the message and send it out.
|
||||
// FIXME -- We need to set the message ref correctly,
|
||||
// even if the parsing fails.
|
||||
// The compiler gives a warning here. Let it. It will remind someone to fix it.
|
||||
unsigned ref;
|
||||
bool success = false;
|
||||
try {
|
||||
CPData data;
|
||||
data.parse(*cpData);
|
||||
LOG(INFO) << "CPData " << data;
|
||||
// Transfer out the RPDU -> TPDU -> delivery.
|
||||
ref = data.RPDU().reference();
|
||||
// This handler invokes higher-layer parsers, too.
|
||||
success = handleRPDU(transaction,data.RPDU());
|
||||
}
|
||||
catch (SMSReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (above L3)";
|
||||
// Cause 95, "semantically incorrect message".
|
||||
LCH->send(CPData(L3TI,RPError(95,ref)),3);
|
||||
throw UnexpectedMessage(transaction->ID());
|
||||
}
|
||||
catch (GSM::L3ReadError) {
|
||||
LOG(WARNING) << "SMS parsing failed (in L3)";
|
||||
throw UnsupportedMessage(transaction->ID());
|
||||
}
|
||||
|
||||
// Step 3
|
||||
// Send CP-DATA containing RP-ACK and message reference.
|
||||
if (success) {
|
||||
LOG(INFO) << "sending RPAck in CPData";
|
||||
LCH->send(CPData(L3TI,RPAck(ref)),3);
|
||||
} else {
|
||||
LOG(INFO) << "sending RPError in CPData";
|
||||
// Cause 127 is "internetworking error, unspecified".
|
||||
// See GSM 04.11 Table 8.4.
|
||||
LCH->send(CPData(L3TI,RPError(127,ref)),3);
|
||||
}
|
||||
|
||||
// Step 4
|
||||
// Get CP-ACK from the MS.
|
||||
GSM::L3Frame* CM = getFrameSMS(LCH);
|
||||
if (CM->MTI()!=CPMessage::ACK) {
|
||||
LOG(NOTICE) << "unexpected SMS CP message with MTI=" << CM->MTI() << " " << *CM;
|
||||
throw UnexpectedMessage(transaction->ID());
|
||||
}
|
||||
LOG(DEBUG) << "ack from MS: " << *CM;
|
||||
CPAck ack;
|
||||
ack.parse(*CM);
|
||||
LOG(INFO) << "CPAck " << ack;
|
||||
|
||||
gTransactionTable.remove(transaction);
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
65
Control/SMSControl.h
Normal file
65
Control/SMSControl.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**@file Declarations for common-use control-layer functions. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef SMSCONTROL_H
|
||||
#define SMSCONTROL_H
|
||||
|
||||
#include <SMSMessages.h>
|
||||
|
||||
namespace GSM {
|
||||
class L3Message;
|
||||
class LogicalChannel;
|
||||
class SDCCHLogicalChannel;
|
||||
class SACCHLogicalChannel;
|
||||
class TCHFACCHLogicalChannel;
|
||||
class L3CMServiceRequest;
|
||||
};
|
||||
|
||||
|
||||
namespace Control {
|
||||
|
||||
/** MOSMS state machine. */
|
||||
void MOSMSController(const GSM::L3CMServiceRequest *req, GSM::LogicalChannel *LCH);
|
||||
|
||||
/** MOSMS-with-parallel-call state machine. */
|
||||
void InCallMOSMSStarter(TransactionEntry *parallelCall);
|
||||
|
||||
/** MOSMS-with-parallel-call state machine. */
|
||||
void InCallMOSMSController(const SMS::CPData *msg, TransactionEntry* transaction, GSM::SACCHLogicalChannel *LCH);
|
||||
/**
|
||||
Basic SMS delivery from an established CM.
|
||||
On exit, SAP3 will be in ABM and LCH will still be open.
|
||||
Throws exception for failures in connection layer or for parsing failure.
|
||||
@return true on success in relay layer.
|
||||
*/
|
||||
bool deliverSMSToMS(const char *callingPartyDigits, const char* message, const char* contentType, unsigned TI, GSM::LogicalChannel *LCH);
|
||||
|
||||
/** MTSMS */
|
||||
void MTSMSController(TransactionEntry* transaction, GSM::LogicalChannel *LCH);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
263
Control/TMSITable.cpp
Normal file
263
Control/TMSITable.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "TMSITable.h"
|
||||
#include <Logger.h>
|
||||
#include <Globals.h>
|
||||
#include <sqlite3.h>
|
||||
#include <sqlite3util.h>
|
||||
|
||||
#include <GSML3MMMessages.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
using namespace Control;
|
||||
|
||||
|
||||
static const char* createTMSITable = {
|
||||
"CREATE TABLE IF NOT EXISTS TMSI_TABLE ("
|
||||
"TMSI INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"CREATED INTEGER NOT NULL, " // Unix time of record creation
|
||||
"ACCESSED INTEGER NOT NULL, " // Unix time of last encounter
|
||||
"APP_FLAGS INTEGER DEFAULT 0, " // Application-specific flags
|
||||
"IMSI TEXT UNIQUE NOT NULL, " // IMSI
|
||||
"IMEI TEXT, " // IMEI
|
||||
"L3TI INTEGER DEFAULT 0," // L3 transaction identifier
|
||||
"A5_SUPPORT INTEGER, " // encryption support
|
||||
"POWER_CLASS INTEGER, " // power class
|
||||
"OLD_TMSI INTEGER, " // previous TMSI in old network
|
||||
"PREV_MCC INTEGER, " // previous network MCC
|
||||
"PREV_MNC INTEGER, " // previous network MNC
|
||||
"PREV_LAC INTEGER, " // previous network LAC
|
||||
"DEG_LAT FLOAT, " // RRLP result
|
||||
"DEG_LONG FLOAT " // RRLP result
|
||||
")"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
TMSITable::TMSITable(const char* wPath)
|
||||
{
|
||||
int rc = sqlite3_open(wPath,&mDB);
|
||||
if (rc) {
|
||||
LOG(EMERG) << "Cannot open TMSITable database at " << wPath << ": " << sqlite3_errmsg(mDB);
|
||||
sqlite3_close(mDB);
|
||||
mDB = NULL;
|
||||
return;
|
||||
}
|
||||
if (!sqlite3_command(mDB,createTMSITable)) {
|
||||
LOG(EMERG) << "Cannot create TMSI table";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
TMSITable::~TMSITable()
|
||||
{
|
||||
if (mDB) sqlite3_close(mDB);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned TMSITable::assign(const char* IMSI, const GSM::L3LocationUpdatingRequest* lur)
|
||||
{
|
||||
// Create or find an entry based on IMSI.
|
||||
// Return assigned TMSI.
|
||||
assert(mDB);
|
||||
|
||||
LOG(DEBUG) << "IMSI=" << IMSI;
|
||||
// Is there already a record?
|
||||
unsigned TMSI;
|
||||
if (sqlite3_single_lookup(mDB,"TMSI_TABLE","IMSI",IMSI,"TMSI",TMSI)) {
|
||||
LOG(DEBUG) << "found TMSI " << TMSI;
|
||||
touch(TMSI);
|
||||
return TMSI;
|
||||
}
|
||||
|
||||
// Create a new record.
|
||||
LOG(NOTICE) << "new entry for IMSI " << IMSI;
|
||||
char query[1000];
|
||||
unsigned now = (unsigned)time(NULL);
|
||||
if (!lur) {
|
||||
sprintf(query,
|
||||
"INSERT INTO TMSI_TABLE (IMSI,CREATED,ACCESSED) "
|
||||
"VALUES ('%s',%u,%u)",
|
||||
IMSI,now,now);
|
||||
} else {
|
||||
const GSM::L3LocationAreaIdentity &lai = lur->LAI();
|
||||
const GSM::L3MobileIdentity &mid = lur->mobileID();
|
||||
if (mid.type()==GSM::TMSIType) {
|
||||
sprintf(query,
|
||||
"INSERT INTO TMSI_TABLE (IMSI,CREATED,ACCESSED,PREV_MCC,PREV_MNC,PREV_LAC,OLD_TMSI) "
|
||||
"VALUES ('%s',%u,%u,%u,%u,%u,%u)",
|
||||
IMSI,now,now,lai.MCC(),lai.MNC(),lai.LAC(),mid.TMSI());
|
||||
} else {
|
||||
sprintf(query,
|
||||
"INSERT INTO TMSI_TABLE (IMSI,CREATED,ACCESSED,PREV_MCC,PREV_MNC,PREV_LAC) "
|
||||
"VALUES ('%s',%u,%u,%u,%u,%u)",
|
||||
IMSI,now,now,lai.MCC(),lai.MNC(),lai.LAC());
|
||||
}
|
||||
}
|
||||
if (!sqlite3_command(mDB,query)) {
|
||||
LOG(ALERT) << "TMSI creation failed";
|
||||
return 0;
|
||||
}
|
||||
if (!sqlite3_single_lookup(mDB,"TMSI_TABLE","IMSI",IMSI,"TMSI",TMSI)) {
|
||||
LOG(ERR) << "TMSI database inconsistancy";
|
||||
return 0;
|
||||
}
|
||||
return TMSI;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TMSITable::touch(unsigned TMSI) const
|
||||
{
|
||||
// Update timestamp.
|
||||
char query[100];
|
||||
sprintf(query,"UPDATE TMSI_TABLE SET ACCESSED = %u WHERE TMSI == %u",
|
||||
(unsigned)time(NULL),TMSI);
|
||||
sqlite3_command(mDB,query);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Returned string must be free'd by the caller.
|
||||
char* TMSITable::IMSI(unsigned TMSI) const
|
||||
{
|
||||
char* IMSI = NULL;
|
||||
if (sqlite3_single_lookup(mDB,"TMSI_TABLE","TMSI",TMSI,"IMSI",IMSI)) touch(TMSI);
|
||||
return IMSI;
|
||||
}
|
||||
|
||||
unsigned TMSITable::TMSI(const char* IMSI) const
|
||||
{
|
||||
unsigned TMSI=0;
|
||||
if (sqlite3_single_lookup(mDB,"TMSI_TABLE","IMSI",IMSI,"TMSI",TMSI)) touch(TMSI);
|
||||
return TMSI;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void printAge(unsigned seconds, ostream& os)
|
||||
{
|
||||
static const unsigned k=5;
|
||||
os << setw(4);
|
||||
if (seconds<k*60) {
|
||||
os << seconds << 's';
|
||||
return;
|
||||
}
|
||||
unsigned minutes = (seconds+30) / 60;
|
||||
if (minutes<k*60) {
|
||||
os << minutes << 'm';
|
||||
return;
|
||||
}
|
||||
unsigned hours = (minutes+30) / 60;
|
||||
if (hours<k*24) {
|
||||
os << hours << 'h';
|
||||
return;
|
||||
}
|
||||
os << (hours+12)/24 << 'd';
|
||||
}
|
||||
|
||||
|
||||
void TMSITable::dump(ostream& os) const
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if (sqlite3_prepare_statement(mDB,&stmt,"SELECT TMSI,IMSI,CREATED,ACCESSED FROM TMSI_TABLE")) {
|
||||
LOG(ERR) << "sqlite3_prepare_statement failed";
|
||||
return;
|
||||
}
|
||||
time_t now = time(NULL);
|
||||
while (sqlite3_run_query(mDB,stmt)==SQLITE_ROW) {
|
||||
os << hex << setw(8) << sqlite3_column_int64(stmt,0) << ' ' << dec;
|
||||
os << sqlite3_column_text(stmt,1) << ' ';
|
||||
printAge(now-sqlite3_column_int(stmt,2),os); os << ' ';
|
||||
printAge(now-sqlite3_column_int(stmt,3),os); os << ' ';
|
||||
os << endl;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TMSITable::clear()
|
||||
{
|
||||
sqlite3_command(mDB,"DELETE FROM TMSI_TABLE WHERE 1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool TMSITable::IMEI(const char* IMSI, const char *IMEI)
|
||||
{
|
||||
char query[100];
|
||||
sprintf(query,"UPDATE TMSI_TABLE SET IMEI=\"%s\",ACCESSED=%u WHERE IMSI=\"%s\"",
|
||||
IMEI,(unsigned)time(NULL),IMSI);
|
||||
return sqlite3_command(mDB,query);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool TMSITable::classmark(const char* IMSI, const GSM::L3MobileStationClassmark2& classmark)
|
||||
{
|
||||
int A5Bits = (classmark.A5_1()<<2) + (classmark.A5_2()<<1) + classmark.A5_3();
|
||||
char query[100];
|
||||
sprintf(query,
|
||||
"UPDATE TMSI_TABLE SET A5_SUPPORT=%u,ACCESSED=%u,POWER_CLASS=%u "
|
||||
" WHERE IMSI=\"%s\"",
|
||||
A5Bits,(unsigned)time(NULL),classmark.powerClass(),IMSI);
|
||||
return sqlite3_command(mDB,query);
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned TMSITable::nextL3TI(const char* IMSI)
|
||||
{
|
||||
// FIXME -- This should be a single atomic operation.
|
||||
unsigned l3ti;
|
||||
if (!sqlite3_single_lookup(mDB,"TMSI_TABLE","IMSI",IMSI,"L3TI",l3ti)) {
|
||||
LOG(ERR) << "cannot read L3TI from TMSI_TABLE, using randon L3TI";
|
||||
return random() % 8;
|
||||
}
|
||||
// Note that TI=7 is a reserved value, so value values are 0-6. See GSM 04.07 11.2.3.1.3.
|
||||
unsigned next = (l3ti+1) % 7;
|
||||
char query[200];
|
||||
sprintf(query,"UPDATE TMSI_TABLE SET L3TI=%u,ACCESSED=%u WHERE IMSI='%s'",
|
||||
next, (unsigned)time(NULL),IMSI);
|
||||
if (!sqlite3_command(mDB,query)) {
|
||||
LOG(ALERT) << "cannot write L3TI to TMSI_TABLE";
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
103
Control/TMSITable.h
Normal file
103
Control/TMSITable.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef TMSITABLE_H
|
||||
#define TMSITABLE_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Timeval.h>
|
||||
#include <Threads.h>
|
||||
|
||||
|
||||
struct sqlite3;
|
||||
|
||||
namespace GSM {
|
||||
class L3LocationUpdatingRequest;
|
||||
class L3MobileStationClassmark2;
|
||||
class L3MobileIdentity;
|
||||
}
|
||||
|
||||
|
||||
namespace Control {
|
||||
|
||||
class TMSITable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3 *mDB; ///< database connection
|
||||
|
||||
|
||||
public:
|
||||
|
||||
TMSITable(const char*wPath);
|
||||
|
||||
~TMSITable();
|
||||
|
||||
/**
|
||||
Create a new entry in the table.
|
||||
@param IMSI The IMSI to create an entry for.
|
||||
@param The associated LUR, if any.
|
||||
@return The assigned TMSI.
|
||||
*/
|
||||
unsigned assign(const char* IMSI, const GSM::L3LocationUpdatingRequest* lur=NULL);
|
||||
|
||||
/**
|
||||
Find an IMSI in the table.
|
||||
This is a log-time operation.
|
||||
@param TMSI The TMSI to find.
|
||||
@return Pointer to IMSI to be freed by the caller, or NULL.
|
||||
*/
|
||||
char* IMSI(unsigned TMSI) const;
|
||||
|
||||
/**
|
||||
Find a TMSI in the table.
|
||||
This is a linear-time operation.
|
||||
@param IMSI The IMSI to mach.
|
||||
@return A TMSI value or zero on failure.
|
||||
*/
|
||||
unsigned TMSI(const char* IMSI) const;
|
||||
|
||||
/** Write entries as text to a stream. */
|
||||
void dump(std::ostream&) const;
|
||||
|
||||
/** Clear the table completely. */
|
||||
void clear();
|
||||
|
||||
/** Set the IMEI. */
|
||||
bool IMEI(const char* IMSI, const char* IMEI);
|
||||
|
||||
/** Set the classmark. */
|
||||
bool classmark(const char* IMSI, const GSM::L3MobileStationClassmark2& classmark);
|
||||
|
||||
/** Get the next TI value to use for this IMSI or TMSI. */
|
||||
unsigned nextL3TI(const char* IMSI);
|
||||
|
||||
private:
|
||||
|
||||
/** Update the "accessed" time on a record. */
|
||||
void touch(unsigned TMSI) const;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
777
Control/TransactionTable.cpp
Normal file
777
Control/TransactionTable.cpp
Normal file
@@ -0,0 +1,777 @@
|
||||
/**@file TransactionTable and related classes. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2010, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Process, Inc.
|
||||
* Copyright 2011 Raqnge Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "TransactionTable.h"
|
||||
#include "ControlCommon.h"
|
||||
|
||||
#include <GSMLogicalChannel.h>
|
||||
#include <GSML3Message.h>
|
||||
#include <GSML3CCMessages.h>
|
||||
#include <GSML3RRMessages.h>
|
||||
#include <GSML3MMMessages.h>
|
||||
#include <GSMConfig.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <sqlite3util.h>
|
||||
|
||||
#include <SIPEngine.h>
|
||||
#include <SIPInterface.h>
|
||||
|
||||
#include <CallControl.h>
|
||||
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
using namespace Control;
|
||||
using namespace SIP;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TransactionEntry::initTimers()
|
||||
{
|
||||
// Call this only once.
|
||||
// TODO -- It would be nice if these were all configurable.
|
||||
assert(mTimers.size()==0);
|
||||
mTimers["301"] = Z100Timer(T301ms);
|
||||
mTimers["302"] = Z100Timer(T302ms);
|
||||
mTimers["303"] = Z100Timer(T303ms);
|
||||
mTimers["304"] = Z100Timer(T304ms);
|
||||
mTimers["305"] = Z100Timer(T305ms);
|
||||
mTimers["308"] = Z100Timer(T308ms);
|
||||
mTimers["310"] = Z100Timer(T310ms);
|
||||
mTimers["313"] = Z100Timer(T313ms);
|
||||
mTimers["3113"] = Z100Timer(gConfig.getNum("GSM.Timer.T3113"));
|
||||
mTimers["TR1M"] = Z100Timer(TR1Mms);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Form for MT transactions.
|
||||
TransactionEntry::TransactionEntry(
|
||||
const char* proxy,
|
||||
const L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const L3CMServiceType& wService,
|
||||
const L3CallingPartyBCDNumber& wCalling,
|
||||
GSM::CallState wState,
|
||||
const char *wMessage)
|
||||
:mID(gTransactionTable.newID()),
|
||||
mSubscriber(wSubscriber),mService(wService),
|
||||
mL3TI(gTMSITable.nextL3TI(wSubscriber.digits())),
|
||||
mCalling(wCalling),
|
||||
mSIP(proxy,mSubscriber.digits()),
|
||||
mGSMState(wState),
|
||||
mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
|
||||
mChannel(wChannel),
|
||||
mTerminationRequested(false)
|
||||
{
|
||||
if (wMessage) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
|
||||
else mMessage.assign(""); //mMessage[0]='\0';
|
||||
initTimers();
|
||||
}
|
||||
|
||||
// Form for MOC transactions.
|
||||
TransactionEntry::TransactionEntry(
|
||||
const char* proxy,
|
||||
const L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const L3CMServiceType& wService,
|
||||
unsigned wL3TI,
|
||||
const L3CalledPartyBCDNumber& wCalled)
|
||||
:mID(gTransactionTable.newID()),
|
||||
mSubscriber(wSubscriber),mService(wService),
|
||||
mL3TI(wL3TI),
|
||||
mCalled(wCalled),
|
||||
mSIP(proxy,mSubscriber.digits()),
|
||||
mGSMState(GSM::MOCInitiated),
|
||||
mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
|
||||
mChannel(wChannel),
|
||||
mTerminationRequested(false)
|
||||
{
|
||||
assert(mSubscriber.type()==GSM::IMSIType);
|
||||
mMessage.assign(""); //mMessage[0]='\0';
|
||||
initTimers();
|
||||
}
|
||||
|
||||
|
||||
// Form for SOS transactions.
|
||||
TransactionEntry::TransactionEntry(
|
||||
const char* proxy,
|
||||
const L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const L3CMServiceType& wService,
|
||||
unsigned wL3TI)
|
||||
:mID(gTransactionTable.newID()),
|
||||
mSubscriber(wSubscriber),mService(wService),
|
||||
mL3TI(wL3TI),
|
||||
mSIP(proxy,mSubscriber.digits()),
|
||||
mGSMState(GSM::MOCInitiated),
|
||||
mNumSQLTries(2*gConfig.getNum("Control.NumSQLTries")),
|
||||
mChannel(wChannel),
|
||||
mTerminationRequested(false)
|
||||
{
|
||||
mMessage.assign(""); //mMessage[0]='\0';
|
||||
initTimers();
|
||||
}
|
||||
|
||||
|
||||
// Form for MO-SMS transactions.
|
||||
TransactionEntry::TransactionEntry(
|
||||
const char* proxy,
|
||||
const L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const L3CalledPartyBCDNumber& wCalled,
|
||||
const char* wMessage)
|
||||
:mID(gTransactionTable.newID()),
|
||||
mSubscriber(wSubscriber),
|
||||
mService(GSM::L3CMServiceType::ShortMessage),
|
||||
mL3TI(7),mCalled(wCalled),
|
||||
mSIP(proxy,mSubscriber.digits()),
|
||||
mGSMState(GSM::SMSSubmitting),
|
||||
mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
|
||||
mChannel(wChannel),
|
||||
mTerminationRequested(false)
|
||||
{
|
||||
assert(mSubscriber.type()==GSM::IMSIType);
|
||||
if (wMessage!=NULL) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
|
||||
else mMessage.assign(""); //mMessage[0]='\0';
|
||||
initTimers();
|
||||
}
|
||||
|
||||
// Form for MO-SMS transactions with parallel call.
|
||||
TransactionEntry::TransactionEntry(
|
||||
const char* proxy,
|
||||
const L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel)
|
||||
:mID(gTransactionTable.newID()),
|
||||
mSubscriber(wSubscriber),
|
||||
mService(GSM::L3CMServiceType::ShortMessage),
|
||||
mL3TI(7),
|
||||
mSIP(proxy,mSubscriber.digits()),
|
||||
mGSMState(GSM::SMSSubmitting),
|
||||
mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
|
||||
mChannel(wChannel),
|
||||
mTerminationRequested(false)
|
||||
{
|
||||
assert(mSubscriber.type()==GSM::IMSIType);
|
||||
mMessage[0]='\0';
|
||||
initTimers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
TransactionEntry::~TransactionEntry()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool TransactionEntry::timerExpired(const char* name) const
|
||||
{
|
||||
TimerTable::const_iterator itr = mTimers.find(name);
|
||||
assert(itr!=mTimers.end());
|
||||
ScopedLock lock(mLock);
|
||||
return (itr->second).expired();
|
||||
}
|
||||
|
||||
|
||||
bool TransactionEntry::anyTimerExpired() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
TimerTable::const_iterator itr = mTimers.begin();
|
||||
while (itr!=mTimers.end()) {
|
||||
if ((itr->second).expired()) {
|
||||
LOG(INFO) << itr->first << " expired in " << *this;
|
||||
return true;
|
||||
}
|
||||
++itr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void TransactionEntry::resetTimers()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
TimerTable::iterator itr = mTimers.begin();
|
||||
while (itr!=mTimers.end()) {
|
||||
(itr->second).reset();
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool TransactionEntry::dead() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
|
||||
// Null state?
|
||||
if (mGSMState==GSM::NullState && stateAge()>180*1000) return true;
|
||||
// Stuck in proceeding?
|
||||
if (mSIP.state()==Proceeding && stateAge()>180*1000) return true;
|
||||
|
||||
// Paging timed out?
|
||||
if (mGSMState==GSM::Paging) {
|
||||
TimerTable::const_iterator itr = mTimers.find("3113");
|
||||
assert(itr!=mTimers.end());
|
||||
return (itr->second).expired();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ostream& Control::operator<<(ostream& os, const TransactionEntry& entry)
|
||||
{
|
||||
entry.text(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TransactionEntry::text(ostream& os) const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
os << mID;
|
||||
if (mChannel) os << " " << *mChannel;
|
||||
else os << " no chan";
|
||||
os << " " << mSubscriber;
|
||||
os << " L3TI=" << mL3TI;
|
||||
os << " SIP-call-id=" << mSIP.callID();
|
||||
os << " SIP-proxy=" << mSIP.proxyIP() << ":" << mSIP.proxyPort();
|
||||
os << " " << mService;
|
||||
if (mCalled.digits()[0]) os << " to=" << mCalled.digits();
|
||||
if (mCalling.digits()[0]) os << " from=" << mCalling.digits();
|
||||
os << " GSMState=" << mGSMState;
|
||||
os << " SIPState=" << mSIP.state();
|
||||
os << " (" << (stateAge()+500)/1000 << " sec)";
|
||||
if (mMessage[0]) os << " message=\"" << mMessage << "\"";
|
||||
}
|
||||
|
||||
void TransactionEntry::message(const char *wMessage, size_t length)
|
||||
{
|
||||
/*if (length>520) {
|
||||
LOG(NOTICE) << "truncating long message: " << wMessage;
|
||||
length=520;
|
||||
}*/
|
||||
ScopedLock lock(mLock);
|
||||
//memcpy(mMessage,wMessage,length);
|
||||
//mMessage[length]='\0';
|
||||
mMessage.assign(wMessage, length);
|
||||
}
|
||||
|
||||
void TransactionEntry::messageType(const char *wContentType)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mContentType.assign(wContentType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TransactionEntry::channel(GSM::LogicalChannel* wChannel)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mChannel = wChannel;
|
||||
}
|
||||
|
||||
|
||||
void TransactionEntry::GSMState(GSM::CallState wState)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mGSMState = wState;
|
||||
mStateTimer.now();
|
||||
}
|
||||
|
||||
|
||||
void TransactionEntry::echoSIPState(SIP::SIPState state) const
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
if (mPrevSIPState==state) return;
|
||||
mPrevSIPState = state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SIP::SIPState TransactionEntry::MOCSendINVITE(const char* calledUser, const char* calledDomain, short rtpPort, unsigned codec)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MOCSendINVITE(calledUser,calledDomain,rtpPort,codec);
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MOCResendINVITE()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MOCResendINVITE();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MOCWaitForOK()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MOCWaitForOK();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MOCSendACK()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MOCSendACK();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::SOSSendINVITE(short rtpPort, unsigned codec)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.SOSSendINVITE(rtpPort,codec);
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
SIP::SIPState TransactionEntry::MTCSendTrying()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTCSendTrying();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MTCSendRinging()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTCSendRinging();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MTCWaitForACK()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTCWaitForACK();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MTCCheckForCancel()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTCCheckForCancel();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
SIP::SIPState TransactionEntry::MTCSendOK(short rtpPort, unsigned codec)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTCSendOK(rtpPort,codec);
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MODSendBYE()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MODSendBYE();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MODResendBYE()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MODResendBYE();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MODWaitForOK()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MODWaitForOK();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MTDCheckBYE()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTDCheckBYE();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MTDSendOK()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTDSendOK();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MOSMSSendMESSAGE(const char* calledUser, const char* calledDomain, const char* contentType)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MOSMSSendMESSAGE(calledUser,calledDomain,mMessage.c_str(),contentType);
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MOSMSWaitForSubmit()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MOSMSWaitForSubmit();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
SIP::SIPState TransactionEntry::MTSMSSendOK()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SIP::SIPState state = mSIP.MTSMSSendOK();
|
||||
echoSIPState(state);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool TransactionEntry::sendINFOAndWaitForOK(unsigned info)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mSIP.sendINFOAndWaitForOK(info);
|
||||
}
|
||||
|
||||
void TransactionEntry::SIPUser(const char* IMSI)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mSIP.user(IMSI);
|
||||
}
|
||||
|
||||
void TransactionEntry::SIPUser(const char* callID, const char *IMSI , const char *origID, const char *origHost)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mSIP.user(callID,IMSI,origID,origHost);
|
||||
}
|
||||
|
||||
void TransactionEntry::called(const L3CalledPartyBCDNumber& wCalled)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mCalled = wCalled;
|
||||
}
|
||||
|
||||
|
||||
void TransactionEntry::L3TI(unsigned wL3TI)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mL3TI = wL3TI;
|
||||
}
|
||||
|
||||
|
||||
bool TransactionEntry::terminationRequested()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
bool retVal = mTerminationRequested;
|
||||
mTerminationRequested = false;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TransactionTable::TransactionTable()
|
||||
// This assumes the main application uses sdevrandom.
|
||||
:mIDCounter(random())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
TransactionTable::~TransactionTable()
|
||||
{
|
||||
// Don't bother disposing of the memory,
|
||||
// since this is only invoked when the application exits.
|
||||
if (mDB) sqlite3_close(mDB);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned TransactionTable::newID()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mIDCounter++;
|
||||
}
|
||||
|
||||
|
||||
void TransactionTable::add(TransactionEntry* value)
|
||||
{
|
||||
LOG(INFO) << "new transaction " << *value;
|
||||
ScopedLock lock(mLock);
|
||||
mTable[value->ID()]=value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TransactionEntry* TransactionTable::find(unsigned key)
|
||||
{
|
||||
// Since this is a log-time operation, we don't screw that up by calling clearDeadEntries.
|
||||
|
||||
// ID==0 is a non-valid special case.
|
||||
LOG(DEBUG) << "by key: " << key;
|
||||
assert(key);
|
||||
ScopedLock lock(mLock);
|
||||
TransactionMap::iterator itr = mTable.find(key);
|
||||
if (itr==mTable.end()) return NULL;
|
||||
if (itr->second->dead()) {
|
||||
innerRemove(itr);
|
||||
return NULL;
|
||||
}
|
||||
return (itr->second);
|
||||
}
|
||||
|
||||
|
||||
void TransactionTable::innerRemove(TransactionMap::iterator itr)
|
||||
{
|
||||
LOG(DEBUG) << "removing transaction: " << *(itr->second);
|
||||
gSIPInterface.removeCall(itr->second->SIPCallID());
|
||||
delete itr->second;
|
||||
mTable.erase(itr);
|
||||
}
|
||||
|
||||
|
||||
bool TransactionTable::remove(unsigned key)
|
||||
{
|
||||
// ID==0 is a non-valid special case, and it shouldn't be passed here.
|
||||
if (key==0) {
|
||||
LOG(ERR) << "called with key==0";
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedLock lock(mLock);
|
||||
TransactionMap::iterator itr = mTable.find(key);
|
||||
if (itr==mTable.end()) return false;
|
||||
innerRemove(itr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransactionTable::removePaging(unsigned key)
|
||||
{
|
||||
// ID==0 is a non-valid special case and should not be passed here.
|
||||
assert(key);
|
||||
ScopedLock lock(mLock);
|
||||
TransactionMap::iterator itr = mTable.find(key);
|
||||
if (itr==mTable.end()) return false;
|
||||
if (itr->second->GSMState()!=GSM::Paging) return false;
|
||||
innerRemove(itr);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TransactionTable::clearDeadEntries()
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
TransactionMap::iterator itr = mTable.begin();
|
||||
while (itr!=mTable.end()) {
|
||||
if (!itr->second->dead()) ++itr;
|
||||
else {
|
||||
LOG(DEBUG) << "erasing " << itr->first;
|
||||
TransactionMap::iterator old = itr;
|
||||
itr++;
|
||||
innerRemove(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
TransactionEntry* TransactionTable::find(const GSM::LogicalChannel *chan)
|
||||
{
|
||||
LOG(DEBUG) << "by channel: " << *chan << " (" << chan << ")";
|
||||
|
||||
// Yes, it's linear time.
|
||||
// Since clearDeadEntries is also linear, do that here, too.
|
||||
clearDeadEntries();
|
||||
|
||||
// Brute force search.
|
||||
ScopedLock lock(mLock);
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
const GSM::LogicalChannel* thisChan = itr->second->channel();
|
||||
//LOG(DEBUG) << "looking for " << *chan << " (" << chan << ")" << ", found " << *(thisChan) << " (" << thisChan << ")";
|
||||
if ((void*)thisChan == (void*)chan) return itr->second;
|
||||
}
|
||||
//LOG(DEBUG) << "no match for " << *chan << " (" << chan << ")";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TransactionEntry* TransactionTable::find(const L3MobileIdentity& mobileID, GSM::CallState state)
|
||||
{
|
||||
LOG(DEBUG) << "by ID and state: " << mobileID << " in " << state;
|
||||
|
||||
// Yes, it's linear time.
|
||||
// Since clearDeadEntries is also linear, do that here, too.
|
||||
clearDeadEntries();
|
||||
|
||||
// Brtue force search.
|
||||
ScopedLock lock(mLock);
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
if (itr->second->GSMState() != state) continue;
|
||||
if (itr->second->subscriber() == mobileID) return itr->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TransactionEntry* TransactionTable::find(const L3MobileIdentity& mobileID, const char* callID)
|
||||
{
|
||||
assert(callID);
|
||||
LOG(DEBUG) << "by ID and call-ID: " << mobileID << ", call " << callID;
|
||||
|
||||
string callIDString = string(callID);
|
||||
// Yes, it's linear time.
|
||||
// Since clearDeadEntries is also linear, do that here, too.
|
||||
clearDeadEntries();
|
||||
|
||||
// Brtue force search.
|
||||
ScopedLock lock(mLock);
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
if (itr->second->mSIP.callID() != callIDString) continue;
|
||||
if (itr->second->subscriber() == mobileID) return itr->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
TransactionEntry* TransactionTable::answeredPaging(const L3MobileIdentity& mobileID)
|
||||
{
|
||||
// Yes, it's linear time.
|
||||
// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.
|
||||
|
||||
// Since clearDeadEntries is also linear, do that here, too.
|
||||
clearDeadEntries();
|
||||
|
||||
// Brtue force search.
|
||||
ScopedLock lock(mLock);
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
if (itr->second->GSMState() != GSM::Paging) continue;
|
||||
if (itr->second->subscriber() == mobileID) {
|
||||
// Stop T3113 and change the state.
|
||||
itr->second->GSMState(AnsweredPaging);
|
||||
itr->second->resetTimer("3113");
|
||||
return itr->second;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
GSM::LogicalChannel* TransactionTable::findChannel(const L3MobileIdentity& mobileID)
|
||||
{
|
||||
// Yes, it's linear time.
|
||||
// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.
|
||||
|
||||
// Since clearDeadEntries is also linear, do that here, too.
|
||||
clearDeadEntries();
|
||||
|
||||
// Brtue force search.
|
||||
ScopedLock lock(mLock);
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
if (itr->second->subscriber() != mobileID) continue;
|
||||
GSM::LogicalChannel* chan = itr->second->channel();
|
||||
if (!chan) continue;
|
||||
if (chan->type() == FACCHType) return chan;
|
||||
if (chan->type() == SDCCHType) return chan;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
unsigned TransactionTable::countChan(const GSM::LogicalChannel* chan)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
clearDeadEntries();
|
||||
unsigned count = 0;
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
if (itr->second->channel() == chan) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t TransactionTable::dump(ostream& os) const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
for (TransactionMap::const_iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
os << *(itr->second) << endl;
|
||||
}
|
||||
return mTable.size();
|
||||
}
|
||||
|
||||
|
||||
TransactionEntry* TransactionTable::findLongestCall()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
clearDeadEntries();
|
||||
long longTime = 0;
|
||||
TransactionMap::iterator longCall = mTable.end();
|
||||
for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
|
||||
if (!(itr->second->channel())) continue;
|
||||
if (itr->second->GSMState() != GSM::Active) continue;
|
||||
long runTime = itr->second->stateAge();
|
||||
if (runTime > longTime) {
|
||||
runTime = longTime;
|
||||
longCall = itr;
|
||||
}
|
||||
}
|
||||
if (longCall == mTable.end()) return NULL;
|
||||
return longCall->second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
433
Control/TransactionTable.h
Normal file
433
Control/TransactionTable.h
Normal file
@@ -0,0 +1,433 @@
|
||||
/**@file Declarations for TransactionTable and related classes. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef TRANSACTIONTABLE_H
|
||||
#define TRANSACTIONTABLE_H
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <list>
|
||||
|
||||
#include <Logger.h>
|
||||
#include <Interthread.h>
|
||||
#include <Timeval.h>
|
||||
|
||||
|
||||
#include <GSML3CommonElements.h>
|
||||
#include <GSML3MMElements.h>
|
||||
#include <GSML3CCElements.h>
|
||||
#include <GSML3RRElements.h>
|
||||
#include <SIPEngine.h>
|
||||
|
||||
|
||||
|
||||
struct sqlite3;
|
||||
|
||||
|
||||
/**@namespace Control This namepace is for use by the control layer. */
|
||||
namespace Control {
|
||||
|
||||
typedef std::map<std::string, GSM::Z100Timer> TimerTable;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A TransactionEntry object is used to maintain the state of a transaction
|
||||
as it moves from channel to channel.
|
||||
The object itself is not thread safe.
|
||||
*/
|
||||
class TransactionEntry {
|
||||
|
||||
private:
|
||||
|
||||
mutable Mutex mLock; ///< thread-safe control, shared from gTransactionTable
|
||||
|
||||
/**@name Stable variables, fixed in the constructor or written only once. */
|
||||
//@{
|
||||
unsigned mID; ///< the internal transaction ID, assigned by a TransactionTable
|
||||
|
||||
GSM::L3MobileIdentity mSubscriber; ///< some kind of subscriber ID, preferably IMSI
|
||||
GSM::L3CMServiceType mService; ///< the associated service type
|
||||
unsigned mL3TI; ///< the L3 short transaction ID, the version we *send* to the MS
|
||||
|
||||
GSM::L3CalledPartyBCDNumber mCalled; ///< the associated called party number, if known
|
||||
GSM::L3CallingPartyBCDNumber mCalling; ///< the associated calling party number, if known
|
||||
|
||||
// TODO -- This should be expaned to deal with long messages.
|
||||
//char mMessage[522]; ///< text messaging payload
|
||||
std::string mMessage; ///< text message payload
|
||||
std::string mContentType; ///< text message payload content type
|
||||
//@}
|
||||
|
||||
SIP::SIPEngine mSIP; ///< the SIP IETF RFC-3621 protocol engine
|
||||
mutable SIP::SIPState mPrevSIPState; ///< previous SIP state, prior to most recent transactions
|
||||
GSM::CallState mGSMState; ///< the GSM/ISDN/Q.931 call state
|
||||
Timeval mStateTimer; ///< timestamp of last state change.
|
||||
TimerTable mTimers; ///< table of Z100-type state timers
|
||||
|
||||
unsigned mNumSQLTries; ///< number of SQL tries for DB operations
|
||||
|
||||
GSM::LogicalChannel *mChannel; ///< current channel of the transaction
|
||||
|
||||
bool mTerminationRequested;
|
||||
|
||||
public:
|
||||
|
||||
/** This form is used for MTC or MT-SMS with TI generated by the network. */
|
||||
TransactionEntry(const char* proxy,
|
||||
const GSM::L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const GSM::L3CMServiceType& wService,
|
||||
const GSM::L3CallingPartyBCDNumber& wCalling,
|
||||
GSM::CallState wState = GSM::NullState,
|
||||
const char *wMessage = NULL);
|
||||
|
||||
/** This form is used for MOC, setting mGSMState to MOCInitiated. */
|
||||
TransactionEntry(const char* proxy,
|
||||
const GSM::L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const GSM::L3CMServiceType& wService,
|
||||
unsigned wL3TI,
|
||||
const GSM::L3CalledPartyBCDNumber& wCalled);
|
||||
|
||||
/** This form is used for SOS calls, setting mGSMState to MOCInitiated. */
|
||||
TransactionEntry(const char* proxy,
|
||||
const GSM::L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const GSM::L3CMServiceType& wService,
|
||||
unsigned wL3TI);
|
||||
|
||||
/** Form for MO-SMS; sets yet-unknown TI to 7 and GSM state to SMSSubmitting */
|
||||
TransactionEntry(const char* proxy,
|
||||
const GSM::L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel,
|
||||
const GSM::L3CalledPartyBCDNumber& wCalled,
|
||||
const char* wMessage);
|
||||
|
||||
/** Form for MO-SMS with a parallel call; sets yet-unknown TI to 7 and GSM state to SMSSubmitting */
|
||||
TransactionEntry(const char* proxy,
|
||||
const GSM::L3MobileIdentity& wSubscriber,
|
||||
GSM::LogicalChannel* wChannel);
|
||||
|
||||
|
||||
/** Delete the database entry upon destruction. */
|
||||
~TransactionEntry();
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
unsigned L3TI() const { return mL3TI; }
|
||||
void L3TI(unsigned wL3TI);
|
||||
|
||||
const GSM::LogicalChannel* channel() const { return mChannel; }
|
||||
GSM::LogicalChannel* channel() { return mChannel; }
|
||||
|
||||
void channel(GSM::LogicalChannel* wChannel);
|
||||
|
||||
const GSM::L3MobileIdentity& subscriber() const { return mSubscriber; }
|
||||
|
||||
const GSM::L3CMServiceType& service() const { return mService; }
|
||||
|
||||
const GSM::L3CalledPartyBCDNumber& called() const { return mCalled; }
|
||||
void called(const GSM::L3CalledPartyBCDNumber&);
|
||||
|
||||
const GSM::L3CallingPartyBCDNumber& calling() const { return mCalling; }
|
||||
|
||||
const char* message() const { return mMessage.c_str(); }
|
||||
void message(const char *wMessage, size_t length);
|
||||
const char* messageType() const { return mContentType.c_str(); }
|
||||
void messageType(const char *wContentType);
|
||||
|
||||
unsigned ID() const { return mID; }
|
||||
|
||||
GSM::CallState GSMState() const { ScopedLock lock(mLock); return mGSMState; }
|
||||
void GSMState(GSM::CallState wState);
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/** Initiate the termination process. */
|
||||
void terminate() { ScopedLock lock(mLock); mTerminationRequested=true; }
|
||||
|
||||
bool terminationRequested();
|
||||
|
||||
/**@name SIP-side operations */
|
||||
//@{
|
||||
|
||||
SIP::SIPState SIPState() { ScopedLock lock(mLock); return mSIP.state(); }
|
||||
|
||||
SIP::SIPState MOCSendINVITE(const char* calledUser, const char* calledDomain, short rtpPort, unsigned codec);
|
||||
SIP::SIPState MOCResendINVITE();
|
||||
SIP::SIPState MOCWaitForOK();
|
||||
SIP::SIPState MOCSendACK();
|
||||
void MOCInitRTP() { ScopedLock lock(mLock); return mSIP.MOCInitRTP(); }
|
||||
|
||||
SIP::SIPState SOSSendINVITE(short rtpPort, unsigned codec);
|
||||
SIP::SIPState SOSResendINVITE() { return MOCResendINVITE(); }
|
||||
SIP::SIPState SOSWaitForOK() { return MOCWaitForOK(); }
|
||||
SIP::SIPState SOSSendACK() { return MOCSendACK(); }
|
||||
void SOSInitRTP() { MOCInitRTP(); }
|
||||
|
||||
|
||||
SIP::SIPState MTCSendTrying();
|
||||
SIP::SIPState MTCSendRinging();
|
||||
SIP::SIPState MTCWaitForACK();
|
||||
SIP::SIPState MTCCheckForCancel();
|
||||
SIP::SIPState MTCSendOK(short rtpPort, unsigned codec);
|
||||
void MTCInitRTP() { ScopedLock lock(mLock); mSIP.MTCInitRTP(); }
|
||||
|
||||
SIP::SIPState MODSendBYE();
|
||||
SIP::SIPState MODResendBYE();
|
||||
SIP::SIPState MODWaitForOK();
|
||||
|
||||
SIP::SIPState MTDCheckBYE();
|
||||
SIP::SIPState MTDSendOK();
|
||||
|
||||
// TODO: Remove contentType from here and use the setter above.
|
||||
SIP::SIPState MOSMSSendMESSAGE(const char* calledUser, const char* calledDomain, const char* contentType);
|
||||
SIP::SIPState MOSMSWaitForSubmit();
|
||||
|
||||
SIP::SIPState MTSMSSendOK();
|
||||
|
||||
bool sendINFOAndWaitForOK(unsigned info);
|
||||
|
||||
void txFrame(unsigned char* frame) { return mSIP.txFrame(frame); }
|
||||
int rxFrame(unsigned char* frame) { return mSIP.rxFrame(frame); }
|
||||
bool startDTMF(char key) { return mSIP.startDTMF(key); }
|
||||
void stopDTMF() { mSIP.stopDTMF(); }
|
||||
|
||||
void SIPUser(const std::string& IMSI) { SIPUser(IMSI.c_str()); }
|
||||
void SIPUser(const char* IMSI);
|
||||
void SIPUser(const char* callID, const char *IMSI , const char *origID, const char *origHost);
|
||||
|
||||
const std::string SIPCallID() const { ScopedLock lock(mLock); return mSIP.callID(); }
|
||||
|
||||
// These are called by SIPInterface.
|
||||
void saveINVITE(const osip_message_t* invite, bool local)
|
||||
{ ScopedLock lock(mLock); mSIP.saveINVITE(invite,local); }
|
||||
void saveBYE(const osip_message_t* bye, bool local)
|
||||
{ ScopedLock lock(mLock); mSIP.saveBYE(bye,local); }
|
||||
|
||||
//@}
|
||||
|
||||
unsigned stateAge() const { ScopedLock lock(mLock); return mStateTimer.elapsed(); }
|
||||
|
||||
/**@name Timer access. */
|
||||
//@{
|
||||
|
||||
bool timerExpired(const char* name) const;
|
||||
|
||||
void setTimer(const char* name)
|
||||
{ ScopedLock lock(mLock); return mTimers[name].set(); }
|
||||
|
||||
void setTimer(const char* name, long newLimit)
|
||||
{ ScopedLock lock(mLock); return mTimers[name].set(newLimit); }
|
||||
|
||||
void resetTimer(const char* name)
|
||||
{ ScopedLock lock(mLock); return mTimers[name].reset(); }
|
||||
|
||||
/** Return true if any Q.931 timer is expired. */
|
||||
bool anyTimerExpired() const;
|
||||
|
||||
/** Reset all Q.931 timers. */
|
||||
void resetTimers();
|
||||
|
||||
//@}
|
||||
|
||||
/** Return true if clearing is in progress in the GSM side. */
|
||||
bool clearingGSM() const
|
||||
{ ScopedLock lock(mLock); return (mGSMState==GSM::ReleaseRequest) || (mGSMState==GSM::DisconnectIndication); }
|
||||
|
||||
/** Retrns true if the transaction is "dead". */
|
||||
bool dead() const;
|
||||
|
||||
/** Dump information as text for debugging. */
|
||||
void text(std::ostream&) const;
|
||||
|
||||
private:
|
||||
|
||||
friend class TransactionTable;
|
||||
|
||||
/** Create L3 timers from GSM and Q.931 (network side) */
|
||||
void initTimers();
|
||||
|
||||
/** Echo latest SIPSTATE to the database. */
|
||||
void echoSIPState(SIP::SIPState state) const;
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TransactionEntry&);
|
||||
|
||||
|
||||
/** A map of transactions keyed by ID. */
|
||||
class TransactionMap : public std::map<unsigned,TransactionEntry*> {};
|
||||
|
||||
/**
|
||||
A table for tracking the states of active transactions.
|
||||
*/
|
||||
class TransactionTable {
|
||||
|
||||
private:
|
||||
|
||||
sqlite3 *mDB; ///< database connection
|
||||
|
||||
TransactionMap mTable;
|
||||
mutable Mutex mLock;
|
||||
unsigned mIDCounter;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Create a transaction table.
|
||||
@param path Path fto sqlite3 database file.
|
||||
*/
|
||||
TransactionTable();
|
||||
|
||||
~TransactionTable();
|
||||
|
||||
// TransactionTable does not need a destructor.
|
||||
|
||||
/**
|
||||
Return a new ID for use in the table.
|
||||
*/
|
||||
unsigned newID();
|
||||
|
||||
/**
|
||||
Insert a new entry into the table; deleted by the table later.
|
||||
@param value The entry to insert into the table; will be deleted by the table later.
|
||||
*/
|
||||
void add(TransactionEntry* value);
|
||||
|
||||
/**
|
||||
Find an entry and return a pointer into the table.
|
||||
@param wID The transaction ID to search
|
||||
@return NULL if ID is not found or was dead
|
||||
*/
|
||||
TransactionEntry* find(unsigned wID);
|
||||
|
||||
/**
|
||||
Find the longest-running non-SOS call.
|
||||
@return NULL if there are no calls or if all are SOS.
|
||||
*/
|
||||
TransactionEntry* findLongestCall();
|
||||
|
||||
/**
|
||||
Remove an entry from the table and from gSIPMessageMap.
|
||||
@param wID The transaction ID to search.
|
||||
@return True if the ID was really in the table and deleted.
|
||||
*/
|
||||
bool remove(unsigned wID);
|
||||
|
||||
bool remove(TransactionEntry* transaction) { return remove(transaction->ID()); }
|
||||
|
||||
/**
|
||||
Remove an entry from the table and from gSIPMessageMap,
|
||||
if it is in the Paging state.
|
||||
@param wID The transaction ID to search.
|
||||
@return True if the ID was really in the table and deleted.
|
||||
*/
|
||||
bool removePaging(unsigned wID);
|
||||
|
||||
|
||||
/**
|
||||
Find an entry by its channel pointer.
|
||||
Also clears dead entries during search.
|
||||
@param chan The channel pointer to the first record found.
|
||||
@return pointer to entry or NULL if no active match
|
||||
*/
|
||||
TransactionEntry* find(const GSM::LogicalChannel *chan);
|
||||
|
||||
/**
|
||||
Find an entry in the given state by its mobile ID.
|
||||
Also clears dead entries during search.
|
||||
@param mobileID The mobile to search for.
|
||||
@return pointer to entry or NULL if no match
|
||||
*/
|
||||
TransactionEntry* find(const GSM::L3MobileIdentity& mobileID, GSM::CallState state);
|
||||
|
||||
|
||||
/** Find by subscriber and SIP call ID. */
|
||||
TransactionEntry* find(const GSM::L3MobileIdentity& mobileID, const char* callID);
|
||||
|
||||
/**
|
||||
Find an entry in the Paging state by its mobile ID, change state to AnsweredPaging and reset T3113.
|
||||
Also clears dead entries during search.
|
||||
@param mobileID The mobile to search for.
|
||||
@return pointer to entry or NULL if no match
|
||||
*/
|
||||
TransactionEntry* answeredPaging(const GSM::L3MobileIdentity& mobileID);
|
||||
|
||||
|
||||
/**
|
||||
Find the channel, if any, used for current transactions by this mobile ID.
|
||||
@param mobileID The target mobile subscriber.
|
||||
@return pointer to TCH/FACCH, SDCCH or NULL.
|
||||
*/
|
||||
GSM::LogicalChannel* findChannel(const GSM::L3MobileIdentity& mobileID);
|
||||
|
||||
/** Count the number of transactions using a particular channel. */
|
||||
unsigned countChan(const GSM::LogicalChannel*);
|
||||
|
||||
size_t size() { ScopedLock lock(mLock); return mTable.size(); }
|
||||
|
||||
size_t dump(std::ostream& os) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
friend class TransactionEntry;
|
||||
|
||||
/** Accessor to database connection. */
|
||||
sqlite3* DB() { return mDB; }
|
||||
|
||||
/**
|
||||
Remove "dead" entries from the table.
|
||||
A "dead" entry is a transaction that is no longer active.
|
||||
The caller should hold mLock.
|
||||
*/
|
||||
void clearDeadEntries();
|
||||
|
||||
/**
|
||||
Remove and entry from the table and from gSIPInterface.
|
||||
*/
|
||||
void innerRemove(TransactionMap::iterator);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} //Control
|
||||
|
||||
|
||||
|
||||
/**@addtogroup Globals */
|
||||
//@{
|
||||
/** A single global transaction table in the global namespace. */
|
||||
extern Control::TransactionTable gTransactionTable;
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
495
GSM/GSM610Tables.cpp
Normal file
495
GSM/GSM610Tables.cpp
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "GSM610Tables.h"
|
||||
|
||||
|
||||
/*
|
||||
RFC 3551 RTP A/V Profile July 2003
|
||||
|
||||
|
||||
Octet Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7
|
||||
_____________________________________________________________________
|
||||
0 1 1 0 1 LARc0.0 LARc0.1 LARc0.2 LARc0.3
|
||||
1 LARc0.4 LARc0.5 LARc1.0 LARc1.1 LARc1.2 LARc1.3 LARc1.4 LARc1.5
|
||||
2 LARc2.0 LARc2.1 LARc2.2 LARc2.3 LARc2.4 LARc3.0 LARc3.1 LARc3.2
|
||||
3 LARc3.3 LARc3.4 LARc4.0 LARc4.1 LARc4.2 LARc4.3 LARc5.0 LARc5.1
|
||||
4 LARc5.2 LARc5.3 LARc6.0 LARc6.1 LARc6.2 LARc7.0 LARc7.1 LARc7.2
|
||||
5 Nc0.0 Nc0.1 Nc0.2 Nc0.3 Nc0.4 Nc0.5 Nc0.6 bc0.0
|
||||
6 bc0.1 Mc0.0 Mc0.1 xmaxc00 xmaxc01 xmaxc02 xmaxc03 xmaxc04
|
||||
7 xmaxc05 xmc0.0 xmc0.1 xmc0.2 xmc1.0 xmc1.1 xmc1.2 xmc2.0
|
||||
8 xmc2.1 xmc2.2 xmc3.0 xmc3.1 xmc3.2 xmc4.0 xmc4.1 xmc4.2
|
||||
9 xmc5.0 xmc5.1 xmc5.2 xmc6.0 xmc6.1 xmc6.2 xmc7.0 xmc7.1
|
||||
10 xmc7.2 xmc8.0 xmc8.1 xmc8.2 xmc9.0 xmc9.1 xmc9.2 xmc10.0
|
||||
11 xmc10.1 xmc10.2 xmc11.0 xmc11.1 xmc11.2 xmc12.0 xmc12.1 xcm12.2
|
||||
12 Nc1.0 Nc1.1 Nc1.2 Nc1.3 Nc1.4 Nc1.5 Nc1.6 bc1.0
|
||||
13 bc1.1 Mc1.0 Mc1.1 xmaxc10 xmaxc11 xmaxc12 xmaxc13 xmaxc14
|
||||
14 xmax15 xmc13.0 xmc13.1 xmc13.2 xmc14.0 xmc14.1 xmc14.2 xmc15.0
|
||||
15 xmc15.1 xmc15.2 xmc16.0 xmc16.1 xmc16.2 xmc17.0 xmc17.1 xmc17.2
|
||||
16 xmc18.0 xmc18.1 xmc18.2 xmc19.0 xmc19.1 xmc19.2 xmc20.0 xmc20.1
|
||||
17 xmc20.2 xmc21.0 xmc21.1 xmc21.2 xmc22.0 xmc22.1 xmc22.2 xmc23.0
|
||||
18 xmc23.1 xmc23.2 xmc24.0 xmc24.1 xmc24.2 xmc25.0 xmc25.1 xmc25.2
|
||||
19 Nc2.0 Nc2.1 Nc2.2 Nc2.3 Nc2.4 Nc2.5 Nc2.6 bc2.0
|
||||
20 bc2.1 Mc2.0 Mc2.1 xmaxc20 xmaxc21 xmaxc22 xmaxc23 xmaxc24
|
||||
21 xmaxc25 xmc26.0 xmc26.1 xmc26.2 xmc27.0 xmc27.1 xmc27.2 xmc28.0
|
||||
22 xmc28.1 xmc28.2 xmc29.0 xmc29.1 xmc29.2 xmc30.0 xmc30.1 xmc30.2
|
||||
23 xmc31.0 xmc31.1 xmc31.2 xmc32.0 xmc32.1 xmc32.2 xmc33.0 xmc33.1
|
||||
24 xmc33.2 xmc34.0 xmc34.1 xmc34.2 xmc35.0 xmc35.1 xmc35.2 xmc36.0
|
||||
25 Xmc36.1 xmc36.2 xmc37.0 xmc37.1 xmc37.2 xmc38.0 xmc38.1 xmc38.2
|
||||
26 Nc3.0 Nc3.1 Nc3.2 Nc3.3 Nc3.4 Nc3.5 Nc3.6 bc3.0
|
||||
27 bc3.1 Mc3.0 Mc3.1 xmaxc30 xmaxc31 xmaxc32 xmaxc33 xmaxc34
|
||||
28 xmaxc35 xmc39.0 xmc39.1 xmc39.2 xmc40.0 xmc40.1 xmc40.2 xmc41.0
|
||||
29 xmc41.1 xmc41.2 xmc42.0 xmc42.1 xmc42.2 xmc43.0 xmc43.1 xmc43.2
|
||||
30 xmc44.0 xmc44.1 xmc44.2 xmc45.0 xmc45.1 xmc45.2 xmc46.0 xmc46.1
|
||||
31 xmc46.2 xmc47.0 xmc47.1 xmc47.2 xmc48.0 xmc48.1 xmc48.2 xmc49.0
|
||||
32 xmc49.1 xmc49.2 xmc50.0 xmc50.1 xmc50.2 xmc51.0 xmc51.1 xmc51.2
|
||||
|
||||
Table 3: GSM payload format
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
This file encodes a mapping between
|
||||
GSM 05.03 Table 2 and RFC-3551 Table 3.
|
||||
*/
|
||||
|
||||
/*
|
||||
Naming convention:
|
||||
xxx_p position (bit index)
|
||||
xxx_l length (bit field length)
|
||||
LAR log area ratio
|
||||
N LTP lag
|
||||
b LTP gain
|
||||
M grid
|
||||
Xmax block amplitude
|
||||
x RPE pulses
|
||||
*/
|
||||
|
||||
|
||||
/**@name Lengths of GSM 06.10 fields */
|
||||
//@{
|
||||
const unsigned int LAR1_l=6; ///< log area ratio
|
||||
const unsigned int LAR2_l=6; ///< log area ratio
|
||||
const unsigned int LAR3_l=5; ///< log area ratio
|
||||
const unsigned int LAR4_l=5; ///< log area ratio
|
||||
const unsigned int LAR5_l=4; ///< log area ratio
|
||||
const unsigned int LAR6_l=4; ///< log area ratio
|
||||
const unsigned int LAR7_l=3; ///< log area ratio
|
||||
const unsigned int LAR8_l=3; ///< log area ratio
|
||||
const unsigned int N_l=7; ///< LTP lag
|
||||
const unsigned int b_l=2; ///< LTP gain
|
||||
const unsigned int M_l=2; ///< grid position
|
||||
const unsigned int Xmax_l=6; ///< block amplitude
|
||||
const unsigned int x_l=3; ///< RPE pulses
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/*@name Indecies of GSM 06.10 fields as they appear in RFC-3551 Table 3. */
|
||||
//@{
|
||||
|
||||
/**@name Log area ratios, apply to whole frame. */
|
||||
//@{
|
||||
const unsigned int LAR1_p = 0;
|
||||
const unsigned int LAR2_p = LAR1_p + LAR1_l;
|
||||
const unsigned int LAR3_p = LAR2_p + LAR2_l;
|
||||
const unsigned int LAR4_p = LAR3_p + LAR3_l;
|
||||
const unsigned int LAR5_p = LAR4_p + LAR4_l;
|
||||
const unsigned int LAR6_p = LAR5_p + LAR5_l;
|
||||
const unsigned int LAR7_p = LAR6_p + LAR6_l;
|
||||
const unsigned int LAR8_p = LAR7_p + LAR7_l;
|
||||
//@}
|
||||
/**@name Subframe 1 */
|
||||
//@{
|
||||
const unsigned int N1_p = LAR8_p + LAR8_l;
|
||||
const unsigned int b1_p = N1_p + N_l;
|
||||
const unsigned int M1_p = b1_p + b_l;
|
||||
const unsigned int Xmax1_p = M1_p + M_l;
|
||||
const unsigned int x1_0_p = Xmax1_p + Xmax_l;
|
||||
const unsigned int x1_1_p = x1_0_p + x_l;
|
||||
const unsigned int x1_2_p = x1_1_p + x_l;
|
||||
const unsigned int x1_3_p = x1_2_p + x_l;
|
||||
const unsigned int x1_4_p = x1_3_p + x_l;
|
||||
const unsigned int x1_5_p = x1_4_p + x_l;
|
||||
const unsigned int x1_6_p = x1_5_p + x_l;
|
||||
const unsigned int x1_7_p = x1_6_p + x_l;
|
||||
const unsigned int x1_8_p = x1_7_p + x_l;
|
||||
const unsigned int x1_9_p = x1_8_p + x_l;
|
||||
const unsigned int x1_10_p = x1_9_p + x_l;
|
||||
const unsigned int x1_11_p = x1_10_p + x_l;
|
||||
const unsigned int x1_12_p = x1_11_p + x_l;
|
||||
//@}
|
||||
/**@name Subframe 2 */
|
||||
//@{
|
||||
const unsigned int N2_p = x1_12_p + x_l;
|
||||
const unsigned int b2_p = N2_p + N_l;
|
||||
const unsigned int M2_p = b2_p + b_l;
|
||||
const unsigned int Xmax2_p = M2_p + M_l;
|
||||
const unsigned int x2_0_p = Xmax2_p + Xmax_l;
|
||||
const unsigned int x2_1_p = x2_0_p + x_l;
|
||||
const unsigned int x2_2_p = x2_1_p + x_l;
|
||||
const unsigned int x2_3_p = x2_2_p + x_l;
|
||||
const unsigned int x2_4_p = x2_3_p + x_l;
|
||||
const unsigned int x2_5_p = x2_4_p + x_l;
|
||||
const unsigned int x2_6_p = x2_5_p + x_l;
|
||||
const unsigned int x2_7_p = x2_6_p + x_l;
|
||||
const unsigned int x2_8_p = x2_7_p + x_l;
|
||||
const unsigned int x2_9_p = x2_8_p + x_l;
|
||||
const unsigned int x2_10_p = x2_9_p + x_l;
|
||||
const unsigned int x2_11_p = x2_10_p + x_l;
|
||||
const unsigned int x2_12_p = x2_11_p + x_l;
|
||||
//@}
|
||||
/**@mame Subframe 3 */
|
||||
//@{
|
||||
const unsigned int N3_p = x2_12_p + x_l;
|
||||
const unsigned int b3_p = N3_p + N_l;
|
||||
const unsigned int M3_p = b3_p + b_l;
|
||||
const unsigned int Xmax3_p = M3_p + M_l;
|
||||
const unsigned int x3_0_p = Xmax3_p + Xmax_l;
|
||||
const unsigned int x3_1_p = x3_0_p + x_l;
|
||||
const unsigned int x3_2_p = x3_1_p + x_l;
|
||||
const unsigned int x3_3_p = x3_2_p + x_l;
|
||||
const unsigned int x3_4_p = x3_3_p + x_l;
|
||||
const unsigned int x3_5_p = x3_4_p + x_l;
|
||||
const unsigned int x3_6_p = x3_5_p + x_l;
|
||||
const unsigned int x3_7_p = x3_6_p + x_l;
|
||||
const unsigned int x3_8_p = x3_7_p + x_l;
|
||||
const unsigned int x3_9_p = x3_8_p + x_l;
|
||||
const unsigned int x3_10_p = x3_9_p + x_l;
|
||||
const unsigned int x3_11_p = x3_10_p + x_l;
|
||||
const unsigned int x3_12_p = x3_11_p + x_l;
|
||||
//@}
|
||||
/**@name Subframe 4 */
|
||||
//@{
|
||||
const unsigned int N4_p = x3_12_p + x_l;
|
||||
const unsigned int b4_p = N4_p + N_l;
|
||||
const unsigned int M4_p = b4_p + b_l;
|
||||
const unsigned int Xmax4_p = M4_p + M_l;
|
||||
const unsigned int x4_0_p = Xmax4_p + Xmax_l;
|
||||
const unsigned int x4_1_p = x4_0_p + x_l;
|
||||
const unsigned int x4_2_p = x4_1_p + x_l;
|
||||
const unsigned int x4_3_p = x4_2_p + x_l;
|
||||
const unsigned int x4_4_p = x4_3_p + x_l;
|
||||
const unsigned int x4_5_p = x4_4_p + x_l;
|
||||
const unsigned int x4_6_p = x4_5_p + x_l;
|
||||
const unsigned int x4_7_p = x4_6_p + x_l;
|
||||
const unsigned int x4_8_p = x4_7_p + x_l;
|
||||
const unsigned int x4_9_p = x4_8_p + x_l;
|
||||
const unsigned int x4_10_p = x4_9_p + x_l;
|
||||
const unsigned int x4_11_p = x4_10_p + x_l;
|
||||
const unsigned int x4_12_p = x4_11_p + x_l;
|
||||
//@}
|
||||
//@}
|
||||
|
||||
|
||||
/*
|
||||
This array encodes GSM 05.03 Table 2.
|
||||
It's also GSM 06.10 Table A2.1a.
|
||||
This is the order of bits as they appear in
|
||||
the d[] bits of the GSM TCH/F.
|
||||
RTP[4+g610BitOrder[i]] <=> GSM[i]
|
||||
*/
|
||||
unsigned int GSM::g610BitOrder[260] = {
|
||||
/**@name importance class 1 */
|
||||
//@{
|
||||
/** LAR1:5 */ LAR1_p+LAR1_l-1-5, /* bit 0 */
|
||||
/** Xmax1:5 */ Xmax1_p+Xmax_l-1-5,
|
||||
/** Xmax2:5 */ Xmax2_p+Xmax_l-1-5,
|
||||
/** Xmax3:5 */ Xmax3_p+Xmax_l-1-5,
|
||||
/** Xmax4:5 */ Xmax4_p+Xmax_l-1-5,
|
||||
//@}
|
||||
/**@name importance class 2 */
|
||||
//@{
|
||||
/** LAR1:4 */ LAR1_p+LAR1_l-1-4,
|
||||
/** LAR2:5 */ LAR2_p+LAR2_l-1-5,
|
||||
/** LAR3:4 */ LAR3_p+LAR3_l-1-4,
|
||||
//@}
|
||||
/**@name importance class 3 */
|
||||
//@{
|
||||
/** LAR1:3 */ LAR1_p+LAR1_l-1-3,
|
||||
/** LAR2:4 */ LAR2_p+LAR2_l-1-4,
|
||||
/** LAR3:3 */ LAR3_p+LAR3_l-1-3, /* bit 10 */
|
||||
/** LAR4:4 */ LAR4_p+LAR4_l-1-4,
|
||||
/** N1:6 */ N1_p+N_l-1-6,
|
||||
/** N2:6 */ N2_p+N_l-1-6,
|
||||
/** N3:6 */ N3_p+N_l-1-6,
|
||||
/** N4:6 */ N4_p+N_l-1-6,
|
||||
/** Xmax1:4 */ Xmax1_p+Xmax_l-1-4,
|
||||
/** Xmax2:4 */ Xmax2_p+Xmax_l-1-4,
|
||||
/** Xmax3:4 */ Xmax3_p+Xmax_l-1-4,
|
||||
/** Xmax4:4 */ Xmax4_p+Xmax_l-1-4,
|
||||
/** LAR2:3 */ LAR2_p+LAR2_l-1-3, /* bit 20 */
|
||||
/** LAR5:3 */ LAR5_p+LAR5_l-1-3,
|
||||
/** LAR6:3 */ LAR6_p+LAR6_l-1-3,
|
||||
/** N1:5 */ N1_p+N_l-1-5,
|
||||
/** N2:5 */ N2_p+N_l-1-5,
|
||||
/** N3:5 */ N3_p+N_l-1-5,
|
||||
/** N4:5 */ N4_p+N_l-1-5,
|
||||
/** N1:4 */ N1_p+N_l-1-4,
|
||||
/** N2:4 */ N2_p+N_l-1-4,
|
||||
/** N3:4 */ N3_p+N_l-1-4,
|
||||
/** N4:4 */ N4_p+N_l-1-4, /* bit 30 */
|
||||
/** N1:3 */ N1_p+N_l-1-3,
|
||||
/** N2:3 */ N2_p+N_l-1-3,
|
||||
/** N3:3 */ N3_p+N_l-1-3,
|
||||
/** N4:3 */ N4_p+N_l-1-3,
|
||||
/** N1:2 */ N1_p+N_l-1-2,
|
||||
/** N2:2 */ N2_p+N_l-1-2,
|
||||
/** N3:2 */ N3_p+N_l-1-2,
|
||||
/** N4:2 */ N4_p+N_l-1-2,
|
||||
//@}
|
||||
/**@name importance class 4 */
|
||||
//@{
|
||||
/** Xmax1:3 */ Xmax1_p+Xmax_l-1-3,
|
||||
/** Xmax2:3 */ Xmax2_p+Xmax_l-1-3, /* bit 40 */
|
||||
/** Xmax3:3 */ Xmax3_p+Xmax_l-1-3,
|
||||
/** Xmax4:3 */ Xmax4_p+Xmax_l-1-3,
|
||||
/** LAR1:2 */ LAR1_p+LAR1_l-1-2,
|
||||
/** LAR4:3 */ LAR4_p+LAR4_l-1-3,
|
||||
/** LAR7:2 */ LAR7_p+LAR7_l-1-2,
|
||||
/** N1:1 */ N1_p+N_l-1-1,
|
||||
/** N2:1 */ N2_p+N_l-1-1,
|
||||
/** N3:1 */ N3_p+N_l-1-1,
|
||||
/** N4:1 */ N4_p+N_l-1-1,
|
||||
/** LAR5:2 */ LAR5_p+LAR5_l-1-2, /* bit 50 */
|
||||
/** LAR6:2 */ LAR6_p+LAR6_l-1-2,
|
||||
/** b1:1 */ b1_p+b_l-1-1,
|
||||
/** b2:1 */ b2_p+b_l-1-1,
|
||||
/** b3:1 */ b3_p+b_l-1-1,
|
||||
/** b4:1 */ b4_p+b_l-1-1,
|
||||
/** N1:0 */ N1_p+N_l-1-0,
|
||||
/** N2:0 */ N2_p+N_l-1-0,
|
||||
/** N3:0 */ N3_p+N_l-1-0,
|
||||
/** N4:0 */ N4_p+N_l-1-0,
|
||||
/** M1:1 */ M1_p+M_l-1-1, /* bit 60 */
|
||||
/** M2:1 */ M2_p+M_l-1-1,
|
||||
/** M3:1 */ M3_p+M_l-1-1,
|
||||
/** M4:1 */ M4_p+M_l-1-1,
|
||||
//@}
|
||||
/**@name importance class 5 */
|
||||
//@{
|
||||
/** LAR1:1 */ LAR1_p+LAR1_l-1-1,
|
||||
/** LAR2:2 */ LAR2_p+LAR2_l-1-2,
|
||||
/** LAR3:2 */ LAR3_p+LAR3_l-1-2,
|
||||
/** LAR8:2 */ LAR8_p+LAR8_l-1-2,
|
||||
/** LAR4:2 */ LAR4_p+LAR4_l-1-2,
|
||||
/** LAR5:1 */ LAR5_p+LAR5_l-1-1,
|
||||
/** LAR7:1 */ LAR7_p+LAR7_l-1-1, /* bit 70 */
|
||||
/** b1:0 */ b1_p+b_l-1-0,
|
||||
/** b2:0 */ b2_p+b_l-1-0,
|
||||
/** b3:0 */ b3_p+b_l-1-0,
|
||||
/** b4:0 */ b4_p+b_l-1-0,
|
||||
/** Xmax1:2 */ Xmax1_p+Xmax_l-1-2,
|
||||
/** Xmax2:2 */ Xmax2_p+Xmax_l-1-2,
|
||||
/** Xmax3:2 */ Xmax3_p+Xmax_l-1-2,
|
||||
/** Xmax4:2 */ Xmax4_p+Xmax_l-1-2,
|
||||
/** x1_0:2 */ x1_0_p+x_l-1-2,
|
||||
/** x1_1:2 */ x1_1_p+x_l-1-2, /* bit 80 */
|
||||
/** x1_2:2 */ x1_2_p+x_l-1-2,
|
||||
/** x1_3:2 */ x1_3_p+x_l-1-2,
|
||||
/** x1_4:2 */ x1_4_p+x_l-1-2,
|
||||
/** x1_5:2 */ x1_5_p+x_l-1-2,
|
||||
/** x1_6:2 */ x1_6_p+x_l-1-2,
|
||||
/** x1_7:2 */ x1_7_p+x_l-1-2,
|
||||
/** x1_8:2 */ x1_8_p+x_l-1-2,
|
||||
/** x1_9:2 */ x1_9_p+x_l-1-2,
|
||||
/** x1_10:2 */ x1_10_p+x_l-1-2,
|
||||
/** x1_11:2 */ x1_11_p+x_l-1-2, /* bit 90 */
|
||||
/** x1_12:2 */ x1_12_p+x_l-1-2,
|
||||
/** x2_0:2 */ x2_0_p+x_l-1-2,
|
||||
/** x2_1:2 */ x2_1_p+x_l-1-2,
|
||||
/** x2_2:2 */ x2_2_p+x_l-1-2,
|
||||
/** x2_3:2 */ x2_3_p+x_l-1-2,
|
||||
/** x2_4:2 */ x2_4_p+x_l-1-2,
|
||||
/** x2_5:2 */ x2_5_p+x_l-1-2,
|
||||
/** x2_6:2 */ x2_6_p+x_l-1-2,
|
||||
/** x2_7:2 */ x2_7_p+x_l-1-2,
|
||||
/** x2_8:2 */ x2_8_p+x_l-1-2, /* bit 100 */
|
||||
/** x2_9:2 */ x2_9_p+x_l-1-2,
|
||||
/** x2_10:2 */ x2_10_p+x_l-1-2,
|
||||
/** x2_11:2 */ x2_11_p+x_l-1-2,
|
||||
/** x2_12:2 */ x2_12_p+x_l-1-2,
|
||||
/** x3_0:2 */ x3_0_p+x_l-1-2,
|
||||
/** x3_1:2 */ x3_1_p+x_l-1-2,
|
||||
/** x3_2:2 */ x3_2_p+x_l-1-2,
|
||||
/** x3_3:2 */ x3_3_p+x_l-1-2,
|
||||
/** x3_4:2 */ x3_4_p+x_l-1-2,
|
||||
/** x3_5:2 */ x3_5_p+x_l-1-2, /* bit 110 */
|
||||
/** x3_6:2 */ x3_6_p+x_l-1-2,
|
||||
/** x3_7:2 */ x3_7_p+x_l-1-2,
|
||||
/** x3_8:2 */ x3_8_p+x_l-1-2,
|
||||
/** x3_9:2 */ x3_9_p+x_l-1-2,
|
||||
/** x3_10:2 */ x3_10_p+x_l-1-2,
|
||||
/** x3_11:2 */ x3_11_p+x_l-1-2,
|
||||
/** x3_12:2 */ x3_12_p+x_l-1-2,
|
||||
/** x4_0:2 */ x4_0_p+x_l-1-2,
|
||||
/** x4_1:2 */ x4_1_p+x_l-1-2,
|
||||
/** x4_2:2 */ x4_2_p+x_l-1-2, /* bit 120 */
|
||||
/** x4_3:2 */ x4_3_p+x_l-1-2,
|
||||
/** x4_4:2 */ x4_4_p+x_l-1-2,
|
||||
/** x4_5:2 */ x4_5_p+x_l-1-2,
|
||||
/** x4_6:2 */ x4_6_p+x_l-1-2,
|
||||
/** x4_7:2 */ x4_7_p+x_l-1-2,
|
||||
/** x4_8:2 */ x4_8_p+x_l-1-2,
|
||||
/** x4_9:2 */ x4_9_p+x_l-1-2,
|
||||
/** x4_10:2 */ x4_10_p+x_l-1-2,
|
||||
/** x4_11:2 */ x4_11_p+x_l-1-2,
|
||||
/** x4_12:2 */ x4_12_p+x_l-1-2, /* bit 130 */
|
||||
/** M1:0 */ M1_p+M_l-1-0,
|
||||
/** M2:0 */ M2_p+M_l-1-0,
|
||||
/** M3:0 */ M3_p+M_l-1-0,
|
||||
/** M4:0 */ M4_p+M_l-1-0,
|
||||
/** Xmax1:1 */ Xmax1_p+Xmax_l-1-1,
|
||||
/** Xmax2:1 */ Xmax2_p+Xmax_l-1-1,
|
||||
/** Xmax3:1 */ Xmax3_p+Xmax_l-1-1,
|
||||
/** Xmax4:1 */ Xmax4_p+Xmax_l-1-1,
|
||||
/** x1_0:1 */ x1_0_p+x_l-1-1,
|
||||
/** x1_1:1 */ x1_1_p+x_l-1-1, /* bit 140 */
|
||||
/** x1_2:1 */ x1_2_p+x_l-1-1,
|
||||
/** x1_3:1 */ x1_3_p+x_l-1-1,
|
||||
/** x1_4:1 */ x1_4_p+x_l-1-1,
|
||||
/** x1_5:1 */ x1_5_p+x_l-1-1,
|
||||
/** x1_6:1 */ x1_6_p+x_l-1-1,
|
||||
/** x1_7:1 */ x1_7_p+x_l-1-1,
|
||||
/** x1_8:1 */ x1_8_p+x_l-1-1,
|
||||
/** x1_9:1 */ x1_9_p+x_l-1-1,
|
||||
/** x1_10:1 */ x1_10_p+x_l-1-1,
|
||||
/** x1_11:1 */ x1_11_p+x_l-1-1, /* bit 150 */
|
||||
/** x1_12:1 */ x1_12_p+x_l-1-1,
|
||||
/** x2_0:1 */ x2_0_p+x_l-1-1,
|
||||
/** x2_1:1 */ x2_1_p+x_l-1-1,
|
||||
/** x2_2:1 */ x2_2_p+x_l-1-1,
|
||||
/** x2_3:1 */ x2_3_p+x_l-1-1,
|
||||
/** x2_4:1 */ x2_4_p+x_l-1-1,
|
||||
/** x2_5:1 */ x2_5_p+x_l-1-1,
|
||||
/** x2_6:1 */ x2_6_p+x_l-1-1,
|
||||
/** x2_7:1 */ x2_7_p+x_l-1-1,
|
||||
/** x2_8:1 */ x2_8_p+x_l-1-1, /* bit 160 */
|
||||
/** x2_9:1 */ x2_9_p+x_l-1-1,
|
||||
/** x2_10:1 */ x2_10_p+x_l-1-1,
|
||||
/** x2_11:1 */ x2_11_p+x_l-1-1,
|
||||
/** x2_12:1 */ x2_12_p+x_l-1-1,
|
||||
/** x3_0:1 */ x3_0_p+x_l-1-1,
|
||||
/** x3_1:1 */ x3_1_p+x_l-1-1,
|
||||
/** x3_2:1 */ x3_2_p+x_l-1-1,
|
||||
/** x3_3:1 */ x3_3_p+x_l-1-1,
|
||||
/** x3_4:1 */ x3_4_p+x_l-1-1,
|
||||
/** x3_5:1 */ x3_5_p+x_l-1-1, /* bit 170 */
|
||||
/** x3_6:1 */ x3_6_p+x_l-1-1,
|
||||
/** x3_7:1 */ x3_7_p+x_l-1-1,
|
||||
/** x3_8:1 */ x3_8_p+x_l-1-1,
|
||||
/** x3_9:1 */ x3_9_p+x_l-1-1,
|
||||
/** x3_10:1 */ x3_10_p+x_l-1-1,
|
||||
/** x3_11:1 */ x3_11_p+x_l-1-1,
|
||||
/** x3_12:1 */ x3_12_p+x_l-1-1,
|
||||
/** x4_0:1 */ x4_0_p+x_l-1-1,
|
||||
/** x4_1:1 */ x4_1_p+x_l-1-1,
|
||||
/** x4_2:1 */ x4_2_p+x_l-1-1, /* bit 180 */
|
||||
/** x4_3:1 */ x4_3_p+x_l-1-1,
|
||||
//@}
|
||||
/**@name importance class 6 */
|
||||
//@{
|
||||
/** x4_4:1 */ x4_4_p+x_l-1-1,
|
||||
/** x4_5:1 */ x4_5_p+x_l-1-1,
|
||||
/** x4_6:1 */ x4_6_p+x_l-1-1,
|
||||
/** x4_7:1 */ x4_7_p+x_l-1-1,
|
||||
/** x4_8:1 */ x4_8_p+x_l-1-1,
|
||||
/** x4_9:1 */ x4_9_p+x_l-1-1,
|
||||
/** x4_10:1 */ x4_10_p+x_l-1-1,
|
||||
/** x4_11:1 */ x4_11_p+x_l-1-1,
|
||||
/** x4_12:1 */ x4_12_p+x_l-1-1, /* bit 190 */
|
||||
/** LAR1:0 */ LAR1_p+LAR1_l-1-0,
|
||||
/** LAR2:1 */ LAR2_p+LAR2_l-1-1,
|
||||
/** LAR3:1 */ LAR3_p+LAR3_l-1-1,
|
||||
/** LAR6:1 */ LAR6_p+LAR6_l-1-1,
|
||||
/** LAR7:0 */ LAR7_p+LAR7_l-1-0,
|
||||
/** LAR8:1 */ LAR8_p+LAR8_l-1-1,
|
||||
/** LAR8:0 */ LAR8_p+LAR8_l-1-0,
|
||||
/** LAR3:0 */ LAR3_p+LAR3_l-1-0,
|
||||
/** LAR4:1 */ LAR4_p+LAR4_l-1-1,
|
||||
/** LAR4:0 */ LAR4_p+LAR4_l-1-0,
|
||||
/** LAR5:0 */ LAR5_p+LAR5_l-1-0,
|
||||
/** Xmax1:0 */ Xmax1_p+Xmax_l-1-0,
|
||||
/** Xmax2:0 */ Xmax2_p+Xmax_l-1-0,
|
||||
/** Xmax3:0 */ Xmax3_p+Xmax_l-1-0,
|
||||
/** Xmax4:0 */ Xmax4_p+Xmax_l-1-0,
|
||||
/** x1_0:0 */ x1_0_p+x_l-1-0,
|
||||
/** x1_1:0 */ x1_1_p+x_l-1-0,
|
||||
/** x1_2:0 */ x1_2_p+x_l-1-0,
|
||||
/** x1_3:0 */ x1_3_p+x_l-1-0,
|
||||
/** x1_4:0 */ x1_4_p+x_l-1-0,
|
||||
/** x1_5:0 */ x1_5_p+x_l-1-0,
|
||||
/** x1_6:0 */ x1_6_p+x_l-1-0,
|
||||
/** x1_7:0 */ x1_7_p+x_l-1-0,
|
||||
/** x1_8:0 */ x1_8_p+x_l-1-0,
|
||||
/** x1_9:0 */ x1_9_p+x_l-1-0,
|
||||
/** x1_10:0 */ x1_10_p+x_l-1-0,
|
||||
/** x1_11:0 */ x1_11_p+x_l-1-0,
|
||||
/** x1_12:0 */ x1_12_p+x_l-1-0,
|
||||
/** x2_0:0 */ x2_0_p+x_l-1-0,
|
||||
/** x2_1:0 */ x2_1_p+x_l-1-0,
|
||||
/** x2_2:0 */ x2_2_p+x_l-1-0,
|
||||
/** x2_3:0 */ x2_3_p+x_l-1-0,
|
||||
/** x2_4:0 */ x2_4_p+x_l-1-0,
|
||||
/** x2_5:0 */ x2_5_p+x_l-1-0,
|
||||
/** x2_6:0 */ x2_6_p+x_l-1-0,
|
||||
/** x2_7:0 */ x2_7_p+x_l-1-0,
|
||||
/** x2_8:0 */ x2_8_p+x_l-1-0,
|
||||
/** x2_9:0 */ x2_9_p+x_l-1-0,
|
||||
/** x2_10:0 */ x2_10_p+x_l-1-0,
|
||||
/** x2_11:0 */ x2_11_p+x_l-1-0,
|
||||
/** x2_12:0 */ x2_12_p+x_l-1-0,
|
||||
/** x3_0:0 */ x3_0_p+x_l-1-0,
|
||||
/** x3_1:0 */ x3_1_p+x_l-1-0,
|
||||
/** x3_2:0 */ x3_2_p+x_l-1-0,
|
||||
/** x3_3:0 */ x3_3_p+x_l-1-0,
|
||||
/** x3_4:0 */ x3_4_p+x_l-1-0,
|
||||
/** x3_5:0 */ x3_5_p+x_l-1-0,
|
||||
/** x3_6:0 */ x3_6_p+x_l-1-0,
|
||||
/** x3_7:0 */ x3_7_p+x_l-1-0,
|
||||
/** x3_8:0 */ x3_8_p+x_l-1-0,
|
||||
/** x3_9:0 */ x3_9_p+x_l-1-0,
|
||||
/** x3_10:0 */ x3_10_p+x_l-1-0,
|
||||
/** x3_11:0 */ x3_11_p+x_l-1-0,
|
||||
/** x3_12:0 */ x3_12_p+x_l-1-0,
|
||||
/** x4_0:0 */ x4_0_p+x_l-1-0,
|
||||
/** x4_1:0 */ x4_1_p+x_l-1-0,
|
||||
/** x4_2:0 */ x4_2_p+x_l-1-0,
|
||||
/** x4_3:0 */ x4_3_p+x_l-1-0,
|
||||
/** x4_4:0 */ x4_4_p+x_l-1-0,
|
||||
/** x4_5:0 */ x4_5_p+x_l-1-0,
|
||||
/** x4_6:0 */ x4_6_p+x_l-1-0,
|
||||
/** x4_7:0 */ x4_7_p+x_l-1-0,
|
||||
/** x4_8:0 */ x4_8_p+x_l-1-0,
|
||||
/** x4_9:0 */ x4_9_p+x_l-1-0,
|
||||
/** x4_10:0 */ x4_10_p+x_l-1-0,
|
||||
/** x4_11:0 */ x4_11_p+x_l-1-0,
|
||||
/** x4_12:0 */ x4_12_p+x_l-1-0,
|
||||
/** LAR2:0 */ LAR2_p+LAR2_l-1-0,
|
||||
/** LAR6:0 */ LAR6_p+LAR6_l-1-0
|
||||
//@}
|
||||
};
|
||||
|
||||
30
GSM/GSM610Tables.h
Normal file
30
GSM/GSM610Tables.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSM610TABLES_H
|
||||
#define GSM610TABLES_H
|
||||
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
/** Table #2 from GSM 05.03 */
|
||||
extern unsigned int g610BitOrder[260];
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
396
GSM/GSMCommon.cpp
Normal file
396
GSM/GSMCommon.cpp
Normal file
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "GSMCommon.h"
|
||||
|
||||
using namespace GSM;
|
||||
using namespace std;
|
||||
|
||||
|
||||
const char* GSM::CallStateString(GSM::CallState state)
|
||||
{
|
||||
switch (state) {
|
||||
case NullState: return "null";;
|
||||
case Paging: return "paging";
|
||||
case AnsweredPaging: return "answered-paging";
|
||||
case MOCInitiated: return "MOC-initiated";
|
||||
case MOCProceeding: return "MOC-proceeding";
|
||||
case MTCConfirmed: return "MTC-confirmed";
|
||||
case CallReceived: return "call-received";
|
||||
case CallPresent: return "call-present";
|
||||
case ConnectIndication: return "connect-indication";
|
||||
case Active: return "active";
|
||||
case DisconnectIndication: return "disconnect-indication";
|
||||
case ReleaseRequest: return "release-request";
|
||||
case SMSDelivering: return "SMS-delivery";
|
||||
case SMSSubmitting: return "SMS-submission";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, GSM::CallState state)
|
||||
{
|
||||
const char* str = GSM::CallStateString(state);
|
||||
if (str) os << str;
|
||||
else os << "?" << state << "?";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L3PD val)
|
||||
{
|
||||
switch (val) {
|
||||
case L3CallControlPD: os << "Call Control"; break;
|
||||
case L3MobilityManagementPD: os << "Mobility Management"; break;
|
||||
case L3RadioResourcePD: os << "Radio Resource"; break;
|
||||
default: os << hex << "0x" << (int)val << dec;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
const BitVector GSM::gTrainingSequence[] = {
|
||||
BitVector("00100101110000100010010111"),
|
||||
BitVector("00101101110111100010110111"),
|
||||
BitVector("01000011101110100100001110"),
|
||||
BitVector("01000111101101000100011110"),
|
||||
BitVector("00011010111001000001101011"),
|
||||
BitVector("01001110101100000100111010"),
|
||||
BitVector("10100111110110001010011111"),
|
||||
BitVector("11101111000100101110111100"),
|
||||
};
|
||||
|
||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||
|
||||
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
|
||||
|
||||
|
||||
|
||||
unsigned char GSM::encodeGSMChar(unsigned char ascii)
|
||||
{
|
||||
// Given an ASCII char, return the corresponding GSM char.
|
||||
// Do it with a lookup table, generated on the first call.
|
||||
// You might be tempted to replace this init with some more clever NULL-pointer trick.
|
||||
// -- Don't. This is thread-safe.
|
||||
static char reverseTable[256]={'?'};
|
||||
static volatile bool init = false;
|
||||
if (!init) {
|
||||
for (size_t i=0; i<sizeof(gGSMAlphabet); i++) {
|
||||
reverseTable[(unsigned)gGSMAlphabet[i]]=i;
|
||||
}
|
||||
// Set the flag last to be thread-safe.
|
||||
init=true;
|
||||
}
|
||||
return reverseTable[(unsigned)ascii];
|
||||
}
|
||||
|
||||
|
||||
char GSM::encodeBCDChar(char ascii)
|
||||
{
|
||||
// Given an ASCII char, return the corresponding BCD.
|
||||
if ((ascii>='0') && (ascii<='9')) return ascii-'0';
|
||||
switch (ascii) {
|
||||
case '.': return 11;
|
||||
case '*': return 11;
|
||||
case '#': return 12;
|
||||
case 'a': return 13;
|
||||
case 'b': return 14;
|
||||
case 'c': return 15;
|
||||
default: return 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned GSM::uplinkFreqKHz(GSMBand band, unsigned ARFCN)
|
||||
{
|
||||
switch (band) {
|
||||
case GSM850:
|
||||
assert((ARFCN<252)&&(ARFCN>129));
|
||||
return 824200+200*(ARFCN-128);
|
||||
case EGSM900:
|
||||
if (ARFCN<=124) return 890000+200*ARFCN;
|
||||
assert((ARFCN>974)&&(ARFCN<1024));
|
||||
return 890000+200*(ARFCN-1024);
|
||||
case DCS1800:
|
||||
assert((ARFCN>511)&&(ARFCN<886));
|
||||
return 1710200+200*(ARFCN-512);
|
||||
case PCS1900:
|
||||
assert((ARFCN>511)&&(ARFCN<811));
|
||||
return 1850200+200*(ARFCN-512);
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned GSM::uplinkOffsetKHz(GSMBand band)
|
||||
{
|
||||
switch (band) {
|
||||
case GSM850: return 45000;
|
||||
case EGSM900: return 45000;
|
||||
case DCS1800: return 95000;
|
||||
case PCS1900: return 80000;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned GSM::downlinkFreqKHz(GSMBand band, unsigned ARFCN)
|
||||
{
|
||||
return uplinkFreqKHz(band,ARFCN) + uplinkOffsetKHz(band);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// See GSM 04.08 Table 10.5.68.
|
||||
const unsigned GSM::RACHSpreadSlots[16] =
|
||||
{
|
||||
3,4,5,6,
|
||||
7,8,9,10,
|
||||
11,12,14,16,
|
||||
20,25,32,50
|
||||
};
|
||||
|
||||
// See GSM 04.08 Table 3.1
|
||||
const unsigned GSM::RACHWaitSParam[16] =
|
||||
{
|
||||
55,76,109,163,217,
|
||||
55,76,109,163,217,
|
||||
55,76,109,163,217,
|
||||
55
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
|
||||
{
|
||||
static const int32_t halfModulus = gHyperframe/2;
|
||||
int32_t delta = v1-v2;
|
||||
if (delta>=halfModulus) delta -= gHyperframe;
|
||||
else if (delta<-halfModulus) delta += gHyperframe;
|
||||
return (int32_t) delta;
|
||||
}
|
||||
|
||||
int GSM::FNCompare(int32_t v1, int32_t v2)
|
||||
{
|
||||
int32_t delta = FNDelta(v1,v2);
|
||||
if (delta>0) return 1;
|
||||
if (delta<0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, const Time& t)
|
||||
{
|
||||
os << t.TN() << ":" << t.FN();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Clock::set(const Time& when)
|
||||
{
|
||||
mLock.lock();
|
||||
mBaseTime = Timeval(0);
|
||||
mBaseFN = when.FN();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
int32_t Clock::FN() const
|
||||
{
|
||||
mLock.lock();
|
||||
Timeval now;
|
||||
int32_t deltaSec = now.sec() - mBaseTime.sec();
|
||||
int32_t deltaUSec = now.usec() - mBaseTime.usec();
|
||||
int64_t elapsedUSec = 1000000LL*deltaSec + deltaUSec;
|
||||
int64_t elapsedFrames = elapsedUSec / gFrameMicroseconds;
|
||||
int32_t currentFN = (mBaseFN + elapsedFrames) % gHyperframe;
|
||||
mLock.unlock();
|
||||
return currentFN;
|
||||
}
|
||||
|
||||
|
||||
void Clock::wait(const Time& when) const
|
||||
{
|
||||
int32_t now = FN();
|
||||
int32_t target = when.FN();
|
||||
int32_t delta = FNDelta(target,now);
|
||||
if (delta<1) return;
|
||||
static const int32_t maxSleep = 51*26;
|
||||
if (delta>maxSleep) delta=maxSleep;
|
||||
sleepFrames(delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, TypeOfNumber type)
|
||||
{
|
||||
switch (type) {
|
||||
case UnknownTypeOfNumber: os << "unknown"; break;
|
||||
case InternationalNumber: os << "international"; break;
|
||||
case NationalNumber: os << "national"; break;
|
||||
case NetworkSpecificNumber: os << "network-specific"; break;
|
||||
case ShortCodeNumber: os << "short code"; break;
|
||||
default: os << "?" << (int)type << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, NumberingPlan plan)
|
||||
{
|
||||
switch (plan) {
|
||||
case UnknownPlan: os << "unknown"; break;
|
||||
case E164Plan: os << "E.164/ISDN"; break;
|
||||
case X121Plan: os << "X.121/data"; break;
|
||||
case F69Plan: os << "F.69/Telex"; break;
|
||||
case NationalPlan: os << "national"; break;
|
||||
case PrivatePlan: os << "private"; break;
|
||||
default: os << "?" << (int)plan << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, MobileIDType wID)
|
||||
{
|
||||
switch (wID) {
|
||||
case NoIDType: os << "None"; break;
|
||||
case IMSIType: os << "IMSI"; break;
|
||||
case IMEIType: os << "IMEI"; break;
|
||||
case TMSIType: os << "TMSI"; break;
|
||||
case IMEISVType: os << "IMEISV"; break;
|
||||
default: os << "?" << (int)wID << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, TypeAndOffset tao)
|
||||
{
|
||||
switch (tao) {
|
||||
case TDMA_MISC: os << "(misc)"; break;
|
||||
case TCHF_0: os << "TCH/F"; break;
|
||||
case TCHH_0: os << "TCH/H-0"; break;
|
||||
case TCHH_1: os << "TCH/H-1"; break;
|
||||
case SDCCH_4_0: os << "SDCCH/4-0"; break;
|
||||
case SDCCH_4_1: os << "SDCCH/4-1"; break;
|
||||
case SDCCH_4_2: os << "SDCCH/4-2"; break;
|
||||
case SDCCH_4_3: os << "SDCCH/4-3"; break;
|
||||
case SDCCH_8_0: os << "SDCCH/8-0"; break;
|
||||
case SDCCH_8_1: os << "SDCCH/8-1"; break;
|
||||
case SDCCH_8_2: os << "SDCCH/8-2"; break;
|
||||
case SDCCH_8_3: os << "SDCCH/8-3"; break;
|
||||
case SDCCH_8_4: os << "SDCCH/8-4"; break;
|
||||
case SDCCH_8_5: os << "SDCCH/8-5"; break;
|
||||
case SDCCH_8_6: os << "SDCCH/8-6"; break;
|
||||
case SDCCH_8_7: os << "SDCCH/8-7"; break;
|
||||
case TDMA_BEACON: os << "(beacon)"; break;
|
||||
default: os << "?" << (int)tao << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, ChannelType val)
|
||||
{
|
||||
switch (val) {
|
||||
case UndefinedCHType: os << "undefined"; return os;
|
||||
case SCHType: os << "SCH"; break;
|
||||
case FCCHType: os << "FCCH"; break;
|
||||
case BCCHType: os << "BCCH"; break;
|
||||
case RACHType: os << "RACH"; break;
|
||||
case SDCCHType: os << "SDCCH"; break;
|
||||
case FACCHType: os << "FACCH"; break;
|
||||
case CCCHType: os << "CCCH"; break;
|
||||
case SACCHType: os << "SACCH"; break;
|
||||
case TCHFType: os << "TCH/F"; break;
|
||||
case TCHHType: os << "TCH/H"; break;
|
||||
case AnyTCHType: os << "any TCH"; break;
|
||||
case LoopbackFullType: os << "Loopback Full"; break;
|
||||
case LoopbackHalfType: os << "Loopback Half"; break;
|
||||
case AnyDCCHType: os << "any DCCH"; break;
|
||||
default: os << "?" << (int)val << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Z100Timer::expired() const
|
||||
{
|
||||
assert(mLimitTime!=0);
|
||||
// A non-active timer does not expire.
|
||||
if (!mActive) return false;
|
||||
return mEndTime.passed();
|
||||
}
|
||||
|
||||
void Z100Timer::set()
|
||||
{
|
||||
assert(mLimitTime!=0);
|
||||
mEndTime = Timeval(mLimitTime);
|
||||
mActive=true;
|
||||
}
|
||||
|
||||
void Z100Timer::expire()
|
||||
{
|
||||
mEndTime = Timeval(0);
|
||||
mActive=true;
|
||||
}
|
||||
|
||||
|
||||
void Z100Timer::set(long wLimitTime)
|
||||
{
|
||||
mLimitTime = wLimitTime;
|
||||
set();
|
||||
}
|
||||
|
||||
|
||||
long Z100Timer::remaining() const
|
||||
{
|
||||
if (!mActive) return 0;
|
||||
long rem = mEndTime.remaining();
|
||||
if (rem<0) rem=0;
|
||||
return rem;
|
||||
}
|
||||
|
||||
void Z100Timer::wait() const
|
||||
{
|
||||
while (!expired()) msleep(remaining());
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
609
GSM/GSMCommon.h
Normal file
609
GSM/GSMCommon.h
Normal file
@@ -0,0 +1,609 @@
|
||||
/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSMCOMMON_H
|
||||
#define GSMCOMMON_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
#include <Threads.h>
|
||||
#include <Timeval.h>
|
||||
#include <BitVector.h>
|
||||
|
||||
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
/**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */
|
||||
|
||||
|
||||
/* forward references */
|
||||
class L1FEC;
|
||||
class L2LAPDm;
|
||||
class L3Processor;
|
||||
class LogicalChannel;
|
||||
class L2Header;
|
||||
|
||||
/** Call states based on GSM 04.08 5 and ITU-T Q.931 */
|
||||
enum CallState {
|
||||
NullState,
|
||||
Paging,
|
||||
AnsweredPaging,
|
||||
MOCInitiated,
|
||||
MOCProceeding,
|
||||
MTCConfirmed,
|
||||
CallReceived,
|
||||
CallPresent,
|
||||
ConnectIndication,
|
||||
Active,
|
||||
DisconnectIndication,
|
||||
ReleaseRequest,
|
||||
SMSDelivering,
|
||||
SMSSubmitting,
|
||||
};
|
||||
|
||||
|
||||
/** Return a human-readable string for a GSM::CallState. */
|
||||
const char* CallStateString(CallState state);
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, CallState state);
|
||||
|
||||
|
||||
/** A base class for GSM exceptions. */
|
||||
class GSMError {};
|
||||
|
||||
/** Duration ofa GSM frame, in microseconds. */
|
||||
const unsigned gFrameMicroseconds = 4615;
|
||||
|
||||
|
||||
/** Sleep for a given number of GSM frame periods. */
|
||||
inline void sleepFrames(unsigned frames)
|
||||
{ usleep(frames*gFrameMicroseconds); }
|
||||
|
||||
/** Sleep for 1 GSM frame period. */
|
||||
inline void sleepFrame()
|
||||
{ usleep(gFrameMicroseconds); }
|
||||
|
||||
|
||||
|
||||
/** GSM Training sequences from GSM 05.02 5.2.3. */
|
||||
extern const BitVector gTrainingSequence[];
|
||||
|
||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
||||
extern const BitVector gDummyBurst;
|
||||
|
||||
/** Random access burst synch. sequence */
|
||||
extern const BitVector gRACHSynchSequence;
|
||||
|
||||
enum GSMAlphabet {
|
||||
ALPHABET_7BIT,
|
||||
ALPHABET_8BIT,
|
||||
ALPHABET_UCS2
|
||||
};
|
||||
|
||||
/**@name Support for GSM 7-bit alphabet, GSM 03.38 6.2.1. */
|
||||
//@{
|
||||
/**
|
||||
Indexed by GSM 7-bit, returns ISO-8859-1.
|
||||
We do not support the extended table, so 0x1B is a space.
|
||||
FIXME -- ISO-8859-1 doesn't support Greek!
|
||||
*/
|
||||
static const unsigned char gGSMAlphabet[] = "@\243$\245\350\351\371\354\362\347\n\330\370\r\305\345D_FGLOPCSTZ \306\346\337\311 !\"#\244%&\'()*+,-./0123456789:;<=>?\241ABCDEFGHIJKLMNOPQRSTUVWXYZ\304\326\321\334\247\277abcdefghijklmnopqrstuvwxyz\344\366\361\374\341";
|
||||
unsigned char encodeGSMChar(unsigned char ascii);
|
||||
inline unsigned char decodeGSMChar(unsigned char sms) { return gGSMAlphabet[(unsigned)sms]; }
|
||||
//@}
|
||||
|
||||
|
||||
/**@name BCD-ASCII mapping, GMS 04.08 Table 10.5.118. */
|
||||
//@{
|
||||
/** Indexed by BCD, returns ASCII. */
|
||||
static const char gBCDAlphabet[] = "0123456789.#abc";
|
||||
char encodeBCDChar(char ascii);
|
||||
inline char decodeBCDChar(char bcd) { return gBCDAlphabet[(unsigned)bcd]; }
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Globally-fixed GSM timeout values (all in ms). */
|
||||
//@{
|
||||
/**@name GSM LAPDm timeouts, GSM 04.06 5.8, ITU-T Q.921 5.9 */
|
||||
//@{
|
||||
const unsigned T200ms = 900; ///< LAPDm ACK timeout, set for typical turnaround time
|
||||
//@}
|
||||
/**@name GSM timeouts for radio resource management, GSM 04.08 11.1. */
|
||||
//@{
|
||||
const unsigned T3101ms = 4000; ///< L1 timeout for SDCCH assignment
|
||||
const unsigned T3107ms = 3000; ///< L1 timeout for TCH/FACCH assignment
|
||||
const unsigned T3109ms = 30000; ///< L1 timeout for an existing channel
|
||||
const unsigned T3111ms = 2*T200ms; ///< L1 timeout for reassignment of a channel
|
||||
//@}
|
||||
/**@name GSM timeouts for mobility management, GSM 04.08 11.2. */
|
||||
//@{
|
||||
const unsigned T3260ms = 12000; ///< ID request timeout
|
||||
//@}
|
||||
/**@name GSM timeouts for SMS. GSM 04.11 */
|
||||
//@{
|
||||
const unsigned TR1Mms = 30000; ///< RP-ACK timeout
|
||||
//@}
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 Table 10.5.118 and GSM 03.40 9.1.2.5 */
|
||||
enum TypeOfNumber {
|
||||
UnknownTypeOfNumber = 0,
|
||||
InternationalNumber = 1,
|
||||
NationalNumber = 2,
|
||||
NetworkSpecificNumber = 3,
|
||||
ShortCodeNumber = 4,
|
||||
AlphanumericNumber = 5,
|
||||
AbbreviatedNumber = 6
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, TypeOfNumber);
|
||||
|
||||
|
||||
/** GSM 04.08 Table 10.5.118 and GSM 03.40 9.1.2.5 */
|
||||
enum NumberingPlan {
|
||||
UnknownPlan = 0,
|
||||
E164Plan = 1,
|
||||
X121Plan = 3,
|
||||
F69Plan = 4,
|
||||
NationalPlan = 8,
|
||||
PrivatePlan = 9,
|
||||
ERMESPlan = 10
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, NumberingPlan);
|
||||
|
||||
|
||||
|
||||
/** Codes for GSM band types, GSM 05.05 2. */
|
||||
enum GSMBand {
|
||||
GSM850=850, ///< US cellular
|
||||
EGSM900=900, ///< extended GSM
|
||||
DCS1800=1800, ///< worldwide DCS band
|
||||
PCS1900=1900 ///< US PCS band
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**@name Actual radio carrier frequencies, in kHz, GSM 05.05 2 */
|
||||
//@{
|
||||
unsigned uplinkFreqKHz(GSMBand wBand, unsigned wARFCN);
|
||||
unsigned uplinkOffsetKHz(GSMBand);
|
||||
unsigned downlinkFreqKHz(GSMBand wBand, unsigned wARFCN);
|
||||
//@}
|
||||
|
||||
|
||||
/**@name GSM Logical channel (LCH) types. */
|
||||
//@{
|
||||
/** Codes for logical channel types. */
|
||||
enum ChannelType {
|
||||
///@name Non-dedicated control channels.
|
||||
//@{
|
||||
SCHType, ///< sync
|
||||
FCCHType, ///< frequency correction
|
||||
BCCHType, ///< broadcast control
|
||||
CCCHType, ///< common control, a combination of several sub-types
|
||||
RACHType, ///< random access
|
||||
SACCHType, ///< slow associated control (acutally dedicated, but...)
|
||||
//@}
|
||||
///@name Dedicated control channels (DCCHs).
|
||||
//@{
|
||||
SDCCHType, ///< standalone dedicated control
|
||||
FACCHType, ///< fast associated control
|
||||
//@}
|
||||
///@name Traffic channels
|
||||
//@{
|
||||
TCHFType, ///< full-rate traffic
|
||||
TCHHType, ///< half-rate traffic
|
||||
AnyTCHType, ///< any TCH type
|
||||
//@}
|
||||
///@name Special internal channel types.
|
||||
//@{
|
||||
LoopbackFullType, ///< loopback testing
|
||||
LoopbackHalfType, ///< loopback testing
|
||||
AnyDCCHType, ///< any dedicated control channel
|
||||
UndefinedCHType, ///< undefined
|
||||
//@}
|
||||
};
|
||||
|
||||
|
||||
/** Print channel type name to a stream. */
|
||||
std::ostream& operator<<(std::ostream& os, ChannelType val);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/** Mobile identity types, GSM 04.08 10.5.1.4 */
|
||||
enum MobileIDType {
|
||||
NoIDType = 0,
|
||||
IMSIType = 1,
|
||||
IMEIType = 2,
|
||||
IMEISVType = 3,
|
||||
TMSIType = 4
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, MobileIDType);
|
||||
|
||||
|
||||
/** Type and TDMA offset of a logical channel, from GSM 04.08 10.5.2.5 */
|
||||
enum TypeAndOffset {
|
||||
TDMA_MISC=0,
|
||||
TCHF_0=1,
|
||||
TCHH_0=2, TCHH_1=3,
|
||||
SDCCH_4_0=4, SDCCH_4_1=5, SDCCH_4_2=6, SDCCH_4_3=7,
|
||||
SDCCH_8_0=8, SDCCH_8_1=9, SDCCH_8_2=10, SDCCH_8_3=11,
|
||||
SDCCH_8_4=12, SDCCH_8_5=13, SDCCH_8_6=14, SDCCH_8_7=15,
|
||||
/// An extra one for our internal use.
|
||||
TDMA_BEACON=255
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, TypeAndOffset);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
L3 Protocol Discriminator, GSM 04.08 10.2, GSM 04.07 11.2.3.1.1.
|
||||
*/
|
||||
enum L3PD {
|
||||
L3GroupCallControlPD=0x00,
|
||||
L3BroadcastCallControlPD=0x01,
|
||||
L3PDSS1PD=0x02,
|
||||
L3CallControlPD=0x03,
|
||||
L3PDSS2PD=0x04,
|
||||
L3MobilityManagementPD=0x05,
|
||||
L3RadioResourcePD=0x06,
|
||||
L3MobilityManagementGPRSPD=0x08,
|
||||
L3SMSPD=0x09,
|
||||
L3GPRSSessionManagementPD=0x0a,
|
||||
L3NonCallSSPD=0x0b,
|
||||
L3LocationPD=0x0c,
|
||||
L3ExtendedPD=0x0e,
|
||||
L3TestProcedurePD=0x0f,
|
||||
L3UndefinedPD=-1
|
||||
};
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, L3PD val);
|
||||
|
||||
|
||||
|
||||
|
||||
/**@name Tables related to Tx-integer; GSM 04.08 3.3.1.1.2 and 10.5.2.29. */
|
||||
//@{
|
||||
/** "T" parameter, from GSM 04.08 10.5.2.29. Index is TxInteger. */
|
||||
extern const unsigned RACHSpreadSlots[];
|
||||
/** "S" parameter, from GSM 04.08 3.3.1.1.2. Index is TxInteger. */
|
||||
extern const unsigned RACHWaitSParam[];
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
/**@name Modulus operations for frame numbers. */
|
||||
//@{
|
||||
/** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */
|
||||
const uint32_t gHyperframe = 2048UL * 26UL * 51UL;
|
||||
|
||||
/** Get a clock difference, within the modulus, v1-v2. */
|
||||
int32_t FNDelta(int32_t v1, int32_t v2);
|
||||
|
||||
/**
|
||||
Compare two frame clock values.
|
||||
@return 1 if v1>v2, -1 if v1<v2, 0 if v1==v2
|
||||
*/
|
||||
int FNCompare(int32_t v1, int32_t v2);
|
||||
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
GSM frame clock value. GSM 05.02 4.3
|
||||
No internal thread sync.
|
||||
*/
|
||||
class Time {
|
||||
|
||||
private:
|
||||
|
||||
int mFN; ///< frame number in the hyperframe
|
||||
int mTN; ///< timeslot number
|
||||
|
||||
public:
|
||||
|
||||
Time(int wFN=0, int wTN=0)
|
||||
:mFN(wFN),mTN(wTN)
|
||||
{ }
|
||||
|
||||
|
||||
/** Move the time forward to a given position in a given modulus. */
|
||||
void rollForward(unsigned wFN, unsigned modulus)
|
||||
{
|
||||
assert(modulus<gHyperframe);
|
||||
while ((mFN % modulus) != wFN) mFN=(mFN+1)%gHyperframe;
|
||||
}
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
int FN() const { return mFN; }
|
||||
void FN(unsigned wFN) { mFN = wFN; }
|
||||
unsigned TN() const { return mTN; }
|
||||
void TN(unsigned wTN) { mTN=wTN; }
|
||||
//@}
|
||||
|
||||
/**@name Arithmetic. */
|
||||
//@{
|
||||
|
||||
Time& operator++()
|
||||
{
|
||||
mFN = (mFN+1) % gHyperframe;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Time& decTN(unsigned step=1)
|
||||
{
|
||||
assert(step<=8);
|
||||
mTN -= step;
|
||||
if (mTN<0) {
|
||||
mTN+=8;
|
||||
mFN-=1;
|
||||
if (mFN<0) mFN+=gHyperframe;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Time& incTN(unsigned step=1)
|
||||
{
|
||||
assert(step<=8);
|
||||
mTN += step;
|
||||
if (mTN>7) {
|
||||
mTN-=8;
|
||||
mFN = (mFN+1) % gHyperframe;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Time& operator+=(int step)
|
||||
{
|
||||
// Remember the step might be negative.
|
||||
mFN += step;
|
||||
if (mFN<0) mFN+=gHyperframe;
|
||||
mFN = mFN % gHyperframe;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Time operator-(int step) const
|
||||
{ return operator+(-step); }
|
||||
|
||||
Time operator+(int step) const
|
||||
{
|
||||
Time newVal = *this;
|
||||
newVal += step;
|
||||
return newVal;
|
||||
}
|
||||
|
||||
Time operator+(const Time& other) const
|
||||
{
|
||||
unsigned newTN = (mTN + other.mTN) % 8;
|
||||
uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
|
||||
return Time(newFN,newTN);
|
||||
}
|
||||
|
||||
int operator-(const Time& other) const
|
||||
{
|
||||
return FNDelta(mFN,other.mFN);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Comparisons. */
|
||||
//@{
|
||||
|
||||
bool operator<(const Time& other) const
|
||||
{
|
||||
if (mFN==other.mFN) return (mTN<other.mTN);
|
||||
return FNCompare(mFN,other.mFN)<0;
|
||||
}
|
||||
|
||||
bool operator>(const Time& other) const
|
||||
{
|
||||
if (mFN==other.mFN) return (mTN>other.mTN);
|
||||
return FNCompare(mFN,other.mFN)>0;
|
||||
}
|
||||
|
||||
bool operator<=(const Time& other) const
|
||||
{
|
||||
if (mFN==other.mFN) return (mTN<=other.mTN);
|
||||
return FNCompare(mFN,other.mFN)<=0;
|
||||
}
|
||||
|
||||
bool operator>=(const Time& other) const
|
||||
{
|
||||
if (mFN==other.mFN) return (mTN>=other.mTN);
|
||||
return FNCompare(mFN,other.mFN)>=0;
|
||||
}
|
||||
|
||||
bool operator==(const Time& other) const
|
||||
{
|
||||
return (mFN == other.mFN) && (mTN==other.mTN);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/**@name Standard derivations. */
|
||||
//@{
|
||||
|
||||
/** GSM 05.02 3.3.2.2.1 */
|
||||
unsigned SFN() const { return mFN / (26*51); }
|
||||
|
||||
/** GSM 05.02 3.3.2.2.1 */
|
||||
unsigned T1() const { return SFN() % 2048; }
|
||||
|
||||
/** GSM 05.02 3.3.2.2.1 */
|
||||
unsigned T2() const { return mFN % 26; }
|
||||
|
||||
/** GSM 05.02 3.3.2.2.1 */
|
||||
unsigned T3() const { return mFN % 51; }
|
||||
|
||||
/** GSM 05.02 3.3.2.2.1. */
|
||||
unsigned T3p() const { return (T3()-1)/10; }
|
||||
|
||||
/** GSM 05.02 6.3.1.3. */
|
||||
unsigned TC() const { return (FN()/51) % 8; }
|
||||
|
||||
/** GSM 04.08 10.5.2.30. */
|
||||
unsigned T1p() const { return SFN() % 32; }
|
||||
|
||||
/** GSM 05.02 6.2.3 */
|
||||
unsigned T1R() const { return T1() % 64; }
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Time& ts);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A class for calculating the current GSM frame number.
|
||||
Has built-in concurrency protections.
|
||||
*/
|
||||
class Clock {
|
||||
|
||||
private:
|
||||
|
||||
mutable Mutex mLock;
|
||||
int32_t mBaseFN;
|
||||
Timeval mBaseTime;
|
||||
|
||||
public:
|
||||
|
||||
Clock(const Time& when = Time(0))
|
||||
:mBaseFN(when.FN())
|
||||
{}
|
||||
|
||||
/** Set the clock to a value. */
|
||||
void set(const Time&);
|
||||
|
||||
/** Read the clock. */
|
||||
int32_t FN() const;
|
||||
|
||||
/** Read the clock. */
|
||||
Time get() const { return Time(FN()); }
|
||||
|
||||
/** Block until the clock passes a given time. */
|
||||
void wait(const Time&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
CCITT Z.100 activity timer, as described in GSM 04.06 5.1.
|
||||
All times are in milliseconds.
|
||||
*/
|
||||
class Z100Timer {
|
||||
|
||||
private:
|
||||
|
||||
Timeval mEndTime; ///< the time at which this timer will expire
|
||||
long mLimitTime; ///< timeout in milliseconds
|
||||
bool mActive; ///< true if timer is active
|
||||
|
||||
public:
|
||||
|
||||
/** Create a timer with a given timeout in milliseconds. */
|
||||
Z100Timer(long wLimitTime)
|
||||
:mLimitTime(wLimitTime),
|
||||
mActive(false)
|
||||
{}
|
||||
|
||||
/** Blank constructor; if you use this object, it will assert. */
|
||||
Z100Timer():mLimitTime(0),mActive(false) {}
|
||||
|
||||
/** True if the timer is active and expired. */
|
||||
bool expired() const;
|
||||
|
||||
/** Force the timer into an expired state. */
|
||||
void expire();
|
||||
|
||||
/** Start or restart the timer. */
|
||||
void set();
|
||||
|
||||
/** Start or restart the timer, possibly specifying a new limit. */
|
||||
void set(long wLimitTime);
|
||||
|
||||
/** Stop the timer. */
|
||||
void reset() { assert(mLimitTime!=0); mActive = false; }
|
||||
|
||||
/** Returns true if the timer is active. */
|
||||
bool active() const { return mActive; }
|
||||
|
||||
/**
|
||||
Remaining time until expiration, in milliseconds.
|
||||
Returns zero if the timer has expired.
|
||||
*/
|
||||
long remaining() const;
|
||||
|
||||
/**
|
||||
Block until the timer expires.
|
||||
Returns immediately if the timer is not running.
|
||||
*/
|
||||
void wait() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}; // namespace GSM
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
337
GSM/GSMConfig.cpp
Normal file
337
GSM/GSMConfig.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "GSMConfig.h"
|
||||
#include "GSMTransfer.h"
|
||||
#include "GSMLogicalChannel.h"
|
||||
#include <ControlCommon.h>
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
GSMConfig::GSMConfig()
|
||||
:mBand((GSMBand)gConfig.getNum("GSM.Radio.Band")),
|
||||
mSI5Frame(UNIT_DATA),mSI6Frame(UNIT_DATA),
|
||||
mT3122(gConfig.getNum("GSM.Timer.T3122Min")),
|
||||
mStartTime(::time(NULL))
|
||||
{
|
||||
regenerateBeacon();
|
||||
}
|
||||
|
||||
void GSMConfig::start()
|
||||
{
|
||||
mPowerManager.start();
|
||||
// Do not call this until the paging channels are installed.
|
||||
mPager.start();
|
||||
// Do not call this until AGCHs are installed.
|
||||
mAccessGrantThread.start(Control::AccessGrantServiceLoop,NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void GSMConfig::regenerateBeacon()
|
||||
{
|
||||
// Update everything from the configuration.
|
||||
LOG(NOTICE) << "regenerating system information messages";
|
||||
|
||||
// BSIC components
|
||||
mNCC = gConfig.getNum("GSM.Identity.BSIC.NCC");
|
||||
LOG_ASSERT(mNCC<8);
|
||||
mBCC = gConfig.getNum("GSM.Identity.BSIC.BCC");
|
||||
LOG_ASSERT(mBCC<8);
|
||||
|
||||
// MCC/MNC/LAC
|
||||
mLAI = L3LocationAreaIdentity();
|
||||
|
||||
// Now regenerate all of the system information messages.
|
||||
|
||||
// SI1
|
||||
L3SystemInformationType1 SI1;
|
||||
LOG(INFO) << SI1;
|
||||
L3Frame SI1L3(UNIT_DATA);
|
||||
SI1.write(SI1L3);
|
||||
L2Header SI1Header(L2Length(SI1L3.L2Length()));
|
||||
mSI1Frame = L2Frame(SI1Header,SI1L3);
|
||||
LOG(DEBUG) << "mSI1Frame " << mSI1Frame;
|
||||
|
||||
// SI2
|
||||
L3SystemInformationType2 SI2;
|
||||
LOG(INFO) << SI2;
|
||||
L3Frame SI2L3(UNIT_DATA);
|
||||
SI2.write(SI2L3);
|
||||
L2Header SI2Header(L2Length(SI2L3.L2Length()));
|
||||
mSI2Frame = L2Frame(SI2Header,SI2L3);
|
||||
LOG(DEBUG) << "mSI2Frame " << mSI2Frame;
|
||||
|
||||
// SI3
|
||||
L3SystemInformationType3 SI3;
|
||||
LOG(INFO) << SI3;
|
||||
L3Frame SI3L3(UNIT_DATA);
|
||||
SI3.write(SI3L3);
|
||||
L2Header SI3Header(L2Length(SI3L3.L2Length()));
|
||||
mSI3Frame = L2Frame(SI3Header,SI3L3);
|
||||
LOG(DEBUG) << "mSI3Frame " << mSI3Frame;
|
||||
|
||||
// SI4
|
||||
L3SystemInformationType4 SI4;
|
||||
LOG(INFO) << SI4;
|
||||
L3Frame SI4L3(UNIT_DATA);
|
||||
SI4.write(SI4L3);
|
||||
L2Header SI4Header(L2Length(SI4L3.L2Length()));
|
||||
mSI4Frame = L2Frame(SI4Header,SI4L3);
|
||||
LOG(DEBUG) << "mSI4Frame " << mSI4Frame;
|
||||
|
||||
// SI5
|
||||
L3SystemInformationType5 SI5;
|
||||
LOG(INFO) << SI5;
|
||||
SI5.write(mSI5Frame);
|
||||
LOG(DEBUG) << "mSI5Frame " << mSI5Frame;
|
||||
|
||||
// SI6
|
||||
L3SystemInformationType6 SI6;
|
||||
LOG(INFO) << SI6;
|
||||
SI6.write(mSI6Frame);
|
||||
LOG(DEBUG) "mSI6Frame " << mSI6Frame;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CCCHLogicalChannel* GSMConfig::minimumLoad(CCCHList &chanList)
|
||||
{
|
||||
if (chanList.size()==0) return NULL;
|
||||
CCCHList::iterator chan = chanList.begin();
|
||||
CCCHLogicalChannel *retVal = *chan;
|
||||
unsigned minLoad = (*chan)->load();
|
||||
++chan;
|
||||
while (chan!=chanList.end()) {
|
||||
unsigned thisLoad = (*chan)->load();
|
||||
if (thisLoad<minLoad) {
|
||||
minLoad = thisLoad;
|
||||
retVal = *chan;
|
||||
}
|
||||
++chan;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <class ChanType> ChanType* getChan(vector<ChanType*>& chanList)
|
||||
{
|
||||
const unsigned sz = chanList.size();
|
||||
if (sz==0) return NULL;
|
||||
// Start the search from a random point in the list.
|
||||
//unsigned pos = random() % sz;
|
||||
// HACK -- Try in-order allocation for debugging.
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
ChanType *chan = chanList[i];
|
||||
//ChanType *chan = chanList[pos];
|
||||
if (chan->recyclable()) return chan;
|
||||
//pos = (pos+1) % sz;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SDCCHLogicalChannel *GSMConfig::getSDCCH()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
SDCCHLogicalChannel *chan = getChan<SDCCHLogicalChannel>(mSDCCHPool);
|
||||
if (chan) chan->open();
|
||||
return chan;
|
||||
}
|
||||
|
||||
|
||||
TCHFACCHLogicalChannel *GSMConfig::getTCH()
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
TCHFACCHLogicalChannel *chan = getChan<TCHFACCHLogicalChannel>(mTCHPool);
|
||||
if (chan) chan->open();
|
||||
return chan;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class ChanType> size_t chanAvailable(const vector<ChanType*>& chanList)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (unsigned i=0; i<chanList.size(); i++) {
|
||||
if (chanList[i]->recyclable()) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t GSMConfig::SDCCHAvailable() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return chanAvailable<SDCCHLogicalChannel>(mSDCCHPool);
|
||||
}
|
||||
|
||||
size_t GSMConfig::TCHAvailable() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return chanAvailable<TCHFACCHLogicalChannel>(mTCHPool);
|
||||
}
|
||||
|
||||
|
||||
size_t GSMConfig::totalLoad(const CCCHList& chanList) const
|
||||
{
|
||||
size_t total = 0;
|
||||
for (int i=0; i<chanList.size(); i++) {
|
||||
total += chanList[i]->load();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class ChanType> unsigned countActive(const vector<ChanType*>& chanList)
|
||||
{
|
||||
unsigned active = 0;
|
||||
const unsigned sz = chanList.size();
|
||||
// Start the search from a random point in the list.
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
if (!chanList[i]->recyclable()) active++;
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
|
||||
unsigned GSMConfig::SDCCHActive() const
|
||||
{
|
||||
return countActive(mSDCCHPool);
|
||||
}
|
||||
|
||||
unsigned GSMConfig::TCHActive() const
|
||||
{
|
||||
return countActive(mTCHPool);
|
||||
}
|
||||
|
||||
|
||||
unsigned GSMConfig::T3122() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mT3122;
|
||||
}
|
||||
|
||||
unsigned GSMConfig::growT3122()
|
||||
{
|
||||
unsigned max = gConfig.getNum("GSM.Timer.T3122Max");
|
||||
ScopedLock lock(mLock);
|
||||
unsigned retVal = mT3122;
|
||||
mT3122 += (random() % mT3122) / 2;
|
||||
if (mT3122>max) mT3122=max;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
unsigned GSMConfig::shrinkT3122()
|
||||
{
|
||||
unsigned min = gConfig.getNum("GSM.Timer.T3122Min");
|
||||
ScopedLock lock(mLock);
|
||||
unsigned retVal = mT3122;
|
||||
mT3122 -= (random() % mT3122) / 2;
|
||||
if (mT3122<min) mT3122=min;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GSMConfig::createCombination0(TransceiverManager& TRX, unsigned TN)
|
||||
{
|
||||
// This channel is a dummy burst generator.
|
||||
// This should not be applied to C0T0.
|
||||
LOG_ASSERT(TN!=0);
|
||||
LOG(NOTICE) << "Configuring dummy filling on C0T " << TN;
|
||||
ARFCNManager *radio = TRX.ARFCN();
|
||||
radio->setSlot(TN,0);
|
||||
}
|
||||
|
||||
|
||||
void GSMConfig::createCombinationI(TransceiverManager& TRX, unsigned TN)
|
||||
{
|
||||
LOG_ASSERT(TN!=0);
|
||||
LOG(NOTICE) << "Configuring combination I on C0T" << TN;
|
||||
ARFCNManager *radio = TRX.ARFCN();
|
||||
radio->setSlot(TN,1);
|
||||
TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(TN,gTCHF_T[TN]);
|
||||
chan->downstream(radio);
|
||||
Thread* thread = new Thread;
|
||||
thread->start((void*(*)(void*))Control::DCCHDispatcher,chan);
|
||||
chan->open();
|
||||
gBTS.addTCH(chan);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void GSMConfig::createCombinationVII(TransceiverManager& TRX, unsigned TN)
|
||||
{
|
||||
LOG_ASSERT(TN!=0);
|
||||
LOG(NOTICE) << "Configuring combination VII on C0T" << TN;
|
||||
ARFCNManager *radio = TRX.ARFCN();
|
||||
radio->setSlot(TN,7);
|
||||
for (int i=0; i<8; i++) {
|
||||
SDCCHLogicalChannel* chan = new SDCCHLogicalChannel(TN,gSDCCH8[i]);
|
||||
chan->downstream(radio);
|
||||
Thread* thread = new Thread;
|
||||
thread->start((void*(*)(void*))Control::DCCHDispatcher,chan);
|
||||
chan->open();
|
||||
gBTS.addSDCCH(chan);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GSMConfig::hold(bool val)
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
mHold = val;
|
||||
}
|
||||
|
||||
bool GSMConfig::hold() const
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
return mHold;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
289
GSM/GSMConfig.h
Normal file
289
GSM/GSMConfig.h
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2008-2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSMCONFIG_H
|
||||
#define GSMCONFIG_H
|
||||
|
||||
#include <vector>
|
||||
#include <Interthread.h>
|
||||
|
||||
//#include <ControlCommon.h>
|
||||
#include <RadioResource.h>
|
||||
#include <PowerManager.h>
|
||||
|
||||
#include "GSML3RRElements.h"
|
||||
#include "GSML3CommonElements.h"
|
||||
#include "GSML3RRMessages.h"
|
||||
|
||||
#include "TRXManager.h"
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
class CCCHLogicalChannel;
|
||||
class SDCCHLogicalChannel;
|
||||
class TCHFACCHLogicalChannel;
|
||||
|
||||
class CCCHList : public std::vector<CCCHLogicalChannel*> {};
|
||||
class SDCCHList : public std::vector<SDCCHLogicalChannel*> {};
|
||||
class TCHList : public std::vector<TCHFACCHLogicalChannel*> {};
|
||||
|
||||
/**
|
||||
This object carries the top-level GSM air interface configuration.
|
||||
It serves as a central clearinghouse to get access to everything else in the GSM code.
|
||||
*/
|
||||
class GSMConfig {
|
||||
|
||||
private:
|
||||
|
||||
/** The paging mechanism is built-in. */
|
||||
Control::Pager mPager;
|
||||
|
||||
PowerManager mPowerManager;
|
||||
|
||||
mutable Mutex mLock; ///< multithread access control
|
||||
|
||||
/**@name Groups of CCCH subchannels -- may intersect. */
|
||||
//@{
|
||||
CCCHList mAGCHPool; ///< access grant CCCH subchannels
|
||||
CCCHList mPCHPool; ///< paging CCCH subchannels
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Allocatable channel pools. */
|
||||
//@{
|
||||
SDCCHList mSDCCHPool;
|
||||
TCHList mTCHPool;
|
||||
//@}
|
||||
|
||||
/**@name BSIC. */
|
||||
//@{
|
||||
unsigned mNCC; ///< network color code
|
||||
unsigned mBCC; ///< basestation color code
|
||||
//@}
|
||||
|
||||
GSMBand mBand; ///< BTS operating band
|
||||
|
||||
Clock mClock; ///< local copy of BTS master clock
|
||||
|
||||
/**@name Encoded L2 frames to be sent on the BCCH. */
|
||||
//@{
|
||||
L2Frame mSI1Frame;
|
||||
L2Frame mSI2Frame;
|
||||
L2Frame mSI3Frame;
|
||||
L2Frame mSI4Frame;
|
||||
//@}
|
||||
|
||||
/**@name Encoded L3 frames to be sent on the SACCH. */
|
||||
//@{
|
||||
L3Frame mSI5Frame;
|
||||
L3Frame mSI6Frame;
|
||||
//@}
|
||||
|
||||
int mT3122;
|
||||
|
||||
time_t mStartTime;
|
||||
|
||||
L3LocationAreaIdentity mLAI;
|
||||
|
||||
bool mHold; ///< If true, do not respond to RACH bursts.
|
||||
|
||||
InterthreadQueue<Control::ChannelRequestRecord> mChannelRequestQueue;
|
||||
Thread mAccessGrantThread;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
/** All parameters come from gConfig. */
|
||||
GSMConfig();
|
||||
|
||||
/** Start the internal control loops. */
|
||||
void start();
|
||||
|
||||
/**@name Get references to L2 frames for BCCH SI messages. */
|
||||
//@{
|
||||
const L2Frame& SI1Frame() const { return mSI1Frame; }
|
||||
const L2Frame& SI2Frame() const { return mSI2Frame; }
|
||||
const L2Frame& SI3Frame() const { return mSI3Frame; }
|
||||
const L2Frame& SI4Frame() const { return mSI4Frame; }
|
||||
//@}
|
||||
/**@name Get references to L3 frames for SACCH SI messages. */
|
||||
//@{
|
||||
const L3Frame& SI5Frame() const { return mSI5Frame; }
|
||||
const L3Frame& SI6Frame() const { return mSI6Frame; }
|
||||
//@}
|
||||
|
||||
/** Get the current master clock value. */
|
||||
Time time() const { return mClock.get(); }
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
Control::Pager& pager() { return mPager; }
|
||||
GSMBand band() const { return mBand; }
|
||||
unsigned BCC() const { return mBCC; }
|
||||
unsigned NCC() const { return mNCC; }
|
||||
GSM::Clock& clock() { return mClock; }
|
||||
const L3LocationAreaIdentity& LAI() const { return mLAI; }
|
||||
//@}
|
||||
|
||||
/** Return the BSIC, NCC:BCC. */
|
||||
unsigned BSIC() const { return (mNCC<<3) | mBCC; }
|
||||
|
||||
/**
|
||||
Re-encode the L2Frames for system information messages.
|
||||
Called whenever a beacon parameter is changed.
|
||||
*/
|
||||
void regenerateBeacon();
|
||||
|
||||
/**
|
||||
Hold off on channel allocations; don't answer RACH.
|
||||
@param val true to hold, false to clear hold
|
||||
*/
|
||||
void hold(bool val);
|
||||
|
||||
/**
|
||||
Return true if we are holding off channel allocation.
|
||||
*/
|
||||
bool hold() const;
|
||||
|
||||
protected:
|
||||
|
||||
/** Find a minimum-load CCCH from a list. */
|
||||
CCCHLogicalChannel* minimumLoad(CCCHList &chanList);
|
||||
|
||||
/** Return the total load of a CCCH list. */
|
||||
size_t totalLoad(const CCCHList &chanList) const;
|
||||
|
||||
public:
|
||||
|
||||
size_t AGCHLoad() { return totalLoad(mAGCHPool); }
|
||||
size_t PCHLoad() { return totalLoad(mPCHPool); }
|
||||
|
||||
/**@name Manage CCCH subchannels. */
|
||||
//@{
|
||||
|
||||
/** The add method is not mutex protected and should only be used during initialization. */
|
||||
void addAGCH(CCCHLogicalChannel* wCCCH) { mAGCHPool.push_back(wCCCH); }
|
||||
|
||||
/** The add method is not mutex protected and should only be used during initialization. */
|
||||
void addPCH(CCCHLogicalChannel* wCCCH) { mPCHPool.push_back(wCCCH); }
|
||||
|
||||
/** Return a minimum-load AGCH. */
|
||||
CCCHLogicalChannel* getAGCH() { return minimumLoad(mAGCHPool); }
|
||||
|
||||
/** Return a minimum-load PCH. */
|
||||
CCCHLogicalChannel* getPCH() { return minimumLoad(mPCHPool); }
|
||||
|
||||
/** Return a specific PCH. */
|
||||
CCCHLogicalChannel* getPCH(size_t index)
|
||||
{
|
||||
assert(index<mPCHPool.size());
|
||||
return mPCHPool[index];
|
||||
}
|
||||
|
||||
/** Return the number of configured AGCHs */
|
||||
unsigned numAGCHs() const { return mAGCHPool.size(); }
|
||||
|
||||
/** Enqueue a RACH channel request; to be deleted when dequeued later. */
|
||||
void channelRequest(Control::ChannelRequestRecord *req)
|
||||
{ mChannelRequestQueue.write(req); }
|
||||
|
||||
Control::ChannelRequestRecord* nextChannelRequest()
|
||||
{ return mChannelRequestQueue.read(); }
|
||||
|
||||
void flushChannelRequests()
|
||||
{ mChannelRequestQueue.clear(); }
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Manage SDCCH Pool. */
|
||||
//@{
|
||||
/** The add method is not mutex protected and should only be used during initialization. */
|
||||
void addSDCCH(SDCCHLogicalChannel *wSDCCH) { mSDCCHPool.push_back(wSDCCH); }
|
||||
/** Return a pointer to a usable channel. */
|
||||
SDCCHLogicalChannel *getSDCCH();
|
||||
/** Return true if an SDCCH is available, but do not allocate it. */
|
||||
size_t SDCCHAvailable() const;
|
||||
/** Return number of total SDCCH. */
|
||||
unsigned SDCCHTotal() const { return mSDCCHPool.size(); }
|
||||
/** Return number of active SDCCH. */
|
||||
unsigned SDCCHActive() const;
|
||||
/** Just a reference to the SDCCH pool. */
|
||||
const SDCCHList& SDCCHPool() const { return mSDCCHPool; }
|
||||
//@}
|
||||
|
||||
/**@name Manage TCH pool. */
|
||||
//@{
|
||||
/** The add method is not mutex protected and should only be used during initialization. */
|
||||
void addTCH(TCHFACCHLogicalChannel *wTCH) { mTCHPool.push_back(wTCH); }
|
||||
/** Return a pointer to a usable channel. */
|
||||
TCHFACCHLogicalChannel *getTCH();
|
||||
/** Return true if an TCH is available, but do not allocate it. */
|
||||
size_t TCHAvailable() const;
|
||||
/** Return number of total TCH. */
|
||||
unsigned TCHTotal() const { return mTCHPool.size(); }
|
||||
/** Return number of active TCH. */
|
||||
unsigned TCHActive() const;
|
||||
/** Just a reference to the TCH pool. */
|
||||
const TCHList& TCHPool() const { return mTCHPool; }
|
||||
//@}
|
||||
|
||||
/**@name T3122 management */
|
||||
//@{
|
||||
unsigned T3122() const;
|
||||
unsigned growT3122();
|
||||
unsigned shrinkT3122();
|
||||
//@}
|
||||
|
||||
/**@name Methods to create channel combinations. */
|
||||
//@{
|
||||
/** Combination 0 is a idle slot, as opposed to a non-transmitting one. */
|
||||
void createCombination0(TransceiverManager &TRX, unsigned TN);
|
||||
/** Combination I is full rate traffic. */
|
||||
void createCombinationI(TransceiverManager &TRX, unsigned TN);
|
||||
/** Combination VII is 8 SDCCHs. */
|
||||
void createCombinationVII(TransceiverManager &TRX, unsigned TN);
|
||||
//@}
|
||||
|
||||
/** Return number of seconds since starting. */
|
||||
time_t uptime() const { return ::time(NULL)-mStartTime; }
|
||||
|
||||
/** Get a handle to the power manager. */
|
||||
PowerManager& powerManager() { return mPowerManager; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
}; // GSM
|
||||
|
||||
|
||||
/**@addtogroup Globals */
|
||||
//@{
|
||||
/** A single global GSMConfig object in the global namespace. */
|
||||
extern GSM::GSMConfig gBTS;
|
||||
//@}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
1589
GSM/GSML1FEC.cpp
Normal file
1589
GSM/GSML1FEC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1230
GSM/GSML1FEC.h
Normal file
1230
GSM/GSML1FEC.h
Normal file
File diff suppressed because it is too large
Load Diff
997
GSM/GSML2LAPDm.cpp
Normal file
997
GSM/GSML2LAPDm.cpp
Normal file
@@ -0,0 +1,997 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Many elements follow Daniele Orlandi's <daniele@orlandi.com> vISDN/Q.921
|
||||
implementation, although no code is copied directly.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
As a general rule, the caller need not hold
|
||||
mLock when sending U frames but should hold the
|
||||
lock when transmitting S or I frames.
|
||||
|
||||
This implementation is for use on the BTS side,
|
||||
however, some MS functions are included for unit-
|
||||
testing. For actual use in a BTS, these will need
|
||||
to be compelted.
|
||||
*/
|
||||
|
||||
#include "GSML2LAPDm.h"
|
||||
#include "GSMSAPMux.h"
|
||||
#include <Logger.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
//#define NDEBUG
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L2LAPDm::LAPDState state)
|
||||
{
|
||||
switch (state) {
|
||||
case L2LAPDm::LinkReleased: os << "LinkReleased"; break;
|
||||
case L2LAPDm::AwaitingEstablish: os << "AwaitingEstablish"; break;
|
||||
case L2LAPDm::AwaitingRelease: os << "AwaitingRelease"; break;
|
||||
case L2LAPDm::LinkEstablished: os << "LinkEstablished"; break;
|
||||
case L2LAPDm::ContentionResolution: os << "ContentionResolution"; break;
|
||||
default: os << "?" << (int)state << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void CCCHL2::writeHighSide(const GSM::L3Frame& l3)
|
||||
{
|
||||
OBJLOG(DEBUG) <<"CCCHL2::writeHighSide " << l3;
|
||||
assert(mDownstream);
|
||||
assert(l3.primitive()==UNIT_DATA);
|
||||
L2Header header(L2Length(l3.L2Length()));
|
||||
mDownstream->writeHighSide(L2Frame(header,l3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
L2LAPDm::L2LAPDm(unsigned wC, unsigned wSAPI)
|
||||
:mRunning(false),
|
||||
mC(wC),mR(1-wC),mSAPI(wSAPI),
|
||||
mMaster(NULL),
|
||||
mT200(T200ms),
|
||||
mIdleFrame(DATA)
|
||||
{
|
||||
// sanity checks
|
||||
assert(mC<2);
|
||||
assert(mSAPI<4);
|
||||
|
||||
clearState();
|
||||
|
||||
// Set the idle frame as per GSM 04.06 5.4.2.3.
|
||||
mIdleFrame.fillField(8*0,(mC<<1)|1,8); // address
|
||||
mIdleFrame.fillField(8*1,3,8); // control
|
||||
mIdleFrame.fillField(8*2,1,8); // length
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::writeL1(const L2Frame& frame)
|
||||
{
|
||||
OBJLOG(DEBUG) <<"L2LAPDm::writeL1 " << frame;
|
||||
//assert(mDownstream);
|
||||
if (!mDownstream) return;
|
||||
ScopedLock lock(mLock);
|
||||
mDownstream->writeHighSide(frame);
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::writeL1NoAck(const L2Frame& frame)
|
||||
{
|
||||
// Caller need not hold mLock.
|
||||
OBJLOG(DEBUG) <<"L2LAPDm::writeL1NoAck " << frame;
|
||||
writeL1(frame);
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::writeL1Ack(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// GSM 04.06 5.4.4.2
|
||||
OBJLOG(DEBUG) <<"L2LAPDm::writeL1Ack " << frame;
|
||||
frame.copyTo(mSentFrame);
|
||||
mSentFrame.primitive(frame.primitive());
|
||||
writeL1(frame);
|
||||
mT200.set(T200());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::waitForAck()
|
||||
{
|
||||
// Block until any pending ack is received.
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(DEBUG) <<"L2LAPDm::waitForAck state=" << mState << " VS=" << mVS << " VA=" << mVA;
|
||||
while (true) {
|
||||
if (mState==LinkReleased) break;
|
||||
if ((mState==ContentionResolution) && (mVS==mVA)) break;
|
||||
if ((mState==LinkEstablished) && (mVS==mVA)) break;
|
||||
// HACK -- We should not need a timeout here.
|
||||
mAckSignal.wait(mLock,N200()*T200ms);
|
||||
OBJLOG(DEBUG) <<"L2LAPDm::waitForAck state=" << mState << " VS=" << mVS << " VA=" << mVA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::releaseLink(Primitive releaseType)
|
||||
{
|
||||
OBJLOG(DEBUG) << "mState=" << mState;
|
||||
// Caller should hold mLock.
|
||||
mState = LinkReleased;
|
||||
mEstablishmentInProgress = false;
|
||||
mAckSignal.signal();
|
||||
if (mSAPI==0) writeL1(releaseType);
|
||||
mL3Out.write(new L3Frame(releaseType));
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::clearCounters()
|
||||
{
|
||||
OBJLOG(DEBUG) << "mState=" << mState;
|
||||
// Caller should hold mLock.
|
||||
// This is called upon establishment or re-establihment of ABM.
|
||||
mT200.reset();
|
||||
mVS = 0;
|
||||
mVA = 0;
|
||||
mVR = 0;
|
||||
mRC = 0;
|
||||
mIdleCount=0;
|
||||
mRecvBuffer.clear();
|
||||
discardIQueue();
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::clearState(Primitive releaseType)
|
||||
{
|
||||
OBJLOG(DEBUG) << "mState=" << mState;
|
||||
// Caller should hold mLock.
|
||||
// Reset the state machine.
|
||||
clearCounters();
|
||||
releaseLink(releaseType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::processAck(unsigned NR)
|
||||
{
|
||||
// GSM 04.06 5.5.3, 5.7.2.
|
||||
// Q.921 5.6.3.2, 5.8.2.
|
||||
// Equivalent to vISDN datalink.c:lapd_ack_frames,
|
||||
// but much simpler for LAPDm.
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(DEBUG) << "NR=" << NR << " VA=" << mVA << " VS=" << mVS;
|
||||
mVA=NR;
|
||||
if (mVA==mVS) {
|
||||
mRC=0;
|
||||
mT200.reset();
|
||||
}
|
||||
mAckSignal.signal();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::bufferIFrameData(const L2Frame& frame)
|
||||
{
|
||||
// Concatenate I-frames to form the L3 frame.
|
||||
/*
|
||||
GSM 04.06 5.5.2 states:
|
||||
When a data link layer entity is not in an own receiver busy condition
|
||||
and receives a valid I frame whose send sequence number is equal to the
|
||||
current receive state variable V(R), the data link layer entity shall:
|
||||
- if the M bit is set to "0", concatenate it with previously
|
||||
received frames with the M bit set to "1", if any, and pass the complete
|
||||
layer 3 message unit to the layer 3 entity using the primitive
|
||||
DL-DATA-INDICATION;
|
||||
- if the M bit is set to "1", store the information field of the frame and
|
||||
concatenate it with previously received frames with the M bit set to "1",
|
||||
if any (Note: no information is passed to the layer 3 entity);
|
||||
*/
|
||||
|
||||
OBJLOG(DEBUG) << frame;
|
||||
if (!frame.M()) {
|
||||
// The last or only frame.
|
||||
if (mRecvBuffer.size()==0) {
|
||||
// The only frame -- just send it up.
|
||||
OBJLOG(DEBUG) << "single frame message";
|
||||
mL3Out.write(new L3Frame(frame));
|
||||
return;
|
||||
}
|
||||
// The last of several -- concat and send it up.
|
||||
OBJLOG(DEBUG) << "last frame of message";
|
||||
mL3Out.write(new L3Frame(mRecvBuffer,frame.L3Part()));
|
||||
mRecvBuffer.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// One segment of many -- concat.
|
||||
// This is inefficient but simple.
|
||||
mRecvBuffer = L3Frame(mRecvBuffer,frame.L3Part());
|
||||
OBJLOG(DEBUG) <<"buffering recvBuffer=" << mRecvBuffer;
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::unexpectedMessage()
|
||||
{
|
||||
OBJLOG(NOTICE) << "mState=" << mState;
|
||||
// vISDN datalink.c:unexpeced_message
|
||||
// For LAPD, vISDN just keeps trying.
|
||||
// For LAPDm, just terminate the link.
|
||||
// Caller should hold mLock.
|
||||
abnormalRelease();
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::abnormalRelease()
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState;
|
||||
// GSM 04.06 5.6.4.
|
||||
// We're cutting a corner here that we'll
|
||||
// clean up when L3 is more stable.
|
||||
mL3Out.write(new L3Frame(ERROR));
|
||||
sendUFrameDM(true);
|
||||
writeL1(ERROR);
|
||||
clearState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::retransmissionProcedure()
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// vISDN datalink.c:lapd_invoke_retransmission_procedure
|
||||
// GSM 04.08 5.5.7, bullet point (a)
|
||||
OBJLOG(DEBUG) << "VS=" << mVS << " VA=" << mVA << " RC=" << mRC;
|
||||
mRC++;
|
||||
writeL1(mSentFrame);
|
||||
mT200.set(T200());
|
||||
mAckSignal.signal();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::open()
|
||||
{
|
||||
OBJLOG(DEBUG);
|
||||
{
|
||||
ScopedLock lock(mLock);
|
||||
if (!mRunning) {
|
||||
// We can't call this from the constructor,
|
||||
// since N201 may not be defined yet.
|
||||
mMaxIPayloadBits = 8*N201(L2Control::IFormat);
|
||||
mRunning = true;
|
||||
mUpstreamThread.start((void *(*)(void*))LAPDmServiceLoopAdapter,this);
|
||||
}
|
||||
mL3Out.clear();
|
||||
mL1In.clear();
|
||||
clearCounters();
|
||||
mState = LinkReleased;
|
||||
mAckSignal.signal();
|
||||
}
|
||||
|
||||
if (mSAPI==0) sendIdle();
|
||||
}
|
||||
|
||||
|
||||
void *GSM::LAPDmServiceLoopAdapter(L2LAPDm *lapdm)
|
||||
{
|
||||
lapdm->serviceLoop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::writeHighSide(const L3Frame& frame)
|
||||
{
|
||||
OBJLOG(DEBUG) << frame;
|
||||
switch (frame.primitive()) {
|
||||
case UNIT_DATA:
|
||||
// Send the data in a single U-Frame.
|
||||
sendUFrameUI(frame);
|
||||
break;
|
||||
case DATA:
|
||||
// Send the data as a series of I-Frames.
|
||||
mLock.lock();
|
||||
sendMultiframeData(frame);
|
||||
mLock.unlock();
|
||||
break;
|
||||
case ESTABLISH:
|
||||
// GSM 04.06 5.4.1.2
|
||||
// vISDN datalink.c:lapd_establish_datalink_procedure
|
||||
// The BTS side should never call this in SAP0.
|
||||
// See note in GSM 04.06 5.4.1.1.
|
||||
assert(mSAPI!=0 || mC==0);
|
||||
if (mState==LinkEstablished) break;
|
||||
mLock.lock();
|
||||
clearCounters();
|
||||
mState=AwaitingEstablish;
|
||||
mLock.unlock();
|
||||
sendUFrameSABM();
|
||||
break;
|
||||
case RELEASE:
|
||||
// GSM 04.06 5.4.4.2
|
||||
// vISDN datalink.c:lapd_dl_release_request
|
||||
if (mState==LinkReleased) break;
|
||||
mLock.lock();
|
||||
if (mState==LinkEstablished) waitForAck();
|
||||
clearCounters();
|
||||
mEstablishmentInProgress=false;
|
||||
mState=AwaitingRelease;
|
||||
mT200.set(T200()); // HACK?
|
||||
// Send DISC and wait for UA.
|
||||
// Don't return until released.
|
||||
sendUFrameDISC();
|
||||
waitForAck();
|
||||
mLock.unlock();
|
||||
break;
|
||||
case ERROR:
|
||||
// Forced release.
|
||||
mLock.lock();
|
||||
abnormalRelease();
|
||||
mLock.unlock();
|
||||
break;
|
||||
case HARDRELEASE:
|
||||
mLock.lock();
|
||||
clearState(HARDRELEASE);
|
||||
mLock.unlock();
|
||||
break;
|
||||
default:
|
||||
OBJLOG(ERR) << "unhandled primitive in L3->L2 " << frame;
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::writeLowSide(const L2Frame& frame)
|
||||
{
|
||||
OBJLOG(DEBUG) << frame;
|
||||
mL1In.write(new L2Frame(frame));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::serviceLoop()
|
||||
{
|
||||
mLock.lock();
|
||||
while (mRunning) {
|
||||
// Block for up to T200 expiration, then check T200.
|
||||
// Allow other threads to modify state while blocked.
|
||||
// Add 2 ms to prevent race condition due to roundoff error.
|
||||
unsigned timeout = mT200.remaining() + 2;
|
||||
// If SAP0 is released, other SAPs need to release also.
|
||||
if (mMaster) {
|
||||
if (mMaster->mState==LinkReleased) mState=LinkReleased;
|
||||
}
|
||||
if (!mT200.active()) {
|
||||
if (mState==LinkReleased) timeout=3600000;
|
||||
else timeout = T200();
|
||||
}
|
||||
OBJLOG(DEBUG) << "read blocking up to " << timeout << " ms, state=" << mState;
|
||||
mLock.unlock();
|
||||
// FIXME -- If the link is released, there should be no timeout at all.
|
||||
L2Frame* frame = mL1In.read(timeout);
|
||||
mLock.lock();
|
||||
if (frame!=NULL) {
|
||||
OBJLOG(DEBUG) << "state=" << mState << " received " << *frame;
|
||||
receiveFrame(*frame);
|
||||
delete frame;
|
||||
}
|
||||
if (mT200.expired()) T200Expiration();
|
||||
}
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::T200Expiration()
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// vISDN datalink.c:timer_T200.
|
||||
// GSM 04.06 5.4.1.3, 5.4.4.3, 5.5.7, 5.7.2.
|
||||
OBJLOG(INFO) << "state=" << mState << " RC=" << mRC;
|
||||
mT200.reset();
|
||||
switch (mState) {
|
||||
case AwaitingRelease:
|
||||
releaseLink();
|
||||
break;
|
||||
case ContentionResolution:
|
||||
case LinkEstablished:
|
||||
case AwaitingEstablish:
|
||||
if (mRC>N200()) abnormalRelease();
|
||||
else retransmissionProcedure();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::receiveFrame(const GSM::L2Frame& frame)
|
||||
{
|
||||
OBJLOG(DEBUG) << frame;
|
||||
|
||||
// Caller should hold mLock.
|
||||
|
||||
// Accept and process an incoming frame on the L1->L2 interface.
|
||||
// See vISDN datalink.c:lapd_dlc_recv for another example.
|
||||
|
||||
// Since LAPDm is a lot simpler than LAPD, there are a lot fewer primitives.
|
||||
|
||||
// FIXME -- This is a HACK to fix channels that get stuck in wierd states.
|
||||
// But if channels are stuck in wierd states, it means there's a bug somehwere.
|
||||
/*
|
||||
if (stuckChannel(frame)) {
|
||||
OBJLOG(ERR) << "detected stuck channel, releasing in L2";
|
||||
abnormalRelease();
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
switch (frame.primitive()) {
|
||||
case ESTABLISH:
|
||||
// This primitive means the first L2 frame is on the way.
|
||||
clearCounters();
|
||||
break;
|
||||
case DATA:
|
||||
// Dispatch on the frame type.
|
||||
switch (frame.controlFormat()) {
|
||||
case L2Control::IFormat: receiveIFrame(frame); break;
|
||||
case L2Control::SFormat: receiveSFrame(frame); break;
|
||||
case L2Control::UFormat: receiveUFrame(frame); break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OBJLOG(ERR) << "unhandled primitive in L1->L2 " << frame;
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::receiveUFrame(const L2Frame& frame)
|
||||
{
|
||||
// Also see vISDN datalink.c:lapd_socket_handle_uframe
|
||||
OBJLOG(DEBUG) << frame;
|
||||
switch (frame.UFrameType()) {
|
||||
case L2Control::SABMFrame: receiveUFrameSABM(frame); break;
|
||||
case L2Control::DMFrame: receiveUFrameDM(frame); break;
|
||||
case L2Control::UIFrame: receiveUFrameUI(frame); break;
|
||||
case L2Control::DISCFrame: receiveUFrameDISC(frame); break;
|
||||
case L2Control::UAFrame: receiveUFrameUA(frame); break;
|
||||
default:
|
||||
OBJLOG(NOTICE) << " could not parse U-Bits " << frame;
|
||||
unexpectedMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::receiveUFrameSABM(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// Process the incoming SABM command.
|
||||
// GSM 04.06 3.8.2, 5.4.1
|
||||
// Q.921 5.5.1.2.
|
||||
// Also borrows from vISDN datalink.c:lapd_socket_handle_uframe_sabm.
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
// Ignore frame if P!=1.
|
||||
// See GSM 04.06 5.4.1.2.
|
||||
if (!frame.PF()) return;
|
||||
// Dispatch according to current state.
|
||||
// BTW, LAPDm can always enter multiframe mode when requested,
|
||||
// so that's another big simplification over ISDN/LAPD.
|
||||
switch (mState) {
|
||||
case LinkReleased:
|
||||
// GSM 04.06 5.4.5, 5.4.1.2, 5.4.1.4
|
||||
clearCounters();
|
||||
mEstablishmentInProgress = true;
|
||||
// Tell L3 what happened.
|
||||
mL3Out.write(new L3Frame(ESTABLISH));
|
||||
if (frame.L()) {
|
||||
// Presence of an L3 payload indicates contention resolution.
|
||||
// GSM 04.06 5.4.1.4.
|
||||
mState=ContentionResolution;
|
||||
mContentionCheck = frame.sum();
|
||||
mL3Out.write(new L3Frame(frame.L3Part(),DATA));
|
||||
// Echo back payload.
|
||||
sendUFrameUA(frame);
|
||||
} else {
|
||||
mState=LinkEstablished;
|
||||
sendUFrameUA(frame.PF());
|
||||
}
|
||||
break;
|
||||
case ContentionResolution:
|
||||
// GSM 04.06 5.4.1.4
|
||||
// This guards against the remote possibility that two handsets
|
||||
// are sending on the same channel at the same time.
|
||||
// vISDN's LAPD doesn't need/do this since peers are hard-wired
|
||||
if (frame.sum()!=mContentionCheck) break;
|
||||
mState=LinkEstablished;
|
||||
sendUFrameUA(frame);
|
||||
break;
|
||||
case AwaitingEstablish:
|
||||
// Huh? This would mean both sides sent SABM at the same time/
|
||||
// That should not happen in GSM.
|
||||
sendUFrameUA(frame.PF());
|
||||
OBJLOG(WARNING) << "simulatenous SABM attempts";
|
||||
break;
|
||||
case AwaitingRelease:
|
||||
// If we are awaiting release, we will not enter ABM.
|
||||
// So we send DM to indicate that.
|
||||
sendUFrameDM(frame.PF());
|
||||
break;
|
||||
case LinkEstablished:
|
||||
// Latency in contention resolution, GSM 04.06 5.4.2.1.
|
||||
if (mEstablishmentInProgress) {
|
||||
if (frame.L()) sendUFrameUA(frame);
|
||||
else sendUFrameUA(frame.PF());
|
||||
break;
|
||||
}
|
||||
if (frame.L()) {
|
||||
abnormalRelease();
|
||||
break;
|
||||
}
|
||||
// Re-establishment procedure, GSM 04.06 5.6.3.
|
||||
// This basically resets the ack engine.
|
||||
// We should not actually see this, as of rev 2.4.
|
||||
OBJLOG(WARNING) << "reestablishment not really supported";
|
||||
sendUFrameUA(frame.PF());
|
||||
clearCounters();
|
||||
break;
|
||||
default:
|
||||
unexpectedMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::receiveUFrameDISC(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
mEstablishmentInProgress = false;
|
||||
switch (mState) {
|
||||
case AwaitingEstablish:
|
||||
clearState();
|
||||
break;
|
||||
case LinkReleased:
|
||||
// GSM 04.06 5.4.5
|
||||
sendUFrameDM(frame.PF());
|
||||
clearState();
|
||||
break;
|
||||
case ContentionResolution:
|
||||
case LinkEstablished:
|
||||
// Shut down the link and ack with UA.
|
||||
// GSM 04.06 5.4.4.2.
|
||||
sendUFrameUA(frame.PF());
|
||||
clearState();
|
||||
break;
|
||||
case AwaitingRelease:
|
||||
// We can arrive here if both ends sent DISC at the same time.
|
||||
// GSM 04.06 5.4.6.1.
|
||||
sendUFrameUA(frame.PF());
|
||||
break;
|
||||
default:
|
||||
unexpectedMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::receiveUFrameUA(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// GSM 04.06 3.8.8
|
||||
// vISDN datalink.c:lapd_socket_handle_uframe_ua
|
||||
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
if (!frame.PF()) {
|
||||
unexpectedMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mState) {
|
||||
case AwaitingEstablish:
|
||||
// We sent SABM and the peer responded.
|
||||
clearCounters();
|
||||
mState = LinkEstablished;
|
||||
mAckSignal.signal();
|
||||
mL3Out.write(new L3Frame(ESTABLISH));
|
||||
break;
|
||||
case AwaitingRelease:
|
||||
// We sent DISC and the peer responded.
|
||||
clearState();
|
||||
break;
|
||||
default:
|
||||
unexpectedMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::receiveUFrameDM(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
// GSM 04.06 5.4.5
|
||||
if (mState==LinkReleased) return;
|
||||
// GSM 04.06 5.4.6.3
|
||||
if (!frame.PF()) return;
|
||||
|
||||
// Because we do not support multiple TEIs in LAPDm,
|
||||
// and because we should never get DM in response to SABM,
|
||||
// this procedure is much simpler that in vISDN LAPD.
|
||||
// Unlike LAPD, there's also no reason for LAPDm to not be
|
||||
// able to establish ABM, so if we get this message
|
||||
// we know the channel is screwed up.
|
||||
|
||||
clearState();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::receiveUFrameUI(const L2Frame& frame)
|
||||
{
|
||||
// The zero-length frame is the idle frame.
|
||||
if (frame.L()==0) return;
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
mL3Out.write(new L3Frame(frame.tail(24),UNIT_DATA));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::receiveSFrame(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// See GSM 04.06 5.4.1.4.
|
||||
mEstablishmentInProgress = false;
|
||||
switch (frame.SFrameType()) {
|
||||
case L2Control::RRFrame: receiveSFrameRR(frame); break;
|
||||
case L2Control::REJFrame: receiveSFrameREJ(frame); break;
|
||||
default: unexpectedMessage(); return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::receiveSFrameRR(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
// GSM 04.06 3.8.5.
|
||||
// Q.921 3.6.6.
|
||||
// vISDN datalink.c:lapd_handle_sframe_rr
|
||||
// Again, since LAPDm allows only one outstanding frame
|
||||
// (k=1), this is a lot simpler than in LAPD.
|
||||
switch (mState) {
|
||||
case ContentionResolution:
|
||||
mState = LinkEstablished;
|
||||
// continue to next case...
|
||||
case LinkEstablished:
|
||||
// "inquiry response procedure"
|
||||
// Never actually seen that happen in GSM...
|
||||
if ((frame.CR()!=mC) && (frame.PF())) {
|
||||
sendSFrameRR(true);
|
||||
}
|
||||
processAck(frame.NR());
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void L2LAPDm::receiveSFrameREJ(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
// GSM 04.06 3.8.6, 5.5.4
|
||||
// Q.921 3.7.6, 5.6.4.
|
||||
// vISDN datalink.c:lapd_handle_s_frame_rej.
|
||||
switch (mState) {
|
||||
case ContentionResolution:
|
||||
mState = LinkEstablished;
|
||||
// continue to next case...
|
||||
case LinkEstablished:
|
||||
// FIXME -- The spec says to do this but it breaks multiframe transmission.
|
||||
//mVS = mVA = frame.NR();
|
||||
processAck(frame.NR());
|
||||
if (frame.PF()) {
|
||||
if (frame.CR()!=mC) sendSFrameRR(true);
|
||||
else {
|
||||
unexpectedMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Since k=1, there's really nothing to retransmit,
|
||||
// other than what was just rejected, so kust stop sending it.
|
||||
sendIdle();
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
// Send an idle frame to clear any repeating junk on the channel.
|
||||
sendIdle();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::receiveIFrame(const L2Frame& frame)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// See GSM 04.06 5.4.1.4.
|
||||
mEstablishmentInProgress = false;
|
||||
OBJLOG(INFO) << "state=" << mState << " NS=" << frame.NS() << " NR=" << frame.NR() << " " << frame;
|
||||
// vISDN datalink.c:lapd_handle_iframe
|
||||
// GSM 04.06 5.5.2, 5.7.1
|
||||
// Q.921 5.6.2, 5.8.1
|
||||
switch (mState) {
|
||||
case ContentionResolution:
|
||||
mState=LinkEstablished;
|
||||
// continue to next case...
|
||||
case LinkEstablished:
|
||||
processAck(frame.NR());
|
||||
if (frame.NS()==mVR) {
|
||||
mVR = (mVR+1)%8;
|
||||
bufferIFrameData(frame);
|
||||
sendSFrameRR(frame.PF());
|
||||
} else {
|
||||
// GSM 04.06 5.7.1.
|
||||
// Q.921 5.8.1.
|
||||
sendSFrameREJ(frame.PF());
|
||||
}
|
||||
case LinkReleased:
|
||||
// GSM 04.06 5.4.5
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::sendSFrameRR(bool FBit)
|
||||
{
|
||||
// GSM 04.06 3.8.5.
|
||||
// The caller should hold mLock.
|
||||
OBJLOG(INFO) << "F=" << FBit << " state=" << mState << " VS=" << mVS << " VR=" << mVR;
|
||||
L2Address address(mR,mSAPI);
|
||||
L2Control control(L2Control::SFormat,FBit,0);
|
||||
static const L2Length length;
|
||||
L2Header header(address,control,length);
|
||||
header.control().NR(mVR);
|
||||
writeL1NoAck(L2Frame(header));
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::sendSFrameREJ(bool FBit)
|
||||
{
|
||||
// GSM 04.06 3.8.6.
|
||||
// The caller should hold mLock.
|
||||
OBJLOG(INFO) << "F=" << FBit << " state=" << mState;
|
||||
L2Address address(mR,mSAPI);
|
||||
L2Control control(L2Control::SFormat,FBit,2);
|
||||
static const L2Length length;
|
||||
L2Header header(address,control,length);
|
||||
header.control().NR(mVR);
|
||||
writeL1NoAck(L2Frame(header));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::sendUFrameDM(bool FBit)
|
||||
{
|
||||
// The caller need not hold mLock.
|
||||
OBJLOG(INFO) << "F=" << FBit << " state=" << mState;
|
||||
L2Address address(mR,mSAPI);
|
||||
L2Control control(L2Control::UFormat,FBit,0x03);
|
||||
static const L2Length length;
|
||||
L2Header header(address,control,length);
|
||||
writeL1NoAck(L2Frame(header));
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::sendUFrameUA(bool FBit)
|
||||
{
|
||||
// The caller need not hold mLock.
|
||||
OBJLOG(INFO) << "F=" << FBit << " state=" << mState;
|
||||
L2Address address(mR,mSAPI);
|
||||
L2Control control(L2Control::UFormat,FBit,0x0C);
|
||||
static const L2Length length;
|
||||
L2Header header(address,control,length);
|
||||
writeL1NoAck(L2Frame(header));
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::sendUFrameUA(const L2Frame& frame)
|
||||
{
|
||||
// Send UA frame with a echoed payload.
|
||||
// This is used in the contention resolution procedure.
|
||||
// GSM 04.06 5.4.1.4.
|
||||
// The caller need not hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " " << frame;
|
||||
L2Address address(mR,mSAPI);
|
||||
L2Control control(L2Control::UFormat,frame.PF(),0x0C);
|
||||
L2Length length(frame.L());
|
||||
L2Header header(address,control,length);
|
||||
writeL1NoAck(L2Frame(header,frame.L3Part()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::sendUFrameSABM()
|
||||
{
|
||||
// GMS 04.06 3.8.2, 5.4.1
|
||||
// The caller need not hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState;
|
||||
L2Address address(mC,mSAPI);
|
||||
L2Control control(L2Control::UFormat,1,0x07);
|
||||
static const L2Length length;
|
||||
L2Header header(address,control,length);
|
||||
writeL1Ack(L2Frame(header));
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::sendUFrameDISC()
|
||||
{
|
||||
// GMS 04.06 3.8.3, 5.4.4.2
|
||||
// The caller need not hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState;
|
||||
L2Address address(mC,mSAPI);
|
||||
L2Control control(L2Control::UFormat,1,0x08);
|
||||
static const L2Length length;
|
||||
L2Header header(address,control,length);
|
||||
writeL1Ack(L2Frame(header));
|
||||
}
|
||||
|
||||
|
||||
void L2LAPDm::sendUFrameUI(const L3Frame& l3)
|
||||
{
|
||||
// GSM 04.06 3.8.4, 5.3.2, not supporting the short header format.
|
||||
// The caller need not hold mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " payload=" << l3;
|
||||
L2Address address(mC,mSAPI);
|
||||
L2Control control(L2Control::UFormat,1,0x00);
|
||||
L2Length length(l3.L2Length());
|
||||
L2Header header(address,control,length);
|
||||
writeL1NoAck(L2Frame(header,l3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::sendMultiframeData(const L3Frame& l3)
|
||||
{
|
||||
// See GSM 04.06 5.8.5
|
||||
assert(l3.length()<=251);
|
||||
|
||||
// Implements GSM 04.06 5.4.2
|
||||
// Caller holds mLock.
|
||||
OBJLOG(INFO) << "state=" << mState << " payload=" << l3;
|
||||
// HACK -- Sleep before returning to prevent fast spinning
|
||||
// in SACCH L3 during release.
|
||||
if (mState==LinkReleased) {
|
||||
OBJLOG(ERR) << "attempt to send DATA on released LAPm channel";
|
||||
abnormalRelease();
|
||||
sleepFrames(51);
|
||||
return;
|
||||
}
|
||||
mDiscardIQueue = false;
|
||||
size_t bitsRemaining = l3.size();
|
||||
size_t sendIndex = 0;
|
||||
OBJLOG(DEBUG) << "sendIndex=" << sendIndex<< " bitsRemaining=" << bitsRemaining;
|
||||
while (bitsRemaining>0) {
|
||||
size_t thisChunkSize = bitsRemaining;
|
||||
bool MBit = false;
|
||||
if (thisChunkSize==0) break;
|
||||
if (thisChunkSize>mMaxIPayloadBits) {
|
||||
thisChunkSize = mMaxIPayloadBits;
|
||||
MBit = true;
|
||||
}
|
||||
// This is the point where we block and allow
|
||||
// the upstream thread to modify the LAPDm state.
|
||||
waitForAck();
|
||||
// Did we abort multiframe mode while waiting?
|
||||
if (mDiscardIQueue) {
|
||||
OBJLOG(DEBUG) <<"aborting (discard)";
|
||||
break;
|
||||
}
|
||||
if ((mState!=LinkEstablished) && (mState!=ContentionResolution)) {
|
||||
OBJLOG(DEBUG) << "aborting, state=" << mState;
|
||||
break;
|
||||
}
|
||||
OBJLOG(DEBUG) << "state=" << mState
|
||||
<< " sendIndex=" << sendIndex << " thisChunkSize=" << thisChunkSize
|
||||
<< " bitsRemaining=" << bitsRemaining << " MBit=" << MBit;
|
||||
sendIFrame(l3.segment(sendIndex,thisChunkSize),MBit);
|
||||
sendIndex += thisChunkSize;
|
||||
bitsRemaining -= thisChunkSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L2LAPDm::sendIFrame(const BitVector& payload, bool MBit)
|
||||
{
|
||||
// Caller should hold mLock.
|
||||
// GSM 04.06 5.5.1
|
||||
OBJLOG(INFO) << "M=" << MBit << " VS=" << mVS << " payload=" << payload;
|
||||
// Lots of sanity checking.
|
||||
assert(mState!=LinkReleased);
|
||||
assert(payload.size() <= mMaxIPayloadBits);
|
||||
assert(payload.size()%8 == 0);
|
||||
if (MBit) assert(payload.size()==mMaxIPayloadBits);
|
||||
// Build the header and send it.
|
||||
L2Address address(mC,mSAPI);
|
||||
L2Control control(mVR,mVS,0);
|
||||
L2Length length(payload.size()/8,MBit);
|
||||
L2Header header(address,control,length);
|
||||
mVS = (mVS+1)%8;
|
||||
writeL1Ack(L2Frame(header,payload));
|
||||
}
|
||||
|
||||
|
||||
bool L2LAPDm::stuckChannel(const L2Frame& frame)
|
||||
{
|
||||
// Check for excessive idling.
|
||||
if (frame.DCCHIdle()) mIdleCount++;
|
||||
else mIdleCount=0;
|
||||
return mIdleCount > maxIdle();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
527
GSM/GSML2LAPDm.h
Normal file
527
GSM/GSML2LAPDm.h
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Many elements follow Daniele Orlandi's <daniele@orlandi.com> vISDN/Q.921
|
||||
implementation, although no code is copied directly.
|
||||
*/
|
||||
|
||||
#ifndef L2LAPDM_H
|
||||
#define L2LAPDM_H
|
||||
|
||||
#include "GSMCommon.h"
|
||||
#include "GSMTransfer.h"
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
// Forward refs.
|
||||
class SAPMux;
|
||||
|
||||
/**@name L2 Processing Errors */
|
||||
//@{
|
||||
/** L2 Read Error is thrown if there is an error in the data on the input side. */
|
||||
class L2ReadError : public GSMError { };
|
||||
#define L2_READ_ERROR {throw L2ReadError();}
|
||||
|
||||
/** L2 Write Error is thrown if there is an error in the data on the output side. */
|
||||
class L2WriteError : public GSMError { };
|
||||
#define L2_WRITE_ERROR {throw L2WriteError();}
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Skeleton for data link layer (L2) entities.
|
||||
This is a base class from which the various channel classes are derived.
|
||||
Many derived classes are "thin" and do not implement full LAPDm.
|
||||
This is especially true of the downlink-only classes which do not have
|
||||
equivalents in Q.921 and HDLC.
|
||||
*/
|
||||
class L2DL {
|
||||
|
||||
protected:
|
||||
|
||||
SAPMux *mDownstream; ///< a pointer to the lower layer
|
||||
|
||||
|
||||
public:
|
||||
|
||||
L2DL()
|
||||
:mDownstream(NULL)
|
||||
{ }
|
||||
|
||||
virtual ~L2DL() {}
|
||||
|
||||
|
||||
void downstream(SAPMux *wDownstream)
|
||||
{ mDownstream = wDownstream; }
|
||||
|
||||
virtual void open() = 0;
|
||||
|
||||
/** N201 value for a given frame format on this channel, GSM 04.06 5.8.3. */
|
||||
virtual unsigned N201(GSM::L2Control::ControlFormat) const = 0;
|
||||
|
||||
/** N200 value for this channel, GSM 04.06 5.8.2. */
|
||||
virtual unsigned N200() const = 0;
|
||||
|
||||
/** T200 timeout for this channel, GSM 04.06 5.8.1. */
|
||||
virtual unsigned T200() const { return T200ms; }
|
||||
|
||||
/** Check for establishment of multifame mode; only valid for LAPDm. */
|
||||
virtual bool multiframeMode() const { assert(0); }
|
||||
|
||||
/**
|
||||
The L3->L2 interface.
|
||||
This is a blocking call and does not return until
|
||||
all of the corresponding radio bursts have been
|
||||
enqueued for transmission.
|
||||
That can take up to 1/2 second.
|
||||
*/
|
||||
virtual void writeHighSide(const GSM::L3Frame&) = 0;
|
||||
|
||||
|
||||
/** The L1->L2 interface */
|
||||
virtual void writeLowSide(const GSM::L2Frame&) = 0;
|
||||
|
||||
/** The L2->L3 interface. */
|
||||
virtual L3Frame* readHighSide(unsigned timeout=3600000) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A "thin" L2 for CCCH.
|
||||
This is a downlink-only channel.
|
||||
It supports only the Bbis L2 frame format (GSM 04.06 2.1).
|
||||
The "uplink" component of the CCCH is the RACH.
|
||||
See GSM 04.03 4.1.2.
|
||||
*/
|
||||
class CCCHL2 : public L2DL {
|
||||
|
||||
public:
|
||||
|
||||
unsigned N201(GSM::L2Control::ControlFormat format) const
|
||||
{ assert(format==L2Control::UFormat); return 22; }
|
||||
|
||||
unsigned N200() const { return 0; }
|
||||
|
||||
void open() {}
|
||||
|
||||
void writeLowSide(const GSM::L2Frame&) { assert(0); }
|
||||
|
||||
L3Frame* readHighSide(unsigned timeout=3600000) { assert(0); return NULL; }
|
||||
|
||||
void writeHighSide(const GSM::L3Frame&);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
LAPDm transceiver, GSM 04.06, borrows from ITU-T Q.921 (LAPD) and ISO-13239 (HDLC).
|
||||
Dedicated control channels need full-blown LAPDm.
|
||||
|
||||
LAPDm is best be thought of as lightweight HDLC.
|
||||
The main differences between LAPDm and HDLC are:
|
||||
- LAPDm allows no more than one outstanding unacknowledged I-frame (k=1, GSM 04.06 5.8.4).
|
||||
- LAPDm does not support extended header formats (GSM 04.06 3).
|
||||
- LAPDm supports only the SABM, DISC, DM, UI and UA U-Frames (GSM 04.06 3.4, 3.8.1).
|
||||
- LAPDm supports the RR and REJ S-Frames (GSM 04.06 3.4, 3.8.1), but not RNR (GSM 04.06 3.8.7, see Note).
|
||||
- LAPDm has just one internal timer, T200.
|
||||
- LAPDm supports only one terminal endpoint, whose TEI is implied.
|
||||
- LAPDm should always be able to enter ABM when requested.
|
||||
- LAPDm can never be in a recevier-not-ready condition (GSM 04.06 3.8.7 , see Note).
|
||||
|
||||
In a first release, we can simplify further by:
|
||||
- not supporting the Bter short header format
|
||||
- acking each received I-frame with an RR frame, even when outgoing I-frames are pending
|
||||
- using the Bbis format for L3 messages that use the L2 pseudolength element
|
||||
- just using independent L2s for each active SAP
|
||||
- just using independent L2s on each dedicated channel, which works with k=1
|
||||
*/
|
||||
class L2LAPDm : public L2DL {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
LAPD states, Q.921 4.3.
|
||||
We have few states than vISDN LAPD because LAPDm is simpler.
|
||||
*/
|
||||
enum LAPDState {
|
||||
LinkReleased,
|
||||
AwaitingEstablish, ///< note that the BTS should never be in this state
|
||||
AwaitingRelease,
|
||||
LinkEstablished,
|
||||
ContentionResolution ///< GMS 04.06 5.4.1.4
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
Thread mUpstreamThread; ///< a thread for upstream traffic and T200 timeouts
|
||||
bool mRunning; ///< true once the service loop starts
|
||||
L3FrameFIFO mL3Out; ///< we connect L2->L3 through a FIFO
|
||||
L2FrameFIFO mL1In; ///< we connect L1->L2 through a FIFO
|
||||
|
||||
unsigned mC; ///< the "C" bit for commands, 1 for BTS, 0 for MS
|
||||
unsigned mR; ///< this "R" bit for commands, 0 for BTS, 1 for MS
|
||||
|
||||
unsigned mSAPI; ///< the service access point indicator for this L2
|
||||
|
||||
L2LAPDm *mMaster; ///< This points to the SAP0 LAPDm on this channel.
|
||||
|
||||
|
||||
|
||||
/**@name Mutex-protected state shared by uplink and downlink threads. */
|
||||
//@{
|
||||
mutable Mutex mLock;
|
||||
/**@name State variables from GSM 04.06 3.5.2 */
|
||||
//@{
|
||||
unsigned mVS; ///< GSM 3.5.2.2, Q.921 3.5.2.2, send counter, NS+1 of last sent I-frame
|
||||
unsigned mVA; ///< GSM 3.5.2.3, Q.921 3.5.2.3, ack counter, NR+1 of last acked I-frame
|
||||
unsigned mVR; ///< GSM 3.5.2.5, Q.921 3.5.2.5, recv counter, NR+1 of last recvd I-frame
|
||||
LAPDState mState; ///< current protocol state
|
||||
Signal mAckSignal; ///< signal used to wake a thread waiting for an ack
|
||||
//@}
|
||||
bool mEstablishmentInProgress; ///< flag described in GSM 04.06 5.4.1.4
|
||||
/**@name Segmentation and retransmission. */
|
||||
//@{
|
||||
BitVector mRecvBuffer; ///< buffer to concatenate received I-frames, same role as sk_rcvbuf in vISDN
|
||||
L2Frame mSentFrame; ///< previous ack-able kept for retransmission, same role as sk_write_queue in vISDN
|
||||
bool mDiscardIQueue; ///< a flag used to abort I-frame sending
|
||||
unsigned mContentionCheck; ///< checksum used for contention resolution, GSM 04.06 5.4.1.4.
|
||||
unsigned mRC; ///< retransmission counter, GSM 04.06 5.4.1-5.4.4
|
||||
Z100Timer mT200; ///< retransmission timer, GSM 04.06 5.8.1
|
||||
size_t mMaxIPayloadBits; ///< N201*8 for the I-frame
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/** A handy idle frame. */
|
||||
L2Frame mIdleFrame;
|
||||
|
||||
/** A lock to control multi-threaded access to L1->L2. */
|
||||
Mutex mL1Lock;
|
||||
|
||||
/** HACK -- A count of consecutive idle frames. Used to spot stuck channels. */
|
||||
unsigned mIdleCount;
|
||||
|
||||
/** HACK -- Return maximum allowed idle count. */
|
||||
virtual unsigned maxIdle() const =0;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Construct a LAPDm transceiver.
|
||||
@param wC "Command" bit, "1" for BTS, "0" for MS,
|
||||
GSM 04.06 3.3.2.
|
||||
@param wSAPI Service access point indicatior,
|
||||
GSM 040.6 3.3.3.
|
||||
*/
|
||||
L2LAPDm(unsigned wC=1, unsigned wSAPI=0);
|
||||
|
||||
virtual ~L2LAPDm() {}
|
||||
|
||||
|
||||
/** Process an uplink L2 frame. */
|
||||
void writeLowSide(const GSM::L2Frame&);
|
||||
|
||||
/**
|
||||
Read the L3 output, with a timeout.
|
||||
Caller is responsible for deleting returned object.
|
||||
*/
|
||||
L3Frame* readHighSide(unsigned timeout=3600000)
|
||||
{ return mL3Out.read(timeout); }
|
||||
|
||||
/**
|
||||
Process a downlink L3 frame.
|
||||
This is a blocking call and does not return until
|
||||
all of the corresponding radio bursts have been
|
||||
enqueued for transmission.
|
||||
That can take up to 1/2 second.
|
||||
*/
|
||||
void writeHighSide(const GSM::L3Frame&);
|
||||
|
||||
|
||||
/** Prepare the channel for a new transaction. */
|
||||
virtual void open();
|
||||
|
||||
/** Set the "master" SAP, SAP0; should be called no more than once. */
|
||||
void master(L2LAPDm* wMaster)
|
||||
{ assert(!mMaster); mMaster=wMaster; }
|
||||
|
||||
/** Return true if in multiframe mode. */
|
||||
bool multiframeMode() const
|
||||
{ ScopedLock lock(mLock); return mState==LinkEstablished; }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/** Block until we receive any pending ack. */
|
||||
void waitForAck();
|
||||
|
||||
/** Send an L2Frame on the L2->L1 interface. */
|
||||
void writeL1(const L2Frame&);
|
||||
|
||||
void writeL1Ack(const L2Frame&); ///< send an ack-able frame on L2->L1
|
||||
void writeL1NoAck(const L2Frame&); ///< send a non-acked frame on L2->L1
|
||||
|
||||
/** Abort the link. */
|
||||
void linkError();
|
||||
|
||||
/** Clear the state variables to released condition. */
|
||||
void clearState(Primitive relesaeType=RELEASE);
|
||||
|
||||
/** Clear the ABM-related state variables. */
|
||||
void clearCounters();
|
||||
|
||||
/** Go to the "link released" state. */
|
||||
void releaseLink(Primitive releaseType=RELEASE);
|
||||
|
||||
/** We go here when something goes really wrong. */
|
||||
void abnormalRelease();
|
||||
|
||||
/** Abort link on unexpected message. */
|
||||
void unexpectedMessage();
|
||||
|
||||
/** Process an ack. Also forces state to LinkEstablished. */
|
||||
void processAck(unsigned NR);
|
||||
|
||||
/** Retransmit last ackable frame. */
|
||||
void retransmissionProcedure();
|
||||
|
||||
/** Clear any outgoing L3 frame. */
|
||||
void discardIQueue() { mDiscardIQueue=true; }
|
||||
|
||||
/**
|
||||
Accept and concatenate an I-frame data payload.
|
||||
GSM 04.06 5.5.2 (first 2 bullet points)
|
||||
*/
|
||||
void bufferIFrameData(const L2Frame&);
|
||||
|
||||
/**@name Receive-handlers for the various frame types. */
|
||||
//@{
|
||||
void receiveFrame(const L2Frame&); ///< Top-level frame handler.
|
||||
/*
|
||||
We will postpone support for suspension/resumption of multiframe mode (GSM 04.06 5.4.3).
|
||||
This will greatly simplify the L2 state machine.
|
||||
*/
|
||||
void receiveIFrame(const L2Frame&); ///< GSM 04.06 3.8.1, 5.5.2
|
||||
/**@name U-Frame handlers */
|
||||
//@{
|
||||
void receiveUFrame(const L2Frame&); ///< sub-dispatch for all U-Frames
|
||||
void receiveUFrameSABM(const L2Frame&); ///< GMS 04.06 3.8.2, 5.4.1
|
||||
void receiveUFrameDISC(const L2Frame&); ///< GSM 04.06 3.8.3, 5.4.4.2
|
||||
void receiveUFrameUI(const L2Frame&); ///< GSM 04.06 3.8.4, 5.2.1
|
||||
void receiveUFrameUA(const L2Frame&); ///< GSM 04.06 3.8.8
|
||||
void receiveUFrameDM(const L2Frame&); ///< GSM 04.06 3.8.9, 5.4.4.2
|
||||
//@}
|
||||
/**@name S-Frame handlers */
|
||||
//@{
|
||||
void receiveSFrame(const L2Frame&); ///< sub-dispatch for all S-Frames
|
||||
void receiveSFrameREJ(const L2Frame&); ///< GSM 04.06 3.8.6, 5.5.3
|
||||
void receiveSFrameRR(const L2Frame&); ///< GSM 04.06 3.8.5, 5.5.4
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/**@name Senders for various frame types. */
|
||||
//@{
|
||||
/*
|
||||
In vISDN, these are performed with functions
|
||||
output.c:lapd_send_uframe, datalink.c:lapd_send_sframe,
|
||||
output.c:lapd_prepare_uframe, output.c:lapd_prepare_iframe.
|
||||
We've broken these out into a specific function for each
|
||||
frame type. For example, to send a DISC in vISDN, you call
|
||||
lapd_send_uframe with arguments that specify the DISC frame.
|
||||
In OpenBTS, you just call sendUFrameDISC.
|
||||
*/
|
||||
void sendMultiframeData(const L3Frame&); ///< send an L3 frame in one or more I-frames
|
||||
void sendIFrame(const BitVector&, bool); ///< GSM 04.06 3.8.1, 5.5.1, with payload and "M" flag
|
||||
void sendUFrameSABM(); ///< GMS 04.06 3.8.2, 5.4.1
|
||||
void sendUFrameDISC(); ///< GSM 04.06 3.8.3, 5.4.4.2
|
||||
void sendUFrameUI(const L3Frame&); ///< GSM 04.06 3.8.4, 5.2.1
|
||||
void sendUFrameUA(bool FBit); ///< GSM 04.06 3.8.8, 5.4.1, 5.4.4.2
|
||||
void sendUFrameUA(const L2Frame&); ///< GSM 04.06 3.8.8, 5.4.1.4
|
||||
void sendUFrameDM(bool FBit); ///< GMS 04.06 3.8.9, 5.4.4.2
|
||||
void sendSFrameRR(bool FBit); ///< GSM 04.06 3.8.5, 5.5.2
|
||||
void sendSFrameREJ(bool FBit); ///< GSM 04.06 3.8.6, 5.5.2
|
||||
//@}
|
||||
|
||||
/**
|
||||
Handle expiration of T200.
|
||||
See GSM 04.06 5.8.1 for a definition of T200.
|
||||
See GSM 04.06 5.4.1.3, 5.4.4.3, 5.5.7, 5.7.2 for actions to take upon expiration of T200.
|
||||
*/
|
||||
void T200Expiration();
|
||||
|
||||
/**
|
||||
Send the idle frame, GMS 04.06 5.4.2.3.
|
||||
This sends one idle frame to L1, but that sets up the modulator to start
|
||||
generating the idle pattern repeatedly. See README.IdleFilling.
|
||||
- This should be called in SAP0 when a channel is first opened.
|
||||
- This should be called after sending any frame
|
||||
when no further outgoing frames are pending.
|
||||
- This should be called after receiving a REJ frame.
|
||||
- This need not be called when the channel is closed,
|
||||
as L1 will generate its own filler pattern that is more
|
||||
appropriate in this condition.
|
||||
*/
|
||||
virtual void sendIdle() { writeL1(mIdleFrame); }
|
||||
|
||||
/**
|
||||
Increment or clear the idle count based on the current frame.
|
||||
@return true if we should abort
|
||||
*/
|
||||
bool stuckChannel(const L2Frame&);
|
||||
|
||||
/**
|
||||
The upstream service loop handles incoming L2 frames and T200 timeouts.
|
||||
*/
|
||||
void serviceLoop();
|
||||
|
||||
friend void *LAPDmServiceLoopAdapter(L2LAPDm*);
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream&, L2LAPDm::LAPDState);
|
||||
|
||||
|
||||
/** C-style adapter for LAPDm serice loop. */
|
||||
void *LAPDmServiceLoopAdapter(L2LAPDm*);
|
||||
|
||||
|
||||
|
||||
class SDCCHL2 : public L2LAPDm {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned maxIdle() const { return 50; }
|
||||
|
||||
/** GSM 04.06 5.8.3. We support only A/B formats. */
|
||||
unsigned N201(GSM::L2Control::ControlFormat format) const
|
||||
{ assert(format==L2Control::IFormat); return 20; }
|
||||
|
||||
/** GSM 04.06 5.8.2.1 */
|
||||
unsigned N200() const { return 23; }
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Construct the LAPDm part of the SDCCH.
|
||||
@param wC "Command" bit, "1" for BTS, "0" for MS.
|
||||
@param wSAPI Service access point indicatior.
|
||||
*/
|
||||
SDCCHL2(unsigned wC=1, unsigned wSAPI=0)
|
||||
:L2LAPDm(wC,wSAPI)
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Link Layer for Slow Associated Control Channel (SACCH).
|
||||
GSM 04.06 2 states that UI frames on the SACCH use format B4.
|
||||
However, in GSM 04.08 you see that all messages sent on the SACCH
|
||||
in the UI mode use an "L2 pseudolength" field. So we can greatly
|
||||
simplify the SACCH by just using the B format and letting the
|
||||
length field of the B format stand in for the "L2 pseudolength"
|
||||
field, since their content is identical. This will work well enough
|
||||
until we need to support L3 "rest octets" on the SACCH.
|
||||
*/
|
||||
class SACCHL2 : public L2LAPDm {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned maxIdle() const { return 1000; }
|
||||
|
||||
/** GSM 04.06 5.8.3. We support only A/B formats. */
|
||||
unsigned N201(GSM::L2Control::ControlFormat format) const
|
||||
{ assert(format==L2Control::IFormat); return 18; }
|
||||
|
||||
/** GSM 04.06 5.8.2.1 */
|
||||
unsigned N200() const { return 5; }
|
||||
|
||||
/** T200 timeout for this channel, GSM 04.06 5.8.1. */
|
||||
unsigned T200() const { return 4*T200ms; }
|
||||
|
||||
/** SACCH does not use idle frames. */
|
||||
void sendIdle() {};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Construct the LAPDm part of the SACCH.
|
||||
@param wC "Command" bit, "1" for BTS, "0" for MS.
|
||||
@param wSAPI Service access point indicatior.
|
||||
*/
|
||||
SACCHL2(unsigned wC=1, unsigned wSAPI=0)
|
||||
:L2LAPDm(wC,wSAPI)
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Link Layer for Fast Associated Control Channel (FACCH).
|
||||
*/
|
||||
class FACCHL2 : public L2LAPDm {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned maxIdle() const { return 500; }
|
||||
|
||||
/** GSM 04.06 5.8.3. We support only A/B formats. */
|
||||
unsigned N201(GSM::L2Control::ControlFormat format) const
|
||||
{ assert(format==L2Control::IFormat); return 20; }
|
||||
|
||||
/** GSM 04.06 5.8.2.1 */
|
||||
unsigned N200() const { return 34; }
|
||||
|
||||
/** FACCH does not need idle frames. */
|
||||
void sendIdle() {};
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Construct the LAPDm part of the FACCH.
|
||||
@param wC "Command" bit, "1" for BTS, "0" for MS.
|
||||
@param wSAPI Service access point indicatior.
|
||||
*/
|
||||
FACCHL2(unsigned wC=1, unsigned wSAPI=0)
|
||||
:L2LAPDm(wC,wSAPI)
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}; // namespace GSM
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
314
GSM/GSML3CCElements.cpp
Normal file
314
GSM/GSML3CCElements.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/**@file @brief Call Control messages, GSM 04.08 9.3 */
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "GSML3CCElements.h"
|
||||
#include <Logger.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
void L3BearerCapability::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
// See GSM 10.5.4.5.
|
||||
// This is a hell of a complex element, inherited from ISDN.
|
||||
// But we're going to ignore a lot of it.
|
||||
|
||||
// "octet 3"
|
||||
// We hard code this octet for circuit switched speech.
|
||||
dest.writeField(wp, 0x04, 8);
|
||||
|
||||
// "octet 3a"
|
||||
// We hard code for full rate speech v1, the GSM 06.10 codec.
|
||||
dest.writeField(wp,0x80,8);
|
||||
}
|
||||
|
||||
|
||||
void L3BearerCapability::parseV( const L3Frame& src, size_t &rp, size_t expectedLength )
|
||||
{
|
||||
// See GSM 10.5.4.5.
|
||||
// This is a hell of a complex element, inherited from ISDN.
|
||||
|
||||
// But we're just going to assume circuit-switched speech
|
||||
// with a full rate codec, since every phone supports that.
|
||||
// So we can just ignore this hideously complex element.
|
||||
|
||||
// Just move the read index and return.
|
||||
// Shhh. Our little secret.
|
||||
rp += 8*expectedLength;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void L3BearerCapability::text(ostream& os) const
|
||||
{
|
||||
os << "(default)";
|
||||
}
|
||||
|
||||
|
||||
void L3BCDDigits::parse(const L3Frame& src, size_t &rp, size_t numOctets)
|
||||
{
|
||||
unsigned i=0;
|
||||
size_t readOctets = 0;
|
||||
while (readOctets < numOctets) {
|
||||
unsigned d2 = src.readField(rp,4);
|
||||
unsigned d1 = src.readField(rp,4);
|
||||
readOctets++;
|
||||
mDigits[i++]=d1+'0';
|
||||
if (d2!=0x0f) mDigits[i++]=d2+'0';
|
||||
if (i>maxDigits) L3_READ_ERROR;
|
||||
}
|
||||
mDigits[i++]='\0';
|
||||
}
|
||||
|
||||
|
||||
void L3BCDDigits::write(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
unsigned index = 0;
|
||||
unsigned numDigits = strlen(mDigits);
|
||||
while (index < numDigits) {
|
||||
if ((index+1) < numDigits) dest.writeField(wp,mDigits[index+1]-'0',4);
|
||||
else dest.writeField(wp,0x0f,4);
|
||||
dest.writeField(wp,mDigits[index]-'0',4);
|
||||
index += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t L3BCDDigits::lengthV() const
|
||||
{
|
||||
unsigned sz = strlen(mDigits);
|
||||
return (sz/2) + (sz%2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, const L3BCDDigits& digits)
|
||||
{
|
||||
os << digits.digits();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CalledPartyBCDNumber::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
dest.writeField(wp, 0x01, 1);
|
||||
dest.writeField(wp, mType, 3);
|
||||
dest.writeField(wp, mPlan, 4);
|
||||
mDigits.write(dest,wp);
|
||||
}
|
||||
|
||||
void L3CalledPartyBCDNumber::parseV( const L3Frame &src, size_t &rp, size_t expectedLength )
|
||||
{
|
||||
LOG(DEBUG) << "L3CalledPartyBCDNumber::parseV rp="<<rp<<" expLen="<<expectedLength;
|
||||
// ext bit must be 1
|
||||
if (src.readField(rp, 1) != 1) L3_READ_ERROR;
|
||||
mType = (TypeOfNumber)src.readField(rp, 3);
|
||||
mPlan = (NumberingPlan)src.readField(rp, 4);
|
||||
mDigits.parse(src,rp,expectedLength-1);
|
||||
}
|
||||
|
||||
|
||||
size_t L3CalledPartyBCDNumber::lengthV() const
|
||||
{
|
||||
if (mDigits.lengthV()==0) return 0;
|
||||
return 1 + mDigits.lengthV();
|
||||
}
|
||||
|
||||
void L3CalledPartyBCDNumber::text(ostream& os) const
|
||||
{
|
||||
os << "type=" << mType;
|
||||
os << " plan=" << mPlan;
|
||||
os << " digits=" << mDigits;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CallingPartyBCDNumber::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
// If Octet3a is extended, then write 0 else 1.
|
||||
dest.writeField(wp, (!mHaveOctet3a & 0x01), 1);
|
||||
dest.writeField(wp, mType, 3);
|
||||
dest.writeField(wp, mPlan, 4);
|
||||
|
||||
if(mHaveOctet3a){
|
||||
dest.writeField(wp, 0x01, 1);
|
||||
dest.writeField(wp, mPresentationIndicator, 2);
|
||||
dest.writeField(wp, 0, 3);
|
||||
dest.writeField(wp, mScreeningIndicator, 2);
|
||||
}
|
||||
|
||||
mDigits.write(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CallingPartyBCDNumber::parseV( const L3Frame &src, size_t &rp, size_t expectedLength)
|
||||
{
|
||||
size_t remainingLength = expectedLength;
|
||||
// Read out first bit = 1.
|
||||
mHaveOctet3a = src.readField(rp, 1);
|
||||
mType = (TypeOfNumber)src.readField(rp, 3);
|
||||
mPlan = (NumberingPlan)src.readField(rp, 4);
|
||||
remainingLength -= 1;
|
||||
|
||||
if (mHaveOctet3a) {
|
||||
if (src.readField(rp,1)!=1) L3_READ_ERROR;
|
||||
mPresentationIndicator = src.readField(rp, 3);
|
||||
src.readField(rp,3);
|
||||
mScreeningIndicator = src.readField(rp, 4);
|
||||
remainingLength -= 1;
|
||||
}
|
||||
|
||||
mDigits.parse(src,rp,remainingLength);
|
||||
}
|
||||
|
||||
|
||||
size_t L3CallingPartyBCDNumber::lengthV() const
|
||||
{
|
||||
return 1 + mHaveOctet3a + mDigits.lengthV();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CallingPartyBCDNumber::text(ostream& os) const
|
||||
{
|
||||
os << "type=" << mType;
|
||||
os << " plan=" << mPlan;
|
||||
if (mHaveOctet3a) {
|
||||
os << " presentation=" << mPresentationIndicator;
|
||||
os << " screening=" << mScreeningIndicator;
|
||||
}
|
||||
os << " digits=" << mDigits;
|
||||
}
|
||||
|
||||
|
||||
void L3Cause::parseV(const L3Frame& src, size_t &rp , size_t expectedLength)
|
||||
{
|
||||
size_t pos = rp;
|
||||
rp += 8*expectedLength;
|
||||
|
||||
// Octet 3
|
||||
// We only supprt the GSM coding standard.
|
||||
if (src.readField(pos,4)!=0x0e) L3_READ_ERROR;
|
||||
mLocation = (Location)src.readField(pos,4);
|
||||
|
||||
// Octet 4
|
||||
if (src.readField(pos,1)!=1) L3_READ_ERROR;
|
||||
mCause = src.readField(pos,7);
|
||||
|
||||
// Skip the diagnostics.
|
||||
}
|
||||
|
||||
|
||||
void L3Cause::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// Write Octet3.
|
||||
dest.writeField(wp,0x0e,4);
|
||||
dest.writeField(wp,mLocation,4);
|
||||
|
||||
// Write Octet 4.
|
||||
dest.writeField(wp,0x01,1);
|
||||
dest.writeField(wp,mCause,7);
|
||||
}
|
||||
|
||||
|
||||
void L3Cause::text(ostream& os) const
|
||||
{
|
||||
os << "location=" << mLocation;
|
||||
os << " cause=0x" << hex << mCause << dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CallState::parseV( const L3Frame& src, size_t &rp)
|
||||
{
|
||||
rp +=2;
|
||||
mCallState = src.readField(rp, 6);
|
||||
}
|
||||
|
||||
void L3CallState::writeV( L3Frame& dest, size_t &wp ) const
|
||||
{
|
||||
dest.writeField(wp,3,2);
|
||||
dest.writeField(wp, mCallState, 6);
|
||||
}
|
||||
|
||||
void L3CallState::text(ostream& os) const
|
||||
{
|
||||
os << mCallState;
|
||||
}
|
||||
|
||||
void L3ProgressIndicator::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// octet 3
|
||||
// ext bit, GSM coding standard, spare bit
|
||||
dest.writeField(wp,0x0e,4);
|
||||
dest.writeField(wp,mLocation,4);
|
||||
|
||||
// octet 4
|
||||
dest.writeField(wp,1,1);
|
||||
dest.writeField(wp,mProgress,7);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3ProgressIndicator::text(ostream& os) const
|
||||
{
|
||||
os << "location=" << mLocation;
|
||||
os << " progress=0x" << hex << mProgress << dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3KeypadFacility::parseV(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mIA5 = src.readField(rp,8);
|
||||
}
|
||||
|
||||
|
||||
void L3KeypadFacility::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,mIA5,8);
|
||||
}
|
||||
|
||||
|
||||
void L3KeypadFacility::text(ostream& os) const
|
||||
{
|
||||
os << hex << "0x" << mIA5 << dec;
|
||||
}
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
|
||||
314
GSM/GSML3CCElements.h
Normal file
314
GSM/GSML3CCElements.h
Normal file
@@ -0,0 +1,314 @@
|
||||
/**@file Elements for Call Control, GSM 04.08 10.5.4. */
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef GSML3CCELEMENTS_H
|
||||
#define GSML3CCELEMENTS_H
|
||||
|
||||
#include "GSML3Message.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
/** Bearer Capability, GSM 04.08 10.5.4.5 */
|
||||
class L3BearerCapability : public L3ProtocolElement {
|
||||
|
||||
// The spec for this is really intimidating.
|
||||
// But we're just going to assume circuit-switched speech
|
||||
// with a full rate codec, since every phone supports that.
|
||||
// So we can just ignore this hideously complex element.
|
||||
|
||||
public:
|
||||
|
||||
L3BearerCapability() : L3ProtocolElement() {}
|
||||
|
||||
size_t lengthV() const { return 2; }
|
||||
void writeV( L3Frame& dest, size_t &wp ) const;
|
||||
void parseV( const L3Frame& src, size_t &rp, size_t expectedLength );
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** A general class for BCD numbers as they normally appear in L3. */
|
||||
class L3BCDDigits {
|
||||
|
||||
private:
|
||||
|
||||
static const unsigned maxDigits = 20;
|
||||
char mDigits[maxDigits+1]; ///< ITU-T E.164 limits address to 15 digits
|
||||
|
||||
public:
|
||||
|
||||
L3BCDDigits() { mDigits[0]='\0'; }
|
||||
|
||||
L3BCDDigits(const char* wDigits) { strncpy(mDigits,wDigits,sizeof(mDigits)-1); mDigits[sizeof(mDigits)-1]='\0'; }
|
||||
|
||||
void parse(const L3Frame& src, size_t &rp, size_t numOctets);
|
||||
void write(L3Frame& dest, size_t &wp) const;
|
||||
|
||||
/** Return number of octets needed to encode the digits. */
|
||||
size_t lengthV() const;
|
||||
|
||||
unsigned size() const { return strlen(mDigits); }
|
||||
const char* digits() const { return mDigits; }
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const L3BCDDigits&);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Calling Party BCD Number, GSM 04.08 10.5.4.9 */
|
||||
class L3CallingPartyBCDNumber : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
TypeOfNumber mType;
|
||||
NumberingPlan mPlan;
|
||||
|
||||
L3BCDDigits mDigits;
|
||||
|
||||
/**@name Octet 3a */
|
||||
//@{
|
||||
bool mHaveOctet3a;
|
||||
int mPresentationIndicator;
|
||||
int mScreeningIndicator;
|
||||
//@}
|
||||
|
||||
|
||||
public:
|
||||
|
||||
L3CallingPartyBCDNumber()
|
||||
:mType(UnknownTypeOfNumber),mPlan(UnknownPlan),
|
||||
mHaveOctet3a(false)
|
||||
{ }
|
||||
|
||||
L3CallingPartyBCDNumber( const char * wDigits )
|
||||
:mType(NationalNumber),mPlan(E164Plan),mDigits(wDigits),
|
||||
mHaveOctet3a(false)
|
||||
{ }
|
||||
|
||||
|
||||
NumberingPlan plan() const { return mPlan; }
|
||||
TypeOfNumber type() const { return mType; }
|
||||
const char* digits() const { return mDigits.digits(); }
|
||||
|
||||
size_t lengthV() const;
|
||||
void writeV( L3Frame& dest, size_t &wp ) const;
|
||||
void parseV( const L3Frame& src, size_t &rp, size_t expectedLength);
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** Called Party BCD Number, GSM 04.08 10.5.4.7 */
|
||||
class L3CalledPartyBCDNumber : public L3ProtocolElement {
|
||||
|
||||
|
||||
private:
|
||||
|
||||
TypeOfNumber mType;
|
||||
NumberingPlan mPlan;
|
||||
L3BCDDigits mDigits;
|
||||
|
||||
public:
|
||||
|
||||
L3CalledPartyBCDNumber()
|
||||
:mType(UnknownTypeOfNumber),
|
||||
mPlan(UnknownPlan)
|
||||
{ }
|
||||
|
||||
L3CalledPartyBCDNumber(const char * wDigits)
|
||||
:mType(NationalNumber),mPlan(E164Plan),mDigits(wDigits)
|
||||
{ }
|
||||
|
||||
L3CalledPartyBCDNumber(const L3CallingPartyBCDNumber& other)
|
||||
:mType(other.type()),mPlan(other.plan()),mDigits(other.digits())
|
||||
{ }
|
||||
|
||||
|
||||
NumberingPlan plan() const { return mPlan; }
|
||||
TypeOfNumber type() const { return mType; }
|
||||
const char* digits() const { return mDigits.digits(); }
|
||||
|
||||
size_t lengthV() const ;
|
||||
void writeV( L3Frame& dest, size_t &wp ) const;
|
||||
void parseV( const L3Frame& src, size_t &rp, size_t expectedLength );
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Cause, GSM 04.08 10.5.4.11
|
||||
Read the spec closely: we only have to support coding standard 3 (GSM),
|
||||
and that format doesn't carry the "recommendation" field.
|
||||
*/
|
||||
|
||||
class L3Cause : public L3ProtocolElement {
|
||||
|
||||
public:
|
||||
|
||||
enum Location {
|
||||
User=0,
|
||||
PrivateServingLocal=1,
|
||||
PublicServingLocal=2,
|
||||
Transit=3,
|
||||
PublicServingRemote=4,
|
||||
PrivateServingRemote=5,
|
||||
International=7,
|
||||
BeyondInternetworking=10
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Location mLocation;
|
||||
unsigned mCause;
|
||||
|
||||
public:
|
||||
|
||||
L3Cause(unsigned wCause=0, Location wLocation=PrivateServingLocal)
|
||||
:L3ProtocolElement(),
|
||||
mLocation(wLocation),mCause(wCause)
|
||||
{ }
|
||||
|
||||
Location location() const { return mLocation; }
|
||||
unsigned cause() const { return mCause; }
|
||||
|
||||
// We don't support diagnostics, so length=2.
|
||||
size_t lengthV() const { return 2; }
|
||||
|
||||
void writeV( L3Frame& dest, size_t &wp) const;
|
||||
void parseV( const L3Frame& src, size_t &rp , size_t expectedLength );
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** Call State, GSM 04.08 10.5.4.6. */
|
||||
class L3CallState : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mCallState;
|
||||
|
||||
public:
|
||||
|
||||
/** The default call state is the "Null" state. */
|
||||
L3CallState( unsigned wCallState=0 )
|
||||
:L3ProtocolElement(),
|
||||
mCallState(wCallState)
|
||||
{ }
|
||||
|
||||
size_t lengthV()const { return 1;}
|
||||
void writeV( L3Frame& dest, size_t &wp) const;
|
||||
void parseV( const L3Frame& src, size_t &rp );
|
||||
void parseV(const L3Frame&, size_t&, size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
/** GSM 04.08 10.5.4.21 */
|
||||
class L3ProgressIndicator : public L3ProtocolElement {
|
||||
|
||||
public:
|
||||
|
||||
enum Location {
|
||||
User=0,
|
||||
PrivateServingLocal=1,
|
||||
PublicServingLocal=2,
|
||||
PublicServingRemote=4,
|
||||
PrivateServingRemote=5,
|
||||
BeyondInternetworking=10
|
||||
};
|
||||
|
||||
enum Progress {
|
||||
Unspecified=0,
|
||||
NotISDN=1,
|
||||
DestinationNotISDN=2,
|
||||
OriginationNotISDN=3,
|
||||
ReturnedToISDN=4,
|
||||
InBandAvailable=8,
|
||||
EndToEndISDN=0x20,
|
||||
Queuing=0x40
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Location mLocation;
|
||||
Progress mProgress;
|
||||
|
||||
public:
|
||||
|
||||
/** Default values are unspecified progress in the BTS. */
|
||||
L3ProgressIndicator(Progress wProgress=Unspecified, Location wLocation=PrivateServingLocal)
|
||||
:L3ProtocolElement(),
|
||||
mLocation(wLocation),mProgress(wProgress)
|
||||
{}
|
||||
|
||||
Location location() const { return mLocation; }
|
||||
Progress progress() const { return mProgress; }
|
||||
|
||||
size_t lengthV() const { return 2; }
|
||||
void writeV(L3Frame& dest, size_t &wp ) const;
|
||||
void parseV(const L3Frame&, size_t&, size_t) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.4.17 */
|
||||
class L3KeypadFacility : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
char mIA5;
|
||||
|
||||
public:
|
||||
|
||||
L3KeypadFacility(char wIA5=0)
|
||||
:mIA5(wIA5)
|
||||
{}
|
||||
|
||||
char IA5() const { return mIA5; }
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame&, size_t&) const;
|
||||
void parseV(const L3Frame&, size_t&, size_t) { assert(0); }
|
||||
void parseV(const L3Frame& src, size_t& rp);
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
} // GSM
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
479
GSM/GSML3CCMessages.cpp
Normal file
479
GSM/GSML3CCMessages.cpp
Normal file
@@ -0,0 +1,479 @@
|
||||
/** @file Call Control messags, GSM 04.08 9.3. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include "GSML3CCMessages.h"
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L3CCMessage::MessageType val)
|
||||
{
|
||||
switch (val) {
|
||||
case L3CCMessage::Alerting:
|
||||
os << "Alerting"; break;
|
||||
case L3CCMessage::Connect:
|
||||
os << "Connect"; break;
|
||||
case L3CCMessage::Disconnect:
|
||||
os << "Disconnect"; break;
|
||||
case L3CCMessage::ConnectAcknowledge:
|
||||
os << "Connect Acknowledge"; break;
|
||||
case L3CCMessage::Release:
|
||||
os << "Release"; break;
|
||||
case L3CCMessage::ReleaseComplete:
|
||||
os << "Release Complete"; break;
|
||||
case L3CCMessage::Setup:
|
||||
os << "Setup"; break;
|
||||
case L3CCMessage::EmergencySetup:
|
||||
os << "Emergency Setup"; break;
|
||||
case L3CCMessage::CCStatus:
|
||||
os <<"CC Status"; break;
|
||||
case L3CCMessage::CallConfirmed:
|
||||
os <<"Call Confirmed"; break;
|
||||
case L3CCMessage::CallProceeding:
|
||||
os <<"Call Proceeding"; break;
|
||||
case L3CCMessage::StartDTMF:
|
||||
os << "Start DTMF"; break;
|
||||
case L3CCMessage::StartDTMFReject:
|
||||
os << "Start DTMF Reject"; break;
|
||||
case L3CCMessage::StartDTMFAcknowledge:
|
||||
os << "Start DTMF Acknowledge"; break;
|
||||
case L3CCMessage::StopDTMF:
|
||||
os << "Stop DTMF"; break;
|
||||
case L3CCMessage::StopDTMFAcknowledge:
|
||||
os << "Stop DTMF Acknowledge"; break;
|
||||
case L3CCMessage::Hold:
|
||||
os << "Hold"; break;
|
||||
case L3CCMessage::HoldReject:
|
||||
os << "Hold Reject"; break;
|
||||
default: os << hex << "0x" << (int)val << dec;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
L3CCMessage * GSM::L3CCFactory(L3CCMessage::MessageType MTI)
|
||||
{
|
||||
switch (MTI) {
|
||||
case L3CCMessage::Connect: return new L3Connect();
|
||||
case L3CCMessage::Alerting: return new L3Alerting();
|
||||
case L3CCMessage::Setup: return new L3Setup();
|
||||
case L3CCMessage::EmergencySetup: return new L3EmergencySetup();
|
||||
case L3CCMessage::Disconnect: return new L3Disconnect();
|
||||
case L3CCMessage::CallProceeding: return new L3CallProceeding();
|
||||
case L3CCMessage::Release: return new L3Release();
|
||||
case L3CCMessage::ReleaseComplete: return new L3ReleaseComplete();
|
||||
case L3CCMessage::ConnectAcknowledge: return new L3ConnectAcknowledge();
|
||||
case L3CCMessage::CCStatus: return new L3CCStatus();
|
||||
case L3CCMessage::CallConfirmed: return new L3CallConfirmed();
|
||||
case L3CCMessage::StartDTMF: return new L3StartDTMF();
|
||||
case L3CCMessage::StopDTMF: return new L3StopDTMF();
|
||||
case L3CCMessage::Hold: return new L3Hold();
|
||||
default: {
|
||||
LOG(NOTICE) << "no L3 CC factory support for message "<< MTI;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* parser for Call control messages, will only parse uplink */
|
||||
L3CCMessage * GSM::parseL3CC(const L3Frame& source)
|
||||
{
|
||||
// mask out bit #7 (1011 1111) so use 0xbf, see GSM 04.08 Table 10.3/3.
|
||||
L3CCMessage::MessageType MTI = (L3CCMessage::MessageType)(0xbf & source.MTI());
|
||||
LOG(DEBUG) << "MTI="<<MTI;
|
||||
|
||||
L3CCMessage *retVal = L3CCFactory(MTI);
|
||||
if (retVal==NULL) return NULL;
|
||||
|
||||
retVal->TI(source.TI());
|
||||
retVal->parse(source);
|
||||
LOG(DEBUG) << *retVal;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
void L3CCMessage::write(L3Frame& dest) const
|
||||
{
|
||||
// We override L3Message::write for the transaction identifier.
|
||||
size_t l3len = bitsNeeded();
|
||||
if (dest.size()!=l3len) dest.resize(l3len);
|
||||
size_t wp = 0;
|
||||
dest.writeField(wp,mTI,4);
|
||||
dest.writeField(wp,PD(),4);
|
||||
dest.writeField(wp,MTI(),8);
|
||||
|
||||
writeBody(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CCMessage::text(ostream& os) const
|
||||
{
|
||||
os << "CC " << (MessageType) MTI();
|
||||
os << " TI=" << mTI << " ";
|
||||
}
|
||||
|
||||
|
||||
size_t L3Alerting::l2BodyLength() const
|
||||
{
|
||||
size_t sum=0;
|
||||
if (mHaveProgress) sum += mProgress.lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
void L3Alerting::writeBody(L3Frame &dest, size_t &wp) const
|
||||
{
|
||||
if (mHaveProgress) mProgress.writeTLV(0x1E,dest,wp);
|
||||
}
|
||||
|
||||
void L3Alerting::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
skipTLV(0x1C,src,rp); // skip facility
|
||||
mHaveProgress = mProgress.parseTLV(0x1E,src,rp);
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
void L3Alerting::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if (mHaveProgress) os << "progress=(" << mProgress << ")";
|
||||
}
|
||||
|
||||
|
||||
size_t L3CallProceeding::l2BodyLength() const
|
||||
{
|
||||
size_t sum=0;
|
||||
if (mHaveProgress) sum += mProgress.lengthTLV();
|
||||
if( mHaveBearerCapability) sum += mBearerCapability.lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void L3CallProceeding::writeBody(L3Frame &dest, size_t &wp) const
|
||||
{
|
||||
if( mHaveBearerCapability) mBearerCapability.writeTLV(0x04, dest, wp);
|
||||
if (mHaveProgress) mProgress.writeTLV(0x1E, dest, wp);
|
||||
|
||||
}
|
||||
|
||||
void L3CallProceeding::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
skipTV(0x0D,4,src,rp); // skip repeat indicator
|
||||
skipTLV(0x04,src,rp); // skip bearer capability 1
|
||||
skipTLV(0x04,src,rp); // skip bearer capability 2
|
||||
skipTLV(0x1C,src,rp); // skip facility
|
||||
mHaveProgress = mProgress.parseTLV(0x1E,src,rp);
|
||||
}
|
||||
|
||||
void L3CallProceeding::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if (mHaveProgress) os << "progress=(" << mProgress << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
size_t L3Connect::l2BodyLength() const
|
||||
{
|
||||
size_t len=0;
|
||||
if (mHaveProgress) len += mProgress.lengthTLV();
|
||||
return len;
|
||||
}
|
||||
|
||||
void L3Connect::writeBody(L3Frame &dest, size_t &wp) const
|
||||
{
|
||||
if (mHaveProgress) mProgress.writeTLV(0x1E,dest,wp);
|
||||
}
|
||||
|
||||
void L3Connect::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
skipTLV(0x1c,src,rp); // facility
|
||||
mHaveProgress = mProgress.parseTLV(0x1e,src,rp);
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
|
||||
void L3Connect::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if (mHaveProgress) os << "progress=(" << mProgress << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
size_t L3Release::l2BodyLength() const
|
||||
{
|
||||
size_t sum = 0;
|
||||
if (mHaveCause) sum += mCause.lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void L3Release::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
if (mHaveCause) mCause.writeTLV(0x08,dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3Release::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mHaveCause = mCause.parseTLV(0x08,src,rp);
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
|
||||
void L3Release::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if (mHaveCause) os << "cause=(" << mCause << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t L3ReleaseComplete::l2BodyLength() const
|
||||
{
|
||||
size_t sum = 0;
|
||||
if (mHaveCause) sum += mCause.lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void L3ReleaseComplete::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
if (mHaveCause) mCause.writeTLV(0x08,dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3ReleaseComplete::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mHaveCause = mCause.parseTLV(0x08,src,rp);
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
|
||||
void L3ReleaseComplete::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if (mHaveCause) os << "cause=(" << mCause << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3Setup::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
if (mHaveBearerCapability) mBearerCapability.writeTLV(0x04, dest, wp);
|
||||
if (mHaveCallingPartyBCDNumber) mCallingPartyBCDNumber.writeTLV(0x5C,dest, wp);
|
||||
if (mHaveCalledPartyBCDNumber) mCalledPartyBCDNumber.writeTLV(0x5E,dest, wp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3Setup::parseBody( const L3Frame &src, size_t &rp )
|
||||
{
|
||||
skipTV(0x0D,4,src,rp); // skip Repeat Indicator.
|
||||
skipTLV(0x04,src,rp); // skip Bearer Capability 1.
|
||||
skipTLV(0x04,src,rp); // skip Bearer Capability 2.
|
||||
skipTLV(0x1C,src,rp); // skip Facility.
|
||||
skipTLV(0x1E,src,rp); // skip Progress.
|
||||
skipTLV(0x34,src,rp); // skip Signal.
|
||||
mHaveCallingPartyBCDNumber = mCallingPartyBCDNumber.parseTLV(0x5C,src,rp);
|
||||
skipTLV(0x5D,src,rp); // skip Calling Party Subaddress
|
||||
mHaveCalledPartyBCDNumber = mCalledPartyBCDNumber.parseTLV(0x5E,src,rp);
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
size_t L3Setup::l2BodyLength() const
|
||||
{
|
||||
int len = 0;
|
||||
if(mHaveBearerCapability) len += mBearerCapability.lengthTLV();
|
||||
if(mHaveCalledPartyBCDNumber) len += mCalledPartyBCDNumber.lengthTLV();
|
||||
if(mHaveCallingPartyBCDNumber) len += mCallingPartyBCDNumber.lengthTLV();
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void L3Setup::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if(mHaveCallingPartyBCDNumber)
|
||||
os <<"CallingPartyBCDNumber=("<<mCallingPartyBCDNumber<<")";
|
||||
if(mHaveCalledPartyBCDNumber)
|
||||
os <<"CalledPartyBCDNumber=("<<mCalledPartyBCDNumber<<")";
|
||||
}
|
||||
|
||||
|
||||
void L3CCStatus::parseBody( const L3Frame &src, size_t &rp )
|
||||
{
|
||||
mCause.parseLV(src, rp);
|
||||
mCallState.parseV(src, rp);
|
||||
}
|
||||
|
||||
void L3CCStatus::writeBody(L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
mCause.writeLV(dest, wp);
|
||||
mCallState.writeV(dest, wp);
|
||||
}
|
||||
|
||||
void L3CCStatus::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "cause=(" << mCause << ")";
|
||||
os << " callState=" << mCallState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3Disconnect::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
mCause.writeLV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3Disconnect::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mCause.parseLV(src,rp);
|
||||
}
|
||||
|
||||
|
||||
void L3Disconnect::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "cause=(" << mCause << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CallConfirmed::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
skipTV(0x0D,4,src,rp); // skip repeat indicator
|
||||
skipTLV(0x04,src,rp); // skip bearer capability 1
|
||||
skipTLV(0x04,src,rp); // skip bearer capability 2
|
||||
mHaveCause = mCause.parseTLV(0x08,src,rp);
|
||||
// ignore call control capabilities
|
||||
}
|
||||
|
||||
|
||||
size_t L3CallConfirmed::l2BodyLength() const
|
||||
{
|
||||
size_t sum=0;
|
||||
if (mHaveCause) sum += mCause.lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
void L3CallConfirmed::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
if (mHaveCause) os << "cause=(" << mCause << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3StartDTMF::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "key=" << mKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3StartDTMFAcknowledge::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "key=" << mKey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3StartDTMFReject::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "cause=(" << mCause << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
size_t L3Progress::l2BodyLength() const
|
||||
{
|
||||
return mProgress.lengthLV();
|
||||
}
|
||||
|
||||
void L3Progress::writeBody(L3Frame &dest, size_t &wp) const
|
||||
{
|
||||
mProgress.writeLV(dest,wp);
|
||||
}
|
||||
|
||||
void L3Progress::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mProgress.parseLV(src,rp);
|
||||
// ignore the rest
|
||||
}
|
||||
|
||||
void L3Progress::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "prog_ind=(" << mProgress << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3HoldReject::text(ostream& os) const
|
||||
{
|
||||
L3CCMessage::text(os);
|
||||
os << "cause=(" << mCause << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
649
GSM/GSML3CCMessages.h
Normal file
649
GSM/GSML3CCMessages.h
Normal file
@@ -0,0 +1,649 @@
|
||||
/**@file Messages for Call Control, GSM 04.08 9.3. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSML3CCMESSAGES_H
|
||||
#define GSML3CCMESSAGES_H
|
||||
|
||||
#include "GSMCommon.h"
|
||||
#include "GSML3Message.h"
|
||||
#include "GSML3CommonElements.h"
|
||||
#include "GSML3CCElements.h"
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This a virtual class for L3 messages in the Call Control protocol.
|
||||
These messages are defined in GSM 04.08 9.3.
|
||||
|
||||
GSM call control is based on and nearly identical to ISDN call control.
|
||||
ISDN call control is defined in ITU-T Q.931.
|
||||
*/
|
||||
class L3CCMessage : public L3Message {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mTI; ///< short transaction ID, GSM 04.07 11.2.3.1.3; upper bit is originator flag
|
||||
// Uppder bit is 0 for originating side, see GSM 04.07 11.2.3.1.3.
|
||||
|
||||
public:
|
||||
|
||||
/** GSM 04.08, Table 10.3, bit 6 is "don't care" */
|
||||
enum MessageType {
|
||||
/**@name call establishment */
|
||||
//@{
|
||||
Alerting=0x01,
|
||||
CallConfirmed=0x08,
|
||||
CallProceeding=0x02,
|
||||
Connect=0x07,
|
||||
Setup=0x05,
|
||||
EmergencySetup=0x0e,
|
||||
ConnectAcknowledge=0x0f,
|
||||
Progress=0x03,
|
||||
//@}
|
||||
/**@name call clearing */
|
||||
//@{
|
||||
Disconnect=0x25,
|
||||
Release=0x2d,
|
||||
ReleaseComplete=0x2a,
|
||||
//@}
|
||||
/**@name DTMF */
|
||||
//@{
|
||||
StartDTMF=0x35,
|
||||
StopDTMF=0x31,
|
||||
StopDTMFAcknowledge=0x32,
|
||||
StartDTMFAcknowledge=0x36,
|
||||
StartDTMFReject=0x37,
|
||||
//@}
|
||||
/**@name In-call services */
|
||||
//@{
|
||||
Hold=0x18,
|
||||
HoldReject=0x1a,
|
||||
//@}
|
||||
/**@name error reporting */
|
||||
//@{
|
||||
CCStatus= 0x3d
|
||||
//@}
|
||||
};
|
||||
|
||||
L3CCMessage(unsigned wTI)
|
||||
:L3Message(),mTI(wTI)
|
||||
{}
|
||||
|
||||
|
||||
size_t fullBodyLength() const { return l2BodyLength(); }
|
||||
|
||||
/** Override the write method to include transaction identifiers in header. */
|
||||
void write(L3Frame& dest) const;
|
||||
|
||||
L3PD PD() const { return L3CallControlPD; }
|
||||
|
||||
unsigned TI() const { return mTI; }
|
||||
void TI(unsigned wTI) { mTI = wTI; }
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, L3CCMessage::MessageType MTI);
|
||||
|
||||
|
||||
/**
|
||||
Parse a complete L3 call control message into its object type.
|
||||
@param source The L3 bits.
|
||||
@return A pointer to a new message or NULL on failure.
|
||||
*/
|
||||
L3CCMessage* parseL3CC(const L3Frame& source);
|
||||
|
||||
/**
|
||||
A Factory function to return a L3CCMessage of the specified MTI.
|
||||
Returns NULL if the MTI is not supported.
|
||||
*/
|
||||
L3CCMessage* L3CCFactory(L3CCMessage::MessageType MTI);
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.19 */
|
||||
class L3Release : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
// We're ignoring "facility" and "user-user" for now.
|
||||
bool mHaveCause;
|
||||
L3Cause mCause;
|
||||
|
||||
public:
|
||||
|
||||
L3Release(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveCause(false)
|
||||
{}
|
||||
|
||||
L3Release(unsigned wTI, const L3Cause& wCause)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveCause(true),mCause(wCause)
|
||||
{}
|
||||
|
||||
int MTI() const { return Release; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class L3CCStatus : public L3CCMessage
|
||||
{
|
||||
private:
|
||||
L3Cause mCause;
|
||||
L3CallState mCallState;
|
||||
public:
|
||||
|
||||
L3CCStatus(unsigned wTI=7)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
L3CCStatus(unsigned wTI, const L3Cause &wCause, const L3CallState &wCallState)
|
||||
:L3CCMessage(wTI),
|
||||
mCause(wCause),
|
||||
mCallState(wCallState)
|
||||
{}
|
||||
|
||||
int MTI() const { return CCStatus; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
size_t l2BodyLength() const { return 4; }
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.19 */
|
||||
class L3ReleaseComplete : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
// We're ignoring "facility" and "user-user" for now.
|
||||
bool mHaveCause;
|
||||
L3Cause mCause;
|
||||
|
||||
public:
|
||||
|
||||
L3ReleaseComplete(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveCause(false)
|
||||
{}
|
||||
|
||||
L3ReleaseComplete(unsigned wTI, const L3Cause& wCause)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveCause(true),mCause(wCause)
|
||||
{}
|
||||
|
||||
int MTI() const { return ReleaseComplete; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
GSM 04.08 9.3.23
|
||||
This message can have different forms for uplink and downlink
|
||||
but the TLV format is flexiable enough to allow us to use one class for both.
|
||||
*/
|
||||
class L3Setup : public L3CCMessage
|
||||
{
|
||||
|
||||
// We fill in IEs one at a time as we need them.
|
||||
|
||||
/// Bearer Capability IE
|
||||
bool mHaveBearerCapability;
|
||||
L3BearerCapability mBearerCapability;
|
||||
|
||||
/// Calling Party BCD Number (0x5C O TLV 3-19 ).
|
||||
bool mHaveCallingPartyBCDNumber;
|
||||
L3CallingPartyBCDNumber mCallingPartyBCDNumber;
|
||||
|
||||
/// Called Party BCD Number (0x5E O TLV 3-19).
|
||||
bool mHaveCalledPartyBCDNumber;
|
||||
L3CalledPartyBCDNumber mCalledPartyBCDNumber;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
L3Setup(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveBearerCapability(false),
|
||||
mHaveCallingPartyBCDNumber(false),
|
||||
mHaveCalledPartyBCDNumber(false)
|
||||
{ }
|
||||
|
||||
|
||||
L3Setup(unsigned wTI, const L3CalledPartyBCDNumber& wCalledPartyBCDNumber)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveBearerCapability(false),
|
||||
mHaveCallingPartyBCDNumber(false),
|
||||
mHaveCalledPartyBCDNumber(true),mCalledPartyBCDNumber(wCalledPartyBCDNumber)
|
||||
{ }
|
||||
|
||||
L3Setup(unsigned wTI, const L3CallingPartyBCDNumber& wCallingPartyBCDNumber)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveBearerCapability(false),
|
||||
mHaveCallingPartyBCDNumber(true),mCallingPartyBCDNumber(wCallingPartyBCDNumber),
|
||||
mHaveCalledPartyBCDNumber(false)
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
|
||||
/** Accessors */
|
||||
//@{
|
||||
bool haveCalledPartyBCDNumber() const { return mHaveCalledPartyBCDNumber; }
|
||||
|
||||
const L3CalledPartyBCDNumber& calledPartyBCDNumber() const
|
||||
{ assert(mHaveCalledPartyBCDNumber); return mCalledPartyBCDNumber; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return Setup; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
GSM 04.08 9.3.8
|
||||
*/
|
||||
class L3EmergencySetup : public L3CCMessage
|
||||
{
|
||||
|
||||
// We fill in IEs one at a time as we need them.
|
||||
|
||||
public:
|
||||
|
||||
L3EmergencySetup(unsigned wTI=7)
|
||||
:L3CCMessage(wTI)
|
||||
{ }
|
||||
|
||||
|
||||
int MTI() const { return EmergencySetup; }
|
||||
void parseBody( const L3Frame &src, size_t &rp ) {}
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.3 */
|
||||
class L3CallProceeding : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
// We'fill in IEs one at a time as we need them.
|
||||
|
||||
bool mHaveBearerCapability;
|
||||
L3BearerCapability mBearerCapability;
|
||||
|
||||
bool mHaveProgress;
|
||||
L3ProgressIndicator mProgress;
|
||||
|
||||
public:
|
||||
|
||||
L3CallProceeding(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveBearerCapability(false),
|
||||
mHaveProgress(false)
|
||||
{}
|
||||
|
||||
int MTI() const { return CallProceeding; }
|
||||
void writeBody( L3Frame & dest, size_t &wp ) const;
|
||||
void parseBody( const L3Frame &src, size_t &wp );
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
GSM 04.08 9.3.1.
|
||||
Even though uplink and downlink forms have different optional fields,
|
||||
we can use a single message for both sides.
|
||||
*/
|
||||
class L3Alerting : public L3CCMessage
|
||||
{
|
||||
private:
|
||||
|
||||
bool mHaveProgress;
|
||||
L3ProgressIndicator mProgress; ///< Progress appears in uplink only.
|
||||
|
||||
public:
|
||||
|
||||
L3Alerting(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveProgress(false)
|
||||
{}
|
||||
|
||||
L3Alerting(unsigned wTI,const L3ProgressIndicator& wProgress)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveProgress(true),mProgress(wProgress)
|
||||
{}
|
||||
|
||||
int MTI() const { return Alerting; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void parseBody(const L3Frame& src, size_t &rp);
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.5 */
|
||||
class L3Connect : public L3CCMessage
|
||||
{
|
||||
private:
|
||||
|
||||
bool mHaveProgress;
|
||||
L3ProgressIndicator mProgress; ///< Progress appears in uplink only.
|
||||
|
||||
public:
|
||||
|
||||
L3Connect(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveProgress(false)
|
||||
{}
|
||||
|
||||
L3Connect(unsigned wTI, const L3ProgressIndicator& wProgress)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveProgress(true),mProgress(wProgress)
|
||||
{}
|
||||
|
||||
int MTI() const { return Connect; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody(const L3Frame &src, size_t &wp);
|
||||
size_t l2BodyLength() const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.6 */
|
||||
class L3ConnectAcknowledge : public L3CCMessage
|
||||
{
|
||||
public:
|
||||
|
||||
L3ConnectAcknowledge(unsigned wTI=7)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
int MTI() const { return ConnectAcknowledge; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const {}
|
||||
void parseBody( const L3Frame &src, size_t &rp ) {}
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.7 */
|
||||
class L3Disconnect : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3Cause mCause;
|
||||
|
||||
public:
|
||||
|
||||
/** Initialize with default cause of 0x10 "normal call clearing". */
|
||||
L3Disconnect(unsigned wTI=7, const L3Cause& wCause = L3Cause(0x10))
|
||||
:L3CCMessage(wTI),
|
||||
mCause(wCause)
|
||||
{}
|
||||
|
||||
int MTI() const { return Disconnect; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
size_t l2BodyLength() const { return mCause.lengthLV(); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.2 */
|
||||
class L3CallConfirmed : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
bool mHaveCause;
|
||||
L3Cause mCause;
|
||||
|
||||
public:
|
||||
|
||||
L3CallConfirmed(unsigned wTI=7)
|
||||
:L3CCMessage(wTI),
|
||||
mHaveCause(false)
|
||||
{}
|
||||
|
||||
int MTI() const { return CallConfirmed; }
|
||||
void parseBody(const L3Frame &src, size_t &rp);
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.24 */
|
||||
class L3StartDTMF : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3KeypadFacility mKey;
|
||||
|
||||
public:
|
||||
|
||||
L3StartDTMF(unsigned wTI=7)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
const L3KeypadFacility& key() const { return mKey; }
|
||||
int MTI() const { return StartDTMF; }
|
||||
void parseBody(const L3Frame &src, size_t &rp) { mKey.parseTV(0x2c,src,rp); }
|
||||
size_t l2BodyLength() const { return mKey.lengthTV(); }
|
||||
|
||||
void text(std::ostream& os) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.25 */
|
||||
class L3StartDTMFAcknowledge : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3KeypadFacility mKey;
|
||||
|
||||
public:
|
||||
|
||||
L3StartDTMFAcknowledge(unsigned wTI, const L3KeypadFacility& wKey)
|
||||
:L3CCMessage(wTI),
|
||||
mKey(wKey)
|
||||
{}
|
||||
|
||||
int MTI() const { return StartDTMFAcknowledge; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const { mKey.writeTV(0x2C,dest,wp); }
|
||||
size_t l2BodyLength() const { return mKey.lengthTV(); }
|
||||
|
||||
void text(std::ostream& os) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.26 */
|
||||
class L3StartDTMFReject : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3Cause mCause;
|
||||
|
||||
public:
|
||||
|
||||
/** Reject with default cause 0x3f, "service or option not available". */
|
||||
L3StartDTMFReject(unsigned wTI, const L3Cause& wCause)
|
||||
:L3CCMessage(wTI),
|
||||
mCause(wCause)
|
||||
{}
|
||||
|
||||
int MTI() const { return StartDTMFReject; }
|
||||
void writeBody(L3Frame &src, size_t &rp) const { mCause.writeLV(src,rp); }
|
||||
size_t l2BodyLength() const { return mCause.lengthLV(); }
|
||||
|
||||
void text(std::ostream& os) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.29 */
|
||||
class L3StopDTMF : public L3CCMessage {
|
||||
|
||||
public:
|
||||
|
||||
L3StopDTMF(unsigned wTI=7)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
int MTI() const { return StopDTMF; }
|
||||
void parseBody(const L3Frame &src, size_t &rp) { }
|
||||
size_t l2BodyLength() const { return 0; };
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.30 */
|
||||
class L3StopDTMFAcknowledge : public L3CCMessage {
|
||||
|
||||
public:
|
||||
|
||||
L3StopDTMFAcknowledge(unsigned wTI)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
int MTI() const { return StopDTMFAcknowledge; }
|
||||
void writeBody(L3Frame&, size_t&) const { }
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.17 */
|
||||
class L3Progress : public L3CCMessage
|
||||
{
|
||||
L3ProgressIndicator mProgress;
|
||||
|
||||
public:
|
||||
|
||||
L3Progress(unsigned wTI, const L3ProgressIndicator& wProgress)
|
||||
:L3CCMessage(wTI),
|
||||
mProgress(wProgress)
|
||||
{}
|
||||
|
||||
L3Progress(unsigned wTI)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
int MTI() const { return Progress; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody(const L3Frame &src, size_t &wp);
|
||||
size_t l2BodyLength() const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.10 */
|
||||
class L3Hold : public L3CCMessage
|
||||
{
|
||||
public:
|
||||
|
||||
L3Hold(unsigned wTI=7)
|
||||
:L3CCMessage(wTI)
|
||||
{}
|
||||
|
||||
int MTI() const { return Hold; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const {}
|
||||
void parseBody( const L3Frame &src, size_t &rp ) {}
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.12 */
|
||||
class L3HoldReject : public L3CCMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3Cause mCause;
|
||||
|
||||
public:
|
||||
|
||||
/** Reject with default cause 0x3f, "service or option not available". */
|
||||
L3HoldReject(unsigned wTI, const L3Cause& wCause)
|
||||
:L3CCMessage(wTI),
|
||||
mCause(wCause)
|
||||
{}
|
||||
|
||||
int MTI() const { return HoldReject; }
|
||||
void writeBody(L3Frame &src, size_t &rp) const { mCause.writeLV(src,rp); }
|
||||
size_t l2BodyLength() const { return mCause.lengthLV(); }
|
||||
|
||||
void text(std::ostream& os) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.3.6 */
|
||||
|
||||
|
||||
}; // GSM
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
365
GSM/GSML3CommonElements.cpp
Normal file
365
GSM/GSML3CommonElements.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
/**@file
|
||||
@brief Common elements for L3 messages, GSM 04.08 10.5.1.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include "GSML3CommonElements.h"
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
L3LocationAreaIdentity::L3LocationAreaIdentity(const char*wMCC, const char* wMNC, unsigned wLAC)
|
||||
:L3ProtocolElement(),
|
||||
mLAC(wLAC)
|
||||
{
|
||||
// country code
|
||||
assert(strlen(wMCC)==3);
|
||||
mMCC[0] = wMCC[0]-'0';
|
||||
mMCC[1] = wMCC[1]-'0';
|
||||
mMCC[2] = wMCC[2]-'0';
|
||||
// network code
|
||||
assert(strlen(wMNC)<=3);
|
||||
assert(strlen(wMNC)>=2);
|
||||
mMNC[0] = wMNC[0]-'0';
|
||||
mMNC[1] = wMNC[1]-'0';
|
||||
if (wMNC[2]!='\0') mMNC[2]=wMNC[2]-'0';
|
||||
else mMNC[2]=0x0f;
|
||||
}
|
||||
|
||||
|
||||
void L3LocationAreaIdentity::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,mMCC[1],4);
|
||||
dest.writeField(wp,mMCC[0],4);
|
||||
dest.writeField(wp,mMNC[2],4);
|
||||
dest.writeField(wp,mMCC[2],4);
|
||||
dest.writeField(wp,mMNC[1],4);
|
||||
dest.writeField(wp,mMNC[0],4);
|
||||
dest.writeField(wp,mLAC,16);
|
||||
}
|
||||
|
||||
void L3LocationAreaIdentity::parseV( const L3Frame& src, size_t &rp )
|
||||
{
|
||||
mMCC[1] = src.readField(rp, 4);
|
||||
mMCC[0] = src.readField(rp, 4);
|
||||
mMNC[2] = src.readField(rp, 4);
|
||||
mMCC[2] = src.readField(rp, 4);
|
||||
mMNC[1] = src.readField(rp, 4);
|
||||
mMNC[0] = src.readField(rp, 4);
|
||||
mLAC = src.readField(rp, 16);
|
||||
}
|
||||
|
||||
|
||||
bool L3LocationAreaIdentity::operator==(const L3LocationAreaIdentity& other) const
|
||||
{
|
||||
// MCC
|
||||
if (mMCC[0]!=other.mMCC[0]) return false;
|
||||
if (mMCC[1]!=other.mMCC[1]) return false;
|
||||
if (mMCC[2]!=other.mMCC[2]) return false;
|
||||
// MNC
|
||||
if (mMNC[0]!=other.mMNC[0]) return false;
|
||||
if (mMNC[1]!=other.mMNC[1]) return false;
|
||||
if (mMNC[2]!=other.mMNC[2]) return false;
|
||||
// LAC
|
||||
if (mLAC!=other.mLAC) return false;
|
||||
// So everything matched.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void L3LocationAreaIdentity::text(ostream& os) const
|
||||
{
|
||||
os << "MCC=" << mMCC[0] << mMCC[1] << mMCC[2];
|
||||
os << " MNC=" << mMNC[0] << mMNC[1];
|
||||
if (mMNC[2]<15) os << mMNC[2];
|
||||
os << " LAC=0x" << hex << mLAC << dec;
|
||||
}
|
||||
|
||||
int L3LocationAreaIdentity::MNC() const
|
||||
{
|
||||
int val = mMNC[0]*10 + mMNC[1];
|
||||
if (mMNC[2]<15) val = val*10 + mMNC[2];
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CellIdentity::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,mID,16);
|
||||
}
|
||||
|
||||
|
||||
void L3CellIdentity::text(ostream& os) const
|
||||
{
|
||||
os << mID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool L3MobileIdentity::operator==(const L3MobileIdentity& other) const
|
||||
{
|
||||
if (other.mType!=mType) return false;
|
||||
if (mType==TMSIType) return (mTMSI==other.mTMSI);
|
||||
return (strcmp(mDigits,other.mDigits)==0);
|
||||
}
|
||||
|
||||
bool L3MobileIdentity::operator<(const L3MobileIdentity& other) const
|
||||
{
|
||||
if (other.mType != mType) return mType > other.mType;
|
||||
if (mType == TMSIType) return mTMSI > other.mTMSI;
|
||||
return strcmp(mDigits,other.mDigits)>0;
|
||||
}
|
||||
|
||||
|
||||
size_t L3MobileIdentity::lengthV() const
|
||||
{
|
||||
if (mType==NoIDType) return 1;
|
||||
if (mType==TMSIType) return 5;
|
||||
return 1 + strlen(mDigits)/2;
|
||||
}
|
||||
|
||||
|
||||
void L3MobileIdentity::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// See GSM 04.08 10.5.1.4.
|
||||
|
||||
if (mType==NoIDType) {
|
||||
dest.writeField(wp,0x0f0,8);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mType==TMSIType) {
|
||||
dest.writeField(wp,0x0f4,8);
|
||||
dest.writeField(wp,mTMSI,32);
|
||||
return;
|
||||
}
|
||||
|
||||
int numDigits = strlen(mDigits);
|
||||
assert(numDigits<=15);
|
||||
|
||||
// The first byte.
|
||||
dest.writeField(wp,mDigits[0]-'0',4);
|
||||
dest.writeField(wp,(numDigits%2),1);
|
||||
dest.writeField(wp,mType,3);
|
||||
|
||||
// The other bytes are more regular.
|
||||
int i=1;
|
||||
while (i<numDigits) {
|
||||
if ((i+1)<numDigits) dest.writeField(wp,mDigits[i+1]-'0',4);
|
||||
else dest.writeField(wp,0x0f,4);
|
||||
dest.writeField(wp,mDigits[i]-'0',4);
|
||||
i+=2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L3MobileIdentity::parseV( const L3Frame& src, size_t &rp, size_t expectedLength)
|
||||
{
|
||||
// See GSM 04.08 10.5.1.4.
|
||||
|
||||
size_t endCount = rp + expectedLength*8;
|
||||
|
||||
// Read first digit, a special case.
|
||||
int numDigits = 0;
|
||||
mDigits[numDigits++] = src.readField(rp,4)+'0';
|
||||
|
||||
// Get odd-count flag and identity type.
|
||||
bool oddCount = (bool) src.readField(rp,1);
|
||||
mType = (MobileIDType) src.readField(rp,3);
|
||||
|
||||
switch (mType) {
|
||||
case TMSIType:
|
||||
mDigits[0]='\0';
|
||||
// GSM 03.03 2.4 tells us the TMSI is always 32 bits
|
||||
mTMSI = src.readField(rp,32);
|
||||
break;
|
||||
case IMSIType:
|
||||
case IMEISVType:
|
||||
case IMEIType:
|
||||
while (rp<endCount) {
|
||||
unsigned tmp = src.readField(rp,4);
|
||||
mDigits[numDigits++] = src.readField(rp,4)+'0';
|
||||
mDigits[numDigits++] = tmp + '0';
|
||||
if (numDigits>15) L3_READ_ERROR;
|
||||
}
|
||||
if (!oddCount) numDigits--;
|
||||
mDigits[numDigits]='\0';
|
||||
break;
|
||||
default:
|
||||
LOG(NOTICE) << "non-standard identity type " << (int)mType;
|
||||
mDigits[0]='\0';
|
||||
mType = NoIDType;
|
||||
}
|
||||
}
|
||||
|
||||
void L3MobileIdentity::text(ostream& os) const
|
||||
{
|
||||
os << mType << "=";
|
||||
if (mType==TMSIType) {
|
||||
os << hex << "0x" << mTMSI << dec;
|
||||
return;
|
||||
}
|
||||
os << mDigits;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3MobileStationClassmark1::parseV(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
rp++; // spare
|
||||
mRevisionLevel = src.readField(rp,2);
|
||||
mES_IND = src.readField(rp,1);
|
||||
mA5_1 = src.readField(rp,1);
|
||||
mRFPowerCapability = src.readField(rp,3);
|
||||
}
|
||||
|
||||
void L3MobileStationClassmark1::text(ostream& os) const
|
||||
{
|
||||
os << "revision=" << mRevisionLevel;
|
||||
os << " ES-IND=" << mES_IND;
|
||||
os << " A5/1=" << mA5_1;
|
||||
os << " powerCap=" << mRFPowerCapability;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3MobileStationClassmark2::parseV(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
rp++; // spare
|
||||
mRevisionLevel = src.readField(rp,2);
|
||||
mES_IND = src.readField(rp,1);
|
||||
mA5_1 = src.readField(rp,1);
|
||||
mRFPowerCapability = src.readField(rp,3);
|
||||
rp++; // spare
|
||||
mPSCapability = src.readField(rp,1);
|
||||
mSSScreenIndicator = src.readField(rp,2);
|
||||
mSMCapability = src.readField(rp,1);
|
||||
mVBS = src.readField(rp,1);
|
||||
mVGCS = src.readField(rp,1);
|
||||
mFC = src.readField(rp,1);
|
||||
mCM3 = src.readField(rp,1);
|
||||
rp++; // spare
|
||||
mLCSVACapability = src.readField(rp,1);
|
||||
rp++; // spare
|
||||
mSoLSA = src.readField(rp,1);
|
||||
mCMSF = src.readField(rp,1);
|
||||
mA5_3 = src.readField(rp,1);
|
||||
mA5_2 = src.readField(rp,1);
|
||||
}
|
||||
|
||||
void L3MobileStationClassmark2::parseV(const L3Frame& src, size_t& rp, size_t len)
|
||||
{
|
||||
// This is sometimes send as LV, with extra bytes.
|
||||
// FIXME -- Learn to decode those extra bytes.
|
||||
size_t frp = rp;
|
||||
parseV(src,frp);
|
||||
rp += len*8;
|
||||
}
|
||||
|
||||
void L3MobileStationClassmark2::text(ostream& os) const
|
||||
{
|
||||
os << "revision=" << mRevisionLevel;
|
||||
os << " ES-IND=" << mES_IND;
|
||||
os << " A5/1=" << mA5_1;
|
||||
os << " A5/2=" << mA5_2;
|
||||
os << " A5/3=" << mA5_3;
|
||||
os << " powerCap=" << mRFPowerCapability;
|
||||
os << " PS=" << mPSCapability;
|
||||
os << " SSScrenInd=" << mSSScreenIndicator;
|
||||
os << " SM=" << mSMCapability;
|
||||
os << " VBS=" << mVBS;
|
||||
os << " VGCS=" << mVGCS;
|
||||
os << " FC=" << mFC;
|
||||
os << " CM3=" << mCM3;
|
||||
os << " LCSVA=" << mLCSVACapability;
|
||||
os << " SoLSA=" << mSoLSA;
|
||||
os << " CMSF=" << mCMSF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3MobileStationClassmark3::parseV(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
// FIXME -- This is incomplete.
|
||||
rp+=1; // skip spare bits
|
||||
unsigned mMultiband = src.readField(rp,3);
|
||||
switch (mMultiband) {
|
||||
case 0:
|
||||
case 5: case 6:
|
||||
case 1: case 2: case 4:
|
||||
mA5_7 = src.readField(rp,1);
|
||||
mA5_6 = src.readField(rp,1);
|
||||
mA5_5 = src.readField(rp,1);
|
||||
mA5_4 = src.readField(rp,1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void L3MobileStationClassmark3::parseV(const L3Frame& src, size_t& rp, size_t len)
|
||||
{
|
||||
// We do this because the parser is incomplete.
|
||||
size_t frp = rp;
|
||||
parseV(src,frp);
|
||||
rp += len*8;
|
||||
}
|
||||
|
||||
void L3MobileStationClassmark3::text(ostream& os) const
|
||||
{
|
||||
os << "multiband=" << mMultiband;
|
||||
os << " A5/4=" << mA5_4;
|
||||
os << " A5/5=" << mA5_4;
|
||||
os << " A5/6=" << mA5_4;
|
||||
os << " A5/7=" << mA5_4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CipheringKeySequenceNumber::writeV(L3Frame &dest, size_t& wp) const
|
||||
{
|
||||
dest.writeField(wp,mCIValue,4);
|
||||
}
|
||||
|
||||
void L3CipheringKeySequenceNumber::text(ostream& os) const
|
||||
{
|
||||
os << mCIValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
277
GSM/GSML3CommonElements.h
Normal file
277
GSM/GSML3CommonElements.h
Normal file
@@ -0,0 +1,277 @@
|
||||
/**@file
|
||||
@brief Common elements for L3 messages, GSM 04.08 10.5.1.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008-2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef GSMCOMMONELEMENTS_H
|
||||
#define GSMCOMMONELEMENTS_H
|
||||
|
||||
#include "GSML3Message.h"
|
||||
#include <Globals.h>
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
|
||||
/** Cell Identity, GSM 04.08 10.5.1.1 */
|
||||
class L3CellIdentity : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mID;
|
||||
|
||||
public:
|
||||
|
||||
L3CellIdentity(unsigned wID=gConfig.getNum("GSM.Identity.CI"))
|
||||
:mID(wID)
|
||||
{ }
|
||||
|
||||
size_t lengthV() const { return 2; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { abort(); }
|
||||
void parseV(const L3Frame&, size_t&, size_t) { abort(); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** LAI, GSM 04.08 10.5.1.3 */
|
||||
class L3LocationAreaIdentity : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mMCC[3]; ///< mobile country code digits
|
||||
unsigned mMNC[3]; ///< mobile network code digits
|
||||
unsigned mLAC; ///< location area code
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Initialize the LAI with real values, drawn from gConfig by default.
|
||||
@param wMCC MCC as a string (3 digits).
|
||||
@param wMNC MNC as a string (2 or 3 digits).
|
||||
@param wLAC LAC as a number.
|
||||
*/
|
||||
L3LocationAreaIdentity(
|
||||
const char*wMCC = gConfig.getStr("GSM.Identity.MCC").c_str(),
|
||||
const char* wMNC = gConfig.getStr("GSM.Identity.MNC").c_str(),
|
||||
unsigned wLAC = gConfig.getNum("GSM.Identity.LAC")
|
||||
);
|
||||
|
||||
/** Sometimes we need to compare these things. */
|
||||
bool operator==(const L3LocationAreaIdentity&) const;
|
||||
|
||||
size_t lengthV() const { return 5; }
|
||||
void parseV(const L3Frame& source, size_t &rp);
|
||||
void parseV(const L3Frame&, size_t&, size_t) { abort(); }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
|
||||
int MCC() const { return mMCC[0]*100 + mMCC[1]*10 + mMCC[2]; }
|
||||
int MNC() const;
|
||||
int LAC() const { return mLAC; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Mobile Identity, GSM 04.08, 10.5.1.4 */
|
||||
class L3MobileIdentity : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
|
||||
MobileIDType mType; ///< IMSI, TMSI, or IMEI?
|
||||
char mDigits[16]; ///< GSM 03.03 2.2 limits the IMSI or IMEI to 15 digits.
|
||||
uint32_t mTMSI; ///< GSM 03.03 2.4 specifies the TMSI as 32 bits
|
||||
|
||||
public:
|
||||
|
||||
/** Empty ID */
|
||||
L3MobileIdentity()
|
||||
:L3ProtocolElement(),
|
||||
mType(NoIDType)
|
||||
{ mDigits[0]='\0'; }
|
||||
|
||||
/** TMSI initializer. */
|
||||
L3MobileIdentity(unsigned int wTMSI)
|
||||
:L3ProtocolElement(),
|
||||
mType(TMSIType), mTMSI(wTMSI)
|
||||
{ mDigits[0]='\0'; }
|
||||
|
||||
/** IMSI initializer. */
|
||||
L3MobileIdentity(const char* wDigits)
|
||||
:L3ProtocolElement(),
|
||||
mType(IMSIType)
|
||||
{ assert(strlen(wDigits)<=15); strcpy(mDigits,wDigits); }
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
MobileIDType type() const { return mType; }
|
||||
const char* digits() const { assert(mType!=TMSIType); return mDigits; }
|
||||
unsigned int TMSI() const { assert(mType==TMSIType); return mTMSI; }
|
||||
//@}
|
||||
|
||||
/** Comparison. */
|
||||
bool operator==(const L3MobileIdentity&) const;
|
||||
|
||||
/** Comparison. */
|
||||
bool operator!=(const L3MobileIdentity& other) const { return !operator==(other); }
|
||||
|
||||
/** Comparison. */
|
||||
bool operator<(const L3MobileIdentity&) const;
|
||||
|
||||
size_t lengthV() const;
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV( const L3Frame& src, size_t &rp, size_t expectedLength );
|
||||
void parseV(const L3Frame&, size_t&) { abort(); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Mobile Station Classmark 1, GSM 04.08 10.5.1.5
|
||||
*/
|
||||
class L3MobileStationClassmark1 : public L3ProtocolElement {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mRevisionLevel;
|
||||
unsigned mES_IND;
|
||||
unsigned mA5_1;
|
||||
unsigned mRFPowerCapability;
|
||||
|
||||
public:
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame&, size_t&) const { assert(0); }
|
||||
void parseV(const L3Frame &src, size_t &rp);
|
||||
void parseV(const L3Frame&, size_t&, size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
Mobile Station Classmark 2, GSM 04.08 10.5.1.5
|
||||
*/
|
||||
class L3MobileStationClassmark2 : public L3ProtocolElement {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mRevisionLevel;
|
||||
unsigned mES_IND;
|
||||
unsigned mA5_1;
|
||||
unsigned mA5_3;
|
||||
unsigned mA5_2;
|
||||
unsigned mRFPowerCapability;
|
||||
unsigned mPSCapability;
|
||||
unsigned mSSScreenIndicator;
|
||||
unsigned mSMCapability;
|
||||
unsigned mVBS;
|
||||
unsigned mVGCS;
|
||||
unsigned mFC;
|
||||
unsigned mCM3;
|
||||
unsigned mLCSVACapability;
|
||||
unsigned mSoLSA;
|
||||
unsigned mCMSF;
|
||||
|
||||
public:
|
||||
|
||||
size_t lengthV() const { return 3; }
|
||||
void writeV(L3Frame&, size_t&) const { assert(0); }
|
||||
void parseV(const L3Frame &src, size_t &rp);
|
||||
void parseV(const L3Frame&, size_t&, size_t);
|
||||
void text(std::ostream&) const;
|
||||
|
||||
// These return true if the encryption type is supported.
|
||||
bool A5_1() const { return mA5_1==0; }
|
||||
bool A5_2() const { return mA5_2!=0; }
|
||||
bool A5_3() const { return mA5_3!=0; }
|
||||
|
||||
// Returns the power class, based on power capability encoding.
|
||||
int powerClass() const { return mRFPowerCapability+1; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Mobile Station Classmark 3, GSM 04.08 10.5.1.7
|
||||
FIXME -- We are only parsing the A5/x bits.
|
||||
*/
|
||||
class L3MobileStationClassmark3 : public L3ProtocolElement {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mMultiband;
|
||||
unsigned mA5_4;
|
||||
unsigned mA5_5;
|
||||
unsigned mA5_6;
|
||||
unsigned mA5_7;
|
||||
|
||||
public:
|
||||
|
||||
L3MobileStationClassmark3()
|
||||
:mA5_4(0),mA5_5(0),mA5_6(0),mA5_7(0)
|
||||
{ }
|
||||
|
||||
size_t lengthV() const { return 14; } // HACK -- Return maximum length.
|
||||
void writeV(L3Frame&, size_t&) const { assert(0); }
|
||||
void parseV(const L3Frame &, size_t &);
|
||||
void parseV(const L3Frame&, size_t&, size_t);
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class L3CipheringKeySequenceNumber : public L3ProtocolElement {
|
||||
|
||||
protected:
|
||||
|
||||
unsigned mCIValue;
|
||||
|
||||
public:
|
||||
|
||||
L3CipheringKeySequenceNumber(unsigned wCIValue):
|
||||
mCIValue(wCIValue)
|
||||
{ }
|
||||
|
||||
size_t lengthV() const { return 0; }
|
||||
void writeV(L3Frame&, size_t&) const;
|
||||
void parseV(const L3Frame &, size_t &) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t&, size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // GSM
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
251
GSM/GSML3MMElements.cpp
Normal file
251
GSM/GSML3MMElements.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/**@file
|
||||
@brief Elements for Mobility Management messages, GSM 04.08 9.2.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include <time.h>
|
||||
#include "GSML3MMElements.h"
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
void L3CMServiceType::parseV(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mType = (TypeCode)src.readField(rp,4);
|
||||
}
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L3CMServiceType::TypeCode code)
|
||||
{
|
||||
switch (code) {
|
||||
case L3CMServiceType::MobileOriginatedCall: os << "MOC"; break;
|
||||
case L3CMServiceType::EmergencyCall: os << "Emergency"; break;
|
||||
case L3CMServiceType::ShortMessage: os << "SMS"; break;
|
||||
case L3CMServiceType::SupplementaryService: os << "SS"; break;
|
||||
case L3CMServiceType::VoiceCallGroup: os << "VGCS"; break;
|
||||
case L3CMServiceType::VoiceBroadcast: os << "VBS"; break;
|
||||
case L3CMServiceType::LocationService: os << "LCS"; break;
|
||||
case L3CMServiceType::MobileTerminatedCall: os << "MTC"; break;
|
||||
case L3CMServiceType::MobileTerminatedShortMessage: os << "MTSMS"; break;
|
||||
case L3CMServiceType::TestCall: os << "Test"; break;
|
||||
default: os << "?" << (int)code << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
void L3CMServiceType::text(ostream& os) const
|
||||
{
|
||||
os << mType;
|
||||
}
|
||||
|
||||
void L3RejectCause::writeV( L3Frame& dest, size_t &wp ) const
|
||||
{
|
||||
dest.writeField(wp, mRejectCause, 8);
|
||||
}
|
||||
|
||||
|
||||
void L3RejectCause::text(ostream& os) const
|
||||
{
|
||||
os <<"0x"<< hex << mRejectCause << dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3NetworkName::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
unsigned sz = strlen(mName);
|
||||
// header byte
|
||||
if (mAlphabet == ALPHABET_UCS2) {
|
||||
// Ext: 1b, coding scheme: 001b (UCS2), CI, trailing spare bits: 000b (0)
|
||||
dest.writeField(wp,1,1); // ext bit
|
||||
dest.writeField(wp,1,3); // coding scheme USC2
|
||||
dest.writeField(wp,mCI,1); // show country name?
|
||||
dest.writeField(wp,0,3); // spare bits in last octet
|
||||
// the characters
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
dest.writeField(wp,mName[i],16);
|
||||
}
|
||||
} else {
|
||||
// Ext: 1b, coding scheme: 000b (GSM 03.38 coding scheme),
|
||||
int nameBits = sz*7;
|
||||
int nameBytes = nameBits/8;
|
||||
if (nameBits%8) nameBytes++;
|
||||
int msgBits = nameBytes*8;
|
||||
int spareBits = msgBits-nameBits;
|
||||
dest.writeField(wp,1,1); // ext bit
|
||||
dest.writeField(wp,0,3); // coding scheme USC2
|
||||
dest.writeField(wp,mCI,1); // show country name?
|
||||
dest.writeField(wp,spareBits,3); // spare bits in last octet
|
||||
// Temporary vector "chars" so we can do LSB8MSB() after encoding.
|
||||
BitVector chars(dest.segment(wp,msgBits));
|
||||
size_t twp = 0;
|
||||
// the characters: 7 bit, GSM 03.38 6.1.2.2, 6.2.1
|
||||
for (unsigned i=0; i<sz; i++) {
|
||||
chars.writeFieldReversed(twp,encodeGSMChar(mName[i]),7);
|
||||
}
|
||||
chars.writeField(twp,0,spareBits);
|
||||
chars.LSB8MSB();
|
||||
wp += twp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L3NetworkName::text(std::ostream& os) const
|
||||
{
|
||||
os << mName;
|
||||
}
|
||||
|
||||
|
||||
void L3TimeZoneAndTime::writeV(L3Frame& dest, size_t& wp) const
|
||||
{
|
||||
// See GSM 03.40 9.2.3.11.
|
||||
|
||||
// Convert from seconds since Jan 1, 1970 to calendar time.
|
||||
struct tm fields;
|
||||
const time_t seconds = mTime.sec();
|
||||
if (mType == LOCAL_TIME) {
|
||||
localtime_r(&seconds,&fields);
|
||||
} else {
|
||||
gmtime_r(&seconds,&fields);
|
||||
}
|
||||
// Write the fields in BCD format.
|
||||
// year
|
||||
unsigned year = fields.tm_year % 100;
|
||||
dest.writeField(wp, year % 10, 4);
|
||||
dest.writeField(wp, year / 10, 4);
|
||||
// month
|
||||
unsigned month = fields.tm_mon + 1;
|
||||
dest.writeField(wp, month % 10, 4);
|
||||
dest.writeField(wp, month / 10, 4);
|
||||
// day
|
||||
dest.writeField(wp, fields.tm_mday % 10, 4);
|
||||
dest.writeField(wp, fields.tm_mday / 10, 4);
|
||||
// hour
|
||||
dest.writeField(wp, fields.tm_hour % 10, 4);
|
||||
dest.writeField(wp, fields.tm_hour / 10, 4);
|
||||
// minute
|
||||
dest.writeField(wp, fields.tm_min % 10, 4);
|
||||
dest.writeField(wp, fields.tm_min / 10, 4);
|
||||
// second
|
||||
dest.writeField(wp, fields.tm_sec % 10, 4);
|
||||
dest.writeField(wp, fields.tm_sec / 10, 4);
|
||||
|
||||
// time zone, in 1/4 steps with a sign bit
|
||||
int zone;
|
||||
if (mType == LOCAL_TIME) {
|
||||
zone = fields.tm_gmtoff;
|
||||
} else {
|
||||
// At least under Linux gmtime_r() does not return timezone
|
||||
// information for some reason and we have to use localtime_r()
|
||||
// to reptrieve this information.
|
||||
struct tm fields_local;
|
||||
localtime_r(&seconds,&fields_local);
|
||||
zone = fields_local.tm_gmtoff;
|
||||
}
|
||||
zone = zone / (15*60);
|
||||
unsigned zoneSign = (zone < 0);
|
||||
zone = abs(zone);
|
||||
dest.writeField(wp, zone % 10, 4);
|
||||
dest.writeField(wp, zoneSign, 1);
|
||||
dest.writeField(wp, zone / 10, 3);
|
||||
|
||||
LOG(DEBUG) << "year=" << year << " month=" << month << " day=" << fields.tm_mday
|
||||
<< " hour=" << fields.tm_hour << " min=" << fields.tm_min << " sec=" << fields.tm_sec
|
||||
<< " zone=" << (zoneSign?"-":"+") << zone;
|
||||
}
|
||||
|
||||
|
||||
void L3TimeZoneAndTime::parseV(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
// See GSM 03.40 9.2.3.11.
|
||||
|
||||
// Read it all into a localtime struct tm,
|
||||
// then covert to Unix seconds.
|
||||
struct tm fields;
|
||||
// year
|
||||
fields.tm_year = 2000 + src.readField(rp,4) + src.readField(rp,4)*10;
|
||||
// month
|
||||
fields.tm_mon = 1 + src.readField(rp,4) + src.readField(rp,4)*10;
|
||||
// day
|
||||
fields.tm_mday = src.readField(rp,4) + src.readField(rp,4)*10;
|
||||
// hour
|
||||
fields.tm_hour = src.readField(rp,4) + src.readField(rp,4)*10;
|
||||
// minute
|
||||
fields.tm_min = src.readField(rp,4) + src.readField(rp,4)*10;
|
||||
// second
|
||||
fields.tm_sec = src.readField(rp,4) + src.readField(rp,4)*10;
|
||||
// zone
|
||||
unsigned zone = src.readField(rp,4);
|
||||
unsigned zoneSign = src.readField(rp,1);
|
||||
zone += 10*src.readField(rp,4);
|
||||
if (zoneSign) zone = -zone;
|
||||
fields.tm_gmtoff = zone * 15 * 60;
|
||||
// convert
|
||||
mTime = Timeval(timegm(&fields),0);
|
||||
}
|
||||
|
||||
void L3TimeZoneAndTime::text(ostream& os) const
|
||||
{
|
||||
char timeStr[26];
|
||||
const time_t seconds = mTime.sec();
|
||||
ctime_r(&seconds,timeStr);
|
||||
timeStr[24]='\0';
|
||||
os << timeStr << " (local)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3RAND::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,mRUpper,64);
|
||||
dest.writeField(wp,mRLower,64);
|
||||
}
|
||||
|
||||
void L3RAND::text(ostream& os) const
|
||||
{
|
||||
os << hex << "0x" << mRUpper << mRLower << dec;
|
||||
}
|
||||
|
||||
void L3SRES::parseV(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mValue = src.readField(rp,32);
|
||||
}
|
||||
|
||||
void L3SRES::text(ostream& os) const
|
||||
{
|
||||
os << hex << "0x" << mValue << dec;
|
||||
}
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
230
GSM/GSML3MMElements.h
Normal file
230
GSM/GSML3MMElements.h
Normal file
@@ -0,0 +1,230 @@
|
||||
/**@file @brief Elements for Mobility Management messages, GSM 04.08 9.2. */
|
||||
|
||||
/*
|
||||
* Copyright 2008-2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSML3MMELEMENTS_H
|
||||
#define GSML3MMELEMENTS_H
|
||||
|
||||
#include "GSML3Message.h"
|
||||
#include <Globals.h>
|
||||
|
||||
namespace GSM {
|
||||
|
||||
/** CM Service Type, GSM 04.08 10.5.3.3 */
|
||||
class L3CMServiceType : public L3ProtocolElement {
|
||||
|
||||
public:
|
||||
|
||||
enum TypeCode {
|
||||
UndefinedType=0,
|
||||
MobileOriginatedCall=1,
|
||||
EmergencyCall=2,
|
||||
ShortMessage=4, ///< specifically, MO-SMS
|
||||
SupplementaryService=8,
|
||||
VoiceCallGroup=9,
|
||||
VoiceBroadcast=10,
|
||||
LocationService=11,
|
||||
MobileTerminatedCall=100, ///< non-standard code
|
||||
MobileTerminatedShortMessage=101, ///< non-standard code
|
||||
TestCall=102, ///< non-standard code
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
TypeCode mType;
|
||||
|
||||
public:
|
||||
|
||||
L3CMServiceType(TypeCode wType=UndefinedType)
|
||||
:L3ProtocolElement(),mType(wType)
|
||||
{}
|
||||
|
||||
TypeCode type() const { return mType; }
|
||||
|
||||
bool operator==(const L3CMServiceType& other) const
|
||||
{ return mType == other.mType; }
|
||||
|
||||
size_t lengthV() const { return 0; }
|
||||
void writeV(L3Frame&, size_t&) const { assert(0); }
|
||||
void parseV(const L3Frame &src, size_t &rp);
|
||||
void parseV(const L3Frame&, size_t&, size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, L3CMServiceType::TypeCode code);
|
||||
|
||||
|
||||
/** RejectCause, GSM 04.08 10.5.3.6 */
|
||||
class L3RejectCause : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
int mRejectCause;
|
||||
|
||||
public:
|
||||
|
||||
L3RejectCause( const int wRejectCause=0 )
|
||||
:L3ProtocolElement(),mRejectCause(wRejectCause)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV( L3Frame& dest, size_t &wp ) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Network Name, GSM 04.08 10.5.3.5a
|
||||
This class supports UCS2 and 7-bit (default) encodings.
|
||||
*/
|
||||
class L3NetworkName : public L3ProtocolElement {
|
||||
|
||||
|
||||
private:
|
||||
|
||||
static const unsigned maxLen=93;
|
||||
GSMAlphabet mAlphabet; ///< Alphabet to use for encoding
|
||||
char mName[maxLen+1]; ///< network name as a C string
|
||||
int mCI; ///< CI (Country Initials) bit value
|
||||
|
||||
public:
|
||||
|
||||
/** Set the network name, taking the default from gConfig. */
|
||||
L3NetworkName(const char* wName,
|
||||
GSMAlphabet alphabet=ALPHABET_7BIT,
|
||||
int wCI=gConfig.defines("GSM.ShowCountry"))
|
||||
:L3ProtocolElement(), mAlphabet(alphabet), mCI(wCI)
|
||||
{ strncpy(mName,wName,maxLen); mName[maxLen] = '\0'; }
|
||||
|
||||
size_t lengthV() const
|
||||
{
|
||||
if (mAlphabet == ALPHABET_UCS2)
|
||||
return 1+strlen(mName)*2;
|
||||
else
|
||||
return 1+(strlen(mName)*7+7)/8;
|
||||
}
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
/**
|
||||
Time & Time Zone, GSM 04.08 10.5.3.9, GSM 03.40 9.2.3.11.
|
||||
This class is also used in SMS.
|
||||
*/
|
||||
class L3TimeZoneAndTime : public L3ProtocolElement {
|
||||
public:
|
||||
enum TimeType {
|
||||
LOCAL_TIME, ///< Used in SMS. Time is sent as local time. In this case
|
||||
///< timezone seems to be ignored by handsets (tested with
|
||||
///< Nokia DCT3, Siemens and Windows Mobile 6), but we still
|
||||
///< send it.
|
||||
UTC_TIME ///< Used in MM Info message. Time is sent as UTC time. In
|
||||
///< this case phones seem to regard timezone information.
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
Timeval mTime;
|
||||
TimeType mType;
|
||||
|
||||
public:
|
||||
|
||||
/** Defaults from the current time. */
|
||||
L3TimeZoneAndTime(const Timeval& wTime = Timeval(), TimeType type = LOCAL_TIME)
|
||||
:L3ProtocolElement(),
|
||||
mTime(wTime),
|
||||
mType(type)
|
||||
{}
|
||||
|
||||
const Timeval& time() const { return mTime; }
|
||||
void time(const Timeval& wTime) { mTime=wTime; }
|
||||
|
||||
TimeType type() const { return mType; }
|
||||
void type(TimeType type) { mType=type; }
|
||||
|
||||
size_t lengthV() const { return 7; }
|
||||
void writeV(L3Frame&, size_t&) const;
|
||||
void parseV(const L3Frame& src, size_t &rp);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.3.1 */
|
||||
class L3RAND : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
uint64_t mRUpper; ///< upper 64 bits
|
||||
uint64_t mRLower; ///< lower 64 bits
|
||||
|
||||
public:
|
||||
|
||||
L3RAND(uint64_t wRUpper, uint64_t wRLower):
|
||||
mRUpper(wRUpper),mRLower(wRLower)
|
||||
{ }
|
||||
|
||||
size_t lengthV() const { return 16; }
|
||||
void writeV(L3Frame&, size_t&) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.3.2 */
|
||||
class L3SRES : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
uint32_t mValue;
|
||||
|
||||
public:
|
||||
|
||||
L3SRES(uint32_t wValue):
|
||||
mValue(wValue)
|
||||
{ }
|
||||
|
||||
L3SRES():mValue(0) {}
|
||||
|
||||
uint32_t value() const { return mValue; }
|
||||
|
||||
size_t lengthV() const { return 4; }
|
||||
void writeV(L3Frame&, size_t&) const { assert(0); }
|
||||
void parseV(const L3Frame&, size_t&);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace GSM
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
347
GSM/GSML3MMMessages.cpp
Normal file
347
GSM/GSML3MMMessages.cpp
Normal file
@@ -0,0 +1,347 @@
|
||||
/**@file
|
||||
@brief GSM Mobility Management messages, from GSM 04.08 9.2.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008-2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GSML3CommonElements.h"
|
||||
#include "GSML3MMMessages.h"
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L3MMMessage::MessageType val)
|
||||
{
|
||||
switch (val) {
|
||||
case L3MMMessage::IMSIDetachIndication:
|
||||
os << "IMSI Detach Indication"; break;
|
||||
case L3MMMessage::CMServiceRequest:
|
||||
os << "CM Service Request"; break;
|
||||
case L3MMMessage::CMServiceAccept:
|
||||
os << "CM Service Accept"; break;
|
||||
case L3MMMessage::CMServiceReject:
|
||||
os << "CM Service Reject"; break;
|
||||
case L3MMMessage::CMServiceAbort:
|
||||
os << "CM Service Abort"; break;
|
||||
case L3MMMessage::CMReestablishmentRequest:
|
||||
os << "CM Re-establishment Request"; break;
|
||||
case L3MMMessage::IdentityResponse:
|
||||
os << "Identity Response"; break;
|
||||
case L3MMMessage::IdentityRequest:
|
||||
os << "Identity Request"; break;
|
||||
case L3MMMessage::MMInformation:
|
||||
os << "MM Information"; break;
|
||||
case L3MMMessage::LocationUpdatingAccept:
|
||||
os << "Location Updating Accept"; break;
|
||||
case L3MMMessage::LocationUpdatingReject:
|
||||
os << "Location Updating Reject"; break;
|
||||
case L3MMMessage::LocationUpdatingRequest:
|
||||
os << "Location Updating Request"; break;
|
||||
case L3MMMessage::MMStatus:
|
||||
os << "MM Status"; break;
|
||||
case L3MMMessage::AuthenticationRequest:
|
||||
os << "Authentication Request"; break;
|
||||
case L3MMMessage::AuthenticationResponse:
|
||||
os << "Authentication Response"; break;
|
||||
default: os << hex << "0x" << (int)val << dec;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
L3MMMessage* GSM::L3MMFactory(L3MMMessage::MessageType MTI)
|
||||
{
|
||||
switch (MTI) {
|
||||
case L3MMMessage::LocationUpdatingRequest: return new L3LocationUpdatingRequest;
|
||||
case L3MMMessage::IMSIDetachIndication: return new L3IMSIDetachIndication;
|
||||
case L3MMMessage::CMServiceRequest: return new L3CMServiceRequest;
|
||||
// Since we don't support re-establishment, don't bother parsing this.
|
||||
//case L3MMMessage::CMReestablishmentRequest: return new L3CMReestablishmentRequest;
|
||||
case L3MMMessage::MMStatus: return new L3MMStatus;
|
||||
case L3MMMessage::IdentityResponse: return new L3IdentityResponse;
|
||||
case L3MMMessage::AuthenticationResponse: return new L3AuthenticationResponse;
|
||||
default:
|
||||
LOG(WARNING) << "no L3 MM factory support for message " << MTI;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
L3MMMessage * GSM::parseL3MM(const L3Frame& source)
|
||||
{
|
||||
L3MMMessage::MessageType MTI = (L3MMMessage::MessageType)(0xbf & source.MTI());
|
||||
LOG(DEBUG) << "parseL3MM MTI=" << MTI;
|
||||
|
||||
L3MMMessage *retVal = L3MMFactory(MTI);
|
||||
if (retVal==NULL) return NULL;
|
||||
|
||||
retVal->parse(source);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3MMMessage::text(ostream& os) const
|
||||
{
|
||||
os << "MM " << (MessageType) MTI() << " ";
|
||||
}
|
||||
|
||||
|
||||
void L3LocationUpdatingRequest::parseBody( const L3Frame &src, size_t &rp )
|
||||
{
|
||||
// skip updating type
|
||||
rp += 4;
|
||||
// skip ciphering ket sequence number
|
||||
rp += 4;
|
||||
mLAI.parseV(src,rp);
|
||||
mClassmark.parseV(src,rp);
|
||||
mMobileIdentity.parseLV(src, rp);
|
||||
}
|
||||
|
||||
|
||||
void L3LocationUpdatingRequest::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "LAI=("<<mLAI<<")";
|
||||
os << " MobileIdentity=("<<mMobileIdentity<<")";
|
||||
os << " classmark=(" << mClassmark << ")";
|
||||
}
|
||||
|
||||
|
||||
size_t L3LocationUpdatingRequest::l2BodyLength() const
|
||||
{
|
||||
size_t len = 0;
|
||||
len += 1; // updating type and ciphering key seq num
|
||||
len += mLAI.lengthV();
|
||||
len += mClassmark.lengthV();
|
||||
len += mMobileIdentity.lengthLV();
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t L3LocationUpdatingAccept::l2BodyLength() const
|
||||
{
|
||||
if (mHaveMobileIdentity) return mLAI.lengthV() + mMobileIdentity.lengthTLV();
|
||||
else return mLAI.lengthV();
|
||||
}
|
||||
|
||||
void L3LocationUpdatingAccept::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
mLAI.writeV(dest, wp);
|
||||
if (mHaveMobileIdentity) mMobileIdentity.writeTLV(0x17,dest,wp);
|
||||
}
|
||||
|
||||
void L3LocationUpdatingAccept::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os<<"LAI=("<<mLAI<<")";
|
||||
if (mHaveMobileIdentity) os << "ID=(" << mMobileIdentity << ")";
|
||||
}
|
||||
|
||||
|
||||
void L3LocationUpdatingReject::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
mRejectCause.writeV(dest, wp);
|
||||
}
|
||||
|
||||
void L3LocationUpdatingReject::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os <<"cause="<<mRejectCause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3IMSIDetachIndication::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mClassmark.parseV(src, rp);
|
||||
mMobileIdentity.parseLV(src, rp);
|
||||
}
|
||||
|
||||
void L3IMSIDetachIndication::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "mobileID=(" << mMobileIdentity << ")";
|
||||
os << " classmark=(" << mClassmark << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CMServiceReject::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
mCause.writeV(dest,wp);
|
||||
}
|
||||
|
||||
void L3CMServiceReject::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "cause=" << mCause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CMServiceRequest::parseBody( const L3Frame &src, size_t &rp )
|
||||
{
|
||||
rp += 4; // skip ciphering key seq number
|
||||
mServiceType.parseV(src,rp);
|
||||
mClassmark.parseLV(src,rp);
|
||||
mMobileIdentity.parseLV(src, rp);
|
||||
// ignore priority
|
||||
}
|
||||
|
||||
void L3CMServiceRequest::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "serviceType=" << mServiceType;
|
||||
os << " mobileIdentity=("<<mMobileIdentity<<")";
|
||||
os << " classmark=(" << mClassmark << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CMReestablishmentRequest::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
rp += 8; // skip ciphering
|
||||
mClassmark.parseLV(src,rp);
|
||||
mMobileID.parseLV(src,rp);
|
||||
mHaveLAI = mLAI.parseTLV(0x13,src,rp);
|
||||
}
|
||||
|
||||
void L3CMReestablishmentRequest::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "mobileID=(" << mMobileID << ")";
|
||||
if (mHaveLAI) os << " LAI=(" << mLAI << ")";
|
||||
os << " classmark=(" << mClassmark << ")";
|
||||
}
|
||||
|
||||
|
||||
void L3MMStatus::parseBody( const L3Frame &src, size_t &rp)
|
||||
{
|
||||
mRejectCause.parseV(src, rp);
|
||||
}
|
||||
|
||||
void L3MMStatus::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << " RejectCause= <"<<mRejectCause<<">";
|
||||
}
|
||||
|
||||
|
||||
size_t L3MMInformation::l2BodyLength() const
|
||||
{
|
||||
size_t len=0;
|
||||
if (mShortName.lengthV()>1) len += mShortName.lengthTLV();
|
||||
len += mTime.lengthTV();
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void L3MMInformation::writeBody(L3Frame &dest, size_t &wp) const
|
||||
{
|
||||
if (mShortName.lengthV()>1) mShortName.writeTLV(0x45,dest,wp);
|
||||
mTime.writeTV(0x47,dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3MMInformation::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "short name=(" << mShortName << ")";
|
||||
os << " time=(" << mTime << ")";
|
||||
}
|
||||
|
||||
|
||||
void L3IdentityRequest::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,0,4); // spare half octet
|
||||
dest.writeField(wp,mType,4);
|
||||
}
|
||||
|
||||
|
||||
void L3IdentityRequest::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "type=" << mType;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3IdentityResponse::parseBody(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
mMobileID.parseLV(src,rp);
|
||||
}
|
||||
|
||||
void L3IdentityResponse::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "mobile id=" << mMobileID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3AuthenticationRequest::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,0,4); // spare half octet
|
||||
mCipheringKeySequenceNumber.writeV(dest,wp);
|
||||
mRAND.writeV(dest,wp);
|
||||
}
|
||||
|
||||
void L3AuthenticationRequest::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "cksn=" << mCipheringKeySequenceNumber;
|
||||
os << " RAND=" << mRAND;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3AuthenticationResponse::parseBody(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
mSRES.parseV(src,rp);
|
||||
}
|
||||
|
||||
void L3AuthenticationResponse::text(ostream& os) const
|
||||
{
|
||||
L3MMMessage::text(os);
|
||||
os << "SRES=" << mSRES;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
459
GSM/GSML3MMMessages.h
Normal file
459
GSM/GSML3MMMessages.h
Normal file
@@ -0,0 +1,459 @@
|
||||
/**@file @brief Mobility Management messages, GSM 04.08 9.2. */
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSML3MMMESSAGES_H
|
||||
#define GSML3MMMESSAGES_H
|
||||
|
||||
#include "GSMCommon.h"
|
||||
#include "GSML3Message.h"
|
||||
#include "GSML3CommonElements.h"
|
||||
#include "GSML3MMElements.h"
|
||||
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This a virtual class for L3 messages in the Mobility Management protocol.
|
||||
These messages are defined in GSM 04.08 9.2
|
||||
*/
|
||||
|
||||
class L3MMMessage : public L3Message {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Message type codes for mobility management messages in L3, GSM 04.08, Table 10.2.
|
||||
Note that bit 6 (MSB-less-1) is a "don't care" for these codes.
|
||||
*/
|
||||
enum MessageType {
|
||||
IMSIDetachIndication=0x01,
|
||||
CMServiceAccept=0x21,
|
||||
CMServiceReject=0x22,
|
||||
CMServiceAbort=0x23,
|
||||
CMServiceRequest=0x24,
|
||||
CMReestablishmentRequest=0x28,
|
||||
IdentityResponse=0x19,
|
||||
IdentityRequest=0x18,
|
||||
MMInformation=0x32,
|
||||
LocationUpdatingAccept=0x02,
|
||||
LocationUpdatingReject=0x04,
|
||||
LocationUpdatingRequest=0x08,
|
||||
TMSIReallocationCommand=0x1a,
|
||||
MMStatus=0x31,
|
||||
AuthenticationRequest=0x12,
|
||||
AuthenticationResponse=0x14,
|
||||
AuthenticationReject=0x11,
|
||||
Undefined=-1
|
||||
};
|
||||
|
||||
|
||||
L3MMMessage():L3Message() {}
|
||||
|
||||
size_t fullBodyLength() const { return l2BodyLength(); }
|
||||
|
||||
/** Return the L3 protocol discriptor. */
|
||||
L3PD PD() const { return L3MobilityManagementPD; }
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, L3MMMessage::MessageType val);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A Factory function to return a L3MMMessage of the specified MTI.
|
||||
Returns NULL if the MTI is not supported.
|
||||
*/
|
||||
L3MMMessage* L3MMFactory(L3MMMessage::MessageType MTI);
|
||||
|
||||
/**
|
||||
Parse a complete L3 mobility management message into its object type.
|
||||
@param source The L3 bits.
|
||||
@return A pointer to a new message or NULL on failure.
|
||||
*/
|
||||
L3MMMessage* parseL3MM(const L3Frame& source);
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.15 */
|
||||
class L3LocationUpdatingRequest : public L3MMMessage
|
||||
{
|
||||
L3MobileStationClassmark1 mClassmark;
|
||||
L3MobileIdentity mMobileIdentity; // (LV) 1+len
|
||||
L3LocationAreaIdentity mLAI;
|
||||
|
||||
public:
|
||||
L3LocationUpdatingRequest():L3MMMessage() {}
|
||||
|
||||
const L3MobileIdentity& mobileID() const
|
||||
{ return mMobileIdentity; }
|
||||
const L3LocationAreaIdentity& LAI() const
|
||||
{ return mLAI; }
|
||||
|
||||
int MTI() const { return (int)LocationUpdatingRequest; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.13 */
|
||||
class L3LocationUpdatingAccept : public L3MMMessage
|
||||
{
|
||||
// LAI = (V) length of 6
|
||||
L3LocationAreaIdentity mLAI;
|
||||
bool mHaveMobileIdentity;
|
||||
L3MobileIdentity mMobileIdentity;
|
||||
|
||||
public:
|
||||
|
||||
L3LocationUpdatingAccept(const L3LocationAreaIdentity& wLAI)
|
||||
:L3MMMessage(),mLAI(wLAI),
|
||||
mHaveMobileIdentity(false)
|
||||
{}
|
||||
|
||||
L3LocationUpdatingAccept(
|
||||
const L3LocationAreaIdentity& wLAI,
|
||||
const L3MobileIdentity& wMobileIdentity)
|
||||
:L3MMMessage(),mLAI(wLAI),
|
||||
mHaveMobileIdentity(true),
|
||||
mMobileIdentity(wMobileIdentity)
|
||||
{}
|
||||
|
||||
int MTI() const { return (int)LocationUpdatingAccept; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void writeBody( L3Frame &src, size_t &rp ) const;
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class L3MMStatus : public L3MMMessage
|
||||
{
|
||||
L3RejectCause mRejectCause;
|
||||
|
||||
public:
|
||||
L3MMStatus() : L3MMMessage(){}
|
||||
|
||||
int MTI() const { return (int) MMStatus; }
|
||||
|
||||
size_t l2BodyLength() const { return 3; }
|
||||
void parseBody( const L3Frame& src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.14 */
|
||||
class L3LocationUpdatingReject : public L3MMMessage
|
||||
{
|
||||
L3RejectCause mRejectCause;
|
||||
|
||||
public:
|
||||
|
||||
L3LocationUpdatingReject(const L3RejectCause& cause)
|
||||
:L3MMMessage(),mRejectCause(cause)
|
||||
{}
|
||||
|
||||
int MTI() const { return (int)LocationUpdatingReject; }
|
||||
|
||||
size_t l2BodyLength() const { return mRejectCause.lengthV(); }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.12 */
|
||||
class L3IMSIDetachIndication : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3MobileStationClassmark1 mClassmark;
|
||||
L3MobileIdentity mMobileIdentity;
|
||||
|
||||
public:
|
||||
|
||||
const L3MobileIdentity& mobileID() const
|
||||
{ return mMobileIdentity; }
|
||||
|
||||
int MTI() const { return (int)IMSIDetachIndication; }
|
||||
|
||||
size_t l2BodyLength() const { return 1 + mMobileIdentity.lengthLV(); }
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.5 */
|
||||
class L3CMServiceAccept : public L3MMMessage {
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return (int)CMServiceAccept; }
|
||||
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.7 */
|
||||
class L3CMServiceAbort : public L3MMMessage {
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return (int)CMServiceAbort; }
|
||||
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const {}
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.6 */
|
||||
class L3CMServiceReject : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3RejectCause mCause;
|
||||
|
||||
public:
|
||||
|
||||
L3CMServiceReject(const L3RejectCause& wCause)
|
||||
:L3MMMessage(),
|
||||
mCause(wCause)
|
||||
{}
|
||||
|
||||
int MTI() const { return (int)CMServiceReject; }
|
||||
|
||||
size_t l2BodyLength() const { return mCause.lengthV(); }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.9 */
|
||||
class L3CMServiceRequest : public L3MMMessage
|
||||
{
|
||||
L3MobileStationClassmark2 mClassmark;
|
||||
L3MobileIdentity mMobileIdentity;
|
||||
L3CMServiceType mServiceType;
|
||||
|
||||
public:
|
||||
|
||||
L3CMServiceRequest()
|
||||
:L3MMMessage(),
|
||||
mServiceType()
|
||||
{ }
|
||||
|
||||
/** Accessors */
|
||||
//@{
|
||||
const L3CMServiceType& serviceType() const { return mServiceType; }
|
||||
|
||||
const L3MobileIdentity& mobileID() const
|
||||
{ return mMobileIdentity; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return (int)CMServiceRequest; }
|
||||
|
||||
// (1/2) + (1/2) + 4 +
|
||||
size_t l2BodyLength() const { return 5+mMobileIdentity.lengthLV(); }
|
||||
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.4 */
|
||||
class L3CMReestablishmentRequest : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3MobileStationClassmark2 mClassmark;
|
||||
L3MobileIdentity mMobileID;
|
||||
bool mHaveLAI;
|
||||
L3LocationAreaIdentity mLAI;
|
||||
|
||||
public:
|
||||
|
||||
L3CMReestablishmentRequest()
|
||||
:L3MMMessage(),
|
||||
mHaveLAI(false)
|
||||
{}
|
||||
|
||||
const L3MobileIdentity& mobileID() const { return mMobileID; }
|
||||
|
||||
int MTI() const { return (int)CMReestablishmentRequest; }
|
||||
|
||||
size_t l2BodyLength() const { return 1 + 4 + mMobileID.lengthLV(); }
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.15a */
|
||||
class L3MMInformation : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3NetworkName mShortName;
|
||||
L3TimeZoneAndTime mTime;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Constructor.
|
||||
@param wShortName Abbreviated network name.
|
||||
*/
|
||||
L3MMInformation(const L3NetworkName& wShortName, const L3TimeZoneAndTime& wTime=L3TimeZoneAndTime())
|
||||
:L3MMMessage(),
|
||||
mShortName(wShortName), mTime(wTime)
|
||||
{
|
||||
mTime.type(L3TimeZoneAndTime::UTC_TIME);
|
||||
}
|
||||
|
||||
int MTI() const { return (int)MMInformation; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void writeBody(L3Frame&,size_t&) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** Identity Request, GSM 04.08 9.2.10 */
|
||||
class L3IdentityRequest : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
MobileIDType mType;
|
||||
|
||||
public:
|
||||
|
||||
L3IdentityRequest(MobileIDType wType)
|
||||
:L3MMMessage(),mType(wType)
|
||||
{}
|
||||
|
||||
int MTI() const { return IdentityRequest; }
|
||||
|
||||
size_t l2BodyLength() const { return 1; }
|
||||
void writeBody(L3Frame& dest, size_t& wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Identity Response, GSM 04.08 9.2.11 */
|
||||
class L3IdentityResponse : public L3MMMessage {
|
||||
|
||||
private :
|
||||
|
||||
L3MobileIdentity mMobileID;
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return IdentityResponse; }
|
||||
|
||||
size_t l2BodyLength() const { return mMobileID.lengthLV(); }
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
const L3MobileIdentity& mobileID() const { return mMobileID; }
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.2 */
|
||||
class L3AuthenticationRequest : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3CipheringKeySequenceNumber mCipheringKeySequenceNumber;
|
||||
L3RAND mRAND;
|
||||
|
||||
public:
|
||||
|
||||
L3AuthenticationRequest(
|
||||
const L3CipheringKeySequenceNumber &wCipheringKeySequenceNumber,
|
||||
const L3RAND &wRAND
|
||||
):
|
||||
mCipheringKeySequenceNumber(wCipheringKeySequenceNumber),
|
||||
mRAND(wRAND)
|
||||
{ }
|
||||
|
||||
int MTI() const { return AuthenticationRequest; }
|
||||
|
||||
size_t l2BodyLength() const { return 1 + mRAND.lengthV(); }
|
||||
void writeBody(L3Frame&, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
/** GSM 04.08 9.2.3 */
|
||||
class L3AuthenticationResponse : public L3MMMessage {
|
||||
|
||||
private:
|
||||
|
||||
L3SRES mSRES;
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return AuthenticationResponse; }
|
||||
|
||||
const L3SRES& SRES() const { return mSRES; }
|
||||
|
||||
size_t l2BodyLength() const { return mSRES.lengthV(); }
|
||||
void parseBody(const L3Frame&, size_t &rp);
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.2.1 */
|
||||
class L3AuthenticationReject : public L3MMMessage {
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return AuthenticationReject; }
|
||||
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
void writeBody(L3Frame&, size_t &wp) const { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
}; // namespace GSM
|
||||
|
||||
#endif //#ifndef GSML3MM_H
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
264
GSM/GSML3Message.cpp
Normal file
264
GSM/GSML3Message.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "GSML3Message.h"
|
||||
#include "GSML3RRMessages.h"
|
||||
#include "GSML3MMMessages.h"
|
||||
#include "GSML3CCMessages.h"
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
//#include <SMSTransfer.h>
|
||||
#include <SMSMessages.h>
|
||||
//using namespace SMS;
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
// FIXME -- We actually should not be using this anymore.
|
||||
void L3Message::parse(const L3Frame& source)
|
||||
{
|
||||
size_t rp = 16;
|
||||
parseBody(source,rp);
|
||||
}
|
||||
|
||||
|
||||
void L3Message::write(L3Frame& dest) const
|
||||
{
|
||||
size_t l3len = bitsNeeded();
|
||||
if (dest.size()!=l3len) dest.resize(l3len);
|
||||
size_t wp = 0;
|
||||
// write the standard L3 header
|
||||
dest.writeField(wp,0,4);
|
||||
dest.writeField(wp,PD(),4);
|
||||
dest.writeField(wp,MTI(),8);
|
||||
// write the body
|
||||
writeBody(dest,wp);
|
||||
// set the L2 length or pseudolength
|
||||
dest.L2Length(L2Length());
|
||||
}
|
||||
|
||||
|
||||
L3Frame* L3Message::frame( Primitive prim ) const
|
||||
{
|
||||
L3Frame *newFrame = new L3Frame(prim, bitsNeeded());
|
||||
write(*newFrame);
|
||||
return newFrame;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3Message::text(ostream& os) const
|
||||
{
|
||||
os << "PD=" << PD();
|
||||
os << " MTI=" << MTI();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
size_t GSM::skipLV(const L3Frame& source, size_t& rp)
|
||||
{
|
||||
if (rp==source.size()) return 0;
|
||||
size_t base = rp;
|
||||
size_t length = 8 * source.readField(rp,8);
|
||||
rp += length;
|
||||
return rp-base;
|
||||
}
|
||||
|
||||
|
||||
size_t GSM::skipTLV(unsigned IEI, const L3Frame& source, size_t& rp)
|
||||
{
|
||||
if (rp==source.size()) return 0;
|
||||
size_t base = rp;
|
||||
unsigned thisIEI = source.peekField(rp,8);
|
||||
if (thisIEI != IEI) return 0;
|
||||
rp += 8;
|
||||
size_t length = 8 * source.readField(rp,8);
|
||||
rp += length;
|
||||
return rp-base;
|
||||
}
|
||||
|
||||
|
||||
size_t GSM::skipTV(unsigned IEI, size_t numBits, const L3Frame& source, size_t& rp)
|
||||
{
|
||||
if (rp==source.size()) return 0;
|
||||
size_t base = rp;
|
||||
size_t IEISize;
|
||||
if (numBits>4) IEISize=8;
|
||||
else IEISize=4;
|
||||
unsigned thisIEI = source.peekField(rp,IEISize);
|
||||
if (thisIEI != IEI) return 0;
|
||||
rp += IEISize;
|
||||
rp += numBits;
|
||||
return rp-base;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, const L3Message& msg)
|
||||
{
|
||||
msg.text(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
GSM::L3Message* GSM::parseL3(const GSM::L3Frame& source)
|
||||
{
|
||||
if (source.size()==0) return NULL;
|
||||
|
||||
LOG(DEBUG) << "GSM::parseL3 "<< source;
|
||||
L3PD PD = source.PD();
|
||||
|
||||
L3Message *retVal = NULL;
|
||||
try {
|
||||
switch (PD) {
|
||||
case L3RadioResourcePD: retVal=parseL3RR(source); break;
|
||||
case L3MobilityManagementPD: retVal=parseL3MM(source); break;
|
||||
case L3CallControlPD: retVal=parseL3CC(source); break;
|
||||
case L3SMSPD: retVal=SMS::parseSMS(source); break;
|
||||
default:
|
||||
LOG(NOTICE) << "L3 parsing failed for unsupported protocol " << PD;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
catch (L3ReadError) {
|
||||
LOG(NOTICE) << "L3 parsing failed for " << source;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (retVal) LOG(INFO) << "L3 recv " << *retVal;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3ProtocolElement::parseLV(const L3Frame& source, size_t &rp)
|
||||
{
|
||||
size_t expectedLength = source.readField(rp,8);
|
||||
if (expectedLength==0) return;
|
||||
size_t rpEnd = rp + 8*expectedLength;
|
||||
parseV(source, rp, expectedLength);
|
||||
if (rpEnd != rp) {
|
||||
LOG(NOTICE) << "LV element does not match expected length";
|
||||
L3_READ_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool L3ProtocolElement::parseTV(unsigned IEI, const L3Frame& source, size_t &rp)
|
||||
{
|
||||
if (rp==source.size()) return false;
|
||||
if (lengthV()==0) {
|
||||
unsigned thisIEI = source.peekField(rp,4);
|
||||
if (thisIEI!=IEI) return false;
|
||||
rp += 4;
|
||||
parseV(source,rp);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned thisIEI = source.peekField(rp,8);
|
||||
if (thisIEI!=IEI) return false;
|
||||
rp += 8;
|
||||
parseV(source,rp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool L3ProtocolElement::parseTLV(unsigned IEI, const L3Frame& source, size_t &rp)
|
||||
{
|
||||
if (rp==source.size()) return false;
|
||||
unsigned thisIEI = source.peekField(rp,8);
|
||||
if (thisIEI!=IEI) return false;
|
||||
rp += 8;
|
||||
parseLV(source,rp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3ProtocolElement::writeLV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
unsigned len = lengthV();
|
||||
dest.writeField(wp, len, 8);
|
||||
if (len) writeV(dest, wp);
|
||||
}
|
||||
|
||||
void L3ProtocolElement::writeTLV(unsigned IEI, L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,IEI,8);
|
||||
writeLV(dest,wp);
|
||||
}
|
||||
|
||||
void L3ProtocolElement::writeTV(unsigned IEI, L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
if (lengthV()==0) {
|
||||
dest.writeField(wp,IEI,4);
|
||||
writeV(dest,wp);
|
||||
return;
|
||||
}
|
||||
dest.writeField(wp,IEI,8);
|
||||
writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3ProtocolElement::skipExtendedOctets( const L3Frame& source, size_t &rp )
|
||||
{
|
||||
if (rp==source.size()) return;
|
||||
int endbit = 0;
|
||||
while(!endbit){
|
||||
endbit = source.readField(rp, 1);
|
||||
rp += 7;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, const L3ProtocolElement& elem)
|
||||
{
|
||||
elem.text(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
302
GSM/GSML3Message.h
Normal file
302
GSM/GSML3Message.h
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSML3MESSAGE_H
|
||||
#define GSML3MESSAGE_H
|
||||
|
||||
#include "GSMCommon.h"
|
||||
#include "GSMTransfer.h"
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
/**@name L3 Processing Errors */
|
||||
//@{
|
||||
|
||||
class L3ReadError : public GSMError {
|
||||
public:
|
||||
L3ReadError():GSMError() {}
|
||||
};
|
||||
#define L3_READ_ERROR {throw L3ReadError();}
|
||||
|
||||
class L3WriteError : public GSMError {
|
||||
public:
|
||||
L3WriteError():GSMError() {}
|
||||
};
|
||||
#define L3_WRITE_ERROR {throw L3WriteError();}
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This is virtual base class for the messages of GSM's L3 signalling layer.
|
||||
It defines almost nothing, but is the origination of other classes.
|
||||
*/
|
||||
class L3Message {
|
||||
|
||||
public:
|
||||
|
||||
virtual ~L3Message() {}
|
||||
|
||||
/** Return the expected message body length in bytes, not including L3 header or rest octets. */
|
||||
virtual size_t l2BodyLength() const = 0;
|
||||
|
||||
/**
|
||||
Body length not including header but including rest octets.
|
||||
In subclasses with no rest octets, this returns l2BodyLength.
|
||||
*/
|
||||
virtual size_t fullBodyLength() const =0;
|
||||
|
||||
/** Return the expected message length in bytes, including L3 header, but not including rest octets. */
|
||||
size_t L2Length() const { return l2BodyLength()+2; }
|
||||
|
||||
/** Length including header and rest octets. */
|
||||
size_t FullLength() const { return fullBodyLength()+2; }
|
||||
|
||||
/** Return number of BITS needed to hold message and header. */
|
||||
size_t bitsNeeded() const { return 8*FullLength(); }
|
||||
|
||||
/**
|
||||
The parse() method reads and decodes L3 message bits.
|
||||
This method invokes parseBody, assuming that the L3 header
|
||||
has already been read.
|
||||
*/
|
||||
virtual void parse(const L3Frame& source);
|
||||
|
||||
/**
|
||||
Write message PD, MTI and data bits into a BitVector buffer.
|
||||
This method invokes writeBody.
|
||||
This method is overridden in the CC protocol.
|
||||
*/
|
||||
virtual void write(L3Frame& dest) const;
|
||||
|
||||
/**
|
||||
Generate an L3Frame for this message.
|
||||
The caller is responsible for deleting the memory.
|
||||
*/
|
||||
L3Frame* frame(GSM::Primitive prim=DATA) const;
|
||||
|
||||
/** Return the L3 protocol discriptor. */
|
||||
virtual GSM::L3PD PD() const =0;
|
||||
|
||||
/** Return the messag type indicator (MTI). */
|
||||
virtual int MTI() const =0;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
Write the L3 message body, a method defined in some subclasses.
|
||||
If not defined, this will assert at runtime.
|
||||
*/
|
||||
virtual void writeBody(L3Frame& dest, size_t &writePosition) const;
|
||||
|
||||
/**
|
||||
The parseBody() method starts processing at the first byte following the
|
||||
message type octet in the L3 message, which the caller indicates with the
|
||||
readPosition argument.
|
||||
If not defined, this will assert at runtime.
|
||||
*/
|
||||
virtual void parseBody(const L3Frame& source, size_t &readPosition);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/** Generate a human-readable representation of a message. */
|
||||
virtual void text(std::ostream& os) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**@name Utility functions for message parsers. */
|
||||
//@{
|
||||
/**
|
||||
Skip an unused LV element while parsing.
|
||||
@return number of bits skipped.
|
||||
*/
|
||||
size_t skipLV(const L3Frame& source, size_t &readPosition);
|
||||
|
||||
/**
|
||||
Skip an unused TLV element while parsing.
|
||||
@return number of bits skipped.
|
||||
*/
|
||||
size_t skipTLV(unsigned IEI, const L3Frame& source, size_t &readPosition);
|
||||
|
||||
/**
|
||||
Skip an unused TV element while parsing.
|
||||
@return number of bits skipped.
|
||||
*/
|
||||
size_t skipTV(unsigned IEI, size_t numBits, const L3Frame& source, size_t &readPosition);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Parse a complete L3 message into its object type.
|
||||
Caller is responsible for deleting allocated memory.
|
||||
@param source The L3 bits.
|
||||
@return A pointer to a new message or NULL on failure.
|
||||
*/
|
||||
L3Message* parseL3(const L3Frame& source);
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const GSM::L3Message& msg);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Abstract class used for GSM L3 information elements.
|
||||
See GSM 04.07 11.2.1.1.4 for a description of TLV element formatting.
|
||||
To quote the spec, four categories of standard information elements are defined:
|
||||
- information elements of format V or TV with value part consisting of 1/2 octet (type 1);
|
||||
- information elements of format T with value part consisting of 0 octets (type 2);
|
||||
- information elements of format V or TV with value part that has fixed length of at least one octet (type 3);
|
||||
- information elements of format TLV or LV with value part consisting of zero, one or more octets (type 4);
|
||||
|
||||
*/
|
||||
class L3ProtocolElement {
|
||||
|
||||
public:
|
||||
|
||||
virtual ~L3ProtocolElement() {}
|
||||
|
||||
|
||||
/**
|
||||
Return the length of the value part of the element in bytes.
|
||||
This is the core length method, referenced by all other length methods.
|
||||
Return zero for 1/2 octet fields (type 1 elements).
|
||||
*/
|
||||
virtual size_t lengthV() const =0;
|
||||
|
||||
size_t lengthTV() const { return lengthV() + 1; }
|
||||
|
||||
size_t lengthLV() const { return lengthV() + 1; }
|
||||
|
||||
size_t lengthTLV() const { return lengthLV() + 1; }
|
||||
|
||||
|
||||
/**
|
||||
The parseV method decodes L3 message bits from fixed-length value parts.
|
||||
This is the core parse method for fixed-length parsable elements and
|
||||
all other parse methods use it.
|
||||
@param src The L3Frame to be parsed.
|
||||
@param rp Bit index of read position (updated by read).
|
||||
*/
|
||||
virtual void parseV(const L3Frame& src, size_t &rp ) =0;
|
||||
|
||||
/**
|
||||
The parseV method decodes L3 message bits from variable-length value parts.
|
||||
This is the core parse method for variable-length parsable elements and
|
||||
all other parse methods use it.
|
||||
@param src The L3Frame to be parsed.
|
||||
@param rp Bit index of read position (updated by read).
|
||||
@param expectedLength Length of available field, in bytes.
|
||||
*/
|
||||
virtual void parseV(const L3Frame& src, size_t &rp, size_t expectedLength) =0;
|
||||
|
||||
|
||||
/**
|
||||
Parse LV format.
|
||||
@param src The L3Frame to be parsed.
|
||||
@param rp Bit index of read position (updated by read).
|
||||
*/
|
||||
void parseLV(const L3Frame& src, size_t &rp);
|
||||
|
||||
/**
|
||||
Parse TV format.
|
||||
@param IEI The expected T part value.
|
||||
@param src The L3Frame to be parsed.
|
||||
@param rp Bit index of read position (updated by read).
|
||||
@return true if the IEI matched and the element was actually read.
|
||||
*/
|
||||
bool parseTV(unsigned IEI, const L3Frame& src, size_t &rp);
|
||||
|
||||
/**
|
||||
Parse TLV format.
|
||||
@param IEI The expected T part value.
|
||||
@param src The L3Frame to be parsed.
|
||||
@param rp read index (updated by read).
|
||||
@return true if the IEI matched and the element was actually read.
|
||||
*/
|
||||
bool parseTLV(unsigned IEI, const L3Frame& src, size_t &rp);
|
||||
|
||||
/**
|
||||
Write the V format.
|
||||
This is the core write method for writable elements and
|
||||
all other write methods use it.
|
||||
@param dest The target L3Frame.
|
||||
@param wp The write index (updated by write).
|
||||
*/
|
||||
virtual void writeV(L3Frame& dest, size_t &wp) const =0;
|
||||
|
||||
/**
|
||||
Write LV format.
|
||||
@param dest The target L3Frame.
|
||||
@param wp The write index (updated by write).
|
||||
*/
|
||||
void writeLV(L3Frame& dest, size_t &wp) const;
|
||||
|
||||
/**
|
||||
Write TV format.
|
||||
@param IEI The "information element identifier", ie, the T part.
|
||||
@param dest The target buffer.
|
||||
@param wp The buffer write pointer (updated by write).
|
||||
*/
|
||||
void writeTV(unsigned IEI, L3Frame& dest, size_t &wp) const;
|
||||
|
||||
/**
|
||||
Write TLV format.
|
||||
@param IEI The "information element identifier", the T part.
|
||||
@param dest The target L3Frame.
|
||||
@param wp The write index (updated by write).
|
||||
*/
|
||||
void writeTLV(unsigned IEI, L3Frame& dest, size_t &wp) const;
|
||||
|
||||
/** Generate a human-readable form of the element. */
|
||||
virtual void text(std::ostream& os) const
|
||||
{ os << "(no text())"; }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
Skip over all unsupported extended octets in elements that use extension bits.
|
||||
@param src The L3Frame to skip along.
|
||||
@param rp The read pointer.
|
||||
*/
|
||||
void skipExtendedOctets( const L3Frame& src, size_t &rp );
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const L3ProtocolElement& elem);
|
||||
|
||||
|
||||
}; // GSM
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
715
GSM/GSML3RRElements.cpp
Normal file
715
GSM/GSML3RRElements.cpp
Normal file
@@ -0,0 +1,715 @@
|
||||
/**@file
|
||||
@brief Radio Resource messages, GSM 04.08 9.1.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <iterator> // for L3APDUData::text
|
||||
|
||||
#include "GSML3RRElements.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CellOptionsBCCH::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,0,1);
|
||||
dest.writeField(wp,mPWRC,1);
|
||||
dest.writeField(wp,mDTX,2);
|
||||
dest.writeField(wp,mRADIO_LINK_TIMEOUT,4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CellOptionsBCCH::text(ostream& os) const
|
||||
{
|
||||
os << "PWRC=" << mPWRC;
|
||||
os << " DTX=" << mDTX;
|
||||
os << " RADIO_LINK_TIMEOUT=" << mRADIO_LINK_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CellOptionsSACCH::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,(mDTX>>2)&0x01,1);
|
||||
dest.writeField(wp,mPWRC,1);
|
||||
dest.writeField(wp,mDTX&0x03,2);
|
||||
dest.writeField(wp,mRADIO_LINK_TIMEOUT,4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CellOptionsSACCH::text(ostream& os) const
|
||||
{
|
||||
os << "PWRC=" << mPWRC;
|
||||
os << " DTX=" << mDTX;
|
||||
os << " RADIO_LINK_TIMEOUT=" << mRADIO_LINK_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3CellSelectionParameters::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,mCELL_RESELECT_HYSTERESIS,3);
|
||||
dest.writeField(wp,mMS_TXPWR_MAX_CCH,5);
|
||||
dest.writeField(wp,mACS,1);
|
||||
dest.writeField(wp,mNECI,1);
|
||||
dest.writeField(wp,mRXLEV_ACCESS_MIN,6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CellSelectionParameters::text(ostream& os) const
|
||||
{
|
||||
os << "CELL-RESELECT-HYSTERESIS=" << mCELL_RESELECT_HYSTERESIS;
|
||||
os << " MS-TXPWR-MAX-CCH=" << mMS_TXPWR_MAX_CCH;
|
||||
os << " ACS=" << mACS;
|
||||
os << " NECI=" << mNECI;
|
||||
os << " RXLEV-ACCESS-MIN=" << mRXLEV_ACCESS_MIN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3ControlChannelDescription::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,0,1);
|
||||
dest.writeField(wp,mATT,1);
|
||||
dest.writeField(wp,mBS_AG_BLKS_RES,3);
|
||||
dest.writeField(wp,mCCCH_CONF,3);
|
||||
dest.writeField(wp,0,5);
|
||||
dest.writeField(wp,mBS_PA_MFRMS,3);
|
||||
dest.writeField(wp,mT3212,8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3ControlChannelDescription::text(ostream& os) const
|
||||
{
|
||||
os << "ATT=" << mATT;
|
||||
os << " BS_AG_BLKS_RES=" << mBS_AG_BLKS_RES;
|
||||
os << " CCCH_CONF=" << mCCCH_CONF;
|
||||
os << " BS_PA_MFRMS=" << mBS_PA_MFRMS;
|
||||
os << " T3212=" << mT3212;
|
||||
}
|
||||
|
||||
|
||||
bool L3FrequencyList::contains(unsigned wARFCN) const
|
||||
{
|
||||
for (unsigned i=0; i<mARFCNs.size(); i++) {
|
||||
if (mARFCNs[i]==wARFCN) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
unsigned L3FrequencyList::base() const
|
||||
{
|
||||
if (mARFCNs.size()==0) return 0;
|
||||
unsigned retVal = mARFCNs[0];
|
||||
for (unsigned i=1; i<mARFCNs.size(); i++) {
|
||||
unsigned thisVal = mARFCNs[i];
|
||||
if (thisVal<retVal) retVal=thisVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
unsigned L3FrequencyList::spread() const
|
||||
{
|
||||
if (mARFCNs.size()==0) return 0;
|
||||
unsigned max = mARFCNs[0];
|
||||
for (unsigned i=0; i<mARFCNs.size(); i++) {
|
||||
if (mARFCNs[i]>max) max=mARFCNs[i];
|
||||
}
|
||||
return max - base();
|
||||
}
|
||||
|
||||
|
||||
void L3FrequencyList::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// If this were used as Frequency List, it had to be coded
|
||||
// as the variable bit map format, GSM 04.08 10.5.2.13.7.
|
||||
// But it is used as Cell Channel Description and is coded
|
||||
// as the variable bit map format, GSM 04.08 10.5.2.1b.7.
|
||||
// Difference is in abscence of Length field.
|
||||
|
||||
// The header occupies first 7 most significant bits of
|
||||
// the first V-part octet and should be 1000111b=0x47 for
|
||||
// the variable length bitmap.
|
||||
dest.writeField(wp,0x47,7);
|
||||
|
||||
// base ARFCN
|
||||
unsigned baseARFCN = base();
|
||||
dest.writeField(wp,baseARFCN,10);
|
||||
// bit map
|
||||
unsigned delta = spread();
|
||||
unsigned numBits = 8*lengthV() - 17;
|
||||
if (numBits<delta) { LOG(ALERT) << "L3FrequencyList cannot encode full ARFCN set"; }
|
||||
for (unsigned i=0; i<numBits; i++) {
|
||||
unsigned thisARFCN = baseARFCN + 1 + i;
|
||||
if (contains(thisARFCN)) dest.writeField(wp,1,1);
|
||||
else dest.writeField(wp,0,1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3FrequencyList::text(ostream& os) const
|
||||
{
|
||||
int size = mARFCNs.size();
|
||||
for (int i=0; i<size; i++) {
|
||||
os << mARFCNs[i] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3CellChannelDescription::writeV(L3Frame& dest, size_t& wp) const
|
||||
{
|
||||
dest.fillField(wp,0,3);
|
||||
L3FrequencyList::writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3NeighborCellsDescription::writeV(L3Frame& dest, size_t& wp) const
|
||||
{
|
||||
dest.fillField(wp,0,3);
|
||||
L3FrequencyList::writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3NeighborCellsDescription::text(ostream& os) const
|
||||
{
|
||||
os << "EXT-IND=0 BA-IND=0 ";
|
||||
os << " ARFCNs=(";
|
||||
L3FrequencyList::text(os);
|
||||
os << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3NCCPermitted::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp,mPermitted,8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3NCCPermitted::text(ostream& os) const
|
||||
{
|
||||
os << hex << "0x" << mPermitted << dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3RACHControlParameters::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// GMS 04.08 10.5.2.29
|
||||
dest.writeField(wp, mMaxRetrans, 2);
|
||||
dest.writeField(wp, mTxInteger, 4);
|
||||
dest.writeField(wp, mCellBarAccess, 1);
|
||||
dest.writeField(wp, mRE, 1);
|
||||
dest.writeField(wp, mAC, 16);
|
||||
}
|
||||
|
||||
|
||||
void L3RACHControlParameters::text(ostream& os) const
|
||||
{
|
||||
os << "maxRetrans=" << mMaxRetrans;
|
||||
os << " txInteger=" << mTxInteger;
|
||||
os << " cellBarAccess=" << mCellBarAccess;
|
||||
os << " RE=" << mRE;
|
||||
os << hex << " AC=0x" << mAC << dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3PageMode::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// PageMode is 1/2 octet. Spare[3:2], PM[1:0]
|
||||
dest.writeField(wp, 0x00, 2);
|
||||
dest.writeField(wp, mPageMode, 2);
|
||||
}
|
||||
|
||||
void L3PageMode::parseV( const L3Frame &src, size_t &rp)
|
||||
{
|
||||
// Read out spare bits.
|
||||
rp += 2;
|
||||
// Read out PageMode.
|
||||
mPageMode = src.readField(rp, 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3PageMode::text(ostream& os) const
|
||||
{
|
||||
os << mPageMode;
|
||||
}
|
||||
|
||||
|
||||
void L3DedicatedModeOrTBF::writeV( L3Frame& dest, size_t &wp )const
|
||||
{
|
||||
// 1/2 Octet.
|
||||
dest.writeField(wp, 0, 1);
|
||||
dest.writeField(wp, mTMA, 1);
|
||||
dest.writeField(wp, mDownlink, 1);
|
||||
dest.writeField(wp, mDMOrTBF, 1);
|
||||
}
|
||||
|
||||
|
||||
void L3DedicatedModeOrTBF::text(ostream& os) const
|
||||
{
|
||||
os << "TMA=" << mTMA;
|
||||
os << " Downlink=" << mDownlink;
|
||||
os << " DMOrTBF=" << mDMOrTBF;
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelDescription::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
// GSM 04.08 10.5.2.5
|
||||
// Channel Description Format (non-hopping)
|
||||
// 7 6 5 4 3 2 1 0
|
||||
// [ TSC ][ H=0 ][ SPARE(0,0)][ ARFCN[9:8] ] Octet 3
|
||||
// [ ARFCN[7:0] ] Octet 4 H=0
|
||||
//
|
||||
|
||||
// HACK -- Hard code for non-hopping.
|
||||
assert(mHFlag==0);
|
||||
dest.writeField(wp,mTypeAndOffset,5);
|
||||
dest.writeField(wp,mTN,3);
|
||||
dest.writeField(wp,mTSC,3);
|
||||
dest.writeField(wp,0,3); // H=0 + 2 spares
|
||||
dest.writeField(wp,mARFCN,10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3ChannelDescription::parseV(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
// GSM 04.08 10.5.2.5
|
||||
mTypeAndOffset = (TypeAndOffset)src.readField(rp,5);
|
||||
mTN = src.readField(rp,3);
|
||||
mTSC = src.readField(rp,3);
|
||||
mHFlag = src.readField(rp,1);
|
||||
if (mHFlag) {
|
||||
mMAIO = src.readField(rp,6);
|
||||
mHSN = src.readField(rp,6);
|
||||
} else {
|
||||
rp += 2; // skip 2 spare bits
|
||||
mARFCN = src.readField(rp,10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelDescription::text(std::ostream& os) const
|
||||
{
|
||||
|
||||
os << "typeAndOffset=" << mTypeAndOffset;
|
||||
os << " TN=" << mTN;
|
||||
os << " TSC=" << mTSC;
|
||||
os << " ARFCN=" << mARFCN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3RequestReference::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
|
||||
|
||||
// Request Reference Format.
|
||||
// 7 6 5 4 3 2 1 0
|
||||
// [ RequestReference [7:0] ] Octet 2
|
||||
// [ T1[4:0] ][ T3[5:3] ] Octet 3
|
||||
// [ T3[2:0] ][ T2[4:0] ] Octet 4
|
||||
|
||||
dest.writeField(wp, mRA, 8);
|
||||
dest.writeField(wp, mT1p, 5);
|
||||
dest.writeField(wp, mT3, 6);
|
||||
dest.writeField(wp, mT2, 5);
|
||||
}
|
||||
|
||||
|
||||
void L3RequestReference::text(ostream& os) const
|
||||
{
|
||||
os << "RA=" << mRA;
|
||||
os << " T1'=" << mT1p;
|
||||
os << " T2=" << mT2;
|
||||
os << " T3=" << mT3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3TimingAdvance::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
dest.writeField(wp, 0x00, 2);
|
||||
dest.writeField(wp, mTimingAdvance, 6);
|
||||
}
|
||||
|
||||
void L3TimingAdvance::text(ostream& os) const
|
||||
{
|
||||
os << mTimingAdvance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3RRCause::writeV( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
dest.writeField(wp, mCauseValue, 8);
|
||||
}
|
||||
|
||||
void L3RRCause::parseV( const L3Frame &src, size_t &rp )
|
||||
{
|
||||
mCauseValue = src.readField(rp, 8);
|
||||
}
|
||||
|
||||
void L3RRCause::text(ostream& os) const
|
||||
{
|
||||
os << "0x" << hex << mCauseValue << dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3PowerCommand::writeV( L3Frame &dest, size_t &wp )const
|
||||
{
|
||||
dest.writeField(wp, 0, 3);
|
||||
dest.writeField(wp, mCommand, 5);
|
||||
}
|
||||
|
||||
|
||||
void L3PowerCommand::text(ostream& os) const
|
||||
{
|
||||
os << mCommand;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3ChannelMode::writeV( L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
dest.writeField(wp, mMode, 8);
|
||||
}
|
||||
|
||||
void L3ChannelMode::parseV(const L3Frame& src, size_t& rp)
|
||||
{
|
||||
mMode = (Mode)src.readField(rp,8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L3ChannelMode::Mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case L3ChannelMode::SignallingOnly: os << "signalling"; break;
|
||||
case L3ChannelMode::SpeechV1: os << "speech1"; break;
|
||||
case L3ChannelMode::SpeechV2: os << "speech2"; break;
|
||||
case L3ChannelMode::SpeechV3: os << "speech3"; break;
|
||||
default: os << "?" << (int)mode << "?";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
void L3ChannelMode::text(ostream& os) const
|
||||
{
|
||||
os << mMode;
|
||||
}
|
||||
|
||||
// Application Information IEs
|
||||
|
||||
// APDU ID
|
||||
|
||||
void L3APDUID::writeV( L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// APDU ID is 1/2 octet. Protocol Identifier [3:0]
|
||||
dest.writeField(wp,mProtocolIdentifier,4);
|
||||
}
|
||||
|
||||
void L3APDUID::parseV( const L3Frame &src, size_t &rp)
|
||||
{
|
||||
// Read out Protocol Identifier.
|
||||
mProtocolIdentifier = src.readField(rp, 4);
|
||||
}
|
||||
|
||||
void L3APDUID::text(ostream& os) const
|
||||
{
|
||||
os << mProtocolIdentifier;
|
||||
}
|
||||
|
||||
// APDU Flags
|
||||
|
||||
void L3APDUFlags::writeV( L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// APDU Flags is 1/2 octet. Protocol Identifier [3:0]
|
||||
dest.writeField(wp,0,1); // spare
|
||||
dest.writeField(wp,mCR,1); // C/R
|
||||
dest.writeField(wp,mFirstSegment,1);
|
||||
dest.writeField(wp,mLastSegment,1);
|
||||
}
|
||||
|
||||
void L3APDUFlags::parseV( const L3Frame &src, size_t &rp)
|
||||
{
|
||||
// Read out Protocol Identifier.
|
||||
rp += 1; // skip spare
|
||||
mCR = src.readField(rp, 1);
|
||||
mFirstSegment = src.readField(rp, 1);
|
||||
mLastSegment = src.readField(rp, 1);
|
||||
}
|
||||
|
||||
void L3APDUFlags::text(ostream& os) const
|
||||
{
|
||||
os << mCR << "," << mFirstSegment << "," << mLastSegment;
|
||||
}
|
||||
|
||||
// APDU Data
|
||||
|
||||
L3APDUData::~L3APDUData()
|
||||
{
|
||||
}
|
||||
|
||||
L3APDUData::L3APDUData()
|
||||
:L3ProtocolElement()
|
||||
{
|
||||
}
|
||||
|
||||
L3APDUData::L3APDUData(BitVector data)
|
||||
:L3ProtocolElement()
|
||||
,mData(data)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3APDUData::writeV( L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// we only need to write the data part
|
||||
// TODO - single line please. copy / memcpy, anything better then a for loop
|
||||
LOG(DEBUG) << "L3APDUData: writeV " << mData.size() << " bits";
|
||||
mData.copyToSegment(dest, wp);
|
||||
wp += mData.size() / 8;
|
||||
}
|
||||
|
||||
void L3APDUData::parseV( const L3Frame& src, size_t &rp, size_t expectedLength )
|
||||
{
|
||||
LOG(DEBUG) << "L3APDUData: parseV " << expectedLength << " bytes";
|
||||
mData.resize(expectedLength*8);
|
||||
src.copyToSegment(mData, rp, expectedLength*8); // expectedLength is bytes, not bits
|
||||
//for ( size_t i = 0 ; i < expectedLength ; ++i)
|
||||
// mData[i] = src.readField(rp, 8);
|
||||
}
|
||||
|
||||
void L3APDUData::text(ostream& os) const
|
||||
{
|
||||
// TODO - use the following two lines (get rid of the "char / char*" error)
|
||||
//std::ostream_iterator<std::string> output( os, "" );
|
||||
//std::copy( mData.begin(), mData.end(), output );
|
||||
for (size_t i = 0 ; i < mData.size() ; ++i) {
|
||||
os << (mData[i] ? "1" : "0");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void L3MeasurementResults::parseV(const L3Frame& frame, size_t &rp)
|
||||
{
|
||||
// GSM 04.08 10.5.2.20
|
||||
mBA_USED = frame.readField(rp,1);
|
||||
mDTX_USED = frame.readField(rp,1);
|
||||
mRXLEV_FULL_SERVING_CELL = frame.readField(rp,6);
|
||||
rp++; // spare
|
||||
mMEAS_VALID = frame.readField(rp,1);
|
||||
mRXLEV_SUB_SERVING_CELL = frame.readField(rp,6);
|
||||
rp++; // spare
|
||||
mRXQUAL_FULL_SERVING_CELL = frame.readField(rp,3);
|
||||
mRXQUAL_SUB_SERVING_CELL = frame.readField(rp,3);
|
||||
mNO_NCELL = frame.readField(rp,3);
|
||||
for (unsigned i=0; i<6; i++) {
|
||||
mRXLEV_NCELL[i] = frame.readField(rp,6);
|
||||
mBCCH_FREQ_NCELL[i] = frame.readField(rp,5);
|
||||
mBSIC_NCELL[i] = frame.readField(rp,6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L3MeasurementResults::text(ostream& os) const
|
||||
{
|
||||
// GSM 04.08 10.5.2.20
|
||||
os << "BA_USED=" << mBA_USED;
|
||||
os << " DTX_USED=" << mDTX_USED;
|
||||
os << " MEAS_VALID=" << mMEAS_VALID;
|
||||
// Note that the value of the MEAS-VALID bit is reversed
|
||||
// from what you might expect.
|
||||
if (mMEAS_VALID) return;
|
||||
os << " RXLEV_FULL_SERVING_CELL=" << mRXLEV_FULL_SERVING_CELL;
|
||||
os << " RXLEV_SUB_SERVING_CELL=" << mRXLEV_SUB_SERVING_CELL;
|
||||
os << " RXQUAL_FULL_SERVING_CELL=" << mRXQUAL_FULL_SERVING_CELL;
|
||||
os << " RXQUAL_SUB_SERVING_CELL=" << mRXQUAL_SUB_SERVING_CELL;
|
||||
os << " NO_NCELL=" << mNO_NCELL;
|
||||
// no measurements?
|
||||
if (mNO_NCELL==0) return;
|
||||
// no neighbor list?
|
||||
if (mNO_NCELL==7) return;
|
||||
for (unsigned i=0; i<mNO_NCELL; i++) {
|
||||
os << " RXLEV_NCELL" << i+1 << "=" << mRXLEV_NCELL[i];
|
||||
os << " BCCH_FREQ_NCELL" << i+1 << "=" << mBCCH_FREQ_NCELL[i];
|
||||
os << " BSIC_NCELL" << i+1 << "=" << mBSIC_NCELL[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned L3MeasurementResults::RXLEV_NCELL(unsigned * target) const
|
||||
{
|
||||
for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mRXLEV_NCELL[i];
|
||||
return mNO_NCELL;
|
||||
}
|
||||
|
||||
|
||||
unsigned L3MeasurementResults::BCCH_FREQ_NCELL(unsigned * target) const
|
||||
{
|
||||
for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBCCH_FREQ_NCELL[i];
|
||||
return mNO_NCELL;
|
||||
}
|
||||
|
||||
|
||||
unsigned L3MeasurementResults::BSIC_NCELL(unsigned * target) const
|
||||
{
|
||||
for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBSIC_NCELL[i];
|
||||
return mNO_NCELL;
|
||||
}
|
||||
|
||||
|
||||
int L3MeasurementResults::decodeLevToDBm(unsigned lev) const
|
||||
{
|
||||
// See GSM 05.08 8.1.4.
|
||||
// SCALE is 0 for anything but the ENHANCED MEASUREMENT REPORT message.
|
||||
return -111 + lev;
|
||||
}
|
||||
|
||||
float L3MeasurementResults::decodeQualToBER(unsigned qual) const
|
||||
{
|
||||
// See GSM 05.08 8.2.4.
|
||||
// Convert lowest value as "0" instead of 0.14%.
|
||||
static const float vals[] = {0.0, 0.28, 0.57, 1.13, 2.26, 4.53, 9.05, 18.10};
|
||||
assert(qual<8);
|
||||
return 0.01*vals[qual];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
L3SI3RestOctets::L3SI3RestOctets()
|
||||
:L3RestOctets(),
|
||||
mHaveSelectionParameters(false),
|
||||
mCBQ(0),mCELL_RESELECT_OFFSET(0),
|
||||
mTEMPORARY_OFFSET(0),
|
||||
mPENALTY_TIME(0)
|
||||
{
|
||||
// See GSM 04.08 10.5.2.34 and 05.08 9 Table 1.
|
||||
if (!gConfig.defines("GSM.SI3RO")) return;
|
||||
|
||||
// Optional Cell Selection Parameters.
|
||||
|
||||
// CELL_BAR_QUALIFY. 1 bit. Default value is 0.
|
||||
if (gConfig.defines("GSM.SI3RO.CBQ")) {
|
||||
mCBQ = gConfig.getNum("GSM.SI3RO.CBQ");
|
||||
mHaveSelectionParameters = true;
|
||||
}
|
||||
// CELL_RESELECT_OFFSET. 6 bits. Default value is 0.
|
||||
// C2 offset in 2 dB steps
|
||||
if (gConfig.defines("GSM.SI3RO.CRO")) {
|
||||
mCELL_RESELECT_OFFSET = gConfig.getNum("GSM.SI3RO.CRO");
|
||||
mHaveSelectionParameters = true;
|
||||
}
|
||||
// Another offset to C2 in 10 dB steps, applied during penalty time.
|
||||
// 3 bits. // Default is 0 dB but "7" means "infinity".
|
||||
if (gConfig.defines("GSM.SI3RO.TEMPORARY_OFFSET")) {
|
||||
mTEMPORARY_OFFSET = gConfig.getNum("GSM.SI3RO.TEMPORARY_OFFSET");
|
||||
mHaveSelectionParameters = true;
|
||||
}
|
||||
// The time for which the temporary offset is applied, 20*(n+1).
|
||||
if (gConfig.defines("GSM.SI3RO.PENALTY_TIME")) {
|
||||
mPENALTY_TIME = gConfig.getNum("GSM.SI3RO.PENALTY_TIME");
|
||||
mHaveSelectionParameters = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t L3SI3RestOctets::lengthV() const
|
||||
{
|
||||
size_t sumBits = 0;
|
||||
if (mHaveSelectionParameters) sumBits += 1 + 1+6+3+5;
|
||||
|
||||
size_t octets = sumBits/8;
|
||||
if (sumBits%8) octets += 1;
|
||||
return octets;
|
||||
}
|
||||
|
||||
|
||||
void L3SI3RestOctets::writeV(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
if (mHaveSelectionParameters) {
|
||||
dest.writeH(wp);
|
||||
dest.writeField(wp,mCBQ,1);
|
||||
dest.writeField(wp,mCELL_RESELECT_OFFSET,6);
|
||||
dest.writeField(wp,mTEMPORARY_OFFSET,3);
|
||||
dest.writeField(wp,mPENALTY_TIME,5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L3SI3RestOctets::text(ostream& os) const
|
||||
{
|
||||
if (mHaveSelectionParameters) {
|
||||
os << "CBQ=" << mCBQ;
|
||||
os << " CELL_RESELECT_OFFSET=" << mCELL_RESELECT_OFFSET;
|
||||
os << " TEMPORARY_OFFSET=" << mTEMPORARY_OFFSET;
|
||||
os << " PANALTY_TIME=" << mPENALTY_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
820
GSM/GSML3RRElements.h
Normal file
820
GSM/GSML3RRElements.h
Normal file
@@ -0,0 +1,820 @@
|
||||
/**@file @brief Elements for Radio Resource messsages, GSM 04.08 10.5.2. */
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef GSML3RRELEMENTS_H
|
||||
#define GSML3RRELEMENTS_H
|
||||
|
||||
#include <vector>
|
||||
#include "GSML3Message.h"
|
||||
#include <Globals.h>
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
/** Cell Options (BCCH), GSM 04.08 10.5.2.3 */
|
||||
class L3CellOptionsBCCH : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mPWRC; ///< 1 -> downlink power control may be used
|
||||
unsigned mDTX; ///< discontinuous transmission state
|
||||
unsigned mRADIO_LINK_TIMEOUT; ///< timeout to declare dead phy link
|
||||
|
||||
public:
|
||||
|
||||
/** Sets defaults for no use of DTX or downlink power control. */
|
||||
L3CellOptionsBCCH()
|
||||
:L3ProtocolElement()
|
||||
{
|
||||
// Values dictated by the current implementation are hard-coded.
|
||||
mPWRC=0;
|
||||
mDTX=2;
|
||||
// Configuarable values.
|
||||
mRADIO_LINK_TIMEOUT= gConfig.getNum("GSM.RADIO-LINK-TIMEOUT");
|
||||
}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Cell Options (SACCH), GSM 04.08 10.5.2.3a */
|
||||
class L3CellOptionsSACCH : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mPWRC; ///< 1 -> downlink power control may be used
|
||||
unsigned mDTX; ///< discontinuous transmission state
|
||||
unsigned mRADIO_LINK_TIMEOUT; ///< timeout to declare dead phy link
|
||||
|
||||
public:
|
||||
|
||||
/** Sets defaults for no use of DTX or downlink power control. */
|
||||
L3CellOptionsSACCH()
|
||||
:L3ProtocolElement()
|
||||
{
|
||||
// Values dictated by the current implementation are hard-coded.
|
||||
mPWRC=0;
|
||||
mDTX=2;
|
||||
// Configuarable values.
|
||||
mRADIO_LINK_TIMEOUT=gConfig.getNum("GSM.RADIO-LINK-TIMEOUT");
|
||||
}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Cell Selection Parameters, GSM 04.08 10.5.2.4 */
|
||||
class L3CellSelectionParameters : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mACS;
|
||||
unsigned mNECI;
|
||||
unsigned mCELL_RESELECT_HYSTERESIS;
|
||||
unsigned mMS_TXPWR_MAX_CCH;
|
||||
unsigned mRXLEV_ACCESS_MIN;
|
||||
|
||||
public:
|
||||
|
||||
/** Sets defaults to reduce gratuitous handovers. */
|
||||
L3CellSelectionParameters()
|
||||
:L3ProtocolElement()
|
||||
{
|
||||
// Values dictated by the current implementation are hard-coded.
|
||||
mACS=0; // We don't support SI16 & SI17 yet.
|
||||
// Configurable values.
|
||||
mNECI=gConfig.getNum("GSM.CellSelection.NECI");
|
||||
mMS_TXPWR_MAX_CCH=gConfig.getNum("GSM.CellSelection.MS-TXPWR-MAX-CCH");
|
||||
mRXLEV_ACCESS_MIN=gConfig.getNum("GSM.CellSelection.RXLEV-ACCESS-MIN");
|
||||
mCELL_RESELECT_HYSTERESIS=gConfig.getNum("GSM.CellSelection.CELL-RESELECT-HYSTERESIS");
|
||||
}
|
||||
|
||||
size_t lengthV() const { return 2; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Control Channel Description, GSM 04.08 10.5.2.11 */
|
||||
class L3ControlChannelDescription : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mATT; ///< 1 -> IMSI attach/detach
|
||||
unsigned mBS_AG_BLKS_RES; ///< access grant channel reservation
|
||||
unsigned mCCCH_CONF; ///< channel combination for CCCH
|
||||
unsigned mBS_PA_MFRMS; ///< paging channel configuration
|
||||
unsigned mT3212; ///< periodic updating timeout
|
||||
|
||||
public:
|
||||
|
||||
/** Sets reasonable defaults for a single-ARFCN system. */
|
||||
L3ControlChannelDescription():L3ProtocolElement()
|
||||
{
|
||||
// Values dictated by the current implementation are hard-coded.
|
||||
mBS_AG_BLKS_RES=2; // reserve 2 CCCHs for access grant
|
||||
mBS_PA_MFRMS=0; // minimum PCH spacing
|
||||
// Configurable values.
|
||||
mATT=(unsigned)gConfig.defines("Control.LUR.AttachDetach");
|
||||
mCCCH_CONF=gConfig.getNum("GSM.CCCH.CCCH-CONF");
|
||||
mT3212=gConfig.getNum("GSM.Timer.T3212")/6;
|
||||
}
|
||||
|
||||
size_t lengthV() const { return 3; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
A generic frequency list element base class, GSM 04.08 10.5.2.13.
|
||||
This implementation supports only the "variable bit map" format
|
||||
(GSM 04.08 10.5.2.13.7).
|
||||
*/
|
||||
class L3FrequencyList : public L3ProtocolElement {
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<unsigned> mARFCNs; ///< ARFCN list to encode/decode
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor creates an empty list. */
|
||||
L3FrequencyList():L3ProtocolElement() {}
|
||||
|
||||
L3FrequencyList(const std::vector<unsigned>& wARFCNs)
|
||||
:L3ProtocolElement(),
|
||||
mARFCNs(wARFCNs)
|
||||
{}
|
||||
|
||||
//void push_back(unsigned ARFCN) { mARFCNs.push_back(ARFCN); }
|
||||
void ARFCNs(const std::vector<unsigned>& wARFCNs) { mARFCNs=wARFCNs; }
|
||||
const std::vector<unsigned>& ARFCNs() const { return mARFCNs; }
|
||||
|
||||
size_t lengthV() const { return 16; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
private:
|
||||
|
||||
/**@name ARFCN set browsing. */
|
||||
//@{
|
||||
/** Return minimum-numbered ARFCN. */
|
||||
unsigned base() const;
|
||||
|
||||
/** Return numeric spread of ARFNs. */
|
||||
unsigned spread() const;
|
||||
|
||||
/** Return true if a given ARFCN is in the list. */
|
||||
bool contains(unsigned wARFCN) const;
|
||||
//@}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Cell Channel Description, GSM 04.08 10.5.2.1b.
|
||||
This element is used to provide the Cell Allocation
|
||||
for frequency hopping configurations.
|
||||
It lists the ARFCNs available for hopping and
|
||||
normally lists all of the ARFCNs for the system.
|
||||
It is mandatory, even if you don't use hopping.
|
||||
*/
|
||||
class L3CellChannelDescription : public L3FrequencyList {
|
||||
|
||||
public:
|
||||
|
||||
L3CellChannelDescription()
|
||||
:L3FrequencyList()
|
||||
{}
|
||||
|
||||
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Neighbor Cells Description, GSM 04.08 10.5.2.22
|
||||
(A kind of frequency list.)
|
||||
This element describes neighboring cells that may be
|
||||
candidates for handovers.
|
||||
*/
|
||||
class L3NeighborCellsDescription : public L3FrequencyList {
|
||||
|
||||
public:
|
||||
|
||||
L3NeighborCellsDescription()
|
||||
:L3FrequencyList(gConfig.getVector("GSM.CellSelection.Neighbors"))
|
||||
{}
|
||||
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** NCC Permitted, GSM 04.08 10.5.2.27 */
|
||||
class L3NCCPermitted : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mPermitted; ///< NCC allowance mask (NCCs 0-7)
|
||||
|
||||
public:
|
||||
|
||||
/** Get default parameters from gConfig. */
|
||||
L3NCCPermitted()
|
||||
:L3ProtocolElement()
|
||||
{
|
||||
mPermitted = gConfig.getNum("GSM.CellSelection.NCCsPermitted");
|
||||
}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** RACH Control Parameters GSM 04.08 10.5.2.29 */
|
||||
class L3RACHControlParameters : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mMaxRetrans; ///< code for 1-7 RACH retransmission attempts
|
||||
unsigned mTxInteger; ///< code for 3-50 slots to spread transmission
|
||||
unsigned mCellBarAccess; ///< if true, phones cannot camp
|
||||
unsigned mRE; ///< if true, call reestablishment is not allowed
|
||||
uint16_t mAC; ///< mask of barring flags for the 16 access classes
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor parameters allows all access. */
|
||||
L3RACHControlParameters()
|
||||
:L3ProtocolElement()
|
||||
{
|
||||
// Values ditected by imnplementation are hard-coded.
|
||||
mRE=1;
|
||||
mCellBarAccess=0;
|
||||
// Configurable values.
|
||||
mMaxRetrans = gConfig.getNum("GSM.RACH.MaxRetrans");
|
||||
mTxInteger = gConfig.getNum("GSM.RACH.TxInteger");
|
||||
mAC = gConfig.getNum("GSM.RACH.AC");
|
||||
}
|
||||
|
||||
size_t lengthV() const { return 3; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** PageMode, GSM 04.08 10.5.2.26 */
|
||||
class L3PageMode : public L3ProtocolElement
|
||||
{
|
||||
|
||||
|
||||
unsigned mPageMode;
|
||||
|
||||
public:
|
||||
|
||||
/** Default mode is "normal paging". */
|
||||
L3PageMode(unsigned wPageMode=0)
|
||||
:L3ProtocolElement(),
|
||||
mPageMode(wPageMode)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV( L3Frame& dest, size_t &wp ) const;
|
||||
void parseV( const L3Frame &src, size_t &rp );
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** DedicatedModeOrTBF, GSM 04.08 10.5.2.25b */
|
||||
class L3DedicatedModeOrTBF : public L3ProtocolElement {
|
||||
|
||||
unsigned mDownlink; ///< Indicates the IA reset octets contain additional information.
|
||||
unsigned mTMA; ///< This is part of a 2-message assignment.
|
||||
unsigned mDMOrTBF; ///< Dedicated link (circuit-switched) or temporary block flow (GPRS/pakcet).
|
||||
|
||||
|
||||
public:
|
||||
|
||||
L3DedicatedModeOrTBF()
|
||||
:L3ProtocolElement(),
|
||||
mDownlink(0), mTMA(0), mDMOrTBF(0)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame &dest, size_t &wp ) const;
|
||||
void parseV( const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** ChannelDescription, GSM 04.08 10.5.2.5 */
|
||||
class L3ChannelDescription : public L3ProtocolElement {
|
||||
|
||||
|
||||
// Channel Description Format.
|
||||
// 7 6 5 4 3 2 1 0
|
||||
// [ ChannelTypeTDMAOffset[4:0] ][ TN[2:0] ] Octect 1
|
||||
// [ TSC ][ H=0 ][ SPARE(0,0)][ ARFCN[9:8] ] Octect 2
|
||||
// [ [ H=1 ][ MAIO[5:2] ] Octect 2
|
||||
// [ ARFCN[7:0] ] Octect 3 H=0
|
||||
// [ MAIO[1:0] ][ HSN[5:0] ] Octect 3 H=1
|
||||
//
|
||||
|
||||
// Octet 2.
|
||||
TypeAndOffset mTypeAndOffset; // 5 bit
|
||||
unsigned mTN; //3 bit
|
||||
|
||||
// Octet 3 & 4.
|
||||
unsigned mTSC; // 3 bit
|
||||
unsigned mHFlag; // 1 bit
|
||||
unsigned mARFCN; // 10 bit overflows
|
||||
unsigned mMAIO; // 6 bit overflows
|
||||
unsigned mHSN; // 6 bit
|
||||
|
||||
public:
|
||||
|
||||
/** Non-hopping initializer. */
|
||||
L3ChannelDescription(TypeAndOffset wTypeAndOffset, unsigned wTN,
|
||||
unsigned wTSC, unsigned wARFCN)
|
||||
:mTypeAndOffset(wTypeAndOffset),mTN(wTN),
|
||||
mTSC(wTSC),
|
||||
mHFlag(0),
|
||||
mARFCN(wARFCN),
|
||||
mMAIO(0),mHSN(0)
|
||||
{ }
|
||||
|
||||
/** Blank initializer */
|
||||
L3ChannelDescription()
|
||||
:mTypeAndOffset(TDMA_MISC),
|
||||
mTN(0),mTSC(0),mHFlag(0),mARFCN(0),mMAIO(0),mHSN(0)
|
||||
{ }
|
||||
|
||||
|
||||
size_t lengthV() const { return 3; }
|
||||
void writeV( L3Frame &dest, size_t &wp ) const;
|
||||
void parseV(const L3Frame& src, size_t &rp);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** RequestReference, GSM 04.08 10.5.2.30 */
|
||||
class L3RequestReference : public L3ProtocolElement
|
||||
{
|
||||
|
||||
// Request Reference Format.
|
||||
// 7 6 5 4 3 2 1 0
|
||||
// [ RequestReference [7:0] ] Octet 2
|
||||
// [ T1[4:0] ][ T3[5:3] ] Octet 3
|
||||
// [ T3[2:0] ][ T2[4:0] ] Octet 4
|
||||
|
||||
unsigned mRA; ///< random tag from original RACH burst
|
||||
|
||||
/**@name Timestamp of the corresponing RACH burst. */
|
||||
//@{
|
||||
unsigned mT1p; ///< T1 mod 32
|
||||
unsigned mT2;
|
||||
unsigned mT3;
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
L3RequestReference() {}
|
||||
|
||||
L3RequestReference(unsigned wRA, const GSM::Time& when)
|
||||
:mRA(wRA),
|
||||
mT1p(when.T1()%32),mT2(when.T2()),mT3(when.T3())
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 3; }
|
||||
void writeV(L3Frame &, size_t &wp ) const;
|
||||
void parseV( const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Timing Advance, GSM 04.08 10.5.2.40 */
|
||||
class L3TimingAdvance : public L3ProtocolElement
|
||||
{
|
||||
// TimingAdvance
|
||||
// 7 6 5 4 3 2 1 0
|
||||
// [ spare(0,0) ][ TimingAdvance [5:0] ] Octet 1
|
||||
|
||||
unsigned mTimingAdvance;
|
||||
|
||||
public:
|
||||
|
||||
L3TimingAdvance(unsigned wTimingAdvance=0)
|
||||
:L3ProtocolElement(),
|
||||
mTimingAdvance(wTimingAdvance)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame&, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.31 */
|
||||
class L3RRCause : public L3ProtocolElement
|
||||
{
|
||||
int mCauseValue;
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor cause defaults to "normal event". */
|
||||
L3RRCause(int wValue=0)
|
||||
:L3ProtocolElement()
|
||||
{ mCauseValue=wValue; }
|
||||
|
||||
int causeValue() const { return mCauseValue; }
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame&, size_t&) const;
|
||||
void parseV(const L3Frame&, size_t&);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.28 */
|
||||
class L3PowerCommand : public L3ProtocolElement
|
||||
{
|
||||
unsigned mCommand;
|
||||
|
||||
public:
|
||||
|
||||
L3PowerCommand(unsigned wCommand=0)
|
||||
:L3ProtocolElement(),
|
||||
mCommand(wCommand)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV( L3Frame &dest, size_t &wp ) const;
|
||||
void parseV( const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.6 */
|
||||
class L3ChannelMode : public L3ProtocolElement {
|
||||
|
||||
public:
|
||||
|
||||
enum Mode
|
||||
{
|
||||
SignallingOnly=0,
|
||||
SpeechV1=1,
|
||||
SpeechV2=2,
|
||||
SpeechV3=3
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Mode mMode;
|
||||
|
||||
public:
|
||||
|
||||
L3ChannelMode(Mode wMode=SignallingOnly)
|
||||
:L3ProtocolElement(),
|
||||
mMode(wMode)
|
||||
{}
|
||||
|
||||
bool operator==(const L3ChannelMode& other) const { return mMode==other.mMode; }
|
||||
bool operator!=(const L3ChannelMode& other) const { return mMode!=other.mMode; }
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame&, size_t&) const;
|
||||
void parseV(const L3Frame&, size_t&);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, L3ChannelMode::Mode);
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.43 */
|
||||
class L3WaitIndication : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mValue; ///< T3122 or T3142 value in seconds
|
||||
|
||||
public:
|
||||
|
||||
L3WaitIndication(unsigned seconds)
|
||||
:L3ProtocolElement(),
|
||||
mValue(seconds)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const
|
||||
{ dest.writeField(wp,mValue,8); }
|
||||
void parseV(const L3Frame&, size_t&) { assert(0); }
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream& os) const { os << mValue; }
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
Application Information Information Elements (encapsulates the RRLP message)
|
||||
*/
|
||||
|
||||
/** GSM 04.08 10.5.2.48 */
|
||||
class L3APDUID : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mProtocolIdentifier;
|
||||
|
||||
public:
|
||||
|
||||
/** Default Protocol Identifier is RRLP=0, the only one defined so far (rest are reserved). */
|
||||
L3APDUID(unsigned protocolIdentifier=0)
|
||||
:L3ProtocolElement(),
|
||||
mProtocolIdentifier(protocolIdentifier)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.49 */
|
||||
class L3APDUFlags : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
// TODO - use bool for flags?
|
||||
unsigned mCR;
|
||||
unsigned mFirstSegment;
|
||||
unsigned mLastSegment;
|
||||
// TODO - put enums for CR, FirstSegment, LastSegment
|
||||
|
||||
public:
|
||||
|
||||
/** Default is the flags for a single segment APDU - one that fits in a single
|
||||
Application Information message **/
|
||||
L3APDUFlags(unsigned cr=0, unsigned firstSegment=0, unsigned lastSegment=0)
|
||||
:L3ProtocolElement(),
|
||||
mCR(cr), mFirstSegment(firstSegment), mLastSegment(lastSegment)
|
||||
{}
|
||||
|
||||
size_t lengthV() const { return 1; }
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV(const L3Frame&, size_t&);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.50 */
|
||||
class L3APDUData : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
BitVector mData; // will contain a RRLP message
|
||||
|
||||
public:
|
||||
|
||||
virtual ~L3APDUData();
|
||||
|
||||
/** Default is a zero length APDUData IE */
|
||||
L3APDUData();
|
||||
L3APDUData(BitVector data);
|
||||
|
||||
size_t lengthV() const
|
||||
{
|
||||
// Return number of bytes neede to hold mData
|
||||
size_t sz = mData.size();
|
||||
size_t ln = sz/8;
|
||||
if (sz % 8) ln++;
|
||||
return ln;
|
||||
}
|
||||
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV( const L3Frame& src, size_t &rp, size_t expectedLength );
|
||||
void parseV(const L3Frame&, size_t&) { abort(); }
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 10.5.2.20 */
|
||||
class L3MeasurementResults : public L3ProtocolElement {
|
||||
|
||||
private:
|
||||
|
||||
bool mBA_USED;
|
||||
bool mDTX_USED;
|
||||
bool mMEAS_VALID; ///< 0 for valid, 1 for non-valid
|
||||
unsigned mRXLEV_FULL_SERVING_CELL;
|
||||
unsigned mRXLEV_SUB_SERVING_CELL;
|
||||
unsigned mRXQUAL_FULL_SERVING_CELL;
|
||||
unsigned mRXQUAL_SUB_SERVING_CELL;
|
||||
|
||||
unsigned mNO_NCELL;
|
||||
unsigned mRXLEV_NCELL[6];
|
||||
unsigned mBCCH_FREQ_NCELL[6];
|
||||
unsigned mBSIC_NCELL[6];
|
||||
|
||||
public:
|
||||
|
||||
L3MeasurementResults()
|
||||
:L3ProtocolElement(),
|
||||
mMEAS_VALID(false),
|
||||
mNO_NCELL(0)
|
||||
{ }
|
||||
|
||||
size_t lengthV() const { return 16; }
|
||||
void writeV(L3Frame&, size_t&) const { assert(0); }
|
||||
void parseV(const L3Frame&, size_t&);
|
||||
void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
|
||||
bool BA_USED() const { return mBA_USED; }
|
||||
bool DTX_USED() const { return mDTX_USED; }
|
||||
bool MEAS_VALID() const { return mMEAS_VALID; }
|
||||
unsigned RXLEV_FULL_SERVING_CELL() const { return mRXLEV_FULL_SERVING_CELL; }
|
||||
unsigned RXLEV_SUB_SERVING_CELL() const { return mRXLEV_SUB_SERVING_CELL; }
|
||||
unsigned RXQUAL_FULL_SERVING_CELL() const { return mRXQUAL_FULL_SERVING_CELL; }
|
||||
unsigned RXQUAL_SUB_SERVING_CELL() const { return mRXQUAL_SUB_SERVING_CELL; }
|
||||
|
||||
unsigned NO_NCELL() const { return mNO_NCELL; }
|
||||
unsigned RXLEV_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mRXLEV_NCELL[i]; }
|
||||
unsigned RXLEV_NCELL(unsigned *) const;
|
||||
unsigned BCCH_FREQ_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mBCCH_FREQ_NCELL[i]; }
|
||||
unsigned BCCH_FREQ_NCELL(unsigned *) const;
|
||||
unsigned BSIC_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mBSIC_NCELL[i]; }
|
||||
unsigned BSIC_NCELL(unsigned *) const;
|
||||
//@}
|
||||
|
||||
/**@ Real-unit conversions. */
|
||||
//@{
|
||||
/** Given an encoded level, return a value in dBm. */
|
||||
int decodeLevToDBm(unsigned lev) const;
|
||||
/** Given an encoded quality, return a BER. */
|
||||
float decodeQualToBER(unsigned qual) const;
|
||||
/**@ Converted accessors. */
|
||||
//@{
|
||||
int RXLEV_FULL_SERVING_CELL_dBm() const
|
||||
{ return decodeLevToDBm(mRXLEV_FULL_SERVING_CELL); }
|
||||
int RXLEV_SUB_SERVING_CELL_dBm() const
|
||||
{ return decodeLevToDBm(mRXLEV_SUB_SERVING_CELL); }
|
||||
float RXQUAL_FULL_SERVING_CELL_BER() const
|
||||
{ return decodeQualToBER(mRXQUAL_FULL_SERVING_CELL); }
|
||||
float RXQUAL_SUB_SERVING_CELL_BER() const
|
||||
{ return decodeQualToBER(mRXQUAL_SUB_SERVING_CELL); }
|
||||
int RXLEV_NCELL_dBm(unsigned i) const
|
||||
{ assert(i<mNO_NCELL); return decodeLevToDBm(mRXLEV_NCELL[i]); }
|
||||
//@}
|
||||
//@}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** A special subclass for rest octets, just in case we need it later. */
|
||||
class L3RestOctets : public L3ProtocolElement {
|
||||
|
||||
};
|
||||
|
||||
|
||||
class L3SI3RestOctets : public L3RestOctets {
|
||||
|
||||
private:
|
||||
|
||||
// We do not yet support the full parameter set.
|
||||
|
||||
bool mHaveSelectionParameters;
|
||||
bool mCBQ;
|
||||
unsigned mCELL_RESELECT_OFFSET;
|
||||
unsigned mTEMPORARY_OFFSET;
|
||||
unsigned mPENALTY_TIME;
|
||||
|
||||
public:
|
||||
|
||||
L3SI3RestOctets();
|
||||
|
||||
size_t lengthV() const;
|
||||
void writeV(L3Frame& dest, size_t &wp) const;
|
||||
void parseV( const L3Frame&, size_t&, size_t) { abort(); }
|
||||
void parseV(const L3Frame&, size_t&) { abort(); }
|
||||
void text(std::ostream& os) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // GSM
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
716
GSM/GSML3RRMessages.cpp
Normal file
716
GSM/GSML3RRMessages.cpp
Normal file
@@ -0,0 +1,716 @@
|
||||
/**@file
|
||||
@brief GSM Radio Resorce messages, from GSM 04.08 9.1.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include <typeinfo>
|
||||
#include <iostream>
|
||||
|
||||
#include "GSML3RRMessages.h"
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void L3Message::writeBody(L3Frame&,size_t&) const
|
||||
{
|
||||
LOG(ERR) << "not implemented for " << MTI();
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void L3Message::parseBody(const L3Frame&, size_t&)
|
||||
{
|
||||
LOG(ERR) << "not implemented for " << MTI();
|
||||
assert(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, L3RRMessage::MessageType val)
|
||||
{
|
||||
switch (val) {
|
||||
case L3RRMessage::SystemInformationType1:
|
||||
os << "System Information Type 1"; break;
|
||||
case L3RRMessage::SystemInformationType2:
|
||||
os << "System Information Type 2"; break;
|
||||
case L3RRMessage::SystemInformationType2bis:
|
||||
os << "System Information Type 2bis"; break;
|
||||
case L3RRMessage::SystemInformationType2ter:
|
||||
os << "System Information Type 2ter"; break;
|
||||
case L3RRMessage::SystemInformationType3:
|
||||
os << "System Information Type 3"; break;
|
||||
case L3RRMessage::SystemInformationType4:
|
||||
os << "System Information Type 4"; break;
|
||||
case L3RRMessage::SystemInformationType5:
|
||||
os << "System Information Type 5"; break;
|
||||
case L3RRMessage::SystemInformationType5bis:
|
||||
os << "System Information Type 5bis"; break;
|
||||
case L3RRMessage::SystemInformationType5ter:
|
||||
os << "System Information Type 5ter"; break;
|
||||
case L3RRMessage::SystemInformationType6:
|
||||
os << "System Information Type 6"; break;
|
||||
case L3RRMessage::SystemInformationType7:
|
||||
os << "System Information Type 7"; break;
|
||||
case L3RRMessage::SystemInformationType8:
|
||||
os << "System Information Type 8"; break;
|
||||
case L3RRMessage::SystemInformationType9:
|
||||
os << "System Information Type 9"; break;
|
||||
case L3RRMessage::SystemInformationType13:
|
||||
os << "System Information Type 13"; break;
|
||||
case L3RRMessage::SystemInformationType16:
|
||||
os << "System Information Type 16"; break;
|
||||
case L3RRMessage::SystemInformationType17:
|
||||
os << "System Information Type 17"; break;
|
||||
case L3RRMessage::PagingResponse:
|
||||
os << "Paging Response"; break;
|
||||
case L3RRMessage::PagingRequestType1:
|
||||
os << "Paging Request Type 1"; break;
|
||||
case L3RRMessage::MeasurementReport:
|
||||
os << "Measurement Report"; break;
|
||||
case L3RRMessage::AssignmentComplete:
|
||||
os << "Assignment Complete"; break;
|
||||
case L3RRMessage::ImmediateAssignment:
|
||||
os << "Immediate Assignment"; break;
|
||||
case L3RRMessage::ImmediateAssignmentReject:
|
||||
os << "Immediate Assignment Reject"; break;
|
||||
case L3RRMessage::AssignmentCommand:
|
||||
os << "Assignment Command"; break;
|
||||
case L3RRMessage::AssignmentFailure:
|
||||
os << "Assignment Failure"; break;
|
||||
case L3RRMessage::ChannelRelease:
|
||||
os << "Channel Release"; break;
|
||||
case L3RRMessage::ChannelModeModify:
|
||||
os << "Channel Mode Modify"; break;
|
||||
case L3RRMessage::ChannelModeModifyAcknowledge:
|
||||
os << "Channel Mode Modify Acknowledge"; break;
|
||||
case L3RRMessage::GPRSSuspensionRequest:
|
||||
os << "GPRS Suspension Request"; break;
|
||||
case L3RRMessage::ClassmarkEnquiry:
|
||||
os << "Classmark Enquiry"; break;
|
||||
case L3RRMessage::ClassmarkChange:
|
||||
os << "Classmark Change"; break;
|
||||
case L3RRMessage::RRStatus:
|
||||
os << "RR Status"; break;
|
||||
case L3RRMessage::ApplicationInformation:
|
||||
os << "Application Information"; break;
|
||||
default: os << hex << "0x" << (int)val << dec;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
void L3RRMessage::text(ostream& os) const
|
||||
{
|
||||
os << "RR " << (MessageType) MTI() << " ";
|
||||
}
|
||||
|
||||
|
||||
|
||||
L3RRMessage* GSM::L3RRFactory(L3RRMessage::MessageType MTI)
|
||||
{
|
||||
switch (MTI) {
|
||||
case L3RRMessage::ChannelRelease: return new L3ChannelRelease();
|
||||
case L3RRMessage::AssignmentComplete: return new L3AssignmentComplete();
|
||||
case L3RRMessage::AssignmentFailure: return new L3AssignmentFailure();
|
||||
case L3RRMessage::RRStatus: return new L3RRStatus();
|
||||
case L3RRMessage::PagingResponse: return new L3PagingResponse();
|
||||
case L3RRMessage::ChannelModeModifyAcknowledge: return new L3ChannelModeModifyAcknowledge();
|
||||
case L3RRMessage::ClassmarkChange: return new L3ClassmarkChange();
|
||||
case L3RRMessage::ClassmarkEnquiry: return new L3ClassmarkEnquiry();
|
||||
case L3RRMessage::MeasurementReport: return new L3MeasurementReport();
|
||||
case L3RRMessage::ApplicationInformation: return new L3ApplicationInformation();
|
||||
// Partial support just to get along with some phones.
|
||||
case L3RRMessage::GPRSSuspensionRequest: return new L3GPRSSuspensionRequest();
|
||||
default:
|
||||
LOG(WARNING) << "no L3 RR factory support for " << MTI;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
L3RRMessage* GSM::parseL3RR(const L3Frame& source)
|
||||
{
|
||||
L3RRMessage::MessageType MTI = (L3RRMessage::MessageType)source.MTI();
|
||||
LOG(DEBUG) << "parseL3RR MTI="<<MTI;
|
||||
|
||||
L3RRMessage *retVal = L3RRFactory(MTI);
|
||||
if (retVal==NULL) return NULL;
|
||||
|
||||
retVal->parse(source);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This is a local function to map the GSM::ChannelType enum
|
||||
to one of the codes from GMS 04.08 10.5.2.8.
|
||||
*/
|
||||
unsigned channelNeededCode(ChannelType wType)
|
||||
{
|
||||
switch (wType) {
|
||||
case AnyDCCHType: return 0;
|
||||
case SDCCHType: return 1;
|
||||
case TCHFType: return 2;
|
||||
case AnyTCHType: return 3;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t L3PagingRequestType1::l2BodyLength() const
|
||||
{
|
||||
int sz = mMobileIDs.size();
|
||||
assert(sz<=2);
|
||||
size_t sum=1;
|
||||
sum += mMobileIDs[0].lengthLV();
|
||||
if (sz>1) sum += mMobileIDs[1].lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3PagingRequestType1::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
// See GSM 04.08 9.1.22.
|
||||
// Page Mode Page Mode M V 1/2 10.5.2.26
|
||||
// Channels Needed M V 1/2
|
||||
// Mobile Identity 1 M LV 2-9 10.5.1.4
|
||||
// 0x17 Mobile Identity 2 O TLV 3-10 10.5.1.4
|
||||
|
||||
int sz = mMobileIDs.size();
|
||||
assert(sz<=2);
|
||||
// Remember to reverse orders of 1/2-octet fields.
|
||||
// Because GSM transmits LSB-first within each byte.
|
||||
// channel needed codes
|
||||
dest.writeField(wp,channelNeededCode(mChannelsNeeded[1]),2);
|
||||
dest.writeField(wp,channelNeededCode(mChannelsNeeded[0]),2);
|
||||
// "normal paging", GSM 04.08 Table 10.5.63
|
||||
dest.writeField(wp,0x0,4);
|
||||
// the actual mobile IDs
|
||||
mMobileIDs[0].writeLV(dest,wp);
|
||||
if (sz>1) mMobileIDs[1].writeTLV(0x17,dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3PagingRequestType1::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << " mobileIDs=(";
|
||||
for (unsigned i=0; i<mMobileIDs.size(); i++) {
|
||||
os << "(" << mMobileIDs[i] << "," << mChannelsNeeded[i] << "),";
|
||||
}
|
||||
os << ")";
|
||||
}
|
||||
|
||||
|
||||
size_t L3PagingResponse::l2BodyLength() const
|
||||
{
|
||||
return 1 + mClassmark.lengthLV() + mMobileID.lengthLV();
|
||||
}
|
||||
|
||||
void L3PagingResponse::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
// THIS CODE IS CORRECT. DON'T CHANGE IT. -- DAB
|
||||
rp += 8; // skip cipher key seq # and spare half octet
|
||||
// TREAT THIS AS LV!!
|
||||
mClassmark.parseLV(src,rp);
|
||||
// We only care about the mobile ID.
|
||||
mMobileID.parseLV(src,rp);
|
||||
}
|
||||
|
||||
void L3PagingResponse::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "mobileID=(" << mMobileID << ")";
|
||||
os << " classmark=(" << mClassmark << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3SystemInformationType1::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
/*
|
||||
System Information Message Type 1, GSM 04.08 9.1.31
|
||||
- Cell Channel Description 10.5.2.1b M V 16
|
||||
- RACH Control Parameters 10.5.2.29 M V 3
|
||||
*/
|
||||
mCellChannelDescription.writeV(dest,wp);
|
||||
mRACHControlParameters.writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType1::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "cellChannelDescription=(" << mCellChannelDescription << ")";
|
||||
os << " RACHControlParameters=(" << mRACHControlParameters << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3SystemInformationType2::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
/*
|
||||
System Information Type 2, GSM 04.08 9.1.32.
|
||||
- BCCH Frequency List 10.5.2.22 M V 16
|
||||
- NCC Permitted 10.5.2.27 M V 1
|
||||
- RACH Control Parameter 10.5.2.29 M V 3
|
||||
*/
|
||||
mBCCHFrequencyList.writeV(dest,wp);
|
||||
mNCCPermitted.writeV(dest,wp);
|
||||
mRACHControlParameters.writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType2::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "BCCHFrequencyList=(" << mBCCHFrequencyList << ")";
|
||||
os << " NCCPermitted=(" << mNCCPermitted << ")";
|
||||
os << " RACHControlParameters=(" << mRACHControlParameters << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3SystemInformationType3::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
/*
|
||||
System Information Type 3, GSM 04.08 9.1.35
|
||||
- Cell Identity 10.5.1.1 M V 2
|
||||
- Location Area Identification 10.5.1.3 M V 5
|
||||
- Control Channel Description 10.5.2.11 M V 3
|
||||
- Cell Options (BCCH) 10.5.2.3 M V 1
|
||||
- Cell Selection Parameters 10.5.2.4 M V 2
|
||||
- RACH Control Parameters 10.5.2.29 M V 3
|
||||
- Rest Octets 10.5.2.34 O CSN.1
|
||||
*/
|
||||
LOG(DEBUG) << dest;
|
||||
mCI.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
mLAI.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
mControlChannelDescription.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
mCellOptions.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
mCellSelectionParameters.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
mRACHControlParameters.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
if (mHaveRestOctets) mRestOctets.writeV(dest,wp);
|
||||
LOG(DEBUG) << dest;
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType3::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "LAI=(" << mLAI << ")";
|
||||
os << " CI=" << mCI;
|
||||
os << " controlChannelDescription=(" << mControlChannelDescription << ")";
|
||||
os << " cellOptions=(" << mCellOptions << ")";
|
||||
os << " cellSelectionParameters=(" << mCellSelectionParameters << ")";
|
||||
os << " RACHControlParameters=(" << mRACHControlParameters << ")";
|
||||
if (mHaveRestOctets) os << " SI3RO=(" << mRestOctets << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
L3SystemInformationType4::L3SystemInformationType4()
|
||||
:L3RRMessageNRO()
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
size_t L3SystemInformationType4::l2BodyLength() const
|
||||
{
|
||||
size_t len = mLAI.lengthV();
|
||||
len += mCellSelectionParameters.lengthV();
|
||||
len += mRACHControlParameters.lengthV();
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType4::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
/*
|
||||
System Information Type 4, GSM 04.08 9.1.36
|
||||
- Location Area Identification 10.5.1.3 M V 5
|
||||
- Cell Selection Parameters 10.5.2.4 M V 2
|
||||
- RACH Control Parameters 10.5.2.29 M V 3
|
||||
*/
|
||||
mLAI.writeV(dest,wp);
|
||||
mCellSelectionParameters.writeV(dest,wp);
|
||||
mRACHControlParameters.writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType4::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "LAI=(" << mLAI << ")";
|
||||
os << " cellSelectionParameters=(" << mCellSelectionParameters << ")";
|
||||
os << " RACHControlParameters=(" << mRACHControlParameters << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3SystemInformationType5::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
/*
|
||||
System Information Type 5, GSM 04.08 9.1.37
|
||||
- BCCH Frequency List 10.5.2.22 M V 16
|
||||
*/
|
||||
mBCCHFrequencyList.writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType5::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "BCCHFrequencyList=(" << mBCCHFrequencyList << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void L3SystemInformationType6::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
/*
|
||||
System Information Type 6, GSM 04.08 9.1.40
|
||||
- Cell Identity 10.5.1.11 M V 2
|
||||
- Location Area Identification 10.5.1.3 M V 5
|
||||
- Cell Options (SACCH) 10.5.2.3 M V 1
|
||||
- NCC Permitted 10.5.2.27 M V 1
|
||||
*/
|
||||
mCI.writeV(dest,wp);
|
||||
mLAI.writeV(dest,wp);
|
||||
mCellOptions.writeV(dest,wp);
|
||||
mNCCPermitted.writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3SystemInformationType6::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "CI=" << mCI;
|
||||
os << " LAI=(" << mLAI << ")";
|
||||
os << " cellOptions=(" << mCellOptions << ")";
|
||||
os << " NCCPermitted=(" << mNCCPermitted << ")";
|
||||
}
|
||||
|
||||
|
||||
void L3ImmediateAssignment::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
/*
|
||||
- Page Mode 10.5.2.26 M V 1/2
|
||||
- Dedicated mode or TBF 10.5.2.25b M V 1/2
|
||||
- Channel Description 10.5.2.5 C V 3
|
||||
- Request Reference 10.5.2.30 M V 3
|
||||
- Timing Advance 10.5.2.40 M V 1
|
||||
(ignoring optional elements)
|
||||
*/
|
||||
// reverse order of 1/2-octet fields
|
||||
mDedicatedModeOrTBF.writeV(dest, wp);
|
||||
mPageMode.writeV(dest, wp);
|
||||
mChannelDescription.writeV(dest, wp);
|
||||
mRequestReference.writeV(dest, wp);
|
||||
mTimingAdvance.writeV(dest, wp);
|
||||
// No mobile allocation in non-hopping systems.
|
||||
// A zero-length LV. Just write L=0.
|
||||
dest.writeField(wp,0,8);
|
||||
}
|
||||
|
||||
|
||||
void L3ImmediateAssignment::text(ostream& os) const
|
||||
{
|
||||
os << "PageMode=("<<mPageMode<<")";
|
||||
os << " DedicatedModeOrTBF=("<<mDedicatedModeOrTBF<<")";
|
||||
os << " ChannelDescription=("<<mChannelDescription<<")";
|
||||
os << " RequestReference=("<<mRequestReference<<")";
|
||||
os << " TimingAdvance="<<mTimingAdvance;
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelRequest::text(ostream& os) const
|
||||
{
|
||||
os << "RA=" << mRA;
|
||||
os << " time=" << mTime;
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelRelease::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
mRRCause.writeV(dest, wp);
|
||||
}
|
||||
|
||||
void L3ChannelRelease::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os <<"cause="<< mRRCause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3AssignmentCommand::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
mChannelDescription.writeV(dest, wp);
|
||||
mPowerCommand.writeV(dest, wp);
|
||||
if (mHaveMode1) mMode1.writeTV(0x63,dest,wp);
|
||||
}
|
||||
|
||||
size_t L3AssignmentCommand::l2BodyLength() const
|
||||
{
|
||||
size_t len = mChannelDescription.lengthV();
|
||||
len += mPowerCommand.lengthV();
|
||||
if (mHaveMode1) len += mMode1.lengthTV();
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void L3AssignmentCommand::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os <<"channelDescription=("<<mChannelDescription<<")";
|
||||
os <<" powerCommand="<<mPowerCommand;
|
||||
if (mHaveMode1) os << " mode1=" << mMode1;
|
||||
}
|
||||
|
||||
|
||||
void L3AssignmentComplete::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mCause.parseV(src,rp);
|
||||
}
|
||||
|
||||
|
||||
void L3AssignmentComplete::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "cause=" << mCause;
|
||||
}
|
||||
|
||||
|
||||
void L3AssignmentFailure::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mCause.parseV(src,rp);
|
||||
}
|
||||
|
||||
void L3AssignmentFailure::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "cause=" << mCause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3RRStatus::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
mCause.parseV(src,rp);
|
||||
}
|
||||
|
||||
void L3RRStatus::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "cause=" << mCause;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3ImmediateAssignmentReject::writeBody(L3Frame& dest, size_t &wp) const
|
||||
{
|
||||
unsigned count = mRequestReference.size();
|
||||
assert(count<=4);
|
||||
dest.writeField(wp,0,4); // spare 1/2 octet
|
||||
mPageMode.writeV(dest,wp);
|
||||
for (unsigned i=0; i<count; i++) {
|
||||
mRequestReference[i].writeV(dest,wp);
|
||||
mWaitIndication.writeV(dest,wp);
|
||||
}
|
||||
unsigned fillCount = 4-count;
|
||||
for (unsigned i=0; i<fillCount; i++) {
|
||||
mRequestReference[count-1].writeV(dest,wp);
|
||||
mWaitIndication.writeV(dest,wp);
|
||||
}
|
||||
}
|
||||
|
||||
void L3ImmediateAssignmentReject::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "pageMode=" << mPageMode;
|
||||
os << " T3122=" << mWaitIndication;
|
||||
os << " requestReferences=(";
|
||||
for (unsigned i=0; i<mRequestReference.size(); i++) {
|
||||
os << mRequestReference[i] << ", ";
|
||||
}
|
||||
os << ")";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void L3ChannelModeModify::writeBody(L3Frame &dest, size_t& wp) const
|
||||
{
|
||||
mDescription.writeV(dest,wp);
|
||||
mMode.writeV(dest,wp);
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelModeModify::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "description=(" << mDescription << ")";
|
||||
os << " mode=(" << mMode << ")";
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelModeModifyAcknowledge::parseBody(const L3Frame &src, size_t& rp)
|
||||
{
|
||||
mDescription.parseV(src,rp);
|
||||
mMode.parseV(src,rp);
|
||||
}
|
||||
|
||||
|
||||
void L3ChannelModeModifyAcknowledge::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "description=(" << mDescription << ")";
|
||||
os << " mode=(" << mMode << ")";
|
||||
}
|
||||
|
||||
void L3MeasurementReport::parseBody(const L3Frame& frame, size_t &rp)
|
||||
{
|
||||
mResults.parseV(frame,rp);
|
||||
}
|
||||
|
||||
void L3MeasurementReport::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << mResults;
|
||||
}
|
||||
|
||||
|
||||
// L3ApplicationInformation
|
||||
|
||||
L3ApplicationInformation::~L3ApplicationInformation()
|
||||
{
|
||||
}
|
||||
|
||||
L3ApplicationInformation::L3ApplicationInformation()
|
||||
{
|
||||
}
|
||||
|
||||
L3ApplicationInformation::
|
||||
L3ApplicationInformation(BitVector& data, unsigned protocolIdentifier,
|
||||
unsigned cr, unsigned firstSegment, unsigned lastSegment)
|
||||
: L3RRMessageNRO(), mID(protocolIdentifier)
|
||||
, mFlags(cr, firstSegment, lastSegment)
|
||||
, mData(data)
|
||||
{
|
||||
}
|
||||
|
||||
void L3ApplicationInformation::writeBody( L3Frame &dest, size_t &wp ) const
|
||||
{
|
||||
/*
|
||||
- APDU ID 10.5.2.48 M V 1/2
|
||||
- APDU Flags 10.5.2.49 M V 1/2
|
||||
- APDU Data 10.5.2.50 M LV N
|
||||
*/
|
||||
// reverse order of 1/2-octet fields
|
||||
static size_t start = wp;
|
||||
LOG(DEBUG) << "L3ApplicationInformation: written " << wp - start << " bits";
|
||||
mFlags.writeV(dest, wp);
|
||||
LOG(DEBUG) << "L3ApplicationInformation: written " << wp - start << " bits";
|
||||
mID.writeV(dest, wp);
|
||||
LOG(DEBUG) << "L3ApplicationInformation: written " << wp - start << " bits";
|
||||
mData.writeLV(dest, wp);
|
||||
LOG(DEBUG) << "L3ApplicationInformation: written " << wp - start << " bits";
|
||||
}
|
||||
|
||||
|
||||
void L3ApplicationInformation::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "ID=("<<mID<<")";
|
||||
os << " Flags=("<<mFlags<<")";
|
||||
os << " Data=("<<mData<<")";
|
||||
}
|
||||
|
||||
void L3ApplicationInformation::parseBody(const L3Frame& src, size_t &rp)
|
||||
{
|
||||
// reverse order of 1/2-octet fields
|
||||
mFlags.parseV(src, rp);
|
||||
mID.parseV(src, rp);
|
||||
mData.parseLV(src, rp);
|
||||
}
|
||||
|
||||
size_t L3ApplicationInformation::l2BodyLength() const
|
||||
{
|
||||
return 1 + mData.lengthLV();
|
||||
}
|
||||
|
||||
|
||||
void L3GPRSSuspensionRequest::parseBody(const L3Frame &src, size_t& rp)
|
||||
{
|
||||
// We don't really parse this yet.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void L3ClassmarkChange::parseBody(const L3Frame &src, size_t &rp)
|
||||
{
|
||||
mClassmark.parseLV(src,rp);
|
||||
mHaveAdditionalClassmark = mAdditionalClassmark.parseTLV(0x20,src,rp);
|
||||
}
|
||||
|
||||
size_t L3ClassmarkChange::l2BodyLength() const
|
||||
{
|
||||
size_t sum = mClassmark.lengthLV();
|
||||
if (mHaveAdditionalClassmark) sum += mAdditionalClassmark.lengthTLV();
|
||||
return sum;
|
||||
}
|
||||
|
||||
void L3ClassmarkChange::text(ostream& os) const
|
||||
{
|
||||
L3RRMessage::text(os);
|
||||
os << "classmark=(" << mClassmark << ")";
|
||||
if (mHaveAdditionalClassmark)
|
||||
os << " +classmark=(" << mAdditionalClassmark << ")";
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
863
GSM/GSML3RRMessages.h
Normal file
863
GSM/GSML3RRMessages.h
Normal file
@@ -0,0 +1,863 @@
|
||||
/**@file @brief L3 Radio Resource messages, GSM 04.08 9.1. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2011 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef GSML3RRMESSAGES_H
|
||||
#define GSML3RRMESSAGES_H
|
||||
|
||||
#include "GSMCommon.h"
|
||||
#include "GSML3Message.h"
|
||||
#include "GSML3CommonElements.h"
|
||||
#include "GSML3RRElements.h"
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
/**
|
||||
This a virtual class for L3 messages in the Radio Resource protocol.
|
||||
These messages are defined in GSM 04.08 9.1.
|
||||
*/
|
||||
class L3RRMessage : public L3Message {
|
||||
|
||||
public:
|
||||
|
||||
/** RR Message MTIs, GSM 04.08 Table 10.1/3. */
|
||||
enum MessageType {
|
||||
///@name System Information
|
||||
//@{
|
||||
SystemInformationType1=0x19,
|
||||
SystemInformationType2=0x1a,
|
||||
SystemInformationType2bis=0x02,
|
||||
SystemInformationType2ter=0x03,
|
||||
SystemInformationType3=0x1b,
|
||||
SystemInformationType4=0x1c,
|
||||
SystemInformationType5=0x1d,
|
||||
SystemInformationType5bis=0x05,
|
||||
SystemInformationType5ter=0x06,
|
||||
SystemInformationType6=0x1e,
|
||||
SystemInformationType7=0x1f,
|
||||
SystemInformationType8=0x18,
|
||||
SystemInformationType9=0x04,
|
||||
SystemInformationType13=0x00,
|
||||
SystemInformationType16=0x3d,
|
||||
SystemInformationType17=0x3e,
|
||||
//@}
|
||||
///@name Channel Management
|
||||
//@{
|
||||
AssignmentCommand=0x2e,
|
||||
AssignmentComplete=0x29,
|
||||
AssignmentFailure=0x2f,
|
||||
ChannelRelease=0x0d,
|
||||
ImmediateAssignment=0x3f,
|
||||
ImmediateAssignmentExtended=0x39,
|
||||
ImmediateAssignmentReject=0x3a,
|
||||
AdditionalAssignment=0x3b,
|
||||
//@}
|
||||
///@name Paging
|
||||
//@{
|
||||
PagingRequestType1=0x21,
|
||||
PagingRequestType2=0x22,
|
||||
PagingRequestType3=0x24,
|
||||
PagingResponse=0x27,
|
||||
//@}
|
||||
///@name Handover
|
||||
//@{
|
||||
HandoverCommand=0x2b,
|
||||
//@}
|
||||
///@name ciphering
|
||||
//@{
|
||||
CipheringModeCommand=0x35,
|
||||
//@}
|
||||
///@name miscellaneous
|
||||
//@{
|
||||
ChannelModeModify=0x10,
|
||||
RRStatus=0x12,
|
||||
ChannelModeModifyAcknowledge=0x17,
|
||||
ClassmarkChange=0x16,
|
||||
ClassmarkEnquiry=0x13,
|
||||
MeasurementReport = 0x15,
|
||||
GPRSSuspensionRequest=0x34,
|
||||
//@}
|
||||
///@name special cases -- assigned >8-bit codes to avoid conflicts
|
||||
//@{
|
||||
SynchronizationChannelInformation=0x100,
|
||||
ChannelRequest=0x101,
|
||||
//@}
|
||||
///@name application information - used for RRLP
|
||||
//@{
|
||||
ApplicationInformation=0x38,
|
||||
//@}
|
||||
};
|
||||
|
||||
|
||||
L3RRMessage():L3Message() { }
|
||||
|
||||
/** Return the L3 protocol discriptor. */
|
||||
L3PD PD() const { return L3RadioResourcePD; }
|
||||
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, L3RRMessage::MessageType);
|
||||
|
||||
|
||||
|
||||
/** Subclass for L3 RR Messages with no rest octets. */
|
||||
class L3RRMessageNRO : public L3RRMessage {
|
||||
|
||||
public:
|
||||
|
||||
L3RRMessageNRO():L3RRMessage() { }
|
||||
|
||||
size_t fullBodyLength() const { return l2BodyLength(); }
|
||||
|
||||
};
|
||||
|
||||
/** Subclass for L3 RR messages with rest octets */
|
||||
class L3RRMessageRO : public L3RRMessage {
|
||||
|
||||
public:
|
||||
|
||||
L3RRMessageRO():L3RRMessage() { }
|
||||
|
||||
virtual size_t restOctetsLength() const =0;
|
||||
|
||||
size_t fullBodyLength() const { return l2BodyLength() + restOctetsLength(); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
A Factory function to return a L3RRMessage of the specified MTI.
|
||||
Returns NULL if the MTI is not supported.
|
||||
*/
|
||||
L3RRMessage* L3RRFactory(L3RRMessage::MessageType MTI);
|
||||
|
||||
/**
|
||||
Parse a complete L3 radio resource message into its object type.
|
||||
@param source The L3 bits.
|
||||
@return A pointer to a new message or NULL on failure.
|
||||
*/
|
||||
L3RRMessage* parseL3RR(const L3Frame& source);
|
||||
|
||||
|
||||
/** Paging Request Type 1, GSM 04.08 9.1.22 */
|
||||
class L3PagingRequestType1 : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
std::vector<L3MobileIdentity> mMobileIDs;
|
||||
ChannelType mChannelsNeeded[2];
|
||||
|
||||
|
||||
public:
|
||||
|
||||
L3PagingRequestType1()
|
||||
:L3RRMessageNRO()
|
||||
{
|
||||
// The empty paging request is a single untyped mobile ID.
|
||||
mMobileIDs.push_back(L3MobileIdentity());
|
||||
mChannelsNeeded[0]=AnyDCCHType;
|
||||
mChannelsNeeded[1]=AnyDCCHType;
|
||||
}
|
||||
|
||||
L3PagingRequestType1(const L3MobileIdentity& wId, ChannelType wType)
|
||||
:L3RRMessageNRO()
|
||||
{
|
||||
mMobileIDs.push_back(wId);
|
||||
mChannelsNeeded[0]=wType;
|
||||
mChannelsNeeded[1]=AnyDCCHType;
|
||||
}
|
||||
|
||||
L3PagingRequestType1(const L3MobileIdentity& wId1, ChannelType wType1,
|
||||
const L3MobileIdentity& wId2, ChannelType wType2)
|
||||
:L3RRMessageNRO()
|
||||
{
|
||||
mMobileIDs.push_back(wId1);
|
||||
mChannelsNeeded[0]=wType1;
|
||||
mMobileIDs.push_back(wId2);
|
||||
mChannelsNeeded[1]=wType2;
|
||||
}
|
||||
|
||||
unsigned chanCode(ChannelType) const;
|
||||
|
||||
int MTI() const { return PagingRequestType1; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void writeBody(L3Frame& dest, size_t& wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Paging Response, GSM 04.08 9.1.25 */
|
||||
class L3PagingResponse : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3MobileStationClassmark2 mClassmark;
|
||||
L3MobileIdentity mMobileID;
|
||||
|
||||
public:
|
||||
|
||||
const L3MobileIdentity& mobileID() const { return mMobileID; }
|
||||
|
||||
int MTI() const { return PagingResponse; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void parseBody(const L3Frame& source, size_t &rp);
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
System Information Message Type 1, GSM 04.08 9.1.31
|
||||
- Cell Channel Description 10.5.2.1b M V 16
|
||||
- RACH Control Parameters 10.5.2.29 M V 3
|
||||
*/
|
||||
class L3SystemInformationType1 : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3FrequencyList mCellChannelDescription;
|
||||
L3RACHControlParameters mRACHControlParameters;
|
||||
|
||||
public:
|
||||
|
||||
L3SystemInformationType1():L3RRMessageNRO() {}
|
||||
|
||||
void RACHControlParameters(const L3RACHControlParameters& wRACHControlParameters)
|
||||
{ mRACHControlParameters = wRACHControlParameters; }
|
||||
|
||||
void cellChannelDescription(const L3FrequencyList& wCellChannelDescription)
|
||||
{ mCellChannelDescription = wCellChannelDescription; }
|
||||
|
||||
int MTI() const { return (int)SystemInformationType1; }
|
||||
|
||||
size_t l2BodyLength() const { return 19; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
System Information Type 2, GSM 04.08 9.1.32.
|
||||
- BCCH Frequency List 10.5.2.22 M V 16
|
||||
- NCC Permitted 10.5.2.27 M V 1
|
||||
- RACH Control Parameter 10.5.2.29 M V 3
|
||||
*/
|
||||
class L3SystemInformationType2 : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3NeighborCellsDescription mBCCHFrequencyList;
|
||||
L3NCCPermitted mNCCPermitted;
|
||||
L3RACHControlParameters mRACHControlParameters;
|
||||
|
||||
public:
|
||||
|
||||
L3SystemInformationType2():L3RRMessageNRO() {}
|
||||
|
||||
void BCCHFrequencyList(const L3NeighborCellsDescription& wBCCHFrequencyList)
|
||||
{ mBCCHFrequencyList = wBCCHFrequencyList; }
|
||||
|
||||
void NCCPermitted(const L3NCCPermitted& wNCCPermitted)
|
||||
{ mNCCPermitted = wNCCPermitted; }
|
||||
|
||||
void RACHControlParameters(const L3RACHControlParameters& wRACHControlParameters)
|
||||
{ mRACHControlParameters = wRACHControlParameters; }
|
||||
|
||||
int MTI() const { return (int)SystemInformationType2; }
|
||||
|
||||
size_t l2BodyLength() const { return 20; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
System Information Type 3, GSM 04.08 9.1.35
|
||||
- Cell Identity 10.5.1.1 M V 2
|
||||
- Location Area Identification 10.5.1.3 M V 5
|
||||
- Control Channel Description 10.5.2.11 M V 3
|
||||
- Cell Options (BCCH) 10.5.2.3 M V 1
|
||||
- Cell Selection Parameters 10.5.2.4 M V 2
|
||||
- RACH Control Parameters 10.5.2.29 M V 3
|
||||
*/
|
||||
class L3SystemInformationType3 : public L3RRMessageRO {
|
||||
|
||||
private:
|
||||
|
||||
L3CellIdentity mCI;
|
||||
L3LocationAreaIdentity mLAI;
|
||||
L3ControlChannelDescription mControlChannelDescription;
|
||||
L3CellOptionsBCCH mCellOptions;
|
||||
L3CellSelectionParameters mCellSelectionParameters;
|
||||
L3RACHControlParameters mRACHControlParameters;
|
||||
|
||||
bool mHaveRestOctets;
|
||||
L3SI3RestOctets mRestOctets;
|
||||
|
||||
public:
|
||||
|
||||
L3SystemInformationType3()
|
||||
:L3RRMessageRO(),
|
||||
mHaveRestOctets(gConfig.defines("GSM.SI3RO"))
|
||||
{ }
|
||||
|
||||
void CI(const L3CellIdentity& wCI) { mCI = wCI; }
|
||||
|
||||
void LAI(const L3LocationAreaIdentity& wLAI) { mLAI = wLAI; }
|
||||
|
||||
void controlChannelDescription(const L3ControlChannelDescription& wControlChannelDescription)
|
||||
{ mControlChannelDescription = wControlChannelDescription; }
|
||||
|
||||
void cellOptions(const L3CellOptionsBCCH& wCellOptions)
|
||||
{ mCellOptions = wCellOptions; }
|
||||
|
||||
void cellSelectionParameters (const L3CellSelectionParameters& wCellSelectionParameters)
|
||||
{ mCellSelectionParameters = wCellSelectionParameters; }
|
||||
|
||||
void RACHControlParameters(const L3RACHControlParameters& wRACHControlParameters)
|
||||
{ mRACHControlParameters = wRACHControlParameters; }
|
||||
|
||||
int MTI() const { return (int)SystemInformationType3; }
|
||||
|
||||
size_t l2BodyLength() const { return 16; }
|
||||
size_t restOctetsLength() const { return mRestOctets.lengthV(); }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
System Information Type 4, GSM 04.08 9.1.36
|
||||
- Location Area Identification 10.5.1.3 M V 5
|
||||
- Cell Selection Parameters 10.5.2.4 M V 2
|
||||
- RACH Control Parameters 10.5.2.29 M V 3
|
||||
*/
|
||||
class L3SystemInformationType4 : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3LocationAreaIdentity mLAI;
|
||||
L3CellSelectionParameters mCellSelectionParameters;
|
||||
L3RACHControlParameters mRACHControlParameters;
|
||||
|
||||
public:
|
||||
|
||||
L3SystemInformationType4();
|
||||
|
||||
//void LAI(const L3LocationAreaIdentity& wLAI) { mLAI = wLAI; }
|
||||
|
||||
//void cellSelectionParameters (const L3CellSelectionParameters& wCellSelectionParameters)
|
||||
// { mCellSelectionParameters = wCellSelectionParameters; }
|
||||
|
||||
// void RACHControlParameters(const L3RACHControlParameters& wRACHControlParameters)
|
||||
// { mRACHControlParameters = wRACHControlParameters; }
|
||||
|
||||
int MTI() const { return (int)SystemInformationType4; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
System Information Type 5, GSM 04.08 9.1.37
|
||||
- BCCH Frequency List 10.5.2.22 M V 16
|
||||
*/
|
||||
class L3SystemInformationType5 : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3NeighborCellsDescription mBCCHFrequencyList;
|
||||
|
||||
public:
|
||||
|
||||
L3SystemInformationType5():L3RRMessageNRO() { }
|
||||
|
||||
void BCCHFrequencyList(const L3NeighborCellsDescription& wBCCHFrequencyList)
|
||||
{ mBCCHFrequencyList = wBCCHFrequencyList; }
|
||||
|
||||
int MTI() const { return (int)SystemInformationType5; }
|
||||
|
||||
size_t l2BodyLength() const { return 16; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
System Information Type 6, GSM 04.08 9.1.40
|
||||
- Cell Identity 10.5.1.11 M V 2
|
||||
- Location Area Identification 10.5.1.3 M V 5
|
||||
- Cell Options (SACCH) 10.5.2.3 M V 1
|
||||
- NCC Permitted 10.5.2.27 M V 1
|
||||
*/
|
||||
class L3SystemInformationType6 : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3CellIdentity mCI;
|
||||
L3LocationAreaIdentity mLAI;
|
||||
L3CellOptionsSACCH mCellOptions;
|
||||
L3NCCPermitted mNCCPermitted;
|
||||
|
||||
public:
|
||||
|
||||
L3SystemInformationType6():L3RRMessageNRO() {}
|
||||
|
||||
void CI(const L3CellIdentity& wCI) { mCI = wCI; }
|
||||
|
||||
void LAI(const L3LocationAreaIdentity& wLAI) { mLAI = wLAI; }
|
||||
|
||||
void cellOptions(const L3CellOptionsSACCH& wCellOptions)
|
||||
{ mCellOptions = wCellOptions; }
|
||||
|
||||
void NCCPermitted(const L3NCCPermitted& wNCCPermitted)
|
||||
{ mNCCPermitted = wNCCPermitted; }
|
||||
|
||||
int MTI() const { return (int)SystemInformationType6; }
|
||||
|
||||
size_t l2BodyLength() const { return 9; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Immediate Assignment, GSM 04.08 9.1.18 */
|
||||
class L3ImmediateAssignment : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3PageMode mPageMode;
|
||||
L3DedicatedModeOrTBF mDedicatedModeOrTBF;
|
||||
L3RequestReference mRequestReference;
|
||||
L3ChannelDescription mChannelDescription;
|
||||
L3TimingAdvance mTimingAdvance;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
L3ImmediateAssignment(
|
||||
const L3RequestReference& wRequestReference,
|
||||
const L3ChannelDescription& wChannelDescription,
|
||||
const L3TimingAdvance& wTimingAdvance = L3TimingAdvance(0))
|
||||
:L3RRMessageNRO(),
|
||||
mRequestReference(wRequestReference),
|
||||
mChannelDescription(wChannelDescription),
|
||||
mTimingAdvance(wTimingAdvance)
|
||||
{}
|
||||
|
||||
int MTI() const { return (int)ImmediateAssignment; }
|
||||
size_t l2BodyLength() const { return 9; }
|
||||
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** Immediate Assignment Reject, GSM 04.08 9.1.20 */
|
||||
class L3ImmediateAssignmentReject : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3PageMode mPageMode;
|
||||
std::vector<L3RequestReference> mRequestReference;
|
||||
L3WaitIndication mWaitIndication; ///< All entries get the same wait indication.
|
||||
|
||||
public:
|
||||
|
||||
|
||||
L3ImmediateAssignmentReject(const L3RequestReference& wRequestReference, unsigned seconds)
|
||||
:L3RRMessageNRO(),
|
||||
mWaitIndication(seconds)
|
||||
{ mRequestReference.push_back(wRequestReference); }
|
||||
|
||||
int MTI() const { return (int)ImmediateAssignmentReject; }
|
||||
|
||||
size_t l2BodyLength() const { return 17; }
|
||||
void writeBody(L3Frame &dest, size_t &wp) const;
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.7 */
|
||||
class L3ChannelRelease : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3RRCause mRRCause;
|
||||
|
||||
public:
|
||||
|
||||
/** The default cause is 0x0, "normal event". */
|
||||
L3ChannelRelease(const L3RRCause& cause = L3RRCause(0x0))
|
||||
:L3RRMessageNRO(),mRRCause(cause)
|
||||
{}
|
||||
|
||||
int MTI() const { return (int) ChannelRelease; }
|
||||
|
||||
size_t l2BodyLength() const { return mRRCause.lengthV(); }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
GSM 04.08 9.1.8
|
||||
The channel request message is special because the timestamp
|
||||
is implied by the receive time but not actually present in
|
||||
the message.
|
||||
This messages has no parse or write methods, but is used to
|
||||
transfer information from L1 to the control layer.
|
||||
*/
|
||||
class L3ChannelRequest : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
unsigned mRA; ///< request reference
|
||||
GSM::Time mTime; ///< receive timestamp
|
||||
|
||||
public:
|
||||
|
||||
L3ChannelRequest(unsigned wRA, const GSM::Time& wTime)
|
||||
:L3RRMessageNRO(),
|
||||
mRA(wRA), mTime(wTime)
|
||||
{}
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
unsigned RA() const { return mRA; }
|
||||
const GSM::Time& time() const { return mTime; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return (int)ChannelRequest; }
|
||||
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void parseBody(const L3Frame&, size_t&);
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.2 */
|
||||
class L3AssignmentCommand : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3ChannelDescription mChannelDescription;
|
||||
L3PowerCommand mPowerCommand;
|
||||
|
||||
bool mHaveMode1;
|
||||
L3ChannelMode mMode1;
|
||||
|
||||
public:
|
||||
|
||||
L3AssignmentCommand(const L3ChannelDescription& wChannelDescription,
|
||||
const L3ChannelMode& wMode1 )
|
||||
:L3RRMessageNRO(),
|
||||
mChannelDescription(wChannelDescription),
|
||||
mHaveMode1(true),mMode1(wMode1)
|
||||
{}
|
||||
|
||||
L3AssignmentCommand(const L3ChannelDescription& wChannelDescription)
|
||||
:L3RRMessageNRO(),
|
||||
mChannelDescription(wChannelDescription),
|
||||
mHaveMode1(false)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
int MTI() const { return (int) AssignmentCommand; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void writeBody( L3Frame &dest, size_t &wp ) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.3 */
|
||||
class L3AssignmentComplete : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3RRCause mCause;
|
||||
|
||||
public:
|
||||
|
||||
///@name Accessors.
|
||||
//@{
|
||||
const L3RRCause& cause() const { return mCause; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return (int) AssignmentComplete; }
|
||||
|
||||
size_t l2BodyLength() const { return 1; }
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.3 */
|
||||
class L3AssignmentFailure : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3RRCause mCause;
|
||||
|
||||
public:
|
||||
|
||||
///@name Accessors.
|
||||
//@{
|
||||
const L3RRCause& cause() const { return mCause; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return (int) AssignmentFailure; }
|
||||
|
||||
size_t l2BodyLength() const { return 1; }
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.29 */
|
||||
class L3RRStatus : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3RRCause mCause;
|
||||
|
||||
public:
|
||||
|
||||
///@name Accessors.
|
||||
//@{
|
||||
const L3RRCause& cause() const { return mCause; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return (int) RRStatus; }
|
||||
|
||||
size_t l2BodyLength() const { return 1; }
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.5 */
|
||||
class L3ChannelModeModify : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3ChannelDescription mDescription;
|
||||
L3ChannelMode mMode;
|
||||
|
||||
public:
|
||||
|
||||
L3ChannelModeModify(const L3ChannelDescription& wDescription,
|
||||
const L3ChannelMode& wMode)
|
||||
:L3RRMessageNRO(),
|
||||
mDescription(wDescription),
|
||||
mMode(wMode)
|
||||
{}
|
||||
|
||||
int MTI() const { return (int) ChannelModeModify; }
|
||||
|
||||
size_t l2BodyLength() const
|
||||
{ return mDescription.lengthV() + mMode.lengthV(); }
|
||||
|
||||
void writeBody(L3Frame&, size_t&) const;
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.6 */
|
||||
class L3ChannelModeModifyAcknowledge : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3ChannelDescription mDescription;
|
||||
L3ChannelMode mMode;
|
||||
|
||||
public:
|
||||
|
||||
const L3ChannelDescription& description() const { return mDescription; }
|
||||
const L3ChannelMode& mode() const { return mMode; }
|
||||
|
||||
int MTI() const { return (int) ChannelModeModifyAcknowledge; }
|
||||
|
||||
size_t l2BodyLength() const
|
||||
{ return mDescription.lengthV() + mMode.lengthV(); }
|
||||
|
||||
void parseBody(const L3Frame&, size_t&);
|
||||
void text(std::ostream&) const;
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.21 */
|
||||
class L3MeasurementReport : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
// This is a placeholder. We don't really parse anything yet.
|
||||
L3MeasurementResults mResults;
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return (int) MeasurementReport; }
|
||||
size_t l2BodyLength() const { return mResults.lengthV(); }
|
||||
|
||||
void parseBody(const L3Frame&, size_t&);
|
||||
void text(std::ostream&) const;
|
||||
|
||||
const L3MeasurementResults results() const { return mResults; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.53 */
|
||||
class L3ApplicationInformation : public L3RRMessageNRO {
|
||||
|
||||
private:
|
||||
|
||||
L3APDUID mID;
|
||||
L3APDUFlags mFlags;
|
||||
L3APDUData mData;
|
||||
|
||||
public:
|
||||
|
||||
~L3ApplicationInformation();
|
||||
|
||||
L3ApplicationInformation();
|
||||
// data is the first argument to allow the rest to default, since that is the common case,
|
||||
// sending a single (cr=0, first=0, last=0) RRLP (id=0) APDU wrapped in a ApplicationInformation L3 packet.
|
||||
L3ApplicationInformation(BitVector& data, unsigned protocolIdentifier=0,
|
||||
unsigned cr=0, unsigned firstSegment=0, unsigned lastSegment=0);
|
||||
|
||||
///@name Accessors.
|
||||
//@{
|
||||
const L3APDUID& id() const { return mID; }
|
||||
const L3APDUFlags& flags() const { return mFlags; }
|
||||
const L3APDUData& data() const { return mData; }
|
||||
//@}
|
||||
|
||||
int MTI() const { return (int) ApplicationInformation; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void writeBody( L3Frame&, size_t&) const;
|
||||
void parseBody( const L3Frame &src, size_t &rp );
|
||||
void text(std::ostream&) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.13b */
|
||||
class L3GPRSSuspensionRequest : public L3RRMessageNRO {
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return (int) GPRSSuspensionRequest; }
|
||||
|
||||
size_t l2BodyLength() const { return 11; }
|
||||
|
||||
void parseBody(const L3Frame&, size_t&);
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.12 */
|
||||
class L3ClassmarkEnquiry : public L3RRMessageNRO {
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return (int) ClassmarkEnquiry; }
|
||||
|
||||
size_t l2BodyLength() const { return 0; }
|
||||
void writeBody(L3Frame&, size_t&) const {}
|
||||
};
|
||||
|
||||
|
||||
/** GSM 04.08 9.1.11 */
|
||||
class L3ClassmarkChange : public L3RRMessageNRO {
|
||||
|
||||
protected:
|
||||
|
||||
L3MobileStationClassmark2 mClassmark;
|
||||
bool mHaveAdditionalClassmark;
|
||||
L3MobileStationClassmark3 mAdditionalClassmark;
|
||||
|
||||
public:
|
||||
|
||||
int MTI() const { return (int) ClassmarkChange; }
|
||||
|
||||
size_t l2BodyLength() const;
|
||||
void parseBody(const L3Frame&, size_t&);
|
||||
void text(std::ostream&) const;
|
||||
|
||||
const L3MobileStationClassmark2& classmark() const { return mClassmark; }
|
||||
};
|
||||
|
||||
|
||||
} // GSM
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
412
GSM/GSMLogicalChannel.cpp
Normal file
412
GSM/GSMLogicalChannel.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
/**@file Logical Channel. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
* Copyright 2011 Range Networks, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "GSML3RRElements.h"
|
||||
#include "GSML3Message.h"
|
||||
#include "GSML3RRMessages.h"
|
||||
#include "GSMLogicalChannel.h"
|
||||
#include "GSMConfig.h"
|
||||
|
||||
#include <TransactionTable.h>
|
||||
#include <SMSControl.h>
|
||||
#include <ControlCommon.h>
|
||||
|
||||
#include <Logger.h>
|
||||
#undef WARNING
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
void LogicalChannel::open()
|
||||
{
|
||||
LOG(INFO);
|
||||
if (mSACCH) mSACCH->open();
|
||||
if (mL1) mL1->open();
|
||||
for (int s=0; s<4; s++) {
|
||||
if (mL2[s]) mL2[s]->open();
|
||||
}
|
||||
// Empty any stray transactions in the FIFO from the SIP layer.
|
||||
while (true) {
|
||||
Control::TransactionEntry *trans = mTransactionFIFO.readNoBlock();
|
||||
if (!trans) break;
|
||||
LOG(WARNING) << "flushing stray transaction " << *trans;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LogicalChannel::connect()
|
||||
{
|
||||
mMux.downstream(mL1);
|
||||
if (mL1) mL1->upstream(&mMux);
|
||||
for (int s=0; s<4; s++) {
|
||||
mMux.upstream(mL2[s],s);
|
||||
if (mL2[s]) mL2[s]->downstream(&mMux);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LogicalChannel::downstream(ARFCNManager* radio)
|
||||
{
|
||||
assert(mL1);
|
||||
mL1->downstream(radio);
|
||||
if (mSACCH) mSACCH->downstream(radio);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Serialize and send an L3Message with a given primitive.
|
||||
void LogicalChannel::send(const L3Message& msg,
|
||||
const GSM::Primitive& prim,
|
||||
unsigned SAPI)
|
||||
{
|
||||
LOG(INFO) << "L3 SAP" << SAPI << " sending " << msg;
|
||||
send(L3Frame(msg,prim), SAPI);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
CCCHLogicalChannel::CCCHLogicalChannel(const TDMAMapping& wMapping)
|
||||
:mRunning(false)
|
||||
{
|
||||
mL1 = new CCCHL1FEC(wMapping);
|
||||
mL2[0] = new CCCHL2;
|
||||
connect();
|
||||
}
|
||||
|
||||
|
||||
void CCCHLogicalChannel::open()
|
||||
{
|
||||
LogicalChannel::open();
|
||||
if (!mRunning) {
|
||||
mRunning=true;
|
||||
mServiceThread.start((void*(*)(void*))CCCHLogicalChannelServiceLoopAdapter,this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CCCHLogicalChannel::serviceLoop()
|
||||
{
|
||||
// build the idle frame
|
||||
static const L3PagingRequestType1 filler;
|
||||
static const L3Frame idleFrame(filler,UNIT_DATA);
|
||||
// prime the first idle frame
|
||||
LogicalChannel::send(idleFrame);
|
||||
// run the loop
|
||||
while (true) {
|
||||
L3Frame* frame = mQ.read();
|
||||
if (frame) {
|
||||
LogicalChannel::send(*frame);
|
||||
OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending " << *frame;
|
||||
delete frame;
|
||||
}
|
||||
if (mQ.size()==0) {
|
||||
LogicalChannel::send(idleFrame);
|
||||
OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending idle frame";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *GSM::CCCHLogicalChannelServiceLoopAdapter(CCCHLogicalChannel* chan)
|
||||
{
|
||||
chan->serviceLoop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
L3ChannelDescription LogicalChannel::channelDescription() const
|
||||
{
|
||||
// In some debug cases, L1 may not exist, so we fake this information.
|
||||
if (mL1==NULL) return L3ChannelDescription(TDMA_MISC,0,0,0);
|
||||
|
||||
// In normal cases, we get this information from L1.
|
||||
return L3ChannelDescription(
|
||||
mL1->typeAndOffset(),
|
||||
mL1->TN(),
|
||||
mL1->TSC(),
|
||||
mL1->ARFCN()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SDCCHLogicalChannel::SDCCHLogicalChannel(
|
||||
unsigned wTN,
|
||||
const CompleteMapping& wMapping)
|
||||
{
|
||||
mL1 = new SDCCHL1FEC(wTN,wMapping.LCH());
|
||||
// SAP0 is RR/MM/CC, SAP3 is SMS
|
||||
// SAP1 and SAP2 are not used.
|
||||
L2LAPDm *SAP0L2 = new SDCCHL2(1,0);
|
||||
L2LAPDm *SAP3L2 = new SDCCHL2(1,3);
|
||||
LOG(DEBUG) << "LAPDm pairs SAP0=" << SAP0L2 << " SAP3=" << SAP3L2;
|
||||
SAP3L2->master(SAP0L2);
|
||||
mL2[0] = SAP0L2;
|
||||
mL2[3] = SAP3L2;
|
||||
mSACCH = new SACCHLogicalChannel(wTN,wMapping.SACCH());
|
||||
connect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SACCHLogicalChannel::SACCHLogicalChannel(
|
||||
unsigned wTN,
|
||||
const MappingPair& wMapping)
|
||||
: mRunning(false)
|
||||
{
|
||||
mSACCHL1 = new SACCHL1FEC(wTN,wMapping);
|
||||
mL1 = mSACCHL1;
|
||||
// SAP0 is RR, SAP3 is SMS
|
||||
// SAP1 and SAP2 are not used.
|
||||
mL2[0] = new SACCHL2(1,0);
|
||||
mL2[3] = new SACCHL2(1,3);
|
||||
connect();
|
||||
assert(mSACCH==NULL);
|
||||
}
|
||||
|
||||
|
||||
void SACCHLogicalChannel::open()
|
||||
{
|
||||
LogicalChannel::open();
|
||||
if (!mRunning) {
|
||||
mRunning=true;
|
||||
mServiceThread.start((void*(*)(void*))SACCHLogicalChannelServiceLoopAdapter,this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
L3Message* processSACCHMessage(L3Frame *l3frame)
|
||||
{
|
||||
if (!l3frame) return NULL;
|
||||
LOG(DEBUG) << *l3frame;
|
||||
Primitive prim = l3frame->primitive();
|
||||
if ((prim!=DATA) && (prim!=UNIT_DATA)) {
|
||||
LOG(INFO) << "non-data primitive " << prim;
|
||||
return NULL;
|
||||
}
|
||||
// FIXME -- Why, again, do we need to do this?
|
||||
// L3Frame realFrame = l3frame->segment(24, l3frame->size()-24);
|
||||
L3Message* message = parseL3(*l3frame);
|
||||
if (!message) {
|
||||
LOG(WARNING) << "SACCH recevied unparsable L3 frame " << *l3frame;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
void SACCHLogicalChannel::serviceLoop()
|
||||
{
|
||||
// run the loop
|
||||
unsigned count = 0;
|
||||
while (true) {
|
||||
|
||||
// Throttle back if not active.
|
||||
if (!active()) {
|
||||
OBJLOG(DEBUG) << "SACCH sleeping";
|
||||
sleepFrames(51);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO SMS -- Check to see if the tx queues are empty. If so, send SI5/6,
|
||||
// otherwise sleep and continue;
|
||||
|
||||
// Send alternating SI5/SI6.
|
||||
OBJLOG(DEBUG) << "sending SI5/6 on SACCH";
|
||||
if (count%2) LogicalChannel::send(gBTS.SI5Frame());
|
||||
else LogicalChannel::send(gBTS.SI6Frame());
|
||||
count++;
|
||||
|
||||
// Receive inbound messages.
|
||||
// This read loop flushes stray reports quickly.
|
||||
while (true) {
|
||||
|
||||
OBJLOG(DEBUG) << "polling SACCH for inbound messages";
|
||||
bool nothing = true;
|
||||
|
||||
// Process SAP0 -- RR Measurement reports
|
||||
L3Frame *rrFrame = LogicalChannel::recv(0,0);
|
||||
if (rrFrame) nothing=false;
|
||||
L3Message* rrMessage = processSACCHMessage(rrFrame);
|
||||
delete rrFrame;
|
||||
if (rrMessage) {
|
||||
L3MeasurementReport* measurement = dynamic_cast<L3MeasurementReport*>(rrMessage);
|
||||
if (measurement) {
|
||||
mMeasurementResults = measurement->results();
|
||||
OBJLOG(DEBUG) << "SACCH measurement report " << mMeasurementResults;
|
||||
// Add the measurement results to the table
|
||||
// Note that the typeAndOffset of a SACCH match the host channel.
|
||||
gPhysStatus.setPhysical(this, mMeasurementResults);
|
||||
} else {
|
||||
OBJLOG(NOTICE) << "SACCH SAP0 sent unaticipated message " << rrMessage;
|
||||
}
|
||||
delete rrMessage;
|
||||
}
|
||||
|
||||
// Process SAP3 -- SMS
|
||||
L3Frame *smsFrame = LogicalChannel::recv(0,3);
|
||||
if (smsFrame) nothing=false;
|
||||
L3Message* smsMessage = processSACCHMessage(smsFrame);
|
||||
delete smsFrame;
|
||||
if (smsMessage) {
|
||||
const SMS::CPData* cpData = dynamic_cast<const SMS::CPData*>(smsMessage);
|
||||
if (cpData) {
|
||||
OBJLOG(INFO) << "SMS CPDU " << *cpData;
|
||||
Control::TransactionEntry *transaction = gTransactionTable.find(this);
|
||||
try {
|
||||
if (transaction) {
|
||||
Control::InCallMOSMSController(cpData,transaction,this);
|
||||
} else {
|
||||
OBJLOG(WARNING) << "in-call MOSMS CP-DATA with no corresponding transaction";
|
||||
}
|
||||
} catch (Control::ControlLayerException e) {
|
||||
//LogicalChannel::send(RELEASE,3);
|
||||
gTransactionTable.remove(e.transactionID());
|
||||
}
|
||||
} else {
|
||||
OBJLOG(NOTICE) << "SACCH SAP3 sent unaticipated message " << rrMessage;
|
||||
}
|
||||
delete smsMessage;
|
||||
}
|
||||
|
||||
// Anything from the SIP side?
|
||||
// MTSMS (delivery from SIP to the MS)
|
||||
Control::TransactionEntry *sipTransaction = mTransactionFIFO.readNoBlock();
|
||||
if (sipTransaction) {
|
||||
OBJLOG(INFO) << "SIP-side transaction: " << sipTransaction;
|
||||
assert(sipTransaction->service() == L3CMServiceType::MobileTerminatedShortMessage);
|
||||
try {
|
||||
Control::MTSMSController(sipTransaction,this);
|
||||
} catch (Control::ControlLayerException e) {
|
||||
//LogicalChannel::send(RELEASE,3);
|
||||
gTransactionTable.remove(e.transactionID());
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing happened?
|
||||
if (nothing) break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void *GSM::SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel* chan)
|
||||
{
|
||||
chan->serviceLoop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// These have to go into the .cpp file to prevent an illegal forward reference.
|
||||
void LogicalChannel::setPhy(float wRSSI, float wTimingError)
|
||||
{ assert(mSACCH); mSACCH->setPhy(wRSSI,wTimingError); }
|
||||
void LogicalChannel::setPhy(const LogicalChannel& other)
|
||||
{ assert(mSACCH); mSACCH->setPhy(*other.SACCH()); }
|
||||
float LogicalChannel::RSSI() const
|
||||
{ assert(mSACCH); return mSACCH->RSSI(); }
|
||||
float LogicalChannel::timingError() const
|
||||
{ assert(mSACCH); return mSACCH->timingError(); }
|
||||
int LogicalChannel::actualMSPower() const
|
||||
{ assert(mSACCH); return mSACCH->actualMSPower(); }
|
||||
int LogicalChannel::actualMSTiming() const
|
||||
{ assert(mSACCH); return mSACCH->actualMSTiming(); }
|
||||
|
||||
|
||||
|
||||
TCHFACCHLogicalChannel::TCHFACCHLogicalChannel(
|
||||
unsigned wTN,
|
||||
const CompleteMapping& wMapping)
|
||||
{
|
||||
mTCHL1 = new TCHFACCHL1FEC(wTN,wMapping.LCH());
|
||||
mL1 = mTCHL1;
|
||||
// SAP0 is RR/MM/CC, SAP3 is SMS
|
||||
// SAP1 and SAP2 are not used.
|
||||
mL2[0] = new FACCHL2(1,0);
|
||||
mL2[3] = new FACCHL2(1,3);
|
||||
mSACCH = new SACCHLogicalChannel(wTN,wMapping.SACCH());
|
||||
connect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool LogicalChannel::waitForPrimitive(Primitive primitive, unsigned timeout_ms)
|
||||
{
|
||||
bool waiting = true;
|
||||
while (waiting) {
|
||||
L3Frame *req = recv(timeout_ms);
|
||||
if (req==NULL) {
|
||||
LOG(NOTICE) << "timeout at uptime " << gBTS.uptime() << " frame " << gBTS.time();
|
||||
return false;
|
||||
}
|
||||
waiting = (req->primitive()!=primitive);
|
||||
delete req;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LogicalChannel::waitForPrimitive(Primitive primitive)
|
||||
{
|
||||
bool waiting = true;
|
||||
while (waiting) {
|
||||
L3Frame *req = recv();
|
||||
if (req==NULL) continue;
|
||||
waiting = (req->primitive()!=primitive);
|
||||
delete req;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ostream& GSM::operator<<(ostream& os, const LogicalChannel& chan)
|
||||
{
|
||||
os << chan.descriptiveString();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
void LogicalChannel::addTransaction(Control::TransactionEntry *transaction)
|
||||
{
|
||||
assert(transaction->channel()==this);
|
||||
mTransactionFIFO.write(transaction);
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
||||
548
GSM/GSMLogicalChannel.h
Normal file
548
GSM/GSMLogicalChannel.h
Normal file
@@ -0,0 +1,548 @@
|
||||
/**@file Logical Channel. */
|
||||
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
* Copyright 2010 Kestrel Signal Processing, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses;
|
||||
* see the COPYING file in the main directory for licensing
|
||||
* information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef LOGICALCHANNEL_H
|
||||
#define LOGICALCHANNEL_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "GSML1FEC.h"
|
||||
#include "GSMSAPMux.h"
|
||||
#include "GSML2LAPDm.h"
|
||||
#include "GSML3RRElements.h"
|
||||
#include "GSMTDMA.h"
|
||||
#include <TransactionTable.h>
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
class ARFCNManager;
|
||||
class UDPSocket;
|
||||
|
||||
|
||||
//namespace Control {
|
||||
//class TransactionEntry;
|
||||
//};
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
typedef InterthreadQueue<Control::TransactionEntry> TransactionFIFO;
|
||||
|
||||
class SACCHLogicalChannel;
|
||||
class L3Message;
|
||||
class L3RRMessage;
|
||||
|
||||
|
||||
/**
|
||||
A complete logical channel.
|
||||
Includes processors for L1, L2, L3, as needed.
|
||||
The layered structure of GSM is defined in GSM 04.01 7, as well as many other places.
|
||||
The concept of the logical channel and the channel types are defined in GSM 04.03.
|
||||
This is virtual class; specific channel types are subclasses.
|
||||
*/
|
||||
class LogicalChannel {
|
||||
|
||||
protected:
|
||||
|
||||
/**@name Contained layer processors. */
|
||||
//@{
|
||||
L1FEC *mL1; ///< L1 forward error correction
|
||||
SAPMux mMux; ///< service access point multiplex
|
||||
L2DL *mL2[4]; ///< data link layer state machines, one per SAP
|
||||
//@}
|
||||
|
||||
SACCHLogicalChannel *mSACCH; ///< The associated SACCH, if any.
|
||||
|
||||
/**
|
||||
A FIFO of inbound transactions intiated in the SIP layers on an already-active channel.
|
||||
Unlike most interthread FIFOs, do *NOT* delete the pointers that come out of it.
|
||||
*/
|
||||
TransactionFIFO mTransactionFIFO;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Blank initializer just nulls the pointers.
|
||||
Specific sub-class initializers allocate new components as needed.
|
||||
*/
|
||||
LogicalChannel()
|
||||
:mL1(NULL),mSACCH(NULL)
|
||||
{
|
||||
for (int i=0; i<4; i++) mL2[i]=NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** The destructor doesn't do anything since logical channels should not be destroyed. */
|
||||
virtual ~LogicalChannel() {};
|
||||
|
||||
|
||||
/**@name Accessors. */
|
||||
//@{
|
||||
SACCHLogicalChannel* SACCH() { return mSACCH; }
|
||||
const SACCHLogicalChannel* SACCH() const { return mSACCH; }
|
||||
L3ChannelDescription channelDescription() const;
|
||||
//@}
|
||||
|
||||
|
||||
/**@name Pass-throughs. */
|
||||
//@{
|
||||
|
||||
/** Set L1 physical parameters from a RACH or pre-exsting channel. */
|
||||
virtual void setPhy(float wRSSI, float wTimingError);
|
||||
|
||||
/* Set L1 physical parameters from an existing logical channel. */
|
||||
virtual void setPhy(const LogicalChannel&);
|
||||
|
||||
/**@name L3 interfaces */
|
||||
//@{
|
||||
|
||||
/**
|
||||
Read an L3Frame from SAP0 uplink, blocking, with timeout.
|
||||
The caller is responsible for deleting the returned pointer.
|
||||
The default 15 second timeout works for most L3 operations.
|
||||
@param timeout_ms A read timeout in milliseconds.
|
||||
@param SAPI The service access point indicator from which to read.
|
||||
@return A pointer to an L3Frame, to be deleted by the caller, or NULL on timeout.
|
||||
*/
|
||||
virtual L3Frame * recv(unsigned timeout_ms = 15000, unsigned SAPI=0)
|
||||
{ assert(mL2[SAPI]); return mL2[SAPI]->readHighSide(timeout_ms); }
|
||||
|
||||
/**
|
||||
Send an L3Frame on downlink.
|
||||
This method will block until the message is transferred to the transceiver.
|
||||
@param frame The L3Frame to be sent.
|
||||
@param SAPI The service access point indicator.
|
||||
*/
|
||||
virtual void send(const L3Frame& frame, unsigned SAPI=0)
|
||||
{
|
||||
assert(mL2[SAPI]);
|
||||
LOG(DEBUG) << "SAP"<< SAPI << " " << frame;
|
||||
mL2[SAPI]->writeHighSide(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
Send "naked" primitive down the channel.
|
||||
@param prim The primitive to send.
|
||||
@pram SAPI The service access point on which to send.
|
||||
*/
|
||||
virtual void send(const GSM::Primitive& prim, unsigned SAPI=0)
|
||||
{ assert(mL2[SAPI]); mL2[SAPI]->writeHighSide(L3Frame(prim)); }
|
||||
|
||||
/**
|
||||
Initiate a transaction from the SIP side on an already-active channel.
|
||||
(*/
|
||||
virtual void addTransaction(Control::TransactionEntry* transaction);
|
||||
|
||||
/**
|
||||
Serialize and send an L3Message with a given primitive.
|
||||
@param msg The L3 message.
|
||||
@param prim The primitive to use.
|
||||
*/
|
||||
virtual void send(const L3Message& msg,
|
||||
const GSM::Primitive& prim=DATA,
|
||||
unsigned SAPI=0);
|
||||
|
||||
/**
|
||||
Block on a channel until a given primitive arrives.
|
||||
Any payload is discarded. Block indefinitely, no timeout.
|
||||
@param primitive The primitive to wait for.
|
||||
*/
|
||||
void waitForPrimitive(GSM::Primitive primitive);
|
||||
|
||||
/**
|
||||
Block on a channel until a given primitive arrives.
|
||||
Any payload is discarded. Block indefinitely, no timeout.
|
||||
@param primitive The primitive to wait for.
|
||||
@param timeout_ms The timeout in milliseconds.
|
||||
@return True on success, false on timeout.
|
||||
*/
|
||||
bool waitForPrimitive(GSM::Primitive primitive, unsigned timeout_ms);
|
||||
|
||||
|
||||
|
||||
//@} // L3
|
||||
|
||||
/**@name L1 interfaces */
|
||||
//@{
|
||||
|
||||
/** Write a received radio burst into the "low" side of the channel. */
|
||||
virtual void writeLowSide(const RxBurst& burst) { assert(mL1); mL1->writeLowSide(burst); }
|
||||
|
||||
/** Return true if the channel is safely abandoned (closed or orphaned). */
|
||||
bool recyclable() const { assert(mL1); return mL1->recyclable(); }
|
||||
|
||||
/** Return true if the channel is active. */
|
||||
bool active() const { assert(mL1); return mL1->active(); }
|
||||
|
||||
/** The TDMA parameters for the transmit side. */
|
||||
const TDMAMapping& txMapping() const { assert(mL1); return mL1->txMapping(); }
|
||||
|
||||
/** The TDMAParameters for the receive side. */
|
||||
const TDMAMapping& rcvMapping() const { assert(mL1); return mL1->rcvMapping(); }
|
||||
|
||||
/** GSM 04.08 10.5.2.5 type and offset code. */
|
||||
TypeAndOffset typeAndOffset() const { assert(mL1); return mL1->typeAndOffset(); }
|
||||
|
||||
/** ARFCN */ /* TODO: Use this, or when obtaining the physical info use ARFCN from a diff location? */
|
||||
unsigned ARFCN() const { assert(mL1); return mL1->ARFCN(); }
|
||||
|
||||
/**@name Channel stats from the physical layer */
|
||||
//@{
|
||||
/** Carrier index. */
|
||||
unsigned CN() const { return 0; }
|
||||
/** Slot number. */
|
||||
unsigned TN() const { assert(mL1); return mL1->TN(); }
|
||||
/** Receive FER. */
|
||||
float FER() const { assert(mL1); return mL1->FER(); }
|
||||
/** RSSI wrt full scale. */
|
||||
virtual float RSSI() const;
|
||||
/** Uplink timing error. */
|
||||
virtual float timingError() const;
|
||||
/** Actual MS uplink power. */
|
||||
virtual int actualMSPower() const;
|
||||
/** Actual MS uplink timing advance. */
|
||||
virtual int actualMSTiming() const;
|
||||
//@}
|
||||
|
||||
//@} // L1
|
||||
|
||||
/**@name L2 passthroughs */
|
||||
//@{
|
||||
unsigned N200() const { assert(mL2[0]); return mL2[0]->N200(); }
|
||||
unsigned T200() const { assert(mL2[0]); return mL2[0]->T200(); }
|
||||
bool multiframeMode(unsigned SAPI) const
|
||||
{ assert(mL2[SAPI]); return mL2[SAPI]->multiframeMode(); }
|
||||
//@}
|
||||
|
||||
//@} // passthrough
|
||||
|
||||
|
||||
/** Connect an ARFCN manager to link L1FEC to the radio. */
|
||||
void downstream(ARFCNManager* radio);
|
||||
|
||||
/** Return the channel type. */
|
||||
virtual ChannelType type() const =0;
|
||||
|
||||
/**
|
||||
Make the channel ready for a new transaction.
|
||||
The channel is closed with primitives from L3.
|
||||
*/
|
||||
virtual void open();
|
||||
|
||||
/**@ Debuging functions: will give access to all intermediate layers. */
|
||||
//@{
|
||||
L2DL * debugGetL2(unsigned sapi){ return mL2[sapi]; }
|
||||
L1FEC * debugGetL1(){ return mL1; }
|
||||
//@}
|
||||
|
||||
const char* descriptiveString() const { return mL1->descriptiveString(); }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
Make the normal inter-layer connections.
|
||||
Should be called from inside the constructor after
|
||||
the channel components are created.
|
||||
*/
|
||||
virtual void connect();
|
||||
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const LogicalChannel&);
|
||||
|
||||
|
||||
/**
|
||||
Standalone dedicated control channel.
|
||||
GSM 04.06 4.1.3: "A dedicated control channel (DCCH) is a point-to-point
|
||||
bi-directional or uni-directional control channel. ... A SDCCH (Stand-alone
|
||||
DCCH) is a bi-directional DCCH whose allocation is not linked to the
|
||||
allocation of a TCH. The bit rate of a SDCCH is 598/765 kbit/s.
|
||||
"
|
||||
*/
|
||||
class SDCCHLogicalChannel : public LogicalChannel {
|
||||
|
||||
public:
|
||||
|
||||
SDCCHLogicalChannel(
|
||||
unsigned wTN,
|
||||
const CompleteMapping& wMapping);
|
||||
|
||||
ChannelType type() const { return SDCCHType; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Logical channel for NDCCHs that use Bbis format and a pseudolength.
|
||||
This is a virtual base class this is extended for CCCH & BCCH.
|
||||
See GSM 04.06 4.1.1, 4.1.3.
|
||||
*/
|
||||
class NDCCHLogicalChannel : public LogicalChannel {
|
||||
|
||||
public:
|
||||
|
||||
/** This channel only sends RR protocol messages. */
|
||||
virtual void send(const L3RRMessage& msg)
|
||||
{ LogicalChannel::send((const L3Message&)msg,UNIT_DATA); }
|
||||
|
||||
/** This channel only sends RR protocol messages. */
|
||||
void send(const L3Message&) { assert(0); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Slow associated control channel.
|
||||
|
||||
GSM 04.06 4.1.3: "A SACCH (Slow Associated DCCH) is either a bi-directional or
|
||||
uni-directional DCCH of rate 115/300 or a bi- directional DCCH of rate
|
||||
299/765 kbit/s. An independent SACCH is always allocated together with a TCH
|
||||
or a SDCCH. The co-allocated TCH and SACCH shall be either both bi-directional
|
||||
or both uni-directional."
|
||||
|
||||
We're going to cut a corner for the moment and give the SAACH a "thin" L2 that
|
||||
supports only the UNIT_DATA_* primitives (ie, no multiframe mode). This is OK
|
||||
until we need to transfer SMS for an in-progress call.
|
||||
|
||||
The main role of the SACCH, for now, will be to send SI5 and SI6 messages and
|
||||
to accept uplink mesaurement reports.
|
||||
*/
|
||||
class SACCHLogicalChannel : public LogicalChannel {
|
||||
|
||||
protected:
|
||||
|
||||
SACCHL1FEC *mSACCHL1;
|
||||
Thread mServiceThread; ///< a thread for the service loop
|
||||
bool mRunning; ///< true is the service loop is started
|
||||
|
||||
/** MeasurementResults from the MS. They are caught in serviceLoop, accessed
|
||||
for recording along with GPS and other data in MobilityManagement.cpp */
|
||||
L3MeasurementResults mMeasurementResults;
|
||||
|
||||
public:
|
||||
|
||||
SACCHLogicalChannel(
|
||||
unsigned wTN,
|
||||
const MappingPair& wMapping);
|
||||
|
||||
ChannelType type() const { return SACCHType; }
|
||||
|
||||
void open();
|
||||
|
||||
friend void *SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel*);
|
||||
|
||||
/**@name Pass-through accoessors to L1. */
|
||||
//@{
|
||||
float RSSI() const { return mSACCHL1->RSSI(); }
|
||||
float timingError() const { return mSACCHL1->timingError(); }
|
||||
int actualMSPower() const { return mSACCHL1->actualMSPower(); }
|
||||
int actualMSTiming() const { return mSACCHL1->actualMSTiming(); }
|
||||
void setPhy(float RSSI, float timingError) { mSACCHL1->setPhy(RSSI,timingError); }
|
||||
void setPhy(const SACCHLogicalChannel& other) { mSACCHL1->setPhy(*other.mSACCHL1); }
|
||||
//@}
|
||||
|
||||
/**@name Channel and neighbour cells stats as reported from MS */
|
||||
//@{
|
||||
const L3MeasurementResults& measurementResults() const { return mMeasurementResults; }
|
||||
//@}
|
||||
|
||||
protected:
|
||||
|
||||
/** Read and process a measurement report, called from the service loop. */
|
||||
void getReport();
|
||||
|
||||
/** This is a loop in its own thread that sends SI5 and SI6. */
|
||||
void serviceLoop();
|
||||
|
||||
};
|
||||
|
||||
/** A C interface for the SACCHLogicalChannel embedded loop. */
|
||||
void *SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel*);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Common control channel.
|
||||
The "uplink" component of the CCCH is the RACH.
|
||||
See GSM 04.03 4.1.2: "A common control channel is a point-to-multipoint
|
||||
bi-directional control channel. Common control channels are physically
|
||||
sub-divided into the common control channel (CCCH), the packet common control
|
||||
channel (PCCCH), and the Compact packet common control channel (CPCCCH)."
|
||||
*/
|
||||
class CCCHLogicalChannel : public NDCCHLogicalChannel {
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
Because the CCCH is written by multiple threads,
|
||||
we funnel all of the outgoing messages into a FIFO
|
||||
and empty that FIFO with a service loop.
|
||||
*/
|
||||
|
||||
Thread mServiceThread; ///< a thread for the service loop
|
||||
L3FrameFIFO mQ; ///< because the CCCH is written by multiple threads
|
||||
bool mRunning; ///< a flag to indication that the service loop is running
|
||||
|
||||
public:
|
||||
|
||||
CCCHLogicalChannel(const TDMAMapping& wMapping);
|
||||
|
||||
void open();
|
||||
|
||||
void send(const L3RRMessage& msg)
|
||||
{ mQ.write(new L3Frame((const L3Message&)msg,UNIT_DATA)); }
|
||||
|
||||
void send(const L3Message&) { assert(0); }
|
||||
|
||||
/** This is a loop in its own thread that empties mQ. */
|
||||
void serviceLoop();
|
||||
|
||||
/** Return the number of messages waiting for transmission. */
|
||||
unsigned load() const { return mQ.size(); }
|
||||
|
||||
ChannelType type() const { return CCCHType; }
|
||||
|
||||
friend void *CCCHLogicalChannelServiceLoopAdapter(CCCHLogicalChannel*);
|
||||
|
||||
};
|
||||
|
||||
/** A C interface for the CCCHLogicalChannel embedded loop. */
|
||||
void *CCCHLogicalChannelServiceLoopAdapter(CCCHLogicalChannel*);
|
||||
|
||||
|
||||
|
||||
class TCHFACCHLogicalChannel : public LogicalChannel {
|
||||
|
||||
protected:
|
||||
|
||||
TCHFACCHL1FEC * mTCHL1;
|
||||
|
||||
/**@name Sockets for RTP traffic, must be non-blocking. */
|
||||
//@{
|
||||
UDPSocket * mRTPSocket; ///< RTP traffic
|
||||
UDPSocket * mRTCPSocket; ///< RTP control
|
||||
//@}
|
||||
|
||||
public:
|
||||
|
||||
TCHFACCHLogicalChannel(
|
||||
unsigned wTN,
|
||||
const CompleteMapping& wMapping);
|
||||
|
||||
UDPSocket * RTPSocket() { return mRTPSocket; }
|
||||
UDPSocket * RTCPSocket() { return mRTCPSocket; }
|
||||
|
||||
ChannelType type() const { return FACCHType; }
|
||||
|
||||
void sendTCH(const unsigned char* frame)
|
||||
{ assert(mTCHL1); mTCHL1->sendTCH(frame); }
|
||||
|
||||
unsigned char* recvTCH()
|
||||
{ assert(mTCHL1); return mTCHL1->recvTCH(); }
|
||||
|
||||
unsigned queueSize() const
|
||||
{ assert(mTCHL1); return mTCHL1->queueSize(); }
|
||||
|
||||
bool radioFailure() const
|
||||
{ assert(mTCHL1); return mTCHL1->radioFailure(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**@name Test channels, not actually used in GSM. */
|
||||
//@{
|
||||
|
||||
/**
|
||||
A logical channel that loops L3Frames from input to output.
|
||||
Use a pair of these for control layer testing.
|
||||
*/
|
||||
class L3LoopbackLogicalChannel : public LogicalChannel {
|
||||
|
||||
private:
|
||||
|
||||
L3FrameFIFO mL3Q[4]; ///< a queue used for the loopback
|
||||
|
||||
public:
|
||||
|
||||
L3LoopbackLogicalChannel();
|
||||
|
||||
/** Fake the SDCCH channel type because that makes sense for most tests. */
|
||||
ChannelType type() const { return SDCCHType; }
|
||||
|
||||
/** L3 Loopback */
|
||||
void send(const L3Frame& frame, unsigned SAPI=0)
|
||||
{ mL3Q[SAPI].write(new L3Frame(frame)); }
|
||||
|
||||
/** L3 Loopback */
|
||||
void send(const GSM::Primitive prim, unsigned SAPI=0)
|
||||
{ mL3Q[SAPI].write(new L3Frame(prim)); }
|
||||
|
||||
/** L3 Loopback */
|
||||
L3Frame* recv(unsigned timeout_ms = 15000, unsigned SAPI=0)
|
||||
{ return mL3Q[SAPI].read(timeout_ms); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class SDCCHLogicalChannel_LB : public SDCCHLogicalChannel
|
||||
{
|
||||
public :
|
||||
SDCCHLogicalChannel_LB(
|
||||
unsigned wTN,
|
||||
const CompleteMapping& wMapping);
|
||||
};
|
||||
|
||||
|
||||
class TCHFACCHLogicalChannel_UPLINK : public TCHFACCHLogicalChannel
|
||||
{
|
||||
public:
|
||||
/** Custom constructor, L2 is Uplink instead of downlink. */
|
||||
TCHFACCHLogicalChannel_UPLINK(
|
||||
unsigned wTN,
|
||||
const CompleteMapping& wMapping);
|
||||
|
||||
};
|
||||
|
||||
//@}
|
||||
|
||||
}; // GSM
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
101
GSM/GSMSAPMux.cpp
Normal file
101
GSM/GSMSAPMux.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "GSMSAPMux.h"
|
||||
#include "GSMTransfer.h"
|
||||
#include "GSML1FEC.h"
|
||||
#include "GSML2LAPDm.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
void SAPMux::writeHighSide(const L2Frame& frame)
|
||||
{
|
||||
// The SAP may or may not be present, depending on the channel type.
|
||||
OBJLOG(DEBUG) << frame;
|
||||
ScopedLock lock(mLock);
|
||||
mDownstream->writeHighSide(frame);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SAPMux::writeLowSide(const L2Frame& frame)
|
||||
{
|
||||
OBJLOG(DEBUG) << frame.SAPI() << " " << frame;
|
||||
unsigned SAPI = frame.SAPI();
|
||||
bool data = frame.primitive()==DATA;
|
||||
if (data && (!mUpstream[SAPI])) {
|
||||
LOG(WARNING) << "received DATA for unsupported SAP " << SAPI;
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
mUpstream[SAPI]->writeLowSide(frame);
|
||||
} else {
|
||||
// If this is a non-data primitive, copy it out to every SAP.
|
||||
for (int i=0; i<4; i++) {
|
||||
if (mUpstream[i]) mUpstream[i]->writeLowSide(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoopbackSAPMux::writeHighSide(const L2Frame& frame)
|
||||
{
|
||||
OBJLOG(DEBUG) << "Loopback " << frame;
|
||||
// Substitute primitive
|
||||
L2Frame newFrame(frame);
|
||||
unsigned SAPI = frame.SAPI();
|
||||
switch (frame.primitive()) {
|
||||
case ERROR: SAPI=0; break;
|
||||
case RELEASE: return;
|
||||
default: break;
|
||||
}
|
||||
// Because this is a test fixture, as assert here.
|
||||
// If this were not a text fixture, we would print a warning
|
||||
// and ignore the frame.
|
||||
assert(mUpstream[SAPI]);
|
||||
ScopedLock lock(mLock);
|
||||
mUpstream[SAPI]->writeLowSide(newFrame);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoopbackSAPMux::writeLowSide(const L2Frame& frame)
|
||||
{
|
||||
assert(mDownstream);
|
||||
L2Frame newFrame(frame);
|
||||
mDownstream->writeHighSide(newFrame);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
124
GSM/GSMSAPMux.h
Normal file
124
GSM/GSMSAPMux.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef SAPMUX_H
|
||||
#define SAPMUX_H
|
||||
|
||||
#include "GSMTransfer.h"
|
||||
#include "GSML1FEC.h"
|
||||
|
||||
#include <Logger.h>
|
||||
|
||||
namespace GSM {
|
||||
|
||||
class L2DL;
|
||||
|
||||
|
||||
/**
|
||||
A SAPMux is a multipexer the connects a single L1 to multiple L2s.
|
||||
A "service access point" in GSM/ISDN is analogous to port number in IP.
|
||||
GSM allows up to 4 SAPs, although only two are presently used.
|
||||
See GSM 04.05 5.2 for an introduction.
|
||||
*/
|
||||
class SAPMux {
|
||||
|
||||
protected:
|
||||
|
||||
mutable Mutex mLock;
|
||||
L2DL * mUpstream[4]; ///< one L2 for each SAP, GSM 04.05 5.3
|
||||
L1FEC * mDownstream; ///< a single L1
|
||||
|
||||
public:
|
||||
|
||||
SAPMux(){
|
||||
mUpstream[0] = NULL;
|
||||
mUpstream[1] = NULL;
|
||||
mUpstream[2] = NULL;
|
||||
mUpstream[3] = NULL;
|
||||
mDownstream = NULL;
|
||||
}
|
||||
|
||||
virtual ~SAPMux() {}
|
||||
|
||||
virtual void writeHighSide(const L2Frame& frame);
|
||||
virtual void writeLowSide(const L2Frame& frame);
|
||||
|
||||
void upstream( L2DL * wUpstream, unsigned wSAPI=0 )
|
||||
{ assert(mUpstream[wSAPI]==NULL); mUpstream[wSAPI]=wUpstream; }
|
||||
void downstream( L1FEC * wDownstream )
|
||||
{ assert(mDownstream==NULL); mDownstream=wDownstream; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
The LoopbackSAPMux is a test fixture.
|
||||
Writes to the high side are eachoed back to the high side.
|
||||
Writes to the low side are echoed back to the low side.
|
||||
*/
|
||||
class LoopbackSAPMux : public SAPMux {
|
||||
|
||||
public:
|
||||
|
||||
LoopbackSAPMux():SAPMux() {}
|
||||
|
||||
void writeHighSide(const L2Frame& frame);
|
||||
void writeLowSide(const L2Frame& frame);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
The L1TestPointSAPMux is a test fixture.
|
||||
Writes to the high side pass through to the low side.
|
||||
Writes to the low side are dumped to cout.
|
||||
*/
|
||||
class L1TestPointSAPMux : public SAPMux {
|
||||
|
||||
public:
|
||||
|
||||
L1TestPointSAPMux():SAPMux() {}
|
||||
~L1TestPointSAPMux() {}
|
||||
|
||||
// These are defined in the .h so that we
|
||||
// don't have to link in all of L2 to
|
||||
// use them.
|
||||
|
||||
void writeHighSide(const L2Frame& frame)
|
||||
{
|
||||
assert(mDownstream);
|
||||
mLock.lock();
|
||||
mDownstream->writeHighSide(frame);
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
void writeLowSide(const L2Frame& frame)
|
||||
{
|
||||
LOG(DEBUG) << "SAPMux::writeLowSide frame=" << frame;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace GSM
|
||||
|
||||
|
||||
#endif
|
||||
// vim: ts=4 sw=4
|
||||
52
GSM/GSMTAPDump.cpp
Normal file
52
GSM/GSMTAPDump.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "GSMTAPDump.h"
|
||||
#include "GSMTransfer.h"
|
||||
#include <Sockets.h>
|
||||
#include <Globals.h>
|
||||
|
||||
UDPSocket GSMTAPSocket;
|
||||
|
||||
void gWriteGSMTAP(unsigned ARFCN, unsigned TS, unsigned FN, const GSM::L2Frame& frame)
|
||||
{
|
||||
if (!gConfig.defines("GSMTAP.TargetIP")) return;
|
||||
|
||||
unsigned port = GSMTAP_UDP_PORT; // default port for GSM-TAP
|
||||
if (gConfig.defines("GSMTAP.TargetPort"))
|
||||
port = gConfig.getNum("GSMTAP.TargetPort");
|
||||
|
||||
// Write a GSMTAP packet to the configured destination.
|
||||
GSMTAPSocket.destination(port,gConfig.getStr("GSMTAP.TargetIP").c_str());
|
||||
char buffer[MAX_UDP_LENGTH];
|
||||
gsmtap_hdr header(ARFCN,TS,FN);
|
||||
memcpy(buffer,&header,sizeof(header));
|
||||
frame.pack((unsigned char*)buffer+sizeof(header));
|
||||
GSMTAPSocket.write(buffer, sizeof(header) + frame.size()/8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
29
GSM/GSMTAPDump.h
Normal file
29
GSM/GSMTAPDump.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
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.
|
||||
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSMTAPDUMP_H
|
||||
#define GSMTAPDUMP_H
|
||||
|
||||
#include "gsmtap.h"
|
||||
#include "GSMTransfer.h"
|
||||
|
||||
|
||||
void gWriteGSMTAP(unsigned ARFCN, unsigned TS, unsigned FN, const GSM::L2Frame& frame);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
352
GSM/GSMTDMA.cpp
Normal file
352
GSM/GSMTDMA.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include "GSMTDMA.h"
|
||||
|
||||
|
||||
using namespace GSM;
|
||||
|
||||
|
||||
|
||||
|
||||
TDMAMapping::TDMAMapping(TypeAndOffset
|
||||
wTypeAndOffset, bool wDownlink, bool wUplink, char wAllowedSlots, bool wC0Only,
|
||||
unsigned wRepeatLength, unsigned wNumFrames, const unsigned *wFrameMapping)
|
||||
:mTypeAndOffset(wTypeAndOffset),
|
||||
mDownlink(wDownlink),mUplink(wUplink),mAllowedSlots(wAllowedSlots),mC0Only(wC0Only),
|
||||
mRepeatLength(wRepeatLength),mNumFrames(wNumFrames),mFrameMapping(wFrameMapping)
|
||||
{
|
||||
// Sanity check.
|
||||
assert(mRepeatLength<=mMaxRepeatLength);
|
||||
|
||||
// Default, -1, means a non-occupied position.
|
||||
for (unsigned i=0; i<mMaxRepeatLength; i++) mReverseMapping[i]=-1;
|
||||
|
||||
// Fill in the reverse map, precomputed for speed.
|
||||
for (unsigned i=0; i<mNumFrames; i++) {
|
||||
unsigned mapping = mFrameMapping[i];
|
||||
assert(mapping<mRepeatLength);
|
||||
mReverseMapping[mapping] = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** A macro to save some typing when we set up TDMA maps. */
|
||||
#define MAKE_TDMA_MAPPING(NAME,TYPEANDOFFSET,DOWNLINK,UPLINK,ALLOWEDSLOTS,C0ONLY,REPEAT) \
|
||||
const TDMAMapping GSM::g##NAME##Mapping(TYPEANDOFFSET,DOWNLINK,UPLINK,ALLOWEDSLOTS,C0ONLY, \
|
||||
REPEAT,sizeof(NAME##Frames)/sizeof(unsigned),NAME##Frames)
|
||||
|
||||
const unsigned LoopbackTestFullFrames[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47};
|
||||
MAKE_TDMA_MAPPING(LoopbackTestFull,TDMA_MISC,true,true,0xff,false,51);
|
||||
|
||||
const unsigned FCCHFrames[] = {0,10,20,30,40};
|
||||
MAKE_TDMA_MAPPING(FCCH,TDMA_BEACON,true,false,0x01,true,51);
|
||||
|
||||
const unsigned SCHFrames[] = {1,11,21,31,41};
|
||||
MAKE_TDMA_MAPPING(SCH,TDMA_BEACON,true,false,0x01,true,51);
|
||||
|
||||
const unsigned BCCHFrames[] = {2,3,4,5};
|
||||
MAKE_TDMA_MAPPING(BCCH,TDMA_BEACON,true,false,0x55,true,51);
|
||||
|
||||
// Note that we removed frames for the SDCCH components of the Combination-V C0T0.
|
||||
const unsigned RACHC5Frames[] = {4,5,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,45,46};
|
||||
MAKE_TDMA_MAPPING(RACHC5,TDMA_BEACON,false,true,0x55,true,51);
|
||||
|
||||
// CCCH 0-2 are used in C-IV and C-V. The others are used in C-IV only.
|
||||
|
||||
const unsigned CCCH_0Frames[] = {6,7,8,9};
|
||||
MAKE_TDMA_MAPPING(CCCH_0,TDMA_BEACON,true,false,0x55,true,51);
|
||||
|
||||
const unsigned CCCH_1Frames[] = {12,13,14,15};
|
||||
MAKE_TDMA_MAPPING(CCCH_1,TDMA_BEACON,true,false,0x55,true,51);
|
||||
|
||||
const unsigned CCCH_2Frames[] = {16,17,18,19};
|
||||
MAKE_TDMA_MAPPING(CCCH_2,TDMA_BEACON,true,false,0x55,true,51);
|
||||
|
||||
const unsigned CCCH_3Frames[] = {22,23,24,25};
|
||||
MAKE_TDMA_MAPPING(CCCH_3,TDMA_BEACON,true,false,0x55,true,51);
|
||||
|
||||
// TODO -- Other CCCH subchannels 4-8 for support of C-IV.
|
||||
|
||||
const unsigned SDCCH_4_0DFrames[] = {22,23,24,25};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_0D,SDCCH_4_0,true,false,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_0UFrames[] = {37,38,39,40};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_0U,SDCCH_4_0,false,true,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_1DFrames[] = {26,27,28,29};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_1D,SDCCH_4_1,true,false,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_1UFrames[] = {41,42,43,44};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_1U,SDCCH_4_1,false,true,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_2DFrames[] = {32,33,34,35};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_2D,SDCCH_4_2,true,false,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_2UFrames[] = {47,48,49,50};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_2U,SDCCH_4_2,false,true,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_3DFrames[] = {36,37,38,39};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_3D,SDCCH_4_3,true,false,0x01,true,51);
|
||||
|
||||
const unsigned SDCCH_4_3UFrames[] = {0,1,2,3};
|
||||
MAKE_TDMA_MAPPING(SDCCH_4_3U,SDCCH_4_3,false,true,0x01,true,51);
|
||||
|
||||
|
||||
const unsigned SACCH_C4_0DFrames[] = {42,43,44,45};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_0D,SDCCH_4_0,true,false,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_0UFrames[] = {57,58,59,60};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_0U,SDCCH_4_0,false,true,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_1DFrames[] = {46,47,48,49};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_1D,SDCCH_4_1,true,false,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_1UFrames[] = {61,62,63,64};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_1U,SDCCH_4_1,false,true,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_2DFrames[] = {93,94,95,96};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_2D,SDCCH_4_2,true,false,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_2UFrames[] = {6,7,8,9};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_2U,SDCCH_4_2,false,true,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_3DFrames[] = {97,98,99,100};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_3D,SDCCH_4_3,true,false,0x01,true,102);
|
||||
|
||||
const unsigned SACCH_C4_3UFrames[] = {10,11,12,13};
|
||||
MAKE_TDMA_MAPPING(SACCH_C4_3U,SDCCH_4_3,false,true,0x01,true,102);
|
||||
|
||||
|
||||
const unsigned SDCCH_8_0DFrames[] = {0,1,2,3};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_0D,SDCCH_8_0,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_0UFrames[] = {15,16,17,18};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_0U,SDCCH_8_0,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_1DFrames[] = {4,5,6,7};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_1D,SDCCH_8_1,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_1UFrames[] = {19,20,21,22};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_1U,SDCCH_8_1,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_2DFrames[] = {8,9,10,11};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_2D,SDCCH_8_2,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_2UFrames[] = {23,24,25,26};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_2U,SDCCH_8_2,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_3DFrames[] = {12,13,14,15};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_3D,SDCCH_8_3,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_3UFrames[] = {27,28,29,30};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_3U,SDCCH_8_3,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_4DFrames[] = {16,17,18,19};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_4D,SDCCH_8_4,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_4UFrames[] = {31,32,33,34};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_4U,SDCCH_8_4,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_5DFrames[] = {20,21,22,23};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_5D,SDCCH_8_5,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_5UFrames[] = {35,36,37,38};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_5U,SDCCH_8_5,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_6DFrames[] = {24,25,26,27};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_6D,SDCCH_8_6,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_6UFrames[] = {39,40,41,42};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_6U,SDCCH_8_6,false,true,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_7DFrames[] = {28,29,30,31};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_7D,SDCCH_8_7,true,false,0xFF,true,51);
|
||||
|
||||
const unsigned SDCCH_8_7UFrames[] = {43,44,45,46};
|
||||
MAKE_TDMA_MAPPING(SDCCH_8_7U,SDCCH_8_7,false,true,0xFF,true,51);
|
||||
|
||||
|
||||
const unsigned SACCH_C8_0DFrames[] = {32,33,34,35};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_0D,SDCCH_8_0,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_0UFrames[] = {47,48,49,50};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_0U,SDCCH_8_0,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_1DFrames[] = {36,37,38,39};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_1D,SDCCH_8_1,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_1UFrames[] = {51,52,53,54};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_1U,SDCCH_8_1,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_2DFrames[] = {40,41,42,43};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_2D,SDCCH_8_2,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_2UFrames[] = {55,56,57,58};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_2U,SDCCH_8_2,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_3DFrames[] = {44,45,46,47};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_3D,SDCCH_8_3,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_3UFrames[] = {59,60,61,62};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_3U,SDCCH_8_3,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_4DFrames[] = {83,84,85,86};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_4D,SDCCH_8_4,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_4UFrames[] = {98,99,100,101};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_4U,SDCCH_8_4,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_5DFrames[] = {87,88,89,90};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_5D,SDCCH_8_5,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_5UFrames[] = {0,1,2,3};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_5U,SDCCH_8_5,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_6DFrames[] = {91,92,93,94};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_6D,SDCCH_8_6,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_6UFrames[] = {4,5,6,7};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_6U,SDCCH_8_6,false,true,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_7DFrames[] = {95,96,97,98};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_7D,SDCCH_8_7,true,false,0xFF,true,102);
|
||||
|
||||
const unsigned SACCH_C8_7UFrames[] = {8,9,10,11};
|
||||
MAKE_TDMA_MAPPING(SACCH_C8_7U,SDCCH_8_7,false,true,0xFF,true,102);
|
||||
|
||||
|
||||
|
||||
const unsigned SACCH_TF_T0Frames[] = {12,38,64,90};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T0,TCHF_0,true,true,0x01,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T1Frames[] = {25,51,77,103};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T1,TCHF_0,true,true,0x02,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T2Frames[] = {38,64,90,12};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T2,TCHF_0,true,true,0x04,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T3Frames[] = {51,77,103,25};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T3,TCHF_0,true,true,0x08,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T4Frames[] = {64,90,12,38};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T4,TCHF_0,true,true,0x10,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T5Frames[] = {77,103,25,51};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T5,TCHF_0,true,true,0x20,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T6Frames[] = {90,12,38,64};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T6,TCHF_0,true,true,0x40,true,104);
|
||||
|
||||
const unsigned SACCH_TF_T7Frames[] = {103,25,51,77};
|
||||
MAKE_TDMA_MAPPING(SACCH_TF_T7,TCHF_0,true,true,0x80,true,104);
|
||||
|
||||
const unsigned FACCH_TCHFFrames[] = {0,1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24};
|
||||
MAKE_TDMA_MAPPING(FACCH_TCHF,TCHF_0,true,true,0xff,true,26);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const MappingPair GSM::gSDCCH_4_0Pair(gSDCCH_4_0DMapping,gSDCCH_4_0UMapping);
|
||||
const MappingPair GSM::gSDCCH_4_1Pair(gSDCCH_4_1DMapping,gSDCCH_4_1UMapping);
|
||||
const MappingPair GSM::gSDCCH_4_2Pair(gSDCCH_4_2DMapping,gSDCCH_4_2UMapping);
|
||||
const MappingPair GSM::gSDCCH_4_3Pair(gSDCCH_4_3DMapping,gSDCCH_4_3UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_0Pair(gSDCCH_8_0DMapping,gSDCCH_8_0UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_1Pair(gSDCCH_8_1DMapping,gSDCCH_8_1UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_2Pair(gSDCCH_8_2DMapping,gSDCCH_8_2UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_3Pair(gSDCCH_8_3DMapping,gSDCCH_8_3UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_4Pair(gSDCCH_8_4DMapping,gSDCCH_8_4UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_5Pair(gSDCCH_8_5DMapping,gSDCCH_8_5UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_6Pair(gSDCCH_8_6DMapping,gSDCCH_8_6UMapping);
|
||||
const MappingPair GSM::gSDCCH_8_7Pair(gSDCCH_8_7DMapping,gSDCCH_8_7UMapping);
|
||||
|
||||
const MappingPair GSM::gSACCH_C4_0Pair(gSACCH_C4_0DMapping,gSACCH_C4_0UMapping);
|
||||
const MappingPair GSM::gSACCH_C4_1Pair(gSACCH_C4_1DMapping,gSACCH_C4_1UMapping);
|
||||
const MappingPair GSM::gSACCH_C4_2Pair(gSACCH_C4_2DMapping,gSACCH_C4_2UMapping);
|
||||
const MappingPair GSM::gSACCH_C4_3Pair(gSACCH_C4_3DMapping,gSACCH_C4_3UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_0Pair(gSACCH_C8_0DMapping,gSACCH_C8_0UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_1Pair(gSACCH_C8_1DMapping,gSACCH_C8_1UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_2Pair(gSACCH_C8_2DMapping,gSACCH_C8_2UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_3Pair(gSACCH_C8_3DMapping,gSACCH_C8_3UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_4Pair(gSACCH_C8_4DMapping,gSACCH_C8_4UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_5Pair(gSACCH_C8_5DMapping,gSACCH_C8_5UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_6Pair(gSACCH_C8_6DMapping,gSACCH_C8_6UMapping);
|
||||
const MappingPair GSM::gSACCH_C8_7Pair(gSACCH_C8_7DMapping,gSACCH_C8_7UMapping);
|
||||
|
||||
const MappingPair GSM::gFACCH_TCHFPair(gFACCH_TCHFMapping,gFACCH_TCHFMapping);
|
||||
|
||||
const MappingPair GSM::gSACCH_FT_T0Pair(gSACCH_TF_T0Mapping, gSACCH_TF_T0Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T1Pair(gSACCH_TF_T1Mapping, gSACCH_TF_T1Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T2Pair(gSACCH_TF_T2Mapping, gSACCH_TF_T2Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T3Pair(gSACCH_TF_T3Mapping, gSACCH_TF_T3Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T4Pair(gSACCH_TF_T4Mapping, gSACCH_TF_T4Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T5Pair(gSACCH_TF_T5Mapping, gSACCH_TF_T5Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T6Pair(gSACCH_TF_T6Mapping, gSACCH_TF_T6Mapping);
|
||||
const MappingPair GSM::gSACCH_FT_T7Pair(gSACCH_TF_T7Mapping, gSACCH_TF_T7Mapping);
|
||||
|
||||
|
||||
|
||||
const CompleteMapping GSM::gSDCCH_4_0(gSDCCH_4_0Pair,gSACCH_C4_0Pair);
|
||||
const CompleteMapping GSM::gSDCCH_4_1(gSDCCH_4_1Pair,gSACCH_C4_1Pair);
|
||||
const CompleteMapping GSM::gSDCCH_4_2(gSDCCH_4_2Pair,gSACCH_C4_2Pair);
|
||||
const CompleteMapping GSM::gSDCCH_4_3(gSDCCH_4_3Pair,gSACCH_C4_3Pair);
|
||||
const CompleteMapping GSM::gSDCCH4[4] = {
|
||||
GSM::gSDCCH_4_0, GSM::gSDCCH_4_1, GSM::gSDCCH_4_2, GSM::gSDCCH_4_3,
|
||||
};
|
||||
|
||||
const CompleteMapping GSM::gSDCCH_8_0(gSDCCH_8_0Pair,gSACCH_C8_0Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_1(gSDCCH_8_1Pair,gSACCH_C8_1Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_2(gSDCCH_8_2Pair,gSACCH_C8_2Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_3(gSDCCH_8_3Pair,gSACCH_C8_3Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_4(gSDCCH_8_4Pair,gSACCH_C8_4Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_5(gSDCCH_8_5Pair,gSACCH_C8_5Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_6(gSDCCH_8_6Pair,gSACCH_C8_6Pair);
|
||||
const CompleteMapping GSM::gSDCCH_8_7(gSDCCH_8_7Pair,gSACCH_C8_7Pair);
|
||||
const CompleteMapping GSM::gSDCCH8[8] = {
|
||||
GSM::gSDCCH_8_0, GSM::gSDCCH_8_1, GSM::gSDCCH_8_2, GSM::gSDCCH_8_3,
|
||||
GSM::gSDCCH_8_4, GSM::gSDCCH_8_5, GSM::gSDCCH_8_6, GSM::gSDCCH_8_7,
|
||||
};
|
||||
|
||||
const CompleteMapping GSM::gTCHF_T0(gFACCH_TCHFPair,gSACCH_FT_T0Pair);
|
||||
const CompleteMapping GSM::gTCHF_T1(gFACCH_TCHFPair,gSACCH_FT_T1Pair);
|
||||
const CompleteMapping GSM::gTCHF_T2(gFACCH_TCHFPair,gSACCH_FT_T2Pair);
|
||||
const CompleteMapping GSM::gTCHF_T3(gFACCH_TCHFPair,gSACCH_FT_T3Pair);
|
||||
const CompleteMapping GSM::gTCHF_T4(gFACCH_TCHFPair,gSACCH_FT_T4Pair);
|
||||
const CompleteMapping GSM::gTCHF_T5(gFACCH_TCHFPair,gSACCH_FT_T5Pair);
|
||||
const CompleteMapping GSM::gTCHF_T6(gFACCH_TCHFPair,gSACCH_FT_T6Pair);
|
||||
const CompleteMapping GSM::gTCHF_T7(gFACCH_TCHFPair,gSACCH_FT_T7Pair);
|
||||
const CompleteMapping GSM::gTCHF_T[8] = {
|
||||
GSM::gTCHF_T0, GSM::gTCHF_T1, GSM::gTCHF_T2, GSM::gTCHF_T3,
|
||||
GSM::gTCHF_T4, GSM::gTCHF_T5, GSM::gTCHF_T6, GSM::gTCHF_T7,
|
||||
};
|
||||
|
||||
|
||||
|
||||
354
GSM/GSMTDMA.h
Normal file
354
GSM/GSMTDMA.h
Normal file
@@ -0,0 +1,354 @@
|
||||
/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
|
||||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef GSMTDMA_H
|
||||
#define GSMTDMA_H
|
||||
|
||||
|
||||
#include "GSMCommon.h"
|
||||
|
||||
|
||||
namespace GSM {
|
||||
|
||||
|
||||
/**
|
||||
A description of a channel's multiplexing pattern.
|
||||
From GSM 05.02 Clause 7.
|
||||
This object encodes a line from tables 1-4 in the spec.
|
||||
The columns of interest in this encoding are:
|
||||
- 1, Channel Designation
|
||||
- 2, Subchannel
|
||||
- 3, Direction
|
||||
- 4, Allowable Time Slot Assignments
|
||||
- 5, Allowable RF Channel Assignments
|
||||
- 7, Repeat Length in TDMA Frames
|
||||
- 8, Interleaved Block TDMA Frame Mapping
|
||||
|
||||
Col 6, Burst Type, is implied by 1 & 2 and encoded into the transcevier source code.
|
||||
*/
|
||||
class TDMAMapping {
|
||||
|
||||
public:
|
||||
|
||||
/// The longest "repeat length" of any channel we support is 104 for the SACCH/TF.
|
||||
static const unsigned mMaxRepeatLength = 104;
|
||||
|
||||
private:
|
||||
|
||||
TypeAndOffset mTypeAndOffset; ///< col 1, 2, encoded as per GSM 04.08 10.5.2.5
|
||||
bool mDownlink; ///< col 3, true for downlink channels
|
||||
bool mUplink; ///< col 3, true for uplink channels
|
||||
char mAllowedSlots; ///< col 4, an 8-bit mask
|
||||
bool mC0Only; ///< col 5, true if channel is limited to C0
|
||||
unsigned mRepeatLength; ///< col 7
|
||||
unsigned mNumFrames; ///< number of occupied frames in col 8
|
||||
const unsigned *mFrameMapping; ///< col 8
|
||||
unsigned mReverseMapping[mMaxRepeatLength]; ///< index reversal of mapping, -1 means unused
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
Construct a TDMAMapping, encoding one line of GSM 05.02 Clause 7 Tables 1-4.
|
||||
@param wTypeAndOffset Encoding of "Channel designnation". See GSM 04.08 10.5.2.5.
|
||||
@param wDownlink True for downlink and bidirectional hannels
|
||||
@param wUplink True for uplink and bidirectional channels
|
||||
@param wRepeatLength "Repeat Length in TDMA Frames"
|
||||
@param wNumFrames Number of occupied TDMA frames in frame mapping.
|
||||
@param wFrameMapping "Interleaved Block TDMA Frame Mapping" -- MUST PERSIST!!
|
||||
*/
|
||||
TDMAMapping(TypeAndOffset wTypeAndOffset,
|
||||
bool wDownlink, bool wUplink, char wAllowedSlots, bool wC0Only,
|
||||
unsigned wRepeatLength, unsigned wNumFrames, const unsigned *wFrameMapping);
|
||||
|
||||
/** Given a count of frames sent, return the corresponding frame number. */
|
||||
unsigned frameMapping(unsigned count) const
|
||||
{ return mFrameMapping[count % mNumFrames]; }
|
||||
|
||||
/** Given a frame number, return the corresponding count, modulo patten length. */
|
||||
int reverseMapping(unsigned FN) const
|
||||
{ return mReverseMapping[FN % mRepeatLength]; }
|
||||
|
||||
/**@name Simple accessors. */
|
||||
//@{
|
||||
unsigned numFrames() const { return mNumFrames; }
|
||||
|
||||
unsigned repeatLength() const { return mRepeatLength; }
|
||||
|
||||
TypeAndOffset typeAndOffset() const { return mTypeAndOffset; }
|
||||
|
||||
bool uplink() const { return mUplink; }
|
||||
|
||||
bool downlink() const { return mDownlink; }
|
||||
|
||||
bool C0Only() const { return mC0Only; }
|
||||
//@}
|
||||
|
||||
///< Return true if this channel is allowed on this slot.
|
||||
bool allowedSlot(unsigned slot) const
|
||||
{ return mAllowedSlots & (1<<slot); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**@name Mux parameters for standard channels, from GSM 05.03 Clause 7 Tables 1-4. */
|
||||
//@{
|
||||
/**@name Beacon channels */
|
||||
//@{
|
||||
extern const TDMAMapping gFCCHMapping; ///< GSM 05.02 Clause 7 Table 3 Line 1 B0-B4
|
||||
extern const TDMAMapping gSCHMapping; ///< GSM 05.02 Clause 7 Table 3 Line 2 B0-B4
|
||||
extern const TDMAMapping gBCCHMapping; ///< GSM 05.02 Clause 7 Table 3 Line 3
|
||||
/// GSM 05.02 Clause 7 Table 3 Line 7 B0-B50, excluding C-V SDCCH parts (SDCCH/4 and SCCH/C4)
|
||||
extern const TDMAMapping gRACHC5Mapping;
|
||||
extern const TDMAMapping gCCCH_0Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B0
|
||||
extern const TDMAMapping gCCCH_1Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B1
|
||||
extern const TDMAMapping gCCCH_2Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B2
|
||||
extern const TDMAMapping gCCCH_3Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B3
|
||||
extern const TDMAMapping gCCCH_4Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B4
|
||||
extern const TDMAMapping gCCCH_5Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B5
|
||||
extern const TDMAMapping gCCCH_6Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B6
|
||||
extern const TDMAMapping gCCCH_7Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B7
|
||||
extern const TDMAMapping gCCCH_8Mapping; ///< GSM 05.02 Clause 7 Table 3 Line 5 B8
|
||||
//@}
|
||||
/**@name SDCCH */
|
||||
//@{
|
||||
///@name For Combination V
|
||||
//@{
|
||||
extern const TDMAMapping gSDCCH_4_0DMapping; ///< GSM 05.02 Clause 7 Table 3 Line 10/0D
|
||||
extern const TDMAMapping gSDCCH_4_0UMapping; ///< GSM 05.02 Clause 7 Table 3 Line 10/0U
|
||||
extern const TDMAMapping gSDCCH_4_1DMapping;
|
||||
extern const TDMAMapping gSDCCH_4_1UMapping;
|
||||
extern const TDMAMapping gSDCCH_4_2DMapping;
|
||||
extern const TDMAMapping gSDCCH_4_2UMapping;
|
||||
extern const TDMAMapping gSDCCH_4_3DMapping;
|
||||
extern const TDMAMapping gSDCCH_4_3UMapping;
|
||||
//@}
|
||||
///@name For Combination VII
|
||||
//@{
|
||||
extern const TDMAMapping gSDCCH_8_0DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_0UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_1DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_1UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_2DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_2UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_3DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_3UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_4DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_4UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_5DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_5UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_6DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_6UMapping;
|
||||
extern const TDMAMapping gSDCCH_8_7DMapping;
|
||||
extern const TDMAMapping gSDCCH_8_7UMapping;
|
||||
//@}
|
||||
//@}
|
||||
/**@name SACCH */
|
||||
//@{
|
||||
/**name SACCH for SDCCH */
|
||||
//@{
|
||||
///@name For Combination V
|
||||
//@{
|
||||
extern const TDMAMapping gSACCH_C4_0DMapping;
|
||||
extern const TDMAMapping gSACCH_C4_0UMapping;
|
||||
extern const TDMAMapping gSACCH_C4_1DMapping;
|
||||
extern const TDMAMapping gSACCH_C4_1UMapping;
|
||||
extern const TDMAMapping gSACCH_C4_2DMapping;
|
||||
extern const TDMAMapping gSACCH_C4_2UMapping;
|
||||
extern const TDMAMapping gSACCH_C4_3DMapping;
|
||||
extern const TDMAMapping gSACCH_C4_3UMapping;
|
||||
//@}
|
||||
///@name For Combination VII
|
||||
//@{
|
||||
extern const TDMAMapping gSACCH_C8_0DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_0UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_1DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_1UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_2DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_2UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_3DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_3UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_4DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_4UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_5DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_5UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_6DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_6UMapping;
|
||||
extern const TDMAMapping gSACCH_C8_7DMapping;
|
||||
extern const TDMAMapping gSACCH_C8_7UMapping;
|
||||
//@}
|
||||
//@}
|
||||
/**@name SACCH for TCH/F on different timeslots. */
|
||||
//@{
|
||||
extern const TDMAMapping gSACCH_TF_T0Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T1Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T2Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T3Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T4Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T5Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T6Mapping;
|
||||
extern const TDMAMapping gSACCH_TF_T7Mapping;
|
||||
//@}
|
||||
//@}
|
||||
/**name FACCH+TCH/F placement */
|
||||
//@{
|
||||
extern const TDMAMapping gFACCH_TCHFMapping;
|
||||
//@}
|
||||
/**@name Test fixtures. */
|
||||
extern const TDMAMapping gLoopbackTestFullMapping;
|
||||
extern const TDMAMapping gLoopbackTestHalfUMapping;
|
||||
extern const TDMAMapping gLoopbackTestHalfDMapping;
|
||||
//@}
|
||||
|
||||
|
||||
/** Combined uplink/downlink information. */
|
||||
class MappingPair {
|
||||
|
||||
private:
|
||||
|
||||
const TDMAMapping& mDownlink;
|
||||
const TDMAMapping& mUplink;
|
||||
|
||||
public:
|
||||
|
||||
MappingPair(const TDMAMapping& wDownlink, const TDMAMapping& wUplink)
|
||||
:mDownlink(wDownlink), mUplink(wUplink)
|
||||
{}
|
||||
|
||||
MappingPair(const TDMAMapping& wMapping)
|
||||
:mDownlink(wMapping), mUplink(wMapping)
|
||||
{}
|
||||
|
||||
const TDMAMapping& downlink() const { return mDownlink; }
|
||||
const TDMAMapping& uplink() const { return mUplink; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**@name Common placement pairs. */
|
||||
//@{
|
||||
/**@ SDCCH placement pairs. */
|
||||
//@{
|
||||
extern const MappingPair gSDCCH_4_0Pair;
|
||||
extern const MappingPair gSDCCH_4_1Pair;
|
||||
extern const MappingPair gSDCCH_4_2Pair;
|
||||
extern const MappingPair gSDCCH_4_3Pair;
|
||||
extern const MappingPair gSDCCH_8_0Pair;
|
||||
extern const MappingPair gSDCCH_8_1Pair;
|
||||
extern const MappingPair gSDCCH_8_2Pair;
|
||||
extern const MappingPair gSDCCH_8_3Pair;
|
||||
extern const MappingPair gSDCCH_8_4Pair;
|
||||
extern const MappingPair gSDCCH_8_5Pair;
|
||||
extern const MappingPair gSDCCH_8_6Pair;
|
||||
extern const MappingPair gSDCCH_8_7Pair;
|
||||
//@}
|
||||
/**@ SACCH-for-SDCCH placement pairs. */
|
||||
//@{
|
||||
extern const MappingPair gSACCH_C4_0Pair;
|
||||
extern const MappingPair gSACCH_C4_1Pair;
|
||||
extern const MappingPair gSACCH_C4_2Pair;
|
||||
extern const MappingPair gSACCH_C4_3Pair;
|
||||
extern const MappingPair gSACCH_C8_0Pair;
|
||||
extern const MappingPair gSACCH_C8_1Pair;
|
||||
extern const MappingPair gSACCH_C8_2Pair;
|
||||
extern const MappingPair gSACCH_C8_3Pair;
|
||||
extern const MappingPair gSACCH_C8_4Pair;
|
||||
extern const MappingPair gSACCH_C8_5Pair;
|
||||
extern const MappingPair gSACCH_C8_6Pair;
|
||||
extern const MappingPair gSACCH_C8_7Pair;
|
||||
//@}
|
||||
/**@name Traffic channels. */
|
||||
//@{
|
||||
extern const MappingPair gFACCH_TCHFPair;
|
||||
extern const MappingPair gSACCH_FT_T0Pair;
|
||||
extern const MappingPair gSACCH_FT_T1Pair;
|
||||
extern const MappingPair gSACCH_FT_T2Pair;
|
||||
extern const MappingPair gSACCH_FT_T3Pair;
|
||||
extern const MappingPair gSACCH_FT_T4Pair;
|
||||
extern const MappingPair gSACCH_FT_T5Pair;
|
||||
extern const MappingPair gSACCH_FT_T6Pair;
|
||||
extern const MappingPair gSACCH_FT_T7Pair;
|
||||
//@}
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
/** A CompleteMapping includes uplink, downlink and the SACCH. */
|
||||
class CompleteMapping {
|
||||
|
||||
private:
|
||||
|
||||
const MappingPair& mLCH;
|
||||
const MappingPair& mSACCH;
|
||||
|
||||
public:
|
||||
|
||||
CompleteMapping(const MappingPair& wLCH, const MappingPair& wSACCH)
|
||||
:mLCH(wLCH), mSACCH(wSACCH)
|
||||
{}
|
||||
|
||||
const MappingPair& LCH() const { return mLCH; }
|
||||
const MappingPair& SACCH() const { return mSACCH; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**@name Complete placements for common channel types. */
|
||||
//@{
|
||||
/**@name SDCCH/4 */
|
||||
//@{
|
||||
extern const CompleteMapping gSDCCH_4_0;
|
||||
extern const CompleteMapping gSDCCH_4_1;
|
||||
extern const CompleteMapping gSDCCH_4_2;
|
||||
extern const CompleteMapping gSDCCH_4_3;
|
||||
extern const CompleteMapping gSDCCH4[4];
|
||||
//@}
|
||||
/**@name SDCCH/8 */
|
||||
//@{
|
||||
extern const CompleteMapping gSDCCH_8_0;
|
||||
extern const CompleteMapping gSDCCH_8_1;
|
||||
extern const CompleteMapping gSDCCH_8_2;
|
||||
extern const CompleteMapping gSDCCH_8_3;
|
||||
extern const CompleteMapping gSDCCH_8_4;
|
||||
extern const CompleteMapping gSDCCH_8_5;
|
||||
extern const CompleteMapping gSDCCH_8_6;
|
||||
extern const CompleteMapping gSDCCH_8_7;
|
||||
extern const CompleteMapping gSDCCH8[8];
|
||||
//@}
|
||||
/**@name TCH/F on different slots. */
|
||||
//@{
|
||||
extern const CompleteMapping gTCHF_T0;
|
||||
extern const CompleteMapping gTCHF_T1;
|
||||
extern const CompleteMapping gTCHF_T2;
|
||||
extern const CompleteMapping gTCHF_T3;
|
||||
extern const CompleteMapping gTCHF_T4;
|
||||
extern const CompleteMapping gTCHF_T5;
|
||||
extern const CompleteMapping gTCHF_T6;
|
||||
extern const CompleteMapping gTCHF_T7;
|
||||
extern const CompleteMapping gTCHF_T[8];
|
||||
//@}
|
||||
//@}
|
||||
|
||||
|
||||
}; // namespace GSM
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user