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:
David Burgess
2011-10-07 02:40:51 +00:00
parent f367b8728b
commit c0a5c1509e
218 changed files with 217437 additions and 0 deletions

192
AUTHORS Normal file
View 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

View 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
View 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:

View 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
View 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

View 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

View 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

View 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
;

View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
This is the directory for the OpenBTS command line interface.

724
COPYING Normal file
View 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.

0
ChangeLog Normal file
View File

603
CommonLibs/BitVector.cpp Normal file
View 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
View 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

View 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;
}

View 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
View 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

View 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
View 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
View 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
View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

67
Control/CallControl.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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:

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1230
GSM/GSML1FEC.h Normal file

File diff suppressed because it is too large Load Diff

997
GSM/GSML2LAPDm.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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