aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2009-06-10 05:40:52 +0800
committerHarald Welte <laforge@gnumonks.org>2009-06-10 05:40:52 +0800
commit13e10daa330ea2b699c9aa9d14b3adbd01111fd6 (patch)
treebf9144f9cf625baab472492b3047970cab14ef83 /openbsc
parentf7c43524cfc6e30a0223d3aaff89fe955d6e5146 (diff)
move openbsc into its own subdirectory
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/AUTHORS5
-rw-r--r--openbsc/COPYING339
-rw-r--r--openbsc/Makefile.am10
-rw-r--r--openbsc/README18
-rw-r--r--openbsc/configure.in48
-rw-r--r--openbsc/doc/BS11-OML.txt31
-rw-r--r--openbsc/doc/call-routing.txt25
-rw-r--r--openbsc/doc/e1-data-model.txt172
-rw-r--r--openbsc/include/Makefile.am3
-rw-r--r--openbsc/include/compat_af_isdn.h39
-rw-r--r--openbsc/include/mISDNif.h387
-rw-r--r--openbsc/include/openbsc/Makefile.am5
-rw-r--r--openbsc/include/openbsc/abis_nm.h635
-rw-r--r--openbsc/include/openbsc/abis_rsl.h415
-rw-r--r--openbsc/include/openbsc/call_handling.h64
-rw-r--r--openbsc/include/openbsc/chan_alloc.h49
-rw-r--r--openbsc/include/openbsc/db.h44
-rw-r--r--openbsc/include/openbsc/debug.h39
-rw-r--r--openbsc/include/openbsc/e1_input.h159
-rw-r--r--openbsc/include/openbsc/gsm_04_08.h568
-rw-r--r--openbsc/include/openbsc/gsm_04_11.h172
-rw-r--r--openbsc/include/openbsc/gsm_data.h406
-rw-r--r--openbsc/include/openbsc/gsm_subscriber.h58
-rw-r--r--openbsc/include/openbsc/gsm_utils.h33
-rw-r--r--openbsc/include/openbsc/ipaccess.h37
-rw-r--r--openbsc/include/openbsc/linuxlist.h360
-rw-r--r--openbsc/include/openbsc/misdn.h28
-rw-r--r--openbsc/include/openbsc/msgb.h111
-rw-r--r--openbsc/include/openbsc/openbscdefines.h35
-rw-r--r--openbsc/include/openbsc/paging.h46
-rw-r--r--openbsc/include/openbsc/rs232.h9
-rw-r--r--openbsc/include/openbsc/select.h22
-rw-r--r--openbsc/include/openbsc/signal.h88
-rw-r--r--openbsc/include/openbsc/subchan_demux.h102
-rw-r--r--openbsc/include/openbsc/telnet_interface.h52
-rw-r--r--openbsc/include/openbsc/timer.h71
-rw-r--r--openbsc/include/openbsc/tlv.h171
-rw-r--r--openbsc/include/openbsc/trau_frame.h65
-rw-r--r--openbsc/include/openbsc/trau_mux.h49
-rw-r--r--openbsc/include/vty/Makefile.am1
-rw-r--r--openbsc/include/vty/buffer.h102
-rw-r--r--openbsc/include/vty/command.h355
-rw-r--r--openbsc/include/vty/vector.h62
-rw-r--r--openbsc/include/vty/vty.h150
-rw-r--r--openbsc/openbsc.pc.in11
-rw-r--r--openbsc/src/Makefile.am27
-rw-r--r--openbsc/src/abis_nm.c2332
-rw-r--r--openbsc/src/abis_rsl.c1263
-rw-r--r--openbsc/src/bs11_config.c805
-rw-r--r--openbsc/src/bsc_hack.c1159
-rw-r--r--openbsc/src/chan_alloc.c256
-rw-r--r--openbsc/src/db.c464
-rw-r--r--openbsc/src/debug.c161
-rw-r--r--openbsc/src/e1_config.c108
-rw-r--r--openbsc/src/e1_input.c498
-rw-r--r--openbsc/src/gsm_04_08.c1723
-rw-r--r--openbsc/src/gsm_04_11.c500
-rw-r--r--openbsc/src/gsm_data.c209
-rw-r--r--openbsc/src/gsm_subscriber.c143
-rw-r--r--openbsc/src/gsm_utils.c73
-rw-r--r--openbsc/src/input/ipaccess.c597
-rw-r--r--openbsc/src/input/misdn.c542
-rw-r--r--openbsc/src/ipaccess-config.c198
-rw-r--r--openbsc/src/ipaccess-find.c180
-rw-r--r--openbsc/src/isdnsync.c192
-rw-r--r--openbsc/src/msgb.c70
-rw-r--r--openbsc/src/paging.c262
-rw-r--r--openbsc/src/rs232.c249
-rw-r--r--openbsc/src/select.c107
-rw-r--r--openbsc/src/signal.c80
-rw-r--r--openbsc/src/subchan_demux.c319
-rw-r--r--openbsc/src/telnet_interface.c236
-rw-r--r--openbsc/src/timer.c174
-rw-r--r--openbsc/src/tlv_parser.c105
-rw-r--r--openbsc/src/trau_frame.c255
-rw-r--r--openbsc/src/trau_mux.c212
-rw-r--r--openbsc/src/vty/buffer.c460
-rw-r--r--openbsc/src/vty/cardshell.h5
-rw-r--r--openbsc/src/vty/command.c3416
-rw-r--r--openbsc/src/vty/vector.c186
-rw-r--r--openbsc/src/vty/vty.c1634
-rw-r--r--openbsc/src/vty_interface.c905
-rw-r--r--openbsc/tests/Makefile.am1
-rw-r--r--openbsc/tests/db/Makefile.am8
-rw-r--r--openbsc/tests/db/db_test.c106
-rw-r--r--openbsc/tests/debug/Makefile.am4
-rw-r--r--openbsc/tests/debug/debug_test.c34
-rw-r--r--openbsc/tests/gsm0408/Makefile.am5
-rw-r--r--openbsc/tests/gsm0408/gsm0408_test.c72
-rw-r--r--openbsc/tests/sms.txt50
-rw-r--r--openbsc/tests/sms/Makefile.am5
-rw-r--r--openbsc/tests/sms/sms_test.c110
-rw-r--r--openbsc/tests/timer/Makefile.am5
-rw-r--r--openbsc/tests/timer/timer_test.c70
-rwxr-xr-xopenbsc/tools/hlrstat.pl58
95 files changed, 26284 insertions, 0 deletions
diff --git a/openbsc/AUTHORS b/openbsc/AUTHORS
new file mode 100644
index 000000000..1a74f5102
--- /dev/null
+++ b/openbsc/AUTHORS
@@ -0,0 +1,5 @@
+Harald Welte <laforge@gnumonks.org>
+Holger Freyther <zecke@selfish.org>
+Jan Luebbe <jluebbe@debian.org>
+Stefan Schmidt <stefan@datenfreihafen.org>
+Daniel Willmann <daniel@totalueberwachung.de>
diff --git a/openbsc/COPYING b/openbsc/COPYING
new file mode 100644
index 000000000..d511905c1
--- /dev/null
+++ b/openbsc/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ 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
+this service 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.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ 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
+convey 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 General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/openbsc/Makefile.am b/openbsc/Makefile.am
new file mode 100644
index 000000000..deaba0fa1
--- /dev/null
+++ b/openbsc/Makefile.am
@@ -0,0 +1,10 @@
+AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+SUBDIRS = include src tests
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = openbsc.pc
+
+#dist-hook:
+# rm -rf `find $(distdir) -name .svn`
diff --git a/openbsc/README b/openbsc/README
new file mode 100644
index 000000000..88715f50a
--- /dev/null
+++ b/openbsc/README
@@ -0,0 +1,18 @@
+About OpenBSC
+=============
+
+OpenBSC is a minimalistic implementation of the GSM Network, with particular
+emphasis on the functionality typically provided by the BSC, MSC, HLR, VLR.
+
+Its only current interface is a mISDN based E1 interface utilizing the A-bis
+protocol between BSC and BTS. In other words, you can connect an existing
+GSM Base Transceiver Station (BTS) through E1 to OpenBSC.
+
+So far, it has only been tested with the Siemens microBTS BS-11. Test reports
+with other BTS are appreciated!
+
+This project is still in its early days, and there are lots of areas where it
+doesn't behave as per GSM spec.
+
+December 29, 2008
+ Harald Welte <laforge@gnumonks.org>
diff --git a/openbsc/configure.in b/openbsc/configure.in
new file mode 100644
index 000000000..b886e7ac1
--- /dev/null
+++ b/openbsc/configure.in
@@ -0,0 +1,48 @@
+dnl Process this file with autoconf to produce a configure script
+AC_INIT
+
+AM_INIT_AUTOMAKE(openbsc, 0.0alpha1)
+
+dnl checks for programs
+AC_PROG_MAKE_SET
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+dnl checks for libraries
+AC_SEARCH_LIBS(crypt, crypt,
+ [LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])])
+
+dnl checks for header files
+AC_HEADER_STDC
+
+dnl Checks for typedefs, structures and compiler characteristics
+
+# The following test is taken from WebKit's webkit.m4
+saved_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -fvisibility=hidden "
+AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
+AC_COMPILE_IFELSE([char foo;],
+ [ AC_MSG_RESULT([yes])
+ SYMBOL_VISIBILITY="-fvisibility=hidden"],
+ AC_MSG_RESULT([no]))
+CFLAGS="$saved_CFLAGS"
+AC_SUBST(SYMBOL_VISIBILITY)
+
+
+dnl Generate the output
+AM_CONFIG_HEADER(bscconfig.h)
+
+AC_OUTPUT(
+ openbsc.pc
+ include/openbsc/Makefile
+ include/vty/Makefile
+ include/Makefile
+ src/Makefile
+ tests/Makefile
+ tests/debug/Makefile
+ tests/timer/Makefile
+ tests/sms/Makefile
+ tests/gsm0408/Makefile
+ tests/db/Makefile
+ Makefile)
diff --git a/openbsc/doc/BS11-OML.txt b/openbsc/doc/BS11-OML.txt
new file mode 100644
index 000000000..e5c3299c9
--- /dev/null
+++ b/openbsc/doc/BS11-OML.txt
@@ -0,0 +1,31 @@
+The Siemens BS-11 supports the following additional GSM 12.21 OML operations:
+
+
+CREATE OBJECT
+
+abis_om_fom_hdr.obj_class can be
+A3:
+A5: ALCO, BBSIG, CCLK, GPSU, LI, PA
+A8: EnvaBTSE
+A9: BPORT
+
+the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the
+abis_om_fom_hdr.bts_nr indicates the type of the object.
+
+enum abis_bs11_objtype {
+ BS11_OBJ_ALCO = 0x01,
+ BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */
+ BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */
+ BS11_OBJ_CCLK = 0x04,
+ BS11_OBJ_GPSU = 0x06,
+ BS11_OBJ_LI = 0x07,
+ BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/
+};
+
+In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx
+number.
+
+In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT
+shall be used.
+
+
diff --git a/openbsc/doc/call-routing.txt b/openbsc/doc/call-routing.txt
new file mode 100644
index 000000000..3402f9e33
--- /dev/null
+++ b/openbsc/doc/call-routing.txt
@@ -0,0 +1,25 @@
+Call routing in OpenBSC
+
+Flow of events:
+
+ # MO call initiated by MS, CHANNEL RQD, IMMEDIATE ASSIGN
+ # MS sends CC SETUP message, we assume already on TCH/H FACCH
+ # OpenBSC does a subscriber lookup based on the target extension
+ * If a subscriber is found:
+ # send CALL PROCEEDING message to MO
+ # page the MT subscriber and ask itI to ask for TCH/H
+ # once paging completes, we have the TCH/H for the MT end
+ # send SETUP to MT
+ # receive CALL CONFIRMED from MT
+ # set-up the TRAU mux mapping between the E1 subslots for both TCH/H
+ # receive ALERTING from MT, route ALERTING to MO
+ # receive CONNECT from MT, confirm to MT with CONNECT_ACK
+ # send a CONNECT message to MO, receive CONNECT_ACK from MO
+ * If subscriber is not found:
+ # send RELEASE COMPLETE with apropriate cause to MO (1: unalloacated 3: no route)
+
+
+
+Thoughts about RR/MM:
+
+* we allocate RR/MM entities on demand, when we need them
diff --git a/openbsc/doc/e1-data-model.txt b/openbsc/doc/e1-data-model.txt
new file mode 100644
index 000000000..8594fe456
--- /dev/null
+++ b/openbsc/doc/e1-data-model.txt
@@ -0,0 +1,172 @@
+E1 related data model
+
+This data model describes the physical relationship of the individual
+parts in the network, it is not the logical/protocol side of the GSM
+network.
+
+A BTS is connected to the BSC by some physical link. It could be an actual
+E1 link, but it could also be abis-over-IP with a mixture of TCP and RTP/UDP.
+
+To further complicate the fact, multiple BTS can share one such pysical
+link. On a single E1 line, we can easily accomodate up to three BTS with
+two TRX each.
+
+Thus, it is best for OpenBSC to have some kind of abstraction layer. The BSC's
+view of a BTS connected to it. We call this 'bts_link'. A bts_link can be
+* all the TCP and UDP streams of a Abis-over-IP BTS
+* a set of E1 timeslots for OML, RSL and TRAU connections on a E1 link
+* a serial line exclusively used for OML messages (T-Link)
+
+A bts_link can be registered with the OpenBSC core at runtime.
+
+struct trx_link {
+ struct gsm_bts_trx *trx;
+};
+
+struct bts_link {
+ struct gsm_bts *bts;
+ struct trx_link trx_links[NUM_TRX];
+};
+
+Interface from stack to input core:
+======================================================================
+int abis_rsl_sendmsg(struct msgb *msg);
+ send a message through a RSL link to the TRX specified by the caller in
+ msg->trx.
+
+int abis_rsl_rcvmsg(struct msgb *msg);
+ receive a message from a RSL link from the TRX specified by the
+ caller in msg->trx.
+
+int abis_nm_sendmsg(struct msgb *msg);
+ send a message through a OML link to the BTS specified by the caller in
+ msg->trx->bts. The caller can just use bts->c0 to get the first TRX
+ in a BTS. (OML messages are not really sent to a TRX but to the BTS)
+
+int abis_nm_rcvmsg(struct msgb *msg);
+ receive a message from a OML link from the BTS specified by the caller
+ in msg->trx->bts. The caller can just use bts->c0 to get the first
+ TRX in a BTS.
+
+int abis_link_event(int event, void *data);
+ signal some event (such as layer 1 connect/disconnect) from the
+ input core to the stack.
+
+int subch_demux_in(mx, const u_int8_t *data, int len);
+ receive 'len' bytes from a given E1 timeslot (TRAU frames)
+
+int subchan_mux_out(mx, u_int8_t *data, int len);
+ obtain 'len' bytes of output data to be sent on E1 timeslot
+
+Intrface by Input Core for Input Plugins
+======================================================================
+
+int btslink_register_plugin();
+
+
+Configuration for the E1 input module
+======================================================================
+
+BTS
+ BTS number
+ number of TRX
+ OML link
+ E1 line number
+ timeslot number
+ [subslot number]
+ SAPI
+ TEI
+ for each TRX
+ RSL link
+ E1 line number
+ timeslot number
+ [subslot number]
+ SAPI
+ TEI
+ for each TS
+ E1 line number
+ timeslot number
+ subslot number
+
+
+E1 input module data model
+======================================================================
+
+
+enum e1inp_sign_type {
+ E1INP_SIGN_NONE,
+ E1INP_SIGN_OML,
+ E1INP_SIGN_RSL,
+};
+
+struct e1inp_sign_link {
+ /* list of signalling links */
+ struct llist_head list;
+
+ enum e1inp_sign_type type;
+
+ /* trx for msg->trx of received msgs */
+ struct gsm_bts_trx *trx;
+
+ /* msgb queue of to-be-transmitted msgs */
+ struct llist_head tx_list;
+
+ /* SAPI and TEI on the E1 TS */
+ u_int8_t sapi;
+ u_int8_t tei;
+}
+
+enum e1inp_ts_type {
+ E1INP_TS_TYPE_NONE,
+ E1INP_TS_TYPE_SIGN,
+ E1INP_TS_TYPE_TRAU,
+};
+
+/* A timeslot in the E1 interface */
+struct e1inp_ts {
+ enum e1inp_ts_type type;
+ struct e1inp_line *line;
+ union {
+ struct {
+ struct llist_head sign_links;
+ } sign;
+ struct {
+ /* subchannel demuxer for frames from E1 */
+ struct subch_demux demux;
+ /* subchannel muxer for frames to E1 */
+ struct subch_mux mux;
+ } trau;
+ };
+ union {
+ struct {
+ /* mISDN driver has one fd for each ts */
+ struct bsc_fd;
+ } misdn;
+ } driver;
+};
+
+struct e1inp_line {
+ unsigned int num;
+ char *name;
+
+ struct e1inp_ts ts[NR_E1_TS];
+
+ char *e1inp_driver;
+ void *driver_data;
+};
+
+/* Call from the Stack: configuration of this TS has changed */
+int e1inp_update_ts(struct e1inp_ts *ts);
+
+/* Receive a packet from the E1 driver */
+int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
+ u_int8_t tei, u_int8_t sapi);
+
+/* Send a packet, callback function in the driver */
+int e1driver_tx_ts(struct e1inp_ts *ts, struct msgb *msg)
+
+
+struct e1inp_driver {
+ const char *name;
+ int (*want_write)(struct e1inp_ts *ts);
+};
diff --git a/openbsc/include/Makefile.am b/openbsc/include/Makefile.am
new file mode 100644
index 000000000..36d57a3c5
--- /dev/null
+++ b/openbsc/include/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = openbsc vty
+
+noinst_HEADERS = mISDNif.h
diff --git a/openbsc/include/compat_af_isdn.h b/openbsc/include/compat_af_isdn.h
new file mode 100644
index 000000000..56cbfb3f2
--- /dev/null
+++ b/openbsc/include/compat_af_isdn.h
@@ -0,0 +1,39 @@
+#ifdef MISDN_OLD_AF_COMPATIBILITY
+#undef AF_ISDN
+#undef PF_ISDN
+
+extern int AF_ISDN;
+#define PF_ISDN AF_ISDN
+
+int AF_ISDN;
+
+#endif
+
+extern void init_af_isdn(void);
+
+#ifdef AF_COMPATIBILITY_FUNC
+#ifdef MISDN_OLD_AF_COMPATIBILITY
+void init_af_isdn(void)
+{
+ int s;
+
+ /* test for new value */
+ AF_ISDN = 34;
+ s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE);
+ if (s >= 0) {
+ close(s);
+ return;
+ }
+ AF_ISDN = 27;
+ s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE);
+ if (s >= 0) {
+ close(s);
+ return;
+ }
+}
+#else
+void init_af_isdn(void)
+{
+}
+#endif
+#endif
diff --git a/openbsc/include/mISDNif.h b/openbsc/include/mISDNif.h
new file mode 100644
index 000000000..8e065d24b
--- /dev/null
+++ b/openbsc/include/mISDNif.h
@@ -0,0 +1,387 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU LESSER GENERAL PUBLIC LICENSE for more details.
+ *
+ */
+
+#ifndef mISDNIF_H
+#define mISDNIF_H
+
+#include <stdarg.h>
+#ifdef linux
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#else
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#endif
+
+/*
+ * ABI Version 32 bit
+ *
+ * <8 bit> Major version
+ * - changed if any interface become backwards incompatible
+ *
+ * <8 bit> Minor version
+ * - changed if any interface is extended but backwards compatible
+ *
+ * <16 bit> Release number
+ * - should be incremented on every checkin
+ */
+#define MISDN_MAJOR_VERSION 1
+#define MISDN_MINOR_VERSION 1
+#define MISDN_RELEASE 20
+
+/* primitives for information exchange
+ * generell format
+ * <16 bit 0 >
+ * <8 bit command>
+ * BIT 8 = 1 LAYER private
+ * BIT 7 = 1 answer
+ * BIT 6 = 1 DATA
+ * <8 bit target layer mask>
+ *
+ * Layer = 00 is reserved for general commands
+ Layer = 01 L2 -> HW
+ Layer = 02 HW -> L2
+ Layer = 04 L3 -> L2
+ Layer = 08 L2 -> L3
+ * Layer = FF is reserved for broadcast commands
+ */
+
+#define MISDN_CMDMASK 0xff00
+#define MISDN_LAYERMASK 0x00ff
+
+/* generell commands */
+#define OPEN_CHANNEL 0x0100
+#define CLOSE_CHANNEL 0x0200
+#define CONTROL_CHANNEL 0x0300
+#define CHECK_DATA 0x0400
+
+/* layer 2 -> layer 1 */
+#define PH_ACTIVATE_REQ 0x0101
+#define PH_DEACTIVATE_REQ 0x0201
+#define PH_DATA_REQ 0x2001
+#define MPH_ACTIVATE_REQ 0x0501
+#define MPH_DEACTIVATE_REQ 0x0601
+#define MPH_INFORMATION_REQ 0x0701
+#define PH_CONTROL_REQ 0x0801
+
+/* layer 1 -> layer 2 */
+#define PH_ACTIVATE_IND 0x0102
+#define PH_ACTIVATE_CNF 0x4102
+#define PH_DEACTIVATE_IND 0x0202
+#define PH_DEACTIVATE_CNF 0x4202
+#define PH_DATA_IND 0x2002
+#define PH_DATA_E_IND 0x3002
+#define MPH_ACTIVATE_IND 0x0502
+#define MPH_DEACTIVATE_IND 0x0602
+#define MPH_INFORMATION_IND 0x0702
+#define PH_DATA_CNF 0x6002
+#define PH_CONTROL_IND 0x0802
+#define PH_CONTROL_CNF 0x4802
+
+/* layer 3 -> layer 2 */
+#define DL_ESTABLISH_REQ 0x1004
+#define DL_RELEASE_REQ 0x1104
+#define DL_DATA_REQ 0x3004
+#define DL_UNITDATA_REQ 0x3104
+#define DL_INFORMATION_REQ 0x0004
+
+/* layer 2 -> layer 3 */
+#define DL_ESTABLISH_IND 0x1008
+#define DL_ESTABLISH_CNF 0x5008
+#define DL_RELEASE_IND 0x1108
+#define DL_RELEASE_CNF 0x5108
+#define DL_DATA_IND 0x3008
+#define DL_UNITDATA_IND 0x3108
+#define DL_INFORMATION_IND 0x0008
+
+/* intern layer 2 managment */
+#define MDL_ASSIGN_REQ 0x1804
+#define MDL_ASSIGN_IND 0x1904
+#define MDL_REMOVE_REQ 0x1A04
+#define MDL_REMOVE_IND 0x1B04
+#define MDL_STATUS_UP_IND 0x1C04
+#define MDL_STATUS_DOWN_IND 0x1D04
+#define MDL_STATUS_UI_IND 0x1E04
+#define MDL_ERROR_IND 0x1F04
+#define MDL_ERROR_RSP 0x5F04
+
+/* DL_INFORMATION_IND types */
+#define DL_INFO_L2_CONNECT 0x0001
+#define DL_INFO_L2_REMOVED 0x0002
+
+/* PH_CONTROL types */
+/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */
+#define DTMF_TONE_VAL 0x2000
+#define DTMF_TONE_MASK 0x007F
+#define DTMF_TONE_START 0x2100
+#define DTMF_TONE_STOP 0x2200
+#define DTMF_HFC_COEF 0x4000
+#define DSP_CONF_JOIN 0x2403
+#define DSP_CONF_SPLIT 0x2404
+#define DSP_RECEIVE_OFF 0x2405
+#define DSP_RECEIVE_ON 0x2406
+#define DSP_ECHO_ON 0x2407
+#define DSP_ECHO_OFF 0x2408
+#define DSP_MIX_ON 0x2409
+#define DSP_MIX_OFF 0x240a
+#define DSP_DELAY 0x240b
+#define DSP_JITTER 0x240c
+#define DSP_TXDATA_ON 0x240d
+#define DSP_TXDATA_OFF 0x240e
+#define DSP_TX_DEJITTER 0x240f
+#define DSP_TX_DEJ_OFF 0x2410
+#define DSP_TONE_PATT_ON 0x2411
+#define DSP_TONE_PATT_OFF 0x2412
+#define DSP_VOL_CHANGE_TX 0x2413
+#define DSP_VOL_CHANGE_RX 0x2414
+#define DSP_BF_ENABLE_KEY 0x2415
+#define DSP_BF_DISABLE 0x2416
+#define DSP_BF_ACCEPT 0x2416
+#define DSP_BF_REJECT 0x2417
+#define DSP_PIPELINE_CFG 0x2418
+#define HFC_VOL_CHANGE_TX 0x2601
+#define HFC_VOL_CHANGE_RX 0x2602
+#define HFC_SPL_LOOP_ON 0x2603
+#define HFC_SPL_LOOP_OFF 0x2604
+
+/* DSP_TONE_PATT_ON parameter */
+#define TONE_OFF 0x0000
+#define TONE_GERMAN_DIALTONE 0x0001
+#define TONE_GERMAN_OLDDIALTONE 0x0002
+#define TONE_AMERICAN_DIALTONE 0x0003
+#define TONE_GERMAN_DIALPBX 0x0004
+#define TONE_GERMAN_OLDDIALPBX 0x0005
+#define TONE_AMERICAN_DIALPBX 0x0006
+#define TONE_GERMAN_RINGING 0x0007
+#define TONE_GERMAN_OLDRINGING 0x0008
+#define TONE_AMERICAN_RINGPBX 0x000b
+#define TONE_GERMAN_RINGPBX 0x000c
+#define TONE_GERMAN_OLDRINGPBX 0x000d
+#define TONE_AMERICAN_RINGING 0x000e
+#define TONE_GERMAN_BUSY 0x000f
+#define TONE_GERMAN_OLDBUSY 0x0010
+#define TONE_AMERICAN_BUSY 0x0011
+#define TONE_GERMAN_HANGUP 0x0012
+#define TONE_GERMAN_OLDHANGUP 0x0013
+#define TONE_AMERICAN_HANGUP 0x0014
+#define TONE_SPECIAL_INFO 0x0015
+#define TONE_GERMAN_GASSENBESETZT 0x0016
+#define TONE_GERMAN_AUFSCHALTTON 0x0016
+
+/* MPH_INFORMATION_IND */
+#define L1_SIGNAL_LOS_OFF 0x0010
+#define L1_SIGNAL_LOS_ON 0x0011
+#define L1_SIGNAL_AIS_OFF 0x0012
+#define L1_SIGNAL_AIS_ON 0x0013
+#define L1_SIGNAL_RDI_OFF 0x0014
+#define L1_SIGNAL_RDI_ON 0x0015
+#define L1_SIGNAL_SLIP_RX 0x0020
+#define L1_SIGNAL_SLIP_TX 0x0021
+
+/*
+ * protocol ids
+ * D channel 1-31
+ * B channel 33 - 63
+ */
+
+#define ISDN_P_NONE 0
+#define ISDN_P_BASE 0
+#define ISDN_P_TE_S0 0x01
+#define ISDN_P_NT_S0 0x02
+#define ISDN_P_TE_E1 0x03
+#define ISDN_P_NT_E1 0x04
+#define ISDN_P_TE_UP0 0x05
+#define ISDN_P_NT_UP0 0x06
+
+#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
+ (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
+#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
+ (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
+#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
+#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
+#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
+
+
+#define ISDN_P_LAPD_TE 0x10
+#define ISDN_P_LAPD_NT 0x11
+
+#define ISDN_P_B_MASK 0x1f
+#define ISDN_P_B_START 0x20
+
+#define ISDN_P_B_RAW 0x21
+#define ISDN_P_B_HDLC 0x22
+#define ISDN_P_B_X75SLP 0x23
+#define ISDN_P_B_L2DTMF 0x24
+#define ISDN_P_B_L2DSP 0x25
+#define ISDN_P_B_L2DSPHDLC 0x26
+
+#define OPTION_L2_PMX 1
+#define OPTION_L2_PTP 2
+#define OPTION_L2_FIXEDTEI 3
+#define OPTION_L2_CLEANUP 4
+
+/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
+#define MISDN_MAX_IDLEN 20
+
+struct mISDNhead {
+ unsigned int prim;
+ unsigned int id;
+} __attribute__((packed));
+
+#define MISDN_HEADER_LEN sizeof(struct mISDNhead)
+#define MAX_DATA_SIZE 2048
+#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN)
+#define MAX_DFRAME_LEN 260
+
+#define MISDN_ID_ADDR_MASK 0xFFFF
+#define MISDN_ID_TEI_MASK 0xFF00
+#define MISDN_ID_SAPI_MASK 0x00FF
+#define MISDN_ID_TEI_ANY 0x7F00
+
+#define MISDN_ID_ANY 0xFFFF
+#define MISDN_ID_NONE 0xFFFE
+
+#define GROUP_TEI 127
+#define TEI_SAPI 63
+#define CTRL_SAPI 0
+
+#define MISDN_MAX_CHANNEL 127
+#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
+
+#define SOL_MISDN 0
+
+struct sockaddr_mISDN {
+ sa_family_t family;
+ unsigned char dev;
+ unsigned char channel;
+ unsigned char sapi;
+ unsigned char tei;
+};
+
+struct mISDNversion {
+ unsigned char major;
+ unsigned char minor;
+ unsigned short release;
+};
+
+#define MAX_DEVICE_ID 63
+
+struct mISDN_devinfo {
+ u_int id;
+ u_int Dprotocols;
+ u_int Bprotocols;
+ u_int protocol;
+ u_char channelmap[MISDN_CHMAP_SIZE];
+ u_int nrbchan;
+ char name[MISDN_MAX_IDLEN];
+};
+
+struct mISDN_devrename {
+ u_int id;
+ char name[MISDN_MAX_IDLEN];
+};
+
+struct ph_info_ch {
+ int32_t protocol;
+ int64_t Flags;
+};
+
+struct ph_info_dch {
+ struct ph_info_ch ch;
+ int16_t state;
+ int16_t num_bch;
+};
+
+struct ph_info {
+ struct ph_info_dch dch;
+ struct ph_info_ch bch[];
+};
+
+/* timer device ioctl */
+#define IMADDTIMER _IOR('I', 64, int)
+#define IMDELTIMER _IOR('I', 65, int)
+/* socket ioctls */
+#define IMGETVERSION _IOR('I', 66, int)
+#define IMGETCOUNT _IOR('I', 67, int)
+#define IMGETDEVINFO _IOR('I', 68, int)
+#define IMCTRLREQ _IOR('I', 69, int)
+#define IMCLEAR_L2 _IOR('I', 70, int)
+#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename)
+
+static inline int
+test_channelmap(u_int nr, u_char *map)
+{
+ if (nr <= MISDN_MAX_CHANNEL)
+ return map[nr >> 3] & (1 << (nr & 7));
+ else
+ return 0;
+}
+
+static inline void
+set_channelmap(u_int nr, u_char *map)
+{
+ map[nr >> 3] |= (1 << (nr & 7));
+}
+
+static inline void
+clear_channelmap(u_int nr, u_char *map)
+{
+ map[nr >> 3] &= ~(1 << (nr & 7));
+}
+
+/* CONTROL_CHANNEL parameters */
+#define MISDN_CTRL_GETOP 0x0000
+#define MISDN_CTRL_LOOP 0x0001
+#define MISDN_CTRL_CONNECT 0x0002
+#define MISDN_CTRL_DISCONNECT 0x0004
+#define MISDN_CTRL_PCMCONNECT 0x0010
+#define MISDN_CTRL_PCMDISCONNECT 0x0020
+#define MISDN_CTRL_SETPEER 0x0040
+#define MISDN_CTRL_UNSETPEER 0x0080
+#define MISDN_CTRL_RX_OFF 0x0100
+#define MISDN_CTRL_FILL_EMPTY 0x0200
+#define MISDN_CTRL_GETPEER 0x0400
+#define MISDN_CTRL_HW_FEATURES_OP 0x2000
+#define MISDN_CTRL_HW_FEATURES 0x2001
+#define MISDN_CTRL_HFC_OP 0x4000
+#define MISDN_CTRL_HFC_PCM_CONN 0x4001
+#define MISDN_CTRL_HFC_PCM_DISC 0x4002
+#define MISDN_CTRL_HFC_CONF_JOIN 0x4003
+#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004
+#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005
+#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006
+#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007
+#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008
+
+
+/* socket options */
+#define MISDN_TIME_STAMP 0x0001
+
+struct mISDN_ctrl_req {
+ int op;
+ int channel;
+ int p1;
+ int p2;
+};
+
+/* muxer options */
+#define MISDN_OPT_ALL 1
+#define MISDN_OPT_TEIMGR 2
+
+#endif /* mISDNIF_H */
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
new file mode 100644
index 000000000..86f056d26
--- /dev/null
+++ b/openbsc/include/openbsc/Makefile.am
@@ -0,0 +1,5 @@
+noinst_HEADERS = abis_nm.h abis_rsl.h debug.h db.h gsm_04_08.h gsm_data.h \
+ gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.h \
+ timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
+ subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
+ gsm_utils.h ipaccess.h rs232.h openbscdefines.h
diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h
new file mode 100644
index 000000000..3aac31f3a
--- /dev/null
+++ b/openbsc/include/openbsc/abis_nm.h
@@ -0,0 +1,635 @@
+/* GSM Network Management messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _NM_H
+#define _NM_H
+
+#include <sys/types.h>
+
+#include <openbsc/tlv.h>
+
+/* PRIVATE */
+
+/* generic header in front of every OML message according to TS 08.59 */
+struct abis_om_hdr {
+ u_int8_t mdisc;
+ u_int8_t placement;
+ u_int8_t sequence;
+ u_int8_t length;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_MDISC_FOM 0x80
+#define ABIS_OM_MDISC_MMI 0x40
+#define ABIS_OM_MDISC_TRAU 0x20
+#define ABIS_OM_MDISC_MANUF 0x10
+#define ABIS_OM_PLACEMENT_ONLY 0x80
+#define ABIS_OM_PLACEMENT_FIRST 0x40
+#define ABIS_OM_PLACEMENT_MIDDLE 0x20
+#define ABIS_OM_PLACEMENT_LAST 0x10
+
+struct abis_om_obj_inst {
+ u_int8_t bts_nr;
+ u_int8_t trx_nr;
+ u_int8_t ts_nr;
+} __attribute__ ((packed));
+
+struct abis_om_fom_hdr {
+ u_int8_t msg_type;
+ u_int8_t obj_class;
+ struct abis_om_obj_inst obj_inst;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_FOM_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr))
+
+/* Section 9.1: Message Types */
+enum abis_nm_msgtype {
+ /* SW Download Management Messages */
+ NM_MT_LOAD_INIT = 0x01,
+ NM_MT_LOAD_INIT_ACK,
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_SEG,
+ NM_MT_LOAD_SEG_ACK,
+ NM_MT_LOAD_ABORT,
+ NM_MT_LOAD_END,
+ NM_MT_LOAD_END_ACK,
+ NM_MT_LOAD_END_NACK,
+ NM_MT_SW_ACT_REQ, /* BTS->BSC */
+ NM_MT_SW_ACT_REQ_ACK,
+ NM_MT_SW_ACT_REQ_NACK,
+ NM_MT_ACTIVATE_SW, /* BSC->BTS */
+ NM_MT_ACTIVATE_SW_ACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_SW_ACTIVATED_REP, /* 0x10 */
+ /* A-bis Interface Management Messages */
+ NM_MT_ESTABLISH_TEI = 0x21,
+ NM_MT_ESTABLISH_TEI_ACK,
+ NM_MT_ESTABLISH_TEI_NACK,
+ NM_MT_CONN_TERR_SIGN,
+ NM_MT_CONN_TERR_SIGN_ACK,
+ NM_MT_CONN_TERR_SIGN_NACK,
+ NM_MT_DISC_TERR_SIGN,
+ NM_MT_DISC_TERR_SIGN_ACK,
+ NM_MT_DISC_TERR_SIGN_NACK,
+ NM_MT_CONN_TERR_TRAF,
+ NM_MT_CONN_TERR_TRAF_ACK,
+ NM_MT_CONN_TERR_TRAF_NACK,
+ NM_MT_DISC_TERR_TRAF,
+ NM_MT_DISC_TERR_TRAF_ACK,
+ NM_MT_DISC_TERR_TRAF_NACK,
+ /* Transmission Management Messages */
+ NM_MT_CONN_MDROP_LINK = 0x31,
+ NM_MT_CONN_MDROP_LINK_ACK,
+ NM_MT_CONN_MDROP_LINK_NACK,
+ NM_MT_DISC_MDROP_LINK,
+ NM_MT_DISC_MDROP_LINK_ACK,
+ NM_MT_DISC_MDROP_LINK_NACK,
+ /* Air Interface Management Messages */
+ NM_MT_SET_BTS_ATTR = 0x41,
+ NM_MT_SET_BTS_ATTR_ACK,
+ NM_MT_SET_BTS_ATTR_NACK,
+ NM_MT_SET_RADIO_ATTR,
+ NM_MT_SET_RADIO_ATTR_ACK,
+ NM_MT_SET_RADIO_ATTR_NACK,
+ NM_MT_SET_CHAN_ATTR,
+ NM_MT_SET_CHAN_ATTR_ACK,
+ NM_MT_SET_CHAN_ATTR_NACK,
+ /* Test Management Messages */
+ NM_MT_PERF_TEST = 0x51,
+ NM_MT_PERF_TESET_ACK,
+ NM_MT_PERF_TEST_NACK,
+ NM_MT_TEST_REP,
+ NM_MT_SEND_TEST_REP,
+ NM_MT_SEND_TEST_REP_ACK,
+ NM_MT_SEND_TEST_REP_NACK,
+ NM_MT_STOP_TEST,
+ NM_MT_STOP_TEST_ACK,
+ NM_MT_STOP_TEST_NACK,
+ /* State Management and Event Report Messages */
+ NM_MT_STATECHG_EVENT_REP = 0x61,
+ NM_MT_FAILURE_EVENT_REP,
+ NM_MT_STOP_EVENT_REP,
+ NM_MT_STOP_EVENT_REP_ACK,
+ NM_MT_STOP_EVENT_REP_NACK,
+ NM_MT_REST_EVENT_REP,
+ NM_MT_REST_EVENT_REP_ACK,
+ NM_MT_REST_EVENT_REP_NACK,
+ NM_MT_CHG_ADM_STATE,
+ NM_MT_CHG_ADM_STATE_ACK,
+ NM_MT_CHG_ADM_STATE_NACK,
+ NM_MT_CHG_ADM_STATE_REQ,
+ NM_MT_CHG_ADM_STATE_REQ_ACK,
+ NM_MT_CHG_ADM_STATE_REQ_NACK,
+ NM_MT_REP_OUTST_ALARMS = 0x93,
+ NM_MT_REP_OUTST_ALARMS_ACK,
+ NM_MT_REP_OUTST_ALARMS_NACK,
+ /* Equipment Management Messages */
+ NM_MT_CHANGEOVER = 0x71,
+ NM_MT_CHANGEOVER_ACK,
+ NM_MT_CHANGEOVER_NACK,
+ NM_MT_OPSTART,
+ NM_MT_OPSTART_ACK,
+ NM_MT_OPSTART_NACK,
+ NM_MT_REINIT,
+ NM_MT_REINIT_ACK,
+ NM_MT_REINIT_NACK,
+ NM_MT_SET_SITE_OUT, /* BS11: get alarm ?!? */
+ NM_MT_SET_SITE_OUT_ACK,
+ NM_MT_SET_SITE_OUT_NACK,
+ NM_MT_CHG_HW_CONF = 0x90,
+ NM_MT_CHG_HW_CONF_ACK,
+ NM_MT_CHG_HW_CONF_NACK,
+ /* Measurement Management Messages */
+ NM_MT_MEAS_RES_REQ = 0x8a,
+ NM_MT_MEAS_RES_RESP,
+ NM_MT_STOP_MEAS,
+ NM_MT_START_MEAS,
+ /* Other Messages */
+ NM_MT_GET_ATTR = 0x81,
+ NM_MT_GET_ATTR_RESP,
+ NM_MT_GET_ATTR_NACK,
+ NM_MT_SET_ALARM_THRES,
+ NM_MT_SET_ALARM_THRES_ACK,
+ NM_MT_SET_ALARM_THRES_NACK,
+
+ NM_MT_IPACC_RESTART = 0x87,
+ NM_MT_IPACC_RESTART_ACK,
+};
+
+enum abis_nm_msgtype_bs11 {
+ NM_MT_BS11_RESET_RESOURCE = 0x74,
+
+ NM_MT_BS11_BEGIN_DB_TX = 0xa3,
+ NM_MT_BS11_BEGIN_DB_TX_ACK,
+ NM_MT_BS11_BEGIN_DB_TX_NACK,
+ NM_MT_BS11_END_DB_TX = 0xa6,
+ NM_MT_BS11_END_DB_TX_ACK,
+ NM_MT_BS11_END_DB_TX_NACK,
+ NM_MT_BS11_CREATE_OBJ = 0xa9,
+ NM_MT_BS11_CREATE_OBJ_ACK,
+ NM_MT_BS11_CREATE_OBJ_NACK,
+ NM_MT_BS11_DELETE_OBJ = 0xac,
+ NM_MT_BS11_DELETE_OBJ_ACK,
+ NM_MT_BS11_DELETE_OBJ_NACK,
+
+ NM_MT_BS11_SET_ATTR = 0xd0,
+ NM_MT_BS11_SET_ATTR_ACK,
+ NM_MT_BS11_SET_ATTR_NACK,
+ NM_MT_BS11_LMT_SESSION = 0xdc,
+
+ NM_MT_BS11_GET_STATE = 0xe3,
+ NM_MT_BS11_GET_STATE_ACK,
+ NM_MT_BS11_LMT_LOGON = 0xe5,
+ NM_MT_BS11_LMT_LOGON_ACK,
+ NM_MT_BS11_RESTART = 0xe7,
+ NM_MT_BS11_RESTART_ACK,
+ NM_MT_BS11_DISCONNECT = 0xe9,
+ NM_MT_BS11_DISCONNECT_ACK,
+ NM_MT_BS11_LMT_LOGOFF = 0xec,
+ NM_MT_BS11_LMT_LOGOFF_ACK,
+ NM_MT_BS11_RECONNECT = 0xf1,
+ NM_MT_BS11_RECONNECT_ACK,
+};
+
+enum abis_nm_msgtype_ipacc {
+ NM_MT_IPACC_RSL_CONNECT = 0xe0,
+ NM_MT_IPACC_RSL_CONNECT_ACK,
+ NM_MT_IPACC_RSL_CONNECT_NACK,
+ NM_MT_IPACC_SET_NVATTR = 0xef,
+ NM_MT_IPACC_SET_NVATTR_ACK,
+ NM_MT_IPACC_SET_NVATTR_NACK,
+ NM_MT_IPACC_GET_NVATTR = 0xf2,
+ NM_MT_IPACC_GET_NVATTR_ACK,
+ NM_MT_IPACC_GET_NVATTR_NACK,
+};
+
+enum abis_nm_bs11_cell_alloc {
+ NM_BS11_CANR_GSM = 0x00,
+ NM_BS11_CANR_DCS1800 = 0x01,
+};
+
+/* Section 9.2: Object Class */
+enum abis_nm_obj_class {
+ NM_OC_SITE_MANAGER = 0x00,
+ NM_OC_BTS,
+ NM_OC_RADIO_CARRIER,
+ NM_OC_CHANNEL,
+ NM_OC_BASEB_TRANSC,
+ /* RFU: 05-FE */
+ NM_OC_BS11_ADJC = 0xa0,
+ NM_OC_BS11_HANDOVER = 0xa1,
+ NM_OC_BS11_PWR_CTRL = 0xa2,
+ NM_OC_BS11_BTSE = 0xa3, /* LMT? */
+ NM_OC_BS11_RACK = 0xa4,
+ NM_OC_BS11 = 0xa5, /* 01: ALCO */
+ NM_OC_BS11_TEST = 0xa6,
+ NM_OC_BS11_ENVABTSE = 0xa8,
+ NM_OC_BS11_BPORT = 0xa9,
+
+ NM_OC_GPRS_NSE = 0xf0,
+ NM_OC_GPRS_CELL = 0xf1,
+ NM_OC_GPRS_NSVC0 = 0xf2,
+ NM_OC_GPRS_NSVC1 = 0xf3,
+
+ NM_OC_NULL = 0xff,
+};
+
+/* Section 9.4: Attributes */
+enum abis_nm_attr {
+ NM_ATT_ABIS_CHANNEL = 0x01,
+ NM_ATT_ADD_INFO,
+ NM_ATT_ADD_TEXT,
+ NM_ATT_ADM_STATE,
+ NM_ATT_ARFCN_LIST,
+ NM_ATT_AUTON_REPORT,
+ NM_ATT_AVAIL_STATUS,
+ NM_ATT_BCCH_ARFCN,
+ NM_ATT_BSIC,
+ NM_ATT_BTS_AIR_TIMER,
+ NM_ATT_CCCH_L_I_P,
+ NM_ATT_CCCH_L_T,
+ NM_ATT_CHAN_COMB,
+ NM_ATT_CONN_FAIL_CRIT,
+ NM_ATT_DEST,
+ /* res */
+ NM_ATT_EVENT_TYPE = 0x11, /* BS11: file data ?!? */
+ NM_ATT_FILE_ID,
+ NM_ATT_FILE_VERSION,
+ NM_ATT_GSM_TIME,
+ NM_ATT_HSN,
+ NM_ATT_HW_CONFIG,
+ NM_ATT_HW_DESC,
+ NM_ATT_INTAVE_PARAM,
+ NM_ATT_INTERF_BOUND,
+ NM_ATT_LIST_REQ_ATTR,
+ NM_ATT_MAIO,
+ NM_ATT_MANUF_STATE,
+ NM_ATT_MANUF_THRESH,
+ NM_ATT_MANUF_ID,
+ NM_ATT_MAX_TA,
+ NM_ATT_MDROP_LINK, /* 0x20 */
+ NM_ATT_MDROP_NEXT,
+ NM_ATT_NACK_CAUSES,
+ NM_ATT_NY1,
+ NM_ATT_OPER_STATE,
+ NM_ATT_OVERL_PERIOD,
+ NM_ATT_PHYS_CONF,
+ NM_ATT_POWER_CLASS,
+ NM_ATT_POWER_THRESH,
+ NM_ATT_PROB_CAUSE,
+ NM_ATT_RACH_B_THRESH,
+ NM_ATT_LDAVG_SLOTS,
+ NM_ATT_RAD_SUBC,
+ NM_ATT_RF_MAXPOWR_R,
+ NM_ATT_SITE_INPUTS,
+ NM_ATT_SITE_OUTPUTS,
+ NM_ATT_SOURCE, /* 0x30 */
+ NM_ATT_SPEC_PROB,
+ NM_ATT_START_TIME,
+ NM_ATT_T200,
+ NM_ATT_TEI,
+ NM_ATT_TEST_DUR,
+ NM_ATT_TEST_NO,
+ NM_ATT_TEST_REPORT,
+ NM_ATT_VSWR_THRESH,
+ NM_ATT_WINDOW_SIZE,
+ /* Res */
+ NM_ATT_BS11_RSSI_OFFS = 0x3d,
+ NM_ATT_BS11_TXPWR = 0x3e,
+ NM_ATT_BS11_DIVERSITY = 0x3f,
+ /* Res */
+ NM_ATT_TSC = 0x40,
+ NM_ATT_SW_CONFIG,
+ NM_ATT_SW_DESCR,
+ NM_ATT_SEVERITY,
+ NM_ATT_GET_ARI,
+ NM_ATT_HW_CONF_CHG,
+ NM_ATT_OUTST_ALARM,
+ NM_ATT_FILE_DATA,
+ NM_ATT_MEAS_RES,
+ NM_ATT_MEAS_TYPE,
+
+ NM_ATT_BS11_ESN_FW_CODE_NO = 0x4c,
+ NM_ATT_BS11_ESN_HW_CODE_NO = 0x4f,
+
+ NM_ATT_BS11_ESN_PCB_SERIAL = 0x55,
+ NM_ATT_BS11_EXCESSIVE_DISTANCE = 0x58,
+
+ NM_ATT_BS11_ALL_TEST_CATG = 0x60,
+ NM_ATT_BS11_BTSLS_HOPPING,
+ NM_ATT_BS11_CELL_ALLOC_NR,
+ NM_ATT_BS11_CALL_GLOBAL_ID,
+ NM_ATT_BS11_ENA_INTERF_CLASS = 0x66,
+ NM_ATT_BS11_ENA_INT_INTEC_HANDO = 0x67,
+ NM_ATT_BS11_ENA_INT_INTRC_HANDO = 0x68,
+ NM_ATT_BS11_ENA_MS_PWR_CTRL = 0x69,
+ NM_ATT_BS11_ENA_PWR_BDGT_HO = 0x6a,
+ NM_ATT_BS11_ENA_PWR_CTRL_RLFW = 0x6b,
+ NM_ATT_BS11_ENA_RXLEV_HO = 0x6c,
+ NM_ATT_BS11_ENA_RXQUAL_HO = 0x6d,
+ NM_ATT_BS11_FACCH_QUAL = 0x6e,
+
+ NM_ATT_IPACC_RSL_BSC_IP = 0x80,
+ NM_ATT_IPACC_RSL_BSC_PORT = 0x81,
+ NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */
+ NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */
+ NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts-<mac-as-string> */
+ NM_ATT_IPACC_PRIM_OML_IP = 0x95,
+ NM_ATT_IPACC_SEC_OML_IP = 0x96,
+
+ NM_ATT_BS11_RF_RES_IND_PER = 0x8f,
+
+ NM_ATT_BS11_RX_LEV_MIN_CELL = 0x90,
+ NM_ATT_BS11_ABIS_EXT_TIME = 0x91,
+ NM_ATT_BS11_TIMER_HO_REQUEST = 0x92,
+ NM_ATT_BS11_TIMER_NCELL = 0x93,
+ NM_ATT_BS11_TSYNC = 0x94,
+ NM_ATT_BS11_TTRAU = 0x95,
+ NM_ATT_BS11_EMRG_CFG_MEMBER = 0x9b,
+ NM_ATT_BS11_TRX_AREA = 0x9f,
+
+ NM_ATT_BS11_BCCH_RECONF = 0xd7,
+ NM_ATT_BS11_BIT_ERR_THESH = 0xa0,
+ NM_ATT_BS11_BOOT_SW_VERS = 0xa1,
+ NM_ATT_BS11_CCLK_ACCURACY = 0xa3,
+ NM_ATT_BS11_CCLK_TYPE = 0xa4,
+ NM_ATT_BS11_INP_IMPEDANCE = 0xaa,
+ NM_ATT_BS11_L1_PROT_TYPE = 0xab,
+ NM_ATT_BS11_LINE_CFG = 0xac,
+ NM_ATT_BS11_LI_PORT_1 = 0xad,
+ NM_ATT_BS11_LI_PORT_2 = 0xae,
+
+ NM_ATT_BS11_L1_REM_ALM_TYPE = 0xb0,
+ NM_ATT_BS11_SW_LOAD_INTENDED = 0xbb,
+ NM_ATT_BS11_SW_LOAD_SAFETY = 0xbc,
+ NM_ATT_BS11_SW_LOAD_STORED = 0xbd,
+
+ NM_ATT_BS11_VENDOR_NAME = 0xc1,
+ NM_ATT_BS11_HOPPING_MODE = 0xc5,
+ NM_ATT_BS11_LMT_LOGON_SESSION = 0xc6,
+ NM_ATT_BS11_LMT_LOGIN_TIME = 0xc7,
+ NM_ATT_BS11_LMT_USER_ACC_LEV = 0xc8,
+ NM_ATT_BS11_LMT_USER_NAME = 0xc9,
+
+ NM_ATT_BS11_L1_CONTROL_TS = 0xd8,
+ NM_ATT_BS11_RADIO_MEAS_GRAN = 0xdc, /* in SACCH multiframes */
+ NM_ATT_BS11_RADIO_MEAS_REP = 0xdd,
+
+ NM_ATT_BS11_SH_LAPD_INT_TIMER = 0xe8,
+
+ NM_ATT_BS11_BTS_STATE = 0xf0,
+ NM_ATT_BS11_E1_STATE = 0xf1,
+ NM_ATT_BS11_PLL = 0xf2,
+ NM_ATT_BS11_RX_OFFSET = 0xf3,
+ NM_ATT_BS11_ANT_TYPE = 0xf4,
+ NM_ATT_BS11_PLL_MODE = 0xfc,
+ NM_ATT_BS11_PASSWORD = 0xfd,
+};
+#define NM_ATT_BS11_FILE_DATA NM_ATT_EVENT_TYPE
+
+/* Section 9.4.4: Administrative State */
+enum abis_nm_adm_state {
+ NM_STATE_LOCKED = 0x01,
+ NM_STATE_UNLOCKED = 0x02,
+ NM_STATE_SHUTDOWN = 0x03,
+ NM_STATE_NULL = 0xff,
+};
+
+/* Section 9.4.13: Channel Combination */
+enum abis_nm_chan_comb {
+ NM_CHANC_TCHFull = 0x00,
+ NM_CHANC_TCHHalf = 0x01,
+ NM_CHANC_TCHHalf2 = 0x02,
+ NM_CHANC_SDCCH = 0x03,
+ NM_CHANC_mainBCCH = 0x04,
+ NM_CHANC_BCCCHComb = 0x05,
+ NM_CHANC_BCCH = 0x06,
+ NM_CHANC_BCCH_CBCH = 0x07,
+ NM_CHANC_SDCCH_CBCH = 0x08,
+};
+
+/* Section 9.4.16: Event Type */
+enum abis_nm_event_type {
+ NM_EVT_COMM_FAIL = 0x00,
+ NM_EVT_QOS_FAIL = 0x01,
+ NM_EVT_PROC_FAIL = 0x02,
+ NM_EVT_EQUIP_FAIL = 0x03,
+ NM_EVT_ENV_FAIL = 0x04,
+};
+
+/* Section: 9.4.63: Perceived Severity */
+enum abis_nm_severity {
+ NM_SEVER_CEASED = 0x00,
+ NM_SEVER_CRITICAL = 0x01,
+ NM_SEVER_MAJOR = 0x02,
+ NM_SEVER_MINOR = 0x03,
+ NM_SEVER_WARNING = 0x04,
+ NM_SEVER_INDETERMINATE = 0x05,
+};
+
+/* Section 9.4.43: Probable Cause Type */
+enum abis_nm_pcause_type {
+ NM_PCAUSE_T_X721 = 0x01,
+ NM_PCAUSE_T_GSM = 0x02,
+ NM_PCAUSE_T_MANUF = 0x03,
+};
+
+/* Section 9.4.36: NACK Causes */
+enum abis_nm_nack_cause {
+ /* General Nack Causes */
+ NM_NACK_INCORR_STRUCT = 0x01,
+ NM_NACK_MSGTYPE_INVAL = 0x02,
+ NM_NACK_OBJCLASS_INVAL = 0x05,
+ NM_NACK_OBJCLASS_NOTSUPP = 0x06,
+ NM_NACK_BTSNR_UNKN = 0x07,
+ NM_NACK_TRXNR_UNKN = 0x08,
+ NM_NACK_OBJINST_UNKN = 0x09,
+ NM_NACK_ATTRID_INVAL = 0x0c,
+ NM_NACK_ATTRID_NOTSUPP = 0x0d,
+ NM_NACK_PARAM_RANGE = 0x0e,
+ NM_NACK_ATTRLIST_INCONSISTENT = 0x0f,
+ NM_NACK_SPEC_IMPL_NOTSUPP = 0x10,
+ NM_NACK_CANT_PERFORM = 0x11,
+ /* Specific Nack Causes */
+ NM_NACK_RES_NOTIMPL = 0x19,
+ NM_NACK_RES_NOTAVAIL = 0x1a,
+ NM_NACK_FREQ_NOTAVAIL = 0x1b,
+ NM_NACK_TEST_NOTSUPP = 0x1c,
+ NM_NACK_CAPACITY_RESTR = 0x1d,
+ NM_NACK_PHYSCFG_NOTPERFORM = 0x1e,
+ NM_NACK_TEST_NOTINIT = 0x1f,
+ NM_NACK_PHYSCFG_NOTRESTORE = 0x20,
+ NM_NACK_TEST_NOSUCH = 0x21,
+ NM_NACK_TEST_NOSTOP = 0x22,
+ NM_NACK_MSGINCONSIST_PHYSCFG = 0x23,
+ NM_NACK_FILE_INCOMPLETE = 0x25,
+ NM_NACK_FILE_NOTAVAIL = 0x26,
+ MN_NACK_FILE_NOTACTIVATE = 0x27,
+ NM_NACK_REQ_NOT_GRANT = 0x28,
+ NM_NACK_WAIT = 0x29,
+ NM_NACK_NOTH_REPORT_EXIST = 0x2a,
+ NM_NACK_MEAS_NOTSUPP = 0x2b,
+ NM_NACK_MEAS_NOTSTART = 0x2c,
+};
+
+/* Section 9.4.1 */
+struct abis_nm_channel {
+ u_int8_t attrib;
+ u_int8_t bts_port;
+ u_int8_t timeslot;
+ u_int8_t subslot;
+} __attribute__ ((packed));
+
+/* Siemens BS-11 specific objects in the SienemsHW (0xA5) object class */
+enum abis_bs11_objtype {
+ BS11_OBJ_ALCO = 0x01,
+ BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */
+ BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */
+ BS11_OBJ_CCLK = 0x04,
+ BS11_OBJ_GPSU = 0x06,
+ BS11_OBJ_LI = 0x07,
+ BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/
+};
+
+enum abis_bs11_trx_power {
+ BS11_TRX_POWER_GSM_2W = 0x06,
+ BS11_TRX_POWER_GSM_250mW= 0x07,
+ BS11_TRX_POWER_GSM_80mW = 0x08,
+ BS11_TRX_POWER_GSM_30mW = 0x09,
+ BS11_TRX_POWER_DCS_3W = 0x0a,
+ BS11_TRX_POWER_DCS_1W6 = 0x0b,
+ BS11_TRX_POWER_DCS_500mW= 0x0c,
+ BS11_TRX_POWER_DCS_160mW= 0x0d,
+};
+
+enum abis_bs11_li_pll_mode {
+ BS11_LI_PLL_LOCKED = 2,
+ BS11_LI_PLL_STANDALONE = 3,
+};
+
+enum abis_bs11_phase {
+ BS11_STATE_SOFTWARE_RQD = 0x01,
+ BS11_STATE_LOAD_SMU_INTENDED = 0x11,
+ BS11_STATE_LOAD_SMU_SAFETY = 0x21,
+ BS11_STATE_LOAD_FAILED = 0x31,
+ BS11_STATE_LOAD_DIAGNOSTIC = 0x41,
+ BS11_STATE_WARM_UP = 0x51,
+ BS11_STATE_WARM_UP_2 = 0x52,
+ BS11_STATE_WAIT_MIN_CFG = 0x62,
+ BS11_STATE_MAINTENANCE = 0x72,
+ BS11_STATE_LOAD_MBCCU = 0x92,
+ BS11_STATE_WAIT_MIN_CFG_2 = 0xA2,
+ BS11_STATE_NORMAL = 0x03,
+ BS11_STATE_ABIS_LOAD = 0x13,
+};
+
+
+/* PUBLIC */
+
+struct msgb;
+
+struct abis_nm_cfg {
+ /* callback for unidirectional reports */
+ int (*report_cb)(struct msgb *,
+ struct abis_om_fom_hdr *);
+ /* callback for software activate requests from BTS */
+ int (*sw_act_req)(struct msgb *);
+};
+
+extern int abis_nm_rcvmsg(struct msgb *msg);
+
+int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len);
+int abis_nm_rx(struct msgb *msg);
+int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2);
+int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0,
+ u_int8_t i1, u_int8_t i2, u_int8_t adm_state);
+int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr,
+ u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot,
+ u_int8_t tei);
+int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx,
+ u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot);
+int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
+ u_int8_t e1_port, u_int8_t e1_timeslot,
+ u_int8_t e1_subslot);
+int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len);
+int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len);
+int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb);
+int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
+ u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len);
+int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg);
+int abis_nm_event_reports(struct gsm_bts *bts, int on);
+int abis_nm_reset_resource(struct gsm_bts *bts);
+int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
+ u_int8_t win_size, int forced,
+ gsm_cbfn *cbfn, void *cb_data);
+int abis_nm_software_load_status(struct gsm_bts *bts);
+int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
+ gsm_cbfn *cbfn, void *cb_data);
+
+/* Siemens / BS-11 specific */
+int abis_nm_bs11_reset_resource(struct gsm_bts *bts);
+int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin);
+int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type,
+ u_int8_t idx, u_int8_t attr_len, const u_int8_t *attr);
+int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, u_int8_t idx);
+int abis_nm_bs11_create_bport(struct gsm_bts *bts, u_int8_t idx);
+int abis_nm_bs11_delete_object(struct gsm_bts *bts,
+ enum abis_bs11_objtype type, u_int8_t idx);
+int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port,
+ u_int8_t e1_timeslot, u_int8_t e1_subslot, u_int8_t tei);
+int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts);
+int abis_nm_bs11_get_serno(struct gsm_bts *bts);
+int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, u_int8_t level);
+int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx);
+int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on);
+int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password);
+int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked);
+int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts);
+int abis_nm_bs11_get_cclk(struct gsm_bts *bts);
+int abis_nm_bs11_get_state(struct gsm_bts *bts);
+int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
+ u_int8_t win_size, int forced, gsm_cbfn *cbfn);
+int abis_nm_bs11_set_ext_time(struct gsm_bts *bts);
+int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect);
+int abis_nm_bs11_restart(struct gsm_bts *bts);
+
+/* ip.access nanoBTS specific commands */
+int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
+ u_int8_t obj_class, u_int8_t bts_nr,
+ u_int8_t trx_nr, u_int8_t ts_nr,
+ u_int8_t *attr, int attr_len);
+int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
+ int attr_len);
+int abis_nm_ipaccess_restart(struct gsm_bts *bts);
+
+/* Functions calling into other code parts */
+enum nm_evt {
+ EVT_STATECHG_OPER,
+ EVT_STATECHG_ADM,
+};
+int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state);
+
+const char *nm_opstate_name(u_int8_t os);
+const char *nm_avail_name(u_int8_t avail);
+#endif /* _NM_H */
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
new file mode 100644
index 000000000..532595b90
--- /dev/null
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -0,0 +1,415 @@
+/* GSM Radio Signalling Link messages on the A-bis interface
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _RSL_H
+#define _RSL_H
+
+struct abis_rsl_common_hdr {
+ u_int8_t msg_discr;
+ u_int8_t msg_type;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 */
+struct abis_rsl_rll_hdr {
+ struct abis_rsl_common_hdr c;
+ u_int8_t ie_chan;
+ u_int8_t chan_nr;
+ u_int8_t ie_link_id;
+ u_int8_t link_id;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 and 8.4 */
+struct abis_rsl_dchan_hdr {
+ struct abis_rsl_common_hdr c;
+ u_int8_t ie_chan;
+ u_int8_t chan_nr;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+
+/* Chapter 9.1 */
+#define ABIS_RSL_MDISC_RLL 0x02
+#define ABIS_RSL_MDISC_DED_CHAN 0x08
+#define ABIS_RSL_MDISC_COM_CHAN 0x0c
+#define ABIS_RSL_MDISC_TRX 0x10
+#define ABIS_RSL_MDISC_LOC 0x20
+#define ABIS_RSL_MDISC_IPACCESS 0x7e
+#define ABIS_RSL_MDISC_TRANSP 0x01
+
+#define ABIS_RSL_MDISC_IS_TRANSP(x) (x & 0x01)
+
+/* Chapter 9.1 */
+enum abis_rsl_msgtype {
+ /* Radio Link Layer Management */
+ RSL_MT_DATA_REQ = 0x01,
+ RSL_MT_DATA_IND,
+ RSL_MT_ERROR_IND,
+ RSL_MT_EST_REQ,
+ RSL_MT_EST_CONF,
+ RSL_MT_EST_IND,
+ RSL_MT_REL_REQ,
+ RSL_MT_REL_CONF,
+ RSL_MT_REL_IND,
+ RSL_MT_UNIT_DATA_REQ,
+ RSL_MT_UNIT_DATA_IND, /* 0x0b */
+
+ /* Common Channel Management / TRX Management */
+ RSL_MT_BCCH_INFO = 0x11,
+ RSL_MT_CCCH_LOAD_IND,
+ RSL_MT_CHAN_RQD,
+ RSL_MT_DELETE_IND,
+ RSL_MT_PAGING_CMD,
+ RSL_MT_IMMEDIATE_ASSIGN_CMD,
+ RSL_MT_SMS_BC_REQ,
+ /* empty */
+ RSL_MT_RF_RES_IND = 0x19,
+ RSL_MT_SACCH_FILL,
+ RSL_MT_OVERLOAD,
+ RSL_MT_ERROR_REPORT,
+ RSL_MT_SMS_BC_CMD,
+ RSL_MT_CBCH_LOAD_IND,
+ RSL_MT_NOT_CMD, /* 0x1f */
+
+ /* Dedicate Channel Management */
+ RSL_MT_CHAN_ACTIV = 0x21,
+ RSL_MT_CHAN_ACTIV_ACK,
+ RSL_MT_CHAN_ACTIV_NACK,
+ RSL_MT_CONN_FAIL,
+ RSL_MT_DEACTIVATE_SACCH,
+ RSL_MT_ENCR_CMD,
+ RSL_MT_HANDO_DET,
+ RSL_MT_MEAS_RES,
+ RSL_MT_MODE_MODIFY_REQ,
+ RSL_MT_MODE_MODIFY_ACK,
+ RSL_MT_MODE_MODIFY_NACK,
+ RSL_MT_PHY_CONTEXT_REQ,
+ RSL_MT_PHY_CONTEXT_CONF,
+ RSL_MT_RF_CHAN_REL,
+ RSL_MT_MS_POWER_CONTROL,
+ RSL_MT_BS_POWER_CONTROL, /* 0x30 */
+ RSL_MT_PREPROC_CONFIG,
+ RSL_MT_PREPROC_MEAS_RES,
+ RSL_MT_RF_CHAN_REL_ACK,
+ RSL_MT_SACCH_INFO_MODIFY,
+ RSL_MT_TALKER_DET,
+ RSL_MT_LISTENER_DET,
+ RSL_MT_REMOTE_CODEC_CONF_REP,
+ RSL_MT_RTD_REP,
+ RSL_MT_PRE_HANDO_NOTIF,
+ RSL_MT_MR_CODEC_MOD_REQ,
+ RSL_MT_MR_CODEC_MOD_ACK,
+ RSL_MT_MR_CODEC_MOD_NACK,
+ RSL_MT_MR_CODEC_MOD_PER,
+ RSL_MT_TFO_REP,
+ RSL_MT_TFO_MOD_REQ, /* 0x3f */
+
+ /* ip.access specific RSL message types */
+ RSL_MT_IPAC_BIND = 0x70, /* Bind to local BTS RTP port */
+ RSL_MT_IPAC_BIND_ACK,
+ RSL_MT_IPAC_BIND_NACK,
+ RSL_MT_IPAC_CONNECT = 0x73,
+ RSL_MT_IPAC_CONNECT_ACK,
+ RSL_MT_IPAC_CONNECT_NACK,
+ RSL_MT_IPAC_DISCONNECT_IND = 0x76,
+
+};
+
+/* Chapter 9.3 */
+enum abis_rsl_ie {
+ RSL_IE_CHAN_NR = 0x01,
+ RSL_IE_LINK_IDENT,
+ RSL_IE_ACT_TYPE,
+ RSL_IE_BS_POWER,
+ RSL_IE_CHAN_IDENT,
+ RSL_IE_CHAN_MODE,
+ RSL_IE_ENCR_INFO,
+ RSL_IE_FRAME_NUMBER,
+ RSL_IE_HANDO_REF,
+ RSL_IE_L1_INFO,
+ RSL_IE_L3_INFO,
+ RSL_IE_MS_IDENTITY,
+ RSL_IE_MS_POWER,
+ RSL_IE_PAGING_GROUP,
+ RSL_IE_PAGING_LOAD,
+ RSL_IE_PYHS_CONTEXT = 0x10,
+ RSL_IE_ACCESS_DELAY,
+ RSL_IE_RACH_LOAD,
+ RSL_IE_REQ_REFERENCE,
+ RSL_IE_RELEASE_MODE,
+ RSL_IE_RESOURCE_INFO,
+ RSL_IE_RLM_CAUSE,
+ RSL_IE_STARTNG_TIME,
+ RSL_IE_TIMING_ADVANCE,
+ RSL_IE_UPLINK_MEAS,
+ RSL_IE_CAUSE,
+ RSL_IE_MEAS_RES_NR,
+ RSL_IE_MSG_ID,
+ /* reserved */
+ RSL_IE_SYSINFO_TYPE = 0x1e,
+ RSL_IE_MS_POWER_PARAM,
+ RSL_IE_BS_POWER_PARAM,
+ RSL_IE_PREPROC_PARAM,
+ RSL_IE_PREPROC_MEAS,
+ RSL_IE_IMM_ASS_INFO, /* Phase 1 (3.6.0), later Full below */
+ RSL_IE_SMSCB_INFO = 0x24,
+ RSL_IE_MS_TIMING_OFFSET,
+ RSL_IE_ERR_MSG,
+ RSL_IE_FULL_BCCH_INFO,
+ RSL_IE_CHAN_NEEDED,
+ RSL_IE_CB_CMD_TYPE,
+ RSL_IE_SMSCB_MSG,
+ RSL_IE_FULL_IMM_ASS_INFO,
+ RSL_IE_SACCH_INFO,
+ RSL_IE_CBCH_LOAD_INFO,
+ RSL_IE_SMSCB_CHAN_INDICATOR,
+ RSL_IE_GROUP_CALL_REF,
+ RSL_IE_CHAN_DESC,
+ RSL_IE_NCH_DRX_INFO,
+ RSL_IE_CMD_INDICATOR,
+ RSL_IE_EMLPP_PRIO,
+ RSL_IE_UIC,
+ RSL_IE_MAIN_CHAN_REF,
+ RSL_IE_MR_CONFIG,
+ RSL_IE_MR_CONTROL,
+ RSL_IE_SUP_CODEC_TYPES,
+ RSL_IE_CODEC_CONFIG,
+ RSL_IE_RTD,
+ RSL_IE_TFO_STATUS,
+ RSL_IE_LLP_APDU,
+
+ RSL_IE_IPAC_REMOTE_IP = 0xf0,
+ RSL_IE_IPAC_REMOTE_PORT = 0xf1,
+ RSL_IE_IPAC_LOCAL_PORT = 0xf3,
+ RSL_IE_IPAC_LOCAL_IP = 0xf5,
+};
+
+/* Chapter 9.3.1 */
+#define RSL_CHAN_NR_MASK 0xf8
+#define RSL_CHAN_Bm_ACCHs 0x08
+#define RSL_CHAN_Lm_ACCHs 0x10 /* .. 0x18 */
+#define RSL_CHAN_SDCCH4_ACCH 0x20 /* .. 0x38 */
+#define RSL_CHAN_SDCCH8_ACCH 0x40 /* ...0x78 */
+#define RSL_CHAN_BCCH 0x80
+#define RSL_CHAN_RACH 0x88
+#define RSL_CHAN_PCH_AGCH 0x90
+
+/* Chapter 9.3.3 */
+#define RSL_ACT_TYPE_INITIAL 0x00
+#define RSL_ACT_TYPE_REACT 0x80
+#define RSL_ACT_INTRA_IMM_ASS 0x00
+#define RSL_ACT_INTRA_NORM_ASS 0x01
+#define RSL_ACT_INTER_ASYNC 0x02
+#define RSL_ACT_INTER_SYNC 0x03
+#define RSL_ACT_SECOND_ADD 0x04
+#define RSL_ACT_SECOND_MULTI 0x05
+
+/* Chapter 9.3.6 */
+struct rsl_ie_chan_mode {
+ u_int8_t dtx_dtu;
+ u_int8_t spd_ind;
+ u_int8_t chan_rt;
+ u_int8_t chan_rate;
+} __attribute__ ((packed));
+#define RSL_CMOD_DTXu 0x01 /* uplink */
+#define RSL_CMOD_DTXd 0x02 /* downlink */
+#define RSL_CMOD_SPD_SPEECH 0x01
+#define RSL_CMOD_SPD_DATA 0x02
+#define RSL_CMOD_SPD_SIGN 0x03
+#define RSL_CMOD_CRT_SDCCH 0x01
+#define RSL_CMOD_CRT_TCH_Bm 0x08 /* full-rate */
+#define RSL_CMOD_CRT_TCH_Lm 0x09 /* half-rate */
+/* FIXME: More CRT types */
+#define RSL_CMOD_SP_GSM1 0x01
+#define RSL_CMOD_SP_GSM2 0x11
+#define RSL_CMOD_SP_GSM3 0x21
+
+/* Chapter 9.3.5 */
+struct rsl_ie_chan_ident {
+ /* GSM 04.08 10.5.2.5 */
+ struct {
+ u_int8_t iei;
+ u_int8_t chan_nr; /* enc_chan_nr */
+ u_int8_t oct3;
+ u_int8_t oct4;
+ } chan_desc;
+#if 0 /* spec says we need this but Abissim doesn't use it */
+ struct {
+ u_int8_t tag;
+ u_int8_t len;
+ } mobile_alloc;
+#endif
+} __attribute__ ((packed));
+
+/* Chapter 9.3.22 */
+#define RLL_CAUSE_T200_EXPIRED 0x01
+#define RLL_CAUSE_REEST_REQ 0x02
+#define RLL_CAUSE_UNSOL_UA_RESP 0x03
+#define RLL_CAUSE_UNSOL_DM_RESP 0x04
+#define RLL_CAUSE_UNSOL_DM_RESP_MF 0x05
+#define RLL_CAUSE_UNSOL_SPRV_RESP 0x06
+#define RLL_CAUSE_SEQ_ERR 0x07
+#define RLL_CAUSE_UFRM_INC_PARAM 0x08
+#define RLL_CAUSE_SFRM_INC_PARAM 0x09
+#define RLL_CAUSE_IFRM_INC_MBITS 0x0a
+#define RLL_CAUSE_IFRM_INC_LEN 0x0b
+#define RLL_CAUSE_FRM_UNIMPL 0x0c
+#define RLL_CAUSE_SABM_MF 0x0d
+#define RLL_CAUSE_SABM_INFO_NOTALL 0x0e
+
+/* Chapter 9.3.26 */
+#define RSL_ERRCLS_NORMAL 0x00
+#define RSL_ERRCLS_RESOURCE_UNAVAIL 0x20
+#define RSL_ERRCLS_SERVICE_UNAVAIL 0x30
+#define RSL_ERRCLS_SERVICE_UNIMPL 0x40
+#define RSL_ERRCLS_INVAL_MSG 0x50
+#define RSL_ERRCLS_PROTO_ERROR 0x60
+#define RSL_ERRCLS_INTERWORKING 0x70
+
+#define RSL_ERR_RADIO_IF_FAIL 0x00
+#define RSL_ERR_RADIO_LINK_FAIL 0x01
+#define RSL_ERR_HANDOVER_ACC_FAIL 0x02
+#define RSL_ERR_TALKER_ACC_FAIL 0x03
+#define RSL_ERR_OM_INTERVENTION 0x07
+#define RSL_ERR_EQUIPMENT_FAIL 0x20
+#define RSL_ERR_RR_UNAVAIL 0x21
+#define RSL_ERR_TERR_CH_FAIL 0x22
+#define RSL_ERR_CCCH_OVERLOAD 0x23
+#define RSL_ERR_ACCH_OVERLOAD 0x24
+#define RSL_ERR_PROCESSOR_OVERLOAD 0x25
+#define RSL_ERR_RES_UNAVAIL 0x2f
+#define RSL_ERR_TRANSC_UNAVAIL 0x30
+#define RSL_ERR_SERV_OPT_UNAVAIL 0x3f
+#define RSL_ERR_ENCR_UNIMPL 0x40
+#define RSL_ERR_SEV_OPT_UNIMPL 0x4f
+#define RSL_ERR_RCH_ALR_ACTV_ALLOC 0x50
+#define RSL_ERR_INVALID_MESSAGE 0x5f
+#define RSL_ERR_MSG_DISCR 0x60
+#define RSL_ERR_MSG_TYPE 0x61
+#define RSL_ERR_MSG_SEQA 0x62
+#define RSL_ERR_IE_ERROR 0x63
+#define RSL_ERR_MAND_IE_ERROR 0x64
+#define RSL_ERR_OPT_IE_ERROR 0x65
+#define RSL_ERR_IE_NONEXIST 0x66
+#define RSL_ERR_IE_LENGTH 0x67
+#define RSL_ERR_IE_CONTENT 0x68
+#define RSL_ERR_PROTO 0x6f
+#define RSL_ERR_INTERWORKING 0x7f
+
+/* Chapter 9.3.30 */
+#define RSL_SYSTEM_INFO_8 0x00
+#define RSL_SYSTEM_INFO_1 0x01
+#define RSL_SYSTEM_INFO_2 0x02
+#define RSL_SYSTEM_INFO_3 0x03
+#define RSL_SYSTEM_INFO_4 0x04
+#define RSL_SYSTEM_INFO_5 0x05
+#define RSL_SYSTEM_INFO_6 0x06
+#define RSL_SYSTEM_INFO_7 0x07
+#define RSL_SYSTEM_INFO_16 0x08
+#define RSL_SYSTEM_INFO_17 0x09
+#define RSL_SYSTEM_INFO_2bis 0x0a
+#define RSL_SYSTEM_INFO_2ter 0x0b
+#define RSL_SYSTEM_INFO_5bis 0x0d
+#define RSL_SYSTEM_INFO_5ter 0x0e
+#define RSL_SYSTEM_INFO_10 0x0f
+#define REL_EXT_MEAS_ORDER 0x47
+#define RSL_MEAS_INFO 0x48
+#define RSL_SYSTEM_INFO_13 0x28
+#define RSL_SYSTEM_INFO_2quater 0x29
+#define RSL_SYSTEM_INFO_9 0x2a
+#define RSL_SYSTEM_INFO_18 0x2b
+#define RSL_SYSTEM_INFO_19 0x2c
+#define RSL_SYSTEM_INFO_20 0x2d
+
+/* Chapter 9.3.40 */
+#define RSL_CHANNEED_ANY 0x00
+#define RSL_CHANNEED_SDCCH 0x01
+#define RSL_CHANNEED_TCH_F 0x02
+#define RSL_CHANNEED_TCH_ForH 0x03
+
+/* Chapter 3.3.2.3 Brocast control channel */
+/* CCCH-CONF, NC is not combined */
+#define RSL_BCCH_CCCH_CONF_1_NC 0x00
+#define RSL_BCCH_CCCH_CONF_1_C 0x01
+#define RSL_BCCH_CCCH_CONF_2_NC 0x02
+#define RSL_BCCH_CCCH_CONF_3_NC 0x04
+#define RSL_BCCH_CCCH_CONF_4_NC 0x06
+
+/* BS-PA-MFRMS */
+#define RSL_BS_PA_MFRMS_2 0x00
+#define RSL_BS_PA_MFRMS_3 0x01
+#define RSL_BS_PA_MFRMS_4 0x02
+#define RSL_BS_PA_MFRMS_5 0x03
+#define RSL_BS_PA_MFRMS_6 0x04
+#define RSL_BS_PA_MFRMS_7 0x05
+#define RSL_BS_PA_MFRMS_8 0x06
+#define RSL_BS_PA_MFRMS_9 0x07
+
+
+#include "msgb.h"
+
+int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type,
+ const u_int8_t *data, int len);
+int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type,
+ const u_int8_t *data, int len);
+int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
+ u_int8_t act_type,
+ struct rsl_ie_chan_mode *chan_mode,
+ struct rsl_ie_chan_ident *chan_ident,
+ u_int8_t bs_power, u_int8_t ms_power,
+ u_int8_t ta);
+int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
+ u_int8_t ta, u_int8_t mode);
+int rsl_chan_mode_modify_req(struct gsm_lchan *ts);
+int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
+ u_int8_t *ms_ident, u_int8_t chan_needed);
+int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_needed,
+ struct gsm_subscriber *subscr);
+int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val);
+
+int rsl_data_request(struct msgb *msg, u_int8_t link_id);
+
+/* ip.access specfic RSL extensions */
+int rsl_ipacc_bind(struct gsm_lchan *lchan);
+int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip,
+ u_int16_t port, u_int16_t f8, u_int8_t fc);
+
+int abis_rsl_rcvmsg(struct msgb *msg);
+
+unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans,
+ int n_pag_blocks);
+unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res);
+u_int64_t str_to_imsi(const char *imsi_str);
+u_int8_t lchan2chan_nr(struct gsm_lchan *lchan);
+
+/* to be provided by external code */
+int abis_rsl_sendmsg(struct msgb *msg);
+int rsl_chan_release(struct gsm_lchan *lchan);
+
+/* BCCH related code */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
+int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf);
+int rsl_number_of_paging_subchannels(struct gsm_bts *bts);
+
+#endif /* RSL_MT_H */
+
diff --git a/openbsc/include/openbsc/call_handling.h b/openbsc/include/openbsc/call_handling.h
new file mode 100644
index 000000000..02027889e
--- /dev/null
+++ b/openbsc/include/openbsc/call_handling.h
@@ -0,0 +1,64 @@
+/*
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2008 by Stefan Schmidt <stefan@datenfreihafen.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _CALL_HANDLING_H
+#define _CALL_HANDLING_H
+
+#include "linuxlist.h"
+#include "gsm_subscriber.h"
+#include "timer.h"
+
+/*
+ * State transitions to be seen from the outside
+ */
+#define CALL_STATE_NULL 0
+#define CALL_STATE_SETUP 1
+#define CALL_STATE_PROCEED 2
+#define CALL_STATE_ALERT 3
+#define CALL_STATE_CONNECT 4
+#define CALL_STATE_ACTIVE 5
+#define CALL_STATE_RELEASE 6
+
+struct call_data {
+ struct llist_head entry;
+ void (*state_change_cb)(int oldstate, int newstate, int event, void *data);
+ void *data;
+ char *destination_number;
+
+ /* Internal */
+ int state;
+ char tmsi[GSM_TMSI_LENGTH];
+ struct timer_list t30x; /* to be added for... */
+};
+
+
+int call_initiate(struct call_data *call, char *tmsi);
+void call_abort(struct call_data *call);
+
+/**
+ * Get notified about new incoming calls. The call_data is owned
+ * and managed by the internal call handling.
+ */
+void call_set_callback(void (*cb)(struct call_data *call, void *data), void* data);
+void call_proceed(struct call_data *call_data);
+void call_connect(struct call_data *call_data);
+
+#endif /* _CALL_HANDLING_H */
diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h
new file mode 100644
index 000000000..d6d367c84
--- /dev/null
+++ b/openbsc/include/openbsc/chan_alloc.h
@@ -0,0 +1,49 @@
+/* Management functions to allocate/release struct gsm_lchan */
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#ifndef _CHAN_ALLOC_H
+#define _CHAN_ALLOC_H
+
+#include "gsm_subscriber.h"
+
+/* Special allocator for C0 of BTS */
+struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
+ enum gsm_phys_chan_config pchan);
+
+/* Regular physical channel allocator */
+struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
+ enum gsm_phys_chan_config pchan);
+
+/* Regular physical channel (TS) */
+void ts_free(struct gsm_bts_trx_ts *ts);
+
+/* Find an allocated channel */
+struct gsm_lchan *lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr);
+
+/* Allocate a logical channel (SDCCH, TCH, ...) */
+struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type);
+
+/* Free a logical channel (SDCCH, TCH, ...) */
+void lchan_free(struct gsm_lchan *lchan);
+
+/* Consider releasing the channel */
+int lchan_auto_release(struct gsm_lchan *lchan);
+
+#endif /* _CHAN_ALLOC_H */
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
new file mode 100644
index 000000000..61a3ac47c
--- /dev/null
+++ b/openbsc/include/openbsc/db.h
@@ -0,0 +1,44 @@
+/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _DB_H
+#define _DB_H
+
+#include <sys/types.h>
+
+#include <openbsc/gsm_subscriber.h>
+
+/* one time initialisation */
+int db_init(const char *name);
+int db_prepare();
+int db_fini();
+
+/* subscriber management */
+struct gsm_subscriber* db_create_subscriber(char *imsi);
+struct gsm_subscriber* db_get_subscriber(enum gsm_subscriber_field field, const char *subscr);
+int db_sync_subscriber(struct gsm_subscriber* subscriber);
+int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber);
+int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei);
+
+/* SMS store-and-forward */
+int db_sms_store(struct gsm_sms *sms);
+struct gsm_sms *db_sms_get_unsent(int min_id);
+int db_sms_mark_sent(struct gsm_sms *sms);
+#endif /* _DB_H */
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
new file mode 100644
index 000000000..63f9e671c
--- /dev/null
+++ b/openbsc/include/openbsc/debug.h
@@ -0,0 +1,39 @@
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#define DEBUG
+
+#define DRLL 0x0001
+#define DCC 0x0002
+#define DMM 0x0004
+#define DRR 0x0008
+#define DRSL 0x0010
+#define DNM 0x0020
+
+#define DMNCC 0x0080
+#define DSMS 0x0100
+#define DPAG 0x0200
+
+#define DMI 0x1000
+#define DMIB 0x2000
+#define DMUX 0x4000
+#define DINP 0x8000
+
+#ifdef DEBUG
+#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
+#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
+#else
+#define DEBUGP(xss, fmt, args...)
+#define DEBUGPC(ss, fmt, args...)
+#endif
+
+#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
+
+char *hexdump(unsigned char *buf, int len);
+void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...);
+void debug_parse_category_mask(const char* mask);
+void debug_use_color(int use_color);
+void debug_timestamp(int enable);
+extern unsigned int debug_mask;
+
+#endif /* _DEBUG_H */
diff --git a/openbsc/include/openbsc/e1_input.h b/openbsc/include/openbsc/e1_input.h
new file mode 100644
index 000000000..132672364
--- /dev/null
+++ b/openbsc/include/openbsc/e1_input.h
@@ -0,0 +1,159 @@
+#ifndef _E1_INPUT_H
+#define _E1_INPUT_H
+
+#include <stdlib.h>
+#include <netinet/in.h>
+
+#include <openbsc/linuxlist.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/msgb.h>
+#include <openbsc/select.h>
+#include <openbsc/subchan_demux.h>
+
+#define NUM_E1_TS 32
+
+enum e1inp_sign_type {
+ E1INP_SIGN_NONE,
+ E1INP_SIGN_OML,
+ E1INP_SIGN_RSL,
+};
+const char *e1inp_signtype_name(enum e1inp_sign_type tp);
+
+struct e1inp_ts;
+
+struct e1inp_sign_link {
+ /* list of signalling links */
+ struct llist_head list;
+
+ /* to which timeslot do we belong? */
+ struct e1inp_ts *ts;
+
+ enum e1inp_sign_type type;
+
+ /* trx for msg->trx of received msgs */
+ struct gsm_bts_trx *trx;
+
+ /* msgb queue of to-be-transmitted msgs */
+ struct llist_head tx_list;
+
+ /* SAPI and TEI on the E1 TS */
+ u_int8_t sapi;
+ u_int8_t tei;
+
+ union {
+ struct {
+ u_int8_t channel;
+ } misdn;
+ } driver;
+};
+
+enum e1inp_ts_type {
+ E1INP_TS_TYPE_NONE,
+ E1INP_TS_TYPE_SIGN,
+ E1INP_TS_TYPE_TRAU,
+};
+const char *e1inp_tstype_name(enum e1inp_ts_type tp);
+
+/* A timeslot in the E1 interface */
+struct e1inp_ts {
+ enum e1inp_ts_type type;
+ int num;
+
+ /* to which line do we belong ? */
+ struct e1inp_line *line;
+
+ union {
+ struct {
+ /* list of all signalling links on this TS */
+ struct llist_head sign_links;
+ /* timer when to dequeue next frame */
+ struct timer_list tx_timer;
+ } sign;
+ struct {
+ /* subchannel demuxer for frames from E1 */
+ struct subch_demux demux;
+ /* subchannel muxer for frames to E1 */
+ struct subch_mux mux;
+ } trau;
+ };
+ union {
+ struct {
+ /* mISDN driver has one fd for each ts */
+ struct bsc_fd fd;
+ } misdn;
+ struct {
+ /* ip.access driver has one fd for each ts */
+ struct bsc_fd fd;
+ } ipaccess;
+
+ } driver;
+};
+
+struct e1inp_driver {
+ struct llist_head list;
+ const char *name;
+ int (*want_write)(struct e1inp_ts *ts);
+};
+
+struct e1inp_line {
+ struct llist_head list;
+ unsigned int num;
+ const char *name;
+
+ /* array of timestlots */
+ struct e1inp_ts ts[NUM_E1_TS];
+
+ struct e1inp_driver *driver;
+ void *driver_data;
+};
+
+/* register a driver with the E1 core */
+int e1inp_driver_register(struct e1inp_driver *drv);
+
+/* register a line with the E1 core */
+int e1inp_line_register(struct e1inp_line *line);
+
+/* find a sign_link for given TEI and SAPI in a TS */
+struct e1inp_sign_link *
+e1inp_lookup_sign_link(struct e1inp_ts *ts, u_int8_t tei,
+ u_int8_t sapi);
+
+/* create a new signalling link in a E1 timeslot */
+struct e1inp_sign_link *
+e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
+ struct gsm_bts_trx *trx, u_int8_t tei,
+ u_int8_t sapi);
+
+/* configure and initialize one e1inp_ts */
+int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
+ enum e1inp_ts_type type);
+
+/* Call from the Stack: configuration of this TS has changed */
+int e1inp_update_ts(struct e1inp_ts *ts);
+
+/* Receive a packet from the E1 driver */
+int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
+ u_int8_t tei, u_int8_t sapi);
+
+/* called by driver if it wants to transmit on a given TS */
+struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
+ struct e1inp_sign_link **sign_link);
+
+/* called by driver in case some kind of link state event */
+int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi);
+
+/* Write LAPD frames to the fd. */
+void e1_set_pcap_fd(int fd);
+
+/* called by TRAU muxer to obtain the destination mux entity */
+struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr);
+
+/* e1_config.c */
+int e1_config(struct gsm_bts *bts, int cardnr, int release_l2);
+int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin);
+int ipaccess_setup(struct gsm_network *gsmnet);
+
+extern struct llist_head e1inp_driver_list;
+extern struct llist_head e1inp_line_list;
+
+#endif /* _E1_INPUT_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
new file mode 100644
index 000000000..fe18f4ee5
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -0,0 +1,568 @@
+#ifndef _GSM_04_08_H
+#define _GSM_04_08_H
+
+/* GSM TS 04.08 definitions */
+struct gsm_lchan;
+
+struct gsm48_classmark1 {
+ u_int8_t spare:1,
+ rev_level:2,
+ es_ind:1,
+ a5_1:1,
+ pwr_lev:3;
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.5 */
+struct gsm48_chan_desc {
+ u_int8_t chan_nr;
+ union {
+ struct {
+ u_int8_t maio_high:4,
+ h:1,
+ tsc:3;
+ u_int8_t hsn:6,
+ maio_low:2;
+ } h1;
+ struct {
+ u_int8_t arfcn_high:2,
+ spare:2,
+ h:1,
+ tsc:3;
+ u_int8_t arfcn_low;
+ } h0;
+ };
+} __attribute__ ((packed));
+
+/* Chapter 10.5.2.30 */
+struct gsm48_req_ref {
+ u_int8_t ra;
+ u_int8_t t3_high:3,
+ t1_:5;
+ u_int8_t t2:5,
+ t3_low:3;
+} __attribute__ ((packed));
+
+/* Chapter 9.1.5 */
+struct gsm48_chan_mode_modify {
+ struct gsm48_chan_desc chan_desc;
+ u_int8_t mode;
+} __attribute__ ((packed));
+
+#define GSM48_CMODE_SIGN 0x00
+#define GSM48_CMODE_SPEECH_V1 0x01
+#define GSM48_CMODE_SPEECH_EFR 0x21
+#define GSM48_CMODE_SPEECH_AMR 0x41
+#define GSM48_CMODE_DATA_14k5 0x0f
+#define GSM48_CMODE_DATA_12k0 0x03
+#define GSM48_CMODE_DATA_6k0 0x0b
+#define GSM48_CMODE_DATA_3k6 0x23
+
+/* Chapter 9.1.18 */
+struct gsm48_imm_ass {
+ u_int8_t l2_plen;
+ u_int8_t proto_discr;
+ u_int8_t msg_type;
+ u_int8_t page_mode;
+ struct gsm48_chan_desc chan_desc;
+ struct gsm48_req_ref req_ref;
+ u_int8_t timing_advance;
+ u_int8_t mob_alloc_len;
+ u_int8_t mob_alloc[0];
+} __attribute__ ((packed));
+
+/* Chapter 10.5.1.3 */
+struct gsm48_loc_area_id {
+ u_int8_t digits[3]; /* BCD! */
+ u_int16_t lac;
+} __attribute__ ((packed));
+
+/* Section 9.2.15 */
+struct gsm48_loc_upd_req {
+ u_int8_t type:4,
+ key_seq:4;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_classmark1 classmark1;
+ u_int8_t mi_len;
+ u_int8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.1 */
+struct gsm48_hdr {
+ u_int8_t proto_discr;
+ u_int8_t msg_type;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.3x System information Type header */
+struct gsm48_system_information_type_header {
+ u_int8_t l2_plen;
+ u_int8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ u_int8_t system_information;
+} __attribute__ ((packed));
+
+struct gsm48_rach_control {
+ u_int8_t re :1,
+ cell_bar :1,
+ tx_integer :4,
+ max_trans :2;
+ u_int8_t t2;
+ u_int8_t t3;
+} __attribute__ ((packed));
+
+/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
+struct gsm48_control_channel_descr {
+ u_int8_t ccch_conf :3,
+ bs_ag_blks_res :3,
+ att :1,
+ spare1 :1;
+ u_int8_t bs_pa_mfrms : 3,
+ spare2 :5;
+ u_int8_t t3212;
+} __attribute__ ((packed));
+
+/* Section 9.2.9 CM service request */
+struct gsm48_service_request {
+ u_int8_t cm_service_type : 4,
+ cipher_key_seq : 4;
+ /* length + 3 bytes */
+ u_int32_t classmark;
+ u_int8_t mi_len;
+ u_int8_t mi[0];
+ /* optional priority level */
+} __attribute__ ((packed));
+
+/* Section 9.1.31 System information Type 1 */
+struct gsm48_system_information_type_1 {
+ struct gsm48_system_information_type_header header;
+ u_int8_t cell_channel_description[16];
+ struct gsm48_rach_control rach_control;
+ u_int8_t s1_reset;
+} __attribute__ ((packed));
+
+/* Section 9.1.32 System information Type 2 */
+struct gsm48_system_information_type_2 {
+ struct gsm48_system_information_type_header header;
+ u_int8_t bcch_frequency_list[16];
+ u_int8_t ncc_permitted;
+ struct gsm48_rach_control rach_control;
+} __attribute__ ((packed));
+
+/* Section 9.1.35 System information Type 3 */
+struct gsm48_system_information_type_3 {
+ struct gsm48_system_information_type_header header;
+ u_int16_t cell_identity;
+ struct gsm48_loc_area_id lai;
+ struct gsm48_control_channel_descr control_channel_desc;
+ u_int8_t cell_options;
+ u_int8_t cell_selection[2];
+ struct gsm48_rach_control rach_control;
+ u_int8_t s3_reset_octets[4];
+} __attribute__ ((packed));
+
+/* Section 9.1.36 System information Type 4 */
+struct gsm48_system_information_type_4 {
+ struct gsm48_system_information_type_header header;
+ struct gsm48_loc_area_id lai;
+ u_int8_t cell_selection[2];
+ struct gsm48_rach_control rach_control;
+ /* optional CBCH conditional CBCH... followed by
+ mandantory SI 4 Reset Octets
+ */
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 9.1.37 System information Type 5 */
+struct gsm48_system_information_type_5 {
+ u_int8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ u_int8_t system_information;
+ u_int8_t bcch_frequency_list[16];
+} __attribute__ ((packed));
+
+/* Section 9.1.40 System information Type 6 */
+struct gsm48_system_information_type_6 {
+ u_int8_t rr_protocol_discriminator :4,
+ skip_indicator:4;
+ u_int8_t system_information;
+ u_int8_t cell_identity[2];
+ struct gsm48_loc_area_id lai;
+ u_int8_t cell_options;
+ u_int8_t ncc_permitted;
+ u_int8_t si_6_reset[0];
+} __attribute__ ((packed));
+
+/* Section 9.2.12 IMSI Detach Indication */
+struct gsm48_imsi_detach_ind {
+ struct gsm48_classmark1 classmark1;
+ u_int8_t mi_len;
+ u_int8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.2 + GSM 04.07 12.2.3.1.1 */
+#define GSM48_PDISC_GROUP_CC 0x00
+#define GSM48_PDISC_BCAST_CC 0x01
+#define GSM48_PDISC_PDSS1 0x02
+#define GSM48_PDISC_CC 0x03
+#define GSM48_PDISC_PDSS2 0x04
+#define GSM48_PDISC_MM 0x05
+#define GSM48_PDISC_RR 0x06
+#define GSM48_PDISC_MM_GPRS 0x08
+#define GSM48_PDISC_SMS 0x09
+#define GSM48_PDISC_SM_GPRS 0x0a
+#define GSM48_PDISC_NC_SS 0x0b
+#define GSM48_PDISC_LOC 0x0c
+#define GSM48_PDISC_MASK 0x0f
+#define GSM48_PDISC_USSD 0x11
+
+/* Section 10.4 */
+#define GSM48_MT_RR_INIT_REQ 0x3c
+#define GSM48_MT_RR_ADD_ASS 0x3b
+#define GSM48_MT_RR_IMM_ASS 0x3f
+#define GSM48_MT_RR_IMM_ASS_EXT 0x39
+#define GSM48_MT_RR_IMM_ASS_REJ 0x3a
+
+#define GSM48_MT_RR_CIPH_M_CMD 0x35
+#define GSM48_MT_RR_CIPH_M_COMPL 0x32
+
+#define GSM48_MT_RR_CFG_CHG_CMD 0x30
+#define GSM48_MT_RR_CFG_CHG_ACK 0x31
+#define GSM48_MT_RR_CFG_CHG_REJ 0x33
+
+#define GSM48_MT_RR_ASS_CMD 0x2e
+#define GSM48_MT_RR_ASS_COMPL 0x29
+#define GSM48_MT_RR_ASS_FAIL 0x2f
+#define GSM48_MT_RR_HANDO_CMD 0x2b
+#define GSM48_MT_RR_HANDO_COMPL 0x2c
+#define GSM48_MT_RR_HANDO_FAIL 0x28
+#define GSM48_MT_RR_HANDO_INFO 0x2d
+
+#define GSM48_MT_RR_CELL_CHG_ORDER 0x08
+#define GSM48_MT_RR_PDCH_ASS_CMD 0x23
+
+#define GSM48_MT_RR_CHAN_REL 0x0d
+#define GSM48_MT_RR_PART_REL 0x0a
+#define GSM48_MT_RR_PART_REL_COMP 0x0f
+
+#define GSM48_MT_RR_PAG_REQ_1 0x21
+#define GSM48_MT_RR_PAG_REQ_2 0x22
+#define GSM48_MT_RR_PAG_REQ_3 0x24
+#define GSM48_MT_RR_PAG_RESP 0x27
+#define GSM48_MT_RR_NOTIF_NCH 0x20
+#define GSM48_MT_RR_NOTIF_FACCH 0x25
+#define GSM48_MT_RR_NOTIF_RESP 0x26
+
+#define GSM48_MT_RR_SYSINFO_8 0x18
+#define GSM48_MT_RR_SYSINFO_1 0x19
+#define GSM48_MT_RR_SYSINFO_2 0x1a
+#define GSM48_MT_RR_SYSINFO_3 0x1b
+#define GSM48_MT_RR_SYSINFO_4 0x1c
+#define GSM48_MT_RR_SYSINFO_5 0x1d
+#define GSM48_MT_RR_SYSINFO_6 0x1e
+#define GSM48_MT_RR_SYSINFO_7 0x1f
+
+#define GSM48_MT_RR_SYSINFO_2bis 0x02
+#define GSM48_MT_RR_SYSINFO_2ter 0x03
+#define GSM48_MT_RR_SYSINFO_5bis 0x05
+#define GSM48_MT_RR_SYSINFO_5ter 0x06
+#define GSM48_MT_RR_SYSINFO_9 0x04
+#define GSM48_MT_RR_SYSINFO_13 0x00
+
+#define GSM48_MT_RR_SYSINFO_16 0x3d
+#define GSM48_MT_RR_SYSINFO_17 0x3e
+
+#define GSM48_MT_RR_CHAN_MODE_MODIF 0x10
+#define GSM48_MT_RR_STATUS 0x12
+#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK 0x17
+#define GSM48_MT_RR_FREQ_REDEF 0x14
+#define GSM48_MT_RR_MEAS_REP 0x15
+#define GSM48_MT_RR_CLSM_CHG 0x16
+#define GSM48_MT_RR_CLSM_ENQ 0x13
+#define GSM48_MT_RR_EXT_MEAS_REP 0x36
+#define GSM48_MT_RR_EXT_MEAS_REP_ORD 0x37
+#define GSM48_MT_RR_GPRS_SUSP_REQ 0x34
+
+#define GSM48_MT_RR_VGCS_UPL_GRANT 0x08
+#define GSM48_MT_RR_UPLINK_RELEASE 0x0e
+#define GSM48_MT_RR_UPLINK_FREE 0x0c
+#define GSM48_MT_RR_UPLINK_BUSY 0x2a
+#define GSM48_MT_RR_TALKER_IND 0x11
+
+#define GSM48_MT_RR_APP_INFO 0x38
+
+/* Table 10.2/3GPP TS 04.08 */
+#define GSM48_MT_MM_IMSI_DETACH_IND 0x01
+#define GSM48_MT_MM_LOC_UPD_ACCEPT 0x02
+#define GSM48_MT_MM_LOC_UPD_REJECT 0x04
+#define GSM48_MT_MM_LOC_UPD_REQUEST 0x08
+
+#define GSM48_MT_MM_AUTH_REJ 0x11
+#define GSM48_MT_MM_AUTH_REQ 0x12
+#define GSM48_MT_MM_AUTH_RESP 0x14
+#define GSM48_MT_MM_ID_REQ 0x18
+#define GSM48_MT_MM_ID_RESP 0x19
+#define GSM48_MT_MM_TMSI_REALL_CMD 0x1a
+#define GSM48_MT_MM_TMSI_REALL_COMPL 0x1b
+
+#define GSM48_MT_MM_CM_SERV_ACC 0x21
+#define GSM48_MT_MM_CM_SERV_REJ 0x22
+#define GSM48_MT_MM_CM_SERV_ABORT 0x23
+#define GSM48_MT_MM_CM_SERV_REQ 0x24
+#define GSM48_MT_MM_CM_SERV_PROMPT 0x25
+#define GSM48_MT_MM_CM_REEST_REQ 0x28
+#define GSM48_MT_MM_ABORT 0x29
+
+#define GSM48_MT_MM_NULL 0x30
+#define GSM48_MT_MM_STATUS 0x31
+#define GSM48_MT_MM_INFO 0x32
+
+/* Table 10.3/3GPP TS 04.08 */
+#define GSM48_MT_CC_ALERTING 0x01
+#define GSM48_MT_CC_CALL_CONF 0x08
+#define GSM48_MT_CC_CALL_PROC 0x02
+#define GSM48_MT_CC_CONNECT 0x07
+#define GSM48_MT_CC_CONNECT_ACK 0x0f
+#define GSM48_MT_CC_EMERG_SETUP 0x0e
+#define GSM48_MT_CC_PROGRESS 0x03
+#define GSM48_MT_CC_ESTAB 0x04
+#define GSM48_MT_CC_ESTAB_CONF 0x06
+#define GSM48_MT_CC_RECALL 0x0b
+#define GSM48_MT_CC_START_CC 0x09
+#define GSM48_MT_CC_SETUP 0x05
+
+#define GSM48_MT_CC_MODIFY 0x17
+#define GSM48_MT_CC_MODIFY_COMPL 0x1f
+#define GSM48_MT_CC_MODIFY_REJECT 0x13
+#define GSM48_MT_CC_USER_INFO 0x10
+#define GSM48_MT_CC_HOLD 0x18
+#define GSM48_MT_CC_HOLD_ACK 0x19
+#define GSM48_MT_CC_HOLD_REJ 0x1a
+#define GSM48_MT_CC_RETR 0x1c
+#define GSM48_MT_CC_RETR_ACK 0x1d
+#define GSM48_MT_CC_RETR_REJ 0x1e
+
+#define GSM48_MT_CC_DISCONNECT 0x25
+#define GSM48_MT_CC_RELEASE 0x2d
+#define GSM48_MT_CC_RELEASE_COMPL 0x2a
+
+#define GSM48_MT_CC_CONG_CTRL 0x39
+#define GSM48_MT_CC_NOTIFY 0x3e
+#define GSM48_MT_CC_STATUS 0x3d
+#define GSM48_MT_CC_STATUS_ENQ 0x34
+#define GSM48_MT_CC_START_DTMF 0x35
+#define GSM48_MT_CC_STOP_DTMF 0x31
+#define GSM48_MT_CC_STOP_DTMF_ACK 0x32
+#define GSM48_MT_CC_START_DTMF_ACK 0x36
+#define GSM48_MT_CC_START_DTMF_REJ 0x37
+#define GSM48_MT_CC_FACILITY 0x3a
+
+/* FIXME: Table 10.4 / 10.4a (GPRS) */
+
+/* Section 10.5.2.26, Table 10.5.64 */
+#define GSM48_PM_MASK 0x03
+#define GSM48_PM_NORMAL 0x00
+#define GSM48_PM_EXTENDED 0x01
+#define GSM48_PM_REORG 0x02
+#define GSM48_PM_SAME 0x03
+
+/* Chapter 10.5.3.5 / Table 10.5.93 */
+#define GSM48_LUPD_NORMAL 0x0
+#define GSM48_LUPD_PERIODIC 0x1
+#define GSM48_LUPD_IMSI_ATT 0x2
+#define GSM48_LUPD_RESERVED 0x3
+
+/* Table 10.5.4 */
+#define GSM_MI_TYPE_MASK 0x07
+#define GSM_MI_TYPE_NONE 0x00
+#define GSM_MI_TYPE_IMSI 0x01
+#define GSM_MI_TYPE_IMEI 0x02
+#define GSM_MI_TYPE_IMEISV 0x03
+#define GSM_MI_TYPE_TMSI 0x04
+#define GSM_MI_ODD 0x08
+
+#define GSM48_IE_MOBILE_ID 0x17
+#define GSM48_IE_NAME_LONG 0x43 /* 10.5.3.5a */
+#define GSM48_IE_NAME_SHORT 0x45 /* 10.5.3.5a */
+#define GSM48_IE_UTC 0x46 /* 10.5.3.8 */
+#define GSM48_IE_NET_TIME_TZ 0x47 /* 10.5.3.9 */
+#define GSM48_IE_LSA_IDENT 0x48 /* 10.5.3.11 */
+
+#define GSM48_IE_BEARER_CAP 0x04 /* 10.5.4.5 */
+#define GSM48_IE_CAUSE 0x08 /* 10.5.4.11 */
+#define GSM48_IE_CC_CAP 0x15 /* 10.5.4.5a */
+#define GSM48_IE_ALERT 0x19 /* 10.5.4.26 */
+#define GSM48_IE_FACILITY 0x1c /* 10.5.4.15 */
+#define GSM48_IE_PROGR_IND 0x1e /* 10.5.4.21 */
+#define GSM48_IE_AUX_STATUS 0x24 /* 10.5.4.4 */
+#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */
+#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */
+#define GSM48_IE_CONN_NUM 0x4c /* 10.5.4.13 */
+#define GSM48_IE_CONN_SUBADDR 0x4d /* 10.5.4.14 */
+#define GSM48_IE_CALLING_BCD 0x5c /* 10.5.4.9 */
+#define GSM48_IE_CALLING_SUB 0x5d /* 10.5.4.10 */
+#define GSM48_IE_CALLED_BCD 0x5e /* 10.5.4.7 */
+#define GSM48_IE_CALLED_SUB 0x6d /* 10.5.4.8 */
+#define GSM48_IE_REDIR_BCD 0x74 /* 10.5.4.21a */
+#define GSM48_IE_REDIR_SUB 0x75 /* 10.5.4.21b */
+#define GSM48_IE_LOWL_COMPAT 0x7c /* 10.5.4.18 */
+#define GSM48_IE_HIGHL_COMPAT 0x7d /* 10.5.4.16 */
+#define GSM48_IE_USER_USER 0x7e /* 10.5.4.25 */
+#define GSM48_IE_SS_VERS 0x7f /* 10.5.4.24 */
+#define GSM48_IE_MORE_DATA 0xa0 /* 10.5.4.19 */
+#define GSM48_IE_CLIR_SUPP 0xa1 /* 10.5.4.11a */
+#define GSM48_IE_CLIR_INVOC 0xa2 /* 10.5.4.11b */
+#define GSM48_IE_REV_C_SETUP 0xa3 /* 10.5.4.22a */
+
+/* Section 10.5.4.11 / Table 10.5.122 */
+#define GSM48_CAUSE_CS_GSM 0x60
+
+/* Section 9.1.2 / Table 9.3 */
+#define GSM48_IE_FRQLIST_AFTER 0x05
+#define GSM48_IE_CELL_CH_DESC 0x62
+#define GSM48_IE_MSLOT_DESC 0x10
+#define GSM48_IE_CHANMODE_1 0x63
+#define GSM48_IE_CHANMODE_2 0x11
+#define GSM48_IE_CHANMODE_3 0x13
+#define GSM48_IE_CHANMODE_4 0x14
+#define GSM48_IE_CHANMODE_5 0x15
+#define GSM48_IE_CHANMODE_6 0x16
+#define GSM48_IE_CHANMODE_7 0x17
+#define GSM48_IE_CHANMODE_8 0x18
+#define GSM48_IE_CHANDESC_2 0x64
+/* FIXME */
+
+/* Section 10.5.4.23 / Table 10.5.130 */
+enum gsm48_signal_val {
+ GSM48_SIGNAL_DIALTONE = 0x00,
+ GSM48_SIGNAL_RINGBACK = 0x01,
+ GSM48_SIGNAL_INTERCEPT = 0x02,
+ GSM48_SIGNAL_NET_CONG = 0x03,
+ GSM48_SIGNAL_BUSY = 0x04,
+ GSM48_SIGNAL_CONFIRM = 0x05,
+ GSM48_SIGNAL_ANSWER = 0x06,
+ GSM48_SIGNAL_CALL_WAIT = 0x07,
+ GSM48_SIGNAL_OFF_HOOK = 0x08,
+ GSM48_SIGNAL_OFF = 0x3f,
+ GSM48_SIGNAL_ALERT_OFF = 0x4f,
+};
+
+enum gsm48_cause_loc {
+ GSM48_CAUSE_LOC_USER = 0x00,
+ GSM48_CAUSE_LOC_PRN_S_LU = 0x01,
+ GSM48_CAUSE_LOC_PUN_S_LU = 0x02,
+ GSM48_CAUSE_LOC_TRANS_NET = 0x03,
+ GSM48_CAUSE_LOC_PUN_S_RU = 0x04,
+ GSM48_CAUSE_LOC_PRN_S_RU = 0x05,
+ /* not defined */
+ GSM48_CAUSE_LOC_INN_NET = 0x07,
+ GSM48_CAUSE_LOC_NET_BEYOND = 0x0a,
+};
+
+/* Section 10.5.2.31 RR Cause / Table 10.5.70 */
+enum gsm48_rr_cause {
+ GSM48_RR_CAUSE_NORMAL = 0x00,
+ GSM48_RR_CAUSE_ABNORMAL_UNSPEC = 0x01,
+ GSM48_RR_CAUSE_ABNORMAL_UNACCT = 0x02,
+ GSM48_RR_CAUSE_ABNORMAL_TIMER = 0x03,
+ GSM48_RR_CAUSE_ABNORMAL_NOACT = 0x04,
+ GSM48_RR_CAUSE_PREMPTIVE_REL = 0x05,
+ GSM48_RR_CAUSE_HNDOVER_IMP = 0x06,
+ GSM48_RR_CAUSE_CHAN_MODE_UNACCT = 0x07,
+ GSM48_RR_CAUSE_FREQ_NOT_IMPL = 0x08,
+ GSM48_RR_CAUSE_CALL_CLEARED = 0x41,
+ GSM48_RR_CAUSE_SEMANT_INCORR = 0x5f,
+ GSM48_RR_CAUSE_INVALID_MAND_INF = 0x60,
+ GSM48_RR_CAUSE_MSG_TYPE_N = 0x61,
+ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT= 0x62,
+ GSM48_RR_CAUSE_COND_IE_ERROR = 0x64,
+ GSM48_RR_CAUSE_NO_CELL_ALLOC_A = 0x65,
+ GSM48_RR_CAUSE_PROT_ERROR_UNSPC = 0x6f,
+};
+
+/* Annex G, GSM specific cause values for mobility management */
+enum gsm48_reject_value {
+ GSM48_REJECT_IMSI_UNKNOWN_IN_HLR = 2,
+ GSM48_REJECT_ILLEGAL_MS = 3,
+ GSM48_REJECT_IMSI_UNKNOWN_IN_VLR = 4,
+ GSM48_REJECT_IMEI_NOT_ACCEPTED = 5,
+ GSM48_REJECT_ILLEGAL_ME = 6,
+ GSM48_REJECT_PLMN_NOT_ALLOWED = 11,
+ GSM48_REJECT_LOC_NOT_ALLOWED = 12,
+ GSM48_REJECT_ROAMING_NOT_ALLOWED = 13,
+ GSM48_REJECT_NETWORK_FAILURE = 17,
+ GSM48_REJECT_CONGESTION = 22,
+ GSM48_REJECT_SRV_OPT_NOT_SUPPORTED = 32,
+ GSM48_REJECT_RQD_SRV_OPT_NOT_SUPPORTED = 33,
+ GSM48_REJECT_SRV_OPT_TMP_OUT_OF_ORDER = 34,
+ GSM48_REJECT_CALL_CAN_NOT_BE_IDENTIFIED = 38,
+ GSM48_REJECT_INCORRECT_MESSAGE = 95,
+ GSM48_REJECT_INVALID_MANDANTORY_INF = 96,
+ GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED = 97,
+ GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE = 98,
+ GSM48_REJECT_INF_ELEME_NOT_IMPLEMENTED = 99,
+ GSM48_REJECT_CONDTIONAL_IE_ERROR = 100,
+ GSM48_REJECT_MSG_NOT_COMPATIBLE = 101,
+ GSM48_REJECT_PROTOCOL_ERROR = 111,
+
+ /* according to G.6 Additional cause codes for GMM */
+ GSM48_REJECT_GPRS_NOT_ALLOWED = 7,
+ GSM48_REJECT_SERVICES_NOT_ALLOWED = 8,
+ GSM48_REJECT_MS_IDENTITY_NOT_DERVIVABLE = 9,
+ GSM48_REJECT_IMPLICITLY_DETACHED = 10,
+ GSM48_REJECT_GPRS_NOT_ALLOWED_IN_PLMN = 14,
+ GSM48_REJECT_MSC_TMP_NOT_REACHABLE = 16,
+};
+
+
+/* extracted from a L3 measurement report IE */
+struct gsm_meas_rep_cell {
+ u_int8_t rxlev;
+ u_int8_t bcch_freq; /* fixme: translate to ARFCN */
+ u_int8_t bsic;
+};
+
+struct gsm_meas_rep {
+ unsigned int flags;
+ u_int8_t rxlev_full;
+ u_int8_t rxqual_full;
+ u_int8_t rxlev_sub;
+ u_int8_t rxqual_sub;
+ int num_cell;
+ struct gsm_meas_rep_cell cell[6];
+};
+#define MEAS_REP_F_DTX 0x01
+#define MEAS_REP_F_VALID 0x02
+#define MEAS_REP_F_BA1 0x04
+
+void gsm48_parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
+ int len);
+
+
+struct msgb;
+struct gsm_bts;
+struct gsm_subscriber;
+
+/* config options controlling the behaviour of the lower leves */
+void gsm0408_allow_everyone(int allow);
+void gsm0408_set_reject_cause(int cause);
+
+int gsm0408_rcvmsg(struct msgb *msg);
+void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
+ u_int16_t mnc, u_int16_t lac);
+int gsm48_cc_tx_setup(struct gsm_lchan *lchan, struct gsm_subscriber *calling);
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra);
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra);
+
+int gsm48_tx_mm_info(struct gsm_lchan *lchan);
+struct msgb *gsm48_msgb_alloc(void);
+int gsm48_sendmsg(struct msgb *msg);
+int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
+
+int gsm48_send_rr_release(struct gsm_lchan *lchan);
+
+/* convert a ASCII phone number to call-control BCD */
+int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
+ u_int8_t type, const char *input);
+u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv);
+
+#endif
diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
new file mode 100644
index 000000000..12c607f54
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -0,0 +1,172 @@
+#ifndef _GSM_04_11_H
+#define _GSM_04_11_H
+
+/* GSM TS 04.11 definitions */
+
+/* Chapter 8.1.2 (refers to GSM 04.07 Chapter 11.2.3.1.1 */
+#define GSM411_PDISC_SMS 0x09
+
+/* Chapter 8.1.3 */
+#define GSM411_MT_CP_DATA 0x01
+#define GSM411_MT_CP_ACK 0x04
+#define GSM411_MT_CP_ERROR 0x10
+
+enum gsm411_cp_ie {
+ GSM411_CP_IE_USER_DATA = 0x01, /* 8.1.4.1 */
+ GSM411_CP_IE_CAUSE = 0x02, /* 8.1.4.2. */
+};
+
+/* Chapter 8.2.2 */
+#define GSM411_MT_RP_DATA_MO 0x00
+#define GSM411_MT_RP_DATA_MT 0x01
+#define GSM411_MT_RP_ACK_MO 0x02
+#define GSM411_MT_RP_ACK_MT 0x03
+#define GSM411_MT_RP_ERROR_MO 0x04
+#define GSM411_MT_RP_ERROR_MT 0x04
+#define GSM411_MT_RP_SMMA_MO 0x05
+
+enum gsm411_rp_ie {
+ GSM411_IE_RP_USER_DATA = 0x41, /* 8.2.5.3 */
+ GSM411_IE_RP_CAUSE = 0x42, /* 8.2.5.4 */
+};
+
+/* Chapter 8.2.1 */
+struct gsm411_rp_hdr {
+ u_int8_t len;
+ u_int8_t msg_type;
+ u_int8_t msg_ref;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+/* our own enum, not related to on-air protocol */
+enum sms_alphabet {
+ DCS_NONE,
+ DCS_7BIT_DEFAULT,
+ DCS_UCS2,
+ DCS_8BIT_DATA,
+};
+
+/* SMS submit PDU */
+struct sms_submit {
+ u_int8_t *smsc;
+ u_int8_t mti:2;
+ u_int8_t vpf:2;
+ u_int8_t msg_ref;
+ u_int8_t pid;
+ u_int8_t dcs;
+ u_int8_t *vp;
+ u_int8_t ud_len;
+ u_int8_t *user_data;
+
+ /* interpreted */
+ u_int8_t mms:1;
+ u_int8_t sri:1;
+ u_int8_t udhi:1;
+ u_int8_t rp:1;
+ enum sms_alphabet alphabet;
+ char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes BCD == 20 bytes string */
+ unsigned long validity_mins;
+ char decoded[256];
+};
+
+/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */
+#define GSM340_SMS_DELIVER_SC2MS 0x00
+#define GSM340_SMS_DELIVER_REP_MS2SC 0x00
+#define GSM340_SMS_STATUS_REP_SC2MS 0x02
+#define GSM340_SMS_COMMAND_MS2SC 0x02
+#define GSM340_SMS_SUBMIT_MS2SC 0x01
+#define GSM340_SMS_SUBMIT_REP_SC2MS 0x01
+#define GSM340_SMS_RESSERVED 0x03
+
+/* GSM 03.40 / Chapter 9.2.3.2: TP-More-Messages-to-Send */
+#define GSM340_TP_MMS_MORE 0
+#define GSM340_TP_MMS_NO_MORE 1
+
+/* GSM 03.40 / Chapter 9.2.3.3: TP-Validity-Period-Format */
+#define GSM340_TP_VPF_NONE 0
+#define GSM340_TP_VPF_RELATIVE 2
+#define GSM340_TP_VPF_ENHANCED 1
+#define GSM340_TP_VPF_ABSOLUTE 3
+
+/* GSM 03.40 / Chapter 9.2.3.4: TP-Status-Report-Indication */
+#define GSM340_TP_SRI_NONE 0
+#define GSM340_TP_SRI_PRESENT 1
+
+/* GSM 03.40 / Chapter 9.2.3.5: TP-Status-Report-Request */
+#define GSM340_TP_SRR_NONE 0
+#define GSM340_TP_SRR_REQUESTED 1
+
+/* GSM 03.40 / Chapter 9.2.3.9: TP-Protocol-Identifier */
+/* telematic interworking (001 or 111 in bits 7-5) */
+#define GSM340_TP_PID_IMPLICIT 0x00
+#define GSM340_TP_PID_TELEX 0x01
+#define GSM340_TP_PID_FAX_G3 0x02
+#define GSM340_TP_PID_FAX_G4 0x03
+#define GSM340_TP_PID_VOICE 0x04
+#define GSM430_TP_PID_ERMES 0x05
+#define GSM430_TP_PID_NATIONAL_PAGING 0x06
+#define GSM430_TP_PID_VIDEOTEX 0x07
+#define GSM430_TP_PID_TELETEX_UNSPEC 0x08
+#define GSM430_TP_PID_TELETEX_PSPDN 0x09
+#define GSM430_TP_PID_TELETEX_CSPDN 0x0a
+#define GSM430_TP_PID_TELETEX_PSTN 0x0b
+#define GSM430_TP_PID_TELETEX_ISDN 0x0c
+#define GSM430_TP_PID_TELETEX_UCI 0x0d
+#define GSM430_TP_PID_MSG_HANDLING 0x10
+#define GSM430_TP_PID_MSG_X400 0x11
+#define GSM430_TP_PID_EMAIL 0x12
+#define GSM430_TP_PID_GSM_MS 0x1f
+/* if bit 7 = 0 and bit 6 = 1 */
+#define GSM430_TP_PID_SMS_TYPE_0 0
+#define GSM430_TP_PID_SMS_TYPE_1 1
+#define GSM430_TP_PID_SMS_TYPE_2 2
+#define GSM430_TP_PID_SMS_TYPE_3 3
+#define GSM430_TP_PID_SMS_TYPE_4 4
+#define GSM430_TP_PID_SMS_TYPE_5 5
+#define GSM430_TP_PID_SMS_TYPE_6 6
+#define GSM430_TP_PID_SMS_TYPE_7 7
+#define GSM430_TP_PID_RETURN_CALL_MSG 0x1f
+#define GSM430_TP_PID_ME_DATA_DNLOAD 0x3d
+#define GSM430_TP_PID_ME_DE_PERSONAL 0x3e
+#define GSM430_TP_PID_ME_SIM_DNLOAD 0x3f
+
+/* GSM 03.38 Chapter 4: SMS Data Coding Scheme */
+#define GSM338_DCS_00_
+
+#define GSM338_DCS_1110_7BIT (0 << 2)
+#define GSM338_DCS_1111_7BIT (0 << 2)
+#define GSM338_DCS_1111_8BIT_DATA (1 << 2)
+#define GSM338_DCS_1111_CLASS0 0
+#define GSM338_DCS_1111_CLASS1_ME 1
+#define GSM338_DCS_1111_CLASS2_SIM 2
+#define GSM338_DCS_1111_CLASS3_TE 3 /* See TS 07.05 */
+
+
+/* SMS deliver PDU */
+struct sms_deliver {
+ u_int8_t *smsc;
+ u_int8_t mti:2;
+ u_int8_t rd:1;
+ u_int8_t vpf:2;
+ u_int8_t srr:1;
+ u_int8_t udhi:1;
+ u_int8_t rp:1;
+ u_int8_t msg_ref;
+ u_int8_t *orig_addr;
+ u_int8_t pid;
+ u_int8_t dcs;
+ u_int8_t vp;
+ u_int8_t ud_len;
+ u_int8_t *user_data;
+};
+
+struct msgb;
+
+int gsm0411_rcv_sms(struct msgb *msg);
+
+int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms);
+
+struct msgb *gsm411_msgb_alloc(void);
+int gsm0411_sendmsg(struct msgb *msg);
+
+#endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
new file mode 100644
index 000000000..e85adf829
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -0,0 +1,406 @@
+#ifndef _GSM_DATA_H
+#define _GSM_DATA_H
+
+#include <sys/types.h>
+
+#include <openbsc/timer.h>
+#include <openbsc/gsm_04_08.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define GSM_MAX_BTS 8
+#define BTS_MAX_TRX 8
+#define TRX_NR_TS 8
+#define TS_MAX_LCHAN 8
+
+#define HARDCODED_ARFCN 123
+#define HARDCODED_TSC 7
+#define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */
+
+enum gsm_hooks {
+ GSM_HOOK_NM_SWLOAD,
+ GSM_HOOK_RR_PAGING,
+};
+
+enum gsm_paging_event {
+ GSM_PAGING_SUCCEEDED,
+ GSM_PAGING_EXPIRED,
+};
+
+struct msgb;
+typedef int gsm_cbfn(unsigned int hooknum,
+ unsigned int event,
+ struct msgb *msg,
+ void *data, void *param);
+
+/*
+ * Use the channel. As side effect the lchannel recycle timer
+ * will be started.
+ */
+#define LCHAN_RELEASE_TIMEOUT 4, 0
+#define use_lchan(lchan) \
+ do { lchan->use_count++; \
+ DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
+ lchan->nr, lchan->use_count); \
+ bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0);
+
+#define put_lchan(lchan) \
+ do { lchan->use_count--; \
+ DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
+ lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
+ lchan->nr, lchan->use_count); \
+ } while(0);
+
+
+/* communications link with a BTS */
+struct gsm_bts_link {
+ struct gsm_bts *bts;
+};
+
+enum gsm_call_type {
+ GSM_CT_NONE,
+ GSM_CT_MO,
+ GSM_CT_MT,
+};
+
+enum gsm_call_state {
+ GSM_CSTATE_NULL,
+ GSM_CSTATE_INITIATED,
+ GSM_CSTATE_ACTIVE,
+ GSM_CSTATE_RELEASE_REQ,
+};
+
+struct gsm_lchan;
+struct gsm_subscriber;
+
+/* One end of a call */
+struct gsm_call {
+ enum gsm_call_type type;
+ enum gsm_call_state state;
+ u_int8_t transaction_id; /* 10.3.2 */
+
+ /* the 'local' channel */
+ struct gsm_lchan *local_lchan;
+ /* the 'remote' channel */
+ struct gsm_lchan *remote_lchan;
+
+ /* the 'remote' subscriber */
+ struct gsm_subscriber *called_subscr;
+};
+
+
+enum gsm_phys_chan_config {
+ GSM_PCHAN_NONE,
+ GSM_PCHAN_CCCH,
+ GSM_PCHAN_CCCH_SDCCH4,
+ GSM_PCHAN_TCH_F,
+ GSM_PCHAN_TCH_H,
+ GSM_PCHAN_SDCCH8_SACCH8C,
+ GSM_PCHAN_UNKNOWN,
+};
+
+enum gsm_chan_t {
+ GSM_LCHAN_NONE,
+ GSM_LCHAN_SDCCH,
+ GSM_LCHAN_TCH_F,
+ GSM_LCHAN_TCH_H,
+ GSM_LCHAN_UNKNOWN,
+};
+
+
+/* Channel Request reason */
+enum gsm_chreq_reason_t {
+ GSM_CHREQ_REASON_EMERG,
+ GSM_CHREQ_REASON_PAG,
+ GSM_CHREQ_REASON_CALL,
+ GSM_CHREQ_REASON_LOCATION_UPD,
+ GSM_CHREQ_REASON_OTHER,
+};
+
+/* Network Management State */
+struct gsm_nm_state {
+ u_int8_t operational;
+ u_int8_t administrative;
+ u_int8_t availability;
+};
+struct gsm_attr {
+ u_int8_t len;
+ u_int8_t data[0];
+};
+
+/*
+ * LOCATION UPDATING REQUEST state
+ *
+ * Our current operation is:
+ * - Get imei/tmsi
+ * - Accept/Reject according to global policy
+ */
+struct gsm_loc_updating_operation {
+ struct timer_list updating_timer;
+ int waiting_for_imsi : 1;
+ int waiting_for_imei : 1;
+};
+
+struct gsm_lchan {
+ /* The TS that we're part of */
+ struct gsm_bts_trx_ts *ts;
+ /* The logical subslot number in the TS */
+ u_int8_t nr;
+ /* The logical channel type */
+ enum gsm_chan_t type;
+ /* If TCH, traffic channel mode */
+ enum gsm_chan_t tch_mode;
+ /* Power levels for MS and BTS */
+ u_int8_t bs_power;
+ u_int8_t ms_power;
+
+ /* To whom we are allocated at the moment */
+ struct gsm_subscriber *subscr;
+
+ /* Timer started to release the channel */
+ struct timer_list release_timer;
+
+ /* local end of a call, if any */
+ struct gsm_call call;
+
+ /* temporary user data, to be removed... and merged into gsm_call */
+ void *user_data;
+
+ /*
+ * Operations that have a state and might be pending
+ */
+ struct gsm_loc_updating_operation *loc_operation;
+
+ /* use count. how many users use this channel */
+ unsigned int use_count;
+};
+
+struct gsm_e1_subslot {
+ /* Number of E1 link */
+ u_int8_t e1_nr;
+ /* Number of E1 TS inside E1 link */
+ u_int8_t e1_ts;
+ /* Sub-slot within the E1 TS, 0xff if full TS */
+ u_int8_t e1_ts_ss;
+};
+
+#define BTS_TRX_F_ACTIVATED 0x0001
+/* One Timeslot in a TRX */
+struct gsm_bts_trx_ts {
+ struct gsm_bts_trx *trx;
+ /* number of this timeslot at the TRX */
+ u_int8_t nr;
+
+ enum gsm_phys_chan_config pchan;
+
+ unsigned int flags;
+ struct gsm_nm_state nm_state;
+ struct gsm_attr *nm_attr;
+
+ /* To which E1 subslot are we connected */
+ struct gsm_e1_subslot e1_link;
+ struct {
+ u_int32_t bound_ip;
+ u_int16_t bound_port;
+ u_int8_t attr_fc;
+ u_int16_t attr_f8;
+ } abis_ip;
+
+ struct gsm_lchan lchan[TS_MAX_LCHAN];
+};
+
+/* One TRX in a BTS */
+struct gsm_bts_trx {
+ struct gsm_bts *bts;
+ /* number of this TRX in the BTS */
+ u_int8_t nr;
+ /* how do we talk RSL with this TRX? */
+ struct e1inp_sign_link *rsl_link;
+ struct gsm_nm_state nm_state;
+ struct gsm_attr *nm_attr;
+ struct {
+ struct gsm_nm_state nm_state;
+ } bb_transc;
+
+ u_int16_t arfcn;
+
+ union {
+ struct {
+ struct {
+ struct gsm_nm_state nm_state;
+ } bbsig;
+ struct {
+ struct gsm_nm_state nm_state;
+ } pa;
+ } bs11;
+ };
+ struct gsm_bts_trx_ts ts[TRX_NR_TS];
+};
+
+enum gsm_bts_type {
+ GSM_BTS_TYPE_UNKNOWN,
+ GSM_BTS_TYPE_BS11,
+ GSM_BTS_TYPE_NANOBTS_900,
+ GSM_BTS_TYPE_NANOBTS_1800,
+};
+
+/**
+ * A pending paging request
+ */
+struct gsm_paging_request {
+ /* list_head for list of all paging requests */
+ struct llist_head entry;
+ /* the subscriber which we're paging. Later gsm_paging_request
+ * should probably become a part of the gsm_subscriber struct? */
+ struct gsm_subscriber *subscr;
+ /* back-pointer to the BTS on which we are paging */
+ struct gsm_bts *bts;
+ /* what kind of channel type do we ask the MS to establish */
+ int chan_type;
+
+ /* Timer 3113: how long do we try to page? */
+ struct timer_list T3113;
+
+ /* callback to be called in case paging completes */
+ gsm_cbfn *cbfn;
+ void *cbfn_param;
+};
+#define T3113_VALUE 60, 0
+
+/*
+ * This keeps track of the paging status of one BTS. It
+ * includes a number of pending requests, a back pointer
+ * to the gsm_bts, a timer and some more state.
+ */
+struct gsm_bts_paging_state {
+ /* pending requests */
+ struct llist_head pending_requests;
+ struct gsm_paging_request *last_request;
+ struct gsm_bts *bts;
+
+ struct timer_list work_timer;
+
+ /* load */
+ u_int16_t available_slots;
+};
+
+struct gsm_envabtse {
+ struct gsm_nm_state nm_state;
+};
+
+/* One BTS */
+struct gsm_bts {
+ struct gsm_network *network;
+ /* number of ths BTS in network */
+ u_int8_t nr;
+ /* location area code of this BTS */
+ u_int8_t location_area_code;
+ /* Training Sequence Code */
+ u_int8_t tsc;
+ /* Base Station Identification Code (BSIC) */
+ u_int8_t bsic;
+ /* type of BTS */
+ enum gsm_bts_type type;
+ /* how do we talk OML with this TRX? */
+ struct e1inp_sign_link *oml_link;
+
+ /* Abis network management O&M handle */
+ struct abis_nm_h *nmh;
+ struct gsm_nm_state nm_state;
+ struct gsm_attr *nm_attr;
+
+ /* number of this BTS on given E1 link */
+ u_int8_t bts_nr;
+
+ struct gsm48_control_channel_descr chan_desc;
+
+ /* paging state and control */
+ struct gsm_bts_paging_state paging;
+
+ /* CCCH is on C0 */
+ struct gsm_bts_trx *c0;
+
+ struct {
+ struct gsm_nm_state nm_state;
+ } site_mgr;
+
+ /* ip.accesss Unit ID's have Site/BTS/TRX layout */
+ union {
+ struct {
+ u_int16_t site_id;
+ u_int16_t bts_id;
+ } ip_access;
+ struct {
+ struct {
+ struct gsm_nm_state nm_state;
+ } cclk;
+ struct {
+ struct gsm_nm_state nm_state;
+ } rack;
+ struct gsm_envabtse envabtse[4];
+ } bs11;
+ };
+
+ /* transceivers */
+ int num_trx;
+ struct gsm_bts_trx trx[BTS_MAX_TRX+1];
+};
+
+struct gsm_network {
+ /* global parameters */
+ u_int16_t country_code;
+ u_int16_t network_code;
+ char *name_long;
+ char *name_short;
+
+ unsigned int num_bts;
+ /* private lists */
+ struct gsm_bts bts[GSM_MAX_BTS+1];
+};
+
+#define SMS_HDR_SIZE 128
+#define SMS_TEXT_SIZE 256
+struct gsm_sms {
+ u_int64_t id;
+ struct gsm_subscriber *sender;
+ struct gsm_subscriber *receiver;
+
+ unsigned char header[SMS_HDR_SIZE];
+ char text[SMS_TEXT_SIZE];
+};
+
+struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
+ u_int16_t country_code, u_int16_t network_code);
+
+const char *gsm_pchan_name(enum gsm_phys_chan_config c);
+const char *gsm_lchan_name(enum gsm_chan_t c);
+const char *gsm_chreq_name(enum gsm_chreq_reason_t c);
+char *gsm_ts_name(struct gsm_bts_trx_ts *ts);
+
+enum gsm_e1_event {
+ EVT_E1_NONE,
+ EVT_E1_TEI_UP,
+ EVT_E1_TEI_DN,
+};
+
+void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
+ u_int8_t e1_ts, u_int8_t e1_ts_ss);
+enum gsm_bts_type parse_btstype(char *arg);
+char *btstype2str(enum gsm_bts_type type);
+struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
+ struct gsm_bts *start_bts);
+
+static inline int is_ipaccess_bts(struct gsm_bts *bts)
+{
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS_900:
+ case GSM_BTS_TYPE_NANOBTS_1800:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+#endif
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
new file mode 100644
index 000000000..1ca79e2ae
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -0,0 +1,58 @@
+#ifndef _GSM_SUBSCR_H
+#define _GSM_SUBSCR_H
+
+#include <sys/types.h>
+#include "gsm_data.h"
+#include "linuxlist.h"
+
+#define GSM_IMEI_LENGTH 17
+#define GSM_IMSI_LENGTH 17
+#define GSM_TMSI_LENGTH 17
+#define GSM_NAME_LENGTH 128
+#define GSM_EXTENSION_LENGTH 128
+
+struct gsm_subscriber {
+ long long unsigned int id;
+ char imsi[GSM_IMSI_LENGTH];
+ char tmsi[GSM_TMSI_LENGTH];
+ u_int16_t lac;
+ char name[GSM_NAME_LENGTH];
+ char extension[GSM_EXTENSION_LENGTH];
+ int authorized;
+
+ /* for internal management */
+ int use_count;
+ struct llist_head entry;
+
+ /* those are properties of the equipment, but they
+ * are applicable to the subscriber at the moment */
+ struct gsm48_classmark1 classmark1;
+ u_int8_t classmark2_len;
+ u_int8_t classmark2[3];
+ u_int8_t classmark3_len;
+ u_int8_t classmark3[14];
+};
+
+enum gsm_subscriber_field {
+ GSM_SUBSCRIBER_IMSI,
+ GSM_SUBSCRIBER_TMSI,
+ GSM_SUBSCRIBER_EXTENSION,
+};
+
+enum gsm_subscriber_update_reason {
+ GSM_SUBSCRIBER_UPDATE_ATTACHED,
+ GSM_SUBSCRIBER_UPDATE_DETACHED,
+};
+
+struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
+struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr);
+struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi);
+struct gsm_subscriber *subscr_get_by_imsi(const char *imsi);
+struct gsm_subscriber *subscr_get_by_extension(const char *ext);
+int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
+void subscr_put_channel(struct gsm_lchan *lchan);
+
+/* internal */
+struct gsm_subscriber *subscr_alloc(void);
+
+#endif /* _GSM_SUBSCR_H */
diff --git a/openbsc/include/openbsc/gsm_utils.h b/openbsc/include/openbsc/gsm_utils.h
new file mode 100644
index 000000000..c46837185
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_utils.h
@@ -0,0 +1,33 @@
+/* GSM utility functions, e.g. coding and decoding */
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef GSM_UTILS_H
+#define GSM_UTILS_H
+
+#include <sys/types.h>
+
+int gsm_7bit_decode(char *decoded, const u_int8_t *user_data, u_int8_t length);
+int gsm_7bit_encode(u_int8_t *result, const char *data);
+
+#endif
diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h
new file mode 100644
index 000000000..d6ded3551
--- /dev/null
+++ b/openbsc/include/openbsc/ipaccess.h
@@ -0,0 +1,37 @@
+#ifndef _IPACCESS_H
+#define _IPACCESS_H
+
+struct ipaccess_head {
+ u_int8_t zero;
+ u_int8_t len;
+ u_int8_t proto;
+ u_int8_t data[0];
+} __attribute__ ((packed));
+
+enum ipaccess_proto {
+ IPAC_PROTO_RSL = 0x00,
+ IPAC_PROTO_IPACCESS = 0xfe,
+ IPAC_PROTO_OML = 0xff,
+};
+
+enum ipaccess_msgtype {
+ IPAC_MSGT_PING = 0x00,
+ IPAC_MSGT_PONG = 0x01,
+ IPAC_MSGT_ID_GET = 0x04,
+ IPAC_MSGT_ID_RESP = 0x05,
+ IPAC_MSGT_ID_ACK = 0x06,
+};
+
+enum ipaccess_id_tags {
+ IPAC_IDTAG_SERNR = 0x00,
+ IPAC_IDTAG_UNITNAME = 0x01,
+ IPAC_IDTAG_LOCATION1 = 0x02,
+ IPAC_IDTAG_LOCATION2 = 0x03,
+ IPAC_IDTAG_EQUIPVERS = 0x04,
+ IPAC_IDTAG_SWVERSION = 0x05,
+ IPAC_IDTAG_IPADDR = 0x06,
+ IPAC_IDTAG_MACADDR = 0x07,
+ IPAC_IDTAG_UNIT = 0x08,
+};
+
+#endif /* _IPACCESS_H */
diff --git a/openbsc/include/openbsc/linuxlist.h b/openbsc/include/openbsc/linuxlist.h
new file mode 100644
index 000000000..fb99c5ec8
--- /dev/null
+++ b/openbsc/include/openbsc/linuxlist.h
@@ -0,0 +1,360 @@
+#ifndef _LINUX_LLIST_H
+#define _LINUX_LLIST_H
+
+#include <stddef.h>
+
+#ifndef inline
+#define inline __inline__
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (typeof( ((type *)0)->member ) *)(ptr); \
+ (type *)( (char *)__mptr - offsetof(type, member) );})
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1 ((void *) 0x00100100)
+#define LLIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked llist implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct llist_head {
+ struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LLIST_HEAD(name) \
+ struct llist_head name = LLIST_HEAD_INIT(name)
+
+#define INIT_LLIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *_new,
+ struct llist_head *prev,
+ struct llist_head *next)
+{
+ next->prev = _new;
+ _new->next = next;
+ _new->prev = prev;
+ prev->next = _new;
+}
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head, head->next);
+}
+
+/**
+ * llist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *_new, struct llist_head *head)
+{
+ __llist_add(_new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * llist_del - deletes entry from llist.
+ * @entry: the element to delete from the llist.
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ entry->next = (struct llist_head *)LLIST_POISON1;
+ entry->prev = (struct llist_head *)LLIST_POISON2;
+}
+
+/**
+ * llist_del_init - deletes entry from llist and reinitialize it.
+ * @entry: the element to delete from the llist.
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ INIT_LLIST_HEAD(entry);
+}
+
+/**
+ * llist_move - delete from one llist and add as another's head
+ * @llist: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add(llist, head);
+}
+
+/**
+ * llist_move_tail - delete from one llist and add as another's tail
+ * @llist: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+ struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add_tail(llist, head);
+}
+
+/**
+ * llist_empty - tests whether a llist is empty
+ * @head: the llist to test.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+ struct llist_head *head)
+{
+ struct llist_head *first = llist->next;
+ struct llist_head *last = llist->prev;
+ struct llist_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * llist_splice - join two llists
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+ if (!llist_empty(llist))
+ __llist_splice(llist, head);
+}
+
+/**
+ * llist_splice_init - join two llists and reinitialise the emptied llist.
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+ struct llist_head *head)
+{
+ if (!llist_empty(llist)) {
+ __llist_splice(llist, head);
+ INIT_LLIST_HEAD(llist);
+ }
+}
+
+/**
+ * llist_entry - get the struct for this entry
+ * @ptr: the &struct llist_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * llist_for_each_prev - iterate over a llist backwards
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * llist_for_each_safe - iterate over a llist safe against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * llist_for_each_entry - iterate over llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_reverse - iterate backwards over llist of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_reverse(pos, head, member) \
+ for (pos = llist_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/**
+ * llist_for_each_entry_continue - iterate over llist of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_continue(pos, head, member) \
+ for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ n = llist_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu - iterate over an rcu-protected llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
+ * against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu - iterate over rcu llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu - iterate over an rcu-protected llist
+ * continuing after existing point.
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+
+#endif
diff --git a/openbsc/include/openbsc/misdn.h b/openbsc/include/openbsc/misdn.h
new file mode 100644
index 000000000..d3631e794
--- /dev/null
+++ b/openbsc/include/openbsc/misdn.h
@@ -0,0 +1,28 @@
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef MISDN_H
+#define MISDN_H
+
+#include "e1_input.h"
+
+int mi_setup(int cardnr, struct e1inp_line *line, int release_l2);
+int _abis_nm_sendmsg(struct msgb *msg);
+
+#endif
diff --git a/openbsc/include/openbsc/msgb.h b/openbsc/include/openbsc/msgb.h
new file mode 100644
index 000000000..2c31d1587
--- /dev/null
+++ b/openbsc/include/openbsc/msgb.h
@@ -0,0 +1,111 @@
+#ifndef _MSGB_H
+#define _MSGB_H
+
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/linuxlist.h>
+
+struct bts_link;
+
+struct msgb {
+ struct llist_head list;
+
+ /* ptr to the physical E1 link to the BTS(s) */
+ struct gsm_bts_link *bts_link;
+
+ /* Part of which TRX logical channel we were received / transmitted */
+ struct gsm_bts_trx *trx;
+ struct gsm_lchan *lchan;
+
+ unsigned char *l2h;
+ unsigned char *l3h;
+ unsigned char *smsh;
+
+ u_int16_t data_len;
+ u_int16_t len;
+
+ unsigned char *head;
+ unsigned char *tail;
+ unsigned char *data;
+ unsigned char _data[0];
+};
+
+extern struct msgb *msgb_alloc(u_int16_t size);
+extern void msgb_free(struct msgb *m);
+extern void msgb_enqueue(struct llist_head *queue, struct msgb *msg);
+extern struct msgb *msgb_dequeue(struct llist_head *queue);
+
+#define msgb_l2(m) ((void *)(m->l2h))
+#define msgb_l3(m) ((void *)(m->l3h))
+#define msgb_sms(m) ((void *)(m->smsh))
+
+static inline unsigned int msgb_l2len(const struct msgb *msgb)
+{
+ return msgb->tail - (u_int8_t *)msgb_l2(msgb);
+}
+
+static inline unsigned int msgb_l3len(const struct msgb *msgb)
+{
+ return msgb->tail - (u_int8_t *)msgb_l3(msgb);
+}
+
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+ return msgb->len - msgb->data_len;
+}
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+ unsigned char *tmp = msgb->tail;
+ msgb->tail += len;
+ msgb->len += len;
+ return tmp;
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+ msgb->data -= len;
+ msgb->len += len;
+ return msgb->data;
+}
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+ msgb->len -= len;
+ return msgb->data += len;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+ return (msgb->data + msgb->data_len) - msgb->tail;
+}
+
+/* increase the headroom of an empty msgb, reducing the tailroom */
+static inline void msgb_reserve(struct msgb *msg, int len)
+{
+ msg->data += len;
+ msg->tail += len;
+}
+
+static inline struct msgb *msgb_alloc_headroom(int size, int headroom)
+{
+ struct msgb *msg = msgb_alloc(size);
+ if (msg)
+ msgb_reserve(msg, headroom);
+ return msg;
+}
+
+#endif /* _MSGB_H */
diff --git a/openbsc/include/openbsc/openbscdefines.h b/openbsc/include/openbsc/openbscdefines.h
new file mode 100644
index 000000000..082e5922e
--- /dev/null
+++ b/openbsc/include/openbsc/openbscdefines.h
@@ -0,0 +1,35 @@
+/*
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef OPENBSCDEFINES_H
+#define OPENBSCDEFINES_H
+
+#ifdef BUILDING_ON_WINDOWS
+ #ifdef BUILDING_OPENBSC
+ #define BSC_API __declspec(dllexport)
+ #else
+ #define BSC_API __declspec(dllimport)
+ #endif
+#else
+ #define BSC_API __attribute__((visibility("default")))
+#endif
+
+#endif
diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h
new file mode 100644
index 000000000..de512d1ae
--- /dev/null
+++ b/openbsc/include/openbsc/paging.h
@@ -0,0 +1,46 @@
+/* Paging helper and manager.... */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef PAGING_H
+#define PAGING_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "linuxlist.h"
+#include "gsm_data.h"
+#include "gsm_subscriber.h"
+#include "timer.h"
+
+/* call once for every gsm_bts... */
+void paging_init(struct gsm_bts *bts);
+
+/* schedule paging request */
+void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+ int type, gsm_cbfn *cbfn, void *data);
+
+/* stop paging requests */
+void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+ struct gsm_lchan *lchan);
+
+/* update paging load */
+void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t);
+
+#endif
diff --git a/openbsc/include/openbsc/rs232.h b/openbsc/include/openbsc/rs232.h
new file mode 100644
index 000000000..61187ca62
--- /dev/null
+++ b/openbsc/include/openbsc/rs232.h
@@ -0,0 +1,9 @@
+#ifndef _RS232_H
+#define _RS232_H
+
+int rs232_setup(const char *serial_port, unsigned int delay_ms,
+ struct gsm_bts *bts);
+
+int handle_serial_msg(struct msgb *msg);
+
+#endif /* _RS232_H */
diff --git a/openbsc/include/openbsc/select.h b/openbsc/include/openbsc/select.h
new file mode 100644
index 000000000..c2af1d787
--- /dev/null
+++ b/openbsc/include/openbsc/select.h
@@ -0,0 +1,22 @@
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#include <openbsc/linuxlist.h>
+
+#define BSC_FD_READ 0x0001
+#define BSC_FD_WRITE 0x0002
+#define BSC_FD_EXCEPT 0x0004
+
+struct bsc_fd {
+ struct llist_head list;
+ int fd;
+ unsigned int when;
+ int (*cb)(struct bsc_fd *fd, unsigned int what);
+ void *data;
+ unsigned int priv_nr;
+};
+
+int bsc_register_fd(struct bsc_fd *fd);
+void bsc_unregister_fd(struct bsc_fd *fd);
+int bsc_select_main(int polling);
+#endif /* _BSC_SELECT_H */
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
new file mode 100644
index 000000000..4a583f689
--- /dev/null
+++ b/openbsc/include/openbsc/signal.h
@@ -0,0 +1,88 @@
+/* Generic signalling/notification infrastructure */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef OPENBSC_SIGNAL_H
+#define OPENBSC_SIGNAL_H
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+
+
+/*
+ * Signalling subsystems
+ */
+enum signal_subsystems {
+ SS_PAGING,
+ SS_SMS,
+ SS_ABISIP,
+ SS_NM,
+ SS_LCHAN,
+};
+
+/* SS_PAGING signals */
+enum signal_paging {
+ S_PAGING_COMPLETED,
+};
+
+/* SS_ABISIP signals */
+enum signal_abisip {
+ S_ABISIP_BIND_ACK,
+};
+
+/* SS_NM signals */
+enum signal_nm {
+ S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */
+ S_NM_FAIL_REP, /* GSM 12.21 failure event report */
+};
+
+/* SS_LCHAN signals */
+enum signal_lchan {
+ /*
+ * The lchan got freed with an use_count != 0 and error
+ * recovery needs to be carried out from within the
+ * signal handler.
+ */
+ S_LCHAN_UNEXPECTED_RELEASE,
+};
+
+typedef int signal_cbfn(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data);
+
+struct paging_signal_data {
+ struct gsm_subscriber *subscr;
+ struct gsm_bts *bts;
+
+ /* NULL in case the paging didn't work */
+ struct gsm_lchan *lchan;
+};
+
+/* Management */
+int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data);
+
+/* Dispatch */
+void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data);
+
+
+#endif
diff --git a/openbsc/include/openbsc/subchan_demux.h b/openbsc/include/openbsc/subchan_demux.h
new file mode 100644
index 000000000..9661b0481
--- /dev/null
+++ b/openbsc/include/openbsc/subchan_demux.h
@@ -0,0 +1,102 @@
+#ifndef _SUBCH_DEMUX_H
+#define _SUBCH_DEMUX_H
+/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <openbsc/linuxlist.h>
+
+#define NR_SUBCH 4
+#define TRAU_FRAME_SIZE 40
+#define TRAU_FRAME_BITS (TRAU_FRAME_SIZE*8)
+
+/***********************************************************************/
+/* DEMULTIPLEXER */
+/***********************************************************************/
+
+struct demux_subch {
+ u_int8_t out_bitbuf[TRAU_FRAME_BITS];
+ u_int16_t out_idx; /* next bit to be written in out_bitbuf */
+ /* number of consecutive zeros that we have received (for sync) */
+ unsigned int consecutive_zeros;
+ /* are we in TRAU frame sync or not? */
+ unsigned int in_sync;
+};
+
+struct subch_demux {
+ /* bitmask of currently active subchannels */
+ u_int8_t chan_activ;
+ /* one demux_subch struct for every subchannel */
+ struct demux_subch subch[NR_SUBCH];
+ /* callback to be called once we have received a complete
+ * frame on a given subchannel */
+ int (*out_cb)(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
+ void *);
+ /* user-provided data, transparently passed to out_cb() */
+ void *data;
+};
+
+/* initialize one demultiplexer instance */
+int subch_demux_init(struct subch_demux *dmx);
+
+/* feed 'len' number of muxed bytes into the demultiplexer */
+int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len);
+
+/* activate decoding/processing for one subchannel */
+int subch_demux_activate(struct subch_demux *dmx, int subch);
+
+/* deactivate decoding/processing for one subchannel */
+int subch_demux_deactivate(struct subch_demux *dmx, int subch);
+
+/***********************************************************************/
+/* MULTIPLEXER */
+/***********************************************************************/
+
+/* one element in the tx_queue of a muxer sub-channel */
+struct subch_txq_entry {
+ struct llist_head list;
+
+ unsigned int bit_len; /* total number of bits in 'bits' */
+ unsigned int next_bit; /* next bit to be transmitted */
+
+ u_int8_t bits[0]; /* one bit per byte */
+};
+
+struct mux_subch {
+ struct llist_head tx_queue;
+};
+
+/* structure representing one instance of the subchannel muxer */
+struct subch_mux {
+ struct mux_subch subch[NR_SUBCH];
+};
+
+/* initialize a subchannel muxer instance */
+int subchan_mux_init(struct subch_mux *mx);
+
+/* request the output of 'len' multiplexed bytes */
+int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len);
+
+/* enqueue some data into one sub-channel of the muxer */
+int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
+ int len);
+
+#endif /* _SUBCH_DEMUX_H */
diff --git a/openbsc/include/openbsc/telnet_interface.h b/openbsc/include/openbsc/telnet_interface.h
new file mode 100644
index 000000000..97357d794
--- /dev/null
+++ b/openbsc/include/openbsc/telnet_interface.h
@@ -0,0 +1,52 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef TELNET_INTERFACE_H
+#define TELNET_INTERFACE_H
+
+#include "gsm_data.h"
+#include "linuxlist.h"
+#include "select.h"
+
+#include <vty/vty.h>
+
+#define TELNET_COMMAND_48 1
+#define TELNET_COMMAND_11 2
+
+struct telnet_connection {
+ struct llist_head entry;
+ struct gsm_network *network;
+ struct bsc_fd fd;
+ struct vty *vty;
+
+ int bts;
+
+ int command;
+ char *imsi;
+ char commands[1024];
+ int read;
+};
+
+
+void telnet_init(struct gsm_network *network, int port);
+
+int bsc_vty_init(struct gsm_network *net);
+
+#endif
diff --git a/openbsc/include/openbsc/timer.h b/openbsc/include/openbsc/timer.h
new file mode 100644
index 000000000..ae67a5a1a
--- /dev/null
+++ b/openbsc/include/openbsc/timer.h
@@ -0,0 +1,71 @@
+/*
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+#include <sys/time.h>
+
+#include "linuxlist.h"
+
+/**
+ * Timer management:
+ * - Create a struct timer_list
+ * - Fill out timeout and use add_timer or
+ * use schedule_timer to schedule a timer in
+ * x seconds and microseconds from now...
+ * - Use del_timer to remove the timer
+ *
+ * Internally:
+ * - We hook into select.c to give a timeval of the
+ * nearest timer. On already passed timers we give
+ * it a 0 to immediately fire after the select
+ * - update_timers will call the callbacks and remove
+ * the timers.
+ *
+ */
+struct timer_list {
+ struct llist_head entry;
+ struct timeval timeout;
+ int active : 1;
+ int handled : 1;
+ int in_list : 1;
+
+ void (*cb)(void*);
+ void *data;
+};
+
+/**
+ * timer management
+ */
+void bsc_add_timer(struct timer_list *timer);
+void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds);
+void bsc_del_timer(struct timer_list *timer);
+int bsc_timer_pending(struct timer_list *timer);
+
+
+/**
+ * internal timer list management
+ */
+struct timeval *bsc_nearest_timer();
+void bsc_prepare_timers();
+int bsc_update_timers();
+
+#endif
diff --git a/openbsc/include/openbsc/tlv.h b/openbsc/include/openbsc/tlv.h
new file mode 100644
index 000000000..ae88e6ed3
--- /dev/null
+++ b/openbsc/include/openbsc/tlv.h
@@ -0,0 +1,171 @@
+#ifndef _TLV_H
+#define _TLV_H
+
+#include <sys/types.h>
+#include <string.h>
+
+#include <openbsc/msgb.h>
+
+#define LV_GROSS_LEN(x) (x+1)
+#define TLV_GROSS_LEN(x) (x+2)
+#define TLV16_GROSS_LEN(x) ((2*x)+2)
+#define TL16V_GROSS_LEN(x) (x+3)
+
+/* TLV generation */
+
+static inline u_int8_t *lv_put(u_int8_t *buf, u_int8_t len,
+ const u_int8_t *val)
+{
+ *buf++ = len;
+ memcpy(buf, val, len);
+ return buf + len;
+}
+
+static inline u_int8_t *tlv_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
+ const u_int8_t *val)
+{
+ *buf++ = tag;
+ *buf++ = len;
+ memcpy(buf, val, len);
+ return buf + len;
+}
+
+static inline u_int8_t *tlv16_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
+ const u_int16_t *val)
+{
+ *buf++ = tag;
+ *buf++ = len;
+ memcpy(buf, val, len*2);
+ return buf + len*2;
+}
+
+static inline u_int8_t *tl16v_put(u_int8_t *buf, u_int8_t tag, u_int16_t len,
+ const u_int8_t *val)
+{
+ *buf++ = tag;
+ *buf++ = len >> 8;
+ *buf++ = len & 0xff;
+ memcpy(buf, val, len);
+ return buf + len*2;
+}
+
+static inline u_int8_t *msgb_tlv16_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int16_t *val)
+{
+ u_int8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
+ return tlv16_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *msgb_tl16v_put(struct msgb *msg, u_int8_t tag, u_int16_t len,
+ const u_int8_t *val)
+{
+ u_int8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));
+ return tl16v_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *v_put(u_int8_t *buf, u_int8_t val)
+{
+ *buf++ = val;
+ return buf;
+}
+
+static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag,
+ u_int8_t val)
+{
+ *buf++ = tag;
+ *buf++ = val;
+ return buf;
+}
+
+static inline u_int8_t *tv16_put(u_int8_t *buf, u_int8_t tag,
+ u_int16_t val)
+{
+ *buf++ = tag;
+ *buf++ = val >> 8;
+ *buf++ = val & 0xff;
+ return buf;
+}
+
+static inline u_int8_t *msgb_lv_put(struct msgb *msg, u_int8_t len, const u_int8_t *val)
+{
+ u_int8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));
+ return lv_put(buf, len, val);
+}
+
+static inline u_int8_t *msgb_tlv_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
+{
+ u_int8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
+ return tlv_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *msgb_tv_put(struct msgb *msg, u_int8_t tag, u_int8_t val)
+{
+ u_int8_t *buf = msgb_put(msg, 2);
+ return tv_put(buf, tag, val);
+}
+
+static inline u_int8_t *msgb_v_put(struct msgb *msg, u_int8_t val)
+{
+ u_int8_t *buf = msgb_put(msg, 1);
+ return v_put(buf, val);
+}
+
+static inline u_int8_t *msgb_tv16_put(struct msgb *msg, u_int8_t tag, u_int16_t val)
+{
+ u_int8_t *buf = msgb_put(msg, 3);
+ return tv16_put(buf, tag, val);
+}
+
+static inline u_int8_t *msgb_tlv_push(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
+{
+ u_int8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
+ return tlv_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *msgb_tv_push(struct msgb *msg, u_int8_t tag, u_int8_t val)
+{
+ u_int8_t *buf = msgb_push(msg, 2);
+ return tv_put(buf, tag, val);
+}
+
+static inline u_int8_t *msgb_tv16_push(struct msgb *msg, u_int8_t tag, u_int16_t val)
+{
+ u_int8_t *buf = msgb_push(msg, 3);
+ return tv16_put(buf, tag, val);
+}
+
+/* TLV parsing */
+
+struct tlv_p_entry {
+ u_int16_t len;
+ const u_int8_t *val;
+};
+
+enum tlv_type {
+ TLV_TYPE_FIXED,
+ TLV_TYPE_T,
+ TLV_TYPE_TV,
+ TLV_TYPE_TLV,
+ TLV_TYPE_TL16V,
+};
+
+struct tlv_def {
+ enum tlv_type type;
+ u_int8_t fixed_len;
+};
+
+struct tlv_definition {
+ struct tlv_def def[0xff];
+};
+
+struct tlv_parsed {
+ struct tlv_p_entry lv[0xff];
+};
+
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
+ const u_int8_t *buf, int buf_len, u_int8_t lv_tag, u_int8_t lv_tag2);
+
+#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
+#define TLVP_LEN(x, y) (x)->lv[y].len
+#define TLVP_VAL(x, y) (x)->lv[y].val
+
+#endif /* _TLV_H */
diff --git a/openbsc/include/openbsc/trau_frame.h b/openbsc/include/openbsc/trau_frame.h
new file mode 100644
index 000000000..5923d4ac1
--- /dev/null
+++ b/openbsc/include/openbsc/trau_frame.h
@@ -0,0 +1,65 @@
+#ifndef _TRAU_FRAME_H
+#define _TRAU_FRAME_H
+/* TRAU frame handling according to GSM TS 08.60 */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/types.h>
+
+/* 21 for FR/EFR, 25 for AMR, 15 for OM, 15 for data, 13 for E-data, 21 idle */
+#define MAX_C_BITS 25
+/* 260 for FR/EFR, 256 for AMR, 264 for OM, 288 for E-data */
+#define MAX_D_BITS 288
+/* for all speech frames */
+#define MAX_T_BITS 4
+/* for OM */
+#define MAX_S_BITS 6
+/* for E-data */
+#define MAX_M_BITS 2
+
+struct decoded_trau_frame {
+ u_int8_t c_bits[MAX_C_BITS];
+ u_int8_t d_bits[MAX_D_BITS];
+ u_int8_t t_bits[MAX_T_BITS];
+ u_int8_t s_bits[MAX_S_BITS];
+ u_int8_t m_bits[MAX_M_BITS];
+};
+
+#define TRAU_FT_FR_UP 0x02 /* 0 0 0 1 0 - 3.5.1.1.1 */
+#define TRAU_FT_FR_DOWN 0x1c /* 1 1 1 0 0 - 3.5.1.1.1 */
+#define TRAU_FT_EFR 0x1a /* 1 1 0 1 0 - 3.5.1.1.1 */
+#define TRAU_FT_AMR 0x06 /* 0 0 1 1 0 - 3.5.1.2 */
+#define TRAU_FT_OM_UP 0x07 /* 0 0 1 0 1 - 3.5.2 */
+#define TRAU_FT_OM_DOWN 0x1b /* 1 1 0 1 1 - 3.5.2 */
+#define TRAU_FT_DATA_UP 0x08 /* 0 1 0 0 0 - 3.5.3 */
+#define TRAU_FT_DATA_DOWN 0x16 /* 1 0 1 1 0 - 3.5.3 */
+#define TRAU_FT_D145_SYNC 0x14 /* 1 0 1 0 0 - 3.5.3 */
+#define TRAU_FT_EDATA 0x1f /* 1 1 1 1 1 - 3.5.4 */
+#define TRAU_FT_IDLE_UP 0x10 /* 1 0 0 0 0 - 3.5.5 */
+#define TRAU_FT_IDLE_DOWN 0x0e /* 0 1 1 1 0 - 3.5.5 */
+
+
+int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits);
+int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr);
+int trau_frame_up2down(struct decoded_trau_frame *fr);
+u_int8_t *trau_idle_frame(void);
+
+
+#endif /* _TRAU_FRAME_H */
diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h
new file mode 100644
index 000000000..90535add4
--- /dev/null
+++ b/openbsc/include/openbsc/trau_mux.h
@@ -0,0 +1,49 @@
+/* Simple TRAU frame reflector to route voice calls */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/* The "TRAU mux map" defines which particular 16kbit sub-slot (in which E1
+ * timeslot on which E1 interface) should be directly muxed to which other
+ * sub-slot. Entries in the mux map are always bi-directional.
+ *
+ * The idea of all this is to directly switch voice channels in the BSC
+ * from one phone to another. We do this right now since we don't support
+ * any external interface for voice channels, and in the future as an
+ * optimization to routing them externally.
+ */
+
+/* map a TRAU mux map entry */
+int trau_mux_map(const struct gsm_e1_subslot *src,
+ const struct gsm_e1_subslot *dst);
+int trau_mux_map_lchan(const struct gsm_lchan *src,
+ const struct gsm_lchan *dst);
+
+/* unmap a TRAU mux map entry */
+int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref);
+
+/* we get called by subchan_demux */
+int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
+ const u_int8_t *trau_bits, int num_bits);
+
+/* add a trau receiver */
+int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref);
+
+/* send trau from application */
+int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf);
diff --git a/openbsc/include/vty/Makefile.am b/openbsc/include/vty/Makefile.am
new file mode 100644
index 000000000..167476630
--- /dev/null
+++ b/openbsc/include/vty/Makefile.am
@@ -0,0 +1 @@
+noinst_HEADERS = buffer.h command.h vector.h vty.h
diff --git a/openbsc/include/vty/buffer.h b/openbsc/include/vty/buffer.h
new file mode 100644
index 000000000..31519400f
--- /dev/null
+++ b/openbsc/include/vty/buffer.h
@@ -0,0 +1,102 @@
+/*
+ * Buffering to output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_BUFFER_H
+#define _ZEBRA_BUFFER_H
+
+#include <sys/types.h>
+
+/* Create a new buffer. Memory will be allocated in chunks of the given
+ size. If the argument is 0, the library will supply a reasonable
+ default size suitable for buffering socket I/O. */
+struct buffer *buffer_new(size_t);
+
+/* Free all data in the buffer. */
+void buffer_reset(struct buffer *);
+
+/* This function first calls buffer_reset to release all buffered data.
+ Then it frees the struct buffer itself. */
+void buffer_free(struct buffer *);
+
+/* Add the given data to the end of the buffer. */
+extern void buffer_put(struct buffer *, const void *, size_t);
+/* Add a single character to the end of the buffer. */
+extern void buffer_putc(struct buffer *, u_char);
+/* Add a NUL-terminated string to the end of the buffer. */
+extern void buffer_putstr(struct buffer *, const char *);
+
+/* Combine all accumulated (and unflushed) data inside the buffer into a
+ single NUL-terminated string allocated using XMALLOC(MTYPE_TMP). Note
+ that this function does not alter the state of the buffer, so the data
+ is still inside waiting to be flushed. */
+char *buffer_getstr(struct buffer *);
+
+/* Returns 1 if there is no pending data in the buffer. Otherwise returns 0. */
+int buffer_empty(struct buffer *);
+
+typedef enum {
+ /* An I/O error occurred. The buffer should be destroyed and the
+ file descriptor should be closed. */
+ BUFFER_ERROR = -1,
+
+ /* The data was written successfully, and the buffer is now empty
+ (there is no pending data waiting to be flushed). */
+ BUFFER_EMPTY = 0,
+
+ /* There is pending data in the buffer waiting to be flushed. Please
+ try flushing the buffer when select indicates that the file descriptor
+ is writeable. */
+ BUFFER_PENDING = 1
+} buffer_status_t;
+
+/* Try to write this data to the file descriptor. Any data that cannot
+ be written immediately is added to the buffer queue. */
+extern buffer_status_t buffer_write(struct buffer *, int fd,
+ const void *, size_t);
+
+/* This function attempts to flush some (but perhaps not all) of
+ the queued data to the given file descriptor. */
+extern buffer_status_t buffer_flush_available(struct buffer *, int fd);
+
+/* The following 2 functions (buffer_flush_all and buffer_flush_window)
+ are for use in lib/vty.c only. They should not be used elsewhere. */
+
+/* Call buffer_flush_available repeatedly until either all data has been
+ flushed, or an I/O error has been encountered, or the operation would
+ block. */
+extern buffer_status_t buffer_flush_all(struct buffer *, int fd);
+
+/* Attempt to write enough data to the given fd to fill a window of the
+ given width and height (and remove the data written from the buffer).
+
+ If !no_more, then a message saying " --More-- " is appended.
+ If erase is true, then first overwrite the previous " --More-- " message
+ with spaces.
+
+ Any write error (including EAGAIN or EINTR) will cause this function
+ to return -1 (because the logic for handling the erase and more features
+ is too complicated to retry the write later).
+*/
+extern buffer_status_t buffer_flush_window(struct buffer *, int fd, int width,
+ int height, int erase, int no_more);
+
+#endif /* _ZEBRA_BUFFER_H */
diff --git a/openbsc/include/vty/command.h b/openbsc/include/vty/command.h
new file mode 100644
index 000000000..f536f2e02
--- /dev/null
+++ b/openbsc/include/vty/command.h
@@ -0,0 +1,355 @@
+/*
+ * Zebra configuration command interface routine
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_COMMAND_H
+#define _ZEBRA_COMMAND_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "vector.h"
+#include "vty.h"
+
+/* Host configuration variable */
+struct host {
+ /* Host name of this router. */
+ char *name;
+
+ /* Password for vty interface. */
+ char *password;
+ char *password_encrypt;
+
+ /* Enable password */
+ char *enable;
+ char *enable_encrypt;
+
+ /* System wide terminal lines. */
+ int lines;
+
+ /* Log filename. */
+ char *logfile;
+
+ /* config file name of this host */
+ char *config;
+
+ /* Flags for services */
+ int advanced;
+ int encrypt;
+
+ /* Banner configuration. */
+ const char *motd;
+ char *motdfile;
+};
+
+/* There are some command levels which called from command node. */
+enum node_type {
+ BTS_NODE,
+ TRX_NODE,
+ TS_NODE,
+ SUBSCR_NODE,
+
+ AUTH_NODE, /* Authentication mode of vty interface. */
+ VIEW_NODE, /* View node. Default mode of vty interface. */
+ AUTH_ENABLE_NODE, /* Authentication mode for change enable. */
+ ENABLE_NODE, /* Enable node. */
+ CONFIG_NODE, /* Config node. Default mode of config file. */
+ SERVICE_NODE, /* Service node. */
+ DEBUG_NODE, /* Debug node. */
+ AAA_NODE, /* AAA node. */
+ KEYCHAIN_NODE, /* Key-chain node. */
+ KEYCHAIN_KEY_NODE, /* Key-chain key node. */
+ INTERFACE_NODE, /* Interface mode node. */
+ ZEBRA_NODE, /* zebra connection node. */
+ TABLE_NODE, /* rtm_table selection node. */
+ RIP_NODE, /* RIP protocol mode node. */
+ RIPNG_NODE, /* RIPng protocol mode node. */
+ BGP_NODE, /* BGP protocol mode which includes BGP4+ */
+ BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */
+ BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */
+ BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */
+ BGP_IPV6_NODE, /* BGP IPv6 address family */
+ OSPF_NODE, /* OSPF protocol mode */
+ OSPF6_NODE, /* OSPF protocol for IPv6 mode */
+ ISIS_NODE, /* ISIS protocol mode */
+ MASC_NODE, /* MASC for multicast. */
+ IRDP_NODE, /* ICMP Router Discovery Protocol mode. */
+ IP_NODE, /* Static ip route node. */
+ ACCESS_NODE, /* Access list node. */
+ PREFIX_NODE, /* Prefix list node. */
+ ACCESS_IPV6_NODE, /* Access list node. */
+ PREFIX_IPV6_NODE, /* Prefix list node. */
+ AS_LIST_NODE, /* AS list node. */
+ COMMUNITY_LIST_NODE, /* Community list node. */
+ RMAP_NODE, /* Route map node. */
+ SMUX_NODE, /* SNMP configuration node. */
+ DUMP_NODE, /* Packet dump node. */
+ FORWARDING_NODE, /* IP forwarding node. */
+ VTY_NODE /* Vty node. */
+};
+
+/* Node which has some commands and prompt string and configuration
+ function pointer . */
+struct cmd_node {
+ /* Node index. */
+ enum node_type node;
+
+ /* Prompt character at vty interface. */
+ const char *prompt;
+
+ /* Is this node's configuration goes to vtysh ? */
+ int vtysh;
+
+ /* Node's configuration write function */
+ int (*func) (struct vty *);
+
+ /* Vector of this node's command list. */
+ vector cmd_vector;
+};
+
+enum {
+ CMD_ATTR_DEPRECATED = 1,
+ CMD_ATTR_HIDDEN,
+};
+
+/* Structure of command element. */
+struct cmd_element {
+ const char *string; /* Command specification by string. */
+ int (*func) (struct cmd_element *, struct vty *, int, const char *[]);
+ const char *doc; /* Documentation of this command. */
+ int daemon; /* Daemon to which this command belong. */
+ vector strvec; /* Pointing out each description vector. */
+ unsigned int cmdsize; /* Command index count. */
+ char *config; /* Configuration string */
+ vector subconfig; /* Sub configuration string */
+ u_char attr; /* Command attributes */
+};
+
+/* Command description structure. */
+struct desc {
+ const char *cmd; /* Command string. */
+ const char *str; /* Command's description. */
+};
+
+/* Return value of the commands. */
+#define CMD_SUCCESS 0
+#define CMD_WARNING 1
+#define CMD_ERR_NO_MATCH 2
+#define CMD_ERR_AMBIGUOUS 3
+#define CMD_ERR_INCOMPLETE 4
+#define CMD_ERR_EXEED_ARGC_MAX 5
+#define CMD_ERR_NOTHING_TODO 6
+#define CMD_COMPLETE_FULL_MATCH 7
+#define CMD_COMPLETE_MATCH 8
+#define CMD_COMPLETE_LIST_MATCH 9
+#define CMD_SUCCESS_DAEMON 10
+
+/* Argc max counts. */
+#define CMD_ARGC_MAX 25
+
+/* Turn off these macros when uisng cpp with extract.pl */
+#ifndef VTYSH_EXTRACT_PL
+
+/* helper defines for end-user DEFUN* macros */
+#define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \
+ struct cmd_element cmdname = \
+ { \
+ .string = cmdstr, \
+ .func = funcname, \
+ .doc = helpstr, \
+ .attr = attrs, \
+ .daemon = dnum, \
+ };
+
+#define DEFUN_CMD_FUNC_DECL(funcname) \
+ static int funcname (struct cmd_element *, struct vty *, int, const char *[]); \
+
+#define DEFUN_CMD_FUNC_TEXT(funcname) \
+ static int funcname \
+ (struct cmd_element *self, struct vty *vty, int argc, const char *argv[])
+
+/* DEFUN for vty command interafce. Little bit hacky ;-). */
+#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUN_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR (funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED) \
+
+/* DEFUN_NOSH for commands that vtysh should ignore */
+#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN(funcname, cmdname, cmdstr, helpstr)
+
+/* DEFSH for vtysh. */
+#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) \
+
+/* DEFUN + DEFSH */
+#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+/* DEFUN + DEFSH with attributes */
+#define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_FUNC_DECL(funcname) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \
+ DEFUN_CMD_FUNC_TEXT(funcname)
+
+#define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+
+#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR (daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED)
+
+/* ALIAS macro which define existing command's alias. */
+#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
+
+#define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0)
+
+#define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, 0)
+
+#define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, 0)
+
+#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
+
+#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, daemon)
+
+#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_DEPRECATED, daemon)
+
+#endif /* VTYSH_EXTRACT_PL */
+
+/* Some macroes */
+#define CMD_OPTION(S) ((S[0]) == '[')
+#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<'))
+#define CMD_VARARG(S) ((S[0]) == '.')
+#define CMD_RANGE(S) ((S[0] == '<'))
+
+#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0))
+#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0))
+#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0))
+#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0))
+
+/* Common descriptions. */
+#define SHOW_STR "Show running system information\n"
+#define IP_STR "IP information\n"
+#define IPV6_STR "IPv6 information\n"
+#define NO_STR "Negate a command or set its defaults\n"
+#define CLEAR_STR "Reset functions\n"
+#define RIP_STR "RIP information\n"
+#define BGP_STR "BGP information\n"
+#define OSPF_STR "OSPF information\n"
+#define NEIGHBOR_STR "Specify neighbor router\n"
+#define DEBUG_STR "Debugging functions (see also 'undebug')\n"
+#define UNDEBUG_STR "Disable debugging functions (see also 'debug')\n"
+#define ROUTER_STR "Enable a routing process\n"
+#define AS_STR "AS number\n"
+#define MBGP_STR "MBGP information\n"
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+#define OUT_STR "Filter outgoing routing updates\n"
+#define IN_STR "Filter incoming routing updates\n"
+#define V4NOTATION_STR "specify by IPv4 address notation(e.g. 0.0.0.0)\n"
+#define OSPF6_NUMBER_STR "Specify by number\n"
+#define INTERFACE_STR "Interface infomation\n"
+#define IFNAME_STR "Interface name(e.g. ep0)\n"
+#define IP6_STR "IPv6 Information\n"
+#define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n"
+#define OSPF6_ROUTER_STR "Enable a routing process\n"
+#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n"
+#define SECONDS_STR "<1-65535> Seconds\n"
+#define ROUTE_STR "Routing Table\n"
+#define PREFIX_LIST_STR "Build a prefix list\n"
+#define OSPF6_DUMP_TYPE_LIST \
+"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)"
+#define ISIS_STR "IS-IS information\n"
+#define AREA_TAG_STR "[area tag]\n"
+
+#define CONF_BACKUP_EXT ".sav"
+
+/* IPv4 only machine should not accept IPv6 address for peer's IP
+ address. So we replace VTY command string like below. */
+#ifdef HAVE_IPV6
+#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) "
+#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) "
+#define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n"
+#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nNeighbor tag\n"
+#else
+#define NEIGHBOR_CMD "neighbor A.B.C.D "
+#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D "
+#define NEIGHBOR_ADDR_STR "Neighbor address\n"
+#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) "
+#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) "
+#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n"
+#endif /* HAVE_IPV6 */
+
+/* Prototypes. */
+void install_node(struct cmd_node *, int (*)(struct vty *));
+void install_default(enum node_type);
+void install_element(enum node_type, struct cmd_element *);
+void sort_node();
+
+/* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated
+ string with a space between each element (allocated using
+ XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */
+char *argv_concat(const char **argv, int argc, int shift);
+
+vector cmd_make_strvec(const char *);
+void cmd_free_strvec(vector);
+vector cmd_describe_command();
+char **cmd_complete_command();
+const char *cmd_prompt(enum node_type);
+int config_from_file(struct vty *, FILE *);
+enum node_type node_parent(enum node_type);
+int cmd_execute_command(vector, struct vty *, struct cmd_element **, int);
+int cmd_execute_command_strict(vector, struct vty *, struct cmd_element **);
+void config_replace_string(struct cmd_element *, char *, ...);
+void cmd_init(int);
+
+/* Export typical functions. */
+extern struct cmd_element config_end_cmd;
+extern struct cmd_element config_exit_cmd;
+extern struct cmd_element config_quit_cmd;
+extern struct cmd_element config_help_cmd;
+extern struct cmd_element config_list_cmd;
+char *host_config_file();
+void host_config_set(char *);
+
+void print_version(const char *);
+
+#endif /* _ZEBRA_COMMAND_H */
diff --git a/openbsc/include/vty/vector.h b/openbsc/include/vty/vector.h
new file mode 100644
index 000000000..00f0079f3
--- /dev/null
+++ b/openbsc/include/vty/vector.h
@@ -0,0 +1,62 @@
+/*
+ * Generic vector interface header.
+ * Copyright (C) 1997, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VECTOR_H
+#define _ZEBRA_VECTOR_H
+
+/* struct for vector */
+struct _vector {
+ unsigned int active; /* number of active slots */
+ unsigned int alloced; /* number of allocated slot */
+ void **index; /* index to data */
+};
+typedef struct _vector *vector;
+
+#define VECTOR_MIN_SIZE 1
+
+/* (Sometimes) usefull macros. This macro convert index expression to
+ array expression. */
+/* Reference slot at given index, caller must ensure slot is active */
+#define vector_slot(V,I) ((V)->index[(I)])
+/* Number of active slots.
+ * Note that this differs from vector_count() as it the count returned
+ * will include any empty slots
+ */
+#define vector_active(V) ((V)->active)
+
+/* Prototypes. */
+vector vector_init(unsigned int size);
+void vector_ensure(vector v, unsigned int num);
+int vector_empty_slot(vector v);
+int vector_set(vector v, void *val);
+int vector_set_index(vector v, unsigned int i, void *val);
+void vector_unset(vector v, unsigned int i);
+unsigned int vector_count(vector v);
+void vector_only_wrapper_free(vector v);
+void vector_only_index_free(void *index);
+void vector_free(vector v);
+vector vector_copy(vector v);
+
+void *vector_lookup(vector, unsigned int);
+void *vector_lookup_ensure(vector, unsigned int);
+
+#endif /* _ZEBRA_VECTOR_H */
diff --git a/openbsc/include/vty/vty.h b/openbsc/include/vty/vty.h
new file mode 100644
index 000000000..4bb785b9f
--- /dev/null
+++ b/openbsc/include/vty/vty.h
@@ -0,0 +1,150 @@
+#ifndef _VTY_H
+#define _VTY_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* GCC have printf type attribute check. */
+#ifdef __GNUC__
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* __GNUC__ */
+
+/* Does the I/O error indicate that the operation should be retried later? */
+#define ERRNO_IO_RETRY(EN) \
+ (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
+
+/* Vty read buffer size. */
+#define VTY_READ_BUFSIZ 512
+
+#define VTY_BUFSIZ 512
+#define VTY_MAXHIST 20
+
+/* Vty events */
+enum event {
+ VTY_SERV,
+ VTY_READ,
+ VTY_WRITE,
+ VTY_TIMEOUT_RESET,
+#ifdef VTYSH
+ VTYSH_SERV,
+ VTYSH_READ,
+ VTYSH_WRITE
+#endif /* VTYSH */
+};
+
+struct vty {
+ FILE *file;
+
+ /* private data, specified by creator */
+ void *priv;
+
+ /* File descripter of this vty. */
+ int fd;
+
+ /* Is this vty connect to file or not */
+ enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type;
+
+ /* Node status of this vty */
+ int node;
+
+ /* Failure count */
+ int fail;
+
+ /* Output buffer. */
+ struct buffer *obuf;
+
+ /* Command input buffer */
+ char *buf;
+
+ /* Command cursor point */
+ int cp;
+
+ /* Command length */
+ int length;
+
+ /* Command max length. */
+ int max;
+
+ /* Histry of command */
+ char *hist[VTY_MAXHIST];
+
+ /* History lookup current point */
+ int hp;
+
+ /* History insert end point */
+ int hindex;
+
+ /* For current referencing point of interface, route-map,
+ access-list etc... */
+ void *index;
+
+ /* For multiple level index treatment such as key chain and key. */
+ void *index_sub;
+
+ /* For escape character. */
+ unsigned char escape;
+
+ /* Current vty status. */
+ enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
+
+ /* IAC handling: was the last character received the IAC
+ * (interpret-as-command) escape character (and therefore the next
+ * character will be the command code)? Refer to Telnet RFC 854. */
+ unsigned char iac;
+
+ /* IAC SB (option subnegotiation) handling */
+ unsigned char iac_sb_in_progress;
+ /* At the moment, we care only about the NAWS (window size) negotiation,
+ * and that requires just a 5-character buffer (RFC 1073):
+ * <NAWS char> <16-bit width> <16-bit height> */
+#define TELNET_NAWS_SB_LEN 5
+ unsigned char sb_buf[TELNET_NAWS_SB_LEN];
+ /* How many subnegotiation characters have we received? We just drop
+ * those that do not fit in the buffer. */
+ size_t sb_len;
+
+ /* Window width/height. */
+ int width;
+ int height;
+
+ /* Configure lines. */
+ int lines;
+
+ int monitor;
+
+ /* In configure mode. */
+ int config;
+};
+
+/* Small macro to determine newline is newline only or linefeed needed. */
+#define VTY_NEWLINE ((vty->type == VTY_TERM) ? "\r\n" : "\n")
+
+static inline char *vty_newline(struct vty *vty)
+{
+ return VTY_NEWLINE;
+}
+
+/* Prototypes. */
+void vty_init (void);
+void vty_init_vtysh (void);
+void vty_reset (void);
+struct vty *vty_new (void);
+struct vty *vty_create (int vty_sock, void *priv);
+int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3);
+int vty_out_newline(struct vty *);
+int vty_read(struct vty *vty);
+void vty_read_config (char *, char *);
+void vty_time_print (struct vty *, int);
+void vty_close (struct vty *);
+char *vty_get_cwd (void);
+void vty_log (const char *level, const char *proto, const char *fmt, va_list);
+int vty_config_lock (struct vty *);
+int vty_config_unlock (struct vty *);
+int vty_shell (struct vty *);
+int vty_shell_serv (struct vty *);
+void vty_hello (struct vty *);
+
+
+#endif
diff --git a/openbsc/openbsc.pc.in b/openbsc/openbsc.pc.in
new file mode 100644
index 000000000..0d00831a2
--- /dev/null
+++ b/openbsc/openbsc.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/openbsc-1.0
+
+Name: OpenBSC
+Description: OpenBSC base station controller
+Requires:
+Version: @VERSION@
+Libs: -L${libdir} -lopenbsc
+Cflags: -I${includedir}
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
new file mode 100644
index 000000000..fbbbfdc94
--- /dev/null
+++ b/openbsc/src/Makefile.am
@@ -0,0 +1,27 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall
+
+sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
+noinst_LIBRARIES = libbsc.a libvty.a
+noinst_HEADERS = vty/cardshell.h
+
+libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
+ gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
+ gsm_04_11.c telnet_interface.c subchan_demux.c \
+ trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
+ input/misdn.c input/ipaccess.c signal.c gsm_utils.c
+
+libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
+
+bsc_hack_SOURCES = bsc_hack.c vty_interface.c
+bsc_hack_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
+
+bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \
+ select.c timer.c rs232.c tlv_parser.c signal.c
+
+ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c
+
+ipaccess_config_SOURCES = ipaccess-config.c
+ipaccess_config_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
+
+isdnsync_SOURCES = isdnsync.c
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
new file mode 100644
index 000000000..74dba2377
--- /dev/null
+++ b/openbsc/src/abis_nm.c
@@ -0,0 +1,2332 @@
+/* GSM Network Management (OML) messages on the A-bis interface
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <time.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/debug.h>
+#include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/misdn.h>
+#include <openbsc/signal.h>
+
+#define OM_ALLOC_SIZE 1024
+#define OM_HEADROOM_SIZE 128
+
+/* unidirectional messages from BTS to BSC */
+static const enum abis_nm_msgtype reports[] = {
+ NM_MT_SW_ACTIVATED_REP,
+ NM_MT_TEST_REP,
+ NM_MT_STATECHG_EVENT_REP,
+ NM_MT_FAILURE_EVENT_REP,
+};
+
+/* messages without ACK/NACK */
+static const enum abis_nm_msgtype no_ack_nack[] = {
+ NM_MT_MEAS_RES_REQ,
+ NM_MT_STOP_MEAS,
+ NM_MT_START_MEAS,
+};
+
+/* Messages related to software load */
+static const enum abis_nm_msgtype sw_load_msgs[] = {
+ NM_MT_LOAD_INIT_ACK,
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_SEG_ACK,
+ NM_MT_LOAD_ABORT,
+ NM_MT_LOAD_END_ACK,
+ NM_MT_LOAD_END_NACK,
+ //NM_MT_SW_ACT_REQ,
+ NM_MT_ACTIVATE_SW_ACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_SW_ACTIVATED_REP,
+};
+
+static const enum abis_nm_msgtype nacks[] = {
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_END_NACK,
+ NM_MT_SW_ACT_REQ_NACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_ESTABLISH_TEI_NACK,
+ NM_MT_CONN_TERR_SIGN_NACK,
+ NM_MT_DISC_TERR_SIGN_NACK,
+ NM_MT_CONN_TERR_TRAF_NACK,
+ NM_MT_DISC_TERR_TRAF_NACK,
+ NM_MT_CONN_MDROP_LINK_NACK,
+ NM_MT_DISC_MDROP_LINK_NACK,
+ NM_MT_SET_BTS_ATTR_NACK,
+ NM_MT_SET_RADIO_ATTR_NACK,
+ NM_MT_SET_CHAN_ATTR_NACK,
+ NM_MT_PERF_TEST_NACK,
+ NM_MT_SEND_TEST_REP_NACK,
+ NM_MT_STOP_TEST_NACK,
+ NM_MT_STOP_EVENT_REP_NACK,
+ NM_MT_REST_EVENT_REP_NACK,
+ NM_MT_CHG_ADM_STATE_NACK,
+ NM_MT_CHG_ADM_STATE_REQ_NACK,
+ NM_MT_REP_OUTST_ALARMS_NACK,
+ NM_MT_CHANGEOVER_NACK,
+ NM_MT_OPSTART_NACK,
+ NM_MT_REINIT_NACK,
+ NM_MT_SET_SITE_OUT_NACK,
+ NM_MT_CHG_HW_CONF_NACK,
+ NM_MT_GET_ATTR_NACK,
+ NM_MT_SET_ALARM_THRES_NACK,
+ NM_MT_BS11_BEGIN_DB_TX_NACK,
+ NM_MT_BS11_END_DB_TX_NACK,
+ NM_MT_BS11_CREATE_OBJ_NACK,
+ NM_MT_BS11_DELETE_OBJ_NACK,
+};
+
+static const char *nack_names[0xff] = {
+ [NM_MT_LOAD_INIT_NACK] = "SOFTWARE LOAD INIT",
+ [NM_MT_LOAD_END_NACK] = "SOFTWARE LOAD END",
+ [NM_MT_SW_ACT_REQ_NACK] = "SOFTWARE ACTIVATE REQUEST",
+ [NM_MT_ACTIVATE_SW_NACK] = "ACTIVATE SOFTWARE",
+ [NM_MT_ESTABLISH_TEI_NACK] = "ESTABLISH TEI",
+ [NM_MT_CONN_TERR_SIGN_NACK] = "CONNECT TERRESTRIAL SIGNALLING",
+ [NM_MT_DISC_TERR_SIGN_NACK] = "DISCONNECT TERRESTRIAL SIGNALLING",
+ [NM_MT_CONN_TERR_TRAF_NACK] = "CONNECT TERRESTRIAL TRAFFIC",
+ [NM_MT_DISC_TERR_TRAF_NACK] = "DISCONNECT TERRESTRIAL TRAFFIC",
+ [NM_MT_CONN_MDROP_LINK_NACK] = "CONNECT MULTI-DROP LINK",
+ [NM_MT_DISC_MDROP_LINK_NACK] = "DISCONNECT MULTI-DROP LINK",
+ [NM_MT_SET_BTS_ATTR_NACK] = "SET BTS ATTRIBUTE",
+ [NM_MT_SET_RADIO_ATTR_NACK] = "SET RADIO ATTRIBUTE",
+ [NM_MT_SET_CHAN_ATTR_NACK] = "SET CHANNEL ATTRIBUTE",
+ [NM_MT_PERF_TEST_NACK] = "PERFORM TEST",
+ [NM_MT_SEND_TEST_REP_NACK] = "SEND TEST REPORT",
+ [NM_MT_STOP_TEST_NACK] = "STOP TEST",
+ [NM_MT_STOP_EVENT_REP_NACK] = "STOP EVENT REPORT",
+ [NM_MT_REST_EVENT_REP_NACK] = "RESET EVENT REPORT",
+ [NM_MT_CHG_ADM_STATE_NACK] = "CHANGE ADMINISTRATIVE STATE",
+ [NM_MT_CHG_ADM_STATE_REQ_NACK] = "CHANGE ADMINISTRATIVE STATE REQUEST",
+ [NM_MT_REP_OUTST_ALARMS_NACK] = "REPORT OUTSTANDING ALARMS",
+ [NM_MT_CHANGEOVER_NACK] = "CHANGEOVER",
+ [NM_MT_OPSTART_NACK] = "OPSTART",
+ [NM_MT_REINIT_NACK] = "REINIT",
+ [NM_MT_SET_SITE_OUT_NACK] = "SET SITE OUTPUT",
+ [NM_MT_CHG_HW_CONF_NACK] = "CHANGE HARDWARE CONFIGURATION",
+ [NM_MT_GET_ATTR_NACK] = "GET ATTRIBUTE",
+ [NM_MT_SET_ALARM_THRES_NACK] = "SET ALARM THRESHOLD",
+ [NM_MT_BS11_BEGIN_DB_TX_NACK] = "BS11 BEGIN DATABASE TRANSMISSION",
+ [NM_MT_BS11_END_DB_TX_NACK] = "BS11 END DATABASE TRANSMISSION",
+ [NM_MT_BS11_CREATE_OBJ_NACK] = "BS11 CREATE OBJECT",
+ [NM_MT_BS11_DELETE_OBJ_NACK] = "BS11 DELETE OBJECT",
+};
+
+/* Chapter 9.4.36 */
+static const char *nack_cause_names[] = {
+ /* General Nack Causes */
+ [NM_NACK_INCORR_STRUCT] = "Incorrect message structure",
+ [NM_NACK_MSGTYPE_INVAL] = "Invalid message type value",
+ [NM_NACK_OBJCLASS_INVAL] = "Invalid Object class value",
+ [NM_NACK_OBJCLASS_NOTSUPP] = "Object class not supported",
+ [NM_NACK_BTSNR_UNKN] = "BTS no. unknown",
+ [NM_NACK_TRXNR_UNKN] = "Baseband Transceiver no. unknown",
+ [NM_NACK_OBJINST_UNKN] = "Object Instance unknown",
+ [NM_NACK_ATTRID_INVAL] = "Invalid attribute identifier value",
+ [NM_NACK_ATTRID_NOTSUPP] = "Attribute identifier not supported",
+ [NM_NACK_PARAM_RANGE] = "Parameter value outside permitted range",
+ [NM_NACK_ATTRLIST_INCONSISTENT] = "Inconsistency in attribute list",
+ [NM_NACK_SPEC_IMPL_NOTSUPP] = "Specified implementation not supported",
+ [NM_NACK_CANT_PERFORM] = "Message cannot be performed",
+ /* Specific Nack Causes */
+ [NM_NACK_RES_NOTIMPL] = "Resource not implemented",
+ [NM_NACK_RES_NOTAVAIL] = "Resource not available",
+ [NM_NACK_FREQ_NOTAVAIL] = "Frequency not available",
+ [NM_NACK_TEST_NOTSUPP] = "Test not supported",
+ [NM_NACK_CAPACITY_RESTR] = "Capacity restrictions",
+ [NM_NACK_PHYSCFG_NOTPERFORM] = "Physical configuration cannot be performed",
+ [NM_NACK_TEST_NOTINIT] = "Test not initiated",
+ [NM_NACK_PHYSCFG_NOTRESTORE] = "Physical configuration cannot be restored",
+ [NM_NACK_TEST_NOSUCH] = "No such test",
+ [NM_NACK_TEST_NOSTOP] = "Test cannot be stopped",
+ [NM_NACK_MSGINCONSIST_PHYSCFG] = "Message inconsistent with physical configuration",
+ [NM_NACK_FILE_INCOMPLETE] = "Complete file notreceived",
+ [NM_NACK_FILE_NOTAVAIL] = "File not available at destination",
+ [MN_NACK_FILE_NOTACTIVATE] = "File cannot be activate",
+ [NM_NACK_REQ_NOT_GRANT] = "Request not granted",
+ [NM_NACK_WAIT] = "Wait",
+ [NM_NACK_NOTH_REPORT_EXIST] = "Nothing reportable existing",
+ [NM_NACK_MEAS_NOTSUPP] = "Measurement not supported",
+ [NM_NACK_MEAS_NOTSTART] = "Measurement not started",
+};
+
+static char namebuf[255];
+static const char *nack_cause_name(u_int8_t cause)
+{
+ if (cause < ARRAY_SIZE(nack_cause_names) && nack_cause_names[cause])
+ return nack_cause_names[cause];
+
+ snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
+ return namebuf;
+}
+
+/* Chapter 9.4.16: Event Type */
+static const char *event_type_names[] = {
+ [NM_EVT_COMM_FAIL] = "communication failure",
+ [NM_EVT_QOS_FAIL] = "quality of service failure",
+ [NM_EVT_PROC_FAIL] = "processing failure",
+ [NM_EVT_EQUIP_FAIL] = "equipment failure",
+ [NM_EVT_ENV_FAIL] = "environment failure",
+};
+
+static const char *event_type_name(u_int8_t cause)
+{
+ if (cause < ARRAY_SIZE(event_type_names) && event_type_names[cause])
+ return event_type_names[cause];
+
+ snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
+ return namebuf;
+}
+
+/* Chapter 9.4.63: Perceived Severity */
+static const char *severity_names[] = {
+ [NM_SEVER_CEASED] = "failure ceased",
+ [NM_SEVER_CRITICAL] = "critical failure",
+ [NM_SEVER_MAJOR] = "major failure",
+ [NM_SEVER_MINOR] = "minor failure",
+ [NM_SEVER_WARNING] = "warning level failure",
+ [NM_SEVER_INDETERMINATE] = "indeterminate failure",
+};
+
+static const char *severity_name(u_int8_t cause)
+{
+ if (cause < ARRAY_SIZE(severity_names) && severity_names[cause])
+ return severity_names[cause];
+
+ snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
+ return namebuf;
+}
+
+/* Attributes that the BSC can set, not only get, according to Section 9.4 */
+static const enum abis_nm_attr nm_att_settable[] = {
+ NM_ATT_ADD_INFO,
+ NM_ATT_ADD_TEXT,
+ NM_ATT_DEST,
+ NM_ATT_EVENT_TYPE,
+ NM_ATT_FILE_DATA,
+ NM_ATT_GET_ARI,
+ NM_ATT_HW_CONF_CHG,
+ NM_ATT_LIST_REQ_ATTR,
+ NM_ATT_MDROP_LINK,
+ NM_ATT_MDROP_NEXT,
+ NM_ATT_NACK_CAUSES,
+ NM_ATT_OUTST_ALARM,
+ NM_ATT_PHYS_CONF,
+ NM_ATT_PROB_CAUSE,
+ NM_ATT_RAD_SUBC,
+ NM_ATT_SOURCE,
+ NM_ATT_SPEC_PROB,
+ NM_ATT_START_TIME,
+ NM_ATT_TEST_DUR,
+ NM_ATT_TEST_NO,
+ NM_ATT_TEST_REPORT,
+ NM_ATT_WINDOW_SIZE,
+ NM_ATT_SEVERITY,
+ NM_ATT_MEAS_RES,
+ NM_ATT_MEAS_TYPE,
+};
+
+static const struct tlv_definition nm_att_tlvdef = {
+ .def = {
+ [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADM_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V },
+ [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV },
+ [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V },
+ [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_BSIC] = { TLV_TYPE_TV },
+ [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV },
+ [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV },
+ [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V },
+ [NM_ATT_DEST] = { TLV_TYPE_TL16V },
+ [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV },
+ [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V },
+ [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_HSN] = { TLV_TYPE_TV },
+ [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V },
+ [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV },
+ [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 },
+ [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAIO] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V },
+ [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAX_TA] = { TLV_TYPE_TV },
+ [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV },
+ [NM_ATT_NY1] = { TLV_TYPE_TV },
+ [NM_ATT_OPER_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V },
+ [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V },
+ [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV },
+ [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV },
+ [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV },
+ [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV },
+ [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SOURCE] = { TLV_TYPE_TL16V },
+ [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV },
+ [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 },
+ [NM_ATT_TEI] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_TEST_NO] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V },
+ [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV },
+ [NM_ATT_TSC] = { TLV_TYPE_TV },
+ [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_SEVERITY] = { TLV_TYPE_TV },
+ [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
+ [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
+ [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
+ [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
+ /* BS11 specifics */
+ [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV },
+ [0xd5] = { TLV_TYPE_TLV },
+ [0xa8] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV },
+ [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV },
+ [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV },
+ [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV },
+ /* ip.access specifics */
+ [NM_ATT_IPACC_RSL_BSC_IP] = { TLV_TYPE_FIXED, 4 },
+ [NM_ATT_IPACC_RSL_BSC_PORT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_IPACC_PRIM_OML_IP] = { TLV_TYPE_FIXED, 6 },
+ [0x95] = { TLV_TYPE_FIXED, 2 },
+ [0x85] = { TLV_TYPE_TV },
+
+ },
+};
+
+int abis_nm_tlv_parse(struct tlv_parsed *tp, const u_int8_t *buf, int len)
+{
+ return tlv_parse(tp, &nm_att_tlvdef, buf, len, 0, 0);
+}
+
+static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (arr[i] == mt)
+ return 1;
+ }
+
+ return 0;
+}
+
+#if 0
+/* is this msgtype the usual ACK/NACK type ? */
+static int is_ack_nack(enum abis_nm_msgtype mt)
+{
+ return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack));
+}
+#endif
+
+/* is this msgtype a report ? */
+static int is_report(enum abis_nm_msgtype mt)
+{
+ return is_in_arr(mt, reports, ARRAY_SIZE(reports));
+}
+
+#define MT_ACK(x) (x+1)
+#define MT_NACK(x) (x+2)
+
+static void fill_om_hdr(struct abis_om_hdr *oh, u_int8_t len)
+{
+ oh->mdisc = ABIS_OM_MDISC_FOM;
+ oh->placement = ABIS_OM_PLACEMENT_ONLY;
+ oh->sequence = 0;
+ oh->length = len;
+}
+
+static void fill_om_fom_hdr(struct abis_om_hdr *oh, u_int8_t len,
+ u_int8_t msg_type, u_int8_t obj_class,
+ u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr)
+{
+ struct abis_om_fom_hdr *foh =
+ (struct abis_om_fom_hdr *) oh->data;
+
+ fill_om_hdr(oh, len+sizeof(*foh));
+ foh->msg_type = msg_type;
+ foh->obj_class = obj_class;
+ foh->obj_inst.bts_nr = bts_nr;
+ foh->obj_inst.trx_nr = trx_nr;
+ foh->obj_inst.ts_nr = ts_nr;
+}
+
+static struct msgb *nm_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE);
+}
+
+/* Send a OML NM Message from BSC to BTS */
+int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
+{
+ msg->trx = bts->c0;
+
+ return _abis_nm_sendmsg(msg);
+}
+
+static int abis_nm_rcvmsg_sw(struct msgb *mb);
+
+static const char *obj_class_name(u_int8_t oc)
+{
+ switch (oc) {
+ case NM_OC_SITE_MANAGER:
+ return "SITE MANAGER";
+ case NM_OC_BTS:
+ return "BTS";
+ case NM_OC_RADIO_CARRIER:
+ return "RADIO CARRIER";
+ case NM_OC_BASEB_TRANSC:
+ return "BASEBAND TRANSCEIVER";
+ case NM_OC_CHANNEL:
+ return "CHANNEL";
+ case NM_OC_BS11_ADJC:
+ return "ADJC";
+ case NM_OC_BS11_HANDOVER:
+ return "HANDOVER";
+ case NM_OC_BS11_PWR_CTRL:
+ return "POWER CONTROL";
+ case NM_OC_BS11_BTSE:
+ return "BTSE";
+ case NM_OC_BS11_RACK:
+ return "RACK";
+ case NM_OC_BS11_TEST:
+ return "TEST";
+ case NM_OC_BS11_ENVABTSE:
+ return "ENVABTSE";
+ case NM_OC_BS11_BPORT:
+ return "BPORT";
+ case NM_OC_GPRS_NSE:
+ return "GPRS NSE";
+ case NM_OC_GPRS_CELL:
+ return "GPRS CELL";
+ case NM_OC_GPRS_NSVC0:
+ return "GPRS NSVC0";
+ case NM_OC_GPRS_NSVC1:
+ return "GPRS NSVC1";
+ case NM_OC_BS11:
+ return "SIEMENSHW";
+ }
+
+ return "UNKNOWN";
+}
+
+const char *nm_opstate_name(u_int8_t os)
+{
+ switch (os) {
+ case 1:
+ return "Disabled";
+ case 2:
+ return "Enabled";
+ case 0xff:
+ return "NULL";
+ default:
+ return "RFU";
+ }
+}
+
+/* Chapter 9.4.7 */
+static const char *avail_names[] = {
+ "In test",
+ "Failed",
+ "Power off",
+ "Off line",
+ "<not used>",
+ "Dependency",
+ "Degraded",
+ "Not installed",
+};
+
+const char *nm_avail_name(u_int8_t avail)
+{
+ if (avail == 0xff)
+ return "OK";
+ if (avail >= ARRAY_SIZE(avail_names))
+ return "UNKNOWN";
+ return avail_names[avail];
+}
+
+const char *nm_adm_name(u_int8_t adm)
+{
+ switch (adm) {
+ case 1:
+ return "Locked";
+ case 2:
+ return "Unlocked";
+ case 3:
+ return "Shutdown";
+ default:
+ return "<not used>";
+ }
+}
+
+/* obtain the gsm_nm_state data structure for a given object instance */
+static struct gsm_nm_state *
+objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
+ struct abis_om_obj_inst *obj_inst)
+{
+ struct gsm_bts_trx *trx;
+ struct gsm_nm_state *nm_state = NULL;
+
+ switch (obj_class) {
+ case NM_OC_BTS:
+ nm_state = &bts->nm_state;
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ nm_state = &trx->nm_state;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ nm_state = &trx->bb_transc.nm_state;
+ break;
+ case NM_OC_CHANNEL:
+ if (obj_inst->trx_nr > bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ if (obj_inst->ts_nr >= TRX_NR_TS)
+ return NULL;
+ nm_state = &trx->ts[obj_inst->ts_nr].nm_state;
+ break;
+ case NM_OC_SITE_MANAGER:
+ nm_state = &bts->site_mgr.nm_state;
+ break;
+ case NM_OC_BS11:
+ switch (obj_inst->bts_nr) {
+ case BS11_OBJ_CCLK:
+ nm_state = &bts->bs11.cclk.nm_state;
+ break;
+ case BS11_OBJ_BBSIG:
+ if (obj_inst->ts_nr > bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->ts_nr];
+ nm_state = &trx->bs11.bbsig.nm_state;
+ break;
+ case BS11_OBJ_PA:
+ if (obj_inst->ts_nr > bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->ts_nr];
+ nm_state = &trx->bs11.pa.nm_state;
+ break;
+ default:
+ return NULL;
+ }
+ case NM_OC_BS11_RACK:
+ nm_state = &bts->bs11.rack.nm_state;
+ break;
+ case NM_OC_BS11_ENVABTSE:
+ if (obj_inst->trx_nr > ARRAY_SIZE(bts->bs11.envabtse))
+ return NULL;
+ nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state;
+ break;
+ }
+ return nm_state;
+}
+
+/* obtain the in-memory data structure of a given object instance */
+static void *
+objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
+ struct abis_om_obj_inst *obj_inst)
+{
+ struct gsm_bts_trx *trx;
+ void *obj = NULL;
+
+ switch (obj_class) {
+ case NM_OC_BTS:
+ obj = bts;
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ obj = trx;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ obj = &trx->bb_transc;
+ break;
+ case NM_OC_CHANNEL:
+ if (obj_inst->trx_nr > bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ if (obj_inst->ts_nr >= TRX_NR_TS)
+ return NULL;
+ obj = &trx->ts[obj_inst->ts_nr];
+ break;
+ case NM_OC_SITE_MANAGER:
+ obj = &bts->site_mgr;
+ break;
+ }
+ return obj;
+}
+
+/* Update the administrative state of a given object in our in-memory data
+ * structures and send an event to the higher layer */
+static int update_admstate(struct gsm_bts *bts, u_int8_t obj_class,
+ struct abis_om_obj_inst *obj_inst, u_int8_t adm_state)
+{
+ struct gsm_nm_state *nm_state, new_state;
+ void *obj;
+ int rc;
+
+ obj = objclass2obj(bts, obj_class, obj_inst);
+ nm_state = objclass2nmstate(bts, obj_class, obj_inst);
+ if (!nm_state)
+ return -1;
+
+ new_state = *nm_state;
+ new_state.administrative = adm_state;
+
+ rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, &new_state);
+
+ nm_state->administrative = adm_state;
+
+ return rc;
+}
+
+static int abis_nm_rx_statechg_rep(struct msgb *mb)
+{
+ struct abis_om_hdr *oh = msgb_l2(mb);
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ struct gsm_bts *bts = mb->trx->bts;
+ struct tlv_parsed tp;
+ struct gsm_nm_state *nm_state, new_state;
+ int rc;
+
+ DEBUGPC(DNM, "STATE CHG: ");
+
+ memset(&new_state, 0, sizeof(new_state));
+
+ nm_state = objclass2nmstate(bts, foh->obj_class, &foh->obj_inst);
+ if (!nm_state) {
+ DEBUGPC(DNM, "\n");
+ return -EINVAL;
+ }
+
+ new_state = *nm_state;
+
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) {
+ new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE);
+ DEBUGPC(DNM, "OP_STATE=%s ", nm_opstate_name(new_state.operational));
+ }
+ if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) {
+ if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0)
+ new_state.availability = 0xff;
+ else
+ new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
+ DEBUGPC(DNM, "AVAIL=%s(%02x) ", nm_avail_name(new_state.availability),
+ new_state.availability);
+ }
+ if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
+ new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
+ DEBUGPC(DNM, "ADM=%02x ", nm_adm_name(new_state.administrative));
+ }
+ DEBUGPC(DNM, "\n");
+
+ if (memcmp(&new_state, nm_state, sizeof(new_state))) {
+ /* Update the operational state of a given object in our in-memory data
+ * structures and send an event to the higher layer */
+ void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst);
+ rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state);
+ *nm_state = new_state;
+ }
+#if 0
+ if (op_state == 1) {
+ /* try to enable objects that are disabled */
+ abis_nm_opstart(bts, foh->obj_class,
+ foh->obj_inst.bts_nr,
+ foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr);
+ }
+#endif
+ return 0;
+}
+
+static int rx_fail_evt_rep(struct msgb *mb)
+{
+ struct abis_om_hdr *oh = msgb_l2(mb);
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ struct tlv_parsed tp;
+
+ DEBUGPC(DNM, "Failure Event Report ");
+
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+
+ if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE))
+ DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE)));
+ if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY))
+ DEBUGPC(DNM, "Severity=%s ", severity_name(*TLVP_VAL(&tp, NM_ATT_SEVERITY)));
+
+ DEBUGPC(DNM, "\n");
+
+ return 0;
+}
+
+static int abis_nm_rcvmsg_report(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ u_int8_t mt = foh->msg_type;
+
+ DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
+ obj_class_name(foh->obj_class), foh->obj_class,
+ foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr);
+
+ //nmh->cfg->report_cb(mb, foh);
+
+ switch (mt) {
+ case NM_MT_STATECHG_EVENT_REP:
+ return abis_nm_rx_statechg_rep(mb);
+ break;
+ case NM_MT_SW_ACTIVATED_REP:
+ DEBUGPC(DNM, "Software Activated Report\n");
+ dispatch_signal(SS_NM, S_NM_SW_ACTIV_REP, mb);
+ break;
+ case NM_MT_FAILURE_EVENT_REP:
+ rx_fail_evt_rep(mb);
+ dispatch_signal(SS_NM, S_NM_FAIL_REP, mb);
+ break;
+ default:
+ DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt);
+ break;
+
+ };
+
+ return 0;
+}
+
+/* Activate the specified software into the BTS */
+static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1,
+ u_int8_t i2, u_int8_t *sw_desc, u_int8_t swdesc_len)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = swdesc_len;
+ u_int8_t *trailer;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2);
+
+ trailer = msgb_put(msg, swdesc_len);
+ memcpy(trailer, sw_desc, swdesc_len);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+static int abis_nm_rx_sw_act_req(struct msgb *mb)
+{
+ struct abis_om_hdr *oh = msgb_l2(mb);
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ int nack = 0;
+ int ret;
+
+ DEBUGP(DNM, "Software Activate Request ");
+
+ if (foh->obj_class >= 0xf0 && foh->obj_class <= 0xf3) {
+ DEBUGPC(DNM, "NACKing for GPRS obj_class 0x%02x\n", foh->obj_class);
+ nack = 1;
+ } else
+ DEBUGPC(DNM, "ACKing and Activating\n");
+
+ ret = abis_nm_sw_act_req_ack(mb->trx->bts, foh->obj_class,
+ foh->obj_inst.bts_nr,
+ foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr, nack,
+ foh->data, oh->length-sizeof(*foh));
+
+ if (nack)
+ return ret;
+
+ /* FIXME: properly parse attributes */
+ return ipacc_sw_activate(mb->trx->bts, foh->obj_class,
+ foh->obj_inst.bts_nr,
+ foh->obj_inst.trx_nr,
+ foh->obj_inst.ts_nr,
+ foh->data + oh->length-sizeof(*foh)-22, 22);
+}
+
+/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */
+static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb)
+{
+ struct abis_om_hdr *oh = msgb_l2(mb);
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ struct tlv_parsed tp;
+ u_int8_t adm_state;
+
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE))
+ return -EINVAL;
+
+ adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
+
+ return update_admstate(mb->trx->bts, foh->obj_class, &foh->obj_inst, adm_state);
+}
+
+static int abis_nm_rx_lmt_event(struct msgb *mb)
+{
+ struct abis_om_hdr *oh = msgb_l2(mb);
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ struct tlv_parsed tp;
+
+ DEBUGP(DNM, "LMT Event ");
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) &&
+ TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) {
+ u_int8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION);
+ DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF");
+ }
+ if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) &&
+ TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) {
+ u_int8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV);
+ DEBUGPC(DNM, "Level=%u ", level);
+ }
+ if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) &&
+ TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) {
+ char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME);
+ DEBUGPC(DNM, "Username=%s ", name);
+ }
+ DEBUGPC(DNM, "\n");
+ /* FIXME: parse LMT LOGON TIME */
+ return 0;
+}
+
+/* Receive a OML NM Message from BTS */
+static int abis_nm_rcvmsg_fom(struct msgb *mb)
+{
+ struct abis_om_hdr *oh = msgb_l2(mb);
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ u_int8_t mt = foh->msg_type;
+
+ /* check for unsolicited message */
+ if (is_report(mt))
+ return abis_nm_rcvmsg_report(mb);
+
+ if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs)))
+ return abis_nm_rcvmsg_sw(mb);
+
+ if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) {
+ struct tlv_parsed tp;
+ if (nack_names[mt])
+ DEBUGP(DNM, "%s NACK ", nack_names[mt]);
+ /* FIXME: NACK cause */
+ else
+ DEBUGP(DNM, "NACK 0x%02x ", mt);
+
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
+ DEBUGPC(DNM, "CAUSE=%s\n",
+ nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
+ else
+ DEBUGPC(DNM, "\n");
+ }
+#if 0
+ /* check if last message is to be acked */
+ if (is_ack_nack(nmh->last_msgtype)) {
+ if (mt == MT_ACK(nmh->last_msgtype)) {
+ fprintf(stderr, "received ACK (0x%x)\n",
+ foh->msg_type);
+ /* we got our ACK, continue sending the next msg */
+ } else if (mt == MT_NACK(nmh->last_msgtype)) {
+ /* we got a NACK, signal this to the caller */
+ fprintf(stderr, "received NACK (0x%x)\n",
+ foh->msg_type);
+ /* FIXME: somehow signal this to the caller */
+ } else {
+ /* really strange things happen */
+ return -EINVAL;
+ }
+ }
+#endif
+
+ switch (mt) {
+ case NM_MT_CHG_ADM_STATE_ACK:
+ return abis_nm_rx_chg_adm_state_ack(mb);
+ break;
+ case NM_MT_SW_ACT_REQ:
+ return abis_nm_rx_sw_act_req(mb);
+ break;
+ case NM_MT_BS11_LMT_SESSION:
+ return abis_nm_rx_lmt_event(mb);
+ break;
+ }
+
+ return 0;
+}
+
+static int abis_nm_rx_ipacc(struct msgb *mb);
+
+static int abis_nm_rcvmsg_manuf(struct msgb *mb)
+{
+ int rc;
+ int bts_type = mb->trx->bts->type;
+
+ switch (bts_type) {
+ case GSM_BTS_TYPE_NANOBTS_900:
+ case GSM_BTS_TYPE_NANOBTS_1800:
+ rc = abis_nm_rx_ipacc(mb);
+ break;
+ default:
+ fprintf(stderr, "don't know how to parse OML for this "
+ "BTS type (%u)\n", bts_type);
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+/* High-Level API */
+/* Entry-point where L2 OML from BTS enters the NM code */
+int abis_nm_rcvmsg(struct msgb *msg)
+{
+ struct abis_om_hdr *oh = msgb_l2(msg);
+ int rc = 0;
+
+ /* Various consistency checks */
+ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
+ fprintf(stderr, "ABIS OML placement 0x%x not supported\n",
+ oh->placement);
+ return -EINVAL;
+ }
+ if (oh->sequence != 0) {
+ fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n",
+ oh->sequence);
+ return -EINVAL;
+ }
+#if 0
+ unsigned int l2_len = msg->tail - (u_int8_t *)msgb_l2(msg);
+ unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr);
+ if (oh->length + hlen > l2_len) {
+ fprintf(stderr, "ABIS OML truncated message (%u > %u)\n",
+ oh->length + sizeof(*oh), l2_len);
+ return -EINVAL;
+ }
+ if (oh->length + hlen < l2_len)
+ fprintf(stderr, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len);
+#endif
+ msg->l3h = (unsigned char *)oh + sizeof(*oh);
+
+ switch (oh->mdisc) {
+ case ABIS_OM_MDISC_FOM:
+ rc = abis_nm_rcvmsg_fom(msg);
+ break;
+ case ABIS_OM_MDISC_MANUF:
+ rc = abis_nm_rcvmsg_manuf(msg);
+ break;
+ case ABIS_OM_MDISC_MMI:
+ case ABIS_OM_MDISC_TRAU:
+ fprintf(stderr, "unimplemented ABIS OML message discriminator 0x%x\n",
+ oh->mdisc);
+ break;
+ default:
+ fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n",
+ oh->mdisc);
+ return -EINVAL;
+ }
+
+ msgb_free(msg);
+ return rc;
+}
+
+#if 0
+/* initialized all resources */
+struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg)
+{
+ struct abis_nm_h *nmh;
+
+ nmh = malloc(sizeof(*nmh));
+ if (!nmh)
+ return NULL;
+
+ nmh->cfg = cfg;
+
+ return nmh;
+}
+
+/* free all resources */
+void abis_nm_fini(struct abis_nm_h *nmh)
+{
+ free(nmh);
+}
+#endif
+
+/* Here we are trying to define a high-level API that can be used by
+ * the actual BSC implementation. However, the architecture is currently
+ * still under design. Ideally the calls to this API would be synchronous,
+ * while the underlying stack behind the APi runs in a traditional select
+ * based state machine.
+ */
+
+/* 6.2 Software Load: */
+enum sw_state {
+ SW_STATE_NONE,
+ SW_STATE_WAIT_INITACK,
+ SW_STATE_WAIT_SEGACK,
+ SW_STATE_WAIT_ENDACK,
+ SW_STATE_WAIT_ACTACK,
+ SW_STATE_ERROR,
+};
+
+struct abis_nm_sw {
+ struct gsm_bts *bts;
+ gsm_cbfn *cbfn;
+ void *cb_data;
+ int forced;
+
+ /* this will become part of the SW LOAD INITIATE */
+ u_int8_t obj_class;
+ u_int8_t obj_instance[3];
+
+ u_int8_t file_id[255];
+ u_int8_t file_id_len;
+
+ u_int8_t file_version[255];
+ u_int8_t file_version_len;
+
+ u_int8_t window_size;
+ u_int8_t seg_in_window;
+
+ int fd;
+ FILE *stream;
+ enum sw_state state;
+ int last_seg;
+};
+
+static struct abis_nm_sw g_sw;
+
+/* 6.2.1 / 8.3.1: Load Data Initiate */
+static int sw_load_init(struct abis_nm_sw *sw)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 3*2 + sw->file_id_len + sw->file_version_len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ /* FIXME: this is BS11 specific format */
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+ msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
+
+static int is_last_line(FILE *stream)
+{
+ char next_seg_buf[256];
+ long pos;
+
+ /* check if we're sending the last line */
+ pos = ftell(stream);
+ if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) {
+ fseek(stream, pos, SEEK_SET);
+ return 1;
+ }
+
+ fseek(stream, pos, SEEK_SET);
+ return 0;
+}
+
+/* 6.2.2 / 8.3.2 Load Data Segment */
+static int sw_load_segment(struct abis_nm_sw *sw)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ char seg_buf[256];
+ char *line_buf = seg_buf+2;
+ unsigned char *tlv;
+ u_int8_t len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+
+ switch (sw->bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) {
+ perror("fgets reading segment");
+ return -EINVAL;
+ }
+ seg_buf[0] = 0x00;
+
+ /* check if we're sending the last line */
+ sw->last_seg = is_last_line(sw->stream);
+ if (sw->last_seg)
+ seg_buf[1] = 0;
+ else
+ seg_buf[1] = 1 + sw->seg_in_window++;
+
+ len = strlen(line_buf) + 2;
+ tlv = msgb_put(msg, TLV_GROSS_LEN(len));
+ tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (u_int8_t *)seg_buf);
+ /* BS11 wants CR + LF in excess of the TLV length !?! */
+ tlv[1] -= 2;
+
+ /* we only now know the exact length for the OM hdr */
+ len = strlen(line_buf)+2;
+ break;
+ default:
+ /* FIXME: Other BTS types */
+ return -1;
+ }
+
+ fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
+
+/* 6.2.4 / 8.3.4 Load Data End */
+static int sw_load_end(struct abis_nm_sw *sw)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ /* FIXME: this is BS11 specific format */
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
+
+/* Activate the specified software into the BTS */
+static int sw_activate(struct abis_nm_sw *sw)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 2*2 + sw->file_id_len + sw->file_version_len;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class,
+ sw->obj_instance[0], sw->obj_instance[1],
+ sw->obj_instance[2]);
+
+ /* FIXME: this is BS11 specific format */
+ msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id);
+ msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len,
+ sw->file_version);
+
+ return abis_nm_sendmsg(sw->bts, msg);
+}
+
+static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
+{
+ char file_id[12+1];
+ char file_version[80+1];
+ int rc;
+
+ sw->fd = open(fname, O_RDONLY);
+ if (sw->fd < 0)
+ return sw->fd;
+
+ switch (sw->bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ sw->stream = fdopen(sw->fd, "r");
+ if (!sw->stream) {
+ perror("fdopen");
+ return -1;
+ }
+ /* read first line and parse file ID and VERSION */
+ rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n",
+ file_id, file_version);
+ if (rc != 2) {
+ perror("parsing header line of software file");
+ return -1;
+ }
+ strcpy((char *)sw->file_id, file_id);
+ sw->file_id_len = strlen(file_id);
+ strcpy((char *)sw->file_version, file_version);
+ sw->file_version_len = strlen(file_version);
+ /* rewind to start of file */
+ rewind(sw->stream);
+ break;
+ default:
+ /* We don't know how to treat them yet */
+ close(sw->fd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void sw_close_file(struct abis_nm_sw *sw)
+{
+ switch (sw->bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ fclose(sw->stream);
+ break;
+ default:
+ close(sw->fd);
+ break;
+ }
+}
+
+/* Fill the window */
+static int sw_fill_window(struct abis_nm_sw *sw)
+{
+ int rc;
+
+ while (sw->seg_in_window < sw->window_size) {
+ rc = sw_load_segment(sw);
+ if (rc < 0)
+ return rc;
+ if (sw->last_seg)
+ break;
+ }
+ return 0;
+}
+
+/* callback function from abis_nm_rcvmsg() handler */
+static int abis_nm_rcvmsg_sw(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ int rc = -1;
+ struct abis_nm_sw *sw = &g_sw;
+ enum sw_state old_state = sw->state;
+
+ //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type);
+
+ switch (sw->state) {
+ case SW_STATE_WAIT_INITACK:
+ switch (foh->msg_type) {
+ case NM_MT_LOAD_INIT_ACK:
+ /* fill window with segments */
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_INIT_ACK, mb,
+ sw->cb_data, NULL);
+ rc = sw_fill_window(sw);
+ sw->state = SW_STATE_WAIT_SEGACK;
+ break;
+ case NM_MT_LOAD_INIT_NACK:
+ if (sw->forced) {
+ DEBUGP(DNM, "FORCED: Ignoring Software Load "
+ "Init NACK\n");
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_INIT_ACK, mb,
+ sw->cb_data, NULL);
+ rc = sw_fill_window(sw);
+ sw->state = SW_STATE_WAIT_SEGACK;
+ } else {
+ DEBUGP(DNM, "Software Load Init NACK\n");
+ /* FIXME: cause */
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_INIT_NACK, mb,
+ sw->cb_data, NULL);
+ sw->state = SW_STATE_ERROR;
+ }
+ break;
+ }
+ break;
+ case SW_STATE_WAIT_SEGACK:
+ switch (foh->msg_type) {
+ case NM_MT_LOAD_SEG_ACK:
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_SEG_ACK, mb,
+ sw->cb_data, NULL);
+ sw->seg_in_window = 0;
+ if (!sw->last_seg) {
+ /* fill window with more segments */
+ rc = sw_fill_window(sw);
+ sw->state = SW_STATE_WAIT_SEGACK;
+ } else {
+ /* end the transfer */
+ sw->state = SW_STATE_WAIT_ENDACK;
+ rc = sw_load_end(sw);
+ }
+ break;
+ }
+ break;
+ case SW_STATE_WAIT_ENDACK:
+ switch (foh->msg_type) {
+ case NM_MT_LOAD_END_ACK:
+ sw_close_file(sw);
+ DEBUGP(DNM, "Software Load End (BTS %u)\n",
+ sw->bts->nr);
+ sw->state = SW_STATE_NONE;
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_END_ACK, mb,
+ sw->cb_data, NULL);
+ break;
+ case NM_MT_LOAD_END_NACK:
+ if (sw->forced) {
+ DEBUGP(DNM, "FORCED: Ignoring Software Load"
+ "End NACK\n");
+ sw->state = SW_STATE_NONE;
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_END_ACK, mb,
+ sw->cb_data, NULL);
+ } else {
+ DEBUGP(DNM, "Software Load End NACK\n");
+ /* FIXME: cause */
+ sw->state = SW_STATE_ERROR;
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_LOAD_END_NACK, mb,
+ sw->cb_data, NULL);
+ }
+ break;
+ }
+ case SW_STATE_WAIT_ACTACK:
+ switch (foh->msg_type) {
+ case NM_MT_ACTIVATE_SW_ACK:
+ /* we're done */
+ DEBUGP(DNM, "Activate Software DONE!\n");
+ sw->state = SW_STATE_NONE;
+ rc = 0;
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_ACTIVATE_SW_ACK, mb,
+ sw->cb_data, NULL);
+ break;
+ case NM_MT_ACTIVATE_SW_NACK:
+ DEBUGP(DNM, "Activate Software NACK\n");
+ /* FIXME: cause */
+ sw->state = SW_STATE_ERROR;
+ if (sw->cbfn)
+ sw->cbfn(GSM_HOOK_NM_SWLOAD,
+ NM_MT_ACTIVATE_SW_NACK, mb,
+ sw->cb_data, NULL);
+ break;
+ }
+ case SW_STATE_NONE:
+ switch (foh->msg_type) {
+ case NM_MT_ACTIVATE_SW_ACK:
+ rc = 0;
+ break;
+ }
+ break;
+ case SW_STATE_ERROR:
+ break;
+ }
+
+ if (rc)
+ DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n",
+ foh->msg_type, old_state, sw->state);
+
+ return rc;
+}
+
+/* Load the specified software into the BTS */
+int abis_nm_software_load(struct gsm_bts *bts, const char *fname,
+ u_int8_t win_size, int forced,
+ gsm_cbfn *cbfn, void *cb_data)
+{
+ struct abis_nm_sw *sw = &g_sw;
+ int rc;
+
+ DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n",
+ bts->nr, fname);
+
+ if (sw->state != SW_STATE_NONE)
+ return -EBUSY;
+
+ sw->bts = bts;
+ sw->obj_class = NM_OC_SITE_MANAGER;
+ sw->obj_instance[0] = 0xff;
+ sw->obj_instance[1] = 0xff;
+ sw->obj_instance[2] = 0xff;
+ sw->window_size = win_size;
+ sw->state = SW_STATE_WAIT_INITACK;
+ sw->cbfn = cbfn;
+ sw->cb_data = cb_data;
+ sw->forced = forced;
+
+ rc = sw_open_file(sw, fname);
+ if (rc < 0) {
+ sw->state = SW_STATE_NONE;
+ return rc;
+ }
+
+ return sw_load_init(sw);
+}
+
+int abis_nm_software_load_status(struct gsm_bts *bts)
+{
+ struct abis_nm_sw *sw = &g_sw;
+ struct stat st;
+ int rc, percent;
+
+ rc = fstat(sw->fd, &st);
+ if (rc < 0) {
+ perror("ERROR during stat");
+ return rc;
+ }
+
+ percent = (ftell(sw->stream) * 100) / st.st_size;
+ return percent;
+}
+
+/* Activate the specified software into the BTS */
+int abis_nm_software_activate(struct gsm_bts *bts, const char *fname,
+ gsm_cbfn *cbfn, void *cb_data)
+{
+ struct abis_nm_sw *sw = &g_sw;
+ int rc;
+
+ DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n",
+ bts->nr, fname);
+
+ if (sw->state != SW_STATE_NONE)
+ return -EBUSY;
+
+ sw->bts = bts;
+ sw->obj_class = NM_OC_SITE_MANAGER;
+ sw->obj_instance[0] = 0xff;
+ sw->obj_instance[1] = 0xff;
+ sw->obj_instance[2] = 0xff;
+ sw->state = SW_STATE_WAIT_ACTACK;
+ sw->cbfn = cbfn;
+ sw->cb_data = cb_data;
+
+ /* Open the file in order to fill some sw struct members */
+ rc = sw_open_file(sw, fname);
+ if (rc < 0) {
+ sw->state = SW_STATE_NONE;
+ return rc;
+ }
+ sw_close_file(sw);
+
+ return sw_activate(sw);
+}
+
+static void fill_nm_channel(struct abis_nm_channel *ch, u_int8_t bts_port,
+ u_int8_t ts_nr, u_int8_t subslot_nr)
+{
+ ch->attrib = NM_ATT_ABIS_CHANNEL;
+ ch->bts_port = bts_port;
+ ch->timeslot = ts_nr;
+ ch->subslot = subslot_nr;
+}
+
+int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr,
+ u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot,
+ u_int8_t tei)
+{
+ struct abis_om_hdr *oh;
+ struct abis_nm_channel *ch;
+ u_int8_t len = sizeof(*ch) + 2;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER,
+ bts->bts_nr, trx_nr, 0xff);
+
+ msgb_tv_put(msg, NM_ATT_TEI, tei);
+
+ ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+ fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */
+int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx,
+ u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot)
+{
+ struct gsm_bts *bts = trx->bts;
+ struct abis_om_hdr *oh;
+ struct abis_nm_channel *ch;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN,
+ NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff);
+
+ ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+ fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+#if 0
+int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
+ struct abis_nm_abis_channel *chan)
+{
+}
+#endif
+
+int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
+ u_int8_t e1_port, u_int8_t e1_timeslot,
+ u_int8_t e1_subslot)
+{
+ struct gsm_bts *bts = ts->trx->bts;
+ struct abis_om_hdr *oh;
+ struct abis_nm_channel *ch;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF,
+ NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr);
+
+ ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+ fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+
+ DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n",
+ gsm_ts_name(ts),
+ e1_port, e1_timeslot, e1_subslot);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+#if 0
+int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
+ struct abis_nm_abis_channel *chan,
+ u_int8_t subchan)
+{
+}
+#endif
+
+/* Chapter 8.6.1 */
+int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t *cur;
+
+ DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr);
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff);
+ cur = msgb_put(msg, attr_len);
+ memcpy(cur, attr, attr_len);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* Chapter 8.6.2 */
+int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, u_int8_t *attr, int attr_len)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t *cur;
+
+ DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr);
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff);
+ cur = msgb_put(msg, attr_len);
+ memcpy(cur, attr, attr_len);
+
+ return abis_nm_sendmsg(trx->bts, msg);
+}
+
+/* Chapter 8.6.3 */
+int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
+{
+ struct gsm_bts *bts = ts->trx->bts;
+ struct abis_om_hdr *oh;
+ u_int16_t arfcn = htons(ts->trx->arfcn);
+ u_int8_t zero = 0x00;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t len = 2 + 2;
+
+ if (bts->type == GSM_BTS_TYPE_BS11)
+ len += 4 + 2 + 2 + 3;
+
+ DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts));
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR,
+ NM_OC_CHANNEL, bts->bts_nr,
+ ts->trx->nr, ts->nr);
+ /* FIXME: don't send ARFCN list, hopping sequence, mAIO, ...*/
+ if (bts->type == GSM_BTS_TYPE_BS11)
+ msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn);
+ msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb);
+ if (bts->type == GSM_BTS_TYPE_BS11) {
+ msgb_tv_put(msg, NM_ATT_HSN, 0x00);
+ msgb_tv_put(msg, NM_ATT_MAIO, 0x00);
+ }
+ msgb_tv_put(msg, NM_ATT_TSC, 0x07); /* training sequence */
+ if (bts->type == GSM_BTS_TYPE_BS11)
+ msgb_tlv_put(msg, 0x59, 1, &zero);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_sw_act_req_ack(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i1,
+ u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t msgtype = NM_MT_SW_ACT_REQ_ACK;
+ u_int8_t len = att_len;
+
+ if (nack) {
+ len += 2;
+ msgtype = NM_MT_SW_ACT_REQ_NACK;
+ }
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3);
+
+ if (attr) {
+ u_int8_t *ptr = msgb_put(msg, att_len);
+ memcpy(ptr, attr, att_len);
+ }
+ if (nack)
+ msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *rawmsg)
+{
+ struct msgb *msg = nm_msgb_alloc();
+ struct abis_om_hdr *oh;
+ u_int8_t *data;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh));
+ fill_om_hdr(oh, len);
+ data = msgb_put(msg, len);
+ memcpy(data, rawmsg, len);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* Siemens specific commands */
+static int __simple_cmd(struct gsm_bts *bts, u_int8_t msg_type)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER,
+ 0xff, 0xff, 0xff);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* Chapter 8.9.2 */
+int abis_nm_opstart(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ DEBUGP(DNM, "Sending OPSTART obj_class=0x%02x obj_inst=(0x%02x, 0x%02x, 0x%02x)\n",
+ obj_class, i0, i1, i2);
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* Chapter 8.8.5 */
+int abis_nm_chg_adm_state(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i0,
+ u_int8_t i1, u_int8_t i2, u_int8_t adm_state)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2);
+ msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+
+int abis_nm_event_reports(struct gsm_bts *bts, int on)
+{
+ if (on == 0)
+ return __simple_cmd(bts, NM_MT_STOP_EVENT_REP);
+ else
+ return __simple_cmd(bts, NM_MT_REST_EVENT_REP);
+}
+
+/* Siemens (or BS-11) specific commands */
+
+int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect)
+{
+ if (reconnect == 0)
+ return __simple_cmd(bts, NM_MT_BS11_DISCONNECT);
+ else
+ return __simple_cmd(bts, NM_MT_BS11_RECONNECT);
+}
+
+int abis_nm_bs11_restart(struct gsm_bts *bts)
+{
+ return __simple_cmd(bts, NM_MT_BS11_RESTART);
+}
+
+
+struct bs11_date_time {
+ u_int16_t year;
+ u_int8_t month;
+ u_int8_t day;
+ u_int8_t hour;
+ u_int8_t min;
+ u_int8_t sec;
+} __attribute__((packed));
+
+
+void get_bs11_date_time(struct bs11_date_time *aet)
+{
+ time_t t;
+ struct tm *tm;
+
+ t = time(NULL);
+ tm = localtime(&t);
+ aet->sec = tm->tm_sec;
+ aet->min = tm->tm_min;
+ aet->hour = tm->tm_hour;
+ aet->day = tm->tm_mday;
+ aet->month = tm->tm_mon;
+ aet->year = htons(1900 + tm->tm_year);
+}
+
+int abis_nm_bs11_reset_resource(struct gsm_bts *bts)
+{
+ return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE);
+}
+
+int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin)
+{
+ if (begin)
+ return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX);
+ else
+ return __simple_cmd(bts, NM_MT_BS11_END_DB_TX);
+}
+
+int abis_nm_bs11_create_object(struct gsm_bts *bts,
+ enum abis_bs11_objtype type, u_int8_t idx,
+ u_int8_t attr_len, const u_int8_t *attr)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t *cur;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ,
+ NM_OC_BS11, type, 0, idx);
+ cur = msgb_put(msg, attr_len);
+ memcpy(cur, attr, attr_len);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_delete_object(struct gsm_bts *bts,
+ enum abis_bs11_objtype type, u_int8_t idx)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ,
+ NM_OC_BS11, type, 0, idx);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, u_int8_t idx)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t zero = 0x00;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ,
+ NM_OC_BS11_ENVABTSE, 0, idx, 0xff);
+ msgb_tlv_put(msg, 0x99, 1, &zero);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_create_bport(struct gsm_bts *bts, u_int8_t idx)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT,
+ idx, 0, 0);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+static const u_int8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL };
+int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER,
+ 0xff, 0xff, 0xff);
+ msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* like abis_nm_conn_terr_traf + set_tei */
+int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port,
+ u_int8_t e1_timeslot, u_int8_t e1_subslot,
+ u_int8_t tei)
+{
+ struct abis_om_hdr *oh;
+ struct abis_nm_channel *ch;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR,
+ NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
+
+ ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+ fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+ msgb_tv_put(msg, NM_ATT_TEI, tei);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, u_int8_t level)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR,
+ NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr);
+ msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level);
+
+ return abis_nm_sendmsg(trx->bts, msg);
+}
+
+int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t attr = NM_ATT_BS11_TXPWR;
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
+ NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr);
+ msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr);
+
+ return abis_nm_sendmsg(trx->bts, msg);
+}
+
+int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t attr[] = { NM_ATT_BS11_PLL_MODE };
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
+ NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00);
+ msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_get_cclk(struct gsm_bts *bts)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ u_int8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY,
+ NM_ATT_BS11_CCLK_TYPE };
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR,
+ NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00);
+ msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr);
+
+ return abis_nm_sendmsg(bts, msg);
+
+}
+
+//static const u_int8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 };
+static const u_int8_t bs11_logon_c8[] = { 0x02 };
+static const u_int8_t bs11_logon_c9[] = "FACTORY";
+
+int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ struct bs11_date_time bdt;
+
+ get_bs11_date_time(&bdt);
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ if (on) {
+ u_int8_t len = 3*2 + sizeof(bdt)
+ + sizeof(bs11_logon_c8) + sizeof(bs11_logon_c9);
+ fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON,
+ NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
+ msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME,
+ sizeof(bdt), (u_int8_t *) &bdt);
+ msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV,
+ sizeof(bs11_logon_c8), bs11_logon_c8);
+ msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME,
+ sizeof(bs11_logon_c9), bs11_logon_c9);
+ } else {
+ fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF,
+ NM_OC_BS11_BTSE, 0xff, 0xff, 0xff);
+ }
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg;
+
+ if (strlen(password) != 10)
+ return -EINVAL;
+
+ msg = nm_msgb_alloc();
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR,
+ NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00);
+ msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const u_int8_t *)password);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */
+int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg;
+ u_int8_t tlv_value;
+
+ msg = nm_msgb_alloc();
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11,
+ BS11_OBJ_LI, 0x00, 0x00);
+
+ if (locked)
+ tlv_value = BS11_LI_PLL_LOCKED;
+ else
+ tlv_value = BS11_LI_PLL_STANDALONE;
+
+ msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_get_state(struct gsm_bts *bts)
+{
+ return __simple_cmd(bts, NM_MT_BS11_GET_STATE);
+}
+
+/* BS11 SWL */
+
+struct abis_nm_bs11_sw {
+ struct gsm_bts *bts;
+ char swl_fname[PATH_MAX];
+ u_int8_t win_size;
+ int forced;
+ struct llist_head file_list;
+ gsm_cbfn *user_cb; /* specified by the user */
+};
+static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw;
+
+struct file_list_entry {
+ struct llist_head list;
+ char fname[PATH_MAX];
+};
+
+struct file_list_entry *fl_dequeue(struct llist_head *queue)
+{
+ struct llist_head *lh;
+
+ if (llist_empty(queue))
+ return NULL;
+
+ lh = queue->next;
+ llist_del(lh);
+
+ return llist_entry(lh, struct file_list_entry, list);
+}
+
+static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw)
+{
+ char linebuf[255];
+ struct llist_head *lh, *lh2;
+ FILE *swl;
+ int rc = 0;
+
+ swl = fopen(bs11_sw->swl_fname, "r");
+ if (!swl)
+ return -ENODEV;
+
+ /* zero the stale file list, if any */
+ llist_for_each_safe(lh, lh2, &bs11_sw->file_list) {
+ llist_del(lh);
+ free(lh);
+ }
+
+ while (fgets(linebuf, sizeof(linebuf), swl)) {
+ char file_id[12+1];
+ char file_version[80+1];
+ struct file_list_entry *fle;
+ static char dir[PATH_MAX];
+
+ if (strlen(linebuf) < 4)
+ continue;
+
+ rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version);
+ if (rc < 0) {
+ perror("ERR parsing SWL file");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (rc < 2)
+ continue;
+
+ fle = malloc(sizeof(*fle));
+ if (!fle) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memset(fle, 0, sizeof(*fle));
+
+ /* construct new filename */
+ strncpy(dir, bs11_sw->swl_fname, sizeof(dir));
+ strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1);
+ strcat(fle->fname, "/");
+ strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname));
+
+ llist_add_tail(&fle->list, &bs11_sw->file_list);
+ }
+
+out:
+ fclose(swl);
+ return rc;
+}
+
+/* bs11 swload specific callback, passed to abis_nm core swload */
+static int bs11_swload_cbfn(unsigned int hook, unsigned int event,
+ struct msgb *msg, void *data, void *param)
+{
+ struct abis_nm_bs11_sw *bs11_sw = data;
+ struct file_list_entry *fle;
+ int rc = 0;
+
+ switch (event) {
+ case NM_MT_LOAD_END_ACK:
+ fle = fl_dequeue(&bs11_sw->file_list);
+ if (fle) {
+ /* start download the next file of our file list */
+ rc = abis_nm_software_load(bs11_sw->bts, fle->fname,
+ bs11_sw->win_size,
+ bs11_sw->forced,
+ &bs11_swload_cbfn, bs11_sw);
+ free(fle);
+ } else {
+ /* activate the SWL */
+ rc = abis_nm_software_activate(bs11_sw->bts,
+ bs11_sw->swl_fname,
+ bs11_swload_cbfn,
+ bs11_sw);
+ }
+ break;
+ case NM_MT_LOAD_SEG_ACK:
+ case NM_MT_LOAD_END_NACK:
+ case NM_MT_LOAD_INIT_ACK:
+ case NM_MT_LOAD_INIT_NACK:
+ case NM_MT_ACTIVATE_SW_NACK:
+ case NM_MT_ACTIVATE_SW_ACK:
+ default:
+ /* fallthrough to the user callback */
+ if (bs11_sw->user_cb)
+ rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL);
+ break;
+ }
+
+ return rc;
+}
+
+/* Siemens provides a SWL file that is a mere listing of all the other
+ * files that are part of a software release. We need to upload first
+ * the list file, and then each file that is listed in the list file */
+int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname,
+ u_int8_t win_size, int forced, gsm_cbfn *cbfn)
+{
+ struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw;
+ struct file_list_entry *fle;
+ int rc = 0;
+
+ INIT_LLIST_HEAD(&bs11_sw->file_list);
+ bs11_sw->bts = bts;
+ bs11_sw->win_size = win_size;
+ bs11_sw->user_cb = cbfn;
+ bs11_sw->forced = forced;
+
+ strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname));
+ rc = bs11_read_swl_file(bs11_sw);
+ if (rc < 0)
+ return rc;
+
+ /* dequeue next item in file list */
+ fle = fl_dequeue(&bs11_sw->file_list);
+ if (!fle)
+ return -EINVAL;
+
+ /* start download the next file of our file list */
+ rc = abis_nm_software_load(bts, fle->fname, win_size, forced,
+ bs11_swload_cbfn, bs11_sw);
+ free(fle);
+ return rc;
+}
+
+#if 0
+static u_int8_t req_attr_btse[] = {
+ NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION,
+ NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV,
+ NM_ATT_BS11_LMT_USER_NAME,
+
+ 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME,
+
+ NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY,
+
+ NM_ATT_BS11_SW_LOAD_STORED };
+
+static u_int8_t req_attr_btsm[] = {
+ NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME,
+ NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID,
+ NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG,
+ NM_ATT_SW_DESCR, NM_ATT_GET_ARI };
+#endif
+
+static u_int8_t req_attr[] = {
+ NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE,
+ 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO,
+ 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL };
+
+int abis_nm_bs11_get_serno(struct gsm_bts *bts)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ /* SiemensHW CCTRL object */
+ fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11,
+ 0x03, 0x00, 0x00);
+ msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_bs11_set_ext_time(struct gsm_bts *bts)
+{
+ struct abis_om_hdr *oh;
+ struct msgb *msg = nm_msgb_alloc();
+ struct bs11_date_time aet;
+
+ get_bs11_date_time(&aet);
+ oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+ /* SiemensHW CCTRL object */
+ fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER,
+ 0xff, 0xff, 0xff);
+ msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (u_int8_t *) &aet);
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* ip.access nanoBTS specific commands */
+static const char ipaccess_magic[] = "com.ipaccess";
+
+
+static int abis_nm_rx_ipacc(struct msgb *msg)
+{
+ struct abis_om_hdr *oh = msgb_l2(msg);
+ struct abis_om_fom_hdr *foh;
+ u_int8_t idstrlen = oh->data[0];
+ struct tlv_parsed tp;
+
+ if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) {
+ DEBUGP(DNM, "id string is not com.ipaccess !?!\n");
+ return -EINVAL;
+ }
+
+ foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen);
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+
+ DEBUGP(DNM, "IPACCESS(0x%02x): ", foh->msg_type);
+
+ switch (foh->msg_type) {
+ case NM_MT_IPACC_RSL_CONNECT_ACK:
+ DEBUGPC(DNM, "RSL CONNECT ACK ");
+ if (TLVP_PRESENT(&tp, NM_ATT_IPACC_RSL_BSC_IP))
+ DEBUGPC(DNM, "IP=%s ",
+ inet_ntoa(*((struct in_addr *)
+ TLVP_VAL(&tp, NM_ATT_IPACC_RSL_BSC_IP))));
+ if (TLVP_PRESENT(&tp, NM_ATT_IPACC_RSL_BSC_PORT))
+ DEBUGPC(DNM, "PORT=%u ",
+ ntohs(*((u_int16_t *)
+ TLVP_VAL(&tp, NM_ATT_IPACC_RSL_BSC_PORT))));
+ DEBUGPC(DNM, "\n");
+ break;
+ case NM_MT_IPACC_RSL_CONNECT_NACK:
+ DEBUGPC(DNM, "RSL CONNECT NACK ");
+ if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
+ DEBUGPC(DNM, " CAUSE=%s\n",
+ nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
+ else
+ DEBUGPC(DNM, "\n");
+ break;
+ case NM_MT_IPACC_SET_NVATTR_ACK:
+ DEBUGPC(DNM, "SET NVATTR ACK\n");
+ /* FIXME: decode and show the actual attributes */
+ break;
+ case NM_MT_IPACC_SET_NVATTR_NACK:
+ DEBUGPC(DNM, "SET NVATTR NACK ");
+ if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
+ DEBUGPC(DNM, " CAUSE=%s\n",
+ nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
+ else
+ DEBUGPC(DNM, "\n");
+ break;
+ default:
+ DEBUGPC(DNM, "unknown\n");
+ break;
+ }
+ return 0;
+}
+
+/* send an ip-access manufacturer specific message */
+int abis_nm_ipaccess_msg(struct gsm_bts *bts, u_int8_t msg_type,
+ u_int8_t obj_class, u_int8_t bts_nr,
+ u_int8_t trx_nr, u_int8_t ts_nr,
+ u_int8_t *attr, int attr_len)
+{
+ struct msgb *msg = nm_msgb_alloc();
+ struct abis_om_hdr *oh;
+ struct abis_om_fom_hdr *foh;
+ u_int8_t *data;
+
+ /* construct the 12.21 OM header, observe the erroneous length */
+ oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh));
+ fill_om_hdr(oh, sizeof(*foh) + attr_len);
+ oh->mdisc = ABIS_OM_MDISC_MANUF;
+
+ /* add the ip.access magic */
+ data = msgb_put(msg, sizeof(ipaccess_magic)+1);
+ *data++ = sizeof(ipaccess_magic);
+ memcpy(data, ipaccess_magic, sizeof(ipaccess_magic));
+
+ /* fill the 12.21 FOM header */
+ foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh));
+ foh->msg_type = msg_type;
+ foh->obj_class = obj_class;
+ foh->obj_inst.bts_nr = bts_nr;
+ foh->obj_inst.trx_nr = trx_nr;
+ foh->obj_inst.ts_nr = ts_nr;
+
+ if (attr && attr_len) {
+ data = msgb_put(msg, attr_len);
+ memcpy(data, attr, attr_len);
+ }
+
+ return abis_nm_sendmsg(bts, msg);
+}
+
+/* set some attributes in NVRAM */
+int abis_nm_ipaccess_set_nvattr(struct gsm_bts *bts, u_int8_t *attr,
+ int attr_len)
+{
+ return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_NVATTR,
+ NM_OC_BASEB_TRANSC, 0, 0, 0xff, attr,
+ attr_len);
+}
+
+/* restart / reboot an ip.access nanoBTS */
+int abis_nm_ipaccess_restart(struct gsm_bts *bts)
+{
+ return __simple_cmd(bts, NM_MT_IPACC_RESTART);
+}
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
new file mode 100644
index 000000000..4b2a7fccf
--- /dev/null
+++ b/openbsc/src/abis_rsl.c
@@ -0,0 +1,1263 @@
+/* GSM Radio Signalling Link messages on the A-bis interface
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/debug.h>
+#include <openbsc/tlv.h>
+#include <openbsc/paging.h>
+#include <openbsc/signal.h>
+
+#define RSL_ALLOC_SIZE 1024
+#define RSL_ALLOC_HEADROOM 128
+
+#define MAX(a, b) (a) >= (b) ? (a) : (b)
+
+static const struct tlv_definition rsl_att_tlvdef = {
+ .def = {
+ [RSL_IE_CHAN_NR] = { TLV_TYPE_TV },
+ [RSL_IE_LINK_IDENT] = { TLV_TYPE_TV },
+ [RSL_IE_ACT_TYPE] = { TLV_TYPE_TV },
+ [RSL_IE_BS_POWER] = { TLV_TYPE_TV },
+ [RSL_IE_CHAN_IDENT] = { TLV_TYPE_TLV },
+ [RSL_IE_CHAN_MODE] = { TLV_TYPE_TLV },
+ [RSL_IE_ENCR_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_FRAME_NUMBER] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_HANDO_REF] = { TLV_TYPE_TV },
+ [RSL_IE_L1_INFO] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_L3_INFO] = { TLV_TYPE_TL16V },
+ [RSL_IE_MS_IDENTITY] = { TLV_TYPE_TLV },
+ [RSL_IE_MS_POWER] = { TLV_TYPE_TV },
+ [RSL_IE_PAGING_GROUP] = { TLV_TYPE_TV },
+ [RSL_IE_PAGING_LOAD] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_PYHS_CONTEXT] = { TLV_TYPE_TLV },
+ [RSL_IE_ACCESS_DELAY] = { TLV_TYPE_TV },
+ [RSL_IE_RACH_LOAD] = { TLV_TYPE_TLV },
+ [RSL_IE_REQ_REFERENCE] = { TLV_TYPE_FIXED, 3 },
+ [RSL_IE_RELEASE_MODE] = { TLV_TYPE_TV },
+ [RSL_IE_RESOURCE_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_RLM_CAUSE] = { TLV_TYPE_TLV },
+ [RSL_IE_STARTNG_TIME] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
+ [RSL_IE_UPLINK_MEAS] = { TLV_TYPE_TLV },
+ [RSL_IE_CAUSE] = { TLV_TYPE_TLV },
+ [RSL_IE_MEAS_RES_NR] = { TLV_TYPE_TV },
+ [RSL_IE_MSG_ID] = { TLV_TYPE_TV },
+ [RSL_IE_SYSINFO_TYPE] = { TLV_TYPE_TV },
+ [RSL_IE_MS_POWER_PARAM] = { TLV_TYPE_TLV },
+ [RSL_IE_BS_POWER_PARAM] = { TLV_TYPE_TLV },
+ [RSL_IE_PREPROC_PARAM] = { TLV_TYPE_TLV },
+ [RSL_IE_PREPROC_MEAS] = { TLV_TYPE_TLV },
+ [RSL_IE_IMM_ASS_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_SMSCB_INFO] = { TLV_TYPE_FIXED, 23 },
+ [RSL_IE_MS_TIMING_OFFSET] = { TLV_TYPE_TV },
+ [RSL_IE_ERR_MSG] = { TLV_TYPE_TLV },
+ [RSL_IE_FULL_BCCH_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_CHAN_NEEDED] = { TLV_TYPE_TV },
+ [RSL_IE_CB_CMD_TYPE] = { TLV_TYPE_TV },
+ [RSL_IE_SMSCB_MSG] = { TLV_TYPE_TLV },
+ [RSL_IE_FULL_IMM_ASS_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_SACCH_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_CBCH_LOAD_INFO] = { TLV_TYPE_TV },
+ [RSL_IE_SMSCB_CHAN_INDICATOR] = { TLV_TYPE_TV },
+ [RSL_IE_GROUP_CALL_REF] = { TLV_TYPE_TLV },
+ [RSL_IE_CHAN_DESC] = { TLV_TYPE_TLV },
+ [RSL_IE_NCH_DRX_INFO] = { TLV_TYPE_TLV },
+ [RSL_IE_CMD_INDICATOR] = { TLV_TYPE_TLV },
+ [RSL_IE_EMLPP_PRIO] = { TLV_TYPE_TV },
+ [RSL_IE_UIC] = { TLV_TYPE_TLV },
+ [RSL_IE_MAIN_CHAN_REF] = { TLV_TYPE_TV },
+ [RSL_IE_MR_CONFIG] = { TLV_TYPE_TLV },
+ [RSL_IE_MR_CONTROL] = { TLV_TYPE_TV },
+ [RSL_IE_SUP_CODEC_TYPES] = { TLV_TYPE_TLV },
+ [RSL_IE_CODEC_CONFIG] = { TLV_TYPE_TLV },
+ [RSL_IE_RTD] = { TLV_TYPE_TV },
+ [RSL_IE_TFO_STATUS] = { TLV_TYPE_TV },
+ [RSL_IE_LLP_APDU] = { TLV_TYPE_TLV },
+ [RSL_IE_IPAC_REMOTE_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_REMOTE_PORT] = { TLV_TYPE_FIXED, 2 },
+ [RSL_IE_IPAC_LOCAL_IP] = { TLV_TYPE_FIXED, 4 },
+ [RSL_IE_IPAC_LOCAL_PORT] = { TLV_TYPE_FIXED, 2 },
+ [0xf4] = { TLV_TYPE_TV },
+ [0xf8] = { TLV_TYPE_FIXED, 2 },
+ [0xfc] = { TLV_TYPE_TV },
+ },
+};
+#define rsl_tlv_parse(dec, buf, len) \
+ tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0)
+
+static u_int8_t mdisc_by_msgtype(u_int8_t msg_type)
+{
+ /* mask off the transparent bit ? */
+ msg_type &= 0xfe;
+
+ if ((msg_type & 0xf0) == 0x00)
+ return ABIS_RSL_MDISC_RLL;
+ if ((msg_type & 0xf0) == 0x10) {
+ if (msg_type >= 0x19 && msg_type <= 0x22)
+ return ABIS_RSL_MDISC_TRX;
+ else
+ return ABIS_RSL_MDISC_COM_CHAN;
+ }
+ if ((msg_type & 0xe0) == 0x20)
+ return ABIS_RSL_MDISC_DED_CHAN;
+
+ return ABIS_RSL_MDISC_LOC;
+}
+
+static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh,
+ u_int8_t msg_type)
+{
+ dh->c.msg_discr = mdisc_by_msgtype(msg_type);
+ dh->c.msg_type = msg_type;
+ dh->ie_chan = RSL_IE_CHAN_NR;
+}
+
+static inline void init_llm_hdr(struct abis_rsl_rll_hdr *dh,
+ u_int8_t msg_type)
+{
+ /* dh->c.msg_discr = mdisc_by_msgtype(msg_type); */
+ dh->c.msg_discr = ABIS_RSL_MDISC_RLL;
+ dh->c.msg_type = msg_type;
+ dh->ie_chan = RSL_IE_CHAN_NR;
+ dh->ie_link_id = RSL_IE_LINK_IDENT;
+}
+
+
+/* encode channel number as per Section 9.3.1 */
+u_int8_t rsl_enc_chan_nr(u_int8_t type, u_int8_t subch, u_int8_t timeslot)
+{
+ u_int8_t ret;
+
+ ret = (timeslot & 0x07) | type;
+
+ switch (type) {
+ case RSL_CHAN_Lm_ACCHs:
+ subch &= 0x01;
+ break;
+ case RSL_CHAN_SDCCH4_ACCH:
+ subch &= 0x07;
+ break;
+ case RSL_CHAN_SDCCH8_ACCH:
+ subch &= 0x07;
+ break;
+ default:
+ /* no subchannels allowed */
+ subch = 0x00;
+ break;
+ }
+ ret |= (subch << 3);
+
+ return ret;
+}
+
+/* determine logical channel based on TRX and channel number IE */
+struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
+{
+ struct gsm_lchan *lchan;
+ u_int8_t ts_nr = chan_nr & 0x07;
+ u_int8_t cbits = chan_nr >> 3;
+ u_int8_t lch_idx;
+ struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr];
+
+ if (cbits == 0x01) {
+ lch_idx = 0; /* TCH/F */
+ if (ts->pchan != GSM_PCHAN_TCH_F)
+ fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ chan_nr, ts->pchan);
+ } else if ((cbits & 0x1e) == 0x02) {
+ lch_idx = cbits & 0x1; /* TCH/H */
+ if (ts->pchan != GSM_PCHAN_TCH_H)
+ fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ chan_nr, ts->pchan);
+ } else if ((cbits & 0x1c) == 0x04) {
+ lch_idx = cbits & 0x3; /* SDCCH/4 */
+ if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
+ fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ chan_nr, ts->pchan);
+ } else if ((cbits & 0x18) == 0x08) {
+ lch_idx = cbits & 0x7; /* SDCCH/8 */
+ if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C)
+ fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ chan_nr, ts->pchan);
+ } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
+ lch_idx = 0;
+ if (ts->pchan != GSM_PCHAN_CCCH &&
+ ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
+ fprintf(stderr, "chan_nr=0x%02x but pchan=%u\n",
+ chan_nr, ts->pchan);
+ /* FIXME: we should not return first sdcch4 !!! */
+ } else {
+ fprintf(stderr, "unknown chan_nr=0x%02x\n", chan_nr);
+ return NULL;
+ }
+
+ lchan = &ts->lchan[lch_idx];
+
+ return lchan;
+}
+
+u_int8_t lchan2chan_nr(struct gsm_lchan *lchan)
+{
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+ u_int8_t cbits, chan_nr;
+
+ switch (ts->pchan) {
+ case GSM_PCHAN_TCH_F:
+ cbits = 0x01;
+ break;
+ case GSM_PCHAN_TCH_H:
+ cbits = 0x02;
+ cbits += lchan->nr;
+ break;
+ case GSM_PCHAN_CCCH_SDCCH4:
+ cbits = 0x04;
+ cbits += lchan->nr;
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ cbits = 0x08;
+ cbits += lchan->nr;
+ break;
+ default:
+ case GSM_PCHAN_CCCH:
+ cbits = 0x10;
+ break;
+ }
+
+ chan_nr = (cbits << 3) | (ts->nr & 0x7);
+
+ return chan_nr;
+}
+
+/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */
+u_int64_t str_to_imsi(const char *imsi_str)
+{
+ u_int64_t ret;
+
+ ret = strtoull(imsi_str, NULL, 10);
+
+ return ret;
+}
+
+/* Table 5 Clause 7 TS 05.02 */
+unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res)
+{
+ if (!bs_ccch_sdcch_comb)
+ return 9 - bs_ag_blks_res;
+ else
+ return 3 - bs_ag_blks_res;
+}
+
+/* Chapter 6.5.2 of TS 05.02 */
+unsigned int get_ccch_group(u_int64_t imsi, unsigned int bs_cc_chans,
+ unsigned int n_pag_blocks)
+{
+ return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks;
+}
+
+/* Chapter 6.5.2 of TS 05.02 */
+unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans,
+ int n_pag_blocks)
+{
+ return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks;
+}
+
+static struct msgb *rsl_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM);
+}
+
+#define MACBLOCK_SIZE 23
+static void pad_macblock(u_int8_t *out, const u_int8_t *in, int len)
+{
+ memcpy(out, in, len);
+
+ if (len < MACBLOCK_SIZE)
+ memset(out+len, 0x2b, MACBLOCK_SIZE-len);
+}
+
+static void print_rsl_cause(u_int8_t *cause_tlv)
+{
+ u_int8_t cause_len;
+ int i;
+
+ if (cause_tlv[0] != RSL_IE_CAUSE)
+ return;
+
+ cause_len = cause_tlv[1];
+ DEBUGPC(DRSL, "CAUSE: ");
+ for (i = 0; i < cause_len; i++)
+ DEBUGPC(DRSL, "%02x ", cause_tlv[2+i]);
+}
+
+/* Send a BCCH_INFO message as per Chapter 8.5.1 */
+int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type,
+ const u_int8_t *data, int len)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh);
+ init_dchan_hdr(dh, RSL_MT_BCCH_INFO);
+ dh->chan_nr = RSL_CHAN_BCCH;
+
+ msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
+ msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
+
+ msg->trx = trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type,
+ const u_int8_t *data, int len)
+{
+ struct abis_rsl_common_hdr *ch;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
+ ch->msg_discr = ABIS_RSL_MDISC_TRX;
+ ch->msg_type = RSL_MT_SACCH_FILL;
+
+ msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
+ msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data);
+
+ msg->trx = trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Chapter 8.4.1 */
+#if 0
+int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
+ u_int8_t act_type,
+ struct rsl_ie_chan_mode *chan_mode,
+ struct rsl_ie_chan_ident *chan_ident,
+ u_int8_t bs_power, u_int8_t ms_power,
+ u_int8_t ta)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
+ dh->chan_nr = chan_nr;
+
+ msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
+ /* For compatibility with Phase 1 */
+ msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode),
+ (u_int8_t *) chan_mode);
+ msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
+ (u_int8_t *) chan_ident);
+#if 0
+ msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
+ (u_int8_t *) &encr_info);
+#endif
+ msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power);
+ msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power);
+ msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
+
+ msg->trx = trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+#endif
+
+int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
+ u_int8_t ta, u_int8_t mode)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ u_int8_t chan_nr = lchan2chan_nr(lchan);
+ u_int16_t arfcn = lchan->ts->trx->arfcn;
+ struct rsl_ie_chan_mode cm;
+ struct rsl_ie_chan_ident ci;
+
+ memset(&cm, 0, sizeof(cm));
+ /* FIXME: what to do with data calls ? */
+ cm.dtx_dtu = 0x00;
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ cm.spd_ind = RSL_CMOD_SPD_SIGN;
+ cm.chan_rt = RSL_CMOD_CRT_SDCCH;
+ cm.chan_rate = 0x00;
+ break;
+ case GSM_LCHAN_TCH_F:
+ cm.chan_rt = RSL_CMOD_CRT_TCH_Bm;
+ switch (mode) {
+ case RSL_CMOD_SPD_SIGN:
+ cm.spd_ind = RSL_CMOD_SPD_SIGN;
+ cm.chan_rate = 0x00;
+ break;
+ case RSL_CMOD_SPD_SPEECH:
+ cm.spd_ind = RSL_CMOD_SPD_SPEECH;
+ cm.chan_rate = RSL_CMOD_SP_GSM2;
+ break;
+ }
+ break;
+ case GSM_LCHAN_TCH_H:
+ DEBUGP(DRSL, "Unimplemented TCH_H activation\n");
+ return -1;
+ case GSM_LCHAN_UNKNOWN:
+ case GSM_LCHAN_NONE:
+ return -1;
+ }
+
+ memset(&ci, 0, sizeof(ci));
+ ci.chan_desc.iei = 0x64;
+ ci.chan_desc.chan_nr = chan_nr;
+ ci.chan_desc.oct3 = (lchan->ts->trx->bts->tsc << 5) | ((arfcn & 0x3ff) >> 8);
+ ci.chan_desc.oct4 = arfcn & 0xff;
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
+ dh->chan_nr = chan_nr;
+
+ msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
+ /* For compatibility with Phase 1 */
+ msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
+ (u_int8_t *) &cm);
+ msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
+ (u_int8_t *) &ci);
+#if 0
+ msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
+ (u_int8_t *) &encr_info);
+#endif
+ msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
+ msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
+ msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Chapter 8.4.9 */
+int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ u_int8_t chan_nr = lchan2chan_nr(lchan);
+ struct rsl_ie_chan_mode cm;
+
+ memset(&cm, 0, sizeof(cm));
+
+ /* FIXME: what to do with data calls ? */
+ cm.dtx_dtu = 0x00;
+ switch (lchan->type) {
+ /* todo more modes */
+ case GSM_LCHAN_TCH_F:
+ cm.spd_ind = RSL_CMOD_SPD_SPEECH;
+ cm.chan_rt = RSL_CMOD_CRT_TCH_Bm;
+ switch(lchan->tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ cm.chan_rate = RSL_CMOD_SP_GSM1;
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ cm.chan_rate = RSL_CMOD_SP_GSM2;
+ break;
+ default:
+ DEBUGP(DRSL, "Unimplemented channel modification\n");
+ return -1;
+ }
+ break;
+ default:
+ DEBUGP(DRSL, "Unimplemented channel modification\n");
+ return -1;
+ }
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ);
+ dh->chan_nr = chan_nr;
+
+ msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm),
+ (u_int8_t *) &cm);
+#if 0
+ msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
+ (u_int8_t *) &encr_info);
+#endif
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Chapter 9.1.7 of 04.08 */
+int rsl_chan_release(struct gsm_lchan *lchan)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL);
+ dh->chan_nr = lchan2chan_nr(lchan);
+
+ msg->lchan = lchan;
+ msg->trx = lchan->ts->trx;
+
+ DEBUGP(DRSL, "Channel Release CMD channel=%s chan_nr=0x%02x\n",
+ gsm_ts_name(lchan->ts), dh->chan_nr);
+
+ return abis_rsl_sendmsg(msg);
+}
+
+int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
+ u_int8_t *ms_ident, u_int8_t chan_needed)
+{
+ struct abis_rsl_dchan_hdr *dh;
+ struct msgb *msg = rsl_msgb_alloc();
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_PAGING_CMD);
+ dh->chan_nr = RSL_CHAN_PCH_AGCH;
+
+ msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group);
+ msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2);
+ msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed);
+
+ msg->trx = bts->c0;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+int rsl_paging_cmd_subscr(struct gsm_bts *bts, u_int8_t chan_need,
+ struct gsm_subscriber *subscr)
+{
+#if 0
+ u_int8_t mi[128];
+ unsigned int mi_len;
+ u_int8_t paging_group;
+#endif
+
+ return -1;
+}
+
+int imsi_str2bcd(u_int8_t *bcd_out, const char *str_in)
+{
+ int i, len = strlen(str_in);
+
+ for (i = 0; i < len; i++) {
+ int num = str_in[i] - 0x30;
+ if (num < 0 || num > 9)
+ return -1;
+ if (i % 2 == 0)
+ bcd_out[i/2] = num;
+ else
+ bcd_out[i/2] |= (num << 4);
+ }
+
+ return 0;
+}
+
+/* Chapter 8.5.6 */
+int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_dchan_hdr *dh;
+ u_int8_t buf[MACBLOCK_SIZE];
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD);
+ dh->chan_nr = RSL_CHAN_PCH_AGCH;
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val);
+ break;
+ default:
+ /* If phase 2, construct a FULL_IMM_ASS_INFO */
+ pad_macblock(buf, val, len);
+ msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf);
+ break;
+ }
+
+ msg->trx = bts->c0;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Send "DATA REQUEST" message with given L3 Info payload */
+/* Chapter 8.3.1 */
+int rsl_data_request(struct msgb *msg, u_int8_t link_id)
+{
+ u_int8_t l3_len = msg->tail - (u_int8_t *)msgb_l3(msg);
+ struct abis_rsl_rll_hdr *rh;
+
+ if (msg->lchan == NULL) {
+ fprintf(stderr, "cannot send DATA REQUEST to unknown lchan\n");
+ return -EINVAL;
+ }
+
+ /* First push the L3 IE tag and length */
+ msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len);
+
+ /* Then push the RSL header */
+ rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
+ init_llm_hdr(rh, RSL_MT_DATA_REQ);
+ rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+ rh->chan_nr = lchan2chan_nr(msg->lchan);
+ rh->link_id = link_id;
+
+ msg->trx = msg->lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+/* Chapter 8.4.2: Channel Activate Acknowledge */
+static int rsl_rx_chan_act_ack(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
+
+ /* BTS has confirmed channel activation, we now need
+ * to assign the activated channel to the MS */
+ if (rslh->ie_chan != RSL_IE_CHAN_NR)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Chapter 8.4.3: Channel Activate NACK */
+static int rsl_rx_chan_act_nack(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+
+ /* BTS has rejected channel activation ?!? */
+ if (dh->ie_chan != RSL_IE_CHAN_NR)
+ return -EINVAL;
+
+ rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
+ DEBUGPC(DRSL, "CAUSE=0x%02x ", *TLVP_VAL(&tp, RSL_IE_CAUSE));
+
+ return 0;
+}
+
+/* Chapter 8.4.4: Connection Failure Indication */
+static int rsl_rx_conn_fail(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+
+ DEBUGPC(DRSL, "CONNECTION FAIL: ");
+ print_rsl_cause(dh->data);
+
+ rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+
+ if (msg->trx->bts->type == GSM_BTS_TYPE_BS11) {
+ /* FIXME: we have no idea what cause 0x18 is !!! */
+ if (TLVP_PRESENT(&tp, RSL_IE_CAUSE) &&
+ TLVP_LEN(&tp, RSL_IE_CAUSE) >= 1 &&
+ *TLVP_VAL(&tp, RSL_IE_CAUSE) == 0x18) {
+ if (msg->lchan->use_count > 0) {
+ DEBUGPC(DRSL, "Cause 0x18 IGNORING, lchan in use! (%d times)\n", msg->lchan->use_count);
+ return 0;
+ }
+ }
+ }
+
+ DEBUGPC(DRSL, "RELEASING.\n");
+
+ /* FIXME: only free it after channel release ACK */
+ return rsl_chan_release(msg->lchan);
+}
+
+static int rsl_rx_meas_res(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tp;
+
+ DEBUGPC(DRSL, "MEASUREMENT RESULT ");
+ rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
+
+ if (TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR))
+ DEBUGPC(DRSL, "NR=%d ", *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR));
+ if (TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS)) {
+ u_int8_t len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS);
+ const u_int8_t *val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS);
+ if (len >= 3) {
+ if (val[0] & 0x40)
+ DEBUGPC(DRSL, "DTXd ");
+ DEBUGPC(DRSL, "RXL-FULL-up=%d RXL-SUB-up=%d ",
+ val[0] & 0x3f, val[1] & 0x3f);
+ DEBUGPC(DRSL, "RXQ-FULL-up=%d RXQ-SUB-up=%d ",
+ val[2]>>3 & 0x7, val[2] & 0x7);
+ }
+ }
+ if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER))
+ DEBUGPC(DRSL, "BS_POWER=%d ", *TLVP_VAL(&tp, RSL_IE_BS_POWER));
+ if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET))
+ DEBUGPC(DRSL, "MS_TO=%d ",
+ *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET));
+ if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO))
+ DEBUGPC(DRSL, "L1 ");
+ if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) {
+ DEBUGPC(DRSL, "L3\n");
+ msg->l3h = TLVP_VAL(&tp, RSL_IE_L3_INFO);
+ return gsm0408_rcvmsg(msg);
+ } else
+ DEBUGPC(DRSL, "\n");
+
+ return 0;
+}
+
+static int abis_rsl_rx_dchan(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+ char *ts_name;
+
+ msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr);
+ ts_name = gsm_ts_name(msg->lchan->ts);
+
+ DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ", ts_name, rslh->chan_nr);
+
+ switch (rslh->c.msg_type) {
+ case RSL_MT_CHAN_ACTIV_ACK:
+ DEBUGPC(DRSL, "CHANNEL ACTIVATE ACK\n");
+ rc = rsl_rx_chan_act_ack(msg);
+ break;
+ case RSL_MT_CHAN_ACTIV_NACK:
+ DEBUGPC(DRSL, "CHANNEL ACTIVATE NACK\n");
+ rc = rsl_rx_chan_act_nack(msg);
+ break;
+ case RSL_MT_CONN_FAIL:
+ rc = rsl_rx_conn_fail(msg);
+ break;
+ case RSL_MT_MEAS_RES:
+ rc = rsl_rx_meas_res(msg);
+ break;
+ case RSL_MT_RF_CHAN_REL_ACK:
+ DEBUGPC(DRSL, "RF CHANNEL RELEASE ACK\n");
+ lchan_free(msg->lchan);
+ break;
+ case RSL_MT_MODE_MODIFY_ACK:
+ DEBUGPC(DRSL, "CHANNEL MODE MODIFY ACK\n");
+ break;
+ case RSL_MT_MODE_MODIFY_NACK:
+ DEBUGPC(DRSL, "CHANNEL MODE MODIFY NACK\n");
+ break;
+ case RSL_MT_PHY_CONTEXT_CONF:
+ case RSL_MT_PREPROC_MEAS_RES:
+ case RSL_MT_TALKER_DET:
+ case RSL_MT_LISTENER_DET:
+ case RSL_MT_REMOTE_CODEC_CONF_REP:
+ case RSL_MT_MR_CODEC_MOD_ACK:
+ case RSL_MT_MR_CODEC_MOD_NACK:
+ case RSL_MT_MR_CODEC_MOD_PER:
+ DEBUGPC(DRSL, "Unimplemented Abis RSL DChan msg 0x%02x\n",
+ rslh->c.msg_type);
+ break;
+ default:
+ DEBUGPC(DRSL, "unknown Abis RSL DChan msg 0x%02x\n",
+ rslh->c.msg_type);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int rsl_rx_error_rep(struct msgb *msg)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+
+ DEBUGP(DRSL, "ERROR REPORT ");
+ print_rsl_cause(rslh->data);
+ DEBUGPC(DRSL, "\n");
+
+ return 0;
+}
+
+static int abis_rsl_rx_trx(struct msgb *msg)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+
+ switch (rslh->msg_type) {
+ case RSL_MT_ERROR_REPORT:
+ rc = rsl_rx_error_rep(msg);
+ break;
+ case RSL_MT_RF_RES_IND:
+ /* interference on idle channels of TRX */
+ //DEBUGP(DRSL, "TRX: RF Interference Indication\n");
+ break;
+ case RSL_MT_OVERLOAD:
+ /* indicate CCCH / ACCH / processor overload */
+ DEBUGP(DRSL, "TRX: CCCH/ACCH/CPU Overload\n");
+ break;
+ default:
+ DEBUGP(DRSL, "Unknown Abis RSL TRX message type 0x%02x\n",
+ rslh->msg_type);
+ return -EINVAL;
+ }
+ return rc;
+}
+
+/* MS has requested a channel on the RACH */
+static int rsl_rx_chan_rqd(struct msgb *msg)
+{
+ struct gsm_bts *bts = msg->trx->bts;
+ struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);
+ struct gsm48_req_ref *rqd_ref;
+ struct gsm48_imm_ass ia;
+ enum gsm_chan_t lctype;
+ enum gsm_chreq_reason_t chreq_reason;
+ struct gsm_lchan *lchan;
+ u_int8_t rqd_ta;
+ int ret;
+
+ u_int16_t arfcn;
+ u_int8_t ts_number, subch;
+
+ /* parse request reference to be used in immediate assign */
+ if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE)
+ return -EINVAL;
+
+ rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];
+
+ /* parse access delay and use as TA */
+ if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY)
+ return -EINVAL;
+ rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];
+
+ /* determine channel type (SDCCH/TCH_F/TCH_H) based on
+ * request reference RA */
+ lctype = get_ctype_by_chreq(bts, rqd_ref->ra);
+ chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra);
+
+ /* check availability / allocate channel */
+ lchan = lchan_alloc(bts, lctype);
+ if (!lchan) {
+ fprintf(stderr, "CHAN RQD: no resources\n");
+ /* FIXME: send some kind of reject ?!? */
+ return -ENOMEM;
+ }
+
+ ts_number = lchan->ts->nr;
+ arfcn = lchan->ts->trx->arfcn;
+ subch = lchan->nr;
+
+ lchan->ms_power = lchan->bs_power = 0x0f; /* 30dB reduction */
+ rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, RSL_CMOD_SPD_SIGN);
+
+ /* create IMMEDIATE ASSIGN 04.08 messge */
+ memset(&ia, 0, sizeof(ia));
+ ia.l2_plen = 0x2d;
+ ia.proto_discr = GSM48_PDISC_RR;
+ ia.msg_type = GSM48_MT_RR_IMM_ASS;
+ ia.page_mode = GSM48_PM_SAME;
+ ia.chan_desc.chan_nr = lchan2chan_nr(lchan);
+ ia.chan_desc.h0.h = 0;
+ ia.chan_desc.h0.arfcn_high = arfcn >> 8;
+ ia.chan_desc.h0.arfcn_low = arfcn & 0xff;
+ ia.chan_desc.h0.tsc = 7;
+ /* use request reference extracted from CHAN_RQD */
+ memcpy(&ia.req_ref, rqd_ref, sizeof(ia.req_ref));
+ ia.timing_advance = rqd_ta;
+ ia.mob_alloc_len = 0;
+
+ DEBUGP(DRSL, "Activating ARFCN(%u) TS(%u) SS(%u) lctype %s "
+ "chan_nr=0x%02x r=%s ra=0x%02x\n",
+ arfcn, ts_number, subch, gsm_lchan_name(lchan->type),
+ ia.chan_desc.chan_nr, gsm_chreq_name(chreq_reason),
+ rqd_ref->ra);
+
+ /* FIXME: Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
+
+ /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
+ ret = rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
+
+ return ret;
+}
+
+/* MS has requested a channel on the RACH */
+static int rsl_rx_ccch_load(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
+ u_int16_t pg_buf_space;
+ u_int16_t rach_slot_count = -1;
+ u_int16_t rach_busy_count = -1;
+ u_int16_t rach_access_count = -1;
+
+ switch (rslh->data[0]) {
+ case RSL_IE_PAGING_LOAD:
+ pg_buf_space = rslh->data[1] << 8 | rslh->data[2];
+ paging_update_buffer_space(msg->trx->bts, pg_buf_space);
+ break;
+ case RSL_IE_RACH_LOAD:
+ if (msg->data_len >= 7) {
+ rach_slot_count = rslh->data[2] << 8 | rslh->data[3];
+ rach_busy_count = rslh->data[4] << 8 | rslh->data[5];
+ rach_access_count = rslh->data[6] << 8 | rslh->data[7];
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int abis_rsl_rx_cchan(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg);
+ int rc = 0;
+
+ msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr);
+
+ switch (rslh->c.msg_type) {
+ case RSL_MT_CHAN_RQD:
+ /* MS has requested a channel on the RACH */
+ rc = rsl_rx_chan_rqd(msg);
+ break;
+ case RSL_MT_CCCH_LOAD_IND:
+ /* current load on the CCCH */
+ rc = rsl_rx_ccch_load(msg);
+ break;
+ case RSL_MT_DELETE_IND:
+ /* CCCH overloaded, IMM_ASSIGN was dropped */
+ case RSL_MT_CBCH_LOAD_IND:
+ /* current load on the CBCH */
+ fprintf(stderr, "Unimplemented Abis RSL TRX message type "
+ "0x%02x\n", rslh->c.msg_type);
+ break;
+ default:
+ fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n",
+ rslh->c.msg_type);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int rsl_rx_rll_err_ind(struct msgb *msg)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ u_int8_t *rlm_cause = rllh->data;
+
+ DEBUGPC(DRLL, "cause=0x%02x", rlm_cause[1]);
+
+ return 0;
+}
+
+/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
+ 0x02, 0x06,
+ 0x01, 0x20,
+ 0x02, 0x00,
+ 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */
+
+static int abis_rsl_rx_rll(struct msgb *msg)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int rc = 0;
+ char *ts_name;
+
+ msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr);
+ ts_name = gsm_ts_name(msg->lchan->ts);
+ DEBUGP(DRLL, "channel=%s chan_nr=0x%02x ", ts_name, rllh->chan_nr);
+
+ switch (rllh->c.msg_type) {
+ case RSL_MT_DATA_IND:
+ DEBUGPC(DRLL, "DATA INDICATION\n");
+ if (msgb_l2len(msg) >
+ sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
+ rllh->data[0] == RSL_IE_L3_INFO) {
+ msg->l3h = &rllh->data[3];
+ return gsm0408_rcvmsg(msg);
+ }
+ break;
+ case RSL_MT_EST_IND:
+ DEBUGPC(DRLL, "ESTABLISH INDICATION\n");
+ if (msgb_l2len(msg) >
+ sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
+ rllh->data[0] == RSL_IE_L3_INFO) {
+ msg->l3h = &rllh->data[3];
+ return gsm0408_rcvmsg(msg);
+ }
+ break;
+ case RSL_MT_REL_IND:
+ DEBUGPC(DRLL, "RELEASE INDICATION ");
+ break;
+ case RSL_MT_REL_CONF:
+ DEBUGPC(DRLL, "RELEASE CONFIRMATION ");
+ break;
+ case RSL_MT_ERROR_IND:
+ DEBUGPC(DRLL, "ERROR INDICATION ");
+ rc = rsl_rx_rll_err_ind(msg);
+ break;
+ case RSL_MT_UNIT_DATA_IND:
+ DEBUGPC(DRLL, "unimplemented Abis RLL message type 0x%02x ",
+ rllh->c.msg_type);
+ break;
+ default:
+ DEBUGPC(DRLL, "unknown Abis RLL message type 0x%02x ",
+ rllh->c.msg_type);
+ }
+ DEBUGPC(DRLL, "\n");
+ return rc;
+}
+
+/* ip.access specific RSL extensions */
+int rsl_ipacc_bind(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_dchan_hdr *dh;
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_IPAC_BIND);
+ dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
+ dh->chan_nr = lchan2chan_nr(lchan);
+
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+int rsl_ipacc_connect(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, u_int16_t f8, u_int8_t fc)
+{
+ struct msgb *msg = rsl_msgb_alloc();
+ struct abis_rsl_dchan_hdr *dh;
+ u_int8_t *att_f8, *att_ip, *att_port;
+
+ dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+ init_dchan_hdr(dh, RSL_MT_IPAC_CONNECT);
+ dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
+ dh->chan_nr = lchan2chan_nr(lchan);
+
+ att_f8 = msgb_put(msg, sizeof(f8)+1);
+ att_f8[0] = 0xf8;
+ att_f8[1] = f8 >> 8;
+ att_f8[2] = f8 & 0xff;
+
+ att_ip = msgb_put(msg, sizeof(ip)+1);
+ att_ip[0] = RSL_IE_IPAC_REMOTE_IP;
+ att_ip[1] = ip >> 24;
+ att_ip[2] = ip >> 16;
+ att_ip[3] = ip >> 8;
+ att_ip[4] = ip & 0xff;
+ //att_ip[4] = 11;
+
+ att_port = msgb_put(msg, sizeof(port)+1);
+ att_port[0] = RSL_IE_IPAC_REMOTE_PORT;
+ att_port[1] = port >> 8;
+ att_port[2] = port & 0xff;
+
+ msgb_tv_put(msg, 0xf4, 1); /* F4 01 */
+ msgb_tv_put(msg, 0xfc, fc); /* FC 7F */
+ msg->trx = lchan->ts->trx;
+
+ return abis_rsl_sendmsg(msg);
+}
+
+static int abis_rsl_rx_ipacc_bindack(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tv;
+ struct gsm_bts_trx_ts *ts = msg->lchan->ts;
+ struct in_addr ip;
+ u_int16_t port, attr_f8;
+
+ /* the BTS has acknowledged a local bind, it now tells us the IP
+ * address and port number to which it has bound the given logical
+ * channel */
+
+ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) ||
+ !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) ||
+ !TLVP_PRESENT(&tv, 0xfc) ||
+ !TLVP_PRESENT(&tv, 0xf8)) {
+ DEBUGPC(DRSL, "mandatory IE missing");
+ return -EINVAL;
+ }
+ ip.s_addr = *((u_int32_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_IP));
+ port = *((u_int16_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_PORT));
+ attr_f8 = *((u_int16_t *) TLVP_VAL(&tv, 0xf8));
+
+ DEBUGPC(DRSL, "IP=%s PORT=%d FC=%d F8=%d",
+ inet_ntoa(ip), ntohs(port), *TLVP_VAL(&tv, 0xfc),
+ ntohs(attr_f8));
+
+ /* update our local information about this TS */
+ ts->abis_ip.bound_ip = ntohl(ip.s_addr);
+ ts->abis_ip.bound_port = ntohs(port);
+ ts->abis_ip.attr_f8 = ntohs(attr_f8);
+ ts->abis_ip.attr_fc = *TLVP_VAL(&tv, 0xfc);
+
+ dispatch_signal(SS_ABISIP, S_ABISIP_BIND_ACK, msg->lchan);
+
+ return 0;
+}
+
+static int abis_rsl_rx_ipacc_disc_ind(struct msgb *msg)
+{
+ struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct tlv_parsed tv;
+
+ rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
+ if (!TLVP_PRESENT(&tv, RSL_IE_CAUSE)) {
+ DEBUGPC(DRSL, "mandatory IE missing! ");
+ return -EINVAL;
+ }
+
+ DEBUGPC(DRSL, "cause=0x%02x ", *TLVP_VAL(&tv, RSL_IE_CAUSE));
+
+ return 0;
+}
+
+static int abis_rsl_rx_ipacc(struct msgb *msg)
+{
+ struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+ int rc = 0;
+
+ msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr);
+ DEBUGP(DRSL, "channel=%s chan_nr=0x%02x ",
+ gsm_ts_name(msg->lchan->ts), rllh->chan_nr);
+
+ switch (rllh->c.msg_type) {
+ case RSL_MT_IPAC_BIND_ACK:
+ DEBUGPC(DRSL, "IPAC_BIND_ACK ");
+ rc = abis_rsl_rx_ipacc_bindack(msg);
+ break;
+ case RSL_MT_IPAC_BIND_NACK:
+ /* somehow the BTS was unable to bind the lchan to its local
+ * port?!? */
+ DEBUGPC(DRSL, "IPAC_BIND_NACK ");
+ break;
+ case RSL_MT_IPAC_CONNECT_ACK:
+ /* the BTS tells us that a connect operation was successful */
+ DEBUGPC(DRSL, "IPAC_CONNECT_ACK ");
+ break;
+ case RSL_MT_IPAC_CONNECT_NACK:
+ /* somehow the BTS was unable to connect the lchan to a remote
+ * port */
+ DEBUGPC(DRSL, "IPAC_CONNECT_NACK ");
+ break;
+ case RSL_MT_IPAC_DISCONNECT_IND:
+ DEBUGPC(DRSL, "IPAC_DISCONNECT_IND ");
+ rc = abis_rsl_rx_ipacc_disc_ind(msg);
+ break;
+ default:
+ DEBUGPC(DRSL, "Unknown ip.access msg_type 0x%02x", rllh->c.msg_type);
+ break;
+ }
+ DEBUGPC(DRSL, "\n");
+
+ return rc;
+}
+
+
+/* Entry-point where L2 RSL from BTS enters */
+int abis_rsl_rcvmsg(struct msgb *msg)
+{
+ struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ;
+ int rc = 0;
+
+ switch (rslh->msg_discr & 0xfe) {
+ case ABIS_RSL_MDISC_RLL:
+ rc = abis_rsl_rx_rll(msg);
+ break;
+ case ABIS_RSL_MDISC_DED_CHAN:
+ rc = abis_rsl_rx_dchan(msg);
+ break;
+ case ABIS_RSL_MDISC_COM_CHAN:
+ rc = abis_rsl_rx_cchan(msg);
+ break;
+ case ABIS_RSL_MDISC_TRX:
+ rc = abis_rsl_rx_trx(msg);
+ break;
+ case ABIS_RSL_MDISC_LOC:
+ fprintf(stderr, "unimplemented RSL msg disc 0x%02x\n",
+ rslh->msg_discr);
+ break;
+ case ABIS_RSL_MDISC_IPACCESS:
+ rc = abis_rsl_rx_ipacc(msg);
+ break;
+ default:
+ fprintf(stderr, "unknown RSL message discriminator 0x%02x\n",
+ rslh->msg_discr);
+ return -EINVAL;
+ }
+ msgb_free(msg);
+ return rc;
+}
+
+
+/* Section 3.3.2.3 . I think this looks like a table */
+int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf)
+{
+ switch (ccch_conf) {
+ case RSL_BCCH_CCCH_CONF_1_NC:
+ return 1;
+ case RSL_BCCH_CCCH_CONF_1_C:
+ return 1;
+ case RSL_BCCH_CCCH_CONF_2_NC:
+ return 2;
+ case RSL_BCCH_CCCH_CONF_3_NC:
+ return 3;
+ case RSL_BCCH_CCCH_CONF_4_NC:
+ return 4;
+ default:
+ return -1;
+ }
+}
+
+int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf)
+{
+ switch (ccch_conf) {
+ case RSL_BCCH_CCCH_CONF_1_NC:
+ return 0;
+ case RSL_BCCH_CCCH_CONF_1_C:
+ return 1;
+ case RSL_BCCH_CCCH_CONF_2_NC:
+ return 0;
+ case RSL_BCCH_CCCH_CONF_3_NC:
+ return 0;
+ case RSL_BCCH_CCCH_CONF_4_NC:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+/* From Table 10.5.33 of GSM 04.08 */
+int rsl_number_of_paging_subchannels(struct gsm_bts *bts)
+{
+ if (bts->chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) {
+ return MAX(1, (3 - bts->chan_desc.bs_ag_blks_res))
+ * (bts->chan_desc.bs_pa_mfrms + 2);
+ } else {
+ return (9 - bts->chan_desc.bs_ag_blks_res)
+ * (bts->chan_desc.bs_pa_mfrms + 2);
+ }
+}
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
new file mode 100644
index 000000000..656feb453
--- /dev/null
+++ b/openbsc/src/bs11_config.c
@@ -0,0 +1,805 @@
+/* Siemens BS-11 microBTS configuration tool */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This software is based on ideas (but not code) of BS11Config
+ * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
+#include <openbsc/debug.h>
+#include <openbsc/select.h>
+#include <openbsc/rs232.h>
+
+/* state of our bs11_config application */
+enum bs11cfg_state {
+ STATE_NONE,
+ STATE_LOGON_WAIT,
+ STATE_LOGON_ACK,
+ STATE_SWLOAD,
+ STATE_QUERY,
+};
+static enum bs11cfg_state bs11cfg_state = STATE_NONE;
+static char *command;
+struct timer_list status_timer;
+
+static const u_int8_t obj_li_attr[] = {
+ NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00,
+ NM_ATT_BS11_L1_PROT_TYPE, 0x00,
+ NM_ATT_BS11_LINE_CFG, 0x00,
+};
+static const u_int8_t obj_bbsig0_attr[] = {
+ NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00,
+ NM_ATT_BS11_DIVERSITY, 0x01, 0x00,
+};
+static const u_int8_t obj_pa0_attr[] = {
+ NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW,
+};
+static const char *trx1_password = "1111111111";
+#define TEI_OML 25
+
+static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
+
+
+int handle_serial_msg(struct msgb *rx_msg);
+
+/* create all objects for an initial configuration */
+static int create_objects(struct gsm_bts *bts)
+{
+ fprintf(stdout, "Crating Objects for minimal config\n");
+ abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr),
+ obj_li_attr);
+ abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL);
+ abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL);
+ abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL);
+ abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0,
+ sizeof(obj_bbsig0_attr), obj_bbsig0_attr);
+ abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0,
+ sizeof(obj_pa0_attr), obj_pa0_attr);
+ abis_nm_bs11_create_envaBTSE(bts, 0);
+ abis_nm_bs11_create_envaBTSE(bts, 1);
+ abis_nm_bs11_create_envaBTSE(bts, 2);
+ abis_nm_bs11_create_envaBTSE(bts, 3);
+
+ abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML);
+
+ abis_nm_bs11_set_trx_power(&bts->trx[0], BS11_TRX_POWER_GSM_30mW);
+
+ sleep(1);
+
+ abis_nm_bs11_set_trx1_pw(bts, trx1_password);
+
+ sleep(1);
+
+ return 0;
+}
+
+static int create_trx1(struct gsm_bts *bts)
+{
+ u_int8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12];
+ u_int8_t *cur = bbsig1_attr;
+
+ fprintf(stdout, "Crating Objects for TRX1\n");
+
+ abis_nm_bs11_set_trx1_pw(bts, trx1_password);
+
+ sleep(1);
+
+ cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10,
+ (u_int8_t *)trx1_password);
+ memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr));
+ abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1,
+ sizeof(bbsig1_attr), bbsig1_attr);
+ abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1,
+ sizeof(obj_pa0_attr), obj_pa0_attr);
+ abis_nm_bs11_set_trx_power(&bts->trx[1], BS11_TRX_POWER_GSM_30mW);
+
+ return 0;
+}
+
+static char *serial_port = "/dev/ttyUSB0";
+static char *fname_safety = "BTSBMC76.SWI";
+static char *fname_software = "HS011106.SWL";
+static int delay_ms = 0;
+static int win_size = 8;
+static int param_disconnect = 0;
+static int param_restart = 0;
+static int param_forced = 0;
+static struct gsm_bts *g_bts;
+
+static int file_is_readable(const char *fname)
+{
+ int rc;
+ struct stat st;
+
+ rc = stat(fname, &st);
+ if (rc < 0)
+ return 0;
+
+ if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR))
+ return 1;
+
+ return 0;
+}
+
+static int percent;
+static int percent_old;
+
+/* callback function passed to the ABIS OML code */
+static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg,
+ void *data, void *param)
+{
+ if (hook != GSM_HOOK_NM_SWLOAD)
+ return 0;
+
+ switch (event) {
+ case NM_MT_LOAD_INIT_ACK:
+ fprintf(stdout, "Software Load Initiate ACK\n");
+ break;
+ case NM_MT_LOAD_INIT_NACK:
+ fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
+ exit(5);
+ break;
+ case NM_MT_LOAD_END_ACK:
+ if (data) {
+ /* we did a safety load and must activate it */
+ abis_nm_software_activate(g_bts, fname_safety,
+ swload_cbfn, g_bts);
+ sleep(5);
+ }
+ break;
+ case NM_MT_LOAD_END_NACK:
+ fprintf(stderr, "ERROR: Software Load End NACK\n");
+ exit(3);
+ break;
+ case NM_MT_ACTIVATE_SW_NACK:
+ fprintf(stderr, "ERROR: Activate Software NACK\n");
+ exit(4);
+ break;
+ case NM_MT_ACTIVATE_SW_ACK:
+ bs11cfg_state = STATE_NONE;
+
+ break;
+ case NM_MT_LOAD_SEG_ACK:
+ percent = abis_nm_software_load_status(g_bts);
+ if (percent > percent_old)
+ printf("Software Download Progress: %d%%\n", percent);
+ percent_old = percent;
+ break;
+ }
+ return 0;
+}
+
+static const char *bs11_link_state[] = {
+ [0x00] = "Down",
+ [0x01] = "Up",
+ [0x02] = "Restoring",
+};
+
+static const char *linkstate_name(u_int8_t linkstate)
+{
+ if (linkstate > ARRAY_SIZE(bs11_link_state))
+ return "Unknown";
+
+ return bs11_link_state[linkstate];
+}
+
+static const char *mbccu_load[] = {
+ [0] = "No Load",
+ [1] = "Load BTSCAC",
+ [2] = "Load BTSDRX",
+ [3] = "Load BTSBBX",
+ [4] = "Load BTSARC",
+ [5] = "Load",
+};
+
+static const char *mbccu_load_name(u_int8_t linkstate)
+{
+ if (linkstate > ARRAY_SIZE(mbccu_load))
+ return "Unknown";
+
+ return mbccu_load[linkstate];
+}
+
+static const char *bts_phase_name(u_int8_t phase)
+{
+ switch (phase) {
+ case BS11_STATE_WARM_UP:
+ case BS11_STATE_WARM_UP_2:
+ return "Warm Up";
+ break;
+ case BS11_STATE_LOAD_SMU_SAFETY:
+ return "Load SMU Safety";
+ break;
+ case BS11_STATE_LOAD_SMU_INTENDED:
+ return "Load SMU Intended";
+ break;
+ case BS11_STATE_LOAD_MBCCU:
+ return "Load MBCCU";
+ break;
+ case BS11_STATE_SOFTWARE_RQD:
+ return "Software required";
+ break;
+ case BS11_STATE_WAIT_MIN_CFG:
+ case BS11_STATE_WAIT_MIN_CFG_2:
+ return "Wait minimal config";
+ break;
+ case BS11_STATE_MAINTENANCE:
+ return "Maintenance";
+ break;
+ case BS11_STATE_NORMAL:
+ return "Normal";
+ break;
+ case BS11_STATE_ABIS_LOAD:
+ return "Abis load";
+ break;
+ default:
+ return "Unknown";
+ break;
+ }
+}
+
+static const char *trx_power_name(u_int8_t pwr)
+{
+ switch (pwr) {
+ case BS11_TRX_POWER_GSM_2W:
+ return "2W (GSM)";
+ case BS11_TRX_POWER_GSM_250mW:
+ return "250mW (GSM)";
+ case BS11_TRX_POWER_GSM_80mW:
+ return "80mW (GSM)";
+ case BS11_TRX_POWER_GSM_30mW:
+ return "30mW (GSM)";
+ case BS11_TRX_POWER_DCS_3W:
+ return "3W (DCS)";
+ case BS11_TRX_POWER_DCS_1W6:
+ return "1.6W (DCS)";
+ case BS11_TRX_POWER_DCS_500mW:
+ return "500mW (DCS)";
+ case BS11_TRX_POWER_DCS_160mW:
+ return "160mW (DCS)";
+ default:
+ return "unknown value";
+ }
+}
+
+static const char *pll_mode_name(u_int8_t mode)
+{
+ switch (mode) {
+ case BS11_LI_PLL_LOCKED:
+ return "E1 Locked";
+ case BS11_LI_PLL_STANDALONE:
+ return "Standalone";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *cclk_acc_name(u_int8_t acc)
+{
+ switch (acc) {
+ case 0:
+ /* Out of the demanded +/- 0.05ppm */
+ return "Medium";
+ case 1:
+ /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */
+ return "High";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *obj_name(struct abis_om_fom_hdr *foh)
+{
+ static char retbuf[256];
+
+ retbuf[0] = 0;
+
+ switch (foh->obj_class) {
+ case NM_OC_BS11:
+ strcat(retbuf, "BS11 ");
+ switch (foh->obj_inst.bts_nr) {
+ case BS11_OBJ_PA:
+ sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ",
+ foh->obj_inst.ts_nr);
+ break;
+ case BS11_OBJ_LI:
+ sprintf(retbuf+strlen(retbuf), "Line Interface ");
+ break;
+ case BS11_OBJ_CCLK:
+ sprintf(retbuf+strlen(retbuf), "CCLK ");
+ break;
+ }
+ break;
+ case NM_OC_SITE_MANAGER:
+ strcat(retbuf, "SITE MANAGER ");
+ break;
+ }
+ return retbuf;
+}
+
+static void print_state(struct tlv_parsed *tp)
+{
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) {
+ u_int8_t phase, mbccu;
+ if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) {
+ phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE);
+ printf("PHASE: %u %-20s ", phase & 0xf,
+ bts_phase_name(phase));
+ }
+ if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) {
+ mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1);
+ printf("MBCCU0: %-11s MBCCU1: %-11s ",
+ mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4));
+ }
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) &&
+ TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) {
+ u_int8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE);
+ printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf));
+ }
+ printf("\n");
+}
+
+static int print_attr(struct tlv_parsed *tp)
+{
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) {
+ printf("\tBS-11 ESN PCB Serial Number: %s\n",
+ TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL));
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) {
+ printf("\tBS-11 ESN Hardware Code Number: %s\n",
+ TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6);
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) {
+ printf("\tBS-11 ESN Firmware Code Number: %s\n",
+ TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6);
+ }
+#if 0
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) {
+ printf("BS-11 Boot Software Version: %s\n",
+ TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6);
+ }
+#endif
+ if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) &&
+ TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) {
+ const u_int8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL);
+ printf("\tE1 Channel: Port=%u Timeslot=%u ",
+ chan[0], chan[1]);
+ if (chan[2] == 0xff)
+ printf("(Full Slot)\n");
+ else
+ printf("Subslot=%u\n", chan[2]);
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_TEI))
+ printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI));
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) &&
+ TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) {
+ printf("\tTRX Power: %s\n",
+ trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR)));
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) &&
+ TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) {
+ printf("\tPLL Mode: %s\n",
+ pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE)));
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) &&
+ TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) {
+ const u_int8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL);
+ printf("\tPLL Set Value=%d, Work Value=%d\n",
+ vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]);
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) &&
+ TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) {
+ const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY);
+ printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc);
+ }
+ if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) &&
+ TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) {
+ const u_int8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE);
+ printf("\tCCLK Type=%d\n", *acc);
+ }
+
+
+ return 0;
+}
+
+static void cmd_query(void)
+{
+ bs11cfg_state = STATE_QUERY;
+ abis_nm_bs11_get_serno(g_bts);
+ abis_nm_bs11_get_oml_tei_ts(g_bts);
+ abis_nm_bs11_get_pll_mode(g_bts);
+ abis_nm_bs11_get_cclk(g_bts);
+ abis_nm_bs11_get_trx_power(&g_bts->trx[0]);
+ abis_nm_bs11_get_trx_power(&g_bts->trx[1]);
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+}
+
+/* handle a response from the BTS to a GET STATE command */
+static int handle_state_resp(enum abis_bs11_phase state)
+{
+ int rc = 0;
+
+ switch (state) {
+ case BS11_STATE_WARM_UP:
+ case BS11_STATE_LOAD_SMU_SAFETY:
+ case BS11_STATE_LOAD_SMU_INTENDED:
+ case BS11_STATE_LOAD_MBCCU:
+ break;
+ case BS11_STATE_SOFTWARE_RQD:
+ bs11cfg_state = STATE_SWLOAD;
+ /* send safety load. Use g_bts as private 'param'
+ * argument, so our swload_cbfn can distinguish
+ * a safety load from a regular software */
+ if (file_is_readable(fname_safety))
+ rc = abis_nm_software_load(g_bts, fname_safety,
+ win_size, param_forced,
+ swload_cbfn, g_bts);
+ else
+ fprintf(stderr, "No valid Safety Load file \"%s\"\n",
+ fname_safety);
+ break;
+ case BS11_STATE_WAIT_MIN_CFG:
+ case BS11_STATE_WAIT_MIN_CFG_2:
+ bs11cfg_state = STATE_SWLOAD;
+ rc = create_objects(g_bts);
+ break;
+ case BS11_STATE_MAINTENANCE:
+ if (command) {
+ if (!strcmp(command, "disconnect"))
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ else if (!strcmp(command, "reconnect"))
+ rc = abis_nm_bs11_bsc_disconnect(g_bts, 1);
+ else if (!strcmp(command, "software")
+ && bs11cfg_state != STATE_SWLOAD) {
+ bs11cfg_state = STATE_SWLOAD;
+ /* send software (FIXME: over A-bis?) */
+ if (file_is_readable(fname_software))
+ rc = abis_nm_bs11_load_swl(g_bts, fname_software,
+ win_size, param_forced,
+ swload_cbfn);
+ else
+ fprintf(stderr, "No valid Software file \"%s\"\n",
+ fname_software);
+ } else if (!strcmp(command, "delete-trx1")) {
+ printf("Locing BBSIG and PA objects of TRX1\n");
+ abis_nm_chg_adm_state(g_bts, NM_OC_BS11,
+ BS11_OBJ_BBSIG, 0, 1,
+ NM_STATE_LOCKED);
+ abis_nm_chg_adm_state(g_bts, NM_OC_BS11,
+ BS11_OBJ_PA, 0, 1,
+ NM_STATE_LOCKED);
+ sleep(1);
+ printf("Deleting BBSIG and PA objects of TRX1\n");
+ abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1);
+ abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1);
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+ } else if (!strcmp(command, "create-trx1")) {
+ create_trx1(g_bts);
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+ } else if (!strcmp(command, "pll-e1-locked")) {
+ abis_nm_bs11_set_pll_locked(g_bts, 1);
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+ } else if (!strcmp(command, "pll-standalone")) {
+ abis_nm_bs11_set_pll_locked(g_bts, 0);
+ sleep(1);
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ command = NULL;
+ } else if (!strcmp(command, "oml-tei")) {
+ abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML);
+ command = NULL;
+ } else if (!strcmp(command, "restart")) {
+ abis_nm_bs11_restart(g_bts);
+ command = NULL;
+ } else if (!strcmp(command, "query")) {
+ cmd_query();
+ }
+ }
+ break;
+ case BS11_STATE_NORMAL:
+ if (command) {
+ if (!strcmp(command, "reconnect"))
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ else if (!strcmp(command, "disconnect"))
+ abis_nm_bs11_bsc_disconnect(g_bts, 0);
+ else if (!strcmp(command, "query")) {
+ cmd_query();
+ }
+ } else if (param_disconnect) {
+ param_disconnect = 0;
+ abis_nm_bs11_bsc_disconnect(g_bts, 0);
+ if (param_restart) {
+ param_restart = 0;
+ abis_nm_bs11_restart(g_bts);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+/* handle a fully-received message/packet from the RS232 port */
+int handle_serial_msg(struct msgb *rx_msg)
+{
+ struct abis_om_hdr *oh;
+ struct abis_om_fom_hdr *foh;
+ struct tlv_parsed tp;
+ int rc = -1;
+
+#if 0
+ if (rx_msg->len < LAPD_HDR_LEN
+ + sizeof(struct abis_om_fom_hdr)
+ + sizeof(struct abis_om_hdr)) {
+ if (!memcmp(rx_msg->data + 2, too_fast,
+ sizeof(too_fast))) {
+ fprintf(stderr, "BS11 tells us we're too "
+ "fast, try --delay bigger than %u\n",
+ delay_ms);
+ return -E2BIG;
+ } else
+ fprintf(stderr, "unknown BS11 message\n");
+ }
+#endif
+
+ oh = (struct abis_om_hdr *) msgb_l2(rx_msg);
+ foh = (struct abis_om_fom_hdr *) oh->data;
+ switch (foh->msg_type) {
+ case NM_MT_BS11_LMT_LOGON_ACK:
+ printf("LMT LOGON: ACK\n\n");
+ if (bs11cfg_state == STATE_NONE)
+ bs11cfg_state = STATE_LOGON_ACK;
+ rc = abis_nm_bs11_get_state(g_bts);
+ break;
+ case NM_MT_BS11_LMT_LOGOFF_ACK:
+ printf("LMT LOGOFF: ACK\n");
+ exit(0);
+ break;
+ case NM_MT_BS11_GET_STATE_ACK:
+ rc = abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ print_state(&tp);
+ if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) &&
+ TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1)
+ rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE));
+ break;
+ case NM_MT_GET_ATTR_RESP:
+ printf("\n%sATTRIBUTES:\n", obj_name(foh));
+ abis_nm_tlv_parse(&tp, foh->data, oh->length-sizeof(*foh));
+ rc = print_attr(&tp);
+ //hexdump(foh->data, oh->length-sizeof(*foh));
+ break;
+ case NM_MT_BS11_SET_ATTR_ACK:
+ printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n",
+ foh->obj_class, foh->obj_inst.bts_nr,
+ foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
+ rc = 0;
+ break;
+ case NM_MT_BS11_SET_ATTR_NACK:
+ printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n",
+ foh->obj_class, foh->obj_inst.bts_nr,
+ foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
+ break;
+ default:
+ rc = abis_nm_rcvmsg(rx_msg);
+ }
+ if (rc < 0) {
+ perror("ERROR in main loop");
+ //break;
+ }
+ if (rc == 1)
+ return rc;
+
+ switch (bs11cfg_state) {
+ case STATE_NONE:
+ abis_nm_bs11_factory_logon(g_bts, 1);
+ break;
+ case STATE_LOGON_ACK:
+ bsc_schedule_timer(&status_timer, 5, 0);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
+{
+ return 0;
+}
+
+void status_timer_cb(void *data)
+{
+ abis_nm_bs11_get_state(g_bts);
+}
+
+static void print_banner(void)
+{
+ printf("bs11_config (C) 2009 by Harald Welte and Dieter Spaar\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+}
+
+static void print_help(void)
+{
+ printf("bs11_config [options] [command]\n");
+ printf("\nSupported options:\n");
+ printf("\t-h --help\t\t\tPrint this help text\n");
+ printf("\t-p --port </dev/ttyXXX>\t\tSpecify serial port\n");
+ printf("\t-s --software <file>\t\tSpecify Software file\n");
+ printf("\t-S --safety <file>\t\tSpecify Safety Load file\n");
+ printf("\t-d --delay <ms>\t\tSpecify delay in milliseconds\n");
+ printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n");
+ printf("\t-w --win-size <num>\t\tSpecify Window Size\n");
+ printf("\t-f --forced\t\t\tForce Software Load\n");
+ printf("\nSupported commands:\n");
+ printf("\tquery\t\tQuery the BS-11 about serial number and configuration\n");
+ printf("\tdisconnect\tDisconnect A-bis link (go into administrative state)\n");
+ printf("\tresconnect\tReconnect A-bis link (go into normal state)\n");
+ printf("\trestart\t\tRestart the BTS\n");
+ printf("\tsoftware\tDownload Software (only in administrative state)\n");
+ printf("\tcreate-trx1\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n");
+ printf("\tdelete-trx1\tDelete objects for TRX1\n");
+ printf("\tpll-e1-locked\tSet the PLL to be locked to E1 clock\n");
+ printf("\tpll-standalone\tSet the PLL to be in standalone mode\n");
+ printf("\toml-tei\tSet OML E1 TS and TEI\n");
+}
+
+static void handle_options(int argc, char **argv)
+{
+ int option_index = 0;
+ print_banner();
+
+ while (1) {
+ int c;
+ static struct option long_options[] = {
+ { "help", 0, 0, 'h' },
+ { "port", 1, 0, 'p' },
+ { "software", 1, 0, 's' },
+ { "safety", 1, 0, 'S' },
+ { "delay", 1, 0, 'd' },
+ { "disconnect", 0, 0, 'D' },
+ { "win-size", 1, 0, 'w' },
+ { "forced", 0, 0, 'f' },
+ { "restart", 0, 0, 'r' },
+ { "debug", 1, 0, 'b'},
+ };
+
+ c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:",
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_help();
+ exit(0);
+ case 'p':
+ serial_port = optarg;
+ break;
+ case 'b':
+ debug_parse_category_mask(optarg);
+ break;
+ case 's':
+ fname_software = optarg;
+ break;
+ case 'S':
+ fname_safety = optarg;
+ break;
+ case 'd':
+ delay_ms = atoi(optarg);
+ break;
+ case 'w':
+ win_size = atoi(optarg);
+ break;
+ case 'D':
+ param_disconnect = 1;
+ break;
+ case 'f':
+ param_forced = 1;
+ break;
+ case 'r':
+ param_disconnect = 1;
+ param_restart = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (optind < argc)
+ command = argv[optind];
+}
+
+static int num_sigint;
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "\nsignal %u received\n", signal);
+
+ switch (signal) {
+ case SIGINT:
+ num_sigint++;
+ abis_nm_bs11_factory_logon(g_bts, 0);
+ if (num_sigint >= 3)
+ exit(0);
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct gsm_network *gsmnet;
+ int rc;
+
+ handle_options(argc, argv);
+
+ gsmnet = gsm_network_init(1, 1, 1, GSM_BTS_TYPE_BS11);
+ if (!gsmnet) {
+ fprintf(stderr, "Unable to allocate gsm network\n");
+ exit(1);
+ }
+ g_bts = &gsmnet->bts[0];
+
+ rc = rs232_setup(serial_port, delay_ms, g_bts);
+ if (rc < 0) {
+ fprintf(stderr, "Problem setting up serial port\n");
+ exit(1);
+ }
+
+ signal(SIGINT, &signal_handler);
+
+ abis_nm_bs11_factory_logon(g_bts, 1);
+ //abis_nm_bs11_get_serno(g_bts);
+
+ status_timer.cb = status_timer_cb;
+
+ while (1) {
+ bsc_select_main(0);
+ }
+
+ abis_nm_bs11_factory_logon(g_bts, 0);
+
+ exit(0);
+}
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
new file mode 100644
index 000000000..7aa8b9aef
--- /dev/null
+++ b/openbsc/src/bsc_hack.c
@@ -0,0 +1,1159 @@
+/* A hackish minimal BSC (+MSC +HLR) implementation */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <openbsc/db.h>
+#include <openbsc/timer.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/select.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/debug.h>
+#include <openbsc/misdn.h>
+#include <openbsc/telnet_interface.h>
+#include <openbsc/paging.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/signal.h>
+
+/* global pointer to the gsm network data structure */
+static struct gsm_network *gsmnet;
+
+/* MCC and MNC for the Location Area Identifier */
+static int MCC = 1;
+static int MNC = 1;
+static int LAC = 1;
+static int ARFCN = HARDCODED_ARFCN;
+static int cardnr = 0;
+static int release_l2 = 0;
+static enum gsm_bts_type BTS_TYPE = GSM_BTS_TYPE_BS11;
+static const char *database_name = "hlr.sqlite3";
+
+/* The following definitions are for OM and NM packets that we cannot yet
+ * generate by code but we just pass on */
+
+// BTS Site Manager, SET ATTRIBUTES
+
+/*
+ Object Class: BTS Site Manager
+ Instance 1: FF
+ Instance 2: FF
+ Instance 3: FF
+SET ATTRIBUTES
+ sAbisExternalTime: 2007/09/08 14:36:11
+ omLAPDRelTimer: 30sec
+ shortLAPDIntTimer: 5sec
+ emergencyTimer1: 10 minutes
+ emergencyTimer2: 0 minutes
+*/
+
+unsigned char msg_1[] =
+{
+ 0xD0, 0x00, 0xFF, 0xFF, 0xFF,
+ NM_ATT_BS11_ABIS_EXT_TIME, 0x07,
+ 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE,
+ 0x02,
+ 0x00, 0x1E,
+ NM_ATT_BS11_SH_LAPD_INT_TIMER,
+ 0x01, 0x05,
+ 0x42, 0x02, 0x00, 0x0A,
+ 0x44, 0x02, 0x00, 0x00
+};
+
+// BTS, SET BTS ATTRIBUTES
+
+/*
+ Object Class: BTS
+ BTS relat. Number: 0
+ Instance 2: FF
+ Instance 3: FF
+SET BTS ATTRIBUTES
+ bsIdentityCode / BSIC:
+ PLMN_colour_code: 7h
+ BS_colour_code: 7h
+ BTS Air Timer T3105: 4 ,unit 10 ms
+ btsIsHopping: FALSE
+ periodCCCHLoadIndication: 1sec
+ thresholdCCCHLoadIndication: 0%
+ cellAllocationNumber: 00h = GSM 900
+ enableInterferenceClass: 00h = Disabled
+ fACCHQual: 6 (FACCH stealing flags minus 1)
+ intaveParameter: 31 SACCH multiframes
+ interferenceLevelBoundaries:
+ Interference Boundary 1: 0Ah
+ Interference Boundary 2: 0Fh
+ Interference Boundary 3: 14h
+ Interference Boundary 4: 19h
+ Interference Boundary 5: 1Eh
+ mSTxPwrMax: 11
+ GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm
+ DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm
+ PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm
+ 30=33dBm, 31=32dBm
+ ny1:
+ Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20
+ powerOutputThresholds:
+ Out Power Fault Threshold: -10 dB
+ Red Out Power Threshold: - 6 dB
+ Excessive Out Power Threshold: 5 dB
+ rACHBusyThreshold: -127 dBm
+ rACHLoadAveragingSlots: 250 ,number of RACH burst periods
+ rfResourceIndicationPeriod: 125 SACCH multiframes
+ T200:
+ SDCCH: 044 in 5 ms
+ FACCH/Full rate: 031 in 5 ms
+ FACCH/Half rate: 041 in 5 ms
+ SACCH with TCH SAPI0: 090 in 10 ms
+ SACCH with SDCCH: 090 in 10 ms
+ SDCCH with SAPI3: 090 in 5 ms
+ SACCH with TCH SAPI3: 135 in 10 ms
+ tSync: 9000 units of 10 msec
+ tTrau: 9000 units of 10 msec
+ enableUmLoopTest: 00h = disabled
+ enableExcessiveDistance: 00h = Disabled
+ excessiveDistance: 64km
+ hoppingMode: 00h = baseband hopping
+ cellType: 00h = Standard Cell
+ BCCH ARFCN / bCCHFrequency: 1
+*/
+
+unsigned char msg_2[] =
+{
+ 0x41, NM_OC_BTS, 0x00, 0xFF, 0xFF,
+ NM_ATT_BSIC, HARDCODED_BSIC,
+ NM_ATT_BTS_AIR_TIMER, 0x04,
+ NM_ATT_BS11_BTSLS_HOPPING, 0x00,
+ NM_ATT_CCCH_L_I_P, 0x01,
+ NM_ATT_CCCH_L_T, 0x00,
+ NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM,
+ NM_ATT_BS11_ENA_INTERF_CLASS, 0x01,
+ NM_ATT_BS11_FACCH_QUAL, 0x06,
+ /* interference avg. period in numbers of SACCH multifr */
+ NM_ATT_INTAVE_PARAM, 0x1F,
+ NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B,
+ NM_ATT_CCCH_L_T, 0x23,
+ NM_ATT_GSM_TIME, 0x28, 0x00,
+ NM_ATT_ADM_STATE, 0x03,
+ NM_ATT_RACH_B_THRESH, 0x7F,
+ NM_ATT_LDAVG_SLOTS, 0x00, 0xFA,
+ NM_ATT_BS11_RF_RES_IND_PER, 0x7D,
+ NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87,
+ NM_ATT_BS11_TSYNC, 0x23, 0x28,
+ NM_ATT_BS11_TTRAU, 0x23, 0x28,
+ NM_ATT_TEST_DUR, 0x01, 0x00,
+ NM_ATT_OUTST_ALARM, 0x01, 0x00,
+ NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40,
+ NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00,
+ NM_ATT_BS11_PLL, 0x01, 0x00,
+ NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/,
+};
+
+// Handover Recognition, SET ATTRIBUTES
+
+/*
+Illegal Contents GSM Formatted O&M Msg
+ Object Class: Handover Recognition
+ BTS relat. Number: 0
+ Instance 2: FF
+ Instance 3: FF
+SET ATTRIBUTES
+ enableDelayPowerBudgetHO: 00h = Disabled
+ enableDistanceHO: 00h = Disabled
+ enableInternalInterCellHandover: 00h = Disabled
+ enableInternalIntraCellHandover: 00h = Disabled
+ enablePowerBudgetHO: 00h = Disabled
+ enableRXLEVHO: 00h = Disabled
+ enableRXQUALHO: 00h = Disabled
+ hoAveragingDistance: 8 SACCH multiframes
+ hoAveragingLev:
+ A_LEV_HO: 8 SACCH multiframes
+ W_LEV_HO: 1 SACCH multiframes
+ hoAveragingPowerBudget: 16 SACCH multiframes
+ hoAveragingQual:
+ A_QUAL_HO: 8 SACCH multiframes
+ W_QUAL_HO: 2 SACCH multiframes
+ hoLowerThresholdLevDL: (10 - 110) dBm
+ hoLowerThresholdLevUL: (5 - 110) dBm
+ hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8%
+ hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8%
+ hoThresholdLevDLintra : (20 - 110) dBm
+ hoThresholdLevULintra: (20 - 110) dBm
+ hoThresholdMsRangeMax: 20 km
+ nCell: 06h
+ timerHORequest: 3 ,unit 2 SACCH multiframes
+*/
+
+unsigned char msg_3[] =
+{
+ 0xD0, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF,
+ 0xD0, 0x00,
+ 0x64, 0x00,
+ 0x67, 0x00,
+ 0x68, 0x00,
+ 0x6A, 0x00,
+ 0x6C, 0x00,
+ 0x6D, 0x00,
+ 0x6F, 0x08,
+ 0x70, 0x08, 0x01,
+ 0x71, 0x10, 0x10, 0x10,
+ 0x72, 0x08, 0x02,
+ 0x73, 0x0A,
+ 0x74, 0x05,
+ 0x75, 0x06,
+ 0x76, 0x06,
+ 0x78, 0x14,
+ 0x79, 0x14,
+ 0x7A, 0x14,
+ 0x7D, 0x06,
+ 0x92, 0x03, 0x20, 0x01, 0x00,
+ 0x45, 0x01, 0x00,
+ 0x48, 0x01, 0x00,
+ 0x5A, 0x01, 0x00,
+ 0x5B, 0x01, 0x05,
+ 0x5E, 0x01, 0x1A,
+ 0x5F, 0x01, 0x20,
+ 0x9D, 0x01, 0x00,
+ 0x47, 0x01, 0x00,
+ 0x5C, 0x01, 0x64,
+ 0x5D, 0x01, 0x1E,
+ 0x97, 0x01, 0x20,
+ 0xF7, 0x01, 0x3C,
+};
+
+// Power Control, SET ATTRIBUTES
+
+/*
+ Object Class: Power Control
+ BTS relat. Number: 0
+ Instance 2: FF
+ Instance 3: FF
+SET ATTRIBUTES
+ enableMsPowerControl: 00h = Disabled
+ enablePowerControlRLFW: 00h = Disabled
+ pcAveragingLev:
+ A_LEV_PC: 4 SACCH multiframes
+ W_LEV_PC: 1 SACCH multiframes
+ pcAveragingQual:
+ A_QUAL_PC: 4 SACCH multiframes
+ W_QUAL_PC: 2 SACCH multiframes
+ pcLowerThresholdLevDL: 0Fh
+ pcLowerThresholdLevUL: 0Ah
+ pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4%
+ pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4%
+ pcRLFThreshold: 0Ch
+ pcUpperThresholdLevDL: 14h
+ pcUpperThresholdLevUL: 0Fh
+ pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2%
+ pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2%
+ powerConfirm: 2 ,unit 2 SACCH multiframes
+ powerControlInterval: 2 ,unit 2 SACCH multiframes
+ powerIncrStepSize: 02h = 4 dB
+ powerRedStepSize: 01h = 2 dB
+ radioLinkTimeoutBs: 64 SACCH multiframes
+ enableBSPowerControl: 00h = disabled
+*/
+
+unsigned char msg_4[] =
+{
+ 0xD0, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF,
+ NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00,
+ NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00,
+ 0x7E, 0x04, 0x01,
+ 0x7F, 0x04, 0x02,
+ 0x80, 0x0F,
+ 0x81, 0x0A,
+ 0x82, 0x05,
+ 0x83, 0x05,
+ 0x84, 0x0C,
+ 0x85, 0x14,
+ 0x86, 0x0F,
+ 0x87, 0x04,
+ 0x88, 0x04,
+ 0x89, 0x02,
+ 0x8A, 0x02,
+ 0x8B, 0x02,
+ 0x8C, 0x01,
+ 0x8D, 0x40,
+ 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl
+};
+
+
+// Transceiver, SET TRX ATTRIBUTES (TRX 0)
+
+/*
+ Object Class: Transceiver
+ BTS relat. Number: 0
+ Tranceiver number: 0
+ Instance 3: FF
+SET TRX ATTRIBUTES
+ aRFCNList (HEX): 0001
+ txPwrMaxReduction: 00h = 30dB
+ radioMeasGran: 254 SACCH multiframes
+ radioMeasRep: 01h = enabled
+ memberOfEmergencyConfig: 01h = TRUE
+ trxArea: 00h = TRX doesn't belong to a concentric cell
+*/
+
+unsigned char msg_6[] =
+{
+ 0x44, NM_OC_RADIO_CARRIER, 0x00, 0x00, 0xFF,
+ NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/,
+ NM_ATT_RF_MAXPOWR_R, 0x00,
+ NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0xFE,
+ NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01,
+ NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01,
+ NM_ATT_BS11_TRX_AREA, 0x01, 0x00,
+};
+
+static unsigned char nanobts_attr_bts[] = {
+ NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73,
+ /* interference avg. period in numbers of SACCH multifr */
+ NM_ATT_INTAVE_PARAM, 0x06,
+ /* conn fail based on SACCH error rate */
+ NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10,
+ NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8,
+ NM_ATT_MAX_TA, 0x3f,
+ NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */
+ NM_ATT_CCCH_L_T, 10, /* percent */
+ NM_ATT_CCCH_L_I_P, 1, /* seconds */
+ NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */
+ NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */
+ NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */
+ NM_ATT_NY1, 10, /* 10 retransmissions of physical config */
+ NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
+ NM_ATT_BSIC, HARDCODED_BSIC,
+};
+
+static unsigned char nanobts_attr_radio[] = {
+ NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */
+ NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff,
+};
+
+static unsigned char nanobts_attr_e0[] = {
+ 0x85, 0x00,
+ 0x81, 0x0b, 0xbb, /* TCP PORT for RSL */
+};
+
+/* Callback function to be called whenever we get a GSM 12.21 state change event */
+int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
+{
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+
+ /* This is currently only required on nanoBTS */
+
+ switch (evt) {
+ case EVT_STATECHG_OPER:
+ switch (obj_class) {
+ case NM_OC_SITE_MANAGER:
+ bts = container_of(obj, struct gsm_bts, site_mgr);
+ if (old_state->operational != 2 && new_state->operational == 2) {
+ abis_nm_opstart(bts, NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff);
+ }
+ break;
+ case NM_OC_BTS:
+ bts = obj;
+ if (new_state->availability == 5) {
+ abis_nm_set_bts_attr(bts, nanobts_attr_bts,
+ sizeof(nanobts_attr_bts));
+ abis_nm_opstart(bts, NM_OC_BTS,
+ bts->bts_nr, 0xff, 0xff);
+ abis_nm_chg_adm_state(bts, NM_OC_BTS,
+ bts->bts_nr, 0xff, 0xff,
+ NM_STATE_UNLOCKED);
+ }
+ break;
+ case NM_OC_CHANNEL:
+ ts = obj;
+ trx = ts->trx;
+ if (new_state->availability == 5) {
+ if (ts->nr == 0 && trx == trx->bts->c0)
+ abis_nm_set_channel_attr(ts, NM_CHANC_BCCH_CBCH);
+ else
+ abis_nm_set_channel_attr(ts, NM_CHANC_TCHFull);
+ abis_nm_opstart(trx->bts, NM_OC_CHANNEL,
+ trx->bts->bts_nr, trx->nr, ts->nr);
+ abis_nm_chg_adm_state(trx->bts, NM_OC_CHANNEL,
+ trx->bts->bts_nr, trx->nr, ts->nr,
+ NM_STATE_UNLOCKED);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ //DEBUGP(DMM, "Unhandled state change in %s:%d\n", __func__, __LINE__);
+ break;
+ }
+ return 0;
+}
+
+/* Callback function to be called every time we receive a 12.21 SW activated report */
+static int sw_activ_rep(struct msgb *mb)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(mb);
+ struct gsm_bts_trx *trx = mb->trx;
+
+ switch (foh->obj_class) {
+ case NM_OC_BASEB_TRANSC:
+ /* TRX software is active, tell it to initiate RSL Link */
+ abis_nm_ipaccess_msg(trx->bts, 0xe0, NM_OC_BASEB_TRANSC,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ nanobts_attr_e0, sizeof(nanobts_attr_e0));
+ abis_nm_opstart(trx->bts, NM_OC_BASEB_TRANSC,
+ trx->bts->bts_nr, trx->nr, 0xff);
+ abis_nm_chg_adm_state(trx->bts, NM_OC_BASEB_TRANSC,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ NM_STATE_UNLOCKED);
+ break;
+ case NM_OC_RADIO_CARRIER:
+ abis_nm_set_radio_attr(trx, nanobts_attr_radio,
+ sizeof(nanobts_attr_radio));
+ abis_nm_opstart(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff);
+ abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER,
+ trx->bts->bts_nr, trx->nr, 0xff,
+ NM_STATE_UNLOCKED);
+ break;
+ }
+ return 0;
+}
+
+/* Callback function to be called every time we receive a signal from NM */
+static int nm_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ switch (signal) {
+ case S_NM_SW_ACTIV_REP:
+ return sw_activ_rep(signal_data);
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void bootstrap_om_nanobts(struct gsm_bts *bts)
+{
+ /* We don't do callback based bootstrapping, but event driven (see above) */
+}
+
+static void bootstrap_om_bs11(struct gsm_bts *bts)
+{
+ struct gsm_bts_trx *trx = &bts->trx[0];
+
+ /* stop sending event reports */
+ abis_nm_event_reports(bts, 0);
+
+ /* begin DB transmission */
+ abis_nm_bs11_db_transmission(bts, 1);
+
+ /* end DB transmission */
+ abis_nm_bs11_db_transmission(bts, 0);
+
+ /* Reset BTS Site manager resource */
+ abis_nm_bs11_reset_resource(bts);
+
+ /* begin DB transmission */
+ abis_nm_bs11_db_transmission(bts, 1);
+
+ abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
+ abis_nm_raw_msg(bts, sizeof(msg_2), msg_2); /* set BTS attr */
+ abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
+ abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */
+
+ /* Connect signalling of bts0/trx0 to e1_0/ts1/64kbps */
+ abis_nm_conn_terr_sign(trx, 0, 1, 0xff);
+ abis_nm_raw_msg(bts, sizeof(msg_6), msg_6); /* SET TRX ATTRIBUTES */
+
+ /* Use TEI 1 for signalling */
+ abis_nm_establish_tei(bts, 0, 0, 1, 0xff, 0x01);
+ abis_nm_set_channel_attr(&trx->ts[0], NM_CHANC_SDCCH_CBCH);
+
+#ifdef HAVE_TRX1
+ /* TRX 1 */
+ abis_nm_conn_terr_sign(&bts->trx[1], 0, 1, 0xff);
+ /* FIXME: TRX ATTRIBUTE */
+ abis_nm_establish_tei(bts, 0, 0, 1, 0xff, 0x02);
+#endif
+
+ /* SET CHANNEL ATTRIBUTE TS1 */
+ abis_nm_set_channel_attr(&trx->ts[1], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts1 to e1_0/ts2/b */
+ abis_nm_conn_terr_traf(&trx->ts[1], 0, 2, 1);
+
+ /* SET CHANNEL ATTRIBUTE TS2 */
+ abis_nm_set_channel_attr(&trx->ts[2], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts2 to e1_0/ts2/c */
+ abis_nm_conn_terr_traf(&trx->ts[2], 0, 2, 2);
+
+ /* SET CHANNEL ATTRIBUTE TS3 */
+ abis_nm_set_channel_attr(&trx->ts[3], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts3 to e1_0/ts2/d */
+ abis_nm_conn_terr_traf(&trx->ts[3], 0, 2, 3);
+
+ /* SET CHANNEL ATTRIBUTE TS4 */
+ abis_nm_set_channel_attr(&trx->ts[4], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts4 to e1_0/ts3/a */
+ abis_nm_conn_terr_traf(&trx->ts[4], 0, 3, 0);
+
+ /* SET CHANNEL ATTRIBUTE TS5 */
+ abis_nm_set_channel_attr(&trx->ts[5], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts5 to e1_0/ts3/b */
+ abis_nm_conn_terr_traf(&trx->ts[5], 0, 3, 1);
+
+ /* SET CHANNEL ATTRIBUTE TS6 */
+ abis_nm_set_channel_attr(&trx->ts[6], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts6 to e1_0/ts3/c */
+ abis_nm_conn_terr_traf(&trx->ts[6], 0, 3, 2);
+
+ /* SET CHANNEL ATTRIBUTE TS7 */
+ abis_nm_set_channel_attr(&trx->ts[7], NM_CHANC_TCHFull);
+ /* Connect traffic of bts0/trx0/ts7 to e1_0/ts3/d */
+ abis_nm_conn_terr_traf(&trx->ts[7], 0, 3, 3);
+
+ /* end DB transmission */
+ abis_nm_bs11_db_transmission(bts, 0);
+
+ /* Reset BTS Site manager resource */
+ abis_nm_bs11_reset_resource(bts);
+
+ /* restart sending event reports */
+ abis_nm_event_reports(bts, 1);
+}
+
+static void bootstrap_om(struct gsm_bts *bts)
+{
+ fprintf(stdout, "bootstrapping OML for BTS %u\n", bts->nr);
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_BS11:
+ bootstrap_om_bs11(bts);
+ break;
+ case GSM_BTS_TYPE_NANOBTS_900:
+ case GSM_BTS_TYPE_NANOBTS_1800:
+ bootstrap_om_nanobts(bts);
+ break;
+ default:
+ fprintf(stderr, "Unable to bootstrap OML: Unknown BTS type %d\n", bts->type);
+ }
+}
+
+static int shutdown_om(struct gsm_bts *bts)
+{
+ /* stop sending event reports */
+ abis_nm_event_reports(bts, 0);
+
+ /* begin DB transmission */
+ abis_nm_bs11_db_transmission(bts, 1);
+
+ /* end DB transmission */
+ abis_nm_bs11_db_transmission(bts, 0);
+
+ /* Reset BTS Site manager resource */
+ abis_nm_bs11_reset_resource(bts);
+
+ return 0;
+}
+
+static int shutdown_net(struct gsm_network *net)
+{
+ int i;
+ for (i = 0; i < net->num_bts; i++) {
+ int rc;
+ rc = shutdown_om(&net->bts[i]);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+struct bcch_info {
+ u_int8_t type;
+ u_int8_t len;
+ const u_int8_t *data;
+};
+
+/*
+SYSTEM INFORMATION TYPE 1
+ Cell channel description
+ Format-ID bit map 0
+ CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
+ RACH Control Parameters
+ maximum 7 retransmissions
+ 8 slots used to spread transmission
+ cell not barred for access
+ call reestablishment not allowed
+ Access Control Class = 0000
+*/
+static u_int8_t si1[] = {
+ /* header */0x55, 0x06, 0x19,
+ /* ccdesc */0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/,
+ /* rach */0xD5, 0x00, 0x00,
+ /* s1 reset*/0x2B
+};
+
+/*
+ SYSTEM INFORMATION TYPE 2
+ Neighbour Cells Description
+ EXT-IND: Carries the complete BA
+ BA-IND = 0
+ Format-ID bit map 0
+ CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ NCC permitted (NCC) = FF
+ RACH Control Parameters
+ maximum 7 retransmissions
+ 8 slots used to spread transmission
+ cell not barred for access
+ call reestablishment not allowed
+ Access Control Class = 0000
+*/
+static u_int8_t si2[] = {
+ /* header */0x59, 0x06, 0x1A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* ncc */0xFF,
+ /* rach*/0xD5, 0x00, 0x00
+};
+
+/*
+SYSTEM INFORMATION TYPE 3
+ Cell identity = 00001 (1h)
+ Location area identification
+ Mobile Country Code (MCC): 001
+ Mobile Network Code (MNC): 01
+ Location Area Code (LAC): 00001 (1h)
+ Control Channel Description
+ Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach
+ 0 blocks reserved for access grant
+ 1 channel used for CCCH, with SDCCH
+ 5 multiframes period for PAGING REQUEST
+ Time-out T3212 = 0
+ Cell Options BCCH
+ Power control indicator: not set
+ MSs shall not use uplink DTX
+ Radio link timeout = 36
+ Cell Selection Parameters
+ Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
+ max.TX power level MS may use for CCH = 2 <- according to GSM05.05 39dBm (max)
+ Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
+ Half rate support (NECI): New establishment causes are not supported
+ min.RX signal level for MS = 0
+ RACH Control Parameters
+ maximum 7 retransmissions
+ 8 slots used to spread transmission
+ cell not barred for access
+ call reestablishment not allowed
+ Access Control Class = 0000
+ SI 3 Rest Octets
+ Cell Bar Qualify (CBQ): 0
+ Cell Reselect Offset = 0 dB
+ Temporary Offset = 0 dB
+ Penalty Time = 20 s
+ System Information 2ter Indicator (2TI): 0 = not available
+ Early Classmark Sending Control (ECSC): 0 = forbidden
+ Scheduling Information is not sent in SYSTEM INFORMATION TYPE 9 on the BCCH
+*/
+static u_int8_t si3[] = {
+ /* header */0x49, 0x06, 0x1B,
+ /* cell */0x00, 0x01,
+ /* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
+ /* desc */0x01, 0x03, 0x00,
+ /* option*/0x28,
+ /* selection*/0x62, 0x00,
+ /* rach */0xD5, 0x00, 0x00,
+ /* reset*/0x80, 0x00, 0x00, 0x2B
+};
+
+/*
+SYSTEM INFORMATION TYPE 4
+ Location area identification
+ Mobile Country Code (MCC): 001
+ Mobile Network Code (MNC): 01
+ Location Area Code (LAC): 00001 (1h)
+ Cell Selection Parameters
+ Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
+ max.TX power level MS may use for CCH = 2
+ Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
+ Half rate support (NECI): New establishment causes are not supported
+ min.RX signal level for MS = 0
+ RACH Control Parameters
+ maximum 7 retransmissions
+ 8 slots used to spread transmission
+ cell not barred for access
+ call reestablishment not allowed
+ Access Control Class = 0000
+ Channel Description
+ Type = SDCCH/4[2]
+ Timeslot Number: 0
+ Training Sequence Code: 7h
+ ARFCN: 1
+ SI Rest Octets
+ Cell Bar Qualify (CBQ): 0
+ Cell Reselect Offset = 0 dB
+ Temporary Offset = 0 dB
+ Penalty Time = 20 s
+*/
+static u_int8_t si4[] = {
+ /* header */0x41, 0x06, 0x1C,
+ /* lai */0x00, 0xF1, 0x10, 0x00, 0x01,
+ /* sel */0x62, 0x00,
+ /* rach*/0xD5, 0x00, 0x00,
+ /* var */0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/, 0x80, 0x00, 0x00,
+ 0x2B, 0x2B, 0x2B
+};
+
+/*
+ SYSTEM INFORMATION TYPE 5
+ Neighbour Cells Description
+ EXT-IND: Carries the complete BA
+ BA-IND = 0
+ Format-ID bit map 0
+ CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+*/
+
+static u_int8_t si5[] = {
+ /* header without l2 len*/0x06, 0x1D,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+// SYSTEM INFORMATION TYPE 6
+
+/*
+SACCH FILLING
+ System Info Type: SYSTEM INFORMATION 6
+ L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF
+
+SYSTEM INFORMATION TYPE 6
+ Cell identity = 00001 (1h)
+ Location area identification
+ Mobile Country Code (MCC): 001
+ Mobile Network Code (MNC): 01
+ Location Area Code (LAC): 00001 (1h)
+ Cell Options SACCH
+ Power control indicator: not set
+ MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H.
+ Radio link timeout = 36
+ NCC permitted (NCC) = FF
+*/
+
+static u_int8_t si6[] = {
+ /* header */0x06, 0x1E,
+ /* cell id*/ 0x00, 0x01,
+ /* lai */ 0x00, 0xF1, 0x10, 0x00, 0x01,
+ /* options */ 0x28,
+ /* ncc */ 0xFF,
+};
+
+
+
+static const struct bcch_info bcch_infos[] = {
+ {
+ .type = RSL_SYSTEM_INFO_1,
+ .len = sizeof(si1),
+ .data = si1,
+ }, {
+ .type = RSL_SYSTEM_INFO_2,
+ .len = sizeof(si2),
+ .data = si2,
+ }, {
+ .type = RSL_SYSTEM_INFO_3,
+ .len = sizeof(si3),
+ .data = si3,
+ }, {
+ .type = RSL_SYSTEM_INFO_4,
+ .len = sizeof(si4),
+ .data = si4,
+ },
+};
+
+static_assert(sizeof(si1) == sizeof(struct gsm48_system_information_type_1), type1)
+static_assert(sizeof(si2) == sizeof(struct gsm48_system_information_type_2), type2)
+static_assert(sizeof(si3) == sizeof(struct gsm48_system_information_type_3), type3)
+static_assert(sizeof(si4) >= sizeof(struct gsm48_system_information_type_4), type4)
+static_assert(sizeof(si5) == sizeof(struct gsm48_system_information_type_5), type5)
+static_assert(sizeof(si6) >= sizeof(struct gsm48_system_information_type_6), type6)
+
+/* set all system information types */
+static int set_system_infos(struct gsm_bts_trx *trx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcch_infos); i++) {
+ rsl_bcch_info(trx, bcch_infos[i].type,
+ bcch_infos[i].data,
+ bcch_infos[i].len);
+ }
+ rsl_sacch_filling(trx, RSL_SYSTEM_INFO_5, si5, sizeof(si5));
+ rsl_sacch_filling(trx, RSL_SYSTEM_INFO_6, si6, sizeof(si6));
+
+ return 0;
+}
+
+/*
+ * Patch the various SYSTEM INFORMATION tables to update
+ * the LAI
+ */
+static void patch_tables(struct gsm_bts *bts)
+{
+ u_int8_t arfcn_low = bts->trx[0].arfcn & 0xff;
+ u_int8_t arfcn_high = (bts->trx[0].arfcn >> 8) & 0x0f;
+ /* covert the raw packet to the struct */
+ struct gsm48_system_information_type_3 *type_3 =
+ (struct gsm48_system_information_type_3*)&si3;
+ struct gsm48_system_information_type_4 *type_4 =
+ (struct gsm48_system_information_type_4*)&si4;
+ struct gsm48_system_information_type_6 *type_6 =
+ (struct gsm48_system_information_type_6*)&si6;
+ struct gsm48_loc_area_id lai;
+
+ gsm0408_generate_lai(&lai, bts->network->country_code,
+ bts->network->network_code,
+ bts->location_area_code);
+
+ /* assign the MCC and MNC */
+ type_3->lai = lai;
+ type_4->lai = lai;
+ type_6->lai = lai;
+
+ /* patch ARFCN into BTS Attributes */
+ msg_2[74] &= 0xf0;
+ msg_2[74] |= arfcn_high;
+ msg_2[75] = arfcn_low;
+ nanobts_attr_bts[42] &= 0xf0;
+ nanobts_attr_bts[42] |= arfcn_high;
+ nanobts_attr_bts[43] = arfcn_low;
+
+ /* patch ARFCN into TRX Attributes */
+ msg_6[7] &= 0xf0;
+ msg_6[7] |= arfcn_high;
+ msg_6[8] = arfcn_low;
+ nanobts_attr_radio[5] &= 0xf0;
+ nanobts_attr_radio[5] |= arfcn_high;
+ nanobts_attr_radio[6] = arfcn_low;
+
+ type_4->data[2] &= 0xf0;
+ type_4->data[2] |= arfcn_high;
+ type_4->data[3] = arfcn_low;
+
+ /* patch Control Channel Description 10.5.2.11 */
+ type_3->control_channel_desc = bts->chan_desc;
+
+ /* patch BSIC */
+ msg_2[6] = bts->bsic;
+ nanobts_attr_bts[sizeof(nanobts_attr_bts)-1] = bts->bsic;
+}
+
+
+static void bootstrap_rsl(struct gsm_bts_trx *trx)
+{
+ fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) "
+ "using MCC=%u MNC=%u\n", trx->nr, trx->bts->nr, MCC, MNC);
+ set_system_infos(trx);
+}
+
+void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
+{
+ switch (event) {
+ case EVT_E1_TEI_UP:
+ switch (type) {
+ case E1INP_SIGN_OML:
+ bootstrap_om(trx->bts);
+ break;
+ case E1INP_SIGN_RSL:
+ bootstrap_rsl(trx);
+ break;
+ default:
+ break;
+ }
+ break;
+ case EVT_E1_TEI_DN:
+ fprintf(stderr, "Lost some E1 TEI link\n");
+ /* FIXME: deal with TEI or L1 link loss */
+ break;
+ default:
+ break;
+ }
+}
+
+static int bootstrap_bts(struct gsm_bts *bts)
+{
+ bts->location_area_code = LAC;
+ bts->trx[0].arfcn = ARFCN;
+
+ /* Control Channel Description */
+ memset(&bts->chan_desc, 0, sizeof(struct gsm48_control_channel_descr));
+ bts->chan_desc.att = 1;
+ bts->chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C;
+ bts->chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5;
+ bts->chan_desc.t3212 = 0;
+
+ patch_tables(bts);
+
+ paging_init(bts);
+
+ if (bts->type == GSM_BTS_TYPE_BS11) {
+ struct gsm_bts_trx *trx = &bts->trx[0];
+ set_ts_e1link(&trx->ts[0], 0, 1, 0xff);
+ set_ts_e1link(&trx->ts[1], 0, 2, 1);
+ set_ts_e1link(&trx->ts[2], 0, 2, 2);
+ set_ts_e1link(&trx->ts[3], 0, 2, 3);
+ set_ts_e1link(&trx->ts[4], 0, 3, 0);
+ set_ts_e1link(&trx->ts[5], 0, 3, 1);
+ set_ts_e1link(&trx->ts[6], 0, 3, 2);
+ set_ts_e1link(&trx->ts[7], 0, 3, 3);
+#ifdef HAVE_TRX1
+ /* TRX 1 */
+ trx = &bts->trx[1];
+ set_ts_e1link(&trx->ts[0], 0, 1, 0xff);
+ set_ts_e1link(&trx->ts[1], 0, 2, 1);
+ set_ts_e1link(&trx->ts[2], 0, 2, 2);
+ set_ts_e1link(&trx->ts[3], 0, 2, 3);
+ set_ts_e1link(&trx->ts[4], 0, 3, 0);
+ set_ts_e1link(&trx->ts[5], 0, 3, 1);
+ set_ts_e1link(&trx->ts[6], 0, 3, 2);
+ set_ts_e1link(&trx->ts[7], 0, 3, 3);
+#endif
+ }
+
+ return 0;
+}
+
+static int bootstrap_network(void)
+{
+ struct gsm_bts *bts;
+
+ /* initialize our data structures */
+ gsmnet = gsm_network_init(2, BTS_TYPE, MCC, MNC);
+ if (!gsmnet)
+ return -ENOMEM;
+
+ gsmnet->name_long = "OpenBSC";
+ gsmnet->name_short = "OpenBSC";
+
+ bts = &gsmnet->bts[0];
+ bootstrap_bts(bts);
+
+ if (db_init(database_name)) {
+ printf("DB: Failed to init database. Please check the option settings.\n");
+ return -1;
+ }
+ printf("DB: Database initialized.\n");
+
+ if (db_prepare()) {
+ printf("DB: Failed to prepare database.\n");
+ return -1;
+ }
+ printf("DB: Database prepared.\n");
+
+ telnet_init(gsmnet, 4242);
+
+ register_signal_handler(SS_NM, nm_sig_cb, NULL);
+
+ /* E1 mISDN input setup */
+ if (BTS_TYPE == GSM_BTS_TYPE_BS11) {
+ gsmnet->num_bts = 1;
+ return e1_config(bts, cardnr, release_l2);
+ } else {
+ /* FIXME: do this dynamic */
+ bts->ip_access.site_id = 1801;
+ bts->ip_access.bts_id = 0;
+ bts = &gsmnet->bts[1];
+ bootstrap_bts(bts);
+ bts->ip_access.site_id = 1800;
+ bts->ip_access.bts_id = 0;
+ return ipaccess_setup(gsmnet);
+ }
+}
+
+static void create_pcap_file(char *file)
+{
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode);
+
+ if (fd < 0) {
+ perror("Failed to open file for pcap");
+ return;
+ }
+
+ e1_set_pcap_fd(fd);
+}
+
+static void print_usage()
+{
+ printf("Usage: bsc_hack\n");
+}
+
+static void print_help()
+{
+ printf(" Some useful help...\n");
+ printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
+ printf(" -s --disable-color\n");
+ printf(" -n --network-code number(MNC) \n");
+ printf(" -c --country-code number (MCC) \n");
+ printf(" -L --location-area-code number (LAC) \n");
+ printf(" -f --arfcn number The frequency ARFCN\n");
+ printf(" -l --database db-name The database to use\n");
+ printf(" -a --authorize-everyone Allow everyone into the network.\n");
+ printf(" -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\n");
+ printf(" -p --pcap file The filename of the pcap file\n");
+ printf(" -t --bts-type type The BTS type (bs11, nanobts900, nanobts1800)\n");
+ printf(" -C --cardnr number For bs11 select E1 card number other than 0\n");
+ printf(" -R --release-l2 Releases mISDN layer 2 after exit, to unload driver.\n");
+ printf(" -h --help this text\n");
+}
+
+static void handle_options(int argc, char** argv)
+{
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"debug", 1, 0, 'd'},
+ {"disable-color", 0, 0, 's'},
+ {"network-code", 1, 0, 'n'},
+ {"country-code", 1, 0, 'c'},
+ {"location-area-code", 1, 0, 'L'},
+ {"database", 1, 0, 'l'},
+ {"authorize-everyone", 0, 0, 'a'},
+ {"reject-cause", 1, 0, 'r'},
+ {"pcap", 1, 0, 'p'},
+ {"arfcn", 1, 0, 'f'},
+ {"bts-type", 1, 0, 't'},
+ {"cardnr", 1, 0, 'C'},
+ {"release-l2", 0, 0, 'R'},
+ {"timestamp", 0, 0, 'T'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:T",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ print_help();
+ exit(0);
+ case 's':
+ debug_use_color(0);
+ break;
+ case 'd':
+ debug_parse_category_mask(optarg);
+ break;
+ case 'n':
+ MNC = atoi(optarg);
+ break;
+ case 'c':
+ MCC = atoi(optarg);
+ break;
+ case 'L':
+ LAC = atoi(optarg);
+ break;
+ case 'f':
+ ARFCN = atoi(optarg);
+ break;
+ case 'l':
+ database_name = strdup(optarg);
+ break;
+ case 'a':
+ gsm0408_allow_everyone(1);
+ break;
+ case 'r':
+ gsm0408_set_reject_cause(atoi(optarg));
+ break;
+ case 'p':
+ create_pcap_file(optarg);
+ break;
+ case 't':
+ BTS_TYPE = parse_btstype(optarg);
+ break;
+ case 'C':
+ cardnr = atoi(optarg);
+ break;
+ case 'R':
+ release_l2 = 1;
+ break;
+ case 'T':
+ debug_timestamp(1);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGHUP:
+ case SIGABRT:
+ shutdown_net(gsmnet);
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ /* parse options */
+ handle_options(argc, argv);
+
+ /* seed the PRNG */
+ srand(time(NULL));
+
+ rc = bootstrap_network();
+ if (rc < 0)
+ exit(1);
+
+ signal(SIGHUP, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+
+ while (1) {
+ bsc_select_main(0);
+ }
+}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
new file mode 100644
index 000000000..77a4f57bc
--- /dev/null
+++ b/openbsc/src/chan_alloc.c
@@ -0,0 +1,256 @@
+/* GSM Channel allocation routines
+ *
+ * (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/debug.h>
+#include <openbsc/signal.h>
+
+static void auto_release_channel(void *_lchan);
+
+struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
+ enum gsm_phys_chan_config pchan)
+{
+ struct gsm_bts_trx *trx = &bts->trx[0];
+ struct gsm_bts_trx_ts *ts = &trx->ts[0];
+
+ if (pchan != GSM_PCHAN_CCCH &&
+ pchan != GSM_PCHAN_CCCH_SDCCH4)
+ return NULL;
+
+ if (ts->pchan != GSM_PCHAN_NONE)
+ return NULL;
+
+ ts->pchan = pchan;
+
+ return ts;
+}
+
+static const enum abis_nm_chan_comb chcomb4pchan[] = {
+ [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH,
+ [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCCHComb,
+ [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull,
+ [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH,
+ /* FIXME: bounds check */
+};
+
+/* Allocate a logical channel (TS) */
+struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
+ enum gsm_phys_chan_config pchan)
+{
+ int i, j;
+ for (i = 0; i < bts->num_trx; i++) {
+ struct gsm_bts_trx *trx = &bts->trx[i];
+ int from, to;
+
+ /* the following constraints are pure policy,
+ * no requirement to put this restriction in place */
+ switch (pchan) {
+ case GSM_PCHAN_CCCH:
+ case GSM_PCHAN_CCCH_SDCCH4:
+ from = 0; to = 0;
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ from = 1; to = 1;
+ break;
+ case GSM_PCHAN_TCH_F:
+ case GSM_PCHAN_TCH_H:
+ from = 2; to = 7;
+ break;
+ default:
+ return NULL;
+ }
+
+ for (j = from; j <= to; j++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[j];
+ if (ts->pchan == GSM_PCHAN_NONE) {
+ ts->pchan = pchan;
+ /* set channel attribute on OML */
+ abis_nm_set_channel_attr(ts, chcomb4pchan[pchan]);
+ return ts;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Free a physical channel (TS) */
+void ts_free(struct gsm_bts_trx_ts *ts)
+{
+ ts->pchan = GSM_PCHAN_NONE;
+}
+
+static const u_int8_t subslots_per_pchan[] = {
+ [GSM_PCHAN_NONE] = 0,
+ [GSM_PCHAN_CCCH] = 0,
+ [GSM_PCHAN_CCCH_SDCCH4] = 4,
+ [GSM_PCHAN_TCH_F] = 1,
+ [GSM_PCHAN_TCH_H] = 2,
+ [GSM_PCHAN_SDCCH8_SACCH8C] = 8.
+};
+
+static struct gsm_lchan *
+_lc_find(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
+{
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ int i, j, ss;
+ for (i = 0; i < bts->num_trx; i++) {
+ trx = &bts->trx[i];
+ for (j = 0; j < 8; j++) {
+ ts = &trx->ts[j];
+ if (ts->pchan != pchan)
+ continue;
+ /* check if all sub-slots are allocated yet */
+ for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) {
+ struct gsm_lchan *lc = &ts->lchan[ss];
+ if (lc->type == GSM_LCHAN_NONE)
+ return lc;
+ }
+ }
+ }
+ /* we cannot allocate more of these */
+ if (pchan == GSM_PCHAN_CCCH_SDCCH4)
+ return NULL;
+
+ /* if we've reached here, we need to allocate a new physical
+ * channel for the logical channel type requested */
+ ts = ts_alloc(bts, pchan);
+ if (!ts) {
+ /* no more radio resources */
+ return NULL;
+ }
+ return &ts->lchan[0];
+}
+
+/* Allocate a logical channel */
+struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
+{
+ struct gsm_lchan *lchan = NULL;
+
+ switch (type) {
+ case GSM_LCHAN_SDCCH:
+ lchan = _lc_find(bts, GSM_PCHAN_CCCH_SDCCH4);
+ if (lchan == NULL)
+ lchan = _lc_find(bts, GSM_PCHAN_SDCCH8_SACCH8C);
+ break;
+ case GSM_LCHAN_TCH_F:
+ lchan = _lc_find(bts, GSM_PCHAN_TCH_F);
+ break;
+ case GSM_LCHAN_TCH_H:
+ lchan =_lc_find(bts, GSM_PCHAN_TCH_H);
+ break;
+ default:
+ fprintf(stderr, "Unknown gsm_chan_t %u\n", type);
+ }
+
+ if (lchan) {
+ lchan->type = type;
+ lchan->use_count = 0;
+
+ /* Configure the time and start it so it will be closed */
+ lchan->release_timer.cb = auto_release_channel;
+ lchan->release_timer.data = lchan;
+ bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
+ }
+
+ return lchan;
+}
+
+/* Free a logical channel */
+void lchan_free(struct gsm_lchan *lchan)
+{
+ lchan->type = GSM_LCHAN_NONE;
+ if (lchan->subscr) {
+ subscr_put(lchan->subscr);
+ lchan->subscr = 0;
+ }
+
+ /* We might kill an active channel... */
+ if (lchan->use_count != 0) {
+ dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan);
+ lchan->use_count = 0;
+ }
+
+ /* stop the timer */
+ bsc_del_timer(&lchan->release_timer);
+
+ /* FIXME: ts_free() the timeslot, if we're the last logical
+ * channel using it */
+}
+
+/* Consider releasing the channel now */
+int lchan_auto_release(struct gsm_lchan *lchan)
+{
+ if (lchan->use_count > 0) {
+ return 0;
+ }
+
+ /* Assume we have GSM04.08 running and send a release */
+ if (lchan->subscr) {
+ gsm48_send_rr_release(lchan);
+ }
+
+ /* spoofed? message */
+ if (lchan->use_count < 0) {
+ DEBUGP(DRLL, "Channel count is negative: %d\n", lchan->use_count);
+ }
+
+ DEBUGP(DRLL, "Recycling the channel with: %d (%x)\n", lchan->nr, lchan->nr);
+ rsl_chan_release(lchan);
+ return 1;
+}
+
+/* Auto release the channel when the use count is zero */
+static void auto_release_channel(void *_lchan)
+{
+ struct gsm_lchan *lchan = _lchan;
+
+ if (!lchan_auto_release(lchan))
+ bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
+}
+
+struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) {
+ int trx, ts_no, lchan_no;
+
+ for (trx = 0; trx < bts->num_trx; ++trx) {
+ for (ts_no = 0; ts_no < 8; ++ts_no) {
+ for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) {
+ struct gsm_lchan *lchan =
+ &bts->trx[trx].ts[ts_no].lchan[lchan_no];
+ if (subscr == lchan->subscr)
+ return lchan;
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
new file mode 100644
index 000000000..600699ae7
--- /dev/null
+++ b/openbsc/src/db.c
@@ -0,0 +1,464 @@
+/* Simple HLR/VLR database backend using dbi */
+/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/db.h>
+
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dbi/dbi.h>
+
+static char *db_basename = NULL;
+static char *db_dirname = NULL;
+static dbi_conn conn;
+
+static char *create_stmts[] = {
+ "CREATE TABLE IF NOT EXISTS Meta ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "key TEXT UNIQUE NOT NULL, "
+ "value TEXT NOT NULL"
+ ")",
+ "INSERT OR IGNORE INTO Meta "
+ "(key, value) "
+ "VALUES "
+ "('revision', '1')",
+ "CREATE TABLE IF NOT EXISTS Subscriber ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "created TIMESTAMP NOT NULL, "
+ "updated TIMESTAMP NOT NULL, "
+ "imsi NUMERIC UNIQUE NOT NULL, "
+ "name TEXT, "
+ "extension TEXT UNIQUE, "
+ "authorized INTEGER NOT NULL DEFAULT 0, "
+ "tmsi TEXT UNIQUE, "
+ "lac INTEGER NOT NULL DEFAULT 0"
+ ")",
+ "CREATE TABLE IF NOT EXISTS Equipment ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "created TIMESTAMP NOT NULL, "
+ "updated TIMESTAMP NOT NULL, "
+ "name TEXT, "
+ "imei NUMERIC UNIQUE NOT NULL"
+ ")",
+ "CREATE TABLE IF NOT EXISTS EquipmentWatch ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "created TIMESTAMP NOT NULL, "
+ "updated TIMESTAMP NOT NULL, "
+ "subscriber_id NUMERIC NOT NULL, "
+ "equipment_id NUMERIC NOT NULL, "
+ "UNIQUE (subscriber_id, equipment_id) "
+ ")",
+ "CREATE TABLE IF NOT EXISTS SMS ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "created TIMESTAMP NOT NULL, "
+ "sent TIMESTAMP, "
+ "sender_id NUMERIC NOT NULL, "
+ "receiver_id NUMERIC NOT NULL, "
+ "header NUMERIC, "
+ "text TEXT NOT NULL "
+ ")",
+ "CREATE TABLE IF NOT EXISTS VLR ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "created TIMESTAMP NOT NULL, "
+ "updated TIMESTAMP NOT NULL, "
+ "subscriber_id NUMERIC UNIQUE NOT NULL, "
+ "last_bts NUMERIC NOT NULL "
+ ")",
+};
+
+void db_error_func(dbi_conn conn, void* data) {
+ const char* msg;
+ dbi_conn_error(conn, &msg);
+ printf("DBI: %s\n", msg);
+}
+
+int db_init(const char *name) {
+ dbi_initialize(NULL);
+ conn = dbi_conn_new("sqlite3");
+ if (conn==NULL) {
+ printf("DB: Failed to create connection.\n");
+ return 1;
+ }
+
+ dbi_conn_error_handler( conn, db_error_func, NULL );
+
+ /* MySQL
+ dbi_conn_set_option(conn, "host", "localhost");
+ dbi_conn_set_option(conn, "username", "your_name");
+ dbi_conn_set_option(conn, "password", "your_password");
+ dbi_conn_set_option(conn, "dbname", "your_dbname");
+ dbi_conn_set_option(conn, "encoding", "UTF-8");
+ */
+
+ /* SqLite 3 */
+ db_basename = strdup(name);
+ db_dirname = strdup(name);
+ dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
+ dbi_conn_set_option(conn, "dbname", basename(db_basename));
+
+ if (dbi_conn_connect(conn) < 0) {
+ free(db_dirname);
+ free(db_basename);
+ db_dirname = db_basename = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+int db_prepare() {
+ dbi_result result;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(create_stmts); i++) {
+ result = dbi_conn_query(conn, create_stmts[i]);
+ if (result==NULL) {
+ printf("DB: Failed to create some table.\n");
+ return 1;
+ }
+ dbi_result_free(result);
+ }
+
+ return 0;
+}
+
+int db_fini() {
+ dbi_conn_close(conn);
+ dbi_shutdown();
+
+ if (db_dirname)
+ free(db_dirname);
+ if (db_basename)
+ free(db_basename);
+ return 0;
+}
+
+struct gsm_subscriber* db_create_subscriber(char *imsi) {
+ dbi_result result;
+ struct gsm_subscriber* subscr;
+
+ /* Is this subscriber known in the db? */
+ subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
+ if (subscr) {
+ result = dbi_conn_queryf(conn,
+ "UPDATE Subscriber set updated = datetime('now') "
+ "WHERE imsi = %s " , imsi);
+ if (result==NULL) {
+ printf("DB: failed to update timestamp\n");
+ } else {
+ dbi_result_free(result);
+ }
+ return subscr;
+ }
+
+ subscr = subscr_alloc();
+ if (!subscr)
+ return NULL;
+ result = dbi_conn_queryf(conn,
+ "INSERT INTO Subscriber "
+ "(imsi, created, updated) "
+ "VALUES "
+ "(%s, datetime('now'), datetime('now')) ",
+ imsi
+ );
+ if (result==NULL) {
+ printf("DB: Failed to create Subscriber by IMSI.\n");
+ }
+ subscr->id = dbi_conn_sequence_last(conn, NULL);
+ strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1);
+ dbi_result_free(result);
+ printf("DB: New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi);
+ return subscr;
+}
+
+struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, const char *id) {
+ dbi_result result;
+ const char *string;
+ char *quoted;
+ struct gsm_subscriber *subscr;
+
+ switch (field) {
+ case GSM_SUBSCRIBER_IMSI:
+ dbi_conn_quote_string_copy(conn, id, &quoted);
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM Subscriber "
+ "WHERE imsi = %s ",
+ quoted
+ );
+ free(quoted);
+ break;
+ case GSM_SUBSCRIBER_TMSI:
+ dbi_conn_quote_string_copy(conn, id, &quoted);
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM Subscriber "
+ "WHERE tmsi = %s ",
+ quoted
+ );
+ free(quoted);
+ break;
+ case GSM_SUBSCRIBER_EXTENSION:
+ dbi_conn_quote_string_copy(conn, id, &quoted);
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM Subscriber "
+ "WHERE extension = %s ",
+ quoted
+ );
+ free(quoted);
+ break;
+ default:
+ printf("DB: Unknown query selector for Subscriber.\n");
+ return NULL;
+ }
+ if (result==NULL) {
+ printf("DB: Failed to query Subscriber.\n");
+ return NULL;
+ }
+ if (!dbi_result_next_row(result)) {
+ printf("DB: Failed to find the Subscriber. '%u' '%s'\n",
+ field, id);
+ dbi_result_free(result);
+ return NULL;
+ }
+
+ subscr = subscr_alloc();
+ subscr->id = dbi_result_get_ulonglong(result, "id");
+ string = dbi_result_get_string(result, "imsi");
+ if (string)
+ strncpy(subscr->imsi, string, GSM_IMSI_LENGTH);
+
+ string = dbi_result_get_string(result, "tmsi");
+ if (string)
+ strncpy(subscr->tmsi, string, GSM_TMSI_LENGTH);
+
+ string = dbi_result_get_string(result, "name");
+ if (string)
+ strncpy(subscr->name, string, GSM_NAME_LENGTH);
+
+ string = dbi_result_get_string(result, "extension");
+ if (string)
+ strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH);
+
+ subscr->lac = dbi_result_get_uint(result, "lac");
+ subscr->authorized = dbi_result_get_uint(result, "authorized");
+ printf("DB: Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %s, EXTEN '%s', LAC %hu, AUTH %u\n",
+ subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
+ subscr->lac, subscr->authorized);
+ dbi_result_free(result);
+ return subscr;
+}
+
+int db_sync_subscriber(struct gsm_subscriber* subscriber) {
+ dbi_result result;
+ result = dbi_conn_queryf(conn,
+ "UPDATE Subscriber "
+ "SET updated = datetime('now'), "
+ "tmsi = '%s', "
+ "lac = %i, "
+ "authorized = %i "
+ "WHERE imsi = %s ",
+ subscriber->tmsi, subscriber->lac, subscriber->authorized, subscriber->imsi
+ );
+
+ if (result==NULL) {
+ printf("DB: Failed to update Subscriber (by IMSI).\n");
+ return 1;
+ }
+ dbi_result_free(result);
+ return 0;
+}
+
+int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber) {
+ dbi_result result=NULL;
+ char* tmsi_quoted;
+ for (;;) {
+ sprintf(subscriber->tmsi, "%i", rand());
+ dbi_conn_quote_string_copy(conn, subscriber->tmsi, &tmsi_quoted);
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM Subscriber "
+ "WHERE tmsi = %s ",
+ tmsi_quoted
+ );
+ free(tmsi_quoted);
+ if (result==NULL) {
+ printf("DB: Failed to query Subscriber while allocating new TMSI.\n");
+ return 1;
+ }
+ if (dbi_result_get_numrows(result)){
+ dbi_result_free(result);
+ continue;
+ }
+ if (!dbi_result_next_row(result)) {
+ dbi_result_free(result);
+ printf("DB: Allocated TMSI %s for IMSI %s.\n", subscriber->tmsi, subscriber->imsi);
+ return db_sync_subscriber(subscriber);
+ }
+ dbi_result_free(result);
+ }
+ return 0;
+}
+
+int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IMEI_LENGTH]) {
+ u_int64_t equipment_id, watch_id;
+ dbi_result result;
+
+ result = dbi_conn_queryf(conn,
+ "INSERT OR IGNORE INTO Equipment "
+ "(imei, created, updated) "
+ "VALUES "
+ "(%s, datetime('now'), datetime('now')) ",
+ imei
+ );
+ if (result==NULL) {
+ printf("DB: Failed to create Equipment by IMEI.\n");
+ return 1;
+ }
+ equipment_id = 0;
+ if (dbi_result_get_numrows_affected(result)) {
+ equipment_id = dbi_conn_sequence_last(conn, NULL);
+ }
+ dbi_result_free(result);
+ if (equipment_id) {
+ printf("DB: New Equipment: ID %llu, IMEI %s\n", equipment_id, imei);
+ }
+ else {
+ result = dbi_conn_queryf(conn,
+ "SELECT id FROM Equipment "
+ "WHERE imei = %s ",
+ imei
+ );
+ if (result==NULL) {
+ printf("DB: Failed to query Equipment by IMEI.\n");
+ return 1;
+ }
+ if (!dbi_result_next_row(result)) {
+ printf("DB: Failed to find the Equipment.\n");
+ dbi_result_free(result);
+ return 1;
+ }
+ equipment_id = dbi_result_get_ulonglong(result, "id");
+ dbi_result_free(result);
+ }
+
+ result = dbi_conn_queryf(conn,
+ "INSERT OR IGNORE INTO EquipmentWatch "
+ "(subscriber_id, equipment_id, created, updated) "
+ "VALUES "
+ "(%llu, %llu, datetime('now'), datetime('now')) ",
+ subscriber->id, equipment_id
+ );
+ if (result==NULL) {
+ printf("DB: Failed to create EquipmentWatch.\n");
+ return 1;
+ }
+ watch_id = 0;
+ if (dbi_result_get_numrows_affected(result)) {
+ watch_id = dbi_conn_sequence_last(conn, NULL);
+ }
+ dbi_result_free(result);
+ if (watch_id) {
+ printf("DB: New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
+ }
+ else {
+ result = dbi_conn_queryf(conn,
+ "UPDATE EquipmentWatch "
+ "SET updated = datetime('now') "
+ "WHERE subscriber_id = %llu AND equipment_id = %llu ",
+ subscriber->id, equipment_id
+ );
+ if (result==NULL) {
+ printf("DB: Failed to update EquipmentWatch.\n");
+ return 1;
+ }
+ dbi_result_free(result);
+ printf("DB: Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", equipment_id, subscriber->imsi, imei);
+ }
+
+ return 0;
+}
+
+/* store an [unsent] SMS to the database */
+int db_sms_store(struct gsm_sms *sms)
+{
+ dbi_result result;
+ char *q_text;
+
+ dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
+ result = dbi_conn_queryf(conn,
+ "INSERT INTO SMS "
+ "(created,sender_id,receiver_id,header,text) VALUES "
+ "(datetime('now'),%llu,%llu,%s,%s)",
+ sms->sender->id,
+ sms->receiver ? sms->receiver->id : 0,
+ NULL, q_text);
+ free(q_text);
+
+ if (!result)
+ return -EIO;
+
+ dbi_result_free(result);
+ return 0;
+}
+
+/* retrieve the next unsent SMS with ID >= min_id */
+struct gsm_sms *db_sms_get_unsent(int min_id)
+{
+ dbi_result result;
+ struct gsm_sms *sms = malloc(sizeof(*sms));
+
+ if (!sms) {
+ free(sms);
+ return NULL;
+ }
+
+ result = dbi_conn_queryf(conn,
+ "SELECT * FROM SMS "
+ "WHERE id >= %llu ORDER BY id", min_id);
+ if (!result) {
+ free(sms);
+ return NULL;
+ }
+
+ /* FIXME: fill gsm_sms from database */
+
+ dbi_result_free(result);
+ return sms;
+}
+
+/* mark a given SMS as read */
+int db_sms_mark_sent(struct gsm_sms *sms)
+{
+ dbi_result result;
+
+ result = dbi_conn_queryf(conn,
+ "UPDATE SMS "
+ "SET sent = datetime('now') "
+ "WHERE id = %llu", sms->id);
+ if (!result) {
+ printf("DB: Failed to mark SMS %llu as sent.\n", sms->id);
+ return 1;
+ }
+
+ dbi_result_free(result);
+ return 0;
+}
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
new file mode 100644
index 000000000..aeb993097
--- /dev/null
+++ b/openbsc/src/debug.c
@@ -0,0 +1,161 @@
+/* Debugging/Logging support code */
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+
+#include <openbsc/debug.h>
+
+unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB);
+
+struct debug_info {
+ const char *name;
+ const char *color;
+ const char *description;
+ int number;
+};
+
+#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
+ { .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
+
+#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
+
+static const struct debug_info debug_info[] = {
+ DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
+ DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
+ DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "")
+ DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "")
+ DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "")
+ DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "")
+ DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "")
+ DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "")
+ DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "")
+ DEBUG_CATEGORY(DINP, "DINP", "", "")
+ DEBUG_CATEGORY(DMI, "DMI", "", "")
+ DEBUG_CATEGORY(DMIB, "DMIB", "", "")
+ DEBUG_CATEGORY(DMUX, "DMUX", "", "")
+};
+
+static int use_color = 1;
+
+void debug_use_color(int color)
+{
+ use_color = color;
+}
+
+static int print_timestamp = 0;
+
+void debug_timestamp(int enable)
+{
+ print_timestamp = enable;
+}
+
+
+/*
+ * Parse the category mask.
+ * category1:category2:category3
+ */
+void debug_parse_category_mask(const char *_mask)
+{
+ unsigned int new_mask = 0;
+ int i = 0;
+ char *mask = strdup(_mask);
+ char *category_token = NULL;
+
+ category_token = strtok(mask, ":");
+ do {
+ for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
+ if (strcasecmp(debug_info[i].name, category_token) == 0)
+ new_mask |= debug_info[i].number;
+ }
+ } while ((category_token = strtok(NULL, ":")));
+
+
+ free(mask);
+ debug_mask = new_mask;
+}
+
+const char* color(int subsys)
+{
+ int i = 0;
+
+ for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) {
+ if (debug_info[i].number == subsys)
+ return debug_info[i].color;
+ }
+
+ return "";
+}
+
+void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+ FILE *outfd = stderr;
+
+ if (!(debug_mask & subsys))
+ return;
+
+ va_start(ap, format);
+
+ fprintf(outfd, "%s", color(subsys));
+
+ if (!cont) {
+ if (print_timestamp) {
+ char *timestr;
+ time_t tm;
+ tm = time(NULL);
+ timestr = ctime(&tm);
+ timestr[strlen(timestr)-1] = '\0';
+ fprintf(outfd, "%s ", timestr);
+ }
+ fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line);
+ }
+ vfprintf(outfd, format, ap);
+ fprintf(outfd, "\033[0;m");
+
+ va_end(ap);
+
+ fflush(outfd);
+}
+
+static char hexd_buff[4096];
+
+char *hexdump(unsigned char *buf, int len)
+{
+ int i;
+ char *cur = hexd_buff;
+
+ hexd_buff[0] = 0;
+ for (i = 0; i < len; i++) {
+ int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
+ int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
+ if (rc <= 0)
+ break;
+ cur += rc;
+ }
+ hexd_buff[sizeof(hexd_buff)-1] = 0;
+ return hexd_buff;
+}
+
diff --git a/openbsc/src/e1_config.c b/openbsc/src/e1_config.c
new file mode 100644
index 000000000..fc23b55e1
--- /dev/null
+++ b/openbsc/src/e1_config.c
@@ -0,0 +1,108 @@
+#include <string.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/trau_mux.h>
+#include <openbsc/misdn.h>
+
+#define SAPI_L2ML 0
+#define SAPI_OML 62
+#define SAPI_RSL 0 /* 63 ? */
+
+#define TEI_L2ML 127
+#define TEI_OML 25
+#define TEI_RSL 1
+
+/* do some compiled-in configuration for our BTS/E1 setup */
+int e1_config(struct gsm_bts *bts, int cardnr, int release_l2)
+{
+ struct e1inp_line *line;
+ struct e1inp_ts *sign_ts;
+ struct e1inp_sign_link *oml_link, *rsl_link;
+
+ line = malloc(sizeof(*line));
+ if (!line)
+ return -ENOMEM;
+ memset(line, 0, sizeof(*line));
+
+ /* create E1 timeslots for signalling and TRAU frames */
+ e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
+ e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_TRAU);
+ e1inp_ts_config(&line->ts[3-1], line, E1INP_TS_TYPE_TRAU);
+
+ /* create signalling links for TS1 */
+ sign_ts = &line->ts[1-1];
+ oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
+ bts->c0, TEI_OML, SAPI_OML);
+ rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
+ bts->c0, TEI_RSL, SAPI_RSL);
+
+ /* create back-links from bts/trx */
+ bts->oml_link = oml_link;
+ bts->c0->rsl_link = rsl_link;
+
+ /* enable subchannel demuxer on TS2 */
+ subch_demux_activate(&line->ts[2-1].trau.demux, 1);
+ subch_demux_activate(&line->ts[2-1].trau.demux, 2);
+ subch_demux_activate(&line->ts[2-1].trau.demux, 3);
+
+ /* enable subchannel demuxer on TS3 */
+ subch_demux_activate(&line->ts[3-1].trau.demux, 0);
+ subch_demux_activate(&line->ts[3-1].trau.demux, 1);
+ subch_demux_activate(&line->ts[3-1].trau.demux, 2);
+ subch_demux_activate(&line->ts[3-1].trau.demux, 3);
+
+#ifdef HAVE_TRX1
+ /* create E1 timeslots for TRAU frames of TRX1 */
+ e1inp_ts_config(&line->ts[4-1], line, E1INP_TS_TYPE_TRAU);
+ e1inp_ts_config(&line->ts[5-1], line, E1INP_TS_TYPE_TRAU);
+
+ /* create RSL signalling link for TRX1 */
+ sign_ts = &line->ts[1-1];
+ rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL,
+ &bts->trx[1], TEI_RSL+1, SAPI_RSL);
+ /* create back-links from trx */
+ bts->trx[1].rsl_link = rsl_link;
+#endif
+
+ return mi_setup(cardnr, line, release_l2);
+}
+
+/* configure pseudo E1 line in ip.access style and connect to BTS */
+int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
+{
+ struct e1inp_line *line;
+ struct e1inp_ts *sign_ts, *rsl_ts;
+ struct e1inp_sign_link *oml_link, *rsl_link;
+
+ line = malloc(sizeof(*line));
+ if (!line)
+ return NULL;
+ memset(line, 0, sizeof(*line));
+
+ /* create E1 timeslots for signalling and TRAU frames */
+ e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
+ e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
+
+ /* create signalling links for TS1 */
+ sign_ts = &line->ts[1-1];
+ rsl_ts = &line->ts[2-1];
+ oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
+ bts->c0, 0, 0xff);
+ rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
+ bts->c0, 0, 0);
+
+ /* create back-links from bts/trx */
+ bts->oml_link = oml_link;
+ bts->c0->rsl_link = rsl_link;
+
+ /* default port at BTS for incoming connections is 3006 */
+ if (sin->sin_port == 0)
+ sin->sin_port = htons(3006);
+
+ return ipaccess_connect(line, sin);
+}
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
new file mode 100644
index 000000000..c3c7c7597
--- /dev/null
+++ b/openbsc/src/e1_input.c
@@ -0,0 +1,498 @@
+/* OpenBSC Abis interface to E1 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <mISDNif.h>
+
+//#define AF_COMPATIBILITY_FUNC
+//#include <compat_af_isdn.h>
+#ifndef AF_ISDN
+#define AF_ISDN 34
+#define PF_ISDN AF_ISDN
+#endif
+
+#include <openbsc/select.h>
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/trau_mux.h>
+
+#define NUM_E1_TS 32
+
+/* list of all E1 drivers */
+LLIST_HEAD(e1inp_driver_list);
+
+/* list of all E1 lines */
+LLIST_HEAD(e1inp_line_list);
+
+/* to be implemented, e.g. by bsc_hack.c */
+void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx);
+
+/*
+ * pcap writing of the misdn load
+ * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
+ */
+#define DLT_LINUX_LAPD 177
+#define PCAP_INPUT 0
+#define PCAP_OUTPUT 1
+
+struct pcap_hdr {
+ u_int32_t magic_number;
+ u_int16_t version_major;
+ u_int16_t version_minor;
+ int32_t thiszone;
+ u_int32_t sigfigs;
+ u_int32_t snaplen;
+ u_int32_t network;
+} __attribute__((packed));
+
+struct pcaprec_hdr {
+ u_int32_t ts_sec;
+ u_int32_t ts_usec;
+ u_int32_t incl_len;
+ u_int32_t orig_len;
+} __attribute__((packed));
+
+struct fake_linux_lapd_header {
+ u_int16_t pkttype;
+ u_int16_t hatype;
+ u_int16_t halen;
+ u_int64_t addr;
+ int16_t protocol;
+} __attribute__((packed));
+
+struct lapd_header {
+ u_int8_t ea1 : 1;
+ u_int8_t cr : 1;
+ u_int8_t sapi : 6;
+ u_int8_t ea2 : 1;
+ u_int8_t tei : 7;
+ u_int8_t control_foo; /* fake UM's ... */
+} __attribute__((packed));
+
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2, hatype_offset);
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4, halen_offset);
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6, addr_offset);
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14, proto_offset);
+static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size);
+
+
+static int pcap_fd = -1;
+
+void e1_set_pcap_fd(int fd)
+{
+ int ret;
+ struct pcap_hdr header = {
+ .magic_number = 0xa1b2c3d4,
+ .version_major = 2,
+ .version_minor = 4,
+ .thiszone = 0,
+ .sigfigs = 0,
+ .snaplen = 65535,
+ .network = DLT_LINUX_LAPD,
+ };
+
+ pcap_fd = fd;
+ ret = write(pcap_fd, &header, sizeof(header));
+}
+
+/* This currently only works for the D-Channel */
+static void write_pcap_packet(int direction, int sapi, int tei,
+ struct msgb *msg) {
+ if (pcap_fd < 0)
+ return;
+
+ int ret;
+ time_t cur_time;
+ struct tm *tm;
+
+ struct fake_linux_lapd_header header = {
+ .pkttype = 4,
+ .hatype = 0,
+ .halen = 0,
+ .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1,
+ .protocol = ntohs(48),
+ };
+
+ struct lapd_header lapd_header = {
+ .ea1 = 0,
+ .cr = direction == PCAP_OUTPUT ? 1 : 0,
+ .sapi = sapi & 0x3F,
+ .ea2 = 1,
+ .tei = tei & 0x7F,
+ .control_foo = 0x03 /* UI */,
+ };
+
+ struct pcaprec_hdr payload_header = {
+ .ts_sec = 0,
+ .ts_usec = 0,
+ .incl_len = msg->len + sizeof(struct fake_linux_lapd_header)
+ + sizeof(struct lapd_header)
+ - MISDN_HEADER_LEN,
+ .orig_len = msg->len + sizeof(struct fake_linux_lapd_header)
+ + sizeof(struct lapd_header)
+ - MISDN_HEADER_LEN,
+ };
+
+
+ cur_time = time(NULL);
+ tm = localtime(&cur_time);
+ payload_header.ts_sec = mktime(tm);
+
+ ret = write(pcap_fd, &payload_header, sizeof(payload_header));
+ ret = write(pcap_fd, &header, sizeof(header));
+ ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
+ ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
+ msg->len - MISDN_HEADER_LEN);
+}
+
+static const char *sign_types[] = {
+ [E1INP_SIGN_NONE] = "None",
+ [E1INP_SIGN_OML] = "OML",
+ [E1INP_SIGN_RSL] = "RSL",
+};
+const char *e1inp_signtype_name(enum e1inp_sign_type tp)
+{
+ if (tp >= ARRAY_SIZE(sign_types))
+ return "undefined";
+ return sign_types[tp];
+}
+
+static const char *ts_types[] = {
+ [E1INP_TS_TYPE_NONE] = "None",
+ [E1INP_TS_TYPE_SIGN] = "Signalling",
+ [E1INP_TS_TYPE_TRAU] = "TRAU",
+};
+
+const char *e1inp_tstype_name(enum e1inp_ts_type tp)
+{
+ if (tp >= ARRAY_SIZE(ts_types))
+ return "undefined";
+ return ts_types[tp];
+}
+
+/* callback when a TRAU frame was received */
+static int subch_cb(struct subch_demux *dmx, int ch, u_int8_t *data, int len,
+ void *_priv)
+{
+ struct e1inp_ts *e1i_ts = _priv;
+ struct gsm_e1_subslot src_ss;
+
+ src_ss.e1_nr = e1i_ts->line->num;
+ src_ss.e1_ts = e1i_ts->num;
+ src_ss.e1_ts_ss = ch;
+
+ return trau_mux_input(&src_ss, data, len);
+}
+
+int abis_rsl_sendmsg(struct msgb *msg)
+{
+ struct e1inp_sign_link *sign_link;
+ struct e1inp_driver *e1inp_driver;
+ struct e1inp_ts *e1i_ts;
+
+ msg->l2h = msg->data;
+
+ if (!msg->trx || !msg->trx->rsl_link) {
+ fprintf(stderr, "rsl_sendmsg: msg->trx == NULL\n");
+ return -EINVAL;
+ }
+
+ sign_link = msg->trx->rsl_link;
+ e1i_ts = sign_link->ts;
+ if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) {
+ /* notify the driver we have something to write */
+ e1inp_driver = sign_link->ts->line->driver;
+ e1inp_driver->want_write(e1i_ts);
+ }
+ msgb_enqueue(&sign_link->tx_list, msg);
+
+ /* dump it */
+ write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
+
+ return 0;
+}
+
+int _abis_nm_sendmsg(struct msgb *msg)
+{
+ struct e1inp_sign_link *sign_link;
+ struct e1inp_driver *e1inp_driver;
+ struct e1inp_ts *e1i_ts;
+
+ msg->l2h = msg->data;
+
+ if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) {
+ fprintf(stderr, "nm_sendmsg: msg->trx == NULL\n");
+ return -EINVAL;
+ }
+
+ sign_link = msg->trx->bts->oml_link;
+ e1i_ts = sign_link->ts;
+ if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) {
+ /* notify the driver we have something to write */
+ e1inp_driver = sign_link->ts->line->driver;
+ e1inp_driver->want_write(e1i_ts);
+ }
+ msgb_enqueue(&sign_link->tx_list, msg);
+
+ /* dump it */
+ write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg);
+
+ return 0;
+}
+
+/* Timeslot */
+
+/* configure and initialize one e1inp_ts */
+int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
+ enum e1inp_ts_type type)
+{
+ ts->type = type;
+ ts->line = line;
+
+ switch (type) {
+ case E1INP_TS_TYPE_SIGN:
+ INIT_LLIST_HEAD(&ts->sign.sign_links);
+ break;
+ case E1INP_TS_TYPE_TRAU:
+ subchan_mux_init(&ts->trau.mux);
+ ts->trau.demux.out_cb = subch_cb;
+ ts->trau.demux.data = ts;
+ subch_demux_init(&ts->trau.demux);
+ break;
+ default:
+ fprintf(stderr, "unsupported E1 timeslot type %u\n",
+ ts->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct e1inp_line *e1inp_line_get(u_int8_t e1_nr)
+{
+ struct e1inp_line *e1i_line;
+
+ /* iterate over global list of e1 lines */
+ llist_for_each_entry(e1i_line, &e1inp_line_list, list) {
+ if (e1i_line->num == e1_nr)
+ return e1i_line;
+ }
+ return NULL;
+}
+
+static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr)
+{
+ struct e1inp_line *e1i_line;
+
+ e1i_line = e1inp_line_get(e1_nr);
+ if (!e1i_line)
+ return NULL;
+
+ return &e1i_line->ts[ts_nr-1];
+}
+
+struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr)
+{
+ struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr);
+
+ if (!e1i_ts)
+ return NULL;
+
+ return &e1i_ts->trau.mux;
+}
+
+/* Signalling Link */
+
+struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i,
+ u_int8_t tei, u_int8_t sapi)
+{
+ struct e1inp_sign_link *link;
+
+ llist_for_each_entry(link, &e1i->sign.sign_links, list) {
+ if (link->sapi == sapi && link->tei == tei)
+ return link;
+ }
+
+ return NULL;
+}
+
+/* create a new signalling link in a E1 timeslot */
+
+struct e1inp_sign_link *
+e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
+ struct gsm_bts_trx *trx, u_int8_t tei,
+ u_int8_t sapi)
+{
+ struct e1inp_sign_link *link;
+
+ if (ts->type != E1INP_TS_TYPE_SIGN)
+ return NULL;
+
+ link = malloc(sizeof(*link));
+ if (!link)
+ return NULL;
+
+ memset(link, 0, sizeof(*link));
+
+ link->ts = ts;
+ link->type = type;
+ INIT_LLIST_HEAD(&link->tx_list);
+ link->trx = trx;
+ link->tei = tei;
+ link->sapi = sapi;
+
+ llist_add_tail(&link->list, &ts->sign.sign_links);
+
+ return link;
+}
+
+/* the E1 driver tells us he has received something on a TS */
+int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
+ u_int8_t tei, u_int8_t sapi)
+{
+ struct e1inp_sign_link *link;
+ int ret;
+
+ switch (ts->type) {
+ case E1INP_TS_TYPE_SIGN:
+ /* consult the list of signalling links */
+ write_pcap_packet(PCAP_INPUT, sapi, tei, msg);
+ link = e1inp_lookup_sign_link(ts, tei, sapi);
+ if (!link) {
+ fprintf(stderr, "didn't find singalling link for "
+ "tei %d, sapi %d\n", tei, sapi);
+ return -EINVAL;
+ }
+ switch (link->type) {
+ case E1INP_SIGN_OML:
+ msg->trx = link->trx;
+ ret = abis_nm_rcvmsg(msg);
+ break;
+ case E1INP_SIGN_RSL:
+ msg->trx = link->trx;
+ ret = abis_rsl_rcvmsg(msg);
+ break;
+ default:
+ ret = -EINVAL;
+ fprintf(stderr, "unknown link type %u\n", link->type);
+ break;
+ }
+ break;
+ case E1INP_TS_TYPE_TRAU:
+ ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg));
+ break;
+ default:
+ ret = -EINVAL;
+ fprintf(stderr, "unknown TS type %u\n", ts->type);
+ break;
+ }
+
+ return ret;
+}
+
+#define TSX_ALLOC_SIZE 4096
+
+/* called by driver if it wants to transmit on a given TS */
+struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts,
+ struct e1inp_sign_link **sign_link)
+{
+ struct e1inp_sign_link *link;
+ struct msgb *msg = NULL;
+ int len;
+
+ switch (e1i_ts->type) {
+ case E1INP_TS_TYPE_SIGN:
+ /* FIXME: implement this round robin */
+ llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) {
+ msg = msgb_dequeue(&link->tx_list);
+ if (msg) {
+ if (sign_link)
+ *sign_link = link;
+ break;
+ }
+ }
+ break;
+ case E1INP_TS_TYPE_TRAU:
+ msg = msgb_alloc(TSX_ALLOC_SIZE);
+ if (!msg)
+ return NULL;
+ len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40);
+ msgb_put(msg, 40);
+ break;
+ default:
+ fprintf(stderr, "unsupported E1 TS type %u\n", e1i_ts->type);
+ return NULL;
+ }
+ return msg;
+}
+
+/* called by driver in case some kind of link state event */
+int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi)
+{
+ struct e1inp_sign_link *link;
+
+ link = e1inp_lookup_sign_link(ts, tei, sapi);
+ if (!link)
+ return -EINVAL;
+
+ /* FIXME: report further upwards */
+ input_event(evt, link->type, link->trx);
+ return 0;
+}
+
+/* register a driver with the E1 core */
+int e1inp_driver_register(struct e1inp_driver *drv)
+{
+ llist_add_tail(&drv->list, &e1inp_driver_list);
+ return 0;
+}
+
+/* register a line with the E1 core */
+int e1inp_line_register(struct e1inp_line *line)
+{
+ int i;
+
+ for (i = 0; i < NUM_E1_TS; i++) {
+ line->ts[i].num = i+1;
+ line->ts[i].line = line;
+ }
+
+ llist_add_tail(&line->list, &e1inp_line_list);
+
+ return 0;
+}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
new file mode 100644
index 000000000..274d3b6bf
--- /dev/null
+++ b/openbsc/src/gsm_04_08.c
@@ -0,0 +1,1723 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#include <openbsc/db.h>
+#include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_11.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/paging.h>
+#include <openbsc/signal.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/trau_mux.h>
+
+#define GSM48_ALLOC_SIZE 1024
+#define GSM48_ALLOC_HEADROOM 128
+
+static const struct tlv_definition rsl_att_tlvdef = {
+ .def = {
+ [GSM48_IE_MOBILE_ID] = { TLV_TYPE_TLV },
+ [GSM48_IE_NAME_LONG] = { TLV_TYPE_TLV },
+ [GSM48_IE_NAME_SHORT] = { TLV_TYPE_TLV },
+ [GSM48_IE_UTC] = { TLV_TYPE_TV },
+ [GSM48_IE_NET_TIME_TZ] = { TLV_TYPE_FIXED, 7 },
+ [GSM48_IE_LSA_IDENT] = { TLV_TYPE_TLV },
+
+ [GSM48_IE_BEARER_CAP] = { TLV_TYPE_TLV },
+ [GSM48_IE_CAUSE] = { TLV_TYPE_TLV },
+ [GSM48_IE_CC_CAP] = { TLV_TYPE_TLV },
+ [GSM48_IE_ALERT] = { TLV_TYPE_TLV },
+ [GSM48_IE_FACILITY] = { TLV_TYPE_TLV },
+ [GSM48_IE_PROGR_IND] = { TLV_TYPE_TLV },
+ [GSM48_IE_AUX_STATUS] = { TLV_TYPE_TLV },
+ [GSM48_IE_KPD_FACILITY] = { TLV_TYPE_TV },
+ [GSM48_IE_SIGNAL] = { TLV_TYPE_TV },
+ [GSM48_IE_CONN_NUM] = { TLV_TYPE_TLV },
+ [GSM48_IE_CONN_SUBADDR] = { TLV_TYPE_TLV },
+ [GSM48_IE_CALLING_BCD] = { TLV_TYPE_TLV },
+ [GSM48_IE_CALLING_SUB] = { TLV_TYPE_TLV },
+ [GSM48_IE_CALLED_BCD] = { TLV_TYPE_TLV },
+ [GSM48_IE_CALLED_SUB] = { TLV_TYPE_TLV },
+ [GSM48_IE_REDIR_BCD] = { TLV_TYPE_TLV },
+ [GSM48_IE_REDIR_SUB] = { TLV_TYPE_TLV },
+ [GSM48_IE_LOWL_COMPAT] = { TLV_TYPE_TLV },
+ [GSM48_IE_HIGHL_COMPAT] = { TLV_TYPE_TLV },
+ [GSM48_IE_USER_USER] = { TLV_TYPE_TLV },
+ [GSM48_IE_SS_VERS] = { TLV_TYPE_TLV },
+ [GSM48_IE_MORE_DATA] = { TLV_TYPE_T },
+ [GSM48_IE_CLIR_SUPP] = { TLV_TYPE_T },
+ [GSM48_IE_CLIR_INVOC] = { TLV_TYPE_T },
+ [GSM48_IE_REV_C_SETUP] = { TLV_TYPE_T },
+ /* FIXME: more elements */
+ },
+};
+
+static const char *rr_cause_names[] = {
+ [GSM48_RR_CAUSE_NORMAL] = "Normal event",
+ [GSM48_RR_CAUSE_ABNORMAL_UNSPEC] = "Abnormal release, unspecified",
+ [GSM48_RR_CAUSE_ABNORMAL_UNACCT] = "Abnormal release, channel unacceptable",
+ [GSM48_RR_CAUSE_ABNORMAL_TIMER] = "Abnormal release, timer expired",
+ [GSM48_RR_CAUSE_ABNORMAL_NOACT] = "Abnormal release, no activity on radio path",
+ [GSM48_RR_CAUSE_PREMPTIVE_REL] = "Preemptive release",
+ [GSM48_RR_CAUSE_HNDOVER_IMP] = "Handover impossible, timing advance out of range",
+ [GSM48_RR_CAUSE_CHAN_MODE_UNACCT] = "Channel mode unacceptable",
+ [GSM48_RR_CAUSE_FREQ_NOT_IMPL] = "Frequency not implemented",
+ [GSM48_RR_CAUSE_CALL_CLEARED] = "Call already cleared",
+ [GSM48_RR_CAUSE_SEMANT_INCORR] = "Semantically incorrect message",
+ [GSM48_RR_CAUSE_INVALID_MAND_INF] = "Invalid mandatory information",
+ [GSM48_RR_CAUSE_MSG_TYPE_N] = "Message type non-existant or not implemented",
+ [GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT] = "Message type not compatible with protocol state",
+ [GSM48_RR_CAUSE_COND_IE_ERROR] = "Conditional IE error",
+ [GSM48_RR_CAUSE_NO_CELL_ALLOC_A] = "No cell allocation available",
+ [GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified",
+};
+
+static char strbuf[64];
+
+static const char *rr_cause_name(u_int8_t cause)
+{
+ if (cause < ARRAY_SIZE(rr_cause_names) &&
+ rr_cause_names[cause])
+ return rr_cause_names[cause];
+
+ snprintf(strbuf, sizeof(strbuf), "0x%02x", cause);
+ return strbuf;
+}
+
+static void parse_meas_rep(struct gsm_meas_rep *rep, const u_int8_t *data,
+ int len)
+{
+ memset(rep, 0, sizeof(*rep));
+
+ if (data[0] & 0x80)
+ rep->flags |= MEAS_REP_F_BA1;
+ if (data[0] & 0x40)
+ rep->flags |= MEAS_REP_F_DTX;
+ if (data[1] & 0x40)
+ rep->flags |= MEAS_REP_F_VALID;
+
+ rep->rxlev_full = data[0] & 0x3f;
+ rep->rxlev_sub = data[1] & 0x3f;
+ rep->rxqual_full = (data[3] >> 4) & 0x7;
+ rep->rxqual_sub = (data[3] >> 1) & 0x7;
+ rep->num_cell = data[4] >> 6 | ((data[3] & 0x01) << 2);
+ if (rep->num_cell < 1)
+ return;
+
+ /* an encoding nightmare in perfection */
+
+ rep->cell[0].rxlev = data[4] & 0x3f;
+ rep->cell[0].bcch_freq = data[5] >> 2;
+ rep->cell[0].bsic = ((data[5] & 0x03) << 3) | (data[6] >> 5);
+ if (rep->num_cell < 2)
+ return;
+
+ rep->cell[1].rxlev = ((data[6] & 0x1f) << 1) | (data[7] >> 7);
+ rep->cell[1].bcch_freq = (data[7] >> 2) & 0x1f;
+ rep->cell[1].bsic = ((data[7] & 0x03) << 4) | (data[8] >> 4);
+ if (rep->num_cell < 3)
+ return;
+
+ rep->cell[2].rxlev = ((data[8] & 0x0f) << 2) | (data[9] >> 6);
+ rep->cell[2].bcch_freq = (data[9] >> 1) & 0x1f;
+ rep->cell[2].bsic = ((data[9] & 0x01) << 6) | (data[10] >> 3);
+ if (rep->num_cell < 4)
+ return;
+
+ rep->cell[3].rxlev = ((data[10] & 0x07) << 3) | (data[11] >> 5);
+ rep->cell[3].bcch_freq = data[11] & 0x1f;
+ rep->cell[3].bsic = data[12] >> 2;
+ if (rep->num_cell < 5)
+ return;
+
+ rep->cell[4].rxlev = ((data[12] & 0x03) << 4) | (data[13] >> 4);
+ rep->cell[4].bcch_freq = ((data[13] & 0xf) << 1) | (data[14] >> 7);
+ rep->cell[4].bsic = (data[14] >> 1) & 0x3f;
+ if (rep->num_cell < 6)
+ return;
+
+ rep->cell[5].rxlev = ((data[14] & 0x01) << 5) | (data[15] >> 3);
+ rep->cell[5].bcch_freq = ((data[15] & 0x07) << 2) | (data[16] >> 6);
+ rep->cell[5].bsic = data[16] & 0x3f;
+}
+
+int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
+static int gsm48_tx_simple(struct gsm_lchan *lchan,
+ u_int8_t pdisc, u_int8_t msg_type);
+static void schedule_reject(struct gsm_lchan *lchan);
+
+struct gsm_lai {
+ u_int16_t mcc;
+ u_int16_t mnc;
+ u_int16_t lac;
+};
+
+static int authorize_everonye = 0;
+void gsm0408_allow_everyone(int everyone)
+{
+ printf("Allowing everyone?\n");
+ authorize_everonye = everyone;
+}
+
+static int reject_cause = 0;
+void gsm0408_set_reject_cause(int cause)
+{
+ reject_cause = cause;
+}
+
+static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
+ struct gsm_subscriber *subscriber)
+{
+ if (!subscriber)
+ return 0;
+
+ /*
+ * Do not send accept yet as more information should arrive. Some
+ * phones will not send us the information and we will have to check
+ * what we want to do with that.
+ */
+ if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei))
+ return 0;
+
+ if (authorize_everonye)
+ return 1;
+
+ return subscriber->authorized;
+}
+
+static void release_loc_updating_req(struct gsm_lchan *lchan)
+{
+ if (!lchan->loc_operation)
+ return;
+
+ bsc_del_timer(&lchan->loc_operation->updating_timer);
+ free(lchan->loc_operation);
+ lchan->loc_operation = 0;
+ put_lchan(lchan);
+}
+
+static void allocate_loc_updating_req(struct gsm_lchan *lchan)
+{
+ use_lchan(lchan);
+ release_loc_updating_req(lchan);
+
+ lchan->loc_operation = (struct gsm_loc_updating_operation *)
+ malloc(sizeof(*lchan->loc_operation));
+ memset(lchan->loc_operation, 0, sizeof(*lchan->loc_operation));
+}
+
+static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
+{
+ u_int32_t tmsi;
+
+ if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) {
+ db_subscriber_alloc_tmsi(lchan->subscr);
+ subscr_update(lchan->subscr, msg->trx->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED);
+ tmsi = strtoul(lchan->subscr->tmsi, NULL, 10);
+ release_loc_updating_req(lchan);
+ return gsm0408_loc_upd_acc(msg->lchan, tmsi);
+ }
+
+ return 0;
+}
+
+static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ if (subsys != SS_LCHAN || signal != S_LCHAN_UNEXPECTED_RELEASE)
+ return 0;
+
+ /*
+ * Cancel any outstanding location updating request
+ * operation taking place on the lchan.
+ */
+ struct gsm_lchan *lchan = (struct gsm_lchan *)handler_data;
+ release_loc_updating_req(lchan);
+
+ return 0;
+}
+
+/*
+ * This will be ran by the linker when loading the DSO. We use it to
+ * do system initialization, e.g. registration of signal handlers.
+ */
+static __attribute__((constructor)) void on_dso_load_0408(void)
+{
+ register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL);
+}
+
+static void to_bcd(u_int8_t *bcd, u_int16_t val)
+{
+ bcd[2] = val % 10;
+ val = val / 10;
+ bcd[1] = val % 10;
+ val = val / 10;
+ bcd[0] = val % 10;
+ val = val / 10;
+}
+
+void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
+ u_int16_t mnc, u_int16_t lac)
+{
+ u_int8_t bcd[3];
+
+ to_bcd(bcd, mcc);
+ lai48->digits[0] = bcd[0] | (bcd[1] << 4);
+ lai48->digits[1] = bcd[2];
+
+ to_bcd(bcd, mnc);
+ /* FIXME: do we need three-digit MNC? See Table 10.5.3 */
+#if 0
+ lai48->digits[1] |= bcd[2] << 4;
+ lai48->digits[2] = bcd[0] | (bcd[1] << 4);
+#else
+ lai48->digits[1] |= 0xf << 4;
+ lai48->digits[2] = bcd[1] | (bcd[2] << 4);
+#endif
+
+ lai48->lac = htons(lac);
+}
+
+#define TMSI_LEN 5
+#define MID_TMSI_LEN (TMSI_LEN + 2)
+
+int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi)
+{
+ u_int32_t *tptr = (u_int32_t *) &buf[3];
+
+ buf[0] = GSM48_IE_MOBILE_ID;
+ buf[1] = TMSI_LEN;
+ buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
+ *tptr = htonl(tmsi);
+
+ return 7;
+}
+
+static const char bcd_num_digits[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '*', '#', 'a', 'b', 'c', '\0'
+};
+
+/* decode a 'called party BCD number' as in 10.5.4.7 */
+u_int8_t decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv)
+{
+ u_int8_t in_len = bcd_lv[0];
+ int i;
+
+ if (in_len < 1)
+ return 0;
+
+ for (i = 2; i <= in_len; i++) {
+ /* lower nibble */
+ output_len--;
+ if (output_len <= 1)
+ break;
+ *output++ = bcd_num_digits[bcd_lv[i] & 0xf];
+
+ /* higher nibble */
+ output_len--;
+ if (output_len <= 1)
+ break;
+ *output++ = bcd_num_digits[bcd_lv[i] >> 4];
+ }
+ if (output_len >= 1)
+ *output++ = '\0';
+
+ /* return number type / calling plan */
+ return bcd_lv[1] & 0x3f;
+}
+
+/* convert a single ASCII character to call-control BCD */
+static int asc_to_bcd(const char asc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcd_num_digits); i++) {
+ if (bcd_num_digits[i] == asc)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/* convert a ASCII phone number to 'called party BCD number' */
+int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
+ u_int8_t type, const char *input)
+{
+ int in_len = strlen(input);
+ int i;
+ u_int8_t *bcd_cur = bcd_lv + 2;
+
+ if (in_len/2 + 1 > max_len)
+ return -EIO;
+
+ /* two digits per byte, plus type byte */
+ bcd_lv[0] = in_len/2 + 1;
+ if (in_len % 2)
+ bcd_lv[0]++;
+
+ /* if the caller wants to create a valid 'calling party BCD
+ * number', then the extension bit MUST NOT be set. For the
+ * 'called party BCD number' it MUST be set. *sigh */
+ bcd_lv[1] = type;
+
+ for (i = 0; i < in_len; i++) {
+ int rc = asc_to_bcd(input[i]);
+ if (rc < 0)
+ return rc;
+ if (i % 2 == 0)
+ *bcd_cur = rc;
+ else
+ *bcd_cur++ |= (rc << 4);
+ }
+ /* append padding nibble in case of odd length */
+ if (i % 2)
+ *bcd_cur++ |= 0xf0;
+
+ /* return how many bytes we used */
+ return (bcd_cur - bcd_lv);
+}
+
+struct msgb *gsm48_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM);
+}
+
+int gsm48_sendmsg(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
+
+ if (msg->lchan) {
+ msg->trx = msg->lchan->ts->trx;
+
+ if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) {
+ /* Send a 04.08 call control message, add transaction
+ * ID and TI flag */
+ gh->proto_discr |= msg->lchan->call.transaction_id;
+
+ /* GSM 04.07 Section 11.2.3.1.3 */
+ switch (msg->lchan->call.type) {
+ case GSM_CT_MO:
+ gh->proto_discr |= 0x80;
+ break;
+ case GSM_CT_MT:
+ break;
+ case GSM_CT_NONE:
+ break;
+ }
+ }
+ }
+
+ msg->l3h = msg->data;
+
+ return rsl_data_request(msg, 0);
+}
+
+
+/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
+int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT;
+ gh->data[0] = cause;
+
+ DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
+
+ return gsm48_sendmsg(msg);
+}
+
+/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */
+int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm48_loc_area_id *lai;
+ u_int8_t *mid;
+ int ret;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT;
+
+ lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
+ gsm0408_generate_lai(lai, bts->network->country_code,
+ bts->network->network_code, bts->location_area_code);
+
+ mid = msgb_put(msg, MID_TMSI_LEN);
+ generate_mid_from_tmsi(mid, tmsi);
+
+ DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
+
+ ret = gsm48_sendmsg(msg);
+
+ ret = gsm48_tx_mm_info(lchan);
+
+ return ret;
+}
+
+static char bcd2char(u_int8_t bcd)
+{
+ if (bcd < 0xa)
+ return '0' + bcd;
+ else
+ return 'A' + (bcd - 0xa);
+}
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+static int mi_to_string(char *string, int str_len, u_int8_t *mi, int mi_len)
+{
+ int i;
+ u_int8_t mi_type;
+ char *str_cur = string;
+ u_int32_t tmsi;
+
+ mi_type = mi[0] & GSM_MI_TYPE_MASK;
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_NONE:
+ break;
+ case GSM_MI_TYPE_TMSI:
+ /* Table 10.5.4.3, reverse generate_mid_from_tmsi */
+ if (mi_len == TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
+ memcpy(&tmsi, &mi[1], 4);
+ tmsi = ntohl(tmsi);
+ return snprintf(string, str_len, "%u", tmsi);
+ }
+ break;
+ case GSM_MI_TYPE_IMSI:
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ *str_cur++ = bcd2char(mi[0] >> 4);
+
+ for (i = 1; i < mi_len; i++) {
+ if (str_cur + 2 >= string + str_len)
+ return str_cur - string;
+ *str_cur++ = bcd2char(mi[i] & 0xf);
+ /* skip last nibble in last input byte when GSM_EVEN */
+ if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD))
+ *str_cur++ = bcd2char(mi[i] >> 4);
+ }
+ break;
+ default:
+ break;
+ }
+ *str_cur++ = '\0';
+
+ return str_cur - string;
+}
+
+/* Transmit Chapter 9.2.10 Identity Request */
+static int mm_tx_identity_req(struct gsm_lchan *lchan, u_int8_t id_type)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_ID_REQ;
+ gh->data[0] = id_type;
+
+ return gsm48_sendmsg(msg);
+}
+
+#define MI_SIZE 32
+
+/* Parse Chapter 9.2.11 Identity Response */
+static int mm_rx_id_resp(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm_lchan *lchan = msg->lchan;
+ u_int8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
+ char mi_string[MI_SIZE];
+
+ mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]);
+ DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
+ mi_type, mi_string);
+
+ /*
+ * Rogue messages could trick us but so is life
+ */
+ put_lchan(lchan);
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_IMSI:
+ if (!lchan->subscr)
+ lchan->subscr = db_create_subscriber(mi_string);
+ if (lchan->loc_operation)
+ lchan->loc_operation->waiting_for_imsi = 0;
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ /* update subscribe <-> IMEI mapping */
+ if (lchan->subscr)
+ db_subscriber_assoc_imei(lchan->subscr, mi_string);
+ if (lchan->loc_operation)
+ lchan->loc_operation->waiting_for_imei = 0;
+ break;
+ }
+
+ /* Check if we can let the mobile station enter */
+ return gsm0408_authorize(lchan, msg);
+}
+
+
+static void loc_upd_rej_cb(void *data)
+{
+ struct gsm_lchan *lchan = data;
+
+ release_loc_updating_req(lchan);
+ gsm0408_loc_upd_rej(lchan, reject_cause);
+ lchan_auto_release(lchan);
+}
+
+static void schedule_reject(struct gsm_lchan *lchan)
+{
+ lchan->loc_operation->updating_timer.cb = loc_upd_rej_cb;
+ lchan->loc_operation->updating_timer.data = lchan;
+ bsc_schedule_timer(&lchan->loc_operation->updating_timer, 5, 0);
+}
+
+static const char *lupd_name(u_int8_t type)
+{
+ switch (type) {
+ case GSM48_LUPD_NORMAL:
+ return "NORMAL";
+ case GSM48_LUPD_PERIODIC:
+ return "PEROIDOC";
+ case GSM48_LUPD_IMSI_ATT:
+ return "IMSI ATTACH";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+#define MI_SIZE 32
+/* Chapter 9.2.15: Receive Location Updating Request */
+static int mm_rx_loc_upd_req(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_loc_upd_req *lu;
+ struct gsm_subscriber *subscr;
+ struct gsm_lchan *lchan = msg->lchan;
+ u_int8_t mi_type;
+ char mi_string[MI_SIZE];
+ int rc;
+
+ lu = (struct gsm48_loc_upd_req *) gh->data;
+
+ mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
+
+ mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len);
+
+ DEBUGP(DMM, "LUPDREQ: mi_type=0x%02x MI(%s) type=%s\n", mi_type, mi_string,
+ lupd_name(lu->type));
+
+ /*
+ * Pseudo Spoof detection: Just drop a second/concurrent
+ * location updating request.
+ */
+ if (lchan->loc_operation) {
+ DEBUGP(DMM, "LUPDREQ: ignoring request due an existing one: %p.\n",
+ lchan->loc_operation);
+ gsm0408_loc_upd_rej(lchan, GSM48_REJECT_PROTOCOL_ERROR);
+ return 0;
+ }
+
+ allocate_loc_updating_req(lchan);
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_IMSI:
+ /* we always want the IMEI, too */
+ use_lchan(lchan);
+ rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
+ lchan->loc_operation->waiting_for_imei = 1;
+
+ /* look up subscriber based on IMSI */
+ subscr = db_create_subscriber(mi_string);
+ break;
+ case GSM_MI_TYPE_TMSI:
+ /* we always want the IMEI, too */
+ use_lchan(lchan);
+ rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
+ lchan->loc_operation->waiting_for_imei = 1;
+
+ /* look up the subscriber based on TMSI, request IMSI if it fails */
+ subscr = subscr_get_by_tmsi(mi_string);
+ if (!subscr) {
+ /* send IDENTITY REQUEST message to get IMSI */
+ use_lchan(lchan);
+ rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI);
+ lchan->loc_operation->waiting_for_imsi = 1;
+ }
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ /* no sim card... FIXME: what to do ? */
+ DEBUGP(DMM, "unimplemented mobile identity type\n");
+ break;
+ default:
+ DEBUGP(DMM, "unknown mobile identity type\n");
+ break;
+ }
+
+ lchan->subscr = subscr;
+
+ /*
+ * Schedule the reject timer and check if we can let the
+ * subscriber into our network immediately or if we need to wait
+ * for identity responses.
+ */
+ schedule_reject(lchan);
+ return gsm0408_authorize(lchan, msg);
+}
+
+/* 9.1.5 Channel mode modify */
+int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ struct gsm48_chan_mode_modify *cmm =
+ (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
+ u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
+
+ DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
+
+ lchan->tch_mode = mode;
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
+
+ /* fill the channel information element, this code
+ * should probably be shared with rsl_rx_chan_rqd() */
+ cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
+ cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
+ cmm->chan_desc.h0.h = 0;
+ cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
+ cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
+ cmm->mode = mode;
+
+ return gsm48_sendmsg(msg);
+}
+
+/* Section 9.2.15a */
+int gsm48_tx_mm_info(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm_network *net = lchan->ts->trx->bts->network;
+ u_int8_t *ptr8;
+ u_int16_t *ptr16;
+ int name_len;
+ int i;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_INFO;
+
+ if (net->name_long) {
+ name_len = strlen(net->name_long);
+ /* 10.5.3.5a */
+ ptr8 = msgb_put(msg, 3);
+ ptr8[0] = GSM48_IE_NAME_LONG;
+ ptr8[1] = name_len*2 +1;
+ ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */
+
+ ptr16 = (u_int16_t *) msgb_put(msg, name_len*2);
+ for (i = 0; i < name_len; i++)
+ ptr16[i] = htons(net->name_long[i]);
+
+ /* FIXME: Use Cell Broadcast, not UCS-2, since
+ * UCS-2 is only supported by later revisions of the spec */
+ }
+
+ if (net->name_short) {
+ name_len = strlen(net->name_short);
+ /* 10.5.3.5a */
+ ptr8 = (u_int8_t *) msgb_put(msg, 3);
+ ptr8[0] = GSM48_IE_NAME_LONG;
+ ptr8[1] = name_len*2 + 1;
+ ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */
+
+ ptr16 = (u_int16_t *) msgb_put(msg, name_len*2);
+ for (i = 0; i < name_len; i++)
+ ptr16[i] = htons(net->name_short[i]);
+ }
+
+#if 0
+ /* move back to the top */
+ time_t cur_t;
+ struct tm* cur_time;
+ int tz15min;
+ /* Section 10.5.3.9 */
+ cur_t = time(NULL);
+ cur_time = gmtime(cur_t);
+ ptr8 = msgb_put(msg, 8);
+ ptr8[0] = GSM48_IE_NET_TIME_TZ;
+ ptr8[1] = to_bcd8(cur_time->tm_year % 100);
+ ptr8[2] = to_bcd8(cur_time->tm_mon);
+ ptr8[3] = to_bcd8(cur_time->tm_mday);
+ ptr8[4] = to_bcd8(cur_time->tm_hour);
+ ptr8[5] = to_bcd8(cur_time->tm_min);
+ ptr8[6] = to_bcd8(cur_time->tm_sec);
+ /* 02.42: coded as BCD encoded signed value in units of 15 minutes */
+ tz15min = (cur_time->tm_gmtoff)/(60*15);
+ ptr8[6] = to_bcd8(tz15min);
+ if (tz15min < 0)
+ ptr8[6] |= 0x80;
+#endif
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan)
+{
+ DEBUGP(DMM, "-> CM SERVICE ACK\n");
+ return gsm48_tx_simple(lchan, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC);
+}
+
+/* 9.2.6 CM service reject */
+static int gsm48_tx_mm_serv_rej(struct gsm_lchan *lchan,
+ enum gsm48_reject_value value)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+
+ msg->lchan = lchan;
+ use_lchan(lchan);
+
+ gh->proto_discr = GSM48_PDISC_MM;
+ gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
+ gh->data[0] = value;
+ DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
+
+ return gsm48_sendmsg(msg);
+}
+
+
+/*
+ * Handle CM Service Requests
+ * a) Verify that the packet is long enough to contain the information
+ * we require otherwsie reject with INCORRECT_MESSAGE
+ * b) Try to parse the TMSI. If we do not have one reject
+ * c) Check that we know the subscriber with the TMSI otherwise reject
+ * with a HLR cause
+ * d) Set the subscriber on the gsm_lchan and accept
+ */
+static int gsm48_rx_mm_serv_req(struct msgb *msg)
+{
+ u_int8_t mi_type;
+ char mi_string[MI_SIZE];
+
+ struct gsm_subscriber *subscr;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_service_request *req =
+ (struct gsm48_service_request *)gh->data;
+ /* unfortunately in Phase1 the classmar2 length is variable */
+ u_int8_t classmark2_len = gh->data[1];
+ u_int8_t *classmark2 = gh->data+2;
+ u_int8_t mi_len = *(classmark2 + classmark2_len);
+ u_int8_t *mi = (classmark2 + classmark2_len + 1);
+
+ DEBUGP(DMM, "<- CM SERVICE REQUEST ");
+ if (msg->data_len < sizeof(struct gsm48_service_request*)) {
+ DEBUGPC(DMM, "wrong sized message\n");
+ return gsm48_tx_mm_serv_rej(msg->lchan,
+ GSM48_REJECT_INCORRECT_MESSAGE);
+ }
+
+ if (msg->data_len < req->mi_len + 6) {
+ DEBUGPC(DMM, "does not fit in packet\n");
+ return gsm48_tx_mm_serv_rej(msg->lchan,
+ GSM48_REJECT_INCORRECT_MESSAGE);
+ }
+
+ mi_type = mi[0] & GSM_MI_TYPE_MASK;
+ if (mi_type != GSM_MI_TYPE_TMSI) {
+ DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type);
+ return gsm48_tx_mm_serv_rej(msg->lchan,
+ GSM48_REJECT_INCORRECT_MESSAGE);
+ }
+
+ mi_to_string(mi_string, sizeof(mi_string), mi, mi_len);
+ DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n",
+ req->cm_service_type, mi_type, mi_string);
+
+ subscr = subscr_get_by_tmsi(mi_string);
+
+ /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */
+ if (!subscr)
+ return gsm48_tx_mm_serv_rej(msg->lchan,
+ GSM48_REJECT_IMSI_UNKNOWN_IN_HLR);
+
+ if (!msg->lchan->subscr)
+ msg->lchan->subscr = subscr;
+ else if (msg->lchan->subscr != subscr) {
+ DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
+ subscr_put(subscr);
+ }
+
+ subscr->classmark2_len = classmark2_len;
+ memcpy(subscr->classmark2, classmark2, classmark2_len);
+
+ return gsm48_tx_mm_serv_ack(msg->lchan);
+}
+
+static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm48_imsi_detach_ind *idi =
+ (struct gsm48_imsi_detach_ind *) gh->data;
+ u_int8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK;
+ char mi_string[MI_SIZE];
+ struct gsm_subscriber *subscr;
+
+ mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len);
+ DEBUGP(DMM, "IMSI DETACH INDICATION: mi_type=0x%02x MI(%s): ",
+ mi_type, mi_string);
+
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ subscr = subscr_get_by_tmsi(mi_string);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ subscr = subscr_get_by_imsi(mi_string);
+ break;
+ case GSM_MI_TYPE_IMEI:
+ case GSM_MI_TYPE_IMEISV:
+ /* no sim card... FIXME: what to do ? */
+ DEBUGPC(DMM, "unimplemented mobile identity type\n");
+ break;
+ default:
+ DEBUGPC(DMM, "unknown mobile identity type\n");
+ break;
+ }
+
+ if (subscr) {
+ subscr_update(subscr, msg->trx->bts,
+ GSM_SUBSCRIBER_UPDATE_DETACHED);
+ DEBUGP(DMM, "Subscriber: %s\n",
+ subscr->name ? subscr->name : subscr->imsi);
+ subscr_put(subscr);
+ } else
+ DEBUGP(DMM, "Unknown Subscriber ?!?\n");
+
+ put_lchan(msg->lchan);
+
+ return 0;
+}
+
+static int gsm48_rx_mm_status(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]);
+
+ return 0;
+}
+
+/* Receive a GSM 04.08 Mobility Management (MM) message */
+static int gsm0408_rcv_mm(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ int rc;
+
+ switch (gh->msg_type & 0xbf) {
+ case GSM48_MT_MM_LOC_UPD_REQUEST:
+ DEBUGP(DMM, "LOCATION UPDATING REQUEST\n");
+ rc = mm_rx_loc_upd_req(msg);
+ break;
+ case GSM48_MT_MM_ID_RESP:
+ rc = mm_rx_id_resp(msg);
+ break;
+ case GSM48_MT_MM_CM_SERV_REQ:
+ rc = gsm48_rx_mm_serv_req(msg);
+ break;
+ case GSM48_MT_MM_STATUS:
+ rc = gsm48_rx_mm_status(msg);
+ break;
+ case GSM48_MT_MM_TMSI_REALL_COMPL:
+ DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
+ msg->lchan->subscr ?
+ msg->lchan->subscr->imsi :
+ "unknown subscriber");
+ break;
+ case GSM48_MT_MM_IMSI_DETACH_IND:
+ rc = gsm48_rx_mm_imsi_detach_ind(msg);
+ break;
+ case GSM48_MT_MM_CM_REEST_REQ:
+ DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n");
+ break;
+ case GSM48_MT_MM_AUTH_RESP:
+ DEBUGP(DMM, "AUTHENTICATION RESPONSE: Not implemented\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n",
+ gh->msg_type);
+ break;
+ }
+
+ return rc;
+}
+
+/* Receive a PAGING RESPONSE message from the MS */
+static int gsm48_rr_rx_pag_resp(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t *classmark2_lv = gh->data + 1;
+ u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv;
+ u_int8_t mi_type = mi_lv[1] & GSM_MI_TYPE_MASK;
+ char mi_string[MI_SIZE];
+ struct gsm_subscriber *subscr;
+ struct paging_signal_data sig_data;
+ int rc = 0;
+
+ mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, *mi_lv);
+ DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n",
+ mi_type, mi_string);
+ switch (mi_type) {
+ case GSM_MI_TYPE_TMSI:
+ subscr = subscr_get_by_tmsi(mi_string);
+ break;
+ case GSM_MI_TYPE_IMSI:
+ subscr = subscr_get_by_imsi(mi_string);
+ break;
+ }
+
+ if (!subscr) {
+ DEBUGP(DRR, "<- Can't find any subscriber for this ID\n");
+ /* FIXME: request id? close channel? */
+ return -EINVAL;
+ }
+ DEBUGP(DRR, "<- Channel was requested by %s\n",
+ subscr->name ? subscr->name : subscr->imsi);
+
+ subscr->classmark2_len = *classmark2_lv;
+ memcpy(subscr->classmark2, classmark2_lv+1, *classmark2_lv);
+
+ if (!msg->lchan->subscr) {
+ msg->lchan->subscr = subscr;
+ } else if (msg->lchan->subscr != subscr) {
+ DEBUGP(DRR, "<- Channel already owned by someone else?\n");
+ subscr_put(subscr);
+ return -EINVAL;
+ } else {
+ DEBUGP(DRR, "<- Channel already owned by us\n");
+ subscr_put(subscr);
+ subscr = msg->lchan->subscr;
+ }
+
+ sig_data.subscr = subscr;
+ sig_data.bts = msg->lchan->ts->trx->bts;
+ sig_data.lchan = msg->lchan;
+
+ dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+ paging_request_stop(msg->trx->bts, subscr, msg->lchan);
+
+ /* FIXME: somehow signal the completion of the PAGING to
+ * the entity that requested the paging */
+
+ return rc;
+}
+
+static int gsm48_rx_rr_classmark(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ struct gsm_subscriber *subscr = msg->lchan->subscr;
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ u_int8_t cm2_len, cm3_len = 0;
+ u_int8_t *cm2, *cm3 = NULL;
+
+ DEBUGP(DRR, "CLASSMARK CHANGE ");
+
+ /* classmark 2 */
+ cm2_len = gh->data[0];
+ cm2 = &gh->data[1];
+ DEBUGPC(DRR, "CM2(len=%u) ", cm2_len);
+
+ if (payload_len > cm2_len + 1) {
+ /* we must have a classmark3 */
+ if (gh->data[cm2_len+1] != 0x20) {
+ DEBUGPC(DRR, "ERR CM3 TAG\n");
+ return -EINVAL;
+ }
+ if (cm2_len > 3) {
+ DEBUGPC(DRR, "CM2 too long!\n");
+ return -EINVAL;
+ }
+
+ cm3_len = gh->data[cm2_len+2];
+ cm3 = &gh->data[cm2_len+3];
+ if (cm3_len > 14) {
+ DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len);
+ return -EINVAL;
+ }
+ DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len);
+ }
+ if (subscr) {
+ subscr->classmark2_len = cm2_len;
+ memcpy(subscr->classmark2, cm2, cm2_len);
+ if (cm3) {
+ subscr->classmark3_len = cm3_len;
+ memcpy(subscr->classmark3, cm3, cm3_len);
+ }
+ }
+
+ /* FIXME: store the classmark2/3 values with the equipment register */
+
+ return 0;
+}
+
+static int gsm48_rx_rr_status(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+
+ DEBUGP(DRR, "STATUS rr_cause = %s\n",
+ rr_cause_name(gh->data[0]));
+
+ return 0;
+}
+
+static int gsm48_rx_rr_meas_rep(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ static struct gsm_meas_rep meas_rep;
+
+ DEBUGP(DRR, "MEASUREMENT REPORT ");
+ parse_meas_rep(&meas_rep, gh->data, payload_len);
+ if (meas_rep.flags & MEAS_REP_F_DTX)
+ DEBUGPC(DRR, "DTX ");
+ if (meas_rep.flags & MEAS_REP_F_BA1)
+ DEBUGPC(DRR, "BA1 ");
+ if (!(meas_rep.flags & MEAS_REP_F_VALID))
+ DEBUGPC(DRR, "NOT VALID ");
+ else
+ DEBUGPC(DRR, "FULL(lev=%u, qual=%u) SUB(lev=%u, qual=%u) ",
+ meas_rep.rxlev_full, meas_rep.rxqual_full, meas_rep.rxlev_sub,
+ meas_rep.rxqual_sub);
+
+ DEBUGPC(DRR, "NUM_NEIGH=%u\n", meas_rep.num_cell);
+
+ /* FIXME: put the results somwhere */
+
+ return 0;
+}
+
+/* Receive a GSM 04.08 Radio Resource (RR) message */
+static int gsm0408_rcv_rr(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ int rc = 0;
+
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_CLSM_CHG:
+ rc = gsm48_rx_rr_classmark(msg);
+ break;
+ case GSM48_MT_RR_GPRS_SUSP_REQ:
+ DEBUGP(DRR, "GRPS SUSPEND REQUEST\n");
+ break;
+ case GSM48_MT_RR_PAG_RESP:
+ rc = gsm48_rr_rx_pag_resp(msg);
+ break;
+ case GSM48_MT_RR_CHAN_MODE_MODIF_ACK:
+ DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n");
+ rc = rsl_chan_mode_modify_req(msg->lchan);
+ break;
+ case GSM48_MT_RR_STATUS:
+ rc = gsm48_rx_rr_status(msg);
+ break;
+ case GSM48_MT_RR_MEAS_REP:
+ rc = gsm48_rx_rr_meas_rep(msg);
+ break;
+ default:
+ fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
+ gh->msg_type);
+ break;
+ }
+
+ return rc;
+}
+
+/* 7.1.7 and 9.1.7 Channel release*/
+int gsm48_send_rr_release(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ u_int8_t *cause;
+
+ msg->lchan = lchan;
+ gh->proto_discr = GSM48_PDISC_RR;
+ gh->msg_type = GSM48_MT_RR_CHAN_REL;
+
+ cause = msgb_put(msg, 1);
+ cause[0] = GSM48_RR_CAUSE_NORMAL;
+
+ DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n",
+ lchan->nr, lchan->type);
+
+ return gsm48_sendmsg(msg);
+}
+
+/* Call Control */
+
+/* The entire call control code is written in accordance with Figure 7.10c
+ * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE
+ * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY
+ * it for voice */
+
+static int gsm48_cc_tx_status(struct gsm_lchan *lchan)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ u_int8_t *cause, *call_state;
+
+ gh->proto_discr = GSM48_PDISC_CC;
+
+ msg->lchan = lchan;
+
+ gh->msg_type = GSM48_MT_CC_STATUS;
+
+ cause = msgb_put(msg, 3);
+ cause[0] = 2;
+ cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER;
+ cause[2] = 0x80 | 30; /* response to status inquiry */
+
+ call_state = msgb_put(msg, 1);
+ call_state[0] = 0xc0 | 0x00;
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm48_tx_simple(struct gsm_lchan *lchan,
+ u_int8_t pdisc, u_int8_t msg_type)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ msg->lchan = lchan;
+
+ gh->proto_discr = pdisc;
+ gh->msg_type = msg_type;
+
+ return gsm48_sendmsg(msg);
+}
+
+/* call-back from paging the B-end of the connection */
+static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
+ struct msgb *msg, void *_lchan, void *param)
+{
+ struct gsm_lchan *lchan = _lchan;
+ struct gsm_call *remote_call = param;
+ struct gsm_call *call = &lchan->call;
+ int rc = 0;
+
+ if (hooknum != GSM_HOOK_RR_PAGING)
+ return -EINVAL;
+
+ switch (event) {
+ case GSM_PAGING_SUCCEEDED:
+ DEBUGP(DCC, "paging succeeded!\n");
+ remote_call->remote_lchan = lchan;
+ call->remote_lchan = remote_call->local_lchan;
+ /* send SETUP request to called party */
+ rc = gsm48_cc_tx_setup(lchan, call->remote_lchan->subscr);
+ if (is_ipaccess_bts(lchan->ts->trx->bts))
+ rsl_ipacc_bind(lchan);
+ break;
+ case GSM_PAGING_EXPIRED:
+ DEBUGP(DCC, "paging expired!\n");
+ /* notify caller that we cannot reach called party */
+ /* FIXME: correct cause, etc */
+ rc = gsm48_tx_simple(remote_call->local_lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_RELEASE_COMPL);
+ break;
+ }
+ return rc;
+}
+
+static int gsm48_cc_rx_status_enq(struct msgb *msg)
+{
+ DEBUGP(DCC, "-> STATUS ENQ\n");
+ return gsm48_cc_tx_status(msg->lchan);
+}
+
+static int gsm48_cc_rx_setup(struct msgb *msg)
+{
+ struct gsm_call *call = &msg->lchan->call;
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
+ struct gsm_subscriber *called_subscr;
+ char called_number[(43-2)*2 + 1] = "\0";
+ struct tlv_parsed tp;
+ u_int8_t num_type;
+ int ret;
+
+ if (call->state == GSM_CSTATE_NULL ||
+ call->state == GSM_CSTATE_RELEASE_REQ)
+ use_lchan(msg->lchan);
+
+ call->type = GSM_CT_MO;
+ call->state = GSM_CSTATE_INITIATED;
+ call->local_lchan = msg->lchan;
+ call->transaction_id = gh->proto_discr & 0xf0;
+
+ tlv_parse(&tp, &rsl_att_tlvdef, gh->data, payload_len, 0, 0);
+ if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD))
+ goto err;
+
+ /* Parse the number that was dialed and lookup subscriber */
+ num_type = decode_bcd_number(called_number, sizeof(called_number),
+ TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1);
+
+ DEBUGP(DCC, "A -> SETUP(tid=0x%02x,number='%s')\n", call->transaction_id,
+ called_number);
+
+ called_subscr = subscr_get_by_extension(called_number);
+ if (!called_subscr) {
+ DEBUGP(DCC, "could not find subscriber, RELEASE\n");
+ put_lchan(msg->lchan);
+ return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_RELEASE_COMPL);
+ }
+ if (called_subscr == msg->lchan->subscr) {
+ DEBUGP(DCC, "subscriber calling himself ?!?\n");
+ put_lchan(msg->lchan);
+ subscr_put(called_subscr);
+ return gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_RELEASE_COMPL);
+ }
+
+ subscr_get(msg->lchan->subscr);
+ call->called_subscr = called_subscr;
+
+ /* start paging of the receiving end of the call */
+ /* FIXME: we're assuming that the receiver is at the same BTS
+ * than we are, which is obviously a wrong assumption in multi-BTS
+ * case */
+ paging_request(msg->trx->bts, called_subscr, RSL_CHANNEED_TCH_F,
+ setup_trig_pag_evt, call);
+
+ /* send a CALL PROCEEDING message to the MO */
+ ret = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_CALL_PROC);
+
+ if (is_ipaccess_bts(msg->trx->bts))
+ rsl_ipacc_bind(msg->lchan);
+
+ /* change TCH/F mode to voice */
+ return gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR);
+
+err:
+ /* FIXME: send some kind of RELEASE */
+ return 0;
+}
+
+static int gsm48_cc_rx_alerting(struct msgb *msg)
+{
+ struct gsm_call *call = &msg->lchan->call;
+
+ DEBUGP(DCC, "A -> ALERTING\n");
+
+ /* forward ALERTING to other party */
+ if (!call->remote_lchan)
+ return -EIO;
+
+ DEBUGP(DCC, "B <- ALERTING\n");
+ return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_ALERTING);
+}
+
+/* map two ipaccess RTP streams onto each other */
+static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+ struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts;
+ struct gsm_bts_trx_ts *ts;
+
+ DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n",
+ bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
+ remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr);
+
+ if (bts->type != remote_bts->type) {
+ DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
+ return -EINVAL;
+ }
+
+ switch (bts->type) {
+ case GSM_BTS_TYPE_NANOBTS_900:
+ case GSM_BTS_TYPE_NANOBTS_1800:
+ ts = remote_lchan->ts;
+ rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip, ts->abis_ip.bound_port,
+ lchan->ts->abis_ip.attr_f8, ts->abis_ip.attr_fc);
+
+ ts = lchan->ts;
+ rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip, ts->abis_ip.bound_port,
+ remote_lchan->ts->abis_ip.attr_f8, ts->abis_ip.attr_fc);
+ break;
+ case GSM_BTS_TYPE_BS11:
+ trau_mux_map_lchan(lchan, remote_lchan);
+ break;
+ default:
+ DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int gsm48_cc_rx_connect(struct msgb *msg)
+{
+ struct gsm_call *call = &msg->lchan->call;
+ int rc;
+
+ DEBUGP(DCC, "A -> CONNECT\n");
+
+ rc = tch_map(msg->lchan, call->remote_lchan);
+ if (rc)
+ return -EIO;
+
+ if (!call->remote_lchan)
+ return -EIO;
+
+ DEBUGP(DCC, "A <- CONNECT ACK\n");
+ /* MT+MO: need to respond with CONNECT_ACK and pass on */
+ rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_CONNECT_ACK);
+
+
+ /* forward CONNECT to other party */
+ DEBUGP(DCC, "B <- CONNECT\n");
+ return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_CONNECT);
+}
+
+static int gsm48_cc_rx_disconnect(struct msgb *msg)
+{
+ struct gsm_call *call = &msg->lchan->call;
+ int rc;
+
+
+ /* Section 5.4.3.2 */
+ DEBUGP(DCC, "A -> DISCONNECT (state->RELEASE_REQ)\n");
+ call->state = GSM_CSTATE_RELEASE_REQ;
+ /* FIXME: clear the network connection */
+ DEBUGP(DCC, "A <- RELEASE\n");
+ rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_RELEASE);
+
+ /*
+ * FIXME: This looks wrong! We should have a single
+ * place to do MMCC-REL-CNF/-REQ/-IND and then switch
+ * to the NULL state and relase the call
+ */
+ subscr_put_channel(msg->lchan);
+
+ /* forward DISCONNECT to other party */
+ if (!call->remote_lchan)
+ return -EIO;
+
+ DEBUGP(DCC, "B <- DISCONNECT\n");
+ return gsm48_tx_simple(call->remote_lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_DISCONNECT);
+}
+
+static const u_int8_t calling_bcd[] = { 0xb9, 0x32, 0x24 };
+
+int gsm48_cc_tx_setup(struct gsm_lchan *lchan,
+ struct gsm_subscriber *calling_subscr)
+{
+ struct msgb *msg = gsm48_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm_call *call = &lchan->call;
+ u_int8_t bcd_lv[19];
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+
+ call->type = GSM_CT_MT;
+
+ call->local_lchan = msg->lchan = lchan;
+ use_lchan(lchan);
+
+ gh->proto_discr = GSM48_PDISC_CC;
+ gh->msg_type = GSM48_MT_CC_SETUP;
+
+ msgb_tv_put(msg, GSM48_IE_SIGNAL, GSM48_SIGNAL_DIALTONE);
+ if (calling_subscr) {
+ encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0xb9,
+ calling_subscr->extension);
+ msgb_tlv_put(msg, GSM48_IE_CALLING_BCD,
+ bcd_lv[0], bcd_lv+1);
+ }
+ if (lchan->subscr) {
+ encode_bcd_number(bcd_lv, sizeof(bcd_lv), 0xb9,
+ lchan->subscr->extension);
+ msgb_tlv_put(msg, GSM48_IE_CALLED_BCD,
+ bcd_lv[0], bcd_lv+1);
+ }
+
+ DEBUGP(DCC, "B <- SETUP\n");
+
+ return gsm48_sendmsg(msg);
+}
+
+static int gsm0408_rcv_cc(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t msg_type = gh->msg_type & 0xbf;
+ struct gsm_call *call = &msg->lchan->call;
+ int rc = 0;
+
+ switch (msg_type) {
+ case GSM48_MT_CC_CALL_CONF:
+ /* Response to SETUP */
+ DEBUGP(DCC, "-> CALL CONFIRM\n");
+ /* we now need to MODIFY the channel */
+ rc = gsm48_tx_chan_mode_modify(msg->lchan, GSM48_CMODE_SPEECH_EFR);
+ break;
+ case GSM48_MT_CC_RELEASE_COMPL:
+ /* Answer from MS to RELEASE */
+ DEBUGP(DCC, "-> RELEASE COMPLETE (state->NULL)\n");
+ call->state = GSM_CSTATE_NULL;
+ break;
+ case GSM48_MT_CC_ALERTING:
+ rc = gsm48_cc_rx_alerting(msg);
+ break;
+ case GSM48_MT_CC_CONNECT:
+ rc = gsm48_cc_rx_connect(msg);
+ break;
+ case GSM48_MT_CC_CONNECT_ACK:
+ /* MO: Answer to CONNECT */
+ call->state = GSM_CSTATE_ACTIVE;
+ DEBUGP(DCC, "-> CONNECT_ACK (state->ACTIVE)\n");
+ break;
+ case GSM48_MT_CC_RELEASE:
+ DEBUGP(DCC, "-> RELEASE\n");
+ DEBUGP(DCC, "<- RELEASE_COMPLETE\n");
+ /* need to respond with RELEASE_COMPLETE */
+ rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_RELEASE_COMPL);
+ subscr_put_channel(msg->lchan);
+ call->state = GSM_CSTATE_NULL;
+ break;
+ case GSM48_MT_CC_STATUS_ENQ:
+ rc = gsm48_cc_rx_status_enq(msg);
+ break;
+ case GSM48_MT_CC_DISCONNECT:
+ rc = gsm48_cc_rx_disconnect(msg);
+ break;
+ case GSM48_MT_CC_SETUP:
+ /* MO: wants to establish a call */
+ rc = gsm48_cc_rx_setup(msg);
+ break;
+ case GSM48_MT_CC_EMERG_SETUP:
+ DEBUGP(DCC, "-> EMERGENCY SETUP\n");
+ /* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
+ break;
+ case GSM48_MT_CC_HOLD:
+ DEBUGP(DCC, "-> HOLD (rejecting)\n");
+ rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_HOLD_REJ);
+ break;
+ case GSM48_MT_CC_RETR:
+ DEBUGP(DCC, "-> RETR (rejecting)\n");
+ rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC,
+ GSM48_MT_CC_RETR_REJ);
+ break;
+ default:
+ fprintf(stderr, "Unimplemented GSM 04.08 CC msg type 0x%02x\n",
+ msg_type);
+ break;
+ }
+
+ return rc;
+}
+
+/* here we pass in a msgb from the RSL->RLL. We expect the l3 pointer to be set */
+int gsm0408_rcvmsg(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t pdisc = gh->proto_discr & 0x0f;
+ int rc = 0;
+
+ switch (pdisc) {
+ case GSM48_PDISC_CC:
+ rc = gsm0408_rcv_cc(msg);
+ break;
+ case GSM48_PDISC_MM:
+ rc = gsm0408_rcv_mm(msg);
+ break;
+ case GSM48_PDISC_RR:
+ rc = gsm0408_rcv_rr(msg);
+ break;
+ case GSM48_PDISC_SMS:
+ rc = gsm0411_rcv_sms(msg);
+ break;
+ case GSM48_PDISC_MM_GPRS:
+ case GSM48_PDISC_SM_GPRS:
+ fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n",
+ pdisc);
+ break;
+ default:
+ fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n",
+ pdisc);
+ break;
+ }
+
+ return rc;
+}
+
+enum chreq_type {
+ CHREQ_T_EMERG_CALL,
+ CHREQ_T_CALL_REEST_TCH_F,
+ CHREQ_T_CALL_REEST_TCH_H,
+ CHREQ_T_CALL_REEST_TCH_H_DBL,
+ CHREQ_T_SDCCH,
+ CHREQ_T_TCH_F,
+ CHREQ_T_VOICE_CALL_TCH_H,
+ CHREQ_T_DATA_CALL_TCH_H,
+ CHREQ_T_LOCATION_UPD,
+ CHREQ_T_PAG_R_ANY,
+ CHREQ_T_PAG_R_TCH_F,
+ CHREQ_T_PAG_R_TCH_FH,
+};
+
+/* Section 9.1.8 / Table 9.9 */
+struct chreq {
+ u_int8_t val;
+ u_int8_t mask;
+ enum chreq_type type;
+};
+
+/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */
+static const struct chreq chreq_type_neci1[] = {
+ { 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
+ { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F },
+ { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H },
+ { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL },
+ { 0xe0, 0xe0, CHREQ_T_SDCCH },
+ { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H },
+ { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
+ { 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
+ { 0x10, 0xf0, CHREQ_T_SDCCH },
+ { 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+ { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
+ { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+};
+
+/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
+static const struct chreq chreq_type_neci0[] = {
+ { 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
+ { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H },
+ { 0xe0, 0xe0, CHREQ_T_TCH_F },
+ { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
+ { 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
+ { 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+ { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
+ { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
+};
+
+static const enum gsm_chan_t ctype_by_chreq[] = {
+ [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F,
+ [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F,
+ [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H,
+ [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H,
+ [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F,
+ [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H,
+ [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H,
+ [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_PAG_R_ANY] = GSM_LCHAN_SDCCH,
+ [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
+ [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
+};
+
+static const enum gsm_chreq_reason_t reason_by_chreq[] = {
+ [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG,
+ [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL,
+ [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL,
+ [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL,
+ [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
+ [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
+ [CHREQ_T_PAG_R_ANY] = GSM_CHREQ_REASON_PAG,
+ [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
+ [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
+};
+
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+{
+ int i;
+ /* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+
+ for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
+ const struct chreq *chr = &chreq_type_neci0[i];
+ if ((ra & chr->mask) == chr->val)
+ return ctype_by_chreq[chr->type];
+ }
+ fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
+ return GSM_LCHAN_SDCCH;
+}
+
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+{
+ int i;
+ /* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+
+ for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
+ const struct chreq *chr = &chreq_type_neci0[i];
+ if ((ra & chr->mask) == chr->val)
+ return reason_by_chreq[chr->type];
+ }
+ fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
+ return GSM_CHREQ_REASON_OTHER;
+}
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
new file mode 100644
index 000000000..c29953806
--- /dev/null
+++ b/openbsc/src/gsm_04_11.c
@@ -0,0 +1,500 @@
+/* Point-to-Point (PP) Short Message Service (SMS)
+ * Support on Mobile Radio Interface
+ * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
+
+/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+#include <openbsc/msgb.h>
+#include <openbsc/tlv.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_11.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_utils.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/signal.h>
+#include <openbsc/db.h>
+
+#define GSM411_ALLOC_SIZE 1024
+#define GSM411_ALLOC_HEADROOM 128
+
+struct msgb *gsm411_msgb_alloc(void)
+{
+ return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM);
+}
+
+int gsm0411_sendmsg(struct msgb *msg)
+{
+ if (msg->lchan)
+ msg->trx = msg->lchan->ts->trx;
+
+ msg->l3h = msg->data;
+
+ return rsl_data_request(msg, 0);
+}
+
+
+#if 0
+static u_int8_t gsm0411_tpdu_from_sms(u_int8_t *tpdu, struct sms_deliver *sms)
+{
+}
+#endif
+
+static unsigned long gsm340_validity_period(struct sms_submit *sms)
+{
+ u_int8_t vp;
+ unsigned long minutes;
+
+ switch (sms->vpf) {
+ case GSM340_TP_VPF_RELATIVE:
+ /* Chapter 9.2.3.12.1 */
+ vp = *(sms->vp);
+ if (vp <= 143)
+ minutes = vp + 1 * 5;
+ else if (vp <= 167)
+ minutes = 12*60 + (vp-143) * 30;
+ else if (vp <= 196)
+ minutes = vp-166 * 60 * 24;
+ else
+ minutes = vp-192 * 60 * 24 * 7;
+ break;
+ case GSM340_TP_VPF_ABSOLUTE:
+ /* Chapter 9.2.3.12.2 */
+ /* FIXME: like service center time stamp */
+ DEBUGP(DSMS, "VPI absolute not implemented yet\n");
+ break;
+ case GSM340_TP_VPF_ENHANCED:
+ /* Chapter 9.2.3.12.3 */
+ /* FIXME: implementation */
+ DEBUGP(DSMS, "VPI enhanced not implemented yet\n");
+ break;
+ }
+ return minutes;
+}
+
+/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
+enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
+{
+ u_int8_t cgbits = dcs >> 4;
+ enum sms_alphabet alpha = DCS_NONE;
+
+ if ((cgbits & 0xc) == 0) {
+ if (cgbits & 2)
+ DEBUGP(DSMS, "Compressed SMS not supported yet\n");
+
+ switch (dcs & 3) {
+ case 0:
+ alpha = DCS_7BIT_DEFAULT;
+ break;
+ case 1:
+ alpha = DCS_8BIT_DATA;
+ break;
+ case 2:
+ alpha = DCS_UCS2;
+ break;
+ }
+ } else if (cgbits == 0xc || cgbits == 0xd)
+ alpha = DCS_7BIT_DEFAULT;
+ else if (cgbits == 0xe)
+ alpha = DCS_UCS2;
+ else if (cgbits == 0xf) {
+ if (dcs & 4)
+ alpha = DCS_8BIT_DATA;
+ else
+ alpha = DCS_7BIT_DEFAULT;
+ }
+
+ return alpha;
+}
+
+static int gsm340_rx_sms_submit(struct msgb *msg, struct sms_submit *sms,
+ struct gsm_sms *gsms)
+{
+ if (db_sms_store(gsms) != 0) {
+ DEBUGP(DSMS, "Failed to store SMS in Database\n");
+ free(sms);
+ free(gsms);
+ return -EIO;
+ }
+ return 0;
+}
+
+/* process an incoming TPDU (called from RP-DATA) */
+static int gsm340_rx_tpdu(struct msgb *msg)
+{
+ u_int8_t *smsp = msgb_sms(msg);
+ struct sms_submit *sms;
+ struct gsm_sms *gsms;
+ u_int8_t da_len_bytes;
+ u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
+ int rc = 0;
+
+ sms = malloc(sizeof(*sms));
+ if (!sms)
+ return -ENOMEM;
+ memset(sms, 0, sizeof(*sms));
+
+ gsms = malloc(sizeof(*gsms));
+ if (!gsms) {
+ free(sms);
+ return -ENOMEM;
+ }
+ memset(gsms, 0, sizeof(*gsms));
+
+ /* invert those fields where 0 means active/present */
+ sms->mti = *smsp & 0x03;
+ sms->mms = !!(*smsp & 0x04);
+ sms->vpf = (*smsp & 0x18) >> 3;
+ sms->sri = !!(*smsp & 0x20);
+ sms->udhi= !!(*smsp & 0x40);
+ sms->rp = !!(*smsp & 0x80);
+
+ smsp++;
+ sms->msg_ref = *smsp++;
+
+ /* length in bytes of the destination address */
+ da_len_bytes = 2 + *smsp/2 + *smsp%2;
+ if (da_len_bytes > 12) {
+ DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
+ rc = -EIO;
+ goto out;
+ }
+ memcpy(address_lv, smsp, da_len_bytes);
+ /* mangle first byte to reflect length in bytes, not digits */
+ address_lv[0] = da_len_bytes;
+ /* convert to real number */
+ decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv);
+
+ smsp += da_len_bytes;
+
+ sms->pid = *smsp++;
+
+ sms->dcs = *smsp++;
+ sms->alphabet = gsm338_get_sms_alphabet(sms->dcs);
+
+ switch (sms->vpf) {
+ case GSM340_TP_VPF_RELATIVE:
+ sms->vp = smsp++;
+ break;
+ case GSM340_TP_VPF_ABSOLUTE:
+ case GSM340_TP_VPF_ENHANCED:
+ sms->vp = smsp;
+ smsp += 7;
+ break;
+ default:
+ DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
+ sms->vpf);
+ }
+ sms->ud_len = *smsp++;
+ if (sms->ud_len)
+ sms->user_data = smsp;
+ else
+ sms->user_data = NULL;
+
+ if (sms->ud_len) {
+ switch (sms->alphabet) {
+ case DCS_7BIT_DEFAULT:
+ gsm_7bit_decode(sms->decoded, smsp, sms->ud_len);
+ break;
+ case DCS_8BIT_DATA:
+ case DCS_UCS2:
+ case DCS_NONE:
+ memcpy(sms->decoded, sms->user_data, sms->ud_len);
+ break;
+ }
+ }
+
+ DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
+ "PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
+ "UserData: \"%s\"\n", sms->mti, sms->vpf, sms->msg_ref,
+ sms->pid, sms->dcs, sms->dest_addr, sms->ud_len,
+ sms->alphabet == DCS_7BIT_DEFAULT ? sms->decoded : hexdump(sms->user_data, sms->ud_len));
+
+ dispatch_signal(SS_SMS, 0, sms);
+
+ gsms->sender = msg->lchan->subscr;
+ /* FIXME: sender refcount */
+
+ /* determine gsms->receiver based on dialled number */
+ gsms->receiver = subscr_get_by_extension(sms->dest_addr);
+ if (!gsms->receiver) {
+ rc = 1; /* cause 1: unknown subscriber */
+ goto out;
+ }
+
+ if (sms->user_data)
+ strncpy(gsms->text, sms->decoded, sizeof(gsms->text));
+
+ switch (sms->mti) {
+ case GSM340_SMS_SUBMIT_MS2SC:
+ /* MS is submitting a SMS */
+ rc = gsm340_rx_sms_submit(msg, sms, gsms);
+ break;
+ case GSM340_SMS_COMMAND_MS2SC:
+ case GSM340_SMS_DELIVER_REP_MS2SC:
+ DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms->mti);
+ break;
+ default:
+ DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms->mti);
+ break;
+ }
+
+out:
+ free(gsms);
+ free(sms);
+
+ return rc;
+}
+
+static int gsm411_send_rp_ack(struct gsm_lchan *lchan, u_int8_t trans_id,
+ u_int8_t msg_ref)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm411_rp_hdr *rp;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ // Outgoing needs the highest bit set
+ gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
+ gh->msg_type = GSM411_MT_CP_DATA;
+
+ rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
+ rp->len = 2;
+ rp->msg_type = GSM411_MT_RP_ACK_MT;
+ rp->msg_ref = msg_ref;
+
+ DEBUGP(DSMS, "TX: SMS RP ACK\n");
+
+ return gsm0411_sendmsg(msg);
+}
+
+static int gsm411_send_rp_error(struct gsm_lchan *lchan, u_int8_t trans_id,
+ u_int8_t msg_ref, u_int8_t cause)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm411_rp_hdr *rp;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ // Outgoing needs the highest bit set
+ gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
+ gh->msg_type = GSM411_MT_CP_DATA;
+
+ rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
+ rp->msg_type = GSM411_MT_RP_ERROR_MT;
+ rp->msg_ref = msg_ref;
+ msgb_tv_put(msg, 1, cause);
+
+ DEBUGP(DSMS, "TX: SMS RP ERROR (cause %02d)\n", cause);
+
+ return gsm0411_sendmsg(msg);
+}
+
+/* Receive a 04.11 TPDU inside RP-DATA / user data */
+static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm411_rp_hdr *rph,
+ u_int8_t src_len, u_int8_t *src,
+ u_int8_t dst_len, u_int8_t *dst,
+ u_int8_t tpdu_len, u_int8_t *tpdu)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t trans_id = gh->proto_discr >> 4;
+ int rc = 0;
+
+ if (src_len && src)
+ DEBUGP(DSMS, "RP-DATA (MO) with SRC ?!?\n");
+
+ if (!dst_len || !dst || !tpdu_len || !tpdu) {
+ DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
+ return -EIO;
+ }
+ msg->smsh = tpdu;
+
+ DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
+ //return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
+
+ rc = gsm340_rx_tpdu(msg);
+ if (rc == 0)
+ return gsm411_send_rp_ack(msg->lchan, trans_id, rph->msg_ref);
+ else if (rc > 0)
+ return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
+ else
+ return rc;
+}
+
+/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */
+static int gsm411_rx_rp_data(struct msgb *msg, struct gsm411_rp_hdr *rph)
+{
+ u_int8_t src_len, dst_len, rpud_len;
+ u_int8_t *src = NULL, *dst = NULL , *rp_ud = NULL;
+
+ /* in the MO case, this should always be zero length */
+ src_len = rph->data[0];
+ if (src_len)
+ src = &rph->data[1];
+
+ dst_len = rph->data[1+src_len];
+ if (dst_len)
+ dst = &rph->data[1+src_len+1];
+
+ rpud_len = rph->data[1+src_len+1+dst_len];
+ if (rpud_len)
+ rp_ud = &rph->data[1+src_len+1+dst_len+1];
+
+ DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", src_len, dst_len, rpud_len);
+ return gsm411_rx_rp_ud(msg, rph, src_len, src, dst_len, dst,
+ rpud_len, rp_ud);
+}
+
+static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh)
+{
+ struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
+ u_int8_t msg_type = rp_data->msg_type & 0x07;
+ int rc = 0;
+
+ switch (msg_type) {
+ case GSM411_MT_RP_DATA_MO:
+ DEBUGP(DSMS, "SMS RP-DATA (MO)\n");
+ rc = gsm411_rx_rp_data(msg, rp_data);
+ break;
+ case GSM411_MT_RP_ACK_MO:
+ /* Acnkowledgement to MT RP_DATA */
+ case GSM411_MT_RP_ERROR_MO:
+ /* Error in response to MT RP_DATA */
+ case GSM411_MT_RP_SMMA_MO:
+ /* MS tells us that it has memory for more SMS, we need
+ * to check if we have any pending messages for it and then
+ * transfer those */
+ DEBUGP(DSMS, "Unimplemented RP type 0x%02x\n", msg_type);
+ break;
+ default:
+ DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
+ break;
+ }
+
+ return rc;
+}
+
+int gsm0411_rcv_sms(struct msgb *msg)
+{
+ struct gsm48_hdr *gh = msgb_l3(msg);
+ u_int8_t msg_type = gh->msg_type;
+ int rc = 0;
+
+ switch(msg_type) {
+ case GSM411_MT_CP_DATA:
+ DEBUGP(DSMS, "SMS CP-DATA\n");
+ rc = gsm411_rx_cp_data(msg, gh);
+ break;
+ case GSM411_MT_CP_ACK:
+ DEBUGP(DSMS, "SMS CP-ACK\n");
+ break;
+ case GSM411_MT_CP_ERROR:
+ DEBUGP(DSMS, "SMS CP-ERROR, cause 0x%02x\n", gh->data[0]);
+ break;
+ default:
+ DEBUGP(DSMS, "Unimplemented CP msg_type: 0x%02x\n", msg_type);
+ break;
+ }
+
+
+ return rc;
+}
+
+/* Test TPDU - 25c3 welcome */
+#if 0
+static u_int8_t tpdu_test[] = {
+ 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x92, 0x90, 0x32,
+ 0x24, 0x40, 0x4D, 0xB2, 0xDA, 0x70, 0xD6, 0x9A, 0x97, 0xE5, 0xF6, 0xF4,
+ 0xB8, 0x0C, 0x0A, 0xBB, 0xDD, 0xEF, 0xBA, 0x7B, 0x5C, 0x6E, 0x97, 0xDD,
+ 0x74, 0x1D, 0x08, 0xCA, 0x2E, 0x87, 0xE7, 0x65, 0x50, 0x98, 0x4E, 0x2F,
+ 0xBB, 0xC9, 0x20, 0x3A, 0xBA, 0x0C, 0x3A, 0x4E, 0x9B, 0x20, 0x7A, 0x98,
+ 0xBD, 0x06, 0x85, 0xE9, 0xA0, 0x58, 0x4C, 0x37, 0x83, 0x81, 0xD2, 0x6E,
+ 0xD0, 0x34, 0x1C, 0x66, 0x83, 0x62, 0x21, 0x90, 0xAE, 0x95, 0x02
+};
+#else
+/* Test TPDU - ALL YOUR */
+static u_int8_t tpdu_test[] = {
+ 0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24,
+ 0x32, 0x40, 0x1F, 0x41, 0x26, 0x13, 0x94, 0x7D, 0x56, 0xA5, 0x20, 0x28,
+ 0xF2, 0xE9, 0x2C, 0x82, 0x82, 0xD2, 0x22, 0x48, 0x58, 0x64, 0x3E, 0x9D,
+ 0x47, 0x10, 0xF5, 0x09, 0xAA, 0x4E, 0x01
+};
+#endif
+
+int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms)
+{
+ struct msgb *msg = gsm411_msgb_alloc();
+ struct gsm48_hdr *gh;
+ struct gsm411_rp_hdr *rp;
+ u_int8_t *data;
+
+ msg->lchan = lchan;
+
+ gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+ gh->proto_discr = GSM48_PDISC_SMS;
+ gh->msg_type = GSM411_MT_CP_DATA;
+
+ rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
+ rp->len = sizeof(tpdu_test) + 10;
+ rp->msg_type = GSM411_MT_RP_DATA_MT;
+ rp->msg_ref = 42; /* FIXME: Choose randomly */
+ /* Hardcode OA for now */
+ data = (u_int8_t *)msgb_put(msg, 8);
+ data[0] = 0x07;
+ data[1] = 0x91;
+ data[2] = 0x44;
+ data[3] = 0x77;
+ data[4] = 0x58;
+ data[5] = 0x10;
+ data[6] = 0x06;
+ data[7] = 0x50;
+ data = (u_int8_t *)msgb_put(msg, 1);
+ data[0] = 0;
+
+ /* FIXME: Hardcoded for now */
+ //smslen = gsm0411_tpdu_from_sms(tpdu, sms);
+
+ /* RPDU length */
+ data = (u_int8_t *)msgb_put(msg, 1);
+ data[0] = sizeof(tpdu_test);
+
+ data = (u_int8_t *)msgb_put(msg, sizeof(tpdu_test));
+
+ //memcpy(data, tpdu, smslen);
+ memcpy(data, tpdu_test, sizeof(tpdu_test));
+
+ DEBUGP(DSMS, "TX: SMS SUBMIT\n");
+
+ return gsm0411_sendmsg(msg);
+}
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
new file mode 100644
index 000000000..a78425f95
--- /dev/null
+++ b/openbsc/src/gsm_data.c
@@ -0,0 +1,209 @@
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openbsc/gsm_data.h>
+
+void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
+ u_int8_t e1_ts, u_int8_t e1_ts_ss)
+{
+ ts->e1_link.e1_nr = e1_nr;
+ ts->e1_link.e1_ts = e1_ts;
+ ts->e1_link.e1_ts_ss = e1_ts_ss;
+}
+
+static const char *pchan_names[] = {
+ [GSM_PCHAN_NONE] = "NONE",
+ [GSM_PCHAN_CCCH] = "CCCH",
+ [GSM_PCHAN_CCCH_SDCCH4] = "CCCH+SDCCH4",
+ [GSM_PCHAN_TCH_F] = "TCH/F",
+ [GSM_PCHAN_TCH_H] = "TCH/H",
+ [GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8",
+ [GSM_PCHAN_UNKNOWN] = "UNKNOWN",
+};
+
+const char *gsm_pchan_name(enum gsm_phys_chan_config c)
+{
+ if (c >= ARRAY_SIZE(pchan_names))
+ return "INVALID";
+
+ return pchan_names[c];
+}
+
+static const char *lchan_names[] = {
+ [GSM_LCHAN_NONE] = "NONE",
+ [GSM_LCHAN_SDCCH] = "SDCCH",
+ [GSM_LCHAN_TCH_F] = "TCH/F",
+ [GSM_LCHAN_TCH_H] = "TCH/H",
+ [GSM_LCHAN_UNKNOWN] = "UNKNOWN",
+};
+
+const char *gsm_lchan_name(enum gsm_chan_t c)
+{
+ if (c >= ARRAY_SIZE(lchan_names))
+ return "INVALID";
+
+ return lchan_names[c];
+}
+
+static const char *chreq_names[] = {
+ [GSM_CHREQ_REASON_EMERG] = "EMERGENCY",
+ [GSM_CHREQ_REASON_PAG] = "PAGING",
+ [GSM_CHREQ_REASON_CALL] = "CALL",
+ [GSM_CHREQ_REASON_LOCATION_UPD] = "LOCATION_UPDATE",
+ [GSM_CHREQ_REASON_OTHER] = "OTHER",
+};
+
+const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
+{
+ if (c >= ARRAY_SIZE(chreq_names))
+ return "INVALID";
+
+ return chreq_names[c];
+}
+
+struct gsm_network *gsm_network_init(unsigned int num_bts, enum gsm_bts_type bts_type,
+ u_int16_t country_code, u_int16_t network_code)
+{
+ int i;
+ struct gsm_network *net;
+
+ if (num_bts > GSM_MAX_BTS)
+ return NULL;
+
+ net = malloc(sizeof(*net));
+ if (!net)
+ return NULL;
+ memset(net, 0, sizeof(*net));
+
+ net->country_code = country_code;
+ net->network_code = network_code;
+ net->num_bts = num_bts;
+
+ for (i = 0; i < num_bts; i++) {
+ struct gsm_bts *bts = &net->bts[i];
+ int j;
+
+ bts->network = net;
+ bts->nr = i;
+ bts->type = bts_type;
+ bts->tsc = HARDCODED_TSC;
+ bts->bsic = HARDCODED_BSIC;
+
+ for (j = 0; j < BTS_MAX_TRX; j++) {
+ struct gsm_bts_trx *trx = &bts->trx[j];
+ int k;
+
+ trx->bts = bts;
+ trx->nr = j;
+
+ for (k = 0; k < 8; k++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[k];
+ int l;
+
+ ts->trx = trx;
+ ts->nr = k;
+ ts->pchan = GSM_PCHAN_NONE;
+
+ for (l = 0; l < TS_MAX_LCHAN; l++) {
+ struct gsm_lchan *lchan;
+ lchan = &ts->lchan[l];
+
+ lchan->ts = ts;
+ lchan->nr = l;
+ lchan->type = GSM_LCHAN_NONE;
+ }
+ }
+ }
+
+ bts->num_trx = 1; /* FIXME */
+#ifdef HAVE_TRX1
+ bts->num_trx++;
+#endif
+ bts->c0 = &bts->trx[0];
+ bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4;
+ }
+ return net;
+}
+
+static char ts2str[255];
+
+char *gsm_ts_name(struct gsm_bts_trx_ts *ts)
+{
+ snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)",
+ ts->trx->bts->bts_nr, ts->trx->nr, ts->nr);
+
+ return ts2str;
+}
+
+static const char *bts_types[] = {
+ [GSM_BTS_TYPE_UNKNOWN] = "unknown",
+ [GSM_BTS_TYPE_BS11] = "bs11",
+ [GSM_BTS_TYPE_NANOBTS_900] = "nanobts900",
+ [GSM_BTS_TYPE_NANOBTS_1800] = "nanobts1800",
+};
+
+enum gsm_bts_type parse_btstype(char *arg)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(bts_types); i++) {
+ if (!strcmp(arg, bts_types[i]))
+ return i;
+ }
+ return GSM_BTS_TYPE_BS11; /* Default: BS11 */
+}
+
+char *btstype2str(enum gsm_bts_type type)
+{
+ if (type > ARRAY_SIZE(bts_types))
+ return "undefined";
+ return bts_types[type];
+}
+
+/* Search for a BTS in the given Location Area; optionally start searching
+ * with start_bts (for continuing to search after the first result) */
+struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
+ struct gsm_bts *start_bts)
+{
+ int i;
+ struct gsm_bts *bts;
+ int skip = 0;
+
+ if (start_bts)
+ skip = 1;
+
+ for (i = 0; i < net->num_bts; i++) {
+ bts = &net->bts[i];
+
+ if (skip) {
+ if (start_bts == bts)
+ skip = 0;
+ continue;
+ }
+
+ if (bts->location_area_code == lac)
+ return bts;
+ }
+ return NULL;
+}
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
new file mode 100644
index 000000000..3f608ec30
--- /dev/null
+++ b/openbsc/src/gsm_subscriber.c
@@ -0,0 +1,143 @@
+/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */
+
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/debug.h>
+#include <openbsc/db.h>
+
+
+LLIST_HEAD(active_subscribers);
+
+struct gsm_subscriber *subscr_alloc(void)
+{
+ struct gsm_subscriber *s;
+
+ s = malloc(sizeof(struct gsm_subscriber));
+ if (!s)
+ return NULL;
+
+ memset(s, 0, sizeof(*s));
+ llist_add_tail(&s->entry, &active_subscribers);
+ s->use_count = 1;
+
+ return s;
+}
+
+static void subscr_free(struct gsm_subscriber *subscr)
+{
+ llist_del(&subscr->entry);
+ free(subscr);
+}
+
+struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi)
+{
+ struct gsm_subscriber *subscr;
+
+ /* we might have a record in memory already */
+ llist_for_each_entry(subscr, &active_subscribers, entry) {
+ if (strcmp(subscr->tmsi, tmsi) == 0)
+ return subscr_get(subscr);
+ }
+
+ return db_get_subscriber(GSM_SUBSCRIBER_TMSI, tmsi);
+}
+
+struct gsm_subscriber *subscr_get_by_imsi(const char *imsi)
+{
+ struct gsm_subscriber *subscr;
+
+ llist_for_each_entry(subscr, &active_subscribers, entry) {
+ if (strcmp(subscr->imsi, imsi) == 0)
+ return subscr_get(subscr);
+ }
+
+ return db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
+}
+
+struct gsm_subscriber *subscr_get_by_extension(const char *ext)
+{
+ struct gsm_subscriber *subscr;
+
+ llist_for_each_entry(subscr, &active_subscribers, entry) {
+ if (strcmp(subscr->extension, ext) == 0)
+ return subscr_get(subscr);
+ }
+
+ return db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, ext);
+}
+
+int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
+{
+ /* FIXME: Migrate pending requests from one BSC to another */
+ switch (reason) {
+ case GSM_SUBSCRIBER_UPDATE_ATTACHED:
+ /* Indicate "attached to LAC" */
+ s->lac = bts->location_area_code;
+ break;
+ case GSM_SUBSCRIBER_UPDATE_DETACHED:
+ /* Only detach if we are currently in this area */
+ if (bts->location_area_code == s->lac)
+ s->lac = 0;
+
+ break;
+ default:
+ fprintf(stderr, "subscr_update with unknown reason: %d\n",
+ reason);
+ break;
+ };
+ return db_sync_subscriber(s);
+}
+
+struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
+{
+ subscr->use_count++;
+ DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
+ subscr->extension, subscr->use_count);
+ return subscr;
+}
+
+struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
+{
+ subscr->use_count--;
+ DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
+ subscr->extension, subscr->use_count);
+ if (subscr->use_count <= 0)
+ subscr_free(subscr);
+ return NULL;
+}
+
+void subscr_put_channel(struct gsm_lchan *lchan)
+{
+ /*
+ * FIXME: Continue with other requests now... by checking
+ * the gsm_subscriber inside the gsm_lchan. Drop the ref count
+ * of the lchan after having asked the next requestee to handle
+ * the channel.
+ */
+ put_lchan(lchan);
+}
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
new file mode 100644
index 000000000..0c25b28c6
--- /dev/null
+++ b/openbsc/src/gsm_utils.c
@@ -0,0 +1,73 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/gsm_utils.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_decode(char *text, const u_int8_t *user_data, u_int8_t length)
+{
+ u_int8_t d_off = 0, b_off = 0;
+ u_int8_t i;
+
+ for (i=0;i<length;i++) {
+ text[i] = ((user_data[d_off] + (user_data[d_off+1]<<8)) & (0x7f<<b_off))>>b_off;
+ b_off += 7;
+ if (b_off >= 8) {
+ d_off += 1;
+ b_off -= 8;
+ }
+ }
+ text[i] = '\0';
+ return 0;
+}
+
+/* GSM 03.38 6.2.1 Charachter packing */
+int gsm_7bit_encode(u_int8_t *result, const char *data)
+{
+ int i;
+ u_int8_t d_off = 0, b_off = 0;
+ const int length = strlen(data);
+ int out_length = (length * 8)/7;
+
+ memset(result, 0, out_length);
+
+ for (i = 0; i < length; ++i) {
+ u_int8_t first = (data[i] & 0x7f) << b_off;
+ u_int8_t second = (data[i] & 0x7f) >> (8 - b_off);
+
+ result[d_off] |= first;
+ if (second != 0)
+ result[d_off + 1] = second;
+
+ b_off += 7;
+
+ if (b_off >= 8) {
+ d_off += 1;
+ b_off -= 8;
+ }
+ }
+
+ return out_length;
+}
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
new file mode 100644
index 000000000..ea7f847c2
--- /dev/null
+++ b/openbsc/src/input/ipaccess.c
@@ -0,0 +1,597 @@
+/* OpenBSC Abis input driver for ip.access */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include <openbsc/select.h>
+#include <openbsc/tlv.h>
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/ipaccess.h>
+
+/* data structure for one E1 interface with A-bis */
+struct ia_e1_handle {
+ struct bsc_fd listen_fd;
+ struct bsc_fd rsl_listen_fd;
+ struct gsm_network *gsmnet;
+};
+
+static struct ia_e1_handle *e1h;
+
+
+#define TS1_ALLOC_SIZE 300
+
+static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
+static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
+static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
+ 0x01, IPAC_IDTAG_UNIT,
+ 0x01, IPAC_IDTAG_MACADDR,
+ 0x01, IPAC_IDTAG_LOCATION1,
+ 0x01, IPAC_IDTAG_LOCATION2,
+ 0x01, IPAC_IDTAG_EQUIPVERS,
+ 0x01, IPAC_IDTAG_SWVERSION,
+ 0x01, IPAC_IDTAG_UNITNAME,
+ 0x01, IPAC_IDTAG_SERNR,
+ };
+
+static const char *idtag_names[] = {
+ [IPAC_IDTAG_SERNR] = "Serial_Number",
+ [IPAC_IDTAG_UNITNAME] = "Unit_Name",
+ [IPAC_IDTAG_LOCATION1] = "Location_1",
+ [IPAC_IDTAG_LOCATION2] = "Location_2",
+ [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
+ [IPAC_IDTAG_SWVERSION] = "Software_Version",
+ [IPAC_IDTAG_IPADDR] = "IP_Address",
+ [IPAC_IDTAG_MACADDR] = "MAC_Address",
+ [IPAC_IDTAG_UNIT] = "Unit_ID",
+};
+
+static const char *ipac_idtag_name(int tag)
+{
+ if (tag >= ARRAY_SIZE(idtag_names))
+ return "unknown";
+
+ return idtag_names[tag];
+}
+
+static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
+{
+ u_int8_t t_len;
+ u_int8_t t_tag;
+ u_int8_t *cur = buf;
+
+ while (cur < buf + len) {
+ t_len = *cur++;
+ t_tag = *cur++;
+
+ DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
+
+ dec->lv[t_tag].len = t_len;
+ dec->lv[t_tag].val = cur;
+
+ cur += t_len;
+ }
+ return 0;
+}
+
+struct gsm_bts *find_bts_by_unitid(struct gsm_network *net,
+ u_int16_t site_id, u_int16_t bts_id)
+{
+ int i;
+
+ for (i = 0; i < net->num_bts; i++) {
+ struct gsm_bts *bts = &net->bts[i];
+
+ if (!is_ipaccess_bts(bts))
+ continue;
+
+ if (bts->ip_access.site_id == site_id &&
+ bts->ip_access.bts_id == bts_id)
+ return bts;
+ }
+
+ return NULL;
+}
+
+static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
+ u_int16_t *trx_id)
+{
+ unsigned long ul;
+ char *endptr;
+ const char *nptr;
+
+ nptr = str;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ if (site_id)
+ *site_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ if (bts_id)
+ *bts_id = ul & 0xffff;
+
+ if (*endptr++ != '/')
+ return -EINVAL;
+
+ nptr = endptr;
+ ul = strtoul(nptr, &endptr, 10);
+ if (endptr <= nptr)
+ return -EINVAL;
+ if (trx_id)
+ *trx_id = ul & 0xffff;
+
+ return 0;
+}
+
+static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
+ struct bsc_fd *bfd)
+{
+ struct tlv_parsed tlvp;
+ u_int8_t msg_type = *(msg->l2h);
+ u_int16_t site_id, bts_id, trx_id;
+ struct gsm_bts *bts;
+ int ret = 0;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ret = write(bfd->fd, pong, sizeof(pong));
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DMI, "PONG!\n");
+ break;
+ case IPAC_MSGT_ID_RESP:
+ DEBUGP(DMI, "ID_RESP ");
+ /* parse tags, search for Unit ID */
+ ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
+ msgb_l2len(msg)-2);
+ DEBUGP(DMI, "\n");
+
+ if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT))
+ break;
+
+ /* lookup BTS, create sign_link, ... */
+ parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
+ &site_id, &bts_id, &trx_id);
+ bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id);
+ if (!bts) {
+ DEBUGP(DINP, "Unable to find BTS configuration for "
+ " %u/%u/%u, disconnecting\n", site_id, bts_id,
+ trx_id);
+ return -EIO;
+ }
+ DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
+ if (bfd->priv_nr == 1) {
+ bts->oml_link = e1inp_sign_link_create(&line->ts[1-1],
+ E1INP_SIGN_OML, bts->c0,
+ 0, 0xff);
+ } else if (bfd->priv_nr == 2) {
+ struct e1inp_ts *e1i_ts;
+ struct bsc_fd *newbfd;
+
+ /* FIXME: implement this for non-0 TRX */
+ bfd->data = line = bts->oml_link->ts->line;
+ e1i_ts = &line->ts[2-1];
+ newbfd = &e1i_ts->driver.ipaccess.fd;
+
+ bts->c0->rsl_link =
+ e1inp_sign_link_create(e1i_ts,
+ E1INP_SIGN_RSL, bts->c0,
+ 0, 0);
+ /* get rid of our old temporary bfd */
+ memcpy(newbfd, bfd, sizeof(*newbfd));
+ bsc_unregister_fd(bfd);
+ bsc_register_fd(newbfd);
+ free(bfd);
+ }
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DMI, "ID_ACK? -> ACK!\n");
+ ret = write(bfd->fd, id_ack, sizeof(id_ack));
+ break;
+ }
+ return 0;
+}
+
+/* FIXME: this is per BTS */
+static int oml_up = 0;
+static int rsl_up = 0;
+
+static int handle_ts1_read(struct bsc_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct e1inp_sign_link *link;
+ struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
+ struct ipaccess_head *hh;
+ int ret;
+
+ if (!msg)
+ return -ENOMEM;
+
+ /* first read our 3-byte header */
+ hh = (struct ipaccess_head *) msg->data;
+ ret = recv(bfd->fd, msg->data, 3, 0);
+ if (ret < 0) {
+ fprintf(stderr, "recv error %s\n", strerror(errno));
+ return ret;
+ }
+ if (ret == 0) {
+ fprintf(stderr, "BTS disappeared, dead socket\n");
+ e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
+ e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ }
+ msgb_put(msg, ret);
+
+ /* then read te length as specified in header */
+ msg->l2h = msg->data + sizeof(*hh);
+ ret = recv(bfd->fd, msg->l2h, hh->len, 0);
+ if (ret < hh->len) {
+ fprintf(stderr, "short read!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msgb_put(msg, ret);
+ DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), ret));
+
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ ret = ipaccess_rcvmsg(line, msg, bfd);
+ if (ret < 0) {
+ e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
+ e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
+ bsc_unregister_fd(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ }
+ msgb_free(msg);
+ return ret;
+ }
+ /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
+ * might have free'd it !!! */
+
+ link = e1inp_lookup_sign_link(e1i_ts, 0, hh->proto);
+ if (!link) {
+ printf("no matching signalling link for hh->proto=0x%02x\n", hh->proto);
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->trx = link->trx;
+
+ switch (hh->proto) {
+ case IPAC_PROTO_RSL:
+ if (!rsl_up) {
+ e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_RSL);
+ rsl_up = 1;
+ }
+ ret = abis_rsl_rcvmsg(msg);
+ break;
+ case IPAC_PROTO_OML:
+ if (!oml_up) {
+ e1inp_event(e1i_ts, EVT_E1_TEI_UP, 0, IPAC_PROTO_OML);
+ oml_up = 1;
+ }
+ ret = abis_nm_rcvmsg(msg);
+ break;
+ default:
+ DEBUGP(DMI, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
+ msgb_free(msg);
+ break;
+ }
+ return ret;
+}
+
+static int handle_ts1_write(struct bsc_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct e1inp_sign_link *sign_link;
+ struct msgb *msg;
+ struct ipaccess_head *hh;
+ u_int8_t *l2_data;
+ int ret;
+
+ /* get the next msg for this timeslot */
+ msg = e1inp_tx_ts(e1i_ts, &sign_link);
+ if (!msg) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+
+ l2_data = msg->data;
+
+ /* prepend the ip.access header */
+ hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
+ hh->zero = 0;
+ hh->len = msg->len - sizeof(*hh);
+
+ switch (sign_link->type) {
+ case E1INP_SIGN_OML:
+ hh->proto = IPAC_PROTO_OML;
+ break;
+ case E1INP_SIGN_RSL:
+ hh->proto = IPAC_PROTO_RSL;
+ break;
+ default:
+ msgb_free(msg);
+ return -EINVAL;
+ }
+
+ DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(l2_data, hh->len));
+
+ ret = send(bfd->fd, msg->data, msg->len, 0);
+ msgb_free(msg);
+ usleep(100000);
+
+ return ret;
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ unsigned int idx = ts_nr-1;
+ struct e1inp_ts *e1i_ts;
+ int rc = 0;
+
+ /* In case of early RSL we might not yet have a line */
+
+ if (line)
+ e1i_ts = &line->ts[idx];
+
+ if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) {
+ if (what & BSC_FD_READ)
+ rc = handle_ts1_read(bfd);
+ if (what & BSC_FD_WRITE)
+ rc = handle_ts1_write(bfd);
+ } else
+ fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
+
+ return rc;
+}
+
+
+static int ts_want_write(struct e1inp_ts *e1i_ts)
+{
+ e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+struct e1inp_driver ipaccess_driver = {
+ .name = "ip.access",
+ .want_write = ts_want_write,
+};
+
+/* callback of the OML listening filedescriptor */
+static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
+{
+ int ret;
+ int idx = 0;
+ struct e1inp_line *line;
+ struct e1inp_ts *e1i_ts;
+ struct bsc_fd *bfd;
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (!(what & BSC_FD_READ))
+ return 0;
+
+ ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
+ if (ret < 0) {
+ perror("accept");
+ return ret;
+ }
+ DEBUGP(DINP, "accept()ed new OML link from %s\n", inet_ntoa(sa.sin_addr));
+
+ line = malloc(sizeof(*line));
+ if (!line) {
+ close(ret);
+ return -ENOMEM;
+ }
+ memset(line, 0, sizeof(*line));
+ line->driver = &ipaccess_driver;
+ //line->driver_data = e1h;
+ /* create virrtual E1 timeslots for signalling */
+ e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
+ e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN);
+
+ e1i_ts = &line->ts[idx];
+
+ bfd = &e1i_ts->driver.ipaccess.fd;
+ bfd->fd = ret;
+ bfd->data = line;
+ bfd->priv_nr = 1;
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ;
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ fprintf(stderr, "could not register FD\n");
+ close(bfd->fd);
+ free(line);
+ return ret;
+ }
+
+ /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
+ ret = write(bfd->fd, id_req, sizeof(id_req));
+
+ return e1inp_line_register(line);
+}
+
+static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
+{
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+ struct bsc_fd *bfd = malloc(sizeof(*bfd));
+ int ret;
+
+ if (!(what & BSC_FD_READ))
+ return 0;
+
+ /* Some BTS has connected to us, but we don't know yet which line
+ * (as created by the OML link) to associate it with. Thus, we
+ * aloocate a temporary bfd until we have received ID from BTS */
+
+ bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
+ if (bfd->fd < 0) {
+ perror("accept");
+ return bfd->fd;
+ }
+ DEBUGP(DINP, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
+ bfd->priv_nr = 2;
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ;
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ fprintf(stderr, "could not register FD\n");
+ close(bfd->fd);
+ free(bfd);
+ return ret;
+ }
+ /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
+ ret = write(bfd->fd, id_req, sizeof(id_req));
+
+ return 0;
+}
+
+static int make_sock(struct bsc_fd *bfd, u_int16_t port,
+ int (*cb)(struct bsc_fd *fd, unsigned int what))
+{
+ struct sockaddr_in addr;
+ int ret, on = 1;
+
+ bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ bfd->cb = cb;
+ bfd->when = BSC_FD_READ;
+ //bfd->data = line;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (ret < 0) {
+ fprintf(stderr, "could not bind l2 socket %s\n",
+ strerror(errno));
+ return -EIO;
+ }
+
+ ret = listen(bfd->fd, 1);
+ if (ret < 0) {
+ perror("listen");
+ return ret;
+ }
+
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ perror("register_listen_fd");
+ return ret;
+ }
+ return 0;
+}
+
+/* Actively connect to a BTS. Currently used by ipaccess-config.c */
+int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
+{
+ struct e1inp_ts *e1i_ts = &line->ts[0];
+ struct bsc_fd *bfd = &e1i_ts->driver.ipaccess.fd;
+ int ret, on = 1;
+
+ bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd->data = line;
+ bfd->priv_nr = 1;
+
+ setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
+ if (ret < 0) {
+ fprintf(stderr, "could not connect socket\n");
+ close(bfd->fd);
+ return ret;
+ }
+
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ close(bfd->fd);
+ return ret;
+ }
+
+ line->driver = &ipaccess_driver;
+
+ return e1inp_line_register(line);
+}
+
+int ipaccess_setup(struct gsm_network *gsmnet)
+{
+ int ret;
+
+ /* register the driver with the core */
+ /* FIXME: do this in the plugin initializer function */
+ ret = e1inp_driver_register(&ipaccess_driver);
+ if (ret)
+ return ret;
+
+ e1h = malloc(sizeof(*e1h));
+ memset(e1h, 0, sizeof(*e1h));
+ e1h->gsmnet = gsmnet;
+
+ /* Listen for OML connections */
+ ret = make_sock(&e1h->listen_fd, 3002, listen_fd_cb);
+ if (ret < 0)
+ return ret;
+
+ /* Listen for RSL connections */
+ ret = make_sock(&e1h->rsl_listen_fd, 3003, rsl_listen_fd_cb);
+
+ return ret;
+}
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
new file mode 100644
index 000000000..de8d41f21
--- /dev/null
+++ b/openbsc/src/input/misdn.c
@@ -0,0 +1,542 @@
+/* OpenBSC Abis input driver for mISDNuser */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <mISDNif.h>
+
+//#define AF_COMPATIBILITY_FUNC
+//#include <compat_af_isdn.h>
+#define AF_ISDN 34
+#define PF_ISDN AF_ISDN
+
+#include <openbsc/select.h>
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/e1_input.h>
+
+/* data structure for one E1 interface with A-bis */
+struct mi_e1_handle {
+ /* The mISDN card number of the card we use */
+ int cardnr;
+};
+
+#define TS1_ALLOC_SIZE 300
+
+struct prim_name {
+ unsigned int prim;
+ const char *name;
+};
+
+const struct prim_name prim_names[] = {
+ { PH_CONTROL_IND, "PH_CONTROL_IND" },
+ { PH_DATA_IND, "PH_DATA_IND" },
+ { PH_DATA_CNF, "PH_DATA_CNF" },
+ { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" },
+ { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" },
+ { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" },
+ { DL_RELEASE_IND, "DL_RELEASE_IND" },
+ { DL_RELEASE_CNF, "DL_RELEASE_CNF" },
+ { DL_DATA_IND, "DL_DATA_IND" },
+ { DL_UNITDATA_IND, "DL_UNITDATA_IND" },
+ { DL_INFORMATION_IND, "DL_INFORMATION_IND" },
+ { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" },
+ { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" },
+};
+
+const char *get_prim_name(unsigned int prim)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(prim_names); i++) {
+ if (prim_names[i].prim == prim)
+ return prim_names[i].name;
+ }
+
+ return "UNKNOWN";
+}
+
+static int handle_ts1_read(struct bsc_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct e1inp_sign_link *link;
+ struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
+ struct sockaddr_mISDN l2addr;
+ struct mISDNhead *hh;
+ socklen_t alen;
+ int ret;
+
+ if (!msg)
+ return -ENOMEM;
+
+ hh = (struct mISDNhead *) msg->data;
+
+ alen = sizeof(l2addr);
+ ret = recvfrom(bfd->fd, msg->data, 300, 0,
+ (struct sockaddr *) &l2addr, &alen);
+ if (ret < 0) {
+ fprintf(stderr, "recvfrom error %s\n", strerror(errno));
+ return ret;
+ }
+
+ if (alen != sizeof(l2addr)) {
+ fprintf(stderr, "%s error len\n", __func__);
+ return -EINVAL;
+ }
+
+ msgb_put(msg, ret);
+
+ DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
+ alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);
+
+ DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n",
+ ret, hh->prim, hh->id, get_prim_name(hh->prim));
+
+ switch (hh->prim) {
+ case DL_INFORMATION_IND:
+ /* mISDN tells us which channel number is allocated for this
+ * tuple of (SAPI, TEI). */
+ DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n",
+ l2addr.channel, l2addr.sapi, l2addr.tei);
+ link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
+ if (!link) {
+ DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
+ free(msg);
+ return -EINVAL;
+ }
+ /* save the channel number in the driver private struct */
+ link->driver.misdn.channel = l2addr.channel;
+ break;
+ case DL_ESTABLISH_IND:
+ DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n",
+ l2addr.channel, l2addr.sapi, l2addr.tei);
+ ret = e1inp_event(e1i_ts, EVT_E1_TEI_UP, l2addr.tei, l2addr.sapi);
+ break;
+ case DL_RELEASE_IND:
+ DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n",
+ l2addr.channel, l2addr.sapi, l2addr.tei);
+ ret = e1inp_event(e1i_ts, EVT_E1_TEI_DN, l2addr.tei, l2addr.sapi);
+ break;
+ case DL_DATA_IND:
+ msg->l2h = msg->data + MISDN_HEADER_LEN;
+ DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
+ ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
+ break;
+ case PH_ACTIVATE_IND:
+ DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
+ l2addr.channel, l2addr.sapi, l2addr.tei);
+ break;
+ case PH_DEACTIVATE_IND:
+ DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
+ l2addr.channel, l2addr.sapi, l2addr.tei);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int ts_want_write(struct e1inp_ts *e1i_ts)
+{
+ /* We never include the mISDN B-Channel FD into the
+ * writeset, since it doesn't support poll() based
+ * write flow control */
+ if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
+ return 0;
+
+ e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE;
+
+ return 0;
+}
+
+static void timeout_ts1_write(void *data)
+{
+ struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
+
+ /* trigger write of ts1, due to tx delay timer */
+ ts_want_write(e1i_ts);
+}
+
+static int handle_ts1_write(struct bsc_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct e1inp_sign_link *sign_link;
+ struct sockaddr_mISDN sa;
+ struct msgb *msg;
+ struct mISDNhead *hh;
+ u_int8_t *l2_data;
+ int ret;
+
+ bfd->when &= ~BSC_FD_WRITE;
+
+ /* get the next msg for this timeslot */
+ msg = e1inp_tx_ts(e1i_ts, &sign_link);
+ if (!msg) {
+ /* no message after tx delay timer */
+ return 0;
+ }
+
+ l2_data = msg->data;
+
+ /* prepend the mISDNhead */
+ hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
+ hh->prim = DL_DATA_REQ;
+
+ DEBUGP(DMI, "TX TEI(%d) SAPI(%d): %s\n", sign_link->tei,
+ sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN));
+
+ /* construct the sockaddr */
+ sa.family = AF_ISDN;
+ sa.sapi = sign_link->sapi;
+ sa.dev = sign_link->tei;
+ sa.channel = sign_link->driver.misdn.channel;
+
+ ret = sendto(bfd->fd, msg->data, msg->len, 0,
+ (struct sockaddr *)&sa, sizeof(sa));
+ if (ret < 0)
+ fprintf(stderr, "%s sendto failed %d\n", __func__, ret);
+ msgb_free(msg);
+
+ /* set tx delay timer for next event */
+ e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
+ e1i_ts->sign.tx_timer.data = e1i_ts;
+ bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000);
+
+ return ret;
+}
+
+#define BCHAN_TX_GRAN 160
+/* write to a B channel TS */
+static int handle_tsX_write(struct bsc_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct mISDNhead *hh;
+ u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)];
+ struct subch_mux *mx = &e1i_ts->trau.mux;
+ int ret;
+
+ hh = (struct mISDNhead *) tx_buf;
+ hh->prim = PH_DATA_REQ;
+
+ subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN);
+
+ DEBUGP(DMIB, "BCHAN TX: %s\n",
+ hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN));
+
+ ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
+ if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
+ DEBUGP(DMIB, "send returns %d instead of %u\n", ret,
+ sizeof(*hh) + BCHAN_TX_GRAN);
+
+ return ret;
+}
+
+#define TSX_ALLOC_SIZE 4096
+/* FIXME: read from a B channel TS */
+static int handle_tsX_read(struct bsc_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE);
+ struct mISDNhead *hh;
+ int ret;
+
+ if (!msg)
+ return -ENOMEM;
+
+ hh = (struct mISDNhead *) msg->data;
+
+ ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
+ if (ret < 0) {
+ fprintf(stderr, "recvfrom error %s\n", strerror(errno));
+ return ret;
+ }
+
+ msgb_put(msg, ret);
+
+ if (hh->prim != PH_CONTROL_IND)
+ DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n",
+ ret, hh->prim, hh->id, get_prim_name(hh->prim));
+
+ switch (hh->prim) {
+ case PH_DATA_IND:
+ msg->l2h = msg->data + MISDN_HEADER_LEN;
+ DEBUGP(DMIB, "BCHAN RX: %s\n",
+ hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
+ ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
+ break;
+ case PH_ACTIVATE_IND:
+ case PH_DATA_CNF:
+ /* physical layer indicates that data has been sent,
+ * we thus can send some more data */
+ ret = handle_tsX_write(bfd);
+ default:
+ break;
+ }
+ /* FIXME: why do we free signalling msgs in the caller, and trau not? */
+ msgb_free(msg);
+
+ return ret;
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ unsigned int idx = ts_nr-1;
+ struct e1inp_ts *e1i_ts = &line->ts[idx];
+ int rc = 0;
+
+ switch (e1i_ts->type) {
+ case E1INP_TS_TYPE_SIGN:
+ if (what & BSC_FD_READ)
+ rc = handle_ts1_read(bfd);
+ if (what & BSC_FD_WRITE)
+ rc = handle_ts1_write(bfd);
+ break;
+ case E1INP_TS_TYPE_TRAU:
+ if (what & BSC_FD_READ)
+ rc = handle_tsX_read(bfd);
+ /* We never include the mISDN B-Channel FD into the
+ * writeset, since it doesn't support poll() based
+ * write flow control */
+ break;
+ default:
+ fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
+ break;
+ }
+
+ return rc;
+}
+
+static int activate_bchan(struct e1inp_line *line, int ts, int act)
+{
+ struct mISDNhead hh;
+ int ret;
+ unsigned int idx = ts-1;
+ struct e1inp_ts *e1i_ts = &line->ts[idx];
+ struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
+
+ fprintf(stdout, "activate bchan\n");
+ if (act)
+ hh.prim = PH_ACTIVATE_REQ;
+ else
+ hh.prim = PH_DEACTIVATE_REQ;
+
+ hh.id = MISDN_ID_ANY;
+ ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0);
+ if (ret < 0) {
+ fprintf(stdout, "could not send ACTIVATE_RQ %s\n",
+ strerror(errno));
+ }
+
+ return ret;
+}
+
+struct e1inp_driver misdn_driver = {
+ .name = "mISDNuser",
+ .want_write = ts_want_write,
+};
+
+static int mi_e1_setup(struct e1inp_line *line, int release_l2)
+{
+ struct mi_e1_handle *e1h = line->driver_data;
+ int ts, ret;
+
+ /* TS0 is CRC4, don't need any fd for it */
+ for (ts = 1; ts < NUM_E1_TS; ts++) {
+ unsigned int idx = ts-1;
+ struct e1inp_ts *e1i_ts = &line->ts[idx];
+ struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
+ struct sockaddr_mISDN addr;
+
+ bfd->data = line;
+ bfd->priv_nr = ts;
+ bfd->cb = misdn_fd_cb;
+
+ switch (e1i_ts->type) {
+ case E1INP_TS_TYPE_NONE:
+ continue;
+ break;
+ case E1INP_TS_TYPE_SIGN:
+ bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
+ bfd->when = BSC_FD_READ;
+ break;
+ case E1INP_TS_TYPE_TRAU:
+ bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
+ /* We never include the mISDN B-Channel FD into the
+ * writeset, since it doesn't support poll() based
+ * write flow control */
+ bfd->when = BSC_FD_READ;
+ break;
+ }
+
+ if (bfd->fd < 0) {
+ fprintf(stderr, "%s could not open socket %s\n",
+ __func__, strerror(errno));
+ return bfd->fd;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.family = AF_ISDN;
+ addr.dev = e1h->cardnr;
+ switch (e1i_ts->type) {
+ case E1INP_TS_TYPE_SIGN:
+ addr.channel = 0;
+ /* SAPI not supported yet in kernel */
+ //addr.sapi = e1inp_ts->sign.sapi;
+ addr.sapi = 0;
+ addr.tei = GROUP_TEI;
+ break;
+ case E1INP_TS_TYPE_TRAU:
+ addr.channel = ts;
+ break;
+ default:
+ DEBUGP(DMI, "unsupported E1 TS type: %u\n",
+ e1i_ts->type);
+ break;
+ }
+
+ ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
+ if (ret < 0) {
+ fprintf(stderr, "could not bind l2 socket %s\n",
+ strerror(errno));
+ return -EIO;
+ }
+
+ if (e1i_ts->type == E1INP_TS_TYPE_SIGN) {
+ ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2);
+ if (ret < 0) {
+ fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno));
+ return -EIO;
+ }
+ }
+
+ /* FIXME: only activate B-Channels once we start to
+ * use them to conserve CPU power */
+ if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
+ activate_bchan(line, ts, 1);
+
+ ret = bsc_register_fd(bfd);
+ if (ret < 0) {
+ fprintf(stderr, "could not register FD: %s\n",
+ strerror(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mi_setup(int cardnr, struct e1inp_line *line, int release_l2)
+{
+ struct mi_e1_handle *e1h;
+ int sk, ret, cnt;
+ struct mISDN_devinfo devinfo;
+
+ /* register the driver with the core */
+ /* FIXME: do this in the plugin initializer function */
+ ret = e1inp_driver_register(&misdn_driver);
+ if (ret)
+ return ret;
+
+ /* create the actual line instance */
+ /* FIXME: do this independent of driver registration */
+ e1h = malloc(sizeof(*e1h));
+ memset(e1h, 0, sizeof(*e1h));
+
+ e1h->cardnr = cardnr;
+
+ line->driver = &misdn_driver;
+ line->driver_data = e1h;
+
+ /* open the ISDN card device */
+ sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
+ if (sk < 0) {
+ fprintf(stderr, "%s could not open socket %s\n",
+ __func__, strerror(errno));
+ return sk;
+ }
+
+ ret = ioctl(sk, IMGETCOUNT, &cnt);
+ if (ret) {
+ fprintf(stderr, "%s error getting interf count: %s\n",
+ __func__, strerror(errno));
+ close(sk);
+ return -ENODEV;
+ }
+ //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
+ printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
+#if 1
+ devinfo.id = e1h->cardnr;
+ ret = ioctl(sk, IMGETDEVINFO, &devinfo);
+ if (ret < 0) {
+ fprintf(stdout, "error getting info for device %d: %s\n",
+ e1h->cardnr, strerror(errno));
+ return -ENODEV;
+ }
+ fprintf(stdout, " id: %d\n", devinfo.id);
+ fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols);
+ fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols);
+ fprintf(stdout, " protocol: %d\n", devinfo.protocol);
+ fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan);
+ fprintf(stdout, " name: %s\n", devinfo.name);
+#endif
+
+ if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) {
+ fprintf(stderr, "error: card is not of type E1 (NT-mode)\n");
+ return -EINVAL;
+ }
+
+ if (devinfo.nrbchan != 30) {
+ fprintf(stderr, "error: E1 card has no 30 B-channels\n");
+ return -EINVAL;
+ }
+
+ ret = mi_e1_setup(line, release_l2);
+ if (ret)
+ return ret;
+
+ return e1inp_line_register(line);
+}
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
new file mode 100644
index 000000000..b74e46e89
--- /dev/null
+++ b/openbsc/src/ipaccess-config.c
@@ -0,0 +1,198 @@
+/* ip.access nanoBTS configuration tool */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include <openbsc/select.h>
+#include <openbsc/timer.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/abis_nm.h>
+
+static struct gsm_network *gsmnet;
+
+static int restart;
+static char *prim_oml_ip;
+static char *unit_id;
+
+/*
+static u_int8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 };
+static u_int8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 };
+*/
+
+static void bootstrap_om(struct gsm_bts *bts)
+{
+ int len;
+ static u_int8_t buf[1024];
+
+ printf("OML link established\n");
+
+ if (unit_id) {
+ len = strlen(unit_id);
+ if (len > sizeof(buf)-10)
+ return;
+ buf[0] = NM_ATT_IPACC_UNIT_ID;
+ buf[1] = (len+1) >> 8;
+ buf[2] = (len+1) & 0xff;
+ memcpy(buf+3, unit_id, len);
+ buf[3+len] = 0;
+ printf("setting Unit ID to '%s'\n", unit_id);
+ abis_nm_ipaccess_set_nvattr(bts, buf, 3+len+1);
+ }
+ if (prim_oml_ip) {
+ struct in_addr ia;
+ u_int8_t *cur = buf;
+
+ if (!inet_aton(prim_oml_ip, &ia)) {
+ fprintf(stderr, "invalid IP address: %s\n",
+ prim_oml_ip);
+ return;
+ }
+
+ /* 0x88 + IP + port */
+ len = 1 + sizeof(ia) + 2;
+
+ *cur++ = NM_ATT_IPACC_PRIM_OML_IP;
+ *cur++ = (len) >> 8;
+ *cur++ = (len) & 0xff;
+ *cur++ = 0x88;
+ memcpy(cur, &ia, sizeof(ia));
+ cur += sizeof(ia);
+ *cur++ = 0;
+ *cur++ = 0;
+ printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
+ abis_nm_ipaccess_set_nvattr(bts, buf, 3+len);
+ }
+
+ if (restart) {
+ printf("restarting BTS\n");
+ abis_nm_ipaccess_restart(bts);
+ }
+}
+
+void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
+{
+ switch (event) {
+ case EVT_E1_TEI_UP:
+ switch (type) {
+ case E1INP_SIGN_OML:
+ bootstrap_om(trx->bts);
+ break;
+ case E1INP_SIGN_RSL:
+ /* FIXME */
+ break;
+ default:
+ break;
+ }
+ break;
+ case EVT_E1_TEI_DN:
+ fprintf(stderr, "Lost some E1 TEI link\n");
+ /* FIXME: deal with TEI or L1 link loss */
+ break;
+ default:
+ break;
+ }
+}
+
+int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
+{
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct gsm_bts *bts;
+ struct sockaddr_in sin;
+ int rc, option_index = 0;
+
+ printf("ipaccess-config (C) 2009 by Harald Welte\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ while (1) {
+ int c;
+ static struct option long_options[] = {
+ { "unit-id", 1, 0, 'u' },
+ { "oml-ip", 1, 0, 'o' },
+ { "restart", 0, 0, 'r' },
+ };
+
+ c = getopt_long(argc, argv, "u:o:r", long_options,
+ &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'u':
+ unit_id = optarg;
+ break;
+ case 'o':
+ prim_oml_ip = optarg;
+ break;
+ case 'r':
+ restart = 1;
+ break;
+ }
+ };
+
+ if (optind >= argc) {
+ fprintf(stderr, "you have to specify the IP address of the BTS\n");
+ exit(2);
+ }
+
+ gsmnet = gsm_network_init( 1, GSM_BTS_TYPE_NANOBTS_900, 1, 1);
+ if (!gsmnet)
+ exit(1);
+
+ bts = &gsmnet->bts[0];
+
+ printf("Trying to connect to ip.access BTS ...\n");
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(argv[optind], &sin.sin_addr);
+ rc = ia_config_connect(bts, &sin);
+ if (rc < 0) {
+ perror("Error connecting to the BTS");
+ exit(1);
+ }
+
+ while (1) {
+ rc = bsc_select_main(0);
+ if (rc < 0)
+ exit(3);
+ }
+
+ exit(0);
+}
+
diff --git a/openbsc/src/ipaccess-find.c b/openbsc/src/ipaccess-find.c
new file mode 100644
index 000000000..b3e9814a9
--- /dev/null
+++ b/openbsc/src/ipaccess-find.c
@@ -0,0 +1,180 @@
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include <openbsc/select.h>
+#include <openbsc/timer.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/gsm_data.h>
+
+static const char *idtag_names[] = {
+ [IPAC_IDTAG_SERNR] = "Serial Number",
+ [IPAC_IDTAG_UNITNAME] = "Unit Name",
+ [IPAC_IDTAG_LOCATION1] = "Location 1",
+ [IPAC_IDTAG_LOCATION2] = "Location 2",
+ [IPAC_IDTAG_EQUIPVERS] = "Equipment Version",
+ [IPAC_IDTAG_SWVERSION] = "Software Version",
+ [IPAC_IDTAG_IPADDR] = "IP Address",
+ [IPAC_IDTAG_MACADDR] = "MAC Address",
+ [IPAC_IDTAG_UNIT] = "Unit ID",
+};
+
+static const char *ipac_idtag_name(int tag)
+{
+ if (tag >= ARRAY_SIZE(idtag_names))
+ return "unknown";
+
+ return idtag_names[tag];
+}
+
+static int udp_sock(void)
+{
+ int fd, rc, bc = 1;
+ struct sockaddr_in sa;
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0)
+ return fd;
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(3006);
+ sa.sin_addr.s_addr = INADDR_ANY;
+ inet_aton("192.168.100.11", &sa.sin_addr);
+
+ rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (rc < 0)
+ goto err;
+
+ rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
+ if (rc < 0)
+ goto err;
+
+#if 0
+ rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (rc < 0)
+ goto err;
+#endif
+ return fd;
+
+err:
+ close(fd);
+ return rc;
+}
+
+const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
+ IPAC_MSGT_ID_GET,
+ 0x01, IPAC_IDTAG_MACADDR,
+ 0x01, IPAC_IDTAG_IPADDR,
+ 0x01, IPAC_IDTAG_UNIT,
+ 0x01, IPAC_IDTAG_LOCATION1,
+ 0x01, IPAC_IDTAG_LOCATION2,
+ 0x01, IPAC_IDTAG_EQUIPVERS,
+ 0x01, IPAC_IDTAG_SWVERSION,
+ 0x01, IPAC_IDTAG_UNITNAME,
+ 0x01, IPAC_IDTAG_SERNR,
+ };
+
+
+static int bcast_find(int fd)
+{
+ struct sockaddr_in sa;
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(3006);
+ inet_aton("255.255.255.255", &sa.sin_addr);
+
+ return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
+}
+
+static int parse_response(unsigned char *buf, int len)
+{
+ u_int8_t t_len;
+ u_int8_t t_tag;
+ u_int8_t *cur = buf;
+
+ while (cur < buf + len) {
+ t_len = *cur++;
+ t_tag = *cur++;
+
+ printf("%s='%s' ", ipac_idtag_name(t_tag), cur);
+
+ cur += t_len;
+ }
+ printf("\n");
+ return 0;
+}
+
+static int read_response(int fd)
+{
+ unsigned char buf[255];
+ struct sockaddr_in sa;
+ int len;
+ socklen_t sa_len = sizeof(sa);
+
+ len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
+ if (len < 0)
+ return len;
+
+ return parse_response(buf+6, len-6);
+}
+
+static int bfd_cb(struct bsc_fd *bfd, unsigned int flags)
+{
+ if (flags & BSC_FD_READ)
+ return read_response(bfd->fd);
+ if (flags & BSC_FD_WRITE) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return bcast_find(bfd->fd);
+ }
+ return 0;
+}
+
+static struct timer_list timer;
+
+static void timer_cb(void *_data)
+{
+ struct bsc_fd *bfd = _data;
+
+ bfd->when |= BSC_FD_WRITE;
+
+ bsc_schedule_timer(&timer, 5, 0);
+}
+
+int main(int argc, char **argv)
+{
+ struct bsc_fd bfd;
+ int rc;
+
+ printf("ipaccess-find (C) 2009 by Harald Welte\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ bfd.cb = bfd_cb;
+ bfd.when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd.fd = udp_sock();
+ if (bfd.fd < 0)
+ exit(2);
+
+ bsc_register_fd(&bfd);
+
+ timer.cb = timer_cb;
+ timer.data = &bfd;
+
+ bsc_schedule_timer(&timer, 5, 0);
+
+ printf("Trying to find ip.access BTS by broadcast UDP...\n");
+
+ while (1) {
+ rc = bsc_select_main(0);
+ if (rc < 0)
+ exit(3);
+ }
+
+ exit(0);
+}
+
diff --git a/openbsc/src/isdnsync.c b/openbsc/src/isdnsync.c
new file mode 100644
index 000000000..d8819ac6b
--- /dev/null
+++ b/openbsc/src/isdnsync.c
@@ -0,0 +1,192 @@
+/* isdnsync.c
+ *
+ * Author Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "mISDNif.h"
+#define MISDN_OLD_AF_COMPATIBILITY
+#define AF_COMPATIBILITY_FUNC
+#include "compat_af_isdn.h"
+
+int card = 0;
+int sock = -1;
+
+int mISDN_open(void)
+{
+ int fd, ret;
+ struct mISDN_devinfo devinfo;
+ struct sockaddr_mISDN l2addr;
+
+ fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
+ if (fd < 0) {
+ fprintf(stderr, "could not open socket (%s)\n", strerror(errno));
+ return fd;
+ }
+ devinfo.id = card;
+ ret = ioctl(fd, IMGETDEVINFO, &devinfo);
+ if (ret < 0) {
+ fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno));
+ close(fd);
+ return ret;
+ }
+ close(fd);
+ if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0))
+ && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) {
+ fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno));
+ close(fd);
+ return ret;
+ }
+ fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE);
+ if (fd < 0) {
+ fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno));
+ return fd;
+ }
+ l2addr.family = AF_ISDN;
+ l2addr.dev = card;
+ l2addr.channel = 0;
+ l2addr.sapi = 0;
+ l2addr.tei = 0;
+ ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr));
+ if (ret < 0) {
+ fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno));
+ close(fd);
+ return ret;
+ }
+ sock = fd;
+
+ return sock;
+}
+
+
+void mISDN_handle(void)
+{
+ int ret;
+ fd_set rfd;
+ struct timeval tv;
+ struct sockaddr_mISDN addr;
+ socklen_t alen;
+ unsigned char buffer[2048];
+ struct mISDNhead *hh = (struct mISDNhead *)buffer;
+ int l1 = 0, l2 = 0, tei = 0;
+
+ while(1) {
+again:
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ ret = select(sock+1, &rfd, NULL, NULL, &tv);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno));
+ break;
+ }
+ if (FD_ISSET(sock, &rfd)) {
+ alen = sizeof(addr);
+ ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen);
+ if (ret < 0) {
+ fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno));
+ } else if (ret < MISDN_HEADER_LEN) {
+ fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__);
+ } else {
+ switch(hh->prim) {
+ case MPH_ACTIVATE_IND:
+ case PH_ACTIVATE_IND:
+ if (!l1) {
+ printf("PH_ACTIVATE\n");
+ printf("*** Sync available from interface :-)\n");
+ l1 = 1;
+ }
+ goto again;
+ break;
+ case MPH_DEACTIVATE_IND:
+ case PH_DEACTIVATE_IND:
+ if (l1) {
+ printf("PH_DEACTIVATE\n");
+ printf("*** Lost sync on interface :-(\n");
+ l1 = 0;
+ }
+ goto again;
+ break;
+ case DL_ESTABLISH_IND:
+ case DL_ESTABLISH_CNF:
+ printf("DL_ESTABLISH\n");
+ l2 = 1;
+ goto again;
+ break;
+ case DL_RELEASE_IND:
+ case DL_RELEASE_CNF:
+ printf("DL_RELEASE\n");
+ l2 = 0;
+ goto again;
+ break;
+ case DL_INFORMATION_IND:
+ printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi);
+ tei = 1;
+ break;
+ default:
+// printf("prim %x\n", hh->prim);
+ goto again;
+ }
+ }
+ }
+ if (tei && !l2) {
+ hh->prim = DL_ESTABLISH_REQ;
+ printf("-> activating layer 2\n");
+ sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc <= 1)
+ {
+ printf("Usage: %s <card>\n\n", argv[0]);
+ printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n");
+ printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n");
+ return(0);
+ }
+
+ card = atoi(argv[1]);
+
+ init_af_isdn();
+
+ if ((ret = mISDN_open() < 0))
+ return(ret);
+
+ mISDN_handle();
+
+ close(sock);
+
+ return 0;
+}
diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c
new file mode 100644
index 000000000..ce390e84a
--- /dev/null
+++ b/openbsc/src/msgb.c
@@ -0,0 +1,70 @@
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <openbsc/msgb.h>
+
+struct msgb *msgb_alloc(u_int16_t size)
+{
+ struct msgb *msg = malloc(sizeof(*msg) + size);
+
+ if (!msg)
+ return NULL;
+ memset(msg, 0, sizeof(*msg)+size);
+
+ msg->data_len = size;
+ msg->len = 0;
+ msg->data = msg->_data;
+
+ msg->head = msg->data;
+ msg->data = msg->data;
+ /* reset tail pointer */
+ msg->tail = msg->data;
+ //msg->end = msg->tail + size;
+
+ return msg;
+}
+
+void msgb_free(struct msgb *m)
+{
+ free(m);
+}
+
+void msgb_enqueue(struct llist_head *queue, struct msgb *msg)
+{
+ llist_add_tail(&msg->list, queue);
+}
+
+struct msgb *msgb_dequeue(struct llist_head *queue)
+{
+ struct llist_head *lh;
+
+ if (llist_empty(queue))
+ return NULL;
+
+ lh = queue->next;
+ llist_del(lh);
+
+ return llist_entry(lh, struct msgb, list);
+}
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
new file mode 100644
index 000000000..d777d6642
--- /dev/null
+++ b/openbsc/src/paging.c
@@ -0,0 +1,262 @@
+/* Paging helper and manager.... */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/*
+ * Relevant specs:
+ * 12.21:
+ * - 9.4.12 for CCCH Local Threshold
+ *
+ * 05.58:
+ * - 8.5.2 CCCH Load indication
+ * - 9.3.15 Paging Load
+ *
+ * Approach:
+ * - Send paging command to subscriber
+ * - On Channel Request we will remember the reason
+ * - After the ACK we will request the identity
+ * - Then we will send assign the gsm_subscriber and
+ * - and call a callback
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <openbsc/paging.h>
+#include <openbsc/debug.h>
+#include <openbsc/signal.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/gsm_data.h>
+
+#define PAGING_TIMEOUT 1, 75000
+#define MAX_PAGING_REQUEST 750
+
+static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr)
+{
+ int ccch_conf;
+ int bs_cc_chans;
+ int blocks;
+ unsigned int group;
+
+ ccch_conf = bts->chan_desc.ccch_conf;
+ bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf);
+ /* code word + 2, as 2 channels equals 0x0 */
+ blocks = rsl_number_of_paging_subchannels(bts);
+ group = get_paging_group(str_to_imsi(subscr->imsi),
+ bs_cc_chans, blocks);
+ return group;
+}
+
+/*
+ * Kill one paging request update the internal list...
+ */
+static void paging_remove_request(struct gsm_bts_paging_state *paging_bts,
+ struct gsm_paging_request *to_be_deleted)
+{
+ /* Update the last_request if that is necessary */
+ if (to_be_deleted == paging_bts->last_request) {
+ paging_bts->last_request =
+ (struct gsm_paging_request *)paging_bts->last_request->entry.next;
+ if (&to_be_deleted->entry == &paging_bts->pending_requests)
+ paging_bts->last_request = NULL;
+ }
+
+ bsc_del_timer(&to_be_deleted->T3113);
+ llist_del(&to_be_deleted->entry);
+ subscr_put(to_be_deleted->subscr);
+ free(to_be_deleted);
+}
+
+static void page_ms(struct gsm_paging_request *request)
+{
+ u_int8_t mi[128];
+ unsigned long int tmsi;
+ unsigned int mi_len;
+ unsigned int page_group;
+
+ DEBUGP(DPAG, "Going to send paging commands: '%s'\n",
+ request->subscr->imsi);
+
+ page_group = calculate_group(request->bts, request->subscr);
+ tmsi = strtoul(request->subscr->tmsi, NULL, 10);
+ mi_len = generate_mid_from_tmsi(mi, tmsi);
+ rsl_paging_cmd(request->bts, page_group, mi_len, mi,
+ request->chan_type);
+}
+
+static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts)
+{
+ paging_bts->last_request =
+ (struct gsm_paging_request *)paging_bts->last_request->entry.next;
+ if (&paging_bts->last_request->entry == &paging_bts->pending_requests)
+ paging_bts->last_request = NULL;
+}
+
+/*
+ * This is kicked by the periodic PAGING LOAD Indicator
+ * coming from abis_rsl.c
+ *
+ * We attempt to iterate once over the list of items but
+ * only upto available_slots.
+ */
+static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts)
+{
+ struct gsm_paging_request *initial_request = NULL;
+ struct gsm_paging_request *current_request = NULL;
+
+ /*
+ * Determine if the pending_requests list is empty and
+ * return then.
+ */
+ if (llist_empty(&paging_bts->pending_requests)) {
+ paging_bts->last_request = NULL;
+ /* since the list is empty, no need to reschedule the timer */
+ return;
+ }
+
+ if (!paging_bts->last_request)
+ paging_bts->last_request =
+ (struct gsm_paging_request *)paging_bts->pending_requests.next;
+
+ assert(paging_bts->last_request);
+ initial_request = paging_bts->last_request;
+ current_request = initial_request;
+
+ do {
+ /* handle the paging request now */
+ page_ms(current_request);
+ paging_bts->available_slots--;
+
+ /*
+ * move to the next item. We might wrap around
+ * this means last_request will be NULL and we just
+ * call paging_page_to_next again. It it guranteed
+ * that the list is not empty.
+ */
+ paging_move_to_next(paging_bts);
+ if (!paging_bts->last_request)
+ paging_bts->last_request =
+ (struct gsm_paging_request *)paging_bts->pending_requests.next;
+ current_request = paging_bts->last_request;
+ } while (paging_bts->available_slots > 0
+ && initial_request != current_request);
+
+ bsc_schedule_timer(&paging_bts->work_timer, 1, 0);
+}
+
+static void paging_worker(void *data)
+{
+ struct gsm_bts_paging_state *paging_bts = data;
+
+ paging_handle_pending_requests(paging_bts);
+}
+
+void paging_init(struct gsm_bts *bts)
+{
+ bts->paging.bts = bts;
+ INIT_LLIST_HEAD(&bts->paging.pending_requests);
+ bts->paging.work_timer.cb = paging_worker;
+ bts->paging.work_timer.data = &bts->paging;
+
+ /* Large number, until we get a proper message */
+ bts->paging.available_slots = 100;
+}
+
+static int paging_pending_request(struct gsm_bts_paging_state *bts,
+ struct gsm_subscriber *subscr) {
+ struct gsm_paging_request *req;
+
+ llist_for_each_entry(req, &bts->pending_requests, entry) {
+ if (subscr == req->subscr)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void paging_T3113_expired(void *data)
+{
+ struct gsm_paging_request *req = (struct gsm_paging_request *)data;
+ struct paging_signal_data sig_data;
+
+ DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
+ req, req->subscr->imsi);
+
+ sig_data.subscr = req->subscr,
+ sig_data.bts = req->bts,
+ sig_data.lchan = NULL,
+
+ dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+ if (req->cbfn)
+ req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
+ req->cbfn_param);
+ paging_remove_request(&req->bts->paging, req);
+}
+
+void paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+ int type, gsm_cbfn *cbfn, void *data)
+{
+ struct gsm_bts_paging_state *bts_entry = &bts->paging;
+ struct gsm_paging_request *req;
+
+ if (paging_pending_request(bts_entry, subscr)) {
+ DEBUGP(DPAG, "Paging request already pending\n");
+ return;
+ }
+
+ req = (struct gsm_paging_request *)malloc(sizeof(*req));
+ memset(req, 0, sizeof(*req));
+ req->subscr = subscr_get(subscr);
+ req->bts = bts;
+ req->chan_type = type;
+ req->cbfn = cbfn;
+ req->cbfn_param = data;
+ req->T3113.cb = paging_T3113_expired;
+ req->T3113.data = req;
+ bsc_schedule_timer(&req->T3113, T3113_VALUE);
+ llist_add_tail(&req->entry, &bts_entry->pending_requests);
+
+ if (!bsc_timer_pending(&bts_entry->work_timer))
+ bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
+}
+
+/* we consciously ignore the type of the request here */
+void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
+ struct gsm_lchan *lchan)
+{
+ struct gsm_bts_paging_state *bts_entry = &bts->paging;
+ struct gsm_paging_request *req, *req2;
+
+ llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests,
+ entry) {
+ if (req->subscr == subscr) {
+ if (req->cbfn)
+ req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
+ NULL, lchan, req->cbfn_param);
+ paging_remove_request(&bts->paging, req);
+ break;
+ }
+ }
+}
+
+void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots)
+{
+ bts->paging.available_slots = free_slots;
+}
diff --git a/openbsc/src/rs232.c b/openbsc/src/rs232.c
new file mode 100644
index 000000000..2a64de5ef
--- /dev/null
+++ b/openbsc/src/rs232.c
@@ -0,0 +1,249 @@
+/* OpenBSC BS-11 T-Link interface using POSIX serial port */
+
+/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <fcntl.h>
+
+#include <openbsc/select.h>
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/rs232.h>
+
+/* adaption layer from GSM 08.59 + 12.21 to RS232 */
+
+struct serial_handle {
+ struct bsc_fd fd;
+ struct llist_head tx_queue;
+
+ struct msgb *rx_msg;
+ unsigned int rxmsg_bytes_missing;
+
+ unsigned int delay_ms;
+ struct gsm_bts *bts;
+};
+
+/* FIXME: this needs to go */
+static struct serial_handle _ser_handle, *ser_handle = &_ser_handle;
+
+#define LAPD_HDR_LEN 10
+
+static int handle_ser_write(struct bsc_fd *bfd);
+
+/* callback from abis_nm */
+int _abis_nm_sendmsg(struct msgb *msg)
+{
+ struct serial_handle *sh = ser_handle;
+ u_int8_t *lapd;
+ unsigned int len;
+
+ msg->l2h = msg->data;
+
+ /* prepend LAPD header */
+ lapd = msgb_push(msg, LAPD_HDR_LEN);
+
+ len = msg->len - 2;
+
+ lapd[0] = (len >> 8) & 0xff;
+ lapd[1] = len & 0xff; /* length of bytes startign at lapd[2] */
+ lapd[2] = 0x00;
+ lapd[3] = 0x07;
+ lapd[4] = 0x01;
+ lapd[5] = 0x3e;
+ lapd[6] = 0x00;
+ lapd[7] = 0x00;
+ lapd[8] = msg->len - 10; /* length of bytes starting at lapd[10] */
+ lapd[9] = lapd[8] ^ 0x38;
+
+ msgb_enqueue(&sh->tx_queue, msg);
+ sh->fd.when |= BSC_FD_WRITE;
+
+ /* we try to immediately send */
+ handle_ser_write(&sh->fd);
+
+ return 0;
+}
+
+/* select.c callback in case we can write to the RS232 */
+static int handle_ser_write(struct bsc_fd *bfd)
+{
+ struct serial_handle *sh = bfd->data;
+ struct msgb *msg;
+ int written;
+
+ msg = msgb_dequeue(&sh->tx_queue);
+ if (!msg) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+
+ DEBUGP(DMI, "RS232 TX: %s\n", hexdump(msg->data, msg->len));
+
+ /* send over serial line */
+ written = write(bfd->fd, msg->data, msg->len);
+ if (written < msg->len) {
+ perror("short write:");
+ msgb_free(msg);
+ return -1;
+ }
+
+ msgb_free(msg);
+ usleep(sh->delay_ms*1000);
+
+ return 0;
+}
+
+#define SERIAL_ALLOC_SIZE 300
+
+/* select.c callback in case we can read from the RS232 */
+static int handle_ser_read(struct bsc_fd *bfd)
+{
+ struct serial_handle *sh = bfd->data;
+ struct msgb *msg;
+ int rc = 0;
+
+ if (!sh->rx_msg) {
+ sh->rx_msg = msgb_alloc(SERIAL_ALLOC_SIZE);
+ sh->rx_msg->l2h = NULL;
+ sh->rx_msg->trx = sh->bts->c0;
+ }
+ msg = sh->rx_msg;
+
+ /* first read two byes to obtain length */
+ if (msg->len < 2) {
+ rc = read(sh->fd.fd, msg->tail, 2 - msg->len);
+ if (rc < 0) {
+ perror("ERROR reading from serial port");
+ msgb_free(msg);
+ return rc;
+ }
+ msgb_put(msg, rc);
+
+ if (msg->len >= 2) {
+ /* parse LAPD payload length */
+ if (msg->data[0] != 0)
+ fprintf(stderr, "Suspicious header byte 0: 0x%02x\n",
+ msg->data[0]);
+
+ sh->rxmsg_bytes_missing = msg->data[0] << 8;
+ sh->rxmsg_bytes_missing += msg->data[1];
+
+ if (sh->rxmsg_bytes_missing < LAPD_HDR_LEN -2)
+ fprintf(stderr, "Invalid length in hdr: %u\n",
+ sh->rxmsg_bytes_missing);
+ }
+ } else {
+ /* try to read as many of the missing bytes as are available */
+ rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing);
+ if (rc < 0) {
+ perror("ERROR reading from serial port");
+ msgb_free(msg);
+ return rc;
+ }
+ msgb_put(msg, rc);
+ sh->rxmsg_bytes_missing -= rc;
+
+ if (sh->rxmsg_bytes_missing == 0) {
+ /* we have one complete message now */
+ sh->rx_msg = NULL;
+
+ if (msg->len > LAPD_HDR_LEN)
+ msg->l2h = msg->data + LAPD_HDR_LEN;
+
+ DEBUGP(DMI, "RS232 RX: %s\n", hexdump(msg->data, msg->len));
+ rc = handle_serial_msg(msg);
+ }
+ }
+
+ return rc;
+}
+
+/* select.c callback */
+static int serial_fd_cb(struct bsc_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ)
+ rc = handle_ser_read(bfd);
+
+ if (rc < 0)
+ return rc;
+
+ if (what & BSC_FD_WRITE)
+ rc = handle_ser_write(bfd);
+
+ return rc;
+}
+
+int rs232_setup(const char *serial_port, unsigned int delay_ms,
+ struct gsm_bts *bts)
+{
+ int rc, serial_fd;
+ struct termios tio;
+
+ serial_fd = open(serial_port, O_RDWR);
+ if (serial_fd < 0) {
+ perror("cannot open serial port:");
+ return serial_fd;
+ }
+
+ /* set baudrate */
+ rc = tcgetattr(serial_fd, &tio);
+ if (rc < 0) {
+ perror("tcgetattr()");
+ return rc;
+ }
+ cfsetispeed(&tio, B19200);
+ cfsetospeed(&tio, B19200);
+ tio.c_cflag |= (CREAD | CLOCAL | CS8);
+ tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
+ tio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+ tio.c_iflag |= (INPCK | ISTRIP);
+ tio.c_iflag &= ~(ISTRIP | IXON | IXOFF | IGNBRK | INLCR | ICRNL | IGNCR);
+ tio.c_oflag &= ~(OPOST);
+ rc = tcsetattr(serial_fd, TCSADRAIN, &tio);
+ if (rc < 0) {
+ perror("tcsetattr()");
+ return rc;
+ }
+
+ INIT_LLIST_HEAD(&ser_handle->tx_queue);
+ ser_handle->fd.fd = serial_fd;
+ ser_handle->fd.when = BSC_FD_READ;
+ ser_handle->fd.cb = serial_fd_cb;
+ ser_handle->fd.data = ser_handle;
+ ser_handle->delay_ms = delay_ms;
+ ser_handle->bts = bts;
+ rc = bsc_register_fd(&ser_handle->fd);
+ if (rc < 0) {
+ fprintf(stderr, "could not register FD: %s\n",
+ strerror(rc));
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/select.c b/openbsc/src/select.c
new file mode 100644
index 000000000..11b7e6b49
--- /dev/null
+++ b/openbsc/src/select.c
@@ -0,0 +1,107 @@
+/* select filedescriptor handling, taken from:
+ * userspace logging daemon for the iptables ULOG target
+ * of the linux 2.4 netfilter subsystem.
+ *
+ * (C) 2000-2009 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <fcntl.h>
+#include <openbsc/select.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/timer.h>
+
+static int maxfd = 0;
+static LLIST_HEAD(bsc_fds);
+
+int bsc_register_fd(struct bsc_fd *fd)
+{
+ int flags;
+
+ /* make FD nonblocking */
+ flags = fcntl(fd->fd, F_GETFL);
+ if (flags < 0)
+ return flags;
+ flags |= O_NONBLOCK;
+ flags = fcntl(fd->fd, F_SETFL, flags);
+ if (flags < 0)
+ return flags;
+
+ /* Register FD */
+ if (fd->fd > maxfd)
+ maxfd = fd->fd;
+
+ llist_add_tail(&fd->list, &bsc_fds);
+
+ return 0;
+}
+
+void bsc_unregister_fd(struct bsc_fd *fd)
+{
+ llist_del(&fd->list);
+}
+
+int bsc_select_main(int polling)
+{
+ struct bsc_fd *ufd, *tmp;
+ fd_set readset, writeset, exceptset;
+ int work = 0, rc;
+ struct timeval no_time = {0, 0};
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+
+ /* prepare read and write fdsets */
+ llist_for_each_entry(ufd, &bsc_fds, list) {
+ if (ufd->when & BSC_FD_READ)
+ FD_SET(ufd->fd, &readset);
+
+ if (ufd->when & BSC_FD_WRITE)
+ FD_SET(ufd->fd, &writeset);
+
+ if (ufd->when & BSC_FD_EXCEPT)
+ FD_SET(ufd->fd, &exceptset);
+ }
+
+ if (!polling)
+ bsc_prepare_timers();
+ rc = select(maxfd+1, &readset, &writeset, &exceptset, polling ? &no_time : bsc_nearest_timer());
+ if (rc < 0)
+ return 0;
+
+ /* fire timers */
+ bsc_update_timers();
+
+ /* call registered callback functions */
+ llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
+ int flags = 0;
+
+ if (FD_ISSET(ufd->fd, &readset))
+ flags |= BSC_FD_READ;
+
+ if (FD_ISSET(ufd->fd, &writeset))
+ flags |= BSC_FD_WRITE;
+
+ if (FD_ISSET(ufd->fd, &exceptset))
+ flags |= BSC_FD_EXCEPT;
+
+ if (flags) {
+ work = 1;
+ ufd->cb(ufd, flags);
+ }
+ }
+ return work;
+}
diff --git a/openbsc/src/signal.c b/openbsc/src/signal.c
new file mode 100644
index 000000000..4227c6dc1
--- /dev/null
+++ b/openbsc/src/signal.c
@@ -0,0 +1,80 @@
+/* Generic signalling/notification infrastructure */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static LLIST_HEAD(signal_handler_list);
+
+struct signal_handler {
+ struct llist_head entry;
+ unsigned int subsys;
+ signal_cbfn *cbfn;
+ void *data;
+};
+
+
+int register_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
+{
+ struct signal_handler *sig_data = malloc(sizeof(*sig_data));
+
+ if (!sig_data)
+ return -ENOMEM;
+
+ memset(sig_data, 0, sizeof(*sig_data));
+
+ sig_data->subsys = subsys;
+ sig_data->data = data;
+ sig_data->cbfn = cbfn;
+
+ /* FIXME: check if we already have a handler for this subsys/cbfn/data */
+
+ llist_add_tail(&sig_data->entry, &signal_handler_list);
+
+ return 0;
+}
+
+void unregister_signal_handler(unsigned int subsys, signal_cbfn *cbfn, void *data)
+{
+ struct signal_handler *handler;
+
+ llist_for_each_entry(handler, &signal_handler_list, entry) {
+ if (handler->cbfn == cbfn && handler->data == data
+ && subsys == handler->subsys) {
+ llist_del(&handler->entry);
+ free(handler);
+ break;
+ }
+ }
+}
+
+
+void dispatch_signal(unsigned int subsys, unsigned int signal, void *signal_data)
+{
+ struct signal_handler *handler;
+
+ llist_for_each_entry(handler, &signal_handler_list, entry) {
+ if (handler->subsys != subsys)
+ continue;
+ (*handler->cbfn)(subsys, signal, handler->data, signal_data);
+ }
+}
diff --git a/openbsc/src/subchan_demux.c b/openbsc/src/subchan_demux.c
new file mode 100644
index 000000000..c662bcd0e
--- /dev/null
+++ b/openbsc/src/subchan_demux.c
@@ -0,0 +1,319 @@
+/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/subchan_demux.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/debug.h>
+
+static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
+{
+ sch->out_bitbuf[sch->out_idx++] = bit;
+}
+
+#define SYNC_HDR_BITS 16
+static const u_int8_t nullbytes[SYNC_HDR_BITS];
+
+/* check if we have just completed the 16 bit zero sync header,
+ * in accordance with GSM TS 08.60 Chapter 4.8.1 */
+static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit)
+{
+ if (bit == 0)
+ sch->consecutive_zeros++;
+ else
+ sch->consecutive_zeros = 0;
+
+ if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
+ sch->consecutive_zeros = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* resynchronize to current location */
+static void resync_to_here(struct demux_subch *sch)
+{
+ memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
+
+ /* set index in a way that we can continue receiving bits after
+ * the end of the SYNC header */
+ sch->out_idx = SYNC_HDR_BITS;
+ sch->in_sync = 1;
+}
+
+int subch_demux_init(struct subch_demux *dmx)
+{
+ int i;
+
+ dmx->chan_activ = 0;
+ for (i = 0; i < NR_SUBCH; i++) {
+ struct demux_subch *sch = &dmx->subch[i];
+ sch->out_idx = 0;
+ memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
+ }
+ return 0;
+}
+
+/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
+ * split it into the 16k subchannels */
+int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
+{
+ int i, c;
+
+ /* we avoid partially filled bytes in outbuf */
+ if (len % 4)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++) {
+ u_int8_t inbyte = data[i];
+
+ for (c = 0; c < NR_SUBCH; c++) {
+ struct demux_subch *sch = &dmx->subch[c];
+ u_int8_t inbits;
+ u_int8_t bit;
+
+ /* ignore inactive subchannels */
+ if (!(dmx->chan_activ & (1 << c)))
+ continue;
+
+ inbits = inbyte >> (c << 1);
+
+ /* two bits for each subchannel */
+ if (inbits & 0x01)
+ bit = 1;
+ else
+ bit = 0;
+ append_bit(sch, bit);
+
+ if (sync_hdr_complete(sch, bit))
+ resync_to_here(sch);
+
+ if (inbits & 0x02)
+ bit = 1;
+ else
+ bit = 0;
+ append_bit(sch, bit);
+
+ if (sync_hdr_complete(sch, bit))
+ resync_to_here(sch);
+
+ /* FIXME: verify the first bit in octet 2, 4, 6, ...
+ * according to TS 08.60 4.8.1 */
+
+ /* once we have reached TRAU_FRAME_BITS, call
+ * the TRAU frame handler callback function */
+ if (sch->out_idx >= TRAU_FRAME_BITS) {
+ if (sch->in_sync) {
+ dmx->out_cb(dmx, c, sch->out_bitbuf,
+ sch->out_idx, dmx->data);
+ sch->in_sync = 0;
+ }
+ sch->out_idx = 0;
+ }
+ }
+ }
+ return i;
+}
+
+int subch_demux_activate(struct subch_demux *dmx, int subch)
+{
+ if (subch >= NR_SUBCH)
+ return -EINVAL;
+
+ dmx->chan_activ |= (1 << subch);
+ return 0;
+}
+
+int subch_demux_deactivate(struct subch_demux *dmx, int subch)
+{
+ if (subch >= NR_SUBCH)
+ return -EINVAL;
+
+ dmx->chan_activ &= ~(1 << subch);
+ return 0;
+}
+
+/* MULTIPLEXER */
+
+static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
+{
+ /* allocate and initialize with idle pattern */
+ return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(),
+ TRAU_FRAME_BITS);
+}
+
+/* return the requested number of bits from the specified subchannel */
+static int get_subch_bits(struct subch_mux *mx, int subch,
+ u_int8_t *bits, int num_requested)
+{
+ struct mux_subch *sch = &mx->subch[subch];
+ int num_bits = 0;
+
+ while (num_bits < num_requested) {
+ struct subch_txq_entry *txe;
+ int num_bits_left;
+ int num_bits_thistime;
+
+ /* make sure we have a valid entry at top of tx queue.
+ * if not, add an idle frame */
+ if (llist_empty(&sch->tx_queue))
+ alloc_add_idle_frame(mx, subch);
+
+ if (llist_empty(&sch->tx_queue))
+ return -EIO;
+
+ txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
+ num_bits_left = txe->bit_len - txe->next_bit;
+
+ if (num_bits_left < num_requested)
+ num_bits_thistime = num_bits_left;
+ else
+ num_bits_thistime = num_requested;
+
+ /* pull the bits from the txe */
+ memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime);
+ txe->next_bit += num_bits_thistime;
+
+ /* free the tx_queue entry if it is fully consumed */
+ if (txe->next_bit >= txe->bit_len) {
+ llist_del(&txe->list);
+ free(txe);
+ }
+
+ /* increment global number of bits dequeued */
+ num_bits += num_bits_thistime;
+ }
+
+ return num_requested;
+}
+
+/* compact an array of 8 single-bit bytes into one byte of 8 bits */
+static u_int8_t compact_bits(const u_int8_t *bits)
+{
+ u_int8_t ret = 0;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ ret |= (bits[i] ? 1 : 0) << i;
+
+ return ret;
+}
+
+/* obtain a single output byte from the subchannel muxer */
+static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte)
+{
+ u_int8_t bits[8];
+ int rc;
+
+ /* combine two bits of every subchan */
+ rc = get_subch_bits(mx, 0, &bits[0], 2);
+ rc = get_subch_bits(mx, 1, &bits[2], 2);
+ rc = get_subch_bits(mx, 2, &bits[4], 2);
+ rc = get_subch_bits(mx, 3, &bits[6], 2);
+
+ *byte = compact_bits(bits);
+
+ return rc;
+}
+
+/* Request the output of some muxed bytes from the subchan muxer */
+int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ int rc;
+ rc = mux_output_byte(mx, &data[i]);
+ if (rc < 0)
+ break;
+ }
+ return i;
+}
+
+static int llist_len(struct llist_head *head)
+{
+ struct llist_head *entry;
+ int i = 0;
+
+ llist_for_each(entry, head)
+ i++;
+
+ return i;
+}
+
+/* evict the 'num_evict' number of oldest entries in the queue */
+static void tx_queue_evict(struct mux_subch *sch, int num_evict)
+{
+ struct subch_txq_entry *tqe;
+ int i;
+
+ for (i = 0; i < num_evict; i++) {
+ if (llist_empty(&sch->tx_queue))
+ return;
+
+ tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
+ llist_del(&tqe->list);
+ free(tqe);
+ }
+}
+
+/* enqueue some data into the tx_queue of a given subchannel */
+int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
+ int len)
+{
+ struct mux_subch *sch = &mx->subch[s_nr];
+ struct subch_txq_entry *tqe = malloc(sizeof(*tqe) + len);
+ int list_len = llist_len(&sch->tx_queue);
+
+ if (!tqe)
+ return -ENOMEM;
+
+ memset(tqe, 0, sizeof(*tqe));
+ tqe->bit_len = len;
+ memcpy(tqe->bits, data, len);
+
+ if (list_len > 2)
+ tx_queue_evict(sch, list_len-2);
+
+ llist_add_tail(&tqe->list, &sch->tx_queue);
+
+ return 0;
+}
+
+/* initialize one subchannel muxer instance */
+int subchan_mux_init(struct subch_mux *mx)
+{
+ int i;
+
+ memset(mx, 0, sizeof(*mx));
+ for (i = 0; i < NR_SUBCH; i++) {
+ struct mux_subch *sch = &mx->subch[i];
+ INIT_LLIST_HEAD(&sch->tx_queue);
+ }
+
+ return 0;
+}
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
new file mode 100644
index 000000000..f4ffb529e
--- /dev/null
+++ b/openbsc/src/telnet_interface.c
@@ -0,0 +1,236 @@
+/* minimalistic telnet/network interface it might turn into a wire interface */
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openbsc/telnet_interface.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_04_11.h>
+#include <openbsc/msgb.h>
+#include <openbsc/abis_rsl.h>
+#include <openbsc/paging.h>
+#include <openbsc/signal.h>
+
+#include <vty/buffer.h>
+
+#define WRITE_CONNECTION(fd, msg...) \
+ int ret; \
+ char buf[4096]; \
+ snprintf(buf, sizeof(buf), msg); \
+ ret = write(fd, buf, strlen(buf));
+
+
+/* per connection data */
+LLIST_HEAD(active_connections);
+
+/* per network data */
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what);
+#if 0
+static int telnet_paging_callback(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data);
+static int telnet_sms_callback(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data);
+#endif
+
+static struct bsc_fd server_socket = {
+ .when = BSC_FD_READ,
+ .cb = telnet_new_connection,
+ .priv_nr = 0,
+};
+
+void telnet_init(struct gsm_network *network, int port) {
+ struct sockaddr_in sock_addr;
+ int fd, on = 1;
+
+ bsc_vty_init(network);
+
+ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ if (fd < 0) {
+ perror("Telnet interface socket creation failed");
+ return;
+ }
+
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_port = htons(port);
+ sock_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0) {
+ perror("Telnet interface failed to bind");
+ return;
+ }
+
+ if (listen(fd, 0) < 0) {
+ perror("Telnet interface failed to listen");
+ return;
+ }
+
+ server_socket.data = network;
+ server_socket.fd = fd;
+ bsc_register_fd(&server_socket);
+
+ /* register callbacks */
+#if 0
+ register_signal_handler(SS_PAGING, telnet_paging_callback, network);
+ register_signal_handler(SS_SMS, telnet_sms_callback, network);
+#endif
+}
+
+static void print_welcome(int fd) {
+ int ret;
+ static char *msg =
+ "Welcome to the OpenBSC Control interface\n"
+ "Copyright (C) 2008, 2009 Harald Welte\n"
+ "Contributions by Daniel Willmann, Jan Lübbe, "
+ "Stefan Schmidt, Holger Freyther\n\n"
+ "License GPLv2+: GNU GPL version 2 or later "
+ "<http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change "
+ "and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted "
+ "by law.\nType \"help\" to get a short introduction.\n";
+
+ ret = write(fd, msg, strlen(msg));
+}
+
+int telnet_close_client(struct bsc_fd *fd) {
+ struct telnet_connection *conn = (struct telnet_connection*)fd->data;
+
+ close(fd->fd);
+ bsc_unregister_fd(fd);
+ llist_del(&conn->entry);
+ free(conn);
+ return 0;
+}
+
+static int client_data(struct bsc_fd *fd, unsigned int what)
+{
+ struct telnet_connection *conn = fd->data;
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ conn->fd.when &= ~BSC_FD_READ;
+ rc = vty_read(conn->vty);
+ }
+
+ if (what & BSC_FD_WRITE) {
+ rc = buffer_flush_all(conn->vty->obuf, fd->fd);
+ if (rc == BUFFER_EMPTY)
+ conn->fd.when &= ~BSC_FD_WRITE;
+ }
+
+ return rc;
+}
+
+static int telnet_new_connection(struct bsc_fd *fd, unsigned int what) {
+ struct telnet_connection *connection;
+ struct sockaddr_in sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ int new_connection = accept(fd->fd, (struct sockaddr*)&sockaddr, &len);
+
+ if (new_connection < 0) {
+ perror("telnet accept failed");
+ return -1;
+ }
+
+
+ connection = (struct telnet_connection*)malloc(sizeof(*connection));
+ memset(connection, 0, sizeof(*connection));
+ connection->network = (struct gsm_network*)fd->data;
+ connection->fd.data = connection;
+ connection->fd.fd = new_connection;
+ connection->fd.when = BSC_FD_READ;
+ connection->fd.cb = client_data;
+ connection->bts = 0;
+ bsc_register_fd(&connection->fd);
+ llist_add_tail(&connection->entry, &active_connections);
+
+ print_welcome(new_connection);
+
+ connection->vty = vty_create(new_connection, connection);
+ if (!connection->vty)
+ return -1;
+
+ return 0;
+}
+
+/* callback from VTY code */
+void vty_event(enum event event, int sock, struct vty *vty)
+{
+ struct telnet_connection *connection = vty->priv;
+ struct bsc_fd *bfd = &connection->fd;
+
+ switch (event) {
+ case VTY_READ:
+ bfd->when |= BSC_FD_READ;
+ break;
+ case VTY_WRITE:
+ bfd->when |= BSC_FD_WRITE;
+ break;
+ default:
+ break;
+ }
+}
+
+#if 0
+static int telnet_paging_callback(unsigned int subsys, unsigned int singal,
+ void *handler_data, void *signal_data)
+{
+ struct paging_signal_data *paging = signal_data;
+ struct telnet_connection *con;
+
+ llist_for_each_entry(con, &active_connections, entry) {
+ if (paging->lchan) {
+ WRITE_CONNECTION(con->fd.fd, "Paging succeeded\n");
+ show_lchan(con->fd.fd, paging->lchan);
+ } else {
+ WRITE_CONNECTION(con->fd.fd, "Paging failed for subscriber: %s/%s/%s\n",
+ paging->subscr->imsi,
+ paging->subscr->tmsi,
+ paging->subscr->name);
+ }
+ }
+
+ return 0;
+}
+
+static int telnet_sms_callback(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct sms_submit *sms = signal_data;
+ struct telnet_connection *con;
+
+ llist_for_each_entry(con, &active_connections, entry) {
+ WRITE_CONNECTION(con->fd.fd, "Incoming SMS: %s\n", sms->user_data);
+ }
+
+ return 0;
+}
+#endif
diff --git a/openbsc/src/timer.c b/openbsc/src/timer.c
new file mode 100644
index 000000000..a942ffd6d
--- /dev/null
+++ b/openbsc/src/timer.c
@@ -0,0 +1,174 @@
+/*
+ * (C) 2008,2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <openbsc/timer.h>
+
+static LLIST_HEAD(timer_list);
+static struct timeval s_nearest_time;
+static struct timeval s_select_time;
+
+#define MICRO_SECONDS 1000000LL
+
+#define TIME_SMALLER(left, right) \
+ (left.tv_sec*MICRO_SECONDS+left.tv_usec) <= (right.tv_sec*MICRO_SECONDS+right.tv_usec)
+
+void bsc_add_timer(struct timer_list *timer)
+{
+ struct timer_list *list_timer;
+
+ /* TODO: Optimize and remember the closest item... */
+ timer->active = 1;
+
+ /* this might be called from within update_timers */
+ llist_for_each_entry(list_timer, &timer_list, entry)
+ if (timer == list_timer)
+ return;
+
+ timer->in_list = 1;
+ llist_add(&timer->entry, &timer_list);
+}
+
+void bsc_schedule_timer(struct timer_list *timer, int seconds, int microseconds)
+{
+ struct timeval current_time;
+
+ gettimeofday(&current_time, NULL);
+ unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+ currentTime += seconds * MICRO_SECONDS + microseconds;
+ timer->timeout.tv_sec = currentTime / MICRO_SECONDS;
+ timer->timeout.tv_usec = currentTime % MICRO_SECONDS;
+ bsc_add_timer(timer);
+}
+
+void bsc_del_timer(struct timer_list *timer)
+{
+ if (timer->in_list) {
+ timer->active = 0;
+ timer->in_list = 0;
+ llist_del(&timer->entry);
+ }
+}
+
+int bsc_timer_pending(struct timer_list *timer)
+{
+ return timer->active;
+}
+
+/*
+ * if we have a nearest time return the delta between the current
+ * time and the time of the nearest timer.
+ * If the nearest timer timed out return NULL and then we will
+ * dispatch everything after the select
+ */
+struct timeval *bsc_nearest_timer()
+{
+ struct timeval current_time;
+
+ if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0)
+ return NULL;
+
+ if (gettimeofday(&current_time, NULL) == -1)
+ return NULL;
+
+ unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec;
+ unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec;
+
+ if (nearestTime < currentTime) {
+ s_select_time.tv_sec = 0;
+ s_select_time.tv_usec = 0;
+ } else {
+ s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS;
+ s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS;
+ }
+
+ return &s_select_time;
+}
+
+/*
+ * Find the nearest time and update s_nearest_time
+ */
+void bsc_prepare_timers()
+{
+ struct timer_list *timer, *nearest_timer = NULL;
+ llist_for_each_entry(timer, &timer_list, entry) {
+ if (!nearest_timer || TIME_SMALLER(timer->timeout, nearest_timer->timeout)) {
+ nearest_timer = timer;
+ }
+ }
+
+ if (nearest_timer) {
+ s_nearest_time = nearest_timer->timeout;
+ } else {
+ memset(&s_nearest_time, 0, sizeof(struct timeval));
+ }
+}
+
+/*
+ * fire all timers... and remove them
+ */
+int bsc_update_timers()
+{
+ struct timeval current_time;
+ struct timer_list *timer, *tmp;
+ int work = 0;
+
+ gettimeofday(&current_time, NULL);
+
+ /*
+ * The callbacks might mess with our list and in this case
+ * even llist_for_each_entry_safe is not safe to use. To allow
+ * del_timer, add_timer, schedule_timer to be called from within
+ * the callback we jump through some loops.
+ *
+ * First we set the handled flag of each active timer to zero,
+ * then we iterate over the list and execute the callbacks. As the
+ * list might have been changed (specially the next) from within
+ * the callback we have to start over again. Once every callback
+ * is dispatched we will remove the non-active from the list.
+ *
+ * TODO: If this is a performance issue we can poison a global
+ * variable in add_timer and del_timer and only then restart.
+ */
+ llist_for_each_entry(timer, &timer_list, entry) {
+ timer->handled = 0;
+ }
+
+restart:
+ llist_for_each_entry(timer, &timer_list, entry) {
+ if (!timer->handled && TIME_SMALLER(timer->timeout, current_time)) {
+ timer->handled = 1;
+ timer->active = 0;
+ (*timer->cb)(timer->data);
+ work = 1;
+ goto restart;
+ }
+ }
+
+ llist_for_each_entry_safe(timer, tmp, &timer_list, entry) {
+ timer->handled = 0;
+ if (!timer->active) {
+ bsc_del_timer(timer);
+ }
+ }
+
+ return work;
+}
diff --git a/openbsc/src/tlv_parser.c b/openbsc/src/tlv_parser.c
new file mode 100644
index 000000000..e835f951f
--- /dev/null
+++ b/openbsc/src/tlv_parser.c
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <openbsc/tlv.h>
+
+int tlv_dump(struct tlv_parsed *dec)
+{
+ int i;
+
+ for (i = 0; i <= 0xff; i++) {
+ if (!dec->lv[i].val)
+ continue;
+ printf("T=%02x L=%d\n", i, dec->lv[i].len);
+ }
+ return 0;
+}
+
+/* dec: output: a caller-allocated pointer to a struct tlv_parsed,
+ * def: input: a structure defining the valid TLV tags / configurations
+ * buf: input: the input data buffer to be parsed
+ * buf_len: input: the length of the input data buffer
+ * lv_tag: input: an initial LV tag at the start of the buffer
+ * lv_tag2: input: a second initial LV tag following lv_tag
+ */
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,
+ const u_int8_t *buf, int buf_len, u_int8_t lv_tag,
+ u_int8_t lv_tag2)
+{
+ u_int8_t tag, len = 1;
+ const u_int8_t *pos = buf;
+ int num_parsed = 0;
+
+ memset(dec, 0, sizeof(*dec));
+
+ if (lv_tag) {
+ if (pos > buf + buf_len)
+ return -1;
+ dec->lv[lv_tag].val = pos+1;
+ dec->lv[lv_tag].len = *pos;
+ len = dec->lv[lv_tag].len + 1;
+ if (pos + len > buf + buf_len)
+ return -2;
+ num_parsed++;
+ pos += len;
+ }
+ if (lv_tag2) {
+ if (pos > buf + buf_len)
+ return -1;
+ dec->lv[lv_tag2].val = pos+1;
+ dec->lv[lv_tag2].len = *pos;
+ len = dec->lv[lv_tag2].len + 1;
+ if (pos + len > buf + buf_len)
+ return -2;
+ num_parsed++;
+ pos += len;
+ }
+
+ for (; pos < buf+buf_len; pos += len) {
+ tag = *pos;
+ /* FIXME: use tables for knwon IEI */
+ switch (def->def[tag].type) {
+ case TLV_TYPE_T:
+ /* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
+ dec->lv[tag].val = pos;
+ dec->lv[tag].len = 0;
+ len = 1;
+ num_parsed++;
+ break;
+ case TLV_TYPE_TV:
+ dec->lv[tag].val = pos+1;
+ dec->lv[tag].len = 1;
+ len = 2;
+ num_parsed++;
+ break;
+ case TLV_TYPE_FIXED:
+ dec->lv[tag].val = pos+1;
+ dec->lv[tag].len = def->def[tag].fixed_len;
+ len = def->def[tag].fixed_len + 1;
+ num_parsed++;
+ break;
+ case TLV_TYPE_TLV:
+ /* GSM TS 04.07 11.2.4: Type 4 TLV */
+ if (pos + 1 > buf + buf_len)
+ return -1;
+ dec->lv[tag].val = pos+2;
+ dec->lv[tag].len = *(pos+1);
+ len = dec->lv[tag].len + 2;
+ if (pos + len > buf + buf_len)
+ return -2;
+ num_parsed++;
+ break;
+ case TLV_TYPE_TL16V:
+ if (pos + 2 > buf + buf_len)
+ return -1;
+ dec->lv[tag].val = pos+3;
+ dec->lv[tag].len = *(pos+1) << 8 | *(pos+2);
+ len = dec->lv[tag].len + 3;
+ if (pos + len > buf + buf_len)
+ return -2;
+ num_parsed++;
+ break;
+ }
+ }
+ //tlv_dump(dec);
+ return num_parsed;
+}
+
diff --git a/openbsc/src/trau_frame.c b/openbsc/src/trau_frame.c
new file mode 100644
index 000000000..aa039574b
--- /dev/null
+++ b/openbsc/src/trau_frame.c
@@ -0,0 +1,255 @@
+/* TRAU frame handling according to GSM TS 08.60 */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/trau_frame.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/debug.h>
+
+static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num)
+{
+ int i;
+ u_int32_t ret = 0;
+
+ for (i = offset; i < offset + num; i++) {
+ ret = ret << 1;
+ if (bitbuf[i])
+ ret |= 1;
+ }
+ return ret;
+}
+
+/* Decode according to 3.1.1 */
+static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+ int i;
+ int d_idx = 0;
+
+ /* C1 .. C15 */
+ memcpy(fr->c_bits+0, trau_bits+17, 15);
+ /* C16 .. C21 */
+ memcpy(fr->c_bits+15, trau_bits+310, 6);
+ /* T1 .. T4 */
+ memcpy(fr->t_bits+0, trau_bits+316, 4);
+ /* D1 .. D255 */
+ for (i = 32; i < 304; i+= 16) {
+ memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
+ d_idx += 15;
+ }
+ /* D256 .. D260 */
+ memcpy(fr->d_bits + d_idx, trau_bits + 305, 5);
+}
+
+/* Decode according to 3.1.2 */
+static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+ int i;
+ int d_idx = 0;
+
+ /* C1 .. C15 */
+ memcpy(fr->c_bits+0, trau_bits+17, 15);
+ /* C16 .. C25 */
+ memcpy(fr->c_bits+15, trau_bits+33, 10);
+ /* T1 .. T4 */
+ memcpy(fr->t_bits+0, trau_bits+316, 4);
+ /* D1 .. D5 */
+ memcpy(fr->d_bits, trau_bits+43, 5);
+ /* D6 .. D245 */
+ for (i = 48; i < 304; i += 16) {
+ memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
+ d_idx += 15;
+ }
+ /* D246 .. D256 */
+ memcpy(fr->d_bits + d_idx, trau_bits + 305, 11);
+}
+
+int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+ u_int8_t cbits5 = get_bits(trau_bits, 17, 5);
+
+ switch (cbits5) {
+ case TRAU_FT_FR_UP:
+ case TRAU_FT_FR_DOWN:
+ case TRAU_FT_IDLE_UP:
+ case TRAU_FT_IDLE_DOWN:
+ case TRAU_FT_EFR:
+ decode_fr(fr, trau_bits);
+ break;
+ case TRAU_FT_AMR:
+ decode_amr(fr, trau_bits);
+ break;
+ case TRAU_FT_OM_UP:
+ case TRAU_FT_OM_DOWN:
+ case TRAU_FT_DATA_UP:
+ case TRAU_FT_DATA_DOWN:
+ case TRAU_FT_D145_SYNC:
+ case TRAU_FT_EDATA:
+ DEBUGP(DMUX, "can't decode unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ default:
+ DEBUGP(DMUX, "can't decode unknown TRAU Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 };
+const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 };
+
+/* modify an uplink TRAU frame so we can send it downlink */
+int trau_frame_up2down(struct decoded_trau_frame *fr)
+{
+ u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
+
+ switch (cbits5) {
+ case TRAU_FT_FR_UP:
+ memcpy(fr->c_bits, ft_fr_down_bits, 5);
+ /* clear time alignment */
+ memset(fr->c_bits+5, 0, 6);
+ /* FIXME: SP / BFI in case of DTx */
+ /* C12 .. C21 are spare and coded as '1' */
+ memset(fr->c_bits+11, 0x01, 10);
+ break;
+ case TRAU_FT_EFR:
+ /* clear time alignment */
+ memset(fr->c_bits+5, 0, 6);
+ /* FIXME: set UFE appropriately */
+ /* FIXME: SP / BFI in case of DTx */
+ break;
+ case TRAU_FT_IDLE_UP:
+ memcpy(fr->c_bits, ft_idle_down_bits, 5);
+ /* clear time alignment */
+ memset(fr->c_bits+5, 0, 6);
+ /* FIXME: SP / BFI in case of DTx */
+ /* C12 .. C21 are spare and coded as '1' */
+ memset(fr->c_bits+11, 0x01, 10);
+ break;
+ case TRAU_FT_FR_DOWN:
+ case TRAU_FT_IDLE_DOWN:
+ case TRAU_FT_OM_DOWN:
+ case TRAU_FT_DATA_DOWN:
+ /* we cannot convert a downlink to a downlink frame */
+ return -EINVAL;
+ break;
+ case TRAU_FT_AMR:
+ case TRAU_FT_OM_UP:
+ case TRAU_FT_DATA_UP:
+ case TRAU_FT_D145_SYNC:
+ case TRAU_FT_EDATA:
+ DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ default:
+ DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ }
+
+ return 0;
+
+}
+
+static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
+{
+ int i;
+ int d_idx = 0;
+
+ trau_bits[16] = 1;
+ /* C1 .. C15 */
+ memcpy(trau_bits+17, fr->c_bits+0, 15);
+ /* D1 .. D255 */
+ for (i = 32; i < 304; i+= 16) {
+ trau_bits[i] = 1;
+ memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15);
+ d_idx += 15;
+ }
+ /* D256 .. D260 */
+ trau_bits[304] = 1;
+ memcpy(trau_bits + 305, fr->d_bits + d_idx, 5);
+ /* C16 .. C21 */
+ memcpy(trau_bits+310, fr->c_bits+15, 6);
+
+ /* FIXME: handle timing adjustment */
+
+ /* T1 .. T4 */
+ memcpy(trau_bits+316, fr->t_bits+0, 4);
+}
+
+
+int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
+{
+ u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
+
+ /* 16 bits of sync header */
+ memset(trau_bits, 0, 16);
+
+ switch (cbits5) {
+ case TRAU_FT_FR_UP:
+ case TRAU_FT_FR_DOWN:
+ case TRAU_FT_IDLE_UP:
+ case TRAU_FT_IDLE_DOWN:
+ case TRAU_FT_EFR:
+ encode_fr(trau_bits, fr);
+ break;
+ case TRAU_FT_AMR:
+ case TRAU_FT_OM_UP:
+ case TRAU_FT_OM_DOWN:
+ case TRAU_FT_DATA_UP:
+ case TRAU_FT_DATA_DOWN:
+ case TRAU_FT_D145_SYNC:
+ case TRAU_FT_EDATA:
+ DEBUGP(DMUX, "unimplemented TRAU Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ default:
+ DEBUGP(DMUX, "unknown TRAU Frame Type 0x%02x\n", cbits5);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static struct decoded_trau_frame fr_idle_frame = {
+ .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */
+ .t_bits = { 1, 1, 1, 1 },
+};
+static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS];
+static int dbits_initted;
+
+u_int8_t *trau_idle_frame(void)
+{
+ /* only initialize during the first call */
+ if (!dbits_initted) {
+ /* set all D-bits to 1 */
+ memset(&fr_idle_frame.d_bits, 0x01, 260);
+ encode_fr(encoded_idle_frame, &fr_idle_frame);
+ }
+ return encoded_idle_frame;
+}
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
new file mode 100644
index 000000000..96f858992
--- /dev/null
+++ b/openbsc/src/trau_mux.c
@@ -0,0 +1,212 @@
+/* Simple TRAU frame reflector to route voice calls */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/trau_mux.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/debug.h>
+
+struct map_entry {
+ struct llist_head list;
+ struct gsm_e1_subslot src, dst;
+};
+
+struct upqueue_entry {
+ struct llist_head list;
+ struct gsm_network *net;
+ struct gsm_e1_subslot src;
+ u_int32_t callref;
+};
+
+static LLIST_HEAD(ss_map);
+static LLIST_HEAD(ss_upqueue);
+
+/* map one particular subslot to another subslot */
+int trau_mux_map(const struct gsm_e1_subslot *src,
+ const struct gsm_e1_subslot *dst)
+{
+ struct map_entry *me = malloc(sizeof(*me));
+ if (!me)
+ return -ENOMEM;
+
+ DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
+ "and (e1=%u,ts=%u,ss=%u)\n",
+ src->e1_nr, src->e1_ts, src->e1_ts_ss,
+ dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
+
+ /* make sure to get rid of any stale old mappings */
+ trau_mux_unmap(src, 0);
+ trau_mux_unmap(dst, 0);
+
+ memcpy(&me->src, src, sizeof(me->src));
+ memcpy(&me->dst, dst, sizeof(me->dst));
+ llist_add(&me->list, &ss_map);
+
+ return 0;
+}
+
+int trau_mux_map_lchan(const struct gsm_lchan *src,
+ const struct gsm_lchan *dst)
+{
+ struct gsm_e1_subslot *src_ss, *dst_ss;
+
+ src_ss = &src->ts->e1_link;
+ dst_ss = &dst->ts->e1_link;
+
+ return trau_mux_map(src_ss, dst_ss);
+}
+
+
+/* unmap one particular subslot from another subslot */
+int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref)
+{
+ struct map_entry *me, *me2;
+ struct upqueue_entry *ue, *ue2;
+
+ if (ss)
+ llist_for_each_entry_safe(me, me2, &ss_map, list) {
+ if (!memcmp(&me->src, ss, sizeof(*ss)) ||
+ !memcmp(&me->dst, ss, sizeof(*ss))) {
+ llist_del(&me->list);
+ return 0;
+ }
+ }
+ llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
+ if (ue->callref == callref) {
+ llist_del(&ue->list);
+ return 0;
+ }
+ if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
+ llist_del(&ue->list);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/* look-up an enty in the TRAU mux map */
+static struct gsm_e1_subslot *
+lookup_trau_mux_map(const struct gsm_e1_subslot *src)
+{
+ struct map_entry *me;
+
+ llist_for_each_entry(me, &ss_map, list) {
+ if (!memcmp(&me->src, src, sizeof(*src)))
+ return &me->dst;
+ if (!memcmp(&me->dst, src, sizeof(*src)))
+ return &me->src;
+ }
+ return NULL;
+}
+
+/* look-up an enty in the TRAU upqueue */
+struct upqueue_entry *
+lookup_trau_upqueue(const struct gsm_e1_subslot *src)
+{
+ struct upqueue_entry *ue;
+
+ llist_for_each_entry(ue, &ss_upqueue, list) {
+ if (!memcmp(&ue->src, src, sizeof(*src)))
+ return ue;
+ }
+ return NULL;
+}
+
+/* we get called by subchan_demux */
+int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
+ const u_int8_t *trau_bits, int num_bits)
+{
+ struct decoded_trau_frame tf;
+ u_int8_t trau_bits_out[TRAU_FRAME_BITS];
+ struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
+ struct subch_mux *mx;
+ int rc;
+
+ /* decode TRAU, change it to downlink, re-encode */
+ rc = decode_trau_frame(&tf, trau_bits);
+ if (rc)
+ return rc;
+
+ if (!dst_e1_ss)
+ return -EINVAL;
+
+ mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
+ if (!mx)
+ return -EINVAL;
+
+ trau_frame_up2down(&tf);
+ encode_trau_frame(trau_bits_out, &tf);
+
+ /* and send it to the muxer */
+ return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
+ TRAU_FRAME_BITS);
+}
+
+/* add receiver instance for lchan and callref */
+int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
+{
+ struct gsm_e1_subslot *src_ss;
+ struct upqueue_entry *ue = malloc(sizeof(*ue));
+
+ if (!ue)
+ return -ENOMEM;
+
+ src_ss = &lchan->ts->e1_link;
+
+ DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
+ "and (callref 0x%x)\n",
+ src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
+ callref);
+
+ /* make sure to get rid of any stale old mappings */
+ trau_mux_unmap(src_ss, callref);
+
+ memcpy(&ue->src, src_ss, sizeof(ue->src));
+ ue->net = lchan->ts->trx->bts->network;
+ ue->callref = callref;
+ llist_add(&ue->list, &ss_upqueue);
+
+ return 0;
+}
+
+int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
+{
+ u_int8_t trau_bits_out[TRAU_FRAME_BITS];
+ struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
+ struct subch_mux *mx;
+
+ mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
+ if (!mx)
+ return -EINVAL;
+
+ encode_trau_frame(trau_bits_out, tf);
+
+ /* and send it to the muxer */
+ return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
+ TRAU_FRAME_BITS);
+}
diff --git a/openbsc/src/vty/buffer.c b/openbsc/src/vty/buffer.c
new file mode 100644
index 000000000..63661004d
--- /dev/null
+++ b/openbsc/src/vty/buffer.c
@@ -0,0 +1,460 @@
+/*
+ * Buffering of output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/uio.h>
+
+#include <vty/buffer.h>
+#include <vty/vty.h>
+
+/* Buffer master. */
+struct buffer {
+ /* Data list. */
+ struct buffer_data *head;
+ struct buffer_data *tail;
+
+ /* Size of each buffer_data chunk. */
+ size_t size;
+};
+
+/* Data container. */
+struct buffer_data {
+ struct buffer_data *next;
+
+ /* Location to add new data. */
+ size_t cp;
+
+ /* Pointer to data not yet flushed. */
+ size_t sp;
+
+ /* Actual data stream (variable length). */
+ unsigned char data[0]; /* real dimension is buffer->size */
+};
+
+/* It should always be true that: 0 <= sp <= cp <= size */
+
+/* Default buffer size (used if none specified). It is rounded up to the
+ next page boundery. */
+#define BUFFER_SIZE_DEFAULT 4096
+
+#define BUFFER_DATA_FREE(D) free((D))
+
+/* Make new buffer. */
+struct buffer *buffer_new(size_t size)
+{
+ struct buffer *b;
+
+ b = calloc(1, sizeof(struct buffer));
+
+ if (size)
+ b->size = size;
+ else {
+ static size_t default_size;
+ if (!default_size) {
+ long pgsz = sysconf(_SC_PAGESIZE);
+ default_size =
+ ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
+ }
+ b->size = default_size;
+ }
+
+ return b;
+}
+
+/* Free buffer. */
+void buffer_free(struct buffer *b)
+{
+ buffer_reset(b);
+ free(b);
+}
+
+/* Make string clone. */
+char *buffer_getstr(struct buffer *b)
+{
+ size_t totlen = 0;
+ struct buffer_data *data;
+ char *s;
+ char *p;
+
+ for (data = b->head; data; data = data->next)
+ totlen += data->cp - data->sp;
+ if (!(s = malloc(totlen + 1)))
+ return NULL;
+ p = s;
+ for (data = b->head; data; data = data->next) {
+ memcpy(p, data->data + data->sp, data->cp - data->sp);
+ p += data->cp - data->sp;
+ }
+ *p = '\0';
+ return s;
+}
+
+/* Return 1 if buffer is empty. */
+int buffer_empty(struct buffer *b)
+{
+ return (b->head == NULL);
+}
+
+/* Clear and free all allocated data. */
+void buffer_reset(struct buffer *b)
+{
+ struct buffer_data *data;
+ struct buffer_data *next;
+
+ for (data = b->head; data; data = next) {
+ next = data->next;
+ BUFFER_DATA_FREE(data);
+ }
+ b->head = b->tail = NULL;
+}
+
+/* Add buffer_data to the end of buffer. */
+static struct buffer_data *buffer_add(struct buffer *b)
+{
+ struct buffer_data *d;
+
+ d = malloc(offsetof(struct buffer_data, data[b->size]));
+ if (!d)
+ return NULL;
+ d->cp = d->sp = 0;
+ d->next = NULL;
+
+ if (b->tail)
+ b->tail->next = d;
+ else
+ b->head = d;
+ b->tail = d;
+
+ return d;
+}
+
+/* Write data to buffer. */
+void buffer_put(struct buffer *b, const void *p, size_t size)
+{
+ struct buffer_data *data = b->tail;
+ const char *ptr = p;
+
+ /* We use even last one byte of data buffer. */
+ while (size) {
+ size_t chunk;
+
+ /* If there is no data buffer add it. */
+ if (data == NULL || data->cp == b->size)
+ data = buffer_add(b);
+
+ chunk =
+ ((size <=
+ (b->size - data->cp)) ? size : (b->size - data->cp));
+ memcpy((data->data + data->cp), ptr, chunk);
+ size -= chunk;
+ ptr += chunk;
+ data->cp += chunk;
+ }
+}
+
+/* Insert character into the buffer. */
+void buffer_putc(struct buffer *b, u_char c)
+{
+ buffer_put(b, &c, 1);
+}
+
+/* Put string to the buffer. */
+void buffer_putstr(struct buffer *b, const char *c)
+{
+ buffer_put(b, c, strlen(c));
+}
+
+/* Keep flushing data to the fd until the buffer is empty or an error is
+ encountered or the operation would block. */
+buffer_status_t buffer_flush_all(struct buffer *b, int fd)
+{
+ buffer_status_t ret;
+ struct buffer_data *head;
+ size_t head_sp;
+
+ if (!b->head)
+ return BUFFER_EMPTY;
+ head_sp = (head = b->head)->sp;
+ /* Flush all data. */
+ while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
+ if ((b->head == head) && (head_sp == head->sp)
+ && (errno != EINTR))
+ /* No data was flushed, so kernel buffer must be full. */
+ return ret;
+ head_sp = (head = b->head)->sp;
+ }
+
+ return ret;
+}
+
+#if 0
+/* Flush enough data to fill a terminal window of the given scene (used only
+ by vty telnet interface). */
+buffer_status_t
+buffer_flush_window(struct buffer * b, int fd, int width, int height,
+ int erase_flag, int no_more_flag)
+{
+ int nbytes;
+ int iov_alloc;
+ int iov_index;
+ struct iovec *iov;
+ struct iovec small_iov[3];
+ char more[] = " --More-- ";
+ char erase[] =
+ { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ };
+ struct buffer_data *data;
+ int column;
+
+ if (!b->head)
+ return BUFFER_EMPTY;
+
+ if (height < 1) {
+ zlog_warn
+ ("%s called with non-positive window height %d, forcing to 1",
+ __func__, height);
+ height = 1;
+ } else if (height >= 2)
+ height--;
+ if (width < 1) {
+ zlog_warn
+ ("%s called with non-positive window width %d, forcing to 1",
+ __func__, width);
+ width = 1;
+ }
+
+ /* For erase and more data add two to b's buffer_data count. */
+ if (b->head->next == NULL) {
+ iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
+ iov = small_iov;
+ } else {
+ iov_alloc = ((height * (width + 2)) / b->size) + 10;
+ iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
+ }
+ iov_index = 0;
+
+ /* Previously print out is performed. */
+ if (erase_flag) {
+ iov[iov_index].iov_base = erase;
+ iov[iov_index].iov_len = sizeof erase;
+ iov_index++;
+ }
+
+ /* Output data. */
+ column = 1; /* Column position of next character displayed. */
+ for (data = b->head; data && (height > 0); data = data->next) {
+ size_t cp;
+
+ cp = data->sp;
+ while ((cp < data->cp) && (height > 0)) {
+ /* Calculate lines remaining and column position after displaying
+ this character. */
+ if (data->data[cp] == '\r')
+ column = 1;
+ else if ((data->data[cp] == '\n') || (column == width)) {
+ column = 1;
+ height--;
+ } else
+ column++;
+ cp++;
+ }
+ iov[iov_index].iov_base = (char *)(data->data + data->sp);
+ iov[iov_index++].iov_len = cp - data->sp;
+ data->sp = cp;
+
+ if (iov_index == iov_alloc)
+ /* This should not ordinarily happen. */
+ {
+ iov_alloc *= 2;
+ if (iov != small_iov) {
+ zlog_warn("%s: growing iov array to %d; "
+ "width %d, height %d, size %lu",
+ __func__, iov_alloc, width, height,
+ (u_long) b->size);
+ iov =
+ XREALLOC(MTYPE_TMP, iov,
+ iov_alloc * sizeof(*iov));
+ } else {
+ /* This should absolutely never occur. */
+ zlog_err
+ ("%s: corruption detected: iov_small overflowed; "
+ "head %p, tail %p, head->next %p",
+ __func__, b->head, b->tail, b->head->next);
+ iov =
+ XMALLOC(MTYPE_TMP,
+ iov_alloc * sizeof(*iov));
+ memcpy(iov, small_iov, sizeof(small_iov));
+ }
+ }
+ }
+
+ /* In case of `more' display need. */
+ if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
+ iov[iov_index].iov_base = more;
+ iov[iov_index].iov_len = sizeof more;
+ iov_index++;
+ }
+#ifdef IOV_MAX
+ /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
+ example: Solaris2.6 are defined IOV_MAX size at 16. */
+ {
+ struct iovec *c_iov = iov;
+ nbytes = 0; /* Make sure it's initialized. */
+
+ while (iov_index > 0) {
+ int iov_size;
+
+ iov_size =
+ ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
+ if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+ zlog_warn("%s: writev to fd %d failed: %s",
+ __func__, fd, safe_strerror(errno));
+ break;
+ }
+
+ /* move pointer io-vector */
+ c_iov += iov_size;
+ iov_index -= iov_size;
+ }
+ }
+#else /* IOV_MAX */
+ if ((nbytes = writev(fd, iov, iov_index)) < 0)
+ zlog_warn("%s: writev to fd %d failed: %s",
+ __func__, fd, safe_strerror(errno));
+#endif /* IOV_MAX */
+
+ /* Free printed buffer data. */
+ while (b->head && (b->head->sp == b->head->cp)) {
+ struct buffer_data *del;
+ if (!(b->head = (del = b->head)->next))
+ b->tail = NULL;
+ BUFFER_DATA_FREE(del);
+ }
+
+ if (iov != small_iov)
+ XFREE(MTYPE_TMP, iov);
+
+ return (nbytes < 0) ? BUFFER_ERROR :
+ (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
+}
+#endif
+
+/* This function (unlike other buffer_flush* functions above) is designed
+to work with non-blocking sockets. It does not attempt to write out
+all of the queued data, just a "big" chunk. It returns 0 if it was
+able to empty out the buffers completely, 1 if more flushing is
+required later, or -1 on a fatal write error. */
+buffer_status_t buffer_flush_available(struct buffer * b, int fd)
+{
+
+/* These are just reasonable values to make sure a significant amount of
+data is written. There's no need to go crazy and try to write it all
+in one shot. */
+#ifdef IOV_MAX
+#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
+#else
+#define MAX_CHUNKS 16
+#endif
+#define MAX_FLUSH 131072
+
+ struct buffer_data *d;
+ size_t written;
+ struct iovec iov[MAX_CHUNKS];
+ size_t iovcnt = 0;
+ size_t nbyte = 0;
+
+ for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
+ d = d->next, iovcnt++) {
+ iov[iovcnt].iov_base = d->data + d->sp;
+ nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
+ }
+
+ if (!nbyte)
+ /* No data to flush: should we issue a warning message? */
+ return BUFFER_EMPTY;
+
+ /* only place where written should be sign compared */
+ if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ /* Calling code should try again later. */
+ return BUFFER_PENDING;
+ return BUFFER_ERROR;
+ }
+
+ /* Free printed buffer data. */
+ while (written > 0) {
+ struct buffer_data *d;
+ if (!(d = b->head))
+ break;
+ if (written < d->cp - d->sp) {
+ d->sp += written;
+ return BUFFER_PENDING;
+ }
+
+ written -= (d->cp - d->sp);
+ if (!(b->head = d->next))
+ b->tail = NULL;
+ BUFFER_DATA_FREE(d);
+ }
+
+ return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+
+#undef MAX_CHUNKS
+#undef MAX_FLUSH
+}
+
+buffer_status_t
+buffer_write(struct buffer * b, int fd, const void *p, size_t size)
+{
+ ssize_t nbytes;
+
+#if 0
+ /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
+
+ if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
+ return BUFFER_ERROR;
+#endif
+ if (b->head)
+ /* Buffer is not empty, so do not attempt to write the new data. */
+ nbytes = 0;
+ else if ((nbytes = write(fd, p, size)) < 0) {
+ if (ERRNO_IO_RETRY(errno))
+ nbytes = 0;
+ else
+ return BUFFER_ERROR;
+ }
+ /* Add any remaining data to the buffer. */
+ {
+ size_t written = nbytes;
+ if (written < size)
+ buffer_put(b, ((const char *)p) + written,
+ size - written);
+ }
+ return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+}
diff --git a/openbsc/src/vty/cardshell.h b/openbsc/src/vty/cardshell.h
new file mode 100644
index 000000000..d963a3810
--- /dev/null
+++ b/openbsc/src/vty/cardshell.h
@@ -0,0 +1,5 @@
+#define QUAGGA_PROGNAME "OpenBSC"
+#define QUAGGA_VERSION "0.01"
+#define QUAGGA_COPYRIGHT "Harald Welte <laforge@gnumonks.org>"
+#define CONFIGFILE_MASK 022
+#define SYSCONFDIR "/usr/local/etc"
diff --git a/openbsc/src/vty/command.c b/openbsc/src/vty/command.c
new file mode 100644
index 000000000..94a5c2af2
--- /dev/null
+++ b/openbsc/src/vty/command.c
@@ -0,0 +1,3416 @@
+/*
+ $Id: command.c,v 1.47 2005/04/25 16:26:42 paul Exp $
+
+ Command interpreter routine for virtual terminal [aka TeletYpe]
+ Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published
+by the Free Software Foundation; either version 2, or (at your
+option) any later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#include "cardshell.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#define _XOPEN_SOURCE
+#include <unistd.h>
+#include <assert.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+
+//#include "memory.h"
+//#include "log.h"
+//#include <lib/version.h>
+//#include "thread.h"
+#include <vty/vector.h>
+#include <vty/vty.h>
+#include <vty/command.h>
+//#include "workqueue.h"
+
+#include <openbsc/gsm_data.h>
+
+/* Command vector which includes some level of command lists. Normally
+ each daemon maintains each own cmdvec. */
+vector cmdvec;
+
+/* Host information structure. */
+struct host host;
+
+/* Standard command node structures. */
+struct cmd_node auth_node = {
+ AUTH_NODE,
+ "Password: ",
+};
+
+struct cmd_node view_node = {
+ VIEW_NODE,
+ "%s> ",
+};
+
+struct cmd_node auth_enable_node = {
+ AUTH_ENABLE_NODE,
+ "Password: ",
+};
+
+struct cmd_node enable_node = {
+ ENABLE_NODE,
+ "%s# ",
+};
+
+struct cmd_node config_node = {
+ CONFIG_NODE,
+ "%s(config)# ",
+ 1
+};
+
+/* Default motd string. */
+const char *default_motd = "\r\n\
+Hello, this is " QUAGGA_PROGNAME " (version " QUAGGA_VERSION ").\r\n\
+" QUAGGA_COPYRIGHT "\r\n\
+\r\n";
+
+#if 0
+static struct facility_map {
+ int facility;
+ const char *name;
+ size_t match;
+} syslog_facilities[] = {
+ {
+ LOG_KERN, "kern", 1}, {
+ LOG_USER, "user", 2}, {
+ LOG_MAIL, "mail", 1}, {
+ LOG_DAEMON, "daemon", 1}, {
+ LOG_AUTH, "auth", 1}, {
+ LOG_SYSLOG, "syslog", 1}, {
+ LOG_LPR, "lpr", 2}, {
+ LOG_NEWS, "news", 1}, {
+ LOG_UUCP, "uucp", 2}, {
+ LOG_CRON, "cron", 1},
+#ifdef LOG_FTP
+ {
+ LOG_FTP, "ftp", 1},
+#endif
+ {
+ LOG_LOCAL0, "local0", 6}, {
+ LOG_LOCAL1, "local1", 6}, {
+ LOG_LOCAL2, "local2", 6}, {
+ LOG_LOCAL3, "local3", 6}, {
+ LOG_LOCAL4, "local4", 6}, {
+ LOG_LOCAL5, "local5", 6}, {
+ LOG_LOCAL6, "local6", 6}, {
+ LOG_LOCAL7, "local7", 6}, {
+0, NULL, 0},};
+
+static const char *facility_name(int facility)
+{
+ struct facility_map *fm;
+
+ for (fm = syslog_facilities; fm->name; fm++)
+ if (fm->facility == facility)
+ return fm->name;
+ return "";
+}
+
+static int facility_match(const char *str)
+{
+ struct facility_map *fm;
+
+ for (fm = syslog_facilities; fm->name; fm++)
+ if (!strncmp(str, fm->name, fm->match))
+ return fm->facility;
+ return -1;
+}
+
+static int level_match(const char *s)
+{
+ int level;
+
+ for (level = 0; zlog_priority[level] != NULL; level++)
+ if (!strncmp(s, zlog_priority[level], 2))
+ return level;
+ return ZLOG_DISABLED;
+}
+#endif
+
+/* This is called from main when a daemon is invoked with -v or --version. */
+void print_version(const char *progname)
+{
+ printf("%s version %s\n", progname, QUAGGA_VERSION);
+ printf("%s\n", QUAGGA_COPYRIGHT);
+}
+
+/* Utility function to concatenate argv argument into a single string
+ with inserting ' ' character between each argument. */
+char *argv_concat(const char **argv, int argc, int shift)
+{
+ int i;
+ size_t len;
+ char *str;
+ char *p;
+
+ len = 0;
+ for (i = shift; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ if (!len)
+ return NULL;
+ p = str = malloc(len);
+ for (i = shift; i < argc; i++) {
+ size_t arglen;
+ memcpy(p, argv[i], (arglen = strlen(argv[i])));
+ p += arglen;
+ *p++ = ' ';
+ }
+ *(p - 1) = '\0';
+ return str;
+}
+
+/* Install top node of command vector. */
+void install_node(struct cmd_node *node, int (*func) (struct vty *))
+{
+ vector_set_index(cmdvec, node->node, node);
+ node->func = func;
+ node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
+}
+
+/* Compare two command's string. Used in sort_node (). */
+static int cmp_node(const void *p, const void *q)
+{
+ struct cmd_element *a = *(struct cmd_element **)p;
+ struct cmd_element *b = *(struct cmd_element **)q;
+
+ return strcmp(a->string, b->string);
+}
+
+static int cmp_desc(const void *p, const void *q)
+{
+ struct desc *a = *(struct desc **)p;
+ struct desc *b = *(struct desc **)q;
+
+ return strcmp(a->cmd, b->cmd);
+}
+
+/* Sort each node's command element according to command string. */
+void sort_node()
+{
+ unsigned int i, j;
+ struct cmd_node *cnode;
+ vector descvec;
+ struct cmd_element *cmd_element;
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((cnode = vector_slot(cmdvec, i)) != NULL) {
+ vector cmd_vector = cnode->cmd_vector;
+ qsort(cmd_vector->index, vector_active(cmd_vector),
+ sizeof(void *), cmp_node);
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ && vector_active(cmd_element->strvec)) {
+ descvec =
+ vector_slot(cmd_element->strvec,
+ vector_active
+ (cmd_element->strvec) -
+ 1);
+ qsort(descvec->index,
+ vector_active(descvec),
+ sizeof(void *), cmp_desc);
+ }
+ }
+}
+
+/* Breaking up string into each command piece. I assume given
+ character is separated by a space character. Return value is a
+ vector which includes char ** data element. */
+vector cmd_make_strvec(const char *string)
+{
+ const char *cp, *start;
+ char *token;
+ int strlen;
+ vector strvec;
+
+ if (string == NULL)
+ return NULL;
+
+ cp = string;
+
+ /* Skip white spaces. */
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ /* Return if there is only white spaces */
+ if (*cp == '\0')
+ return NULL;
+
+ if (*cp == '!' || *cp == '#')
+ return NULL;
+
+ /* Prepare return vector. */
+ strvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Copy each command piece and set into vector. */
+ while (1) {
+ start = cp;
+ while (!(isspace((int)*cp) || *cp == '\r' || *cp == '\n') &&
+ *cp != '\0')
+ cp++;
+ strlen = cp - start;
+ token = malloc(strlen + 1);
+ memcpy(token, start, strlen);
+ *(token + strlen) = '\0';
+ vector_set(strvec, token);
+
+ while ((isspace((int)*cp) || *cp == '\n' || *cp == '\r') &&
+ *cp != '\0')
+ cp++;
+
+ if (*cp == '\0')
+ return strvec;
+ }
+}
+
+/* Free allocated string vector. */
+void cmd_free_strvec(vector v)
+{
+ unsigned int i;
+ char *cp;
+
+ if (!v)
+ return;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((cp = vector_slot(v, i)) != NULL)
+ free(cp);
+
+ vector_free(v);
+}
+
+/* Fetch next description. Used in cmd_make_descvec(). */
+static char *cmd_desc_str(const char **string)
+{
+ const char *cp, *start;
+ char *token;
+ int strlen;
+
+ cp = *string;
+
+ if (cp == NULL)
+ return NULL;
+
+ /* Skip white spaces. */
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ /* Return if there is only white spaces */
+ if (*cp == '\0')
+ return NULL;
+
+ start = cp;
+
+ while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
+ cp++;
+
+ strlen = cp - start;
+ token = malloc(strlen + 1);
+ memcpy(token, start, strlen);
+ *(token + strlen) = '\0';
+
+ *string = cp;
+
+ return token;
+}
+
+/* New string vector. */
+static vector cmd_make_descvec(const char *string, const char *descstr)
+{
+ int multiple = 0;
+ const char *sp;
+ char *token;
+ int len;
+ const char *cp;
+ const char *dp;
+ vector allvec;
+ vector strvec = NULL;
+ struct desc *desc;
+
+ cp = string;
+ dp = descstr;
+
+ if (cp == NULL)
+ return NULL;
+
+ allvec = vector_init(VECTOR_MIN_SIZE);
+
+ while (1) {
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ if (*cp == '(') {
+ multiple = 1;
+ cp++;
+ }
+ if (*cp == ')') {
+ multiple = 0;
+ cp++;
+ }
+ if (*cp == '|') {
+ if (!multiple) {
+ fprintf(stderr, "Command parse error!: %s\n",
+ string);
+ exit(1);
+ }
+ cp++;
+ }
+
+ while (isspace((int)*cp) && *cp != '\0')
+ cp++;
+
+ if (*cp == '(') {
+ multiple = 1;
+ cp++;
+ }
+
+ if (*cp == '\0')
+ return allvec;
+
+ sp = cp;
+
+ while (!
+ (isspace((int)*cp) || *cp == '\r' || *cp == '\n'
+ || *cp == ')' || *cp == '|') && *cp != '\0')
+ cp++;
+
+ len = cp - sp;
+
+ token = malloc(len + 1);
+ memcpy(token, sp, len);
+ *(token + len) = '\0';
+
+ desc = calloc(1, sizeof(struct desc));
+ desc->cmd = token;
+ desc->str = cmd_desc_str(&dp);
+
+ if (multiple) {
+ if (multiple == 1) {
+ strvec = vector_init(VECTOR_MIN_SIZE);
+ vector_set(allvec, strvec);
+ }
+ multiple++;
+ } else {
+ strvec = vector_init(VECTOR_MIN_SIZE);
+ vector_set(allvec, strvec);
+ }
+ vector_set(strvec, desc);
+ }
+}
+
+/* Count mandantory string vector size. This is to determine inputed
+ command has enough command length. */
+static int cmd_cmdsize(vector strvec)
+{
+ unsigned int i;
+ int size = 0;
+ vector descvec;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(strvec); i++)
+ if ((descvec = vector_slot(strvec, i)) != NULL) {
+ if ((vector_active(descvec)) == 1
+ && (desc = vector_slot(descvec, 0)) != NULL) {
+ if (desc->cmd == NULL || CMD_OPTION(desc->cmd))
+ return size;
+ else
+ size++;
+ } else
+ size++;
+ }
+ return size;
+}
+
+/* Return prompt character of specified node. */
+const char *cmd_prompt(enum node_type node)
+{
+ struct cmd_node *cnode;
+
+ cnode = vector_slot(cmdvec, node);
+ return cnode->prompt;
+}
+
+/* Install a command into a node. */
+void install_element(enum node_type ntype, struct cmd_element *cmd)
+{
+ struct cmd_node *cnode;
+
+ cnode = vector_slot(cmdvec, ntype);
+
+ if (cnode == NULL) {
+ fprintf(stderr,
+ "Command node %d doesn't exist, please check it\n",
+ ntype);
+ exit(1);
+ }
+
+ vector_set(cnode->cmd_vector, cmd);
+
+ cmd->strvec = cmd_make_descvec(cmd->string, cmd->doc);
+ cmd->cmdsize = cmd_cmdsize(cmd->strvec);
+}
+
+static unsigned char itoa64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void to64(char *s, long v, int n)
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+}
+
+static char *zencrypt(const char *passwd)
+{
+ char salt[6];
+ struct timeval tv;
+ char *crypt(const char *, const char *);
+
+ gettimeofday(&tv, 0);
+
+ to64(&salt[0], random(), 3);
+ to64(&salt[3], tv.tv_usec, 3);
+ salt[5] = '\0';
+
+ return crypt(passwd, salt);
+}
+
+/* This function write configuration of this host. */
+static int config_write_host(struct vty *vty)
+{
+#if 0
+ if (host.name)
+ vty_out(vty, "hostname %s%s", host.name, VTY_NEWLINE);
+
+ if (host.encrypt) {
+ if (host.password_encrypt)
+ vty_out(vty, "password 8 %s%s", host.password_encrypt,
+ VTY_NEWLINE);
+ if (host.enable_encrypt)
+ vty_out(vty, "enable password 8 %s%s",
+ host.enable_encrypt, VTY_NEWLINE);
+ } else {
+ if (host.password)
+ vty_out(vty, "password %s%s", host.password,
+ VTY_NEWLINE);
+ if (host.enable)
+ vty_out(vty, "enable password %s%s", host.enable,
+ VTY_NEWLINE);
+ }
+
+ if (zlog_default->default_lvl != LOG_DEBUG) {
+ vty_out(vty, "! N.B. The 'log trap' command is deprecated.%s",
+ VTY_NEWLINE);
+ vty_out(vty, "log trap %s%s",
+ zlog_priority[zlog_default->default_lvl], VTY_NEWLINE);
+ }
+
+ if (host.logfile
+ && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) {
+ vty_out(vty, "log file %s", host.logfile);
+ if (zlog_default->maxlvl[ZLOG_DEST_FILE] !=
+ zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->
+ maxlvl[ZLOG_DEST_FILE]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
+ vty_out(vty, "log stdout");
+ if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] !=
+ zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->
+ maxlvl[ZLOG_DEST_STDOUT]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ vty_out(vty, "no log monitor%s", VTY_NEWLINE);
+ else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] !=
+ zlog_default->default_lvl)
+ vty_out(vty, "log monitor %s%s",
+ zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],
+ VTY_NEWLINE);
+
+ if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
+ vty_out(vty, "log syslog");
+ if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] !=
+ zlog_default->default_lvl)
+ vty_out(vty, " %s",
+ zlog_priority[zlog_default->
+ maxlvl[ZLOG_DEST_SYSLOG]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (zlog_default->facility != LOG_DAEMON)
+ vty_out(vty, "log facility %s%s",
+ facility_name(zlog_default->facility), VTY_NEWLINE);
+
+ if (zlog_default->record_priority == 1)
+ vty_out(vty, "log record-priority%s", VTY_NEWLINE);
+
+ if (host.advanced)
+ vty_out(vty, "service advanced-vty%s", VTY_NEWLINE);
+
+ if (host.encrypt)
+ vty_out(vty, "service password-encryption%s", VTY_NEWLINE);
+
+ if (host.lines >= 0)
+ vty_out(vty, "service terminal-length %d%s", host.lines,
+ VTY_NEWLINE);
+
+ if (host.motdfile)
+ vty_out(vty, "banner motd file %s%s", host.motdfile,
+ VTY_NEWLINE);
+ else if (!host.motd)
+ vty_out(vty, "no banner motd%s", VTY_NEWLINE);
+
+#endif
+ return 1;
+}
+
+/* Utility function for getting command vector. */
+static vector cmd_node_vector(vector v, enum node_type ntype)
+{
+ struct cmd_node *cnode = vector_slot(v, ntype);
+ return cnode->cmd_vector;
+}
+
+#if 0
+/* Filter command vector by symbol. This function is not actually used;
+ * should it be deleted? */
+static int cmd_filter_by_symbol(char *command, char *symbol)
+{
+ int i, lim;
+
+ if (strcmp(symbol, "IPV4_ADDRESS") == 0) {
+ i = 0;
+ lim = strlen(command);
+ while (i < lim) {
+ if (!
+ (isdigit((int)command[i]) || command[i] == '.'
+ || command[i] == '/'))
+ return 1;
+ i++;
+ }
+ return 0;
+ }
+ if (strcmp(symbol, "STRING") == 0) {
+ i = 0;
+ lim = strlen(command);
+ while (i < lim) {
+ if (!
+ (isalpha((int)command[i]) || command[i] == '_'
+ || command[i] == '-'))
+ return 1;
+ i++;
+ }
+ return 0;
+ }
+ if (strcmp(symbol, "IFNAME") == 0) {
+ i = 0;
+ lim = strlen(command);
+ while (i < lim) {
+ if (!isalnum((int)command[i]))
+ return 1;
+ i++;
+ }
+ return 0;
+ }
+ return 0;
+}
+#endif
+
+/* Completion match types. */
+enum match_type {
+ no_match,
+ extend_match,
+ ipv4_prefix_match,
+ ipv4_match,
+ ipv6_prefix_match,
+ ipv6_match,
+ range_match,
+ vararg_match,
+ partly_match,
+ exact_match
+};
+
+static enum match_type cmd_ipv4_match(const char *str)
+{
+ const char *sp;
+ int dots = 0, nums = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;) {
+ memset(buf, 0, sizeof(buf));
+ sp = str;
+ while (*str != '\0') {
+ if (*str == '.') {
+ if (dots >= 3)
+ return no_match;
+
+ if (*(str + 1) == '.')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy(buf, sp, str - sp);
+ if (atoi(buf) > 255)
+ return no_match;
+
+ nums++;
+
+ if (*str == '\0')
+ break;
+
+ str++;
+ }
+
+ if (nums < 4)
+ return partly_match;
+
+ return exact_match;
+}
+
+static enum match_type cmd_ipv4_prefix_match(const char *str)
+{
+ const char *sp;
+ int dots = 0;
+ char buf[4];
+
+ if (str == NULL)
+ return partly_match;
+
+ for (;;) {
+ memset(buf, 0, sizeof(buf));
+ sp = str;
+ while (*str != '\0' && *str != '/') {
+ if (*str == '.') {
+ if (dots == 3)
+ return no_match;
+
+ if (*(str + 1) == '.' || *(str + 1) == '/')
+ return no_match;
+
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ dots++;
+ break;
+ }
+
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (str - sp > 3)
+ return no_match;
+
+ strncpy(buf, sp, str - sp);
+ if (atoi(buf) > 255)
+ return no_match;
+
+ if (dots == 3) {
+ if (*str == '/') {
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ str++;
+ break;
+ } else if (*str == '\0')
+ return partly_match;
+ }
+
+ if (*str == '\0')
+ return partly_match;
+
+ str++;
+ }
+
+ sp = str;
+ while (*str != '\0') {
+ if (!isdigit((int)*str))
+ return no_match;
+
+ str++;
+ }
+
+ if (atoi(sp) > 32)
+ return no_match;
+
+ return exact_match;
+}
+
+#define IPV6_ADDR_STR "0123456789abcdefABCDEF:.%"
+#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:.%/"
+#define STATE_START 1
+#define STATE_COLON 2
+#define STATE_DOUBLE 3
+#define STATE_ADDR 4
+#define STATE_DOT 5
+#define STATE_SLASH 6
+#define STATE_MASK 7
+
+#ifdef HAVE_IPV6
+
+static enum match_type cmd_ipv6_match(const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ const char *sp = NULL;
+ struct sockaddr_in6 sin6_dummy;
+ int ret;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn(str, IPV6_ADDR_STR) != strlen(str))
+ return no_match;
+
+ /* use inet_pton that has a better support,
+ * for example inet_pton can support the automatic addresses:
+ * ::1.2.3.4
+ */
+ ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr);
+
+ if (ret == 1)
+ return exact_match;
+
+ while (*str != '\0') {
+ switch (state) {
+ case STATE_START:
+ if (*str == ':') {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ } else {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else {
+ if (*(str + 1) != '\0')
+ colons++;
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums++;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '\0') {
+ if (str - sp > 3)
+ return no_match;
+
+ nums++;
+ state = STATE_COLON;
+ }
+ if (*(str + 1) == '.')
+ state = STATE_DOT;
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 8)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+#if 0
+ if (nums < 11)
+ return partly_match;
+#endif /* 0 */
+
+ return exact_match;
+}
+
+static enum match_type cmd_ipv6_prefix_match(const char *str)
+{
+ int state = STATE_START;
+ int colons = 0, nums = 0, double_colon = 0;
+ int mask;
+ const char *sp = NULL;
+ char *endptr = NULL;
+
+ if (str == NULL)
+ return partly_match;
+
+ if (strspn(str, IPV6_PREFIX_STR) != strlen(str))
+ return no_match;
+
+ while (*str != '\0' && state != STATE_MASK) {
+ switch (state) {
+ case STATE_START:
+ if (*str == ':') {
+ if (*(str + 1) != ':' && *(str + 1) != '\0')
+ return no_match;
+ colons--;
+ state = STATE_COLON;
+ } else {
+ sp = str;
+ state = STATE_ADDR;
+ }
+
+ continue;
+ case STATE_COLON:
+ colons++;
+ if (*(str + 1) == '/')
+ return no_match;
+ else if (*(str + 1) == ':')
+ state = STATE_DOUBLE;
+ else {
+ sp = str + 1;
+ state = STATE_ADDR;
+ }
+ break;
+ case STATE_DOUBLE:
+ if (double_colon)
+ return no_match;
+
+ if (*(str + 1) == ':')
+ return no_match;
+ else {
+ if (*(str + 1) != '\0' && *(str + 1) != '/')
+ colons++;
+ sp = str + 1;
+
+ if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ else
+ state = STATE_ADDR;
+ }
+
+ double_colon++;
+ nums += 1;
+ break;
+ case STATE_ADDR:
+ if (*(str + 1) == ':' || *(str + 1) == '.'
+ || *(str + 1) == '\0' || *(str + 1) == '/') {
+ if (str - sp > 3)
+ return no_match;
+
+ for (; sp <= str; sp++)
+ if (*sp == '/')
+ return no_match;
+
+ nums++;
+
+ if (*(str + 1) == ':')
+ state = STATE_COLON;
+ else if (*(str + 1) == '.')
+ state = STATE_DOT;
+ else if (*(str + 1) == '/')
+ state = STATE_SLASH;
+ }
+ break;
+ case STATE_DOT:
+ state = STATE_ADDR;
+ break;
+ case STATE_SLASH:
+ if (*(str + 1) == '\0')
+ return partly_match;
+
+ state = STATE_MASK;
+ break;
+ default:
+ break;
+ }
+
+ if (nums > 11)
+ return no_match;
+
+ if (colons > 7)
+ return no_match;
+
+ str++;
+ }
+
+ if (state < STATE_MASK)
+ return partly_match;
+
+ mask = strtol(str, &endptr, 10);
+ if (*endptr != '\0')
+ return no_match;
+
+ if (mask < 0 || mask > 128)
+ return no_match;
+
+/* I don't know why mask < 13 makes command match partly.
+ Forgive me to make this comments. I Want to set static default route
+ because of lack of function to originate default in ospf6d; sorry
+ yasu
+ if (mask < 13)
+ return partly_match;
+*/
+
+ return exact_match;
+}
+
+#endif /* HAVE_IPV6 */
+
+#define DECIMAL_STRLEN_MAX 10
+
+static int cmd_range_match(const char *range, const char *str)
+{
+ char *p;
+ char buf[DECIMAL_STRLEN_MAX + 1];
+ char *endptr = NULL;
+ unsigned long min, max, val;
+
+ if (str == NULL)
+ return 1;
+
+ val = strtoul(str, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range++;
+ p = strchr(range, '-');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ min = strtoul(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ range = p + 1;
+ p = strchr(range, '>');
+ if (p == NULL)
+ return 0;
+ if (p - range > DECIMAL_STRLEN_MAX)
+ return 0;
+ strncpy(buf, range, p - range);
+ buf[p - range] = '\0';
+ max = strtoul(buf, &endptr, 10);
+ if (*endptr != '\0')
+ return 0;
+
+ if (val < min || val > max)
+ return 0;
+
+ return 1;
+}
+
+/* Make completion match and return match type flag. */
+static enum match_type
+cmd_filter_by_completion(char *command, vector v, unsigned int index)
+{
+ unsigned int i;
+ const char *str;
+ struct cmd_element *cmd_element;
+ enum match_type match_type;
+ vector descvec;
+ struct desc *desc;
+
+ match_type = no_match;
+
+ /* If command and cmd_element string does not match set NULL to vector */
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ if (index >= vector_active(cmd_element->strvec))
+ vector_slot(v, i) = NULL;
+ else {
+ unsigned int j;
+ int matched = 0;
+
+ descvec =
+ vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ str = desc->cmd;
+
+ if (CMD_VARARG(str)) {
+ if (match_type <
+ vararg_match)
+ match_type =
+ vararg_match;
+ matched++;
+ } else if (CMD_RANGE(str)) {
+ if (cmd_range_match
+ (str, command)) {
+ if (match_type <
+ range_match)
+ match_type
+ =
+ range_match;
+
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str)) {
+ if (cmd_ipv6_match
+ (command)) {
+ if (match_type <
+ ipv6_match)
+ match_type
+ =
+ ipv6_match;
+
+ matched++;
+ }
+ } else if (CMD_IPV6_PREFIX(str)) {
+ if (cmd_ipv6_prefix_match(command)) {
+ if (match_type <
+ ipv6_prefix_match)
+ match_type
+ =
+ ipv6_prefix_match;
+
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str)) {
+ if (cmd_ipv4_match
+ (command)) {
+ if (match_type <
+ ipv4_match)
+ match_type
+ =
+ ipv4_match;
+
+ matched++;
+ }
+ } else if (CMD_IPV4_PREFIX(str)) {
+ if (cmd_ipv4_prefix_match(command)) {
+ if (match_type <
+ ipv4_prefix_match)
+ match_type
+ =
+ ipv4_prefix_match;
+ matched++;
+ }
+ } else
+ /* Check is this point's argument optional ? */
+ if (CMD_OPTION(str)
+ ||
+ CMD_VARIABLE(str)) {
+ if (match_type <
+ extend_match)
+ match_type =
+ extend_match;
+ matched++;
+ } else
+ if (strncmp
+ (command, str,
+ strlen(command)) ==
+ 0) {
+ if (strcmp(command, str)
+ == 0)
+ match_type =
+ exact_match;
+ else {
+ if (match_type <
+ partly_match)
+ match_type
+ =
+ partly_match;
+ }
+ matched++;
+ }
+ }
+ if (!matched)
+ vector_slot(v, i) = NULL;
+ }
+ }
+ return match_type;
+}
+
+/* Filter vector by command character with index. */
+static enum match_type
+cmd_filter_by_string(char *command, vector v, unsigned int index)
+{
+ unsigned int i;
+ const char *str;
+ struct cmd_element *cmd_element;
+ enum match_type match_type;
+ vector descvec;
+ struct desc *desc;
+
+ match_type = no_match;
+
+ /* If command and cmd_element string does not match set NULL to vector */
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ /* If given index is bigger than max string vector of command,
+ set NULL */
+ if (index >= vector_active(cmd_element->strvec))
+ vector_slot(v, i) = NULL;
+ else {
+ unsigned int j;
+ int matched = 0;
+
+ descvec =
+ vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ str = desc->cmd;
+
+ if (CMD_VARARG(str)) {
+ if (match_type <
+ vararg_match)
+ match_type =
+ vararg_match;
+ matched++;
+ } else if (CMD_RANGE(str)) {
+ if (cmd_range_match
+ (str, command)) {
+ if (match_type <
+ range_match)
+ match_type
+ =
+ range_match;
+ matched++;
+ }
+ }
+#ifdef HAVE_IPV6
+ else if (CMD_IPV6(str)) {
+ if (cmd_ipv6_match
+ (command) ==
+ exact_match) {
+ if (match_type <
+ ipv6_match)
+ match_type
+ =
+ ipv6_match;
+ matched++;
+ }
+ } else if (CMD_IPV6_PREFIX(str)) {
+ if (cmd_ipv6_prefix_match(command) == exact_match) {
+ if (match_type <
+ ipv6_prefix_match)
+ match_type
+ =
+ ipv6_prefix_match;
+ matched++;
+ }
+ }
+#endif /* HAVE_IPV6 */
+ else if (CMD_IPV4(str)) {
+ if (cmd_ipv4_match
+ (command) ==
+ exact_match) {
+ if (match_type <
+ ipv4_match)
+ match_type
+ =
+ ipv4_match;
+ matched++;
+ }
+ } else if (CMD_IPV4_PREFIX(str)) {
+ if (cmd_ipv4_prefix_match(command) == exact_match) {
+ if (match_type <
+ ipv4_prefix_match)
+ match_type
+ =
+ ipv4_prefix_match;
+ matched++;
+ }
+ } else if (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+ {
+ if (match_type <
+ extend_match)
+ match_type =
+ extend_match;
+ matched++;
+ } else {
+ if (strcmp(command, str)
+ == 0) {
+ match_type =
+ exact_match;
+ matched++;
+ }
+ }
+ }
+ if (!matched)
+ vector_slot(v, i) = NULL;
+ }
+ }
+ return match_type;
+}
+
+/* Check ambiguous match */
+static int
+is_cmd_ambiguous(char *command, vector v, int index, enum match_type type)
+{
+ unsigned int i;
+ unsigned int j;
+ const char *str = NULL;
+ struct cmd_element *cmd_element;
+ const char *matched = NULL;
+ vector descvec;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((cmd_element = vector_slot(v, i)) != NULL) {
+ int match = 0;
+
+ descvec = vector_slot(cmd_element->strvec, index);
+
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ enum match_type ret;
+
+ str = desc->cmd;
+
+ switch (type) {
+ case exact_match:
+ if (!
+ (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+&& strcmp(command, str) == 0)
+ match++;
+ break;
+ case partly_match:
+ if (!
+ (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+&& strncmp(command, str, strlen(command)) == 0) {
+ if (matched
+ && strcmp(matched,
+ str) != 0)
+ return 1; /* There is ambiguous match. */
+ else
+ matched = str;
+ match++;
+ }
+ break;
+ case range_match:
+ if (cmd_range_match
+ (str, command)) {
+ if (matched
+ && strcmp(matched,
+ str) != 0)
+ return 1;
+ else
+ matched = str;
+ match++;
+ }
+ break;
+#ifdef HAVE_IPV6
+ case ipv6_match:
+ if (CMD_IPV6(str))
+ match++;
+ break;
+ case ipv6_prefix_match:
+ if ((ret =
+ cmd_ipv6_prefix_match
+ (command)) != no_match) {
+ if (ret == partly_match)
+ return 2; /* There is incomplete match. */
+
+ match++;
+ }
+ break;
+#endif /* HAVE_IPV6 */
+ case ipv4_match:
+ if (CMD_IPV4(str))
+ match++;
+ break;
+ case ipv4_prefix_match:
+ if ((ret =
+ cmd_ipv4_prefix_match
+ (command)) != no_match) {
+ if (ret == partly_match)
+ return 2; /* There is incomplete match. */
+
+ match++;
+ }
+ break;
+ case extend_match:
+ if (CMD_OPTION(str)
+ || CMD_VARIABLE(str))
+ match++;
+ break;
+ case no_match:
+ default:
+ break;
+ }
+ }
+ if (!match)
+ vector_slot(v, i) = NULL;
+ }
+ return 0;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+static const char *cmd_entry_function(const char *src, const char *dst)
+{
+ /* Skip variable arguments. */
+ if (CMD_OPTION(dst) || CMD_VARIABLE(dst) || CMD_VARARG(dst) ||
+ CMD_IPV4(dst) || CMD_IPV4_PREFIX(dst) || CMD_RANGE(dst))
+ return NULL;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ /* Matched with input string. */
+ if (strncmp(src, dst, strlen(src)) == 0)
+ return dst;
+
+ return NULL;
+}
+
+/* If src matches dst return dst string, otherwise return NULL */
+/* This version will return the dst string always if it is
+ CMD_VARIABLE for '?' key processing */
+static const char *cmd_entry_function_desc(const char *src, const char *dst)
+{
+ if (CMD_VARARG(dst))
+ return dst;
+
+ if (CMD_RANGE(dst)) {
+ if (cmd_range_match(dst, src))
+ return dst;
+ else
+ return NULL;
+ }
+#ifdef HAVE_IPV6
+ if (CMD_IPV6(dst)) {
+ if (cmd_ipv6_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV6_PREFIX(dst)) {
+ if (cmd_ipv6_prefix_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+#endif /* HAVE_IPV6 */
+
+ if (CMD_IPV4(dst)) {
+ if (cmd_ipv4_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ if (CMD_IPV4_PREFIX(dst)) {
+ if (cmd_ipv4_prefix_match(src))
+ return dst;
+ else
+ return NULL;
+ }
+
+ /* Optional or variable commands always match on '?' */
+ if (CMD_OPTION(dst) || CMD_VARIABLE(dst))
+ return dst;
+
+ /* In case of 'command \t', given src is NULL string. */
+ if (src == NULL)
+ return dst;
+
+ if (strncmp(src, dst, strlen(src)) == 0)
+ return dst;
+ else
+ return NULL;
+}
+
+/* Check same string element existence. If it isn't there return
+ 1. */
+static int cmd_unique_string(vector v, const char *str)
+{
+ unsigned int i;
+ char *match;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((match = vector_slot(v, i)) != NULL)
+ if (strcmp(match, str) == 0)
+ return 0;
+ return 1;
+}
+
+/* Compare string to description vector. If there is same string
+ return 1 else return 0. */
+static int desc_unique_string(vector v, const char *str)
+{
+ unsigned int i;
+ struct desc *desc;
+
+ for (i = 0; i < vector_active(v); i++)
+ if ((desc = vector_slot(v, i)) != NULL)
+ if (strcmp(desc->cmd, str) == 0)
+ return 1;
+ return 0;
+}
+
+static int cmd_try_do_shortcut(enum node_type node, char *first_word)
+{
+ if (first_word != NULL &&
+ node != AUTH_NODE &&
+ node != VIEW_NODE &&
+ node != AUTH_ENABLE_NODE &&
+ node != ENABLE_NODE && 0 == strcmp("do", first_word))
+ return 1;
+ return 0;
+}
+
+/* '?' describe command support. */
+static vector
+cmd_describe_command_real(vector vline, struct vty *vty, int *status)
+{
+ unsigned int i;
+ vector cmd_vector;
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ int ret;
+ enum match_type match;
+ char *command;
+ static struct desc desc_cr = { "<cr>", "" };
+
+ /* Set index. */
+ if (vector_active(vline) == 0) {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ } else
+ index = vector_active(vline) - 1;
+
+ /* Make copy vector of current node's command vector. */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ /* Prepare match vector */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+ /* Filter commands. */
+ /* Only words precedes current word will be checked in this loop. */
+ for (i = 0; i < index; i++)
+ if ((command = vector_slot(vline, i))) {
+ match =
+ cmd_filter_by_completion(command, cmd_vector, i);
+
+ if (match == vararg_match) {
+ struct cmd_element *cmd_element;
+ vector descvec;
+ unsigned int j, k;
+
+ for (j = 0; j < vector_active(cmd_vector); j++)
+ if ((cmd_element =
+ vector_slot(cmd_vector, j)) != NULL
+ &&
+ (vector_active
+ (cmd_element->strvec))) {
+ descvec =
+ vector_slot(cmd_element->
+ strvec,
+ vector_active
+ (cmd_element->
+ strvec) - 1);
+ for (k = 0;
+ k < vector_active(descvec);
+ k++) {
+ struct desc *desc =
+ vector_slot(descvec,
+ k);
+ vector_set(matchvec,
+ desc);
+ }
+ }
+
+ vector_set(matchvec, &desc_cr);
+ vector_free(cmd_vector);
+
+ return matchvec;
+ }
+
+ if ((ret =
+ is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ } else if (ret == 2) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ }
+
+ /* Prepare match vector */
+ /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */
+
+ /* Make sure that cmd_vector is filtered based on current word */
+ command = vector_slot(vline, index);
+ if (command)
+ match = cmd_filter_by_completion(command, cmd_vector, index);
+
+ /* Make description vector. */
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i)) != NULL) {
+ const char *string = NULL;
+ vector strvec = cmd_element->strvec;
+
+ /* if command is NULL, index may be equal to vector_active */
+ if (command && index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ /* Check if command is completed. */
+ if (command == NULL
+ && index == vector_active(strvec)) {
+ string = "<cr>";
+ if (!desc_unique_string
+ (matchvec, string))
+ vector_set(matchvec, &desc_cr);
+ } else {
+ unsigned int j;
+ vector descvec =
+ vector_slot(strvec, index);
+ struct desc *desc;
+
+ for (j = 0; j < vector_active(descvec);
+ j++)
+ if ((desc =
+ vector_slot(descvec, j))) {
+ string =
+ cmd_entry_function_desc
+ (command,
+ desc->cmd);
+ if (string) {
+ /* Uniqueness check */
+ if (!desc_unique_string(matchvec, string))
+ vector_set
+ (matchvec,
+ desc);
+ }
+ }
+ }
+ }
+ }
+ vector_free(cmd_vector);
+
+ if (vector_slot(matchvec, 0) == NULL) {
+ vector_free(matchvec);
+ *status = CMD_ERR_NO_MATCH;
+ } else
+ *status = CMD_SUCCESS;
+
+ return matchvec;
+}
+
+vector cmd_describe_command(vector vline, struct vty * vty, int *status)
+{
+ vector ret;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ enum node_type onode;
+ vector shifted_vline;
+ unsigned int index;
+
+ onode = vty->node;
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_describe_command_real(shifted_vline, vty, status);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ return cmd_describe_command_real(vline, vty, status);
+}
+
+/* Check LCD of matched command. */
+static int cmd_lcd(char **matched)
+{
+ int i;
+ int j;
+ int lcd = -1;
+ char *s1, *s2;
+ char c1, c2;
+
+ if (matched[0] == NULL || matched[1] == NULL)
+ return 0;
+
+ for (i = 1; matched[i] != NULL; i++) {
+ s1 = matched[i - 1];
+ s2 = matched[i];
+
+ for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++)
+ if (c1 != c2)
+ break;
+
+ if (lcd < 0)
+ lcd = j;
+ else {
+ if (lcd > j)
+ lcd = j;
+ }
+ }
+ return lcd;
+}
+
+/* Command line completion support. */
+static char **cmd_complete_command_real(vector vline, struct vty *vty,
+ int *status)
+{
+ unsigned int i;
+ vector cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+#define INIT_MATCHVEC_SIZE 10
+ vector matchvec;
+ struct cmd_element *cmd_element;
+ unsigned int index;
+ char **match_str;
+ struct desc *desc;
+ vector descvec;
+ char *command;
+ int lcd;
+
+ if (vector_active(vline) == 0) {
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ } else
+ index = vector_active(vline) - 1;
+
+ /* First, filter by preceeding command string */
+ for (i = 0; i < index; i++)
+ if ((command = vector_slot(vline, i))) {
+ enum match_type match;
+ int ret;
+
+ /* First try completion match, if there is exactly match return 1 */
+ match =
+ cmd_filter_by_completion(command, cmd_vector, i);
+
+ /* If there is exact match then filter ambiguous match else check
+ ambiguousness. */
+ if ((ret =
+ is_cmd_ambiguous(command, cmd_vector, i,
+ match)) == 1) {
+ vector_free(cmd_vector);
+ *status = CMD_ERR_AMBIGUOUS;
+ return NULL;
+ }
+ /*
+ else if (ret == 2)
+ {
+ vector_free (cmd_vector);
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+ */
+ }
+
+ /* Prepare match vector. */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+
+ /* Now we got into completion */
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i))) {
+ const char *string;
+ vector strvec = cmd_element->strvec;
+
+ /* Check field length */
+ if (index >= vector_active(strvec))
+ vector_slot(cmd_vector, i) = NULL;
+ else {
+ unsigned int j;
+
+ descvec = vector_slot(strvec, index);
+ for (j = 0; j < vector_active(descvec); j++)
+ if ((desc = vector_slot(descvec, j))) {
+ if ((string =
+ cmd_entry_function
+ (vector_slot(vline, index),
+ desc->cmd)))
+ if (cmd_unique_string
+ (matchvec, string))
+ vector_set
+ (matchvec,
+ strdup
+ (string));
+ }
+ }
+ }
+
+ /* We don't need cmd_vector any more. */
+ vector_free(cmd_vector);
+
+ /* No matched command */
+ if (vector_slot(matchvec, 0) == NULL) {
+ vector_free(matchvec);
+
+ /* In case of 'command \t' pattern. Do you need '?' command at
+ the end of the line. */
+ if (vector_slot(vline, index) == '\0')
+ *status = CMD_ERR_NOTHING_TODO;
+ else
+ *status = CMD_ERR_NO_MATCH;
+ return NULL;
+ }
+
+ /* Only one matched */
+ if (vector_slot(matchvec, 1) == NULL) {
+ match_str = (char **)matchvec->index;
+ vector_only_wrapper_free(matchvec);
+ *status = CMD_COMPLETE_FULL_MATCH;
+ return match_str;
+ }
+ /* Make it sure last element is NULL. */
+ vector_set(matchvec, NULL);
+
+ /* Check LCD of matched strings. */
+ if (vector_slot(vline, index) != NULL) {
+ lcd = cmd_lcd((char **)matchvec->index);
+
+ if (lcd) {
+ int len = strlen(vector_slot(vline, index));
+
+ if (len < lcd) {
+ char *lcdstr;
+
+ lcdstr = malloc(lcd + 1);
+ memcpy(lcdstr, matchvec->index[0], lcd);
+ lcdstr[lcd] = '\0';
+
+ /* match_str = (char **) &lcdstr; */
+
+ /* Free matchvec. */
+ for (i = 0; i < vector_active(matchvec); i++) {
+ if (vector_slot(matchvec, i))
+ free(vector_slot(matchvec, i));
+ }
+ vector_free(matchvec);
+
+ /* Make new matchvec. */
+ matchvec = vector_init(INIT_MATCHVEC_SIZE);
+ vector_set(matchvec, lcdstr);
+ match_str = (char **)matchvec->index;
+ vector_only_wrapper_free(matchvec);
+
+ *status = CMD_COMPLETE_MATCH;
+ return match_str;
+ }
+ }
+ }
+
+ match_str = (char **)matchvec->index;
+ vector_only_wrapper_free(matchvec);
+ *status = CMD_COMPLETE_LIST_MATCH;
+ return match_str;
+}
+
+char **cmd_complete_command(vector vline, struct vty *vty, int *status)
+{
+ char **ret;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ enum node_type onode;
+ vector shifted_vline;
+ unsigned int index;
+
+ onode = vty->node;
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_complete_command_real(shifted_vline, vty, status);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ return cmd_complete_command_real(vline, vty, status);
+}
+
+/* return parent node */
+/* MUST eventually converge on CONFIG_NODE */
+enum node_type node_parent(enum node_type node)
+{
+ enum node_type ret;
+
+ assert(node > CONFIG_NODE);
+
+ switch (node) {
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ ret = BGP_NODE;
+ break;
+ case KEYCHAIN_KEY_NODE:
+ ret = KEYCHAIN_NODE;
+ break;
+ default:
+ ret = CONFIG_NODE;
+ }
+
+ return ret;
+}
+
+/* Execute command by argument vline vector. */
+static int
+cmd_execute_command_real(vector vline, struct vty *vty,
+ struct cmd_element **cmd)
+{
+ unsigned int i;
+ unsigned int index;
+ vector cmd_vector;
+ struct cmd_element *cmd_element;
+ struct cmd_element *matched_element;
+ unsigned int matched_count, incomplete_count;
+ int argc;
+ const char *argv[CMD_ARGC_MAX];
+ enum match_type match = 0;
+ int varflag;
+ char *command;
+
+ /* Make copy of command elements. */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ for (index = 0; index < vector_active(vline); index++)
+ if ((command = vector_slot(vline, index))) {
+ int ret;
+
+ match =
+ cmd_filter_by_completion(command, cmd_vector,
+ index);
+
+ if (match == vararg_match)
+ break;
+
+ ret =
+ is_cmd_ambiguous(command, cmd_vector, index, match);
+
+ if (ret == 1) {
+ vector_free(cmd_vector);
+ return CMD_ERR_AMBIGUOUS;
+ } else if (ret == 2) {
+ vector_free(cmd_vector);
+ return CMD_ERR_NO_MATCH;
+ }
+ }
+
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
+ incomplete_count = 0;
+
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if ((cmd_element = vector_slot(cmd_vector, i))) {
+ if (match == vararg_match
+ || index >= cmd_element->cmdsize) {
+ matched_element = cmd_element;
+#if 0
+ printf("DEBUG: %s\n", cmd_element->string);
+#endif
+ matched_count++;
+ } else {
+ incomplete_count++;
+ }
+ }
+
+ /* Finish of using cmd_vector. */
+ vector_free(cmd_vector);
+
+ /* To execute command, matched_count must be 1. */
+ if (matched_count == 0) {
+ if (incomplete_count)
+ return CMD_ERR_INCOMPLETE;
+ else
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (matched_count > 1)
+ return CMD_ERR_AMBIGUOUS;
+
+ /* Argument treatment */
+ varflag = 0;
+ argc = 0;
+
+ for (i = 0; i < vector_active(vline); i++) {
+ if (varflag)
+ argv[argc++] = vector_slot(vline, i);
+ else {
+ vector descvec =
+ vector_slot(matched_element->strvec, i);
+
+ if (vector_active(descvec) == 1) {
+ struct desc *desc = vector_slot(descvec, 0);
+
+ if (CMD_VARARG(desc->cmd))
+ varflag = 1;
+
+ if (varflag || CMD_VARIABLE(desc->cmd)
+ || CMD_OPTION(desc->cmd))
+ argv[argc++] = vector_slot(vline, i);
+ } else
+ argv[argc++] = vector_slot(vline, i);
+ }
+
+ if (argc >= CMD_ARGC_MAX)
+ return CMD_ERR_EXEED_ARGC_MAX;
+ }
+
+ /* For vtysh execution. */
+ if (cmd)
+ *cmd = matched_element;
+
+ if (matched_element->daemon)
+ return CMD_SUCCESS_DAEMON;
+
+ /* Execute matched command. */
+ return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+int
+cmd_execute_command(vector vline, struct vty *vty, struct cmd_element **cmd,
+ int vtysh)
+{
+ int ret, saved_ret, tried = 0;
+ enum node_type onode, try_node;
+
+ onode = try_node = vty->node;
+
+ if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
+ vector shifted_vline;
+ unsigned int index;
+
+ vty->node = ENABLE_NODE;
+ /* We can try it on enable node, cos' the vty is authenticated */
+
+ shifted_vline = vector_init(vector_count(vline));
+ /* use memcpy? */
+ for (index = 1; index < vector_active(vline); index++) {
+ vector_set_index(shifted_vline, index - 1,
+ vector_lookup(vline, index));
+ }
+
+ ret = cmd_execute_command_real(shifted_vline, vty, cmd);
+
+ vector_free(shifted_vline);
+ vty->node = onode;
+ return ret;
+ }
+
+ saved_ret = ret = cmd_execute_command_real(vline, vty, cmd);
+
+ if (vtysh)
+ return saved_ret;
+
+ /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && vty->node > CONFIG_NODE) {
+ try_node = node_parent(try_node);
+ vty->node = try_node;
+ ret = cmd_execute_command_real(vline, vty, cmd);
+ tried = 1;
+ if (ret == CMD_SUCCESS || ret == CMD_WARNING) {
+ /* succesfull command, leave the node as is */
+ return ret;
+ }
+ }
+ /* no command succeeded, reset the vty to the original node and
+ return the error for this node */
+ if (tried)
+ vty->node = onode;
+ return saved_ret;
+}
+
+/* Execute command by argument readline. */
+int
+cmd_execute_command_strict(vector vline, struct vty *vty,
+ struct cmd_element **cmd)
+{
+ unsigned int i;
+ unsigned int index;
+ vector cmd_vector;
+ struct cmd_element *cmd_element;
+ struct cmd_element *matched_element;
+ unsigned int matched_count, incomplete_count;
+ int argc;
+ const char *argv[CMD_ARGC_MAX];
+ int varflag;
+ enum match_type match = 0;
+ char *command;
+
+ /* Make copy of command element */
+ cmd_vector = vector_copy(cmd_node_vector(cmdvec, vty->node));
+
+ for (index = 0; index < vector_active(vline); index++)
+ if ((command = vector_slot(vline, index))) {
+ int ret;
+
+ match = cmd_filter_by_string(vector_slot(vline, index),
+ cmd_vector, index);
+
+ /* If command meets '.VARARG' then finish matching. */
+ if (match == vararg_match)
+ break;
+
+ ret =
+ is_cmd_ambiguous(command, cmd_vector, index, match);
+ if (ret == 1) {
+ vector_free(cmd_vector);
+ return CMD_ERR_AMBIGUOUS;
+ }
+ if (ret == 2) {
+ vector_free(cmd_vector);
+ return CMD_ERR_NO_MATCH;
+ }
+ }
+
+ /* Check matched count. */
+ matched_element = NULL;
+ matched_count = 0;
+ incomplete_count = 0;
+ for (i = 0; i < vector_active(cmd_vector); i++)
+ if (vector_slot(cmd_vector, i) != NULL) {
+ cmd_element = vector_slot(cmd_vector, i);
+
+ if (match == vararg_match
+ || index >= cmd_element->cmdsize) {
+ matched_element = cmd_element;
+ matched_count++;
+ } else
+ incomplete_count++;
+ }
+
+ /* Finish of using cmd_vector. */
+ vector_free(cmd_vector);
+
+ /* To execute command, matched_count must be 1. */
+ if (matched_count == 0) {
+ if (incomplete_count)
+ return CMD_ERR_INCOMPLETE;
+ else
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (matched_count > 1)
+ return CMD_ERR_AMBIGUOUS;
+
+ /* Argument treatment */
+ varflag = 0;
+ argc = 0;
+
+ for (i = 0; i < vector_active(vline); i++) {
+ if (varflag)
+ argv[argc++] = vector_slot(vline, i);
+ else {
+ vector descvec =
+ vector_slot(matched_element->strvec, i);
+
+ if (vector_active(descvec) == 1) {
+ struct desc *desc = vector_slot(descvec, 0);
+
+ if (CMD_VARARG(desc->cmd))
+ varflag = 1;
+
+ if (varflag || CMD_VARIABLE(desc->cmd)
+ || CMD_OPTION(desc->cmd))
+ argv[argc++] = vector_slot(vline, i);
+ } else
+ argv[argc++] = vector_slot(vline, i);
+ }
+
+ if (argc >= CMD_ARGC_MAX)
+ return CMD_ERR_EXEED_ARGC_MAX;
+ }
+
+ /* For vtysh execution. */
+ if (cmd)
+ *cmd = matched_element;
+
+ if (matched_element->daemon)
+ return CMD_SUCCESS_DAEMON;
+
+ /* Now execute matched command */
+ return (*matched_element->func) (matched_element, vty, argc, argv);
+}
+
+#if 0
+/* Configration make from file. */
+int config_from_file(struct vty *vty, FILE * fp)
+{
+ int ret;
+ vector vline;
+
+ while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
+ vline = cmd_make_strvec(vty->buf);
+
+ /* In case of comment line */
+ if (vline == NULL)
+ continue;
+ /* Execute configuration command : this is strict match */
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+
+ /* Try again with setting node to CONFIG_NODE */
+ while (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_NOTHING_TODO
+ && vty->node != CONFIG_NODE) {
+ vty->node = node_parent(vty->node);
+ ret = cmd_execute_command_strict(vline, vty, NULL);
+ }
+
+ cmd_free_strvec(vline);
+
+ if (ret != CMD_SUCCESS && ret != CMD_WARNING
+ && ret != CMD_ERR_NOTHING_TODO)
+ return ret;
+ }
+ return CMD_SUCCESS;
+}
+#endif
+
+/* Configration from terminal */
+DEFUN(config_terminal,
+ config_terminal_cmd,
+ "configure terminal",
+ "Configuration from vty interface\n" "Configuration terminal\n")
+{
+ if (vty_config_lock(vty))
+ vty->node = CONFIG_NODE;
+ else {
+ vty_out(vty, "VTY configuration is locked by other VTY%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+/* Enable command */
+DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n")
+{
+ /* If enable password is NULL, change to ENABLE_NODE */
+ if ((host.enable == NULL && host.enable_encrypt == NULL) ||
+ vty->type == VTY_SHELL_SERV)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = AUTH_ENABLE_NODE;
+
+ return CMD_SUCCESS;
+}
+
+/* Disable command */
+DEFUN(disable,
+ config_disable_cmd, "disable", "Turn off privileged mode command\n")
+{
+ if (vty->node == ENABLE_NODE)
+ vty->node = VIEW_NODE;
+ return CMD_SUCCESS;
+}
+
+/* Down vty node level. */
+DEFUN(config_exit,
+ config_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
+{
+ switch (vty->node) {
+ case BTS_NODE:
+ vty->node = VIEW_NODE;
+ vty->index = NULL;
+ break;
+ case TRX_NODE:
+ vty->node = BTS_NODE;
+ {
+ /* set vty->index correctly ! */
+ struct gsm_bts_trx *trx = vty->index;
+ vty->index = trx->bts;
+ }
+ break;
+ case TS_NODE:
+ vty->node = TRX_NODE;
+ {
+ /* set vty->index correctly ! */
+ struct gsm_bts_trx_ts *ts = vty->index;
+ vty->index = ts->trx;
+ }
+ break;
+ case SUBSCR_NODE:
+ vty->node = VIEW_NODE;
+ subscr_put(vty->index);
+ vty->index = NULL;
+ break;
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ if (0) //vty_shell (vty))
+ exit(0);
+ else
+ vty->status = VTY_CLOSE;
+ break;
+ case CONFIG_NODE:
+ vty->node = ENABLE_NODE;
+ vty_config_unlock(vty);
+ break;
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case BGP_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case MASC_NODE:
+ case RMAP_NODE:
+ case VTY_NODE:
+ vty->node = CONFIG_NODE;
+ break;
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ vty->node = BGP_NODE;
+ break;
+ case KEYCHAIN_KEY_NODE:
+ vty->node = KEYCHAIN_NODE;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* quit is alias of exit. */
+ALIAS(config_exit,
+ config_quit_cmd, "quit", "Exit current mode and down to previous mode\n")
+
+/* End of configuration. */
+ DEFUN(config_end,
+ config_end_cmd, "end", "End current mode and change to enable mode.")
+{
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_NODE:
+ case VTY_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ break;
+ default:
+ break;
+ }
+ return CMD_SUCCESS;
+}
+
+/* Show version. */
+DEFUN(show_version,
+ show_version_cmd, "show version", SHOW_STR "Displays program version\n")
+{
+ vty_out(vty, "%s %s (%s).%s", QUAGGA_PROGNAME, QUAGGA_VERSION,
+ host.name ? host.name : "", VTY_NEWLINE);
+ vty_out(vty, "%s%s", QUAGGA_COPYRIGHT, VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+DEFUN(config_help,
+ config_help_cmd, "help", "Description of the interactive help system\n")
+{
+ vty_out(vty,
+ "This VTY provides advanced help features. When you need help,%s\
+anytime at the command line please press '?'.%s\
+%s\
+If nothing matches, the help list will be empty and you must backup%s\
+ until entering a '?' shows the available options.%s\
+Two styles of help are provided:%s\
+1. Full help is available when you are ready to enter a%s\
+command argument (e.g. 'show ?') and describes each possible%s\
+argument.%s\
+2. Partial help is provided when an abbreviated argument is entered%s\
+ and you want to know what arguments match the input%s\
+ (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
+ VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+/* Help display function for all node. */
+DEFUN(config_list, config_list_cmd, "list", "Print command list\n")
+{
+ unsigned int i;
+ struct cmd_node *cnode = vector_slot(cmdvec, vty->node);
+ struct cmd_element *cmd;
+
+ for (i = 0; i < vector_active(cnode->cmd_vector); i++)
+ if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL
+ && !(cmd->attr == CMD_ATTR_DEPRECATED
+ || cmd->attr == CMD_ATTR_HIDDEN))
+ vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+#if 0
+/* Write current configuration into file. */
+DEFUN(config_write_file,
+ config_write_file_cmd,
+ "write file",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write to configuration file\n")
+{
+ unsigned int i;
+ int fd;
+ struct cmd_node *node;
+ char *config_file;
+ char *config_file_tmp = NULL;
+ char *config_file_sav = NULL;
+ struct vty *file_vty;
+
+ /* Check and see if we are operating under vtysh configuration */
+ if (host.config == NULL) {
+ vty_out(vty, "Can't save to configuration file, using vtysh.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Get filename. */
+ config_file = host.config;
+
+ config_file_sav =
+ malloc(strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1);
+ strcpy(config_file_sav, config_file);
+ strcat(config_file_sav, CONF_BACKUP_EXT);
+
+ config_file_tmp = malloc(strlen(config_file) + 8);
+ sprintf(config_file_tmp, "%s.XXXXXX", config_file);
+
+ /* Open file to configuration write. */
+ fd = mkstemp(config_file_tmp);
+ if (fd < 0) {
+ vty_out(vty, "Can't open configuration file %s.%s",
+ config_file_tmp, VTY_NEWLINE);
+ free(config_file_tmp);
+ free(config_file_sav);
+ return CMD_WARNING;
+ }
+
+ /* Make vty for configuration file. */
+ file_vty = vty_new();
+ file_vty->fd = fd;
+ file_vty->type = VTY_FILE;
+
+ /* Config file header print. */
+ vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
+ //vty_time_print (file_vty, 1);
+ vty_out(file_vty, "!\n");
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func) {
+ if ((*node->func) (file_vty))
+ vty_out(file_vty, "!\n");
+ }
+ vty_close(file_vty);
+
+ if (unlink(config_file_sav) != 0)
+ if (errno != ENOENT) {
+ vty_out(vty,
+ "Can't unlink backup configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
+ free(config_file_sav);
+ free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ if (link(config_file, config_file_sav) != 0) {
+ vty_out(vty, "Can't backup old configuration file %s.%s",
+ config_file_sav, VTY_NEWLINE);
+ free(config_file_sav);
+ free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ sync();
+ if (unlink(config_file) != 0) {
+ vty_out(vty, "Can't unlink configuration file %s.%s",
+ config_file, VTY_NEWLINE);
+ free(config_file_sav);
+ free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ if (link(config_file_tmp, config_file) != 0) {
+ vty_out(vty, "Can't save configuration file %s.%s", config_file,
+ VTY_NEWLINE);
+ free(config_file_sav);
+ free(config_file_tmp);
+ unlink(config_file_tmp);
+ return CMD_WARNING;
+ }
+ unlink(config_file_tmp);
+ sync();
+
+ free(config_file_sav);
+ free(config_file_tmp);
+
+ if (chmod(config_file, CONFIGFILE_MASK) != 0) {
+ vty_out(vty, "Can't chmod configuration file %s: %s (%d).%s",
+ config_file, safe_strerror(errno), errno, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "Configuration saved to %s%s", config_file, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+ALIAS(config_write_file,
+ config_write_cmd,
+ "write", "Write running configuration to memory, network, or terminal\n")
+
+ ALIAS(config_write_file,
+ config_write_memory_cmd,
+ "write memory",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write configuration to the file (same as write file)\n")
+
+ ALIAS(config_write_file,
+ copy_runningconfig_startupconfig_cmd,
+ "copy running-config startup-config",
+ "Copy configuration\n"
+ "Copy running config to... \n"
+ "Copy running config to startup config (same as write file)\n")
+
+/* Write current configuration into the terminal. */
+ DEFUN(config_write_terminal,
+ config_write_terminal_cmd,
+ "write terminal",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write to terminal\n")
+{
+ unsigned int i;
+ struct cmd_node *node;
+
+ if (vty->type == VTY_SHELL_SERV) {
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func
+ && node->vtysh) {
+ if ((*node->func) (vty))
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ }
+ } else {
+ vty_out(vty, "%sCurrent configuration:%s", VTY_NEWLINE,
+ VTY_NEWLINE);
+ vty_out(vty, "!%s", VTY_NEWLINE);
+
+ for (i = 0; i < vector_active(cmdvec); i++)
+ if ((node = vector_slot(cmdvec, i)) && node->func) {
+ if ((*node->func) (vty))
+ vty_out(vty, "!%s", VTY_NEWLINE);
+ }
+ vty_out(vty, "end%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+/* Write current configuration into the terminal. */
+ALIAS(config_write_terminal,
+ show_running_config_cmd,
+ "show running-config", SHOW_STR "running configuration\n")
+
+/* Write startup configuration into the terminal. */
+ DEFUN(show_startup_config,
+ show_startup_config_cmd,
+ "show startup-config", SHOW_STR "Contentes of startup configuration\n")
+{
+ char buf[BUFSIZ];
+ FILE *confp;
+
+ confp = fopen(host.config, "r");
+ if (confp == NULL) {
+ vty_out(vty, "Can't open configuration file [%s]%s",
+ host.config, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ while (fgets(buf, BUFSIZ, confp)) {
+ char *cp = buf;
+
+ while (*cp != '\r' && *cp != '\n' && *cp != '\0')
+ cp++;
+ *cp = '\0';
+
+ vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+ }
+
+ fclose(confp);
+
+ return CMD_SUCCESS;
+}
+#endif
+
+/* Hostname configuration */
+DEFUN(config_hostname,
+ hostname_cmd,
+ "hostname WORD",
+ "Set system's network name\n" "This system's network name\n")
+{
+ if (!isalpha((int)*argv[0])) {
+ vty_out(vty, "Please specify string starting with alphabet%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (host.name)
+ free(host.name);
+
+ host.name = strdup(argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_no_hostname,
+ no_hostname_cmd,
+ "no hostname [HOSTNAME]",
+ NO_STR "Reset system's network name\n" "Host name of this router\n")
+{
+ if (host.name)
+ free(host.name);
+ host.name = NULL;
+ return CMD_SUCCESS;
+}
+
+/* VTY interface password set. */
+DEFUN(config_password, password_cmd,
+ "password (8|) WORD",
+ "Assign the terminal connection password\n"
+ "Specifies a HIDDEN password will follow\n"
+ "dummy string \n" "The HIDDEN line password string\n")
+{
+ /* Argument check. */
+ if (argc == 0) {
+ vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (argc == 2) {
+ if (*argv[0] == '8') {
+ if (host.password)
+ free(host.password);
+ host.password = NULL;
+ if (host.password_encrypt)
+ free(host.password_encrypt);
+ host.password_encrypt = strdup(strdup(argv[1]));
+ return CMD_SUCCESS;
+ } else {
+ vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (!isalnum((int)*argv[0])) {
+ vty_out(vty,
+ "Please specify string starting with alphanumeric%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (host.password)
+ free(host.password);
+ host.password = NULL;
+
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt) {
+ if (host.password_encrypt)
+ free(host.password_encrypt);
+ host.password_encrypt = strdup(zencrypt(argv[0]));
+ } else
+#endif
+ host.password = strdup(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(config_password, password_text_cmd,
+ "password LINE",
+ "Assign the terminal connection password\n"
+ "The UNENCRYPTED (cleartext) line password\n")
+
+/* VTY enable password set. */
+ DEFUN(config_enable_password, enable_password_cmd,
+ "enable password (8|) WORD",
+ "Modify enable password parameters\n"
+ "Assign the privileged level password\n"
+ "Specifies a HIDDEN password will follow\n"
+ "dummy string \n" "The HIDDEN 'enable' password string\n")
+{
+ /* Argument check. */
+ if (argc == 0) {
+ vty_out(vty, "Please specify password.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Crypt type is specified. */
+ if (argc == 2) {
+ if (*argv[0] == '8') {
+ if (host.enable)
+ free(host.enable);
+ host.enable = NULL;
+
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = strdup(argv[1]);
+
+ return CMD_SUCCESS;
+ } else {
+ vty_out(vty, "Unknown encryption type.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ if (!isalnum((int)*argv[0])) {
+ vty_out(vty,
+ "Please specify string starting with alphanumeric%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (host.enable)
+ free(host.enable);
+ host.enable = NULL;
+
+ /* Plain password input. */
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt) {
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = strdup(zencrypt(argv[0]));
+ } else
+#endif
+ host.enable = strdup(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(config_enable_password,
+ enable_password_text_cmd,
+ "enable password LINE",
+ "Modify enable password parameters\n"
+ "Assign the privileged level password\n"
+ "The UNENCRYPTED (cleartext) 'enable' password\n")
+
+/* VTY enable password delete. */
+ DEFUN(no_config_enable_password, no_enable_password_cmd,
+ "no enable password",
+ NO_STR
+ "Modify enable password parameters\n"
+ "Assign the privileged level password\n")
+{
+ if (host.enable)
+ free(host.enable);
+ host.enable = NULL;
+
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = NULL;
+
+ return CMD_SUCCESS;
+}
+
+#ifdef VTY_CRYPT_PW
+DEFUN(service_password_encrypt,
+ service_password_encrypt_cmd,
+ "service password-encryption",
+ "Set up miscellaneous service\n" "Enable encrypted passwords\n")
+{
+ if (host.encrypt)
+ return CMD_SUCCESS;
+
+ host.encrypt = 1;
+
+ if (host.password) {
+ if (host.password_encrypt)
+ free(host.password_encrypt);
+ host.password_encrypt = strdup(zencrypt(host.password));
+ }
+ if (host.enable) {
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = strdup(zencrypt(host.enable));
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_service_password_encrypt,
+ no_service_password_encrypt_cmd,
+ "no service password-encryption",
+ NO_STR "Set up miscellaneous service\n" "Enable encrypted passwords\n")
+{
+ if (!host.encrypt)
+ return CMD_SUCCESS;
+
+ host.encrypt = 0;
+
+ if (host.password_encrypt)
+ free(host.password_encrypt);
+ host.password_encrypt = NULL;
+
+ if (host.enable_encrypt)
+ free(host.enable_encrypt);
+ host.enable_encrypt = NULL;
+
+ return CMD_SUCCESS;
+}
+#endif
+
+DEFUN(config_terminal_length, config_terminal_length_cmd,
+ "terminal length <0-512>",
+ "Set terminal line parameters\n"
+ "Set number of lines on a screen\n"
+ "Number of lines on screen (0 for no pausing)\n")
+{
+ int lines;
+ char *endptr = NULL;
+
+ lines = strtol(argv[0], &endptr, 10);
+ if (lines < 0 || lines > 512 || *endptr != '\0') {
+ vty_out(vty, "length is malformed%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ vty->lines = lines;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_terminal_no_length, config_terminal_no_length_cmd,
+ "terminal no length",
+ "Set terminal line parameters\n"
+ NO_STR "Set number of lines on a screen\n")
+{
+ vty->lines = -1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(service_terminal_length, service_terminal_length_cmd,
+ "service terminal-length <0-512>",
+ "Set up miscellaneous service\n"
+ "System wide terminal length configuration\n"
+ "Number of lines of VTY (0 means no line control)\n")
+{
+ int lines;
+ char *endptr = NULL;
+
+ lines = strtol(argv[0], &endptr, 10);
+ if (lines < 0 || lines > 512 || *endptr != '\0') {
+ vty_out(vty, "length is malformed%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ host.lines = lines;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_service_terminal_length, no_service_terminal_length_cmd,
+ "no service terminal-length [<0-512>]",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "System wide terminal length configuration\n"
+ "Number of lines of VTY (0 means no line control)\n")
+{
+ host.lines = -1;
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(do_echo,
+ echo_cmd,
+ "echo .MESSAGE",
+ "Echo a message back to the vty\n" "The message to echo\n")
+{
+ char *message;
+
+ vty_out(vty, "%s%s",
+ ((message =
+ argv_concat(argv, argc, 0)) ? message : ""), VTY_NEWLINE);
+ if (message)
+ free(message);
+ return CMD_SUCCESS;
+}
+
+#if 0
+DEFUN(config_logmsg,
+ config_logmsg_cmd,
+ "logmsg " LOG_LEVELS " .MESSAGE",
+ "Send a message to enabled logging destinations\n"
+ LOG_LEVEL_DESC "The message to send\n")
+{
+ int level;
+ char *message;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+
+ zlog(NULL, level,
+ ((message = argv_concat(argv, argc, 1)) ? message : ""));
+ if (message)
+ free(message);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_logging,
+ show_logging_cmd,
+ "show logging", SHOW_STR "Show current logging configuration\n")
+{
+ struct zlog *zl = zlog_default;
+
+ vty_out(vty, "Syslog logging: ");
+ if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s, facility %s, ident %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
+ facility_name(zl->facility), zl->ident);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Stdout logging: ");
+ if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Monitor logging: ");
+ if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "File logging: ");
+ if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
+ vty_out(vty, "disabled");
+ else
+ vty_out(vty, "level %s, filename %s",
+ zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
+ zl->filename);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_out(vty, "Protocol name: %s%s",
+ zlog_proto_names[zl->protocol], VTY_NEWLINE);
+ vty_out(vty, "Record priority: %s%s",
+ (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_stdout,
+ config_log_stdout_cmd,
+ "log stdout", "Logging control\n" "Set stdout logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_stdout_level,
+ config_log_stdout_level_cmd,
+ "log stdout " LOG_LEVELS,
+ "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, level);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_stdout,
+ no_config_log_stdout_cmd,
+ "no log stdout [LEVEL]",
+ NO_STR "Logging control\n" "Cancel logging to stdout\n" "Logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_monitor,
+ config_log_monitor_cmd,
+ "log monitor",
+ "Logging control\n" "Set terminal line (monitor) logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_monitor_level,
+ config_log_monitor_level_cmd,
+ "log monitor " LOG_LEVELS,
+ "Logging control\n"
+ "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ zlog_set_level(NULL, ZLOG_DEST_MONITOR, level);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_monitor,
+ no_config_log_monitor_cmd,
+ "no log monitor [LEVEL]",
+ NO_STR
+ "Logging control\n"
+ "Disable terminal line (monitor) logging\n" "Logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
+ return CMD_SUCCESS;
+}
+
+static int set_log_file(struct vty *vty, const char *fname, int loglevel)
+{
+ int ret;
+ char *p = NULL;
+ const char *fullpath;
+
+ /* Path detection. */
+ if (!IS_DIRECTORY_SEP(*fname)) {
+ char cwd[MAXPATHLEN + 1];
+ cwd[MAXPATHLEN] = '\0';
+
+ if (getcwd(cwd, MAXPATHLEN) == NULL) {
+ zlog_err("config_log_file: Unable to alloc mem!");
+ return CMD_WARNING;
+ }
+
+ if ((p = malloc(strlen(cwd) + strlen(fname) + 2))
+ == NULL) {
+ zlog_err("config_log_file: Unable to alloc mem!");
+ return CMD_WARNING;
+ }
+ sprintf(p, "%s/%s", cwd, fname);
+ fullpath = p;
+ } else
+ fullpath = fname;
+
+ ret = zlog_set_file(NULL, fullpath, loglevel);
+
+ if (p)
+ free(p);
+
+ if (!ret) {
+ vty_out(vty, "can't open logfile %s\n", fname);
+ return CMD_WARNING;
+ }
+
+ if (host.logfile)
+ free(host.logfile);
+
+ host.logfile = strdup(fname);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_file,
+ config_log_file_cmd,
+ "log file FILENAME",
+ "Logging control\n" "Logging to file\n" "Logging filename\n")
+{
+ return set_log_file(vty, argv[0], zlog_default->default_lvl);
+}
+
+DEFUN(config_log_file_level,
+ config_log_file_level_cmd,
+ "log file FILENAME " LOG_LEVELS,
+ "Logging control\n"
+ "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[1])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ return set_log_file(vty, argv[0], level);
+}
+
+DEFUN(no_config_log_file,
+ no_config_log_file_cmd,
+ "no log file [FILENAME]",
+ NO_STR
+ "Logging control\n" "Cancel logging to file\n" "Logging file name\n")
+{
+ zlog_reset_file(NULL);
+
+ if (host.logfile)
+ free(host.logfile);
+
+ host.logfile = NULL;
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_config_log_file,
+ no_config_log_file_level_cmd,
+ "no log file FILENAME LEVEL",
+ NO_STR
+ "Logging control\n"
+ "Cancel logging to file\n" "Logging file name\n" "Logging level\n")
+
+ DEFUN(config_log_syslog,
+ config_log_syslog_cmd,
+ "log syslog", "Logging control\n" "Set syslog logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_syslog_level,
+ config_log_syslog_level_cmd,
+ "log syslog " LOG_LEVELS,
+ "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC)
+{
+ int level;
+
+ if ((level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, level);
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(config_log_syslog_facility,
+ config_log_syslog_facility_cmd,
+ "log syslog facility " LOG_FACILITIES,
+ "Logging control\n"
+ "Logging goes to syslog\n"
+ "(Deprecated) Facility parameter for syslog messages\n"
+ LOG_FACILITY_DESC)
+{
+ int facility;
+
+ if ((facility = facility_match(argv[0])) < 0)
+ return CMD_ERR_NO_MATCH;
+
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
+ zlog_default->facility = facility;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_syslog,
+ no_config_log_syslog_cmd,
+ "no log syslog [LEVEL]",
+ NO_STR "Logging control\n" "Cancel logging to syslog\n" "Logging level\n")
+{
+ zlog_set_level(NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_config_log_syslog,
+ no_config_log_syslog_facility_cmd,
+ "no log syslog facility " LOG_FACILITIES,
+ NO_STR
+ "Logging control\n"
+ "Logging goes to syslog\n"
+ "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
+
+ DEFUN(config_log_facility,
+ config_log_facility_cmd,
+ "log facility " LOG_FACILITIES,
+ "Logging control\n"
+ "Facility parameter for syslog messages\n" LOG_FACILITY_DESC)
+{
+ int facility;
+
+ if ((facility = facility_match(argv[0])) < 0)
+ return CMD_ERR_NO_MATCH;
+ zlog_default->facility = facility;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_facility,
+ no_config_log_facility_cmd,
+ "no log facility [FACILITY]",
+ NO_STR
+ "Logging control\n"
+ "Reset syslog facility to default (daemon)\n" "Syslog facility\n")
+{
+ zlog_default->facility = LOG_DAEMON;
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(config_log_trap,
+ config_log_trap_cmd,
+ "log trap " LOG_LEVELS,
+ "Logging control\n"
+ "(Deprecated) Set logging level and default for all destinations\n"
+ LOG_LEVEL_DESC)
+{
+ int new_level;
+ int i;
+
+ if ((new_level = level_match(argv[0])) == ZLOG_DISABLED)
+ return CMD_ERR_NO_MATCH;
+
+ zlog_default->default_lvl = new_level;
+ for (i = 0; i < ZLOG_NUM_DESTS; i++)
+ if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
+ zlog_default->maxlvl[i] = new_level;
+ return CMD_SUCCESS;
+}
+
+DEFUN_DEPRECATED(no_config_log_trap,
+ no_config_log_trap_cmd,
+ "no log trap [LEVEL]",
+ NO_STR
+ "Logging control\n"
+ "Permit all logging information\n" "Logging level\n")
+{
+ zlog_default->default_lvl = LOG_DEBUG;
+ return CMD_SUCCESS;
+}
+
+DEFUN(config_log_record_priority,
+ config_log_record_priority_cmd,
+ "log record-priority",
+ "Logging control\n"
+ "Log the priority of the message within the message\n")
+{
+ zlog_default->record_priority = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_config_log_record_priority,
+ no_config_log_record_priority_cmd,
+ "no log record-priority",
+ NO_STR
+ "Logging control\n"
+ "Do not log the priority of the message within the message\n")
+{
+ zlog_default->record_priority = 0;
+ return CMD_SUCCESS;
+}
+#endif
+
+DEFUN(banner_motd_file,
+ banner_motd_file_cmd,
+ "banner motd file [FILE]",
+ "Set banner\n" "Banner for motd\n" "Banner from a file\n" "Filename\n")
+{
+ if (host.motdfile)
+ free(host.motdfile);
+ host.motdfile = strdup(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(banner_motd_default,
+ banner_motd_default_cmd,
+ "banner motd default",
+ "Set banner string\n" "Strings for motd\n" "Default string\n")
+{
+ host.motd = default_motd;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_banner_motd,
+ no_banner_motd_cmd,
+ "no banner motd", NO_STR "Set banner string\n" "Strings for motd\n")
+{
+ host.motd = NULL;
+ if (host.motdfile)
+ free(host.motdfile);
+ host.motdfile = NULL;
+ return CMD_SUCCESS;
+}
+
+/* Set config filename. Called from vty.c */
+void host_config_set(char *filename)
+{
+ host.config = strdup(filename);
+}
+
+void install_default(enum node_type node)
+{
+ install_element(node, &config_exit_cmd);
+ install_element(node, &config_quit_cmd);
+ install_element(node, &config_end_cmd);
+ install_element(node, &config_help_cmd);
+ install_element(node, &config_list_cmd);
+
+#if 0
+ install_element(node, &config_write_terminal_cmd);
+ install_element(node, &config_write_file_cmd);
+ install_element(node, &config_write_memory_cmd);
+ install_element(node, &config_write_cmd);
+ install_element(node, &show_running_config_cmd);
+#endif
+}
+
+/* Initialize command interface. Install basic nodes and commands. */
+void cmd_init(int terminal)
+{
+ /* Allocate initial top vector of commands. */
+ cmdvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Default host value settings. */
+ host.name = NULL;
+ //host.password = NULL;
+ host.password = "foo";
+ host.enable = NULL;
+ host.logfile = NULL;
+ host.config = NULL;
+ host.lines = -1;
+ host.motd = default_motd;
+ host.motdfile = NULL;
+
+ /* Install top nodes. */
+ install_node(&view_node, NULL);
+ install_node(&enable_node, NULL);
+ install_node(&auth_node, NULL);
+ install_node(&auth_enable_node, NULL);
+ install_node(&config_node, config_write_host);
+
+ /* Each node's basic commands. */
+ install_element(VIEW_NODE, &show_version_cmd);
+ if (terminal) {
+ install_element(VIEW_NODE, &config_list_cmd);
+ install_element(VIEW_NODE, &config_exit_cmd);
+ install_element(VIEW_NODE, &config_quit_cmd);
+ install_element(VIEW_NODE, &config_help_cmd);
+ install_element(VIEW_NODE, &config_enable_cmd);
+ install_element(VIEW_NODE, &config_terminal_length_cmd);
+ install_element(VIEW_NODE, &config_terminal_no_length_cmd);
+ install_element(VIEW_NODE, &echo_cmd);
+ }
+
+ if (terminal) {
+ install_default(ENABLE_NODE);
+ install_element(ENABLE_NODE, &config_disable_cmd);
+ install_element(ENABLE_NODE, &config_terminal_cmd);
+ //install_element (ENABLE_NODE, &copy_runningconfig_startupconfig_cmd);
+ }
+ //install_element (ENABLE_NODE, &show_startup_config_cmd);
+ install_element(ENABLE_NODE, &show_version_cmd);
+
+ if (terminal) {
+ install_element(ENABLE_NODE, &config_terminal_length_cmd);
+ install_element(ENABLE_NODE, &config_terminal_no_length_cmd);
+ install_element(ENABLE_NODE, &echo_cmd);
+
+ install_default(CONFIG_NODE);
+ }
+
+ install_element(CONFIG_NODE, &hostname_cmd);
+ install_element(CONFIG_NODE, &no_hostname_cmd);
+
+ if (terminal) {
+ install_element(CONFIG_NODE, &password_cmd);
+ install_element(CONFIG_NODE, &password_text_cmd);
+ install_element(CONFIG_NODE, &enable_password_cmd);
+ install_element(CONFIG_NODE, &enable_password_text_cmd);
+ install_element(CONFIG_NODE, &no_enable_password_cmd);
+
+#ifdef VTY_CRYPT_PW
+ install_element(CONFIG_NODE, &service_password_encrypt_cmd);
+ install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
+#endif
+ install_element(CONFIG_NODE, &banner_motd_default_cmd);
+ install_element(CONFIG_NODE, &banner_motd_file_cmd);
+ install_element(CONFIG_NODE, &no_banner_motd_cmd);
+ install_element(CONFIG_NODE, &service_terminal_length_cmd);
+ install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
+
+ }
+ srand(time(NULL));
+}
diff --git a/openbsc/src/vty/vector.c b/openbsc/src/vty/vector.c
new file mode 100644
index 000000000..76870105b
--- /dev/null
+++ b/openbsc/src/vty/vector.c
@@ -0,0 +1,186 @@
+/* Generic vector interface routine
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <vty/vector.h>
+#include <memory.h>
+
+/* Initialize vector : allocate memory and return vector. */
+vector vector_init(unsigned int size)
+{
+ vector v = calloc(1, sizeof(struct _vector));
+ if (!v)
+ return NULL;
+
+ /* allocate at least one slot */
+ if (size == 0)
+ size = 1;
+
+ v->alloced = size;
+ v->active = 0;
+ v->index = calloc(1, sizeof(void *) * size);
+ if (!v->index) {
+ free(v);
+ return NULL;
+ }
+ return v;
+}
+
+void vector_only_wrapper_free(vector v)
+{
+ free(v);
+}
+
+void vector_only_index_free(void *index)
+{
+ free(index);
+}
+
+void vector_free(vector v)
+{
+ free(v->index);
+ free(v);
+}
+
+vector vector_copy(vector v)
+{
+ unsigned int size;
+ vector new = calloc(1, sizeof(struct _vector));
+ if (!new)
+ return NULL;
+
+ new->active = v->active;
+ new->alloced = v->alloced;
+
+ size = sizeof(void *) * (v->alloced);
+ new->index = calloc(1, size);
+ if (!new->index) {
+ free(new);
+ return NULL;
+ }
+ memcpy(new->index, v->index, size);
+
+ return new;
+}
+
+/* Check assigned index, and if it runs short double index pointer */
+void vector_ensure(vector v, unsigned int num)
+{
+ if (v->alloced > num)
+ return;
+
+ v->index = realloc(v->index, sizeof(void *) * (v->alloced * 2));
+ memset(&v->index[v->alloced], 0, sizeof(void *) * v->alloced);
+ v->alloced *= 2;
+
+ if (v->alloced <= num)
+ vector_ensure(v, num);
+}
+
+/* This function only returns next empty slot index. It dose not mean
+ the slot's index memory is assigned, please call vector_ensure()
+ after calling this function. */
+int vector_empty_slot(vector v)
+{
+ unsigned int i;
+
+ if (v->active == 0)
+ return 0;
+
+ for (i = 0; i < v->active; i++)
+ if (v->index[i] == 0)
+ return i;
+
+ return i;
+}
+
+/* Set value to the smallest empty slot. */
+int vector_set(vector v, void *val)
+{
+ unsigned int i;
+
+ i = vector_empty_slot(v);
+ vector_ensure(v, i);
+
+ v->index[i] = val;
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return i;
+}
+
+/* Set value to specified index slot. */
+int vector_set_index(vector v, unsigned int i, void *val)
+{
+ vector_ensure(v, i);
+
+ v->index[i] = val;
+
+ if (v->active <= i)
+ v->active = i + 1;
+
+ return i;
+}
+
+/* Look up vector. */
+void *vector_lookup(vector v, unsigned int i)
+{
+ if (i >= v->active)
+ return NULL;
+ return v->index[i];
+}
+
+/* Lookup vector, ensure it. */
+void *vector_lookup_ensure(vector v, unsigned int i)
+{
+ vector_ensure(v, i);
+ return v->index[i];
+}
+
+/* Unset value at specified index slot. */
+void vector_unset(vector v, unsigned int i)
+{
+ if (i >= v->alloced)
+ return;
+
+ v->index[i] = NULL;
+
+ if (i + 1 == v->active) {
+ v->active--;
+ while (i && v->index[--i] == NULL && v->active--) ; /* Is this ugly ? */
+ }
+}
+
+/* Count the number of not emplty slot. */
+unsigned int vector_count(vector v)
+{
+ unsigned int i;
+ unsigned count = 0;
+
+ for (i = 0; i < v->active; i++)
+ if (v->index[i] != NULL)
+ count++;
+
+ return count;
+}
diff --git a/openbsc/src/vty/vty.c b/openbsc/src/vty/vty.c
new file mode 100644
index 000000000..ca6fff73c
--- /dev/null
+++ b/openbsc/src/vty/vty.c
@@ -0,0 +1,1634 @@
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <termios.h>
+
+#include <sys/utsname.h>
+#include <sys/param.h>
+
+#include <arpa/telnet.h>
+
+#include "cardshell.h"
+#include <vty/vty.h>
+#include <vty/command.h>
+#include <vty/buffer.h>
+
+extern struct host host;
+
+/* Vector which store each vty structure. */
+static vector vtyvec;
+
+vector Vvty_serv_thread;
+
+char *vty_cwd = NULL;
+
+/* Configure lock. */
+static int vty_config;
+
+static int no_password_check = 1;
+
+static void vty_clear_buf(struct vty *vty)
+{
+ memset(vty->buf, 0, vty->max);
+}
+
+/* Allocate new vty struct. */
+struct vty *vty_new()
+{
+ struct vty *new = malloc(sizeof(struct vty));
+
+ if (!new)
+ goto out;
+
+ new->obuf = buffer_new(0); /* Use default buffer size. */
+ if (!new->obuf)
+ goto out_new;
+ new->buf = calloc(1, VTY_BUFSIZ);
+ if (!new->buf)
+ goto out_obuf;
+
+ new->max = VTY_BUFSIZ;
+
+ return new;
+
+out_obuf:
+ free(new->obuf);
+out_new:
+ free(new);
+ new = NULL;
+out:
+ return new;
+}
+
+/* Authentication of vty */
+static void vty_auth(struct vty *vty, char *buf)
+{
+ char *passwd = NULL;
+ enum node_type next_node = 0;
+ int fail;
+ char *crypt(const char *, const char *);
+
+ switch (vty->node) {
+ case AUTH_NODE:
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt)
+ passwd = host.password_encrypt;
+ else
+#endif
+ passwd = host.password;
+ if (host.advanced)
+ next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+ else
+ next_node = VIEW_NODE;
+ break;
+ case AUTH_ENABLE_NODE:
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt)
+ passwd = host.enable_encrypt;
+ else
+#endif
+ passwd = host.enable;
+ next_node = ENABLE_NODE;
+ break;
+ }
+
+ if (passwd) {
+#ifdef VTY_CRYPT_PW
+ if (host.encrypt)
+ fail = strcmp(crypt(buf, passwd), passwd);
+ else
+#endif
+ fail = strcmp(buf, passwd);
+ } else
+ fail = 1;
+
+ if (!fail) {
+ vty->fail = 0;
+ vty->node = next_node; /* Success ! */
+ } else {
+ vty->fail++;
+ if (vty->fail >= 3) {
+ if (vty->node == AUTH_NODE) {
+ vty_out(vty,
+ "%% Bad passwords, too many failures!%s",
+ VTY_NEWLINE);
+ vty->status = VTY_CLOSE;
+ } else {
+ /* AUTH_ENABLE_NODE */
+ vty->fail = 0;
+ vty_out(vty,
+ "%% Bad enable passwords, too many failures!%s",
+ VTY_NEWLINE);
+ vty->node = VIEW_NODE;
+ }
+ }
+ }
+}
+
+/* Close vty interface. */
+void vty_close(struct vty *vty)
+{
+ int i;
+
+ /* Flush buffer. */
+ buffer_flush_all(vty->obuf, vty->fd);
+
+ /* Free input buffer. */
+ buffer_free(vty->obuf);
+
+ /* Free command history. */
+ for (i = 0; i < VTY_MAXHIST; i++)
+ if (vty->hist[i])
+ free(vty->hist[i]);
+
+ /* Unset vector. */
+ vector_unset(vtyvec, vty->fd);
+
+ /* Close socket. */
+ if (vty->fd > 0)
+ close(vty->fd);
+
+ if (vty->buf)
+ free(vty->buf);
+
+ /* Check configure. */
+ vty_config_unlock(vty);
+
+ /* OK free vty. */
+ free(vty);
+}
+
+int vty_shell(struct vty *vty)
+{
+ return vty->type == VTY_SHELL ? 1 : 0;
+}
+
+
+/* VTY standard output function. */
+int vty_out(struct vty *vty, const char *format, ...)
+{
+ va_list args;
+ int len = 0;
+ int size = 1024;
+ char buf[1024];
+ char *p = NULL;
+
+ if (vty_shell(vty)) {
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ } else {
+ /* Try to write to initial buffer. */
+ va_start(args, format);
+ len = vsnprintf(buf, sizeof buf, format, args);
+ va_end(args);
+
+ /* Initial buffer is not enough. */
+ if (len < 0 || len >= size) {
+ while (1) {
+ if (len > -1)
+ size = len + 1;
+ else
+ size = size * 2;
+
+ p = realloc(p, size);
+ if (!p)
+ return -1;
+
+ va_start(args, format);
+ len = vsnprintf(p, size, format, args);
+ va_end(args);
+
+ if (len > -1 && len < size)
+ break;
+ }
+ }
+
+ /* When initial buffer is enough to store all output. */
+ if (!p)
+ p = buf;
+
+ /* Pointer p must point out buffer. */
+ buffer_put(vty->obuf, (u_char *) p, len);
+
+ /* If p is not different with buf, it is allocated buffer. */
+ if (p != buf)
+ free(p);
+ }
+
+ return len;
+}
+
+int vty_out_newline(struct vty *vty)
+{
+ char *p = vty_newline(vty);
+ buffer_put(vty->obuf, p, strlen(p));
+ return 0;
+}
+
+int vty_config_lock(struct vty *vty)
+{
+ if (vty_config == 0) {
+ vty->config = 1;
+ vty_config = 1;
+ }
+ return vty->config;
+}
+
+int vty_config_unlock(struct vty *vty)
+{
+ if (vty_config == 1 && vty->config == 1) {
+ vty->config = 0;
+ vty_config = 0;
+ }
+ return vty->config;
+}
+
+/* Say hello to vty interface. */
+void vty_hello(struct vty *vty)
+{
+ if (host.motdfile) {
+ FILE *f;
+ char buf[4096];
+
+ f = fopen(host.motdfile, "r");
+ if (f) {
+ while (fgets(buf, sizeof(buf), f)) {
+ char *s;
+ /* work backwards to ignore trailling isspace() */
+ for (s = buf + strlen(buf);
+ (s > buf) && isspace(*(s - 1)); s--) ;
+ *s = '\0';
+ vty_out(vty, "%s%s", buf, VTY_NEWLINE);
+ }
+ fclose(f);
+ } else
+ vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
+ } else if (host.motd)
+ vty_out(vty, host.motd);
+}
+
+/* Put out prompt and wait input from user. */
+static void vty_prompt(struct vty *vty)
+{
+ struct utsname names;
+ const char *hostname;
+
+ if (vty->type == VTY_TERM) {
+ hostname = host.name;
+ if (!hostname) {
+ uname(&names);
+ hostname = names.nodename;
+ }
+ vty_out(vty, cmd_prompt(vty->node), hostname);
+ }
+}
+
+/* Command execution over the vty interface. */
+static int vty_command(struct vty *vty, char *buf)
+{
+ int ret;
+ vector vline;
+
+ /* Split readline string up into the vector */
+ vline = cmd_make_strvec(buf);
+
+ if (vline == NULL)
+ return CMD_SUCCESS;
+
+ ret = cmd_execute_command(vline, vty, NULL, 0);
+ if (ret != CMD_SUCCESS)
+ switch (ret) {
+ case CMD_WARNING:
+ if (vty->type == VTY_FILE)
+ vty_out(vty, "Warning...%s", VTY_NEWLINE);
+ break;
+ case CMD_ERR_AMBIGUOUS:
+ vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ break;
+ case CMD_ERR_NO_MATCH:
+ vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
+ break;
+ case CMD_ERR_INCOMPLETE:
+ vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
+ break;
+ }
+ cmd_free_strvec(vline);
+
+ return ret;
+}
+
+static const char telnet_backward_char = 0x08;
+static const char telnet_space_char = ' ';
+
+/* Basic function to write buffer to vty. */
+static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
+{
+ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+ return;
+
+ /* Should we do buffering here ? And make vty_flush (vty) ? */
+ buffer_put(vty->obuf, buf, nbytes);
+}
+
+/* Ensure length of input buffer. Is buffer is short, double it. */
+static void vty_ensure(struct vty *vty, int length)
+{
+ if (vty->max <= length) {
+ vty->max *= 2;
+ vty->buf = realloc(vty->buf, vty->max);
+ // FIXME: check return
+ }
+}
+
+/* Basic function to insert character into vty. */
+static void vty_self_insert(struct vty *vty, char c)
+{
+ int i;
+ int length;
+
+ vty_ensure(vty, vty->length + 1);
+ length = vty->length - vty->cp;
+ memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
+ vty->buf[vty->cp] = c;
+
+ vty_write(vty, &vty->buf[vty->cp], length + 1);
+ for (i = 0; i < length; i++)
+ vty_write(vty, &telnet_backward_char, 1);
+
+ vty->cp++;
+ vty->length++;
+}
+
+/* Self insert character 'c' in overwrite mode. */
+static void vty_self_insert_overwrite(struct vty *vty, char c)
+{
+ vty_ensure(vty, vty->length + 1);
+ vty->buf[vty->cp++] = c;
+
+ if (vty->cp > vty->length)
+ vty->length++;
+
+ if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
+ return;
+
+ vty_write(vty, &c, 1);
+}
+
+/* Insert a word into vty interface with overwrite mode. */
+static void vty_insert_word_overwrite(struct vty *vty, char *str)
+{
+ int len = strlen(str);
+ vty_write(vty, str, len);
+ strcpy(&vty->buf[vty->cp], str);
+ vty->cp += len;
+ vty->length = vty->cp;
+}
+
+/* Forward character. */
+static void vty_forward_char(struct vty *vty)
+{
+ if (vty->cp < vty->length) {
+ vty_write(vty, &vty->buf[vty->cp], 1);
+ vty->cp++;
+ }
+}
+
+/* Backward character. */
+static void vty_backward_char(struct vty *vty)
+{
+ if (vty->cp > 0) {
+ vty->cp--;
+ vty_write(vty, &telnet_backward_char, 1);
+ }
+}
+
+/* Move to the beginning of the line. */
+static void vty_beginning_of_line(struct vty *vty)
+{
+ while (vty->cp)
+ vty_backward_char(vty);
+}
+
+/* Move to the end of the line. */
+static void vty_end_of_line(struct vty *vty)
+{
+ while (vty->cp < vty->length)
+ vty_forward_char(vty);
+}
+
+/* Add current command line to the history buffer. */
+static void vty_hist_add(struct vty *vty)
+{
+ int index;
+
+ if (vty->length == 0)
+ return;
+
+ index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
+
+ /* Ignore the same string as previous one. */
+ if (vty->hist[index])
+ if (strcmp(vty->buf, vty->hist[index]) == 0) {
+ vty->hp = vty->hindex;
+ return;
+ }
+
+ /* Insert history entry. */
+ if (vty->hist[vty->hindex])
+ free(vty->hist[vty->hindex]);
+ vty->hist[vty->hindex] = strdup(vty->buf);
+
+ /* History index rotation. */
+ vty->hindex++;
+ if (vty->hindex == VTY_MAXHIST)
+ vty->hindex = 0;
+
+ vty->hp = vty->hindex;
+}
+
+/* Get telnet window size. */
+static int
+vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
+{
+#ifdef TELNET_OPTION_DEBUG
+ int i;
+
+ for (i = 0; i < nbytes; i++)
+ {
+ switch (buf[i])
+ {
+ case IAC:
+ vty_out (vty, "IAC ");
+ break;
+ case WILL:
+ vty_out (vty, "WILL ");
+ break;
+ case WONT:
+ vty_out (vty, "WONT ");
+ break;
+ case DO:
+ vty_out (vty, "DO ");
+ break;
+ case DONT:
+ vty_out (vty, "DONT ");
+ break;
+ case SB:
+ vty_out (vty, "SB ");
+ break;
+ case SE:
+ vty_out (vty, "SE ");
+ break;
+ case TELOPT_ECHO:
+ vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
+ break;
+ case TELOPT_SGA:
+ vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
+ break;
+ case TELOPT_NAWS:
+ vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
+ break;
+ default:
+ vty_out (vty, "%x ", buf[i]);
+ break;
+ }
+ }
+ vty_out (vty, "%s", VTY_NEWLINE);
+
+#endif /* TELNET_OPTION_DEBUG */
+
+ switch (buf[0])
+ {
+ case SB:
+ vty->sb_len = 0;
+ vty->iac_sb_in_progress = 1;
+ return 0;
+ break;
+ case SE:
+ {
+ if (!vty->iac_sb_in_progress)
+ return 0;
+
+ if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
+ {
+ vty->iac_sb_in_progress = 0;
+ return 0;
+ }
+ switch (vty->sb_buf[0])
+ {
+ case TELOPT_NAWS:
+ if (vty->sb_len != TELNET_NAWS_SB_LEN)
+ vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
+ "should send %d characters, but we received %lu",
+ TELNET_NAWS_SB_LEN, (u_long)vty->sb_len);
+ else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
+ vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
+ "too small to handle the telnet NAWS option",
+ (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
+ else
+ {
+ vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
+ vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
+#ifdef TELNET_OPTION_DEBUG
+ vty_out(vty, "TELNET NAWS window size negotiation completed: "
+ "width %d, height %d%s",
+ vty->width, vty->height, VTY_NEWLINE);
+#endif
+ }
+ break;
+ }
+ vty->iac_sb_in_progress = 0;
+ return 0;
+ break;
+ }
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Execute current command line. */
+static int vty_execute(struct vty *vty)
+{
+ int ret;
+
+ ret = CMD_SUCCESS;
+
+ switch (vty->node) {
+ case AUTH_NODE:
+ case AUTH_ENABLE_NODE:
+ vty_auth(vty, vty->buf);
+ break;
+ default:
+ ret = vty_command(vty, vty->buf);
+ if (vty->type == VTY_TERM)
+ vty_hist_add(vty);
+ break;
+ }
+
+ /* Clear command line buffer. */
+ vty->cp = vty->length = 0;
+ vty_clear_buf(vty);
+
+ if (vty->status != VTY_CLOSE)
+ vty_prompt(vty);
+
+ return ret;
+}
+
+/* Send WILL TELOPT_ECHO to remote server. */
+static void
+vty_will_echo (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+/* Make suppress Go-Ahead telnet option. */
+static void
+vty_will_suppress_go_ahead (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+/* Make don't use linemode over telnet. */
+static void
+vty_dont_linemode (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+/* Use window size. */
+static void
+vty_do_window_size (struct vty *vty)
+{
+ unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
+ vty_out (vty, "%s", cmd);
+}
+
+static void vty_kill_line_from_beginning(struct vty *);
+static void vty_redraw_line(struct vty *);
+
+/* Print command line history. This function is called from
+ vty_next_line and vty_previous_line. */
+static void vty_history_print(struct vty *vty)
+{
+ int length;
+
+ vty_kill_line_from_beginning(vty);
+
+ /* Get previous line from history buffer */
+ length = strlen(vty->hist[vty->hp]);
+ memcpy(vty->buf, vty->hist[vty->hp], length);
+ vty->cp = vty->length = length;
+
+ /* Redraw current line */
+ vty_redraw_line(vty);
+}
+
+/* Show next command line history. */
+static void vty_next_line(struct vty *vty)
+{
+ int try_index;
+
+ if (vty->hp == vty->hindex)
+ return;
+
+ /* Try is there history exist or not. */
+ try_index = vty->hp;
+ if (try_index == (VTY_MAXHIST - 1))
+ try_index = 0;
+ else
+ try_index++;
+
+ /* If there is not history return. */
+ if (vty->hist[try_index] == NULL)
+ return;
+ else
+ vty->hp = try_index;
+
+ vty_history_print(vty);
+}
+
+/* Show previous command line history. */
+static void vty_previous_line(struct vty *vty)
+{
+ int try_index;
+
+ try_index = vty->hp;
+ if (try_index == 0)
+ try_index = VTY_MAXHIST - 1;
+ else
+ try_index--;
+
+ if (vty->hist[try_index] == NULL)
+ return;
+ else
+ vty->hp = try_index;
+
+ vty_history_print(vty);
+}
+
+/* This function redraw all of the command line character. */
+static void vty_redraw_line(struct vty *vty)
+{
+ vty_write(vty, vty->buf, vty->length);
+ vty->cp = vty->length;
+}
+
+/* Forward word. */
+static void vty_forward_word(struct vty *vty)
+{
+ while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+ vty_forward_char(vty);
+
+ while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+ vty_forward_char(vty);
+}
+
+/* Backward word without skipping training space. */
+static void vty_backward_pure_word(struct vty *vty)
+{
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+ vty_backward_char(vty);
+}
+
+/* Backward word. */
+static void vty_backward_word(struct vty *vty)
+{
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+ vty_backward_char(vty);
+
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+ vty_backward_char(vty);
+}
+
+/* When '^D' is typed at the beginning of the line we move to the down
+ level. */
+static void vty_down_level(struct vty *vty)
+{
+ vty_out(vty, "%s", VTY_NEWLINE);
+ (*config_exit_cmd.func) (NULL, vty, 0, NULL);
+ vty_prompt(vty);
+ vty->cp = 0;
+}
+
+/* When '^Z' is received from vty, move down to the enable mode. */
+static void vty_end_config(struct vty *vty)
+{
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case BGP_VPNV4_NODE:
+ case BGP_IPV4_NODE:
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_NODE:
+ case VTY_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ break;
+ default:
+ /* Unknown node, we have to ignore it. */
+ break;
+ }
+
+ vty_prompt(vty);
+ vty->cp = 0;
+}
+
+/* Delete a charcter at the current point. */
+static void vty_delete_char(struct vty *vty)
+{
+ int i;
+ int size;
+
+ if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ return;
+
+ if (vty->length == 0) {
+ vty_down_level(vty);
+ return;
+ }
+
+ if (vty->cp == vty->length)
+ return; /* completion need here? */
+
+ size = vty->length - vty->cp;
+
+ vty->length--;
+ memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
+ vty->buf[vty->length] = '\0';
+
+ vty_write(vty, &vty->buf[vty->cp], size - 1);
+ vty_write(vty, &telnet_space_char, 1);
+
+ for (i = 0; i < size; i++)
+ vty_write(vty, &telnet_backward_char, 1);
+}
+
+/* Delete a character before the point. */
+static void vty_delete_backward_char(struct vty *vty)
+{
+ if (vty->cp == 0)
+ return;
+
+ vty_backward_char(vty);
+ vty_delete_char(vty);
+}
+
+/* Kill rest of line from current point. */
+static void vty_kill_line(struct vty *vty)
+{
+ int i;
+ int size;
+
+ size = vty->length - vty->cp;
+
+ if (size == 0)
+ return;
+
+ for (i = 0; i < size; i++)
+ vty_write(vty, &telnet_space_char, 1);
+ for (i = 0; i < size; i++)
+ vty_write(vty, &telnet_backward_char, 1);
+
+ memset(&vty->buf[vty->cp], 0, size);
+ vty->length = vty->cp;
+}
+
+/* Kill line from the beginning. */
+static void vty_kill_line_from_beginning(struct vty *vty)
+{
+ vty_beginning_of_line(vty);
+ vty_kill_line(vty);
+}
+
+/* Delete a word before the point. */
+static void vty_forward_kill_word(struct vty *vty)
+{
+ while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
+ vty_delete_char(vty);
+ while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
+ vty_delete_char(vty);
+}
+
+/* Delete a word before the point. */
+static void vty_backward_kill_word(struct vty *vty)
+{
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
+ vty_delete_backward_char(vty);
+ while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
+ vty_delete_backward_char(vty);
+}
+
+/* Transpose chars before or at the point. */
+static void vty_transpose_chars(struct vty *vty)
+{
+ char c1, c2;
+
+ /* If length is short or point is near by the beginning of line then
+ return. */
+ if (vty->length < 2 || vty->cp < 1)
+ return;
+
+ /* In case of point is located at the end of the line. */
+ if (vty->cp == vty->length) {
+ c1 = vty->buf[vty->cp - 1];
+ c2 = vty->buf[vty->cp - 2];
+
+ vty_backward_char(vty);
+ vty_backward_char(vty);
+ vty_self_insert_overwrite(vty, c1);
+ vty_self_insert_overwrite(vty, c2);
+ } else {
+ c1 = vty->buf[vty->cp];
+ c2 = vty->buf[vty->cp - 1];
+
+ vty_backward_char(vty);
+ vty_self_insert_overwrite(vty, c1);
+ vty_self_insert_overwrite(vty, c2);
+ }
+}
+
+/* Do completion at vty interface. */
+static void vty_complete_command(struct vty *vty)
+{
+ int i;
+ int ret;
+ char **matched = NULL;
+ vector vline;
+
+ if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
+ return;
+
+ vline = cmd_make_strvec(vty->buf);
+ if (vline == NULL)
+ return;
+
+ /* In case of 'help \t'. */
+ if (isspace((int)vty->buf[vty->length - 1]))
+ vector_set(vline, '\0');
+
+ matched = cmd_complete_command(vline, vty, &ret);
+
+ cmd_free_strvec(vline);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ switch (ret) {
+ case CMD_ERR_AMBIGUOUS:
+ vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ case CMD_ERR_NO_MATCH:
+ /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ case CMD_COMPLETE_FULL_MATCH:
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ vty_backward_pure_word(vty);
+ vty_insert_word_overwrite(vty, matched[0]);
+ vty_self_insert(vty, ' ');
+ free(matched[0]);
+ break;
+ case CMD_COMPLETE_MATCH:
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ vty_backward_pure_word(vty);
+ vty_insert_word_overwrite(vty, matched[0]);
+ free(matched[0]);
+ vector_only_index_free(matched);
+ return;
+ break;
+ case CMD_COMPLETE_LIST_MATCH:
+ for (i = 0; matched[i] != NULL; i++) {
+ if (i != 0 && ((i % 6) == 0))
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_out(vty, "%-10s ", matched[i]);
+ free(matched[i]);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ case CMD_ERR_NOTHING_TODO:
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ break;
+ default:
+ break;
+ }
+ if (matched)
+ vector_only_index_free(matched);
+}
+
+static void
+vty_describe_fold(struct vty *vty, int cmd_width,
+ unsigned int desc_width, struct desc *desc)
+{
+ char *buf;
+ const char *cmd, *p;
+ int pos;
+
+ cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
+
+ if (desc_width <= 0) {
+ vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
+ VTY_NEWLINE);
+ return;
+ }
+
+ buf = calloc(1, strlen(desc->str) + 1);
+ if (!buf)
+ return;
+
+ for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
+ for (pos = desc_width; pos > 0; pos--)
+ if (*(p + pos) == ' ')
+ break;
+
+ if (pos == 0)
+ break;
+
+ strncpy(buf, p, pos);
+ buf[pos] = '\0';
+ vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
+
+ cmd = "";
+ }
+
+ vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
+
+ free(buf);
+}
+
+/* Describe matched command function. */
+static void vty_describe_command(struct vty *vty)
+{
+ int ret;
+ vector vline;
+ vector describe;
+ unsigned int i, width, desc_width;
+ struct desc *desc, *desc_cr = NULL;
+
+ vline = cmd_make_strvec(vty->buf);
+
+ /* In case of '> ?'. */
+ if (vline == NULL) {
+ vline = vector_init(1);
+ vector_set(vline, '\0');
+ } else if (isspace((int)vty->buf[vty->length - 1]))
+ vector_set(vline, '\0');
+
+ describe = cmd_describe_command(vline, vty, &ret);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ /* Ambiguous error. */
+ switch (ret) {
+ case CMD_ERR_AMBIGUOUS:
+ cmd_free_strvec(vline);
+ vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ return;
+ break;
+ case CMD_ERR_NO_MATCH:
+ cmd_free_strvec(vline);
+ vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+ return;
+ break;
+ }
+
+ /* Get width of command string. */
+ width = 0;
+ for (i = 0; i < vector_active(describe); i++)
+ if ((desc = vector_slot(describe, i)) != NULL) {
+ unsigned int len;
+
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ len = strlen(desc->cmd);
+ if (desc->cmd[0] == '.')
+ len--;
+
+ if (width < len)
+ width = len;
+ }
+
+ /* Get width of description string. */
+ desc_width = vty->width - (width + 6);
+
+ /* Print out description. */
+ for (i = 0; i < vector_active(describe); i++)
+ if ((desc = vector_slot(describe, i)) != NULL) {
+ if (desc->cmd[0] == '\0')
+ continue;
+
+ if (strcmp(desc->cmd, "<cr>") == 0) {
+ desc_cr = desc;
+ continue;
+ }
+
+ if (!desc->str)
+ vty_out(vty, " %-s%s",
+ desc->cmd[0] ==
+ '.' ? desc->cmd + 1 : desc->cmd,
+ VTY_NEWLINE);
+ else if (desc_width >= strlen(desc->str))
+ vty_out(vty, " %-*s %s%s", width,
+ desc->cmd[0] ==
+ '.' ? desc->cmd + 1 : desc->cmd,
+ desc->str, VTY_NEWLINE);
+ else
+ vty_describe_fold(vty, width, desc_width, desc);
+
+#if 0
+ vty_out(vty, " %-*s %s%s", width
+ desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ desc->str ? desc->str : "", VTY_NEWLINE);
+#endif /* 0 */
+ }
+
+ if ((desc = desc_cr)) {
+ if (!desc->str)
+ vty_out(vty, " %-s%s",
+ desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ VTY_NEWLINE);
+ else if (desc_width >= strlen(desc->str))
+ vty_out(vty, " %-*s %s%s", width,
+ desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
+ desc->str, VTY_NEWLINE);
+ else
+ vty_describe_fold(vty, width, desc_width, desc);
+ }
+
+ cmd_free_strvec(vline);
+ vector_free(describe);
+
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+}
+
+/* ^C stop current input and do not add command line to the history. */
+static void vty_stop_input(struct vty *vty)
+{
+ vty->cp = vty->length = 0;
+ vty_clear_buf(vty);
+ vty_out(vty, "%s", VTY_NEWLINE);
+
+ switch (vty->node) {
+ case VIEW_NODE:
+ case ENABLE_NODE:
+ /* Nothing to do. */
+ break;
+ case CONFIG_NODE:
+ case INTERFACE_NODE:
+ case ZEBRA_NODE:
+ case RIP_NODE:
+ case RIPNG_NODE:
+ case BGP_NODE:
+ case RMAP_NODE:
+ case OSPF_NODE:
+ case OSPF6_NODE:
+ case ISIS_NODE:
+ case KEYCHAIN_NODE:
+ case KEYCHAIN_KEY_NODE:
+ case MASC_NODE:
+ case VTY_NODE:
+ vty_config_unlock(vty);
+ vty->node = ENABLE_NODE;
+ break;
+ default:
+ /* Unknown node, we have to ignore it. */
+ break;
+ }
+ vty_prompt(vty);
+
+ /* Set history pointer to the latest one. */
+ vty->hp = vty->hindex;
+}
+
+#define CONTROL(X) ((X) - '@')
+#define VTY_NORMAL 0
+#define VTY_PRE_ESCAPE 1
+#define VTY_ESCAPE 2
+
+/* Escape character command map. */
+static void vty_escape_map(unsigned char c, struct vty *vty)
+{
+ switch (c) {
+ case ('A'):
+ vty_previous_line(vty);
+ break;
+ case ('B'):
+ vty_next_line(vty);
+ break;
+ case ('C'):
+ vty_forward_char(vty);
+ break;
+ case ('D'):
+ vty_backward_char(vty);
+ break;
+ default:
+ break;
+ }
+
+ /* Go back to normal mode. */
+ vty->escape = VTY_NORMAL;
+}
+
+/* Quit print out to the buffer. */
+static void vty_buffer_reset(struct vty *vty)
+{
+ buffer_reset(vty->obuf);
+ vty_prompt(vty);
+ vty_redraw_line(vty);
+}
+
+/* Read data via vty socket. */
+int vty_read(struct vty *vty)
+{
+ int i;
+ int nbytes;
+ unsigned char buf[VTY_READ_BUFSIZ];
+
+ int vty_sock = vty->fd;
+
+ /* Read raw data from socket */
+ if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
+ if (nbytes < 0) {
+ if (ERRNO_IO_RETRY(errno)) {
+ vty_event(VTY_READ, vty_sock, vty);
+ return 0;
+ }
+ }
+ buffer_reset(vty->obuf);
+ vty->status = VTY_CLOSE;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ if (buf[i] == IAC) {
+ if (!vty->iac) {
+ vty->iac = 1;
+ continue;
+ } else {
+ vty->iac = 0;
+ }
+ }
+
+ if (vty->iac_sb_in_progress && !vty->iac) {
+ if (vty->sb_len < sizeof(vty->sb_buf))
+ vty->sb_buf[vty->sb_len] = buf[i];
+ vty->sb_len++;
+ continue;
+ }
+
+ if (vty->iac) {
+ /* In case of telnet command */
+ int ret = 0;
+ ret = vty_telnet_option(vty, buf + i, nbytes - i);
+ vty->iac = 0;
+ i += ret;
+ continue;
+ }
+
+ if (vty->status == VTY_MORE) {
+ switch (buf[i]) {
+ case CONTROL('C'):
+ case 'q':
+ case 'Q':
+ vty_buffer_reset(vty);
+ break;
+#if 0 /* More line does not work for "show ip bgp". */
+ case '\n':
+ case '\r':
+ vty->status = VTY_MORELINE;
+ break;
+#endif
+ default:
+ break;
+ }
+ continue;
+ }
+
+ /* Escape character. */
+ if (vty->escape == VTY_ESCAPE) {
+ vty_escape_map(buf[i], vty);
+ continue;
+ }
+
+ /* Pre-escape status. */
+ if (vty->escape == VTY_PRE_ESCAPE) {
+ switch (buf[i]) {
+ case '[':
+ vty->escape = VTY_ESCAPE;
+ break;
+ case 'b':
+ vty_backward_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ case 'f':
+ vty_forward_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ case 'd':
+ vty_forward_kill_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ case CONTROL('H'):
+ case 0x7f:
+ vty_backward_kill_word(vty);
+ vty->escape = VTY_NORMAL;
+ break;
+ default:
+ vty->escape = VTY_NORMAL;
+ break;
+ }
+ continue;
+ }
+
+ switch (buf[i]) {
+ case CONTROL('A'):
+ vty_beginning_of_line(vty);
+ break;
+ case CONTROL('B'):
+ vty_backward_char(vty);
+ break;
+ case CONTROL('C'):
+ vty_stop_input(vty);
+ break;
+ case CONTROL('D'):
+ vty_delete_char(vty);
+ break;
+ case CONTROL('E'):
+ vty_end_of_line(vty);
+ break;
+ case CONTROL('F'):
+ vty_forward_char(vty);
+ break;
+ case CONTROL('H'):
+ case 0x7f:
+ vty_delete_backward_char(vty);
+ break;
+ case CONTROL('K'):
+ vty_kill_line(vty);
+ break;
+ case CONTROL('N'):
+ vty_next_line(vty);
+ break;
+ case CONTROL('P'):
+ vty_previous_line(vty);
+ break;
+ case CONTROL('T'):
+ vty_transpose_chars(vty);
+ break;
+ case CONTROL('U'):
+ vty_kill_line_from_beginning(vty);
+ break;
+ case CONTROL('W'):
+ vty_backward_kill_word(vty);
+ break;
+ case CONTROL('Z'):
+ vty_end_config(vty);
+ break;
+ case '\n':
+ case '\r':
+ vty_out(vty, "%s", VTY_NEWLINE);
+ vty_execute(vty);
+ break;
+ case '\t':
+ vty_complete_command(vty);
+ break;
+ case '?':
+ if (vty->node == AUTH_NODE
+ || vty->node == AUTH_ENABLE_NODE)
+ vty_self_insert(vty, buf[i]);
+ else
+ vty_describe_command(vty);
+ break;
+ case '\033':
+ if (i + 1 < nbytes && buf[i + 1] == '[') {
+ vty->escape = VTY_ESCAPE;
+ i++;
+ } else
+ vty->escape = VTY_PRE_ESCAPE;
+ break;
+ default:
+ if (buf[i] > 31 && buf[i] < 127)
+ vty_self_insert(vty, buf[i]);
+ break;
+ }
+ }
+
+ /* Check status. */
+ if (vty->status == VTY_CLOSE)
+ vty_close(vty);
+ else {
+ vty_event(VTY_WRITE, vty_sock, vty);
+ vty_event(VTY_READ, vty_sock, vty);
+ }
+ return 0;
+}
+
+/* Create new vty structure. */
+struct vty *
+vty_create (int vty_sock, void *priv)
+{
+ struct vty *vty;
+
+ struct termios t;
+
+ tcgetattr(vty_sock, &t);
+ cfmakeraw(&t);
+ tcsetattr(vty_sock, TCSANOW, &t);
+
+ /* Allocate new vty structure and set up default values. */
+ vty = vty_new ();
+ vty->fd = vty_sock;
+ vty->priv = priv;
+ vty->type = VTY_TERM;
+ if (no_password_check)
+ {
+ if (host.advanced)
+ vty->node = ENABLE_NODE;
+ else
+ vty->node = VIEW_NODE;
+ }
+ else
+ vty->node = AUTH_NODE;
+ vty->fail = 0;
+ vty->cp = 0;
+ vty_clear_buf (vty);
+ vty->length = 0;
+ memset (vty->hist, 0, sizeof (vty->hist));
+ vty->hp = 0;
+ vty->hindex = 0;
+ vector_set_index (vtyvec, vty_sock, vty);
+ vty->status = VTY_NORMAL;
+ if (host.lines >= 0)
+ vty->lines = host.lines;
+ else
+ vty->lines = -1;
+
+ if (! no_password_check)
+ {
+ /* Vty is not available if password isn't set. */
+ if (host.password == NULL && host.password_encrypt == NULL)
+ {
+ vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
+ vty->status = VTY_CLOSE;
+ vty_close (vty);
+ return NULL;
+ }
+ }
+
+ /* Say hello to the world. */
+ vty_hello (vty);
+ if (! no_password_check)
+ vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
+
+ /* Setting up terminal. */
+ vty_will_echo (vty);
+ vty_will_suppress_go_ahead (vty);
+
+ vty_dont_linemode (vty);
+ vty_do_window_size (vty);
+ /* vty_dont_lflow_ahead (vty); */
+
+ vty_prompt (vty);
+
+ /* Add read/write thread. */
+ vty_event (VTY_WRITE, vty_sock, vty);
+ vty_event (VTY_READ, vty_sock, vty);
+
+ return vty;
+}
+
+DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
+{
+ unsigned int i;
+ struct vty *v;
+
+ for (i = 0; i < vector_active(vtyvec); i++)
+ if ((v = vector_slot(vtyvec, i)) != NULL)
+ vty_out(vty, "%svty[%d] %s",
+ v->config ? "*" : " ", i, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+/* Move to vty configuration mode. */
+DEFUN(line_vty,
+ line_vty_cmd,
+ "line vty", "Configure a terminal line\n" "Virtual terminal\n")
+{
+ vty->node = VTY_NODE;
+ return CMD_SUCCESS;
+}
+
+/* vty login. */
+DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
+{
+ no_password_check = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_vty_login,
+ no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
+{
+ no_password_check = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(service_advanced_vty,
+ service_advanced_vty_cmd,
+ "service advanced-vty",
+ "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
+{
+ host.advanced = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_service_advanced_vty,
+ no_service_advanced_vty_cmd,
+ "no service advanced-vty",
+ NO_STR
+ "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
+{
+ host.advanced = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(terminal_monitor,
+ terminal_monitor_cmd,
+ "terminal monitor",
+ "Set terminal line parameters\n"
+ "Copy debug output to the current terminal line\n")
+{
+ vty->monitor = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(terminal_no_monitor,
+ terminal_no_monitor_cmd,
+ "terminal no monitor",
+ "Set terminal line parameters\n"
+ NO_STR "Copy debug output to the current terminal line\n")
+{
+ vty->monitor = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_history,
+ show_history_cmd,
+ "show history", SHOW_STR "Display the session command history\n")
+{
+ int index;
+
+ for (index = vty->hindex + 1; index != vty->hindex;) {
+ if (index == VTY_MAXHIST) {
+ index = 0;
+ continue;
+ }
+
+ if (vty->hist[index] != NULL)
+ vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
+
+ index++;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Display current configuration. */
+static int vty_config_write(struct vty *vty)
+{
+ vty_out(vty, "line vty%s", VTY_NEWLINE);
+
+ /* login */
+ if (no_password_check)
+ vty_out(vty, " no login%s", VTY_NEWLINE);
+
+ vty_out(vty, "!%s", VTY_NEWLINE);
+
+ return CMD_SUCCESS;
+}
+
+struct cmd_node vty_node = {
+ VTY_NODE,
+ "%s(config-line)# ",
+ 1,
+};
+
+/* Reset all VTY status. */
+void vty_reset()
+{
+ unsigned int i;
+ struct vty *vty;
+ struct thread *vty_serv_thread;
+
+ for (i = 0; i < vector_active(vtyvec); i++)
+ if ((vty = vector_slot(vtyvec, i)) != NULL) {
+ buffer_reset(vty->obuf);
+ vty->status = VTY_CLOSE;
+ vty_close(vty);
+ }
+
+ for (i = 0; i < vector_active(Vvty_serv_thread); i++)
+ if ((vty_serv_thread =
+ vector_slot(Vvty_serv_thread, i)) != NULL) {
+ //thread_cancel (vty_serv_thread);
+ vector_slot(Vvty_serv_thread, i) = NULL;
+ close(i);
+ }
+}
+
+static void vty_save_cwd(void)
+{
+ char cwd[MAXPATHLEN];
+ char *c;
+
+ c = getcwd(cwd, MAXPATHLEN);
+
+ if (!c) {
+ chdir(SYSCONFDIR);
+ getcwd(cwd, MAXPATHLEN);
+ }
+
+ vty_cwd = malloc(strlen(cwd) + 1);
+ strcpy(vty_cwd, cwd);
+}
+
+char *vty_get_cwd()
+{
+ return vty_cwd;
+}
+
+int vty_shell_serv(struct vty *vty)
+{
+ return vty->type == VTY_SHELL_SERV ? 1 : 0;
+}
+
+void vty_init_vtysh()
+{
+ vtyvec = vector_init(VECTOR_MIN_SIZE);
+}
+
+/* Install vty's own commands like `who' command. */
+void vty_init()
+{
+ /* For further configuration read, preserve current directory. */
+ vty_save_cwd();
+
+ vtyvec = vector_init(VECTOR_MIN_SIZE);
+
+ /* Install bgp top node. */
+ install_node(&vty_node, vty_config_write);
+
+ install_element(VIEW_NODE, &config_who_cmd);
+ install_element(VIEW_NODE, &show_history_cmd);
+ install_element(ENABLE_NODE, &config_who_cmd);
+ install_element(CONFIG_NODE, &line_vty_cmd);
+ install_element(CONFIG_NODE, &service_advanced_vty_cmd);
+ install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
+ install_element(CONFIG_NODE, &show_history_cmd);
+ install_element(ENABLE_NODE, &terminal_monitor_cmd);
+ install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
+ install_element(ENABLE_NODE, &show_history_cmd);
+
+ install_default(VTY_NODE);
+#if 0
+ install_element(VTY_NODE, &vty_login_cmd);
+ install_element(VTY_NODE, &no_vty_login_cmd);
+#endif
+}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
new file mode 100644
index 000000000..44531dd50
--- /dev/null
+++ b/openbsc/src/vty_interface.c
@@ -0,0 +1,905 @@
+/* OpenBSC interface to quagga VTY */
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <vty/command.h>
+#include <vty/vty.h>
+
+#include <arpa/inet.h>
+
+#include <openbsc/linuxlist.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/db.h>
+
+static struct gsm_network *gsmnet;
+
+struct cmd_node bts_node = {
+ BTS_NODE,
+ "%s(bts)#",
+ 1,
+};
+
+struct cmd_node trx_node = {
+ TRX_NODE,
+ "%s(trx)#",
+ 1,
+};
+
+struct cmd_node ts_node = {
+ TS_NODE,
+ "%s(ts)#",
+ 1,
+};
+
+struct cmd_node subscr_node = {
+ SUBSCR_NODE,
+ "%s(subscriber)#",
+ 1,
+};
+
+static int dummy_config_write(struct vty *v)
+{
+ return CMD_SUCCESS;
+}
+
+static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms)
+{
+ vty_out(vty,"Oper '%s', Admin %u, Avail '%s'%s",
+ nm_opstate_name(nms->operational), nms->administrative,
+ nm_avail_name(nms->availability), VTY_NEWLINE);
+}
+
+static void net_dump_vty(struct vty *vty, struct gsm_network *net)
+{
+ vty_out(vty, "BSC is on Country Code %u, Network Code %u "
+ "and has %u BTS%s", net->country_code, net->network_code,
+ net->num_bts, VTY_NEWLINE);
+ vty_out(vty, " Long network name: '%s'%s",
+ net->name_long, VTY_NEWLINE);
+ vty_out(vty, " Short network name: '%s'%s",
+ net->name_short, VTY_NEWLINE);
+}
+
+DEFUN(show_net, show_net_cmd, "show network",
+ SHOW_STR "Display information about a GSM NETWORK\n")
+{
+ struct gsm_network *net = gsmnet;
+ net_dump_vty(vty, net);
+
+ return CMD_SUCCESS;
+}
+
+static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l)
+{
+ struct e1inp_line *line;
+
+ if (!e1l) {
+ vty_out(vty, " None%s", VTY_NEWLINE);
+ return;
+ }
+
+ line = e1l->ts->line;
+
+ vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s",
+ line->num, line->driver->name, e1l->ts->num,
+ e1inp_signtype_name(e1l->type), VTY_NEWLINE);
+ vty_out(vty, " E1 TEI %u, SAPI %u%s",
+ e1l->tei, e1l->sapi, VTY_NEWLINE);
+}
+
+static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
+{
+ vty_out(vty, "BTS %u is of %s type, has LAC %u, BSIC %u, TSC %u and %u TRX%s",
+ bts->nr, btstype2str(bts->type), bts->location_area_code,
+ bts->bsic, bts->tsc, bts->num_trx, VTY_NEWLINE);
+ if (is_ipaccess_bts(bts))
+ vty_out(vty, " Unit ID: %u/%u/0%s",
+ bts->ip_access.site_id, bts->ip_access.bts_id,
+ VTY_NEWLINE);
+ vty_out(vty, " NM State: ");
+ net_dump_nmstate(vty, &bts->nm_state);
+ vty_out(vty, " Site Mgr NM State: ");
+ net_dump_nmstate(vty, &bts->site_mgr.nm_state);
+ vty_out(vty, " Paging: FIXME pending requests, %u free slots%s",
+ bts->paging.available_slots, VTY_NEWLINE);
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, bts->oml_link);
+ /* FIXME: oml_link, chan_desc */
+}
+
+DEFUN(show_bts, show_bts_cmd, "show bts [number]",
+ SHOW_STR "Display information about a BTS\n"
+ "BTS number")
+{
+ struct gsm_network *net = gsmnet;
+ int bts_nr;
+
+ if (argc != 0) {
+ /* use the BTS number that the user has specified */
+ bts_nr = atoi(argv[0]);
+ if (bts_nr > net->num_bts) {
+ vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts_dump_vty(vty, &net->bts[bts_nr]);
+ return CMD_SUCCESS;
+ }
+ /* print all BTS's */
+ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++)
+ bts_dump_vty(vty, &net->bts[bts_nr]);
+
+ return CMD_SUCCESS;
+}
+
+static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx)
+{
+ vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s",
+ trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE);
+ vty_out(vty, " NM State: ");
+ net_dump_nmstate(vty, &trx->nm_state);
+ vty_out(vty, " Baseband Transceiver NM State: ");
+ net_dump_nmstate(vty, &trx->bb_transc.nm_state);
+ vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE);
+ e1isl_dump_vty(vty, trx->rsl_link);
+}
+
+DEFUN(show_trx,
+ show_trx_cmd,
+ "show trx [bts_nr] [trx_nr]",
+ SHOW_STR "Display information about a TRX\n")
+{
+ struct gsm_network *net = gsmnet;
+ struct gsm_bts *bts = NULL;
+ struct gsm_bts_trx *trx;
+ int bts_nr, trx_nr;
+
+ if (argc >= 1) {
+ /* use the BTS number that the user has specified */
+ bts_nr = atoi(argv[0]);
+ if (bts_nr >= net->num_bts) {
+ vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts = &net->bts[bts_nr];
+ }
+ if (argc >= 2) {
+ trx_nr = atoi(argv[1]);
+ if (trx_nr >= bts->num_trx) {
+ vty_out(vty, "%% can't find TRX '%s'%s", argv[1],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ trx = &bts->trx[trx_nr];
+ trx_dump_vty(vty, trx);
+ return CMD_SUCCESS;
+ }
+ if (bts) {
+ /* print all TRX in this BTS */
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ trx = &bts->trx[trx_nr];
+ trx_dump_vty(vty, trx);
+ }
+ return CMD_SUCCESS;
+ }
+
+ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
+ bts = &net->bts[bts_nr];
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ trx = &bts->trx[trx_nr];
+ trx_dump_vty(vty, trx);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts)
+{
+ struct in_addr ia;
+
+ vty_out(vty, "Timeslot %u of TRX %u in BTS %u, phys cfg %s%s",
+ ts->nr, ts->trx->nr, ts->trx->bts->nr,
+ gsm_pchan_name(ts->pchan), VTY_NEWLINE);
+ vty_out(vty, " NM State: ");
+ net_dump_nmstate(vty, &ts->nm_state);
+ if (is_ipaccess_bts(ts->trx->bts)) {
+ ia.s_addr = ts->abis_ip.bound_ip;
+ vty_out(vty, " Bound IP: %s Port %u FC=%u F8=%u%s",
+ inet_ntoa(ia), ts->abis_ip.bound_port,
+ ts->abis_ip.attr_fc, ts->abis_ip.attr_f8,
+ VTY_NEWLINE);
+ } else {
+ vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s",
+ ts->e1_link.e1_nr, ts->e1_link.e1_ts,
+ ts->e1_link.e1_ts_ss, VTY_NEWLINE);
+ }
+}
+
+DEFUN(show_ts,
+ show_ts_cmd,
+ "show timeslot [bts_nr] [trx_nr] [ts_nr]",
+ SHOW_STR "Display information about a TS\n")
+{
+ struct gsm_network *net = gsmnet;
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ int bts_nr, trx_nr, ts_nr;
+
+ if (argc >= 1) {
+ /* use the BTS number that the user has specified */
+ bts_nr = atoi(argv[0]);
+ if (bts_nr >= net->num_bts) {
+ vty_out(vty, "%% can't find BTS '%s'%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts = &net->bts[bts_nr];
+ }
+ if (argc >= 2) {
+ trx_nr = atoi(argv[1]);
+ if (trx_nr >= bts->num_trx) {
+ vty_out(vty, "%% can't find TRX '%s'%s", argv[1],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ trx = &bts->trx[trx_nr];
+ }
+ if (argc >= 3) {
+ ts_nr = atoi(argv[2]);
+ if (ts_nr >= TRX_NR_TS) {
+ vty_out(vty, "%% can't find TS '%s'%s", argv[2],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ts = &trx->ts[ts_nr];
+ ts_dump_vty(vty, ts);
+ return CMD_SUCCESS;
+ }
+ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
+ bts = &net->bts[bts_nr];
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ trx = &bts->trx[trx_nr];
+ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
+ ts = &trx->ts[ts_nr];
+ ts_dump_vty(vty, ts);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
+{
+ vty_out(vty, " ID: %lu, Authorized: %d%s", subscr->id,
+ subscr->authorized, VTY_NEWLINE);
+ if (subscr->name)
+ vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE);
+ if (subscr->extension)
+ vty_out(vty, " Extension: %s%s", subscr->extension,
+ VTY_NEWLINE);
+ if (subscr->imsi)
+ vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE);
+ if (subscr->tmsi)
+ vty_out(vty, " TMSI: %s%s", subscr->tmsi, VTY_NEWLINE);
+}
+
+static void lchan_dump_vty(struct vty *vty, struct gsm_lchan *lchan)
+{
+ vty_out(vty, "Lchan %u in Timeslot %u of TRX %u in BTS %u, Type %s%s",
+ lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
+ lchan->ts->trx->bts->nr, gsm_lchan_name(lchan->type),
+ VTY_NEWLINE);
+ vty_out(vty, " Use Count: %u%s", lchan->use_count, VTY_NEWLINE);
+ vty_out(vty, " BS Power %u, MS Power %u%s", lchan->bs_power,
+ lchan->ms_power, VTY_NEWLINE);
+ if (lchan->subscr) {
+ vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
+ subscr_dump_vty(vty, lchan->subscr);
+ } else
+ vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
+}
+
+static void call_dump_vty(struct vty *vty, struct gsm_call *call)
+{
+ vty_out(vty, "Call Type %u, State %u, Transaction ID %u%s",
+ call->type, call->state, call->transaction_id, VTY_NEWLINE);
+
+ if (call->local_lchan) {
+ vty_out(vty, "Call Local Channel:%s", VTY_NEWLINE);
+ lchan_dump_vty(vty, call->local_lchan);
+ } else
+ vty_out(vty, "Call has no Local Channel%s", VTY_NEWLINE);
+
+ if (call->remote_lchan) {
+ vty_out(vty, "Call Remote Channel:%s", VTY_NEWLINE);
+ lchan_dump_vty(vty, call->remote_lchan);
+ } else
+ vty_out(vty, "Call has no Remote Channel%s", VTY_NEWLINE);
+
+ if (call->called_subscr) {
+ vty_out(vty, "Called Subscriber:%s", VTY_NEWLINE);
+ subscr_dump_vty(vty, call->called_subscr);
+ } else
+ vty_out(vty, "Call has no Called Subscriber%s", VTY_NEWLINE);
+}
+
+DEFUN(show_lchan,
+ show_lchan_cmd,
+ "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]",
+ SHOW_STR "Display information about a logical channel\n")
+{
+ struct gsm_network *net = gsmnet;
+ struct gsm_bts *bts;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts_trx_ts *ts;
+ struct gsm_lchan *lchan;
+ int bts_nr, trx_nr, ts_nr, lchan_nr;
+
+ if (argc >= 1) {
+ /* use the BTS number that the user has specified */
+ bts_nr = atoi(argv[0]);
+ if (bts_nr >= net->num_bts) {
+ vty_out(vty, "%% can't find BTS %s%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts = &net->bts[bts_nr];
+ }
+ if (argc >= 2) {
+ trx_nr = atoi(argv[1]);
+ if (trx_nr >= bts->num_trx) {
+ vty_out(vty, "%% can't find TRX %s%s", argv[1],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ trx = &bts->trx[trx_nr];
+ }
+ if (argc >= 3) {
+ ts_nr = atoi(argv[2]);
+ if (ts_nr >= TRX_NR_TS) {
+ vty_out(vty, "%% can't find TS %s%s", argv[2],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ts = &trx->ts[ts_nr];
+ }
+ if (argc >= 4) {
+ lchan_nr = atoi(argv[3]);
+ if (lchan_nr >= TS_MAX_LCHAN) {
+ vty_out(vty, "%% can't find LCHAN %s%s", argv[3],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ lchan = &ts->lchan[lchan_nr];
+ lchan_dump_vty(vty, lchan);
+ return CMD_SUCCESS;
+ }
+ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
+ bts = &net->bts[bts_nr];
+ for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {
+ trx = &bts->trx[trx_nr];
+ for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
+ ts = &trx->ts[ts_nr];
+ for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN;
+ lchan_nr++) {
+ lchan = &ts->lchan[lchan_nr];
+ if (lchan->type == GSM_LCHAN_NONE)
+ continue;
+ lchan_dump_vty(vty, lchan);
+ }
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv)
+{
+ vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE);
+}
+
+DEFUN(show_e1drv,
+ show_e1drv_cmd,
+ "show e1_driver",
+ SHOW_STR "Display information about available E1 drivers\n")
+{
+ struct e1inp_driver *drv;
+
+ llist_for_each_entry(drv, &e1inp_driver_list, list)
+ e1drv_dump_vty(vty, drv);
+
+ return CMD_SUCCESS;
+}
+
+static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line)
+{
+ vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s",
+ line->num, line->name ? line->name : "",
+ line->driver->name, VTY_NEWLINE);
+}
+
+DEFUN(show_e1line,
+ show_e1line_cmd,
+ "show e1_line [line_nr]",
+ SHOW_STR "Display information about a E1 line\n")
+{
+ struct e1inp_line *line;
+
+ if (argc >= 1) {
+ int num = atoi(argv[0]);
+ llist_for_each_entry(line, &e1inp_line_list, list) {
+ if (line->num == num) {
+ e1line_dump_vty(vty, line);
+ return CMD_SUCCESS;
+ }
+ }
+ return CMD_WARNING;
+ }
+
+ llist_for_each_entry(line, &e1inp_line_list, list)
+ e1line_dump_vty(vty, line);
+
+ return CMD_SUCCESS;
+}
+
+static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts)
+{
+ vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s",
+ ts->num, ts->line->num, e1inp_tstype_name(ts->type),
+ VTY_NEWLINE);
+}
+
+DEFUN(show_e1ts,
+ show_e1ts_cmd,
+ "show e1_timeslot [line_nr] [ts_nr]",
+ SHOW_STR "Display information about a E1 timeslot\n")
+{
+ struct e1inp_line *line;
+ struct e1inp_ts *ts;
+ int ts_nr;
+
+ if (argc == 0) {
+ llist_for_each_entry(line, &e1inp_line_list, list) {
+ for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
+ ts = &line->ts[ts_nr];
+ e1ts_dump_vty(vty, ts);
+ }
+ }
+ return CMD_SUCCESS;
+ }
+ if (argc >= 1) {
+ int num = atoi(argv[0]);
+ llist_for_each_entry(line, &e1inp_line_list, list) {
+ if (line->num == num)
+ break;
+ }
+ if (!line || line->num != num) {
+ vty_out(vty, "E1 line %s is invalid%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ if (argc >= 2) {
+ ts_nr = atoi(argv[1]);
+ if (ts_nr > NUM_E1_TS) {
+ vty_out(vty, "E1 timeslot %s is invalid%s",
+ argv[1], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ts = &line->ts[ts_nr];
+ e1ts_dump_vty(vty, ts);
+ return CMD_SUCCESS;
+ } else {
+ for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
+ ts = &line->ts[ts_nr];
+ e1ts_dump_vty(vty, ts);
+ }
+ return CMD_SUCCESS;
+ }
+ return CMD_SUCCESS;
+}
+
+static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag)
+{
+ vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE);
+ subscr_dump_vty(vty, pag->subscr);
+}
+
+static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts)
+{
+ struct gsm_paging_request *pag;
+
+ llist_for_each_entry(pag, &bts->paging.pending_requests, entry)
+ paging_dump_vty(vty, pag);
+}
+
+DEFUN(show_paging,
+ show_paging_cmd,
+ "show paging [bts_nr]",
+ SHOW_STR "Display information about paging reuqests of a BTS\n")
+{
+ struct gsm_network *net = gsmnet;
+ struct gsm_bts *bts;
+ int bts_nr;
+
+ if (argc >= 1) {
+ /* use the BTS number that the user has specified */
+ bts_nr = atoi(argv[0]);
+ if (bts_nr >= net->num_bts) {
+ vty_out(vty, "%% can't find BTS %s%s", argv[0],
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts = &net->bts[bts_nr];
+ bts_paging_dump_vty(vty, bts);
+
+ return CMD_SUCCESS;
+ }
+ for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) {
+ bts = &net->bts[bts_nr];
+ bts_paging_dump_vty(vty, bts);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* per-subscriber configuration */
+DEFUN(cfg_subscr,
+ cfg_subscr_cmd,
+ "subscriber IMSI",
+ "Select a Subscriber to configure\n")
+{
+ const char *imsi = argv[0];
+ struct gsm_subscriber *subscr;
+
+ subscr = subscr_get_by_imsi(imsi);
+ if (!subscr) {
+ vty_out(vty, "%% No subscriber for IMSI %s%s",
+ imsi, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty->index = subscr;
+ vty->node = SUBSCR_NODE;
+
+ return CMD_SUCCESS;
+}
+
+
+/* per-BTS configuration */
+DEFUN(cfg_bts,
+ cfg_bts_cmd,
+ "bts BTS_NR",
+ "Select a BTS to configure\n")
+{
+ int bts_nr = atoi(argv[0]);
+ struct gsm_bts *bts;
+
+ if (bts_nr >= GSM_MAX_BTS) {
+ vty_out(vty, "%% This Version of OpenBSC only supports %u BTS%s",
+ GSM_MAX_BTS, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts = &gsmnet->bts[bts_nr];
+ if (bts_nr >= gsmnet->num_bts)
+ gsmnet->num_bts = bts_nr + 1;
+
+ vty->index = bts;
+ vty->node = BTS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_type,
+ cfg_bts_type_cmd,
+ "type TYPE",
+ "Set the BTS type\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ //bts->type =
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_lac,
+ cfg_bts_lac_cmd,
+ "location_area_code <0-255>",
+ "Set the Location Area Code (LAC) of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int lac = atoi(argv[0]);
+
+ if (lac < 0 || lac > 0xff) {
+ vty_out(vty, "%% LAC %d is not in the valid range (0-255)%s",
+ lac, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts->location_area_code = lac;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_tsc,
+ cfg_bts_tsc_cmd,
+ "training_sequence_code <0-255>",
+ "Set the Training Sequence Code (TSC) of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int tsc = atoi(argv[0]);
+
+ if (tsc < 0 || tsc > 0xff) {
+ vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s",
+ tsc, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts->tsc = tsc;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_bsic,
+ cfg_bts_bsic_cmd,
+ "base_station_id_code <0-63>",
+ "Set the Base Station Identity Code (BSIC) of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int bsic = atoi(argv[0]);
+
+ if (bsic < 0 || bsic > 0x3f) {
+ vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s",
+ bsic, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts->bsic = bsic;
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_bts_unit_id,
+ cfg_bts_unit_id_cmd,
+ "unit_id <0-65534> <0-255>",
+ "Set the BTS Unit ID of this BTS\n")
+{
+ struct gsm_bts *bts = vty->index;
+ int site_id = atoi(argv[0]);
+ int bts_id = atoi(argv[1]);
+
+ if (site_id < 0 || site_id > 65534) {
+ vty_out(vty, "%% Site ID %d is not in the valid range%s",
+ site_id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (site_id < 0 || site_id > 255) {
+ vty_out(vty, "%% BTS ID %d is not in the valid range%s",
+ bts_id, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ bts->ip_access.site_id = site_id;
+ bts->ip_access.bts_id = bts_id;
+
+ return CMD_SUCCESS;
+}
+
+/* per TRX configuration */
+DEFUN(cfg_trx,
+ cfg_trx_cmd,
+ "trx TRX_NR",
+ "Select a TRX to configure")
+{
+ int trx_nr = atoi(argv[0]);
+ struct gsm_bts *bts = vty->index;
+ struct gsm_bts_trx *trx;
+
+ if (trx_nr > BTS_MAX_TRX) {
+ vty_out(vty, "%% This version of OpenBSC only supports %u TRX%s",
+ BTS_MAX_TRX+1, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (trx_nr >= bts->num_trx)
+ bts->num_trx = trx_nr+1;
+
+ trx = &bts->trx[trx_nr];
+
+ vty->index = trx;
+ vty->node = TRX_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trx_arfcn,
+ cfg_trx_arfcn_cmd,
+ "arfcn <1-1024>",
+ "Set the ARFCN for this TRX\n")
+{
+ int arfcn = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+
+ /* FIXME: check if this ARFCN is supported by this TRX */
+
+ trx->arfcn = arfcn;
+
+ /* FIXME: patch ARFCN into SYSTEM INFORMATION */
+ /* FIXME: use OML layer to update the ARFCN */
+ /* FIXME: use RSL layer to update SYSTEM INFORMATION */
+
+ return CMD_SUCCESS;
+}
+
+/* per TS configuration */
+DEFUN(cfg_ts,
+ cfg_ts_cmd,
+ "timeslot TS_NR",
+ "Select a Timeslot to configure")
+{
+ int ts_nr = atoi(argv[0]);
+ struct gsm_bts_trx *trx = vty->index;
+ struct gsm_bts_trx_ts *ts;
+
+ if (ts_nr >= TRX_NR_TS) {
+ vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s",
+ TRX_NR_TS, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ts = &trx->ts[ts_nr];
+
+ vty->index = ts;
+ vty->node = TS_NODE;
+
+ return CMD_SUCCESS;
+}
+
+
+/* Subscriber */
+DEFUN(show_subscr,
+ show_subscr_cmd,
+ "show subscriber [IMSI]",
+ SHOW_STR "Display information about a subscriber\n")
+{
+ const char *imsi;
+ struct gsm_subscriber *subscr;
+
+ if (argc >= 1) {
+ imsi = argv[0];
+ subscr = subscr_get_by_imsi(imsi);
+ if (!subscr) {
+ vty_out(vty, "%% unknown subscriber%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ subscr_dump_vty(vty, subscr);
+
+ return CMD_SUCCESS;
+ }
+
+ /* FIXME: iterate over all subscribers ? */
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_subscr_name,
+ cfg_subscr_name_cmd,
+ "name NAME",
+ "Set the name of the subscriber")
+{
+ const char *name = argv[0];
+ struct gsm_subscriber *subscr = vty->index;
+
+ strncpy(subscr->name, name, sizeof(subscr->name));
+
+ db_sync_subscriber(subscr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_subscr_extension,
+ cfg_subscr_extension_cmd,
+ "extension EXTENSION",
+ "Set the extension of the subscriber")
+{
+ const char *name = argv[0];
+ struct gsm_subscriber *subscr = vty->index;
+
+ strncpy(subscr->extension, name, sizeof(subscr->extension));
+
+ db_sync_subscriber(subscr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_subscr_authorized,
+ cfg_subscr_authorized_cmd,
+ "auth <0-1>",
+ "Set the authorization status of the subscriber")
+{
+ int auth = atoi(argv[0]);
+ struct gsm_subscriber *subscr = vty->index;
+
+ if (auth)
+ subscr->authorized = 1;
+ else
+ subscr->authorized = 0;
+
+ db_sync_subscriber(subscr);
+
+ return CMD_SUCCESS;
+}
+
+int bsc_vty_init(struct gsm_network *net)
+{
+ gsmnet = net;
+
+ cmd_init(1);
+ vty_init();
+
+ install_element(VIEW_NODE, &show_net_cmd);
+ install_element(VIEW_NODE, &show_bts_cmd);
+ install_element(VIEW_NODE, &show_trx_cmd);
+ install_element(VIEW_NODE, &show_ts_cmd);
+ install_element(VIEW_NODE, &show_lchan_cmd);
+
+ install_element(VIEW_NODE, &show_e1drv_cmd);
+ install_element(VIEW_NODE, &show_e1line_cmd);
+ install_element(VIEW_NODE, &show_e1ts_cmd);
+
+ install_element(VIEW_NODE, &show_paging_cmd);
+
+ install_element(VIEW_NODE, &show_subscr_cmd);
+
+ install_element(CONFIG_NODE, &cfg_bts_cmd);
+ install_node(&bts_node, dummy_config_write);
+ install_default(BTS_NODE);
+ install_element(BTS_NODE, &cfg_bts_type_cmd);
+ install_element(BTS_NODE, &cfg_bts_lac_cmd);
+ install_element(BTS_NODE, &cfg_bts_tsc_cmd);
+ install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
+
+ install_element(BTS_NODE, &cfg_trx_cmd);
+ install_node(&trx_node, dummy_config_write);
+ install_default(TRX_NODE);
+ install_element(TRX_NODE, &cfg_trx_arfcn_cmd);
+
+ install_element(TRX_NODE, &cfg_ts_cmd);
+ install_node(&ts_node, dummy_config_write);
+ install_default(TS_NODE);
+
+ install_element(CONFIG_NODE, &cfg_subscr_cmd);
+ install_node(&subscr_node, dummy_config_write);
+ install_default(SUBSCR_NODE);
+ install_element(SUBSCR_NODE, &cfg_subscr_name_cmd);
+ install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd);
+ install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd);
+
+ return 0;
+}
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
new file mode 100644
index 000000000..2b72c9c11
--- /dev/null
+++ b/openbsc/tests/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = debug timer sms gsm0408 db
diff --git a/openbsc/tests/db/Makefile.am b/openbsc/tests/db/Makefile.am
new file mode 100644
index 000000000..3d9722c50
--- /dev/null
+++ b/openbsc/tests/db/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+AM_CFLAGS=-Wall -ggdb3
+
+noinst_PROGRAMS = db_test
+
+db_test_SOURCES = db_test.c
+db_test_LDADD = $(top_builddir)/src/libbsc.a -ldl -ldbi
+
diff --git a/openbsc/tests/db/db_test.c b/openbsc/tests/db/db_test.c
new file mode 100644
index 000000000..6962aa3bf
--- /dev/null
+++ b/openbsc/tests/db/db_test.c
@@ -0,0 +1,106 @@
+/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/db.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define COMPARE(original, copy) \
+ if (original->id != copy->id) \
+ fprintf(stderr, "Ids do not match in %s:%d %llu %llu\n", \
+ __FUNCTION__, __LINE__, original->id, copy->id); \
+ if (original->lac != copy->lac) \
+ fprintf(stderr, "LAC do not match in %s:%d %d %d\n", \
+ __FUNCTION__, __LINE__, original->lac, copy->lac); \
+ if (original->authorized != copy->authorized) \
+ fprintf(stderr, "Authorize do not match in %s:%d %d %d\n", \
+ __FUNCTION__, __LINE__, original->authorized, \
+ copy->authorized); \
+ if (strcmp(original->imsi, copy->imsi) != 0) \
+ fprintf(stderr, "IMSIs do not match in %s:%d '%s' '%s'\n", \
+ __FUNCTION__, __LINE__, original->imsi, copy->imsi); \
+ if (strcmp(original->tmsi, copy->tmsi) != 0) \
+ fprintf(stderr, "TMSIs do not match in %s:%d '%s' '%s'\n", \
+ __FUNCTION__, __LINE__, original->tmsi, copy->tmsi); \
+ if (strcmp(original->name, copy->name) != 0) \
+ fprintf(stderr, "names do not match in %s:%d '%s' '%s'\n", \
+ __FUNCTION__, __LINE__, original->name, copy->name); \
+ if (strcmp(original->extension, copy->extension) != 0) \
+ fprintf(stderr, "names do not match in %s:%d '%s' '%s'\n", \
+ __FUNCTION__, __LINE__, original->extension, copy->extension); \
+
+int main() {
+
+ if (db_init("hlr.sqlite3")) {
+ printf("DB: Failed to init database. Please check the option settings.\n");
+ return 1;
+ }
+ printf("DB: Database initialized.\n");
+
+ if (db_prepare()) {
+ printf("DB: Failed to prepare database.\n");
+ return 1;
+ }
+ printf("DB: Database prepared.\n");
+
+ struct gsm_subscriber *alice = NULL;
+ struct gsm_subscriber *alice_db;
+
+ char *alice_imsi = "3243245432345";
+ alice = db_create_subscriber(alice_imsi);
+ db_sync_subscriber(alice);
+ alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice->imsi);
+ COMPARE(alice, alice_db);
+ subscr_put(alice_db);
+ subscr_put(alice);
+
+ alice_imsi = "3693245423445";
+ alice = db_create_subscriber(alice_imsi);
+ db_subscriber_assoc_imei(alice, "1234567890");
+ db_subscriber_alloc_tmsi(alice);
+ alice->lac=42;
+ db_sync_subscriber(alice);
+ alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
+ COMPARE(alice, alice_db);
+ subscr_put(alice);
+ subscr_put(alice_db);
+
+ alice_imsi = "9993245423445";
+ alice = db_create_subscriber(alice_imsi);
+ db_subscriber_alloc_tmsi(alice);
+ alice->lac=42;
+ db_sync_subscriber(alice);
+ db_subscriber_assoc_imei(alice, "1234567890");
+ db_subscriber_assoc_imei(alice, "6543560920");
+ alice_db = db_get_subscriber(GSM_SUBSCRIBER_IMSI, alice_imsi);
+ COMPARE(alice, alice_db);
+ subscr_put(alice);
+ subscr_put(alice_db);
+
+ db_fini();
+
+ return 0;
+}
+
+/* stubs */
+void input_event(void) {}
+void nm_state_event(void) {}
diff --git a/openbsc/tests/debug/Makefile.am b/openbsc/tests/debug/Makefile.am
new file mode 100644
index 000000000..0cdf46ad5
--- /dev/null
+++ b/openbsc/tests/debug/Makefile.am
@@ -0,0 +1,4 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = debug_test
+
+debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c
diff --git a/openbsc/tests/debug/debug_test.c b/openbsc/tests/debug/debug_test.c
new file mode 100644
index 000000000..77ac01532
--- /dev/null
+++ b/openbsc/tests/debug/debug_test.c
@@ -0,0 +1,34 @@
+/* simple test for the debug interface */
+/*
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/debug.h>
+
+
+int main(int argc, char** argv)
+{
+ debug_parse_category_mask("DRLL");
+ DEBUGP(DCC, "You should not see this\n");
+
+ debug_parse_category_mask("DRLL:DCC");
+ DEBUGP(DRLL, "You should see this\n");
+ DEBUGP(DCC, "You should see this\n");
+ DEBUGP(DMM, "You should not see this\n");
+}
diff --git a/openbsc/tests/gsm0408/Makefile.am b/openbsc/tests/gsm0408/Makefile.am
new file mode 100644
index 000000000..51463dcbf
--- /dev/null
+++ b/openbsc/tests/gsm0408/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = gsm0408_test
+
+gsm0408_test_SOURCES = gsm0408_test.c
+gsm0408_test_LDADD = $(top_builddir)/src/libbsc.a -ldbi
diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/openbsc/tests/gsm0408/gsm0408_test.c
new file mode 100644
index 000000000..c99766a72
--- /dev/null
+++ b/openbsc/tests/gsm0408/gsm0408_test.c
@@ -0,0 +1,72 @@
+/* simple test for the gsm0408 formatting functions */
+/*
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <openbsc/gsm_04_08.h>
+
+#define COMPARE(result, op, value) \
+ if (!((result) op (value))) {\
+ fprintf(stderr, "Compare failed. Was %x should be %x in %s:%d\n",result, value, __FILE__, __LINE__); \
+ exit(-1); \
+ }
+
+
+/*
+ * Test Location Area Identifier formatting. Table 10.5.3 of 04.08
+ */
+static void test_location_area_identifier(void)
+{
+ struct gsm48_loc_area_id lai48;
+
+ printf("Testing test location area identifier\n");
+
+ /*
+ * Test the default/test setup. Coming from
+ * bsc_hack.c dumps
+ */
+ gsm0408_generate_lai(&lai48, 1, 1, 1);
+ COMPARE(lai48.digits[0], ==, 0x00);
+ COMPARE(lai48.digits[1], ==, 0xF1);
+ COMPARE(lai48.digits[2], ==, 0x10);
+ COMPARE(lai48.lac, ==, htons(0x0001));
+
+ gsm0408_generate_lai(&lai48, 602, 1, 15);
+ COMPARE(lai48.digits[0], ==, 0x06);
+ COMPARE(lai48.digits[1], ==, 0xF2);
+ COMPARE(lai48.digits[2], ==, 0x10);
+ COMPARE(lai48.lac, ==, htons(0x000f));
+}
+
+int main(int argc, char** argv)
+{
+ test_location_area_identifier();
+}
+
+
+
+/*
+ * Stubs to compile and link
+ */
+void input_event(void) {}
+void nm_state_event(void) {}
diff --git a/openbsc/tests/sms.txt b/openbsc/tests/sms.txt
new file mode 100644
index 000000000..06c885b8b
--- /dev/null
+++ b/openbsc/tests/sms.txt
@@ -0,0 +1,50 @@
+03 02 01 0a 02 43 0b 00 1d 39 01 1a 00 01 00 07 91 55 11 18 31 28 00 0e 31 20 04 81 21 43 00 00 ff 04 d4 f2 9c 0e
+
+03 02 01 0a 02 43 0b 00 1d -
+39 - TransactionID 3, SMS messages :: gh->proto_discr
+01 - CP-DATA :: gh->msg_type
+1a - Length: 26 :: gh->data[0]
+00 - MTI 0 RP-DATA (ms->n)
+01 - MR 1
+00 - RP-OA
+07 - RP-DA (SMSC Length)
+91 - International No, Numbering Plan
+55 11 18 31 28 00 - 551181138200
+0e - RP-UD len (14)
+
+TPDU (14 byte):
+31 - MTI(01), VPF(2:relative), MMS(0), SRI(1), UDHI(0), RP(0)
+20 - Message Reference
+04 - DA length
+81 - Numbering Plan, National number
+21 43 - DA 1234
+00 - PID
+00 - DCS
+ff - Validity period
+04 - User Data length (04)
+d4 f2 9c 0e - gsm_default 7bit encoded "Test" (4 byte)
+
+03 02 01 0a 02 43 0b 00 9f 09 01 9c 00 da 00 07 91 88 96 13 00 00 99 90 11 7b 04 81 22 22 00 08 ff 86 6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d
+
+03 02 01 0a 02 43 0b 00 9f - lower levels
+09 - TransactionID 0, SMS messages
+01 - CP-DATA
+9c - Length: (156)
+00 - MTI 0 RP-DATA (ms->n)
+da - MR (?)
+00 - RP-OA
+07 - RP-DA (SMSC Length)
+91 - International No.
+88 96 13 00 00 99
+90 - RP-UD len (144)
+11 -
+7b - Message Reference
+04 - DA length
+81 - Numbering Plan
+22 22 - Address 2222
+00 - PID
+08 - DCS (UCS2 charset)
+ff - Validity period
+86 - User Data length (134)
+6c 38 8c 50 92 80 88 4c 00 4d 00 4d 00 41 6a 19 67 03 74 06 8c a1 7d b2 00 20 00 20 51 68 74 03 99 96 52 75 7d b2 8d ef 6a 19 67 03 ff 0c 6a 19 67 03 96 f6 98 a8 96 aa ff 01 8b 93 60 a8 80 70 66 0e 51 32 84 c4 ff 0c 97 48 6d 3b 62 95 8c c7 ff 01 73 fe 57 28 52 a0 51 65 90 01 96 50 91 cf 59 27 80 6f 76 df 6d 0b 57 fa 96 8a 91 77 5e 63 53 61 ff 0c 8a cb 4e 0a 7d b2 64 1c 5c 0b 30 0c 6a 19 67 03 30 0d
+
diff --git a/openbsc/tests/sms/Makefile.am b/openbsc/tests/sms/Makefile.am
new file mode 100644
index 000000000..23df8717b
--- /dev/null
+++ b/openbsc/tests/sms/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = sms_test
+
+sms_test_SOURCES = sms_test.c
+sms_test_LDADD = $(top_builddir)/src/libbsc.a -ldl -ldbi
diff --git a/openbsc/tests/sms/sms_test.c b/openbsc/tests/sms/sms_test.c
new file mode 100644
index 000000000..dfc43cf70
--- /dev/null
+++ b/openbsc/tests/sms/sms_test.c
@@ -0,0 +1,110 @@
+/*
+ * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <openbsc/debug.h>
+#include <openbsc/msgb.h>
+#include <openbsc/gsm_04_11.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_utils.h>
+
+/* SMS data from MS starting with layer 3 header */
+static u_int8_t sms1[] = {
+ 0x39, 0x01, 0x1a, 0x00, 0x01, 0x00, 0x07, 0x91, 0x55, 0x11,
+ 0x18, 0x31, 0x28, 0x00, 0x0e, 0x31, 0x20, 0x04, 0x81, 0x21,
+ 0x43, 0x00, 0x00, 0xff, 0x04, 0xd4, 0xf2, 0x9c, 0x0e
+};
+
+static u_int8_t sms2[] = {
+ 0x09, 0x01, 0x9c, 0x00, 0xda, 0x00, 0x07, 0x91, 0x88, 0x96, 0x13,
+ 0x00, 0x00, 0x99, 0x90, 0x11, 0x7b, 0x04, 0x81, 0x22, 0x22, 0x00,
+ 0x08, 0xff, 0x86, 0x6c, 0x38, 0x8c, 0x50, 0x92, 0x80, 0x88, 0x4c,
+ 0x00, 0x4d, 0x00, 0x4d, 0x00, 0x41, 0x6a, 0x19, 0x67, 0x03, 0x74,
+ 0x06, 0x8c, 0xa1, 0x7d, 0xb2, 0x00, 0x20, 0x00, 0x20, 0x51, 0x68,
+ 0x74, 0x03, 0x99, 0x96, 0x52, 0x75, 0x7d, 0xb2, 0x8d, 0xef, 0x6a,
+ 0x19, 0x67, 0x03, 0xff, 0x0c, 0x6a, 0x19, 0x67, 0x03, 0x96, 0xf6,
+ 0x98, 0xa8, 0x96, 0xaa, 0xff, 0x01, 0x8b, 0x93, 0x60, 0xa8, 0x80,
+ 0x70, 0x66, 0x0e, 0x51, 0x32, 0x84, 0xc4, 0xff, 0x0c, 0x97, 0x48,
+ 0x6d, 0x3b, 0x62, 0x95, 0x8c, 0xc7, 0xff, 0x01, 0x73, 0xfe, 0x57,
+ 0x28, 0x52, 0xa0, 0x51, 0x65, 0x90, 0x01, 0x96, 0x50, 0x91, 0xcf,
+ 0x59, 0x27, 0x80, 0x6f, 0x76, 0xdf, 0x6d, 0x0b, 0x57, 0xfa, 0x96,
+ 0x8a, 0x91, 0x77, 0x5e, 0x63, 0x53, 0x61, 0xff, 0x0c, 0x8a, 0xcb,
+ 0x4e, 0x0a, 0x7d, 0xb2, 0x64, 0x1c, 0x5c, 0x0b, 0x30, 0x0c, 0x6a,
+ 0x19, 0x67, 0x03, 0x30, 0x0d
+};
+
+struct sms_datum {
+ u_int8_t len;
+ u_int8_t *data;
+};
+
+static struct sms_datum sms_data[] = {
+ {
+ .len = sizeof(sms1),
+ .data = sms1,
+ }, {
+ .len = sizeof(sms2),
+ .data = sms2,
+ }
+};
+
+#define SMS_NUM (sizeof(sms_data)/sizeof(sms_data[0]))
+
+int main(int argc, char** argv)
+{
+ DEBUGP(DSMS, "SMS testing\n");
+ struct msgb *msg;
+ u_int8_t *sms;
+ u_int8_t i;
+
+ /* test 7-bit coding/decoding */
+ const char *input = "test text";
+ u_int8_t length;
+ u_int8_t coded[256];
+ char result[256];
+
+ length = gsm_7bit_encode(coded, input);
+ gsm_7bit_decode(result, coded, length);
+ if (strcmp(result, input) != 0) {
+ printf("7 Bit coding failed... life sucks\n");
+ printf("Wanted: '%s' got '%s'\n", input, result);
+ }
+
+ for(i=0;i<SMS_NUM;i++) {
+ /* Setup SMS msgb */
+ msg = msgb_alloc(sms_data[i].len);
+ sms = msgb_put(msg, sms_data[i].len);
+
+ memcpy(sms, sms_data[i].data, sms_data[i].len);
+ msg->l3h = sms;
+
+ gsm0411_rcv_sms(msg);
+ msgb_free(msg);
+ }
+
+ gsm0411_send_sms(0, 0);
+}
+
+/* stubs */
+void input_event(void) {}
+void nm_state_event(void) {}
diff --git a/openbsc/tests/timer/Makefile.am b/openbsc/tests/timer/Makefile.am
new file mode 100644
index 000000000..9f12d23ac
--- /dev/null
+++ b/openbsc/tests/timer/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include
+noinst_PROGRAMS = timer_test
+
+timer_test_SOURCES = timer_test.c $(top_srcdir)/src/timer.c $(top_srcdir)/src/select.c
+
diff --git a/openbsc/tests/timer/timer_test.c b/openbsc/tests/timer/timer_test.c
new file mode 100644
index 000000000..26fcbc938
--- /dev/null
+++ b/openbsc/tests/timer/timer_test.c
@@ -0,0 +1,70 @@
+/*
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdio.h>
+
+#include <openbsc/timer.h>
+#include <openbsc/select.h>
+
+static void timer_fired(unsigned long data);
+
+static struct timer_list timer_one = {
+ .cb = timer_fired,
+ .data = (void*)1,
+};
+
+static struct timer_list timer_two = {
+ .cb = timer_fired,
+ .data = (void*)2,
+};
+
+static struct timer_list timer_three = {
+ .cb = timer_fired,
+ .data = (void*)3,
+};
+
+static void timer_fired(unsigned long data)
+{
+ printf("Fired timer: %lu\n", data);
+
+ if (data == 1) {
+ bsc_schedule_timer(&timer_one, 3, 0);
+ bsc_del_timer(&timer_two);
+ } else if (data == 2) {
+ printf("Should not be fired... bug in del_timer\n");
+ } else if (data == 3) {
+ printf("Timer fired not registering again\n");
+ } else {
+ printf("wtf... wrong data\n");
+ }
+}
+
+int main(int argc, char** argv)
+{
+ printf("Starting... timer\n");
+
+ bsc_schedule_timer(&timer_one, 3, 0);
+ bsc_schedule_timer(&timer_two, 5, 0);
+ bsc_schedule_timer(&timer_three, 4, 0);
+
+ while (1) {
+ bsc_select_main(0);
+ }
+}
diff --git a/openbsc/tools/hlrstat.pl b/openbsc/tools/hlrstat.pl
new file mode 100755
index 000000000..a3fd2b81e
--- /dev/null
+++ b/openbsc/tools/hlrstat.pl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+use strict;
+use DBI;
+my $dbh = DBI->connect("dbi:SQLite:dbname=hlr.sqlite3","","");
+
+
+my %mcc_names;
+my %mcc_mnc_names;
+
+sub read_networks($)
+{
+ my $filename = shift;
+ my $cur_name;
+
+ open(INFILE, $filename);
+ while (my $l = <INFILE>) {
+ chomp($l);
+ if ($l =~ /^#/) {
+ next;
+ }
+ if ($l =~ /^\t/) {
+ my ($mcc, $mnc, $brand, $r) = split(' ', $l, 4);
+ #printf("%s|%s|%s\n", $mcc, $mnc, $brand);
+ $mcc_mnc_names{"$mcc-$mnc"} = $brand;
+ $mcc_names{$mcc} = $cur_name;
+ } elsif ($l =~ /^(\w\w)\t(.*)/) {
+ #printf("%s|%s\n", $1, $2);
+ $cur_name = $2;
+ }
+ }
+ close(INFILE);
+}
+
+read_networks("networks.tab");
+
+my %oper_count;
+my %country_count;
+
+my $sth = $dbh->prepare("SELECT imsi FROM subscriber");
+
+$sth->execute();
+
+while (my $href = $sth->fetchrow_hashref) {
+ my ($mcc, $mnc) = $$href{imsi} =~ /(\d{3})(\d{2}).*/;
+ #printf("%s %s-%s \n", $$href{imsi}, $mcc, $mnc);
+ $oper_count{"$mcc-$mnc"}++;
+ $country_count{$mcc}++;
+}
+
+
+foreach my $c (keys %country_count) {
+ printf("%s: %d\n", $mcc_names{$c}, $country_count{$c});
+}
+ foreach my $k (keys %oper_count) {
+ printf("\t%s: %d\n", $mcc_mnc_names{$k}, $oper_count{$k});
+ }
+#//}