diff options
Diffstat (limited to 'src')
328 files changed, 58434 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..5151bfb5 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,90 @@ + +# this is not really used as we don't do 'make install'. You can still specify +# it in case you _want_ to manually 'make install' the target libosmocore. +CROSS_INST_PREFIX=/usr/local/stow/osmocom-bb/arm-elf + +# this is the prefix of your cross-toolchain programs +CROSS_TOOL_PREFIX=arm-elf- + +TOPDIR=$(shell pwd) +OSMOCORE_CONFIGURE_ENV= LIBOSMOCORE_LIBS=$(TOPDIR)/shared/libosmocore/build-host/src/.libs/libosmocore.a \ + LIBOSMOVTY_LIBS=$(TOPDIR)/shared/libosmocore/build-host/src/vty/.libs/libosmovty.a \ + LIBOSMOCORE_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include \ + LIBOSMOVTY_CFLAGS=-I$(TOPDIR)/shared/libosmocore/include + +all: libosmocore-host libosmocore-target layer23 osmocon firmware + +libosmocore-host: shared/libosmocore/build-host/src/.libs/libosmocore.la + +shared/libosmocore/build-host: + mkdir $@ + +shared/libosmocore/configure: shared/libosmocore/configure.in + cd shared/libosmocore && autoreconf -i + +shared/libosmocore/build-host/Makefile: shared/libosmocore/configure shared/libosmocore/build-host + cd shared/libosmocore/build-host && ../configure + +shared/libosmocore/build-host/src/.libs/libosmocore.la: shared/libosmocore/build-host/Makefile + cd shared/libosmocore/build-host && make + + +libosmocore-target: shared/libosmocore/build-target/src/.libs/libosmocore.a + +shared/libosmocore/build-target: + mkdir $@ + +shared/libosmocore/build-target/Makefile: shared/libosmocore/configure shared/libosmocore/build-target + cd shared/libosmocore/build-target && ../configure \ + --host=arm-elf-linux --disable-vty \ + --disable-shared --disable-talloc --disable-tests \ + CC="$(CROSS_TOOL_PREFIX)gcc" CFLAGS="-Os -ffunction-sections -I../../../../target/firmware/include" + +shared/libosmocore/build-target/src/.libs/libosmocore.a: shared/libosmocore/build-target/Makefile + cd shared/libosmocore/build-target && make + + +.PHONY: osmocon +osmocon: host/osmocon/osmocon + +host/osmocon/configure: host/osmocon/configure.ac + cd host/osmocon && autoreconf -i + +host/osmocon/Makefile: host/osmocon/configure + cd host/osmocon && $(OSMOCORE_CONFIGURE_ENV) ./configure + +host/osmocon/osmocon: host/osmocon/Makefile libosmocore-host + make -C host/osmocon + + +.PHONY: layer23 +layer23: host/layer23/layer23 + +host/layer23/configure: host/layer23/configure.ac + cd host/layer23 && autoreconf -i + +host/layer23/Makefile: host/layer23/configure + cd host/layer23 && $(OSMOCORE_CONFIGURE_ENV) ./configure + +host/layer23/layer23: host/layer23/Makefile libosmocore-host + make -C host/layer23 + + +.PHONY: firmware +firmware: libosmocore-target + make -C target/firmware CROSS_COMPILE=$(CROSS_TOOL_PREFIX) + + +clean: + make -C shared/libosmocore/build-host $@ + make -C shared/libosmocore/build-target $@ + make -C host/layer23 $@ + make -C host/osmocon $@ + make -C target/firmware $@ + +distclean: + rm -rf shared/libosmocore/build-host + rm -rf shared/libosmocore/build-target + make -C host/layer23 $@ + make -C host/osmocon $@ + make -C target/firmware $@ diff --git a/src/README.building b/src/README.building new file mode 100644 index 00000000..32d03c95 --- /dev/null +++ b/src/README.building @@ -0,0 +1,20 @@ +== How to build OsmocomBB? == + +=== Prerequisites === + +We assume you are building on a GNU/Linux host system such as Debian +GNU/Linux. Windows builds have been reported successfully using the +Cygwin environment, but we do not officially support this. + + # Get a GNU toolchain (gcc/binutils) for ARM (e.g. from http://gnuarm.com/) + # Set your path to include the arm-elf-* executables of your toolchain + # call 'make' in this (the src) subdirectory + +=== Details === + +The master Makefile will build + + * libosmocore for the host (x86 or whatever you use) + * libosmocore for the target (ARM) + * osmocon and layer23 executables for the host (linking libosmocore) + * the actual target firmware images (in src/target/firmware/board/*/*.bin) diff --git a/src/README.development b/src/README.development new file mode 100644 index 00000000..393b0bd9 --- /dev/null +++ b/src/README.development @@ -0,0 +1,57 @@ += Contributing to OsmocomBB development = + +Feel free to help us by extending the code. Always make sure to +send back all your patches to the baseband-devel@lists.osmocom.org +mailing list - Free Software is all about sharing. + +== src/shared/libosmocore == + +is a library of various utility routines, including linked lists, +message buffers, bit-vectors, memory allocator, signals, select loop +handling, timers - as well as some more specifically GSM related things +like a TLV parser, a Comp128V1 implementation and utility functions for +RSL (TS 08.58) and CC/MM/RR (TS 04.08). + +libosmocore is maintained in git://git.osmocom.org/libosmocore.git, so + + DO NOT DIRECTLY COMMIT TO libosmocore IN THIS REPOSITORY! + +We simply maintain a copy (synchronized by git-subtree) in this +repository for the ease of building and to make sure everyone is using +the proper/compatible version of libosmocore + +Please note, whatever you add to libosmocore will need to build as a +Linux userspace program (using glibc) just as well as on the OsmocomBB +embedded target without OS. So please refrain from using fancy +functions. + + +== src/target/firmware == + +The firmware is what we build for the actual target (phone). It was +written with some idea of modularity in mind, i.e. we have + + * Ti Calypso specific code in calypso/ + * Analog Baseband code in abb/ + * RF Mixer code in rf/ + * Layer1 code in layer1/ + * NOR flash handling in flash/ + * LCD display handlin in display/ + * minimal C-Library code in lib/ + * communications utility routines in comm/ + * Board (phone model/family) specific code in board/ + * board/compal_e88 is the Motorola C115-C124 family + * board/compal_e99 is the Motorola C155 family + * Applications (each app builds one firmware image) in apps/ + + +== src/target_dsp/calypso == + +This is where we keep some (assembly) code that we wrote for +the DSP that is part of the Caylypso DBB. + +== host/layer23 == + +The Layer2 (LAPDm / TS 04.06) and Layer3 (CC/MM/RR / 04.08) +implementations, as they are growing. + diff --git a/src/host/calypso_pll/pll.pl b/src/host/calypso_pll/pll.pl new file mode 100755 index 00000000..52c91319 --- /dev/null +++ b/src/host/calypso_pll/pll.pl @@ -0,0 +1,10 @@ +#!/usr/bin/perl + +my $f_in = 26*1000*1000; + +for (my $mult = 1; $mult < 31; $mult++) { + for (my $div = 0; $div < 3; $div++) { + my $fout = $f_in * ($mult / ($div+1)); + printf("%03.1f MHz (mult=%2u, div=%1u)\n", $fout/(1000*1000), $mult, $div); + } +} diff --git a/src/host/gsm48-andreas/issues.txt b/src/host/gsm48-andreas/issues.txt new file mode 100644 index 00000000..ba7bb827 --- /dev/null +++ b/src/host/gsm48-andreas/issues.txt @@ -0,0 +1,99 @@ +Location updating procedure: + +When SIM is "NOT UPDATED", there is no valid LAI in SIM. LOCATION UPDATING +REQUEST message requires LAI. How is an IMSI attach possible, if no LAI +exists? +We have three conditions: +- LAI in SIM is valid +- LAI in SIM exists (last stored) but marked invalid. +- No LAI in SIM (brand new one) +See Table 10.5.3 TS 04.08 and try to understand the riddle... + + +OpenBSC: +If tx_setup fails during process, the msg must be freed to avoid memory leak. + + +OpenBSC: +Must use *_LOC_PRN_S_LU on protocol side. +Or it uses *_LOC_PUN_S_LU to hide private network type (maybe some cfg option) + + +LCR: +Also LCR must use correct location. +For MS support, it must use *_LOC_USER + + +Measurements: +How do we send MEASUREMENT RESULTS to RSL? (maybe RSL_MT_MEAS_RES) +what triggers the sending? Or do we just send it from time to time to layer 1 +where it is stored and sent when the time is right? (Then we might get +something like RSL_MT_MEAS_CNF, so we can send the next one, if we have new +measurements available, otherwhise the L1 will use the old measurement results +and resends them without confirming it.) + + +RACH: +I need some RACH primitives like: +- RACH request (tx_ph_rach_req already exists) including value and offset of + RACH slot from now. +- RACH confirm, the RACK has been sent (with timeslot where is was sent) +- RACH cancel request: + Since RACH request must be queued in layer 1 until it the right slot is + reached, I need to tell L1 to cancel it. Whenever I receive a confirm, I + schedule the next one until the maximum number has been transmitted or until + an IMMEDIATE ASSIGNMENT is received on CCCH. +Any better idea? Read GSM 04.08 clause 3.3.1.1.2 and 3.3.1.1.3 for more info. + + +RACH request on RSL: +Does it make sense to put RACH request to the set of RSLms primitives? +(i.e. RSL_MT_CHAN_REQ, not RQD!, RSL_MT_CHAN_CNF, RSL_MT_CHAN_CAN) +What do you say? + + +I need to change storage of system informations when searching other cells. +I prefer two modes: +- collecting all SI on serving cell, collecting only relevant data of other cell +- collecting all SI on serving cell and other cells, storing them for each + frequency (netmonitor) + +How do I (radio ressource) know about the channel layout on BCCH? This info is +requred to select correct delays and timings. +-> i think i got it from system informations^ + + +How do I return after L1CTL_DM_EST_REQ back to idle mode? +Using l1ctl_tx_ccch_req? + + +mncc.h of openbsc / layer23: +What about putting all (except call structure) to osmocore? + + +bsic??????? + + +How do I stop scanning frequencies? +The scanning process may currently restart scanning, when process is running. + + +It is absolutely importaint for messages not beeing lost between layers. +What about the serial link between mobile and PC? +-> There must be a security layer! + +One solution: Store all radio action (DM / CCCH / power scan) and start timer. +Send request again, if timer expires. +-> Also send it again, if L1 resets (restart of phone). +We MUST at least have CRCxx on serial interface. + + +power measurements sometimes does not return all measurements. it stops, +especially if a ccch was requested before. this is not good. power measurement +must stop all other layer 1 processes! + + + + + + diff --git a/src/host/layer23/.gitignore b/src/host/layer23/.gitignore new file mode 100644 index 00000000..d3594665 --- /dev/null +++ b/src/host/layer23/.gitignore @@ -0,0 +1,14 @@ +Makefile +Makefile.in +aclocal.m4 +configure +missing +*.o +depcomp +config.* +*.sw? +*.deps +layer2 +install-sh +autom4te.cache +*.a diff --git a/src/host/layer23/COPYING b/src/host/layer23/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/host/layer23/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/src/host/layer23/Makefile.am b/src/host/layer23/Makefile.am new file mode 100644 index 00000000..bc3910fa --- /dev/null +++ b/src/host/layer23/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +SUBDIRS = include src diff --git a/src/host/layer23/README b/src/host/layer23/README new file mode 100644 index 00000000..dd598234 --- /dev/null +++ b/src/host/layer23/README @@ -0,0 +1,42 @@ += OsmocomBB layer23 architecture = + +layer23 is an (incomplete) MS-side implementation of the L2 and L3 GSM +protocols as described in GSM TS 04.06, 04.08 and others. + +== Interfaces == + +L1 (on the phone) uses the L1CTL protocol to talk with layer23 (on the PC). + +L2 (inside layer23) uses the RSLms protocol to talk with the L3 (inside layer23) + + +=== RSLms === + +RSLms is modeled after the GSM TS 08.58 Radio Subsystem Link protocol. Despite +being designed for the network side, RSL seems a good match for the L2/L3 +interface inside a MS, too. + +At least the RLL (Radio Link Layer) part of RSL is 100% as applicable to the MS +side as it is for the ntwork side. + +==== Lower interface (L2 to RSLms) ==== + +Layer2 calls rslms_sendmsg() with a msgb that has the msgb->l2h pointing to a +RSL header (struct abis_rsl_common_hdr). + +==== Upper interface (L3 to RSLms) ==== + +Layer3 calls rslms_recvmsg() with a msgb that has the msgb->l2h pointing to a +RSL header (struct abis_rsl_common_hdr). + +There are utility functions like rslms_tx_rll_req() and rslms_tx_rsll_req_l3() +for creating msgb's with the apropriate RSL/RLL headers. + + +=== LAPDm === + +LAPDm is the GSM TS 04.06 protocol + +The lower interface (to L1) is using L1CTL + +The upper interface (to L3) is using RSLms diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac new file mode 100644 index 00000000..036161c0 --- /dev/null +++ b/src/host/layer23/configure.ac @@ -0,0 +1,28 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT + +AM_INIT_AUTOMAKE(layer23, 0.0.0) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_RANLIB + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + src/Makefile + include/Makefile + include/osmocom/Makefile + Makefile) diff --git a/src/host/layer23/include/Makefile.am b/src/host/layer23/include/Makefile.am new file mode 100644 index 00000000..ca774b6b --- /dev/null +++ b/src/host/layer23/include/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = l1a_l23_interface.h +SUBDIRS = osmocom diff --git a/src/host/layer23/include/l1a_l23_interface.h b/src/host/layer23/include/l1a_l23_interface.h new file mode 120000 index 00000000..2bbc9679 --- /dev/null +++ b/src/host/layer23/include/l1a_l23_interface.h @@ -0,0 +1 @@ +../../../../include/l1a_l23_interface.h
\ No newline at end of file diff --git a/src/host/layer23/include/osmocom/Makefile.am b/src/host/layer23/include/osmocom/Makefile.am new file mode 100644 index 00000000..86537aa5 --- /dev/null +++ b/src/host/layer23/include/osmocom/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = l1ctl.h osmocom_data.h lapdm.h rslms.h layer3.h \ + gsmtap_util.h logging.h diff --git a/src/host/layer23/include/osmocom/file.h b/src/host/layer23/include/osmocom/file.h new file mode 100644 index 00000000..249064d9 --- /dev/null +++ b/src/host/layer23/include/osmocom/file.h @@ -0,0 +1,80 @@ +#ifndef _OSMOCOM_FILE_H +#define _OSMOCOM_FILE_H + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#define OSMOCOM_CONFDIR "/etc/osmocom/" +#define OSMOCOM_PERM 0644 +#define OSMOCOM_FILE FILE +static inline OSMOCOM_FILE *osmocom_fopen(const char *name, const char *mode) +{ + char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1]; + + if (mode[0] != 'r') { + int rc; + + rc = mkdir(OSMOCOM_CONFDIR, OSMOCOM_PERM); + if (rc < 1 && errno != EEXIST) + return NULL; + } + + strcpy(filename, OSMOCOM_CONFDIR); + strcat(filename, name); + + return fopen(filename, mode); +} +#define osmocom_fread fread +#define osmocom_fgets fgets +#define osmocom_fwrite fwrite +#define osmocom_feof feof +#define osmocom_fclose fclose +static inline int OSMOCOM_UNLINK(const char *name) +{ + char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1]; + + strcpy(filename, OSMOCOM_CONFDIR); + strcat(filename, name); + + return unlink(filename); +} +static inline int OSMOCOM_LINK(const char *oldname, const char *newname) +{ + char oldfilename[strlen(OSMOCOM_CONFDIR) + strlen(oldname) + 1]; + char newfilename[strlen(OSMOCOM_CONFDIR) + strlen(newname) + 1]; + + strcpy(oldfilename, OSMOCOM_CONFDIR); + strcat(oldfilename, oldname); + strcpy(newfilename, OSMOCOM_CONFDIR); + strcat(newfilename, newname); + return link(oldfilename, newfilename); +} +static inline int OSMOCOM_MKSTEMP(char *name) +{ + char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1]; + int rc; + + strcpy(filename, OSMOCOM_CONFDIR); + strcat(filename, name); + + rc = mkstemp(filename); + + memcpy(name + strlen(name) - 6, filename + strlen(filename) - 6, 6); + + return rc; +} +static inline int OSMOCOM_CHMOD(const char *name, mode_t mode) +{ + char filename[strlen(OSMOCOM_CONFDIR) + strlen(name) + 1]; + + strcpy(filename, OSMOCOM_CONFDIR); + strcat(filename, name); + + return chmod(filename, mode); +} + +#endif /* _OSMOCOM_FILE_H */ diff --git a/src/host/layer23/include/osmocom/gsm322.h b/src/host/layer23/include/osmocom/gsm322.h new file mode 100755 index 00000000..d9cd2261 --- /dev/null +++ b/src/host/layer23/include/osmocom/gsm322.h @@ -0,0 +1,199 @@ +#ifndef _GSM322_H +#define _GSM322_H + +/* 4.3.1.1 List of states for PLMN slection process (automatic mode) */ +#define GSM322_A0_NULL 0 +#define GSM322_A1_TRYING_RPLMN 1 +#define GSM322_A2_ON_PLMN 2 +#define GSM322_A3_TRYING_PLMN 3 +#define GSM322_A4_WAIT_FOR_PLMN 4 +#define GSM322_A5_HPLMN_SEARCH 5 +#define GSM322_A6_NO_SIM 6 + +/* 4.3.1.2 List of states for PLMN slection process (manual mode) */ +#define GSM322_M0_NULL 0 +#define GSM322_M1_TRYING_RPLMN 1 +#define GSM322_M2_ON_PLMN 2 +#define GSM322_M3_NOT_ON_PLMN 3 +#define GSM322_M4_TRYING_PLMN 4 +#define GSM322_M5_NO_SIM 5 + +/* 4.3.2 List of states for cell selection process */ +#define GSM322_C0_NULL 0 +#define GSM322_C1_NORMAL_CELL_SEL 1 +#define GSM322_C2_STORED_CELL_SEL 2 +#define GSM322_C3_CAMPED_NORMALLY 3 +#define GSM322_C4_NORMAL_CELL_RESEL 4 +#define GSM322_C5_CHOOSE_CELL 5 +#define GSM322_C6_ANY_CELL_SEL 6 +#define GSM322_C7_CAMPED_ANY_CELL 7 +#define GSM322_C8_ANY_CELL_RESEL 8 +#define GSM322_C9_CHOOSE_ANY_CELL 9 +#define GSM322_PLMN_SEARCH 10 +#define GSM322_HPLMN_SEARCH 11 + +/* GSM 03.22 events */ +#define GSM322_EVENT_SWITCH_ON 1 +#define GSM322_EVENT_SWITCH_OFF 2 +#define GSM322_EVENT_SIM_INSERT 3 +#define GSM322_EVENT_SIM_REMOVE 4 +#define GSM322_EVENT_REG_SUCCESS 5 +#define GSM322_EVENT_REG_FAILED 6 +#define GSM322_EVENT_ROAMING_NA 7 +#define GSM322_EVENT_INVALID_SIM 8 +#define GSM322_EVENT_NEW_PLMN 9 +#define GSM322_EVENT_ON_PLMN 10 +#define GSM322_EVENT_PLMN_SEARCH_START 11 +#define GSM322_EVENT_PLMN_SEARCH_END 12 +#define GSM322_EVENT_USER_RESEL 13 +#define GSM322_EVENT_PLMN_AVAIL 14 +#define GSM322_EVENT_CHOSE_PLMN 15 +#define GSM322_EVENT_SEL_MANUAL 16 +#define GSM322_EVENT_SEL_AUTO 17 +#define GSM322_EVENT_CELL_FOUND 18 +#define GSM322_EVENT_NO_CELL_FOUND 19 +#define GSM322_EVENT_LEAVE_IDLE 20 +#define GSM322_EVENT_RET_IDLE 21 +#define GSM322_EVENT_CELL_RESEL 22 +#define GSM322_EVENT_SYSINFO 23 +#define GSM322_EVENT_HPLMN_SEARCH 24 + +enum { + PLMN_MODE_MANUAL, + PLMN_MODE_AUTO +}; + +/* node for each PLMN */ +struct gsm322_plmn_list { + struct llist_head entry; + uint16_t mcc, mnc; + int8_t rxlev_db; /* rx level in real dB */ + uint8_t cause; /* cause value, if PLMN is not allowed */ +}; + +/* node for each forbidden LA */ +struct gsm322_la_list { + struct llist_head entry; + uint16_t mcc, mnc, lac; + uint8_t cause; +}; + +/* node for each BA-List */ +struct gsm322_ba_list { + struct llist_head entry; + uint16_t mcc, mnc; + /* Band allocation for 1024 frequencies. + * First bit of first index is frequency 0. + */ + uint8_t freq[128]; +}; + +#define GSM322_CS_FLAG_SUPPORT 0x01 /* frequency is supported by radio */ +#define GSM322_CS_FLAG_BA 0x02 /* frequency is part of the current ba */ +#define GSM322_CS_FLAG_POWER 0x04 /* frequency was power scanned */ +#define GSM322_CS_FLAG_SIGNAL 0x08 /* valid signal detected */ +#define GSM322_CS_FLAG_SYSINFO 0x10 /* complete sysinfo received */ +#define GSM322_CS_FLAG_BARRED 0x20 /* cell is barred */ +#define GSM322_CS_FLAG_FORBIDD 0x40 /* cell in list of forbidden LAs */ +#define GSM322_CS_FLAG_TEMP_AA 0x80 /* if temporary available and allowable */ + +/* Cell selection list */ +struct gsm322_cs_list { + uint8_t flags; /* see GSM322_CS_FLAG_* */ + int8_t rxlev_db; /* rx level in real dB */ + struct gsm48_sysinfo *sysinfo; +#if 0 + int8_t min_db; /* minimum level to enter cell */ + int8_t max_pwr; /* maximum power to access cell */ + uint16_t class_barr; /* barred classes */ + uint16_t mcc, mnc, lac; /* received mcc, mnc, lac */ +#endif +}; + +/* PLMN search process */ +struct gsm322_plmn { + struct osmocom_ms *ms; + int state; /* GSM322_Ax_* or GSM322_Mx_* */ + + struct llist_head event_queue; /* event messages */ + struct llist_head sorted_plmn; /* list of sorted PLMN */ + struct llist_head forbidden_la; /* forbidden LAs */ + + struct timer_list timer; + + int plmn_curr; /* current index in sorted_plmn */ + uint16_t mcc, mnc; /* current network selected */ +}; + +/* state of CCCH activation */ +#define GSM322_CCCH_ST_IDLE 0 /* no connection */ +#define GSM322_CCCH_ST_INIT 1 /* initalized */ +#define GSM322_CCCH_ST_SYNC 2 /* got sync */ +#define GSM322_CCCH_ST_DATA 3 /* receiveing data */ + +struct gsm48_sysinfo; +/* Cell selection process */ +struct gsm322_cellsel { + struct osmocom_ms *ms; + int state; /* GSM322_Cx_* */ + + struct llist_head event_queue; /* event messages */ + struct llist_head ba_list; /* BCCH Allocation per PLMN */ + + struct timer_list timer; + + uint16_t mcc, mnc; /* current network to search for */ + struct gsm322_cs_list list[1024]; /* cell selection list per freq. */ + + uint8_t powerscan; /* currently scanning for power */ + uint32_t scan_state; /* special state of current scan */ + uint8_t ccch_state; /* special state of current ccch */ + uint16_t arfcn; /* current tuned idle mode arfcn */ + uint8_t ccch_mode; /* curren CCCH_MODE_* */ + struct gsm48_sysinfo *si; /* current sysinfo */ + + uint8_t selected; /* if a cell is selected */ + uint16_t sel_arfcn; + struct gsm48_sysinfo sel_si; /* copy of selected cell, will update */ + uint16_t sel_mcc, sel_mnc, sel_lac, sel_id; +}; + +/* GSM 03.22 message */ +struct gsm322_msg { + int msg_type; + uint16_t mcc, mnc; + uint8_t sysinfo; /* system information type */ + uint8_t reject; /* location update reject cause */ +}; + +#define GSM322_ALLOC_SIZE sizeof(struct gsm322_msg) +#define GSM322_ALLOC_HEADROOM 0 + +int gsm322_init(struct osmocom_ms *ms); +int gsm322_exit(struct osmocom_ms *ms); +struct msgb *gsm322_msgb_alloc(int msg_type); +int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg); +int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg); +int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg); +int gsm322_plmn_dequeue(struct osmocom_ms *ms); +int gsm322_cs_dequeue(struct osmocom_ms *ms); +int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac, uint8_t cause); +int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac); +int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac); +int gsm322_dump_sorted_plmn(struct osmocom_ms *ms); +int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, + void (*print)(void *, const char *, ...), void *priv); +int gsm322_dump_forbidden_la(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv); +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, + void (*print)(void *, const char *, ...), void *priv); +void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro); +void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro); +extern const char *plmn_a_state_names[]; +extern const char *plmn_m_state_names[]; +extern const char *cs_state_names[]; + +#endif /* _GSM322_H */ diff --git a/src/host/layer23/include/osmocom/gsm48_cc.h b/src/host/layer23/include/osmocom/gsm48_cc.h new file mode 100644 index 00000000..d6ea5756 --- /dev/null +++ b/src/host/layer23/include/osmocom/gsm48_cc.h @@ -0,0 +1,18 @@ +#ifndef _GSM48_CC_H +#define _GSM48_CC_H + +struct gsm48_cclayer { + struct osmocom_ms *ms; + + struct llist_head mncc_upqueue; + int (*mncc_recv)(struct osmocom_ms *, int, void *); +}; + +int gsm48_cc_init(struct osmocom_ms *ms); +int gsm48_cc_exit(struct osmocom_ms *ms); +int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg); +int mncc_dequeue(struct osmocom_ms *ms); +int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg); + +#endif /* _GSM48_CC_H */ + diff --git a/src/host/layer23/include/osmocom/gsm48_mm.h b/src/host/layer23/include/osmocom/gsm48_mm.h new file mode 100644 index 00000000..22c59835 --- /dev/null +++ b/src/host/layer23/include/osmocom/gsm48_mm.h @@ -0,0 +1,227 @@ +#ifndef _GSM48_MM_H +#define _GSM48_MM_H + +/* GSM 04.07 9.2.2 */ +#define GSM48_MMXX_MASK 0xf00 +#define GSM48_MMCC_CLASS 0x100 +#define GSM48_MMSS_CLASS 0x200 +#define GSM48_MMSMS_CLASS 0x300 +#define GSM48_MMCC_EST_REQ 0x110 +#define GSM48_MMCC_EST_IND 0x112 +#define GSM48_MMCC_EST_CNF 0x111 +#define GSM48_MMCC_REL_REQ 0x120 +#define GSM48_MMCC_REL_IND 0x122 +#define GSM48_MMCC_DATA_REQ 0x130 +#define GSM48_MMCC_DATA_IND 0x132 +#define GSM48_MMCC_UNIT_DATA_REQ 0x140 +#define GSM48_MMCC_UNIT_DATA_IND 0x142 +#define GSM48_MMCC_SYNC_IND 0x152 +#define GSM48_MMCC_REEST_REQ 0x160 +#define GSM48_MMCC_REEST_CNF 0x161 +#define GSM48_MMCC_ERR_IND 0x172 +#define GSM48_MMCC_PROMPT_IND 0x182 +#define GSM48_MMCC_PROMPT_REJ 0x184 +#define GSM48_MMSS_EST_REQ 0x210 +#define GSM48_MMSS_EST_IND 0x212 +#define GSM48_MMSS_EST_CNF 0x211 +#define GSM48_MMSS_REL_REQ 0x220 +#define GSM48_MMSS_REL_IND 0x222 +#define GSM48_MMSS_DATA_REQ 0x230 +#define GSM48_MMSS_DATA_IND 0x232 +#define GSM48_MMSS_UNIT_DATA_REQ 0x240 +#define GSM48_MMSS_UNIT_DATA_IND 0x242 +#define GSM48_MMSS_REEST_REQ 0x260 +#define GSM48_MMSS_REEST_CNF 0x261 +#define GSM48_MMSS_ERR_IND 0x272 +#define GSM48_MMSS_PROMPT_IND 0x282 +#define GSM48_MMSS_PROMPT_REJ 0x284 +#define GSM48_MMSMS_EST_REQ 0x310 +#define GSM48_MMSMS_EST_IND 0x312 +#define GSM48_MMSMS_EST_CNF 0x311 +#define GSM48_MMSMS_REL_REQ 0x320 +#define GSM48_MMSMS_REL_IND 0x322 +#define GSM48_MMSMS_DATA_REQ 0x330 +#define GSM48_MMSMS_DATA_IND 0x332 +#define GSM48_MMSMS_UNIT_DATA_REQ 0x340 +#define GSM48_MMSMS_UNIT_DATA_IND 0x342 +#define GSM48_MMSMS_REEST_REQ 0x360 +#define GSM48_MMSMS_REEST_CNF 0x361 +#define GSM48_MMSMS_ERR_IND 0x372 +#define GSM48_MMSMS_PROMPT_IND 0x382 +#define GSM48_MMSMS_PROMPT_REJ 0x384 + +#define MMXX_ALLOC_SIZE 256 +#define MMXX_ALLOC_HEADROOM 64 + +/* MMxx-SAP header */ +struct gsm48_mmxx_hdr { + int msg_type; /* MMxx_* primitive */ + uint32_t ref; /* reference to transaction */ + uint32_t transaction_id; /* transaction identifier */ + uint8_t emergency; /* emergency type of call */ + uint8_t cause; /* cause used for release */ +}; + +/* GSM 6.1.2 */ +#define GSM48_MMR_REG_REQ 0x01 +#define GSM48_MMR_REG_CNF 0x02 +#define GSM48_MMR_NREG_REQ 0x03 +#define GSM48_MMR_NREG_IND 0x04 + +/* MMR-SAP header */ +struct gsm48_mmr { + int msg_type; + + uint8_t cause; +}; + +/* GSM 04.07 9.2.1 */ +#define GSM48_MMXX_ST_IDLE 0 +#define GSM48_MMXX_ST_CONN_PEND 1 +#define GSM48_MMXX_ST_DEDICATED 2 +#define GSM48_MMXX_ST_CONN_SUSP 3 +#define GSM48_MMXX_ST_REESTPEND 4 + +/* GSM 04.08 4.1.2.1 */ +#define GSM48_MM_ST_NULL 0 +#define GSM48_MM_ST_LOC_UPD_INIT 3 +#define GSM48_MM_ST_WAIT_OUT_MM_CONN 5 +#define GSM48_MM_ST_MM_CONN_ACTIVE 6 +#define GSM48_MM_ST_IMSI_DETACH_INIT 7 +#define GSM48_MM_ST_PROCESS_CM_SERV_P 8 +#define GSM48_MM_ST_WAIT_NETWORK_CMD 9 +#define GSM48_MM_ST_LOC_UPD_REJ 10 +#define GSM48_MM_ST_WAIT_RR_CONN_LUPD 13 +#define GSM48_MM_ST_WAIT_RR_CONN_MM_CON 14 +#define GSM48_MM_ST_WAIT_RR_CONN_IMSI_D 15 +#define GSM48_MM_ST_WAIT_REEST 17 +#define GSM48_MM_ST_WAIT_RR_ACTIVE 18 +#define GSM48_MM_ST_MM_IDLE 19 +#define GSM48_MM_ST_WAIT_ADD_OUT_MM_CON 20 +#define GSM48_MM_ST_MM_CONN_ACTIVE_VGCS 21 +#define GSM48_MM_ST_WAIT_RR_CONN_VGCS 22 +#define GSM48_MM_ST_LOC_UPD_PEND 23 +#define GSM48_MM_ST_IMSI_DETACH_PEND 24 +#define GSM48_MM_ST_RR_CONN_RELEASE_NA 25 + +/* GSM 04.08 4.1.2.1 */ +#define GSM48_MM_SST_NORMAL_SERVICE 1 +#define GSM48_MM_SST_ATTEMPT_UPDATE 2 +#define GSM48_MM_SST_LIMITED_SERVICE 3 +#define GSM48_MM_SST_NO_IMSI 4 +#define GSM48_MM_SST_NO_CELL_AVAIL 5 +#define GSM48_MM_SST_LOC_UPD_NEEDED 6 +#define GSM48_MM_SST_PLMN_SEARCH 7 +#define GSM48_MM_SST_PLMN_SEARCH_NORMAL 8 +#define GSM48_MM_SST_RX_VGCS_NORMAL 9 +#define GSM48_MM_SST_RX_VGCS_LIMITED 10 + +/* MM events */ +#define GSM48_MM_EVENT_CELL_SELECTED 1 +#define GSM48_MM_EVENT_NO_CELL_FOUND 2 +#define GSM48_MM_EVENT_TIMEOUT_T3210 3 +#define GSM48_MM_EVENT_TIMEOUT_T3211 4 +#define GSM48_MM_EVENT_TIMEOUT_T3212 5 +#define GSM48_MM_EVENT_TIMEOUT_T3213 6 +#define GSM48_MM_EVENT_TIMEOUT_T3220 7 +#define GSM48_MM_EVENT_TIMEOUT_T3230 8 +#define GSM48_MM_EVENT_TIMEOUT_T3240 9 +#define GSM48_MM_EVENT_IMSI_DETACH 10 +#define GSM48_MM_EVENT_POWER_OFF 11 +#define GSM48_MM_EVENT_PAGING 12 +#define GSM48_MM_EVENT_AUTH_RESPONSE 13 +#define GSM48_MM_EVENT_SYSINFO 14 +#define GSM48_MM_EVENT_USER_PLMN_SEL 15 + +/* message for MM events */ +struct gsm48_mm_event { + uint32_t msg_type; + + uint8_t sres[4]; +}; + +/* GSM 04.08 MM timers */ +#define GSM_T3210_MS 20, 0 +#define GSM_T3211_MS 15, 0 +/* T3212 is given by SYSTEM INFORMATION */ +#define GSM_T3213_MS 4, 0 +#define GSM_T3220_MS 5, 0 +#define GSM_T3230_MS 15, 0 +#define GSM_T3240_MS 10, 0 +#define GSM_T3241_MS 300, 0 + +/* MM sublayer instance */ +struct gsm48_mmlayer { + struct osmocom_ms *ms; + int state; + int substate; + + /* queue for RR-SAP, MMxx-SAP, MMR-SAP, events message upwards */ + struct llist_head rr_upqueue; + struct llist_head mmxx_upqueue; + struct llist_head mmr_downqueue; + struct llist_head event_queue; + + /* timers */ + struct timer_list t3210, t3211, t3212, t3213; + struct timer_list t3220, t3230, t3240; + int t3212_value; + int start_t3211; /* remember to start timer */ + + /* list of MM connections */ + struct llist_head mm_conn; + + /* network name */ + char name_short[32]; + char name_long[32]; + + /* location update */ + uint8_t lupd_pending; /* current pending loc. upd. */ + uint8_t lupd_type; /* current coded type */ + uint8_t lupd_attempt; /* attempt counter */ + uint8_t lupd_ra_failure;/* random access failed */ + uint8_t lupd_rej_cause; /* cause of last reject */ + uint8_t lupd_periodic; /* periodic update pending */ + uint8_t lupd_retry; /* pending T3211/T3213 to */ + uint16_t lupd_mcc, lupd_mnc, lupd_lac; + + /* imsi detach */ + uint8_t delay_detach; /* do detach when possible */ + + /* other */ + int mr_substate; /* rem most recent substate */ + uint8_t power_off; /* set, if power off after detach */ +}; + +/* MM connection entry */ +struct gsm48_mm_conn { + struct llist_head list; + struct gsm48_mmlayer *mm; + + /* ref and type form a unique tupple */ + uint32_t ref; /* reference to trans */ + uint8_t protocol; + uint8_t transaction_id; + + int state; +}; + +int gsm48_mm_init(struct osmocom_ms *ms); +int gsm48_mm_exit(struct osmocom_ms *ms); +struct msgb *gsm48_mmr_msgb_alloc(int msg_type); +struct msgb *gsm48_mmevent_msgb_alloc(int msg_type); +int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_rr_dequeue(struct osmocom_ms *ms); +int gsm48_mmxx_dequeue(struct osmocom_ms *ms); +int gsm48_mmr_dequeue(struct osmocom_ms *ms); +int gsm48_mmevent_dequeue(struct osmocom_ms *ms); +int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg); +struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref, + uint8_t transaction_id); +const char *get_mmr_name(int value); +const char *get_mmxx_name(int value); +extern const char *gsm48_mm_state_names[]; +extern const char *gsm48_mm_substate_names[]; + +#endif /* _GSM48_MM_H */ diff --git a/src/host/layer23/include/osmocom/gsm48_rr.h b/src/host/layer23/include/osmocom/gsm48_rr.h new file mode 100644 index 00000000..74ccb27a --- /dev/null +++ b/src/host/layer23/include/osmocom/gsm48_rr.h @@ -0,0 +1,167 @@ +#ifndef _GSM48_RR_H +#define _GSM48_RR_H + +#include "osmocore/protocol/gsm_04_08.h" + +#define GSM_TA_CM 55385 + +/* GSM 04.07 9.1.2 */ +#define GSM48_RR_EST_REQ 0x10 +#define GSM48_RR_EST_IND 0x12 +#define GSM48_RR_EST_CNF 0x11 +#define GSM48_RR_REL_IND 0x22 +#define GSM48_RR_SYNC_IND 0x32 +#define GSM48_RR_DATA_REQ 0x40 +#define GSM48_RR_DATA_IND 0x42 +#define GSM48_RR_UNIT_DATA_IND 0x52 +#define GSM48_RR_ABORT_REQ 0x60 +#define GSM48_RR_ABORT_IND 0x62 +#define GSM48_RR_ACT_REQ 0x70 + +#define RR_EST_CAUSE_EMERGENCY 1 +#define RR_EST_CAUSE_REESTAB_TCH_F 2 +#define RR_EST_CAUSE_REESTAB_TCH_H 3 +#define RR_EST_CAUSE_REESTAB_2_TCH_H 4 +#define RR_EST_CAUSE_ANS_PAG_ANY 5 +#define RR_EST_CAUSE_ANS_PAG_SDCCH 6 +#define RR_EST_CAUSE_ANS_PAG_TCH_F 7 +#define RR_EST_CAUSE_ANS_PAG_TCH_ANY 8 +#define RR_EST_CAUSE_ORIG_TCHF 9 +#define RR_EST_CAUSE_LOC_UPD 12 +#define RR_EST_CAUSE_OTHER_SDCCH 13 + +#define RR_REL_CAUSE_UNDEFINED 0 +#define RR_REL_CAUSE_NORMAL 1 +#define RR_REL_CAUSE_NOT_AUTHORIZED 2 +#define RR_REL_CAUSE_RA_FAILURE 3 +#define RR_REL_CAUSE_T3122 4 +#define RR_REL_CAUSE_TRY_LATER 5 +#define RR_REL_CAUSE_EMERGENCY_ONLY 6 +#define RR_REL_CAUSE_LOST_SIGNAL 7 + +#define L3_ALLOC_SIZE 256 +#define L3_ALLOC_HEADROOM 64 + +#define RR_ALLOC_SIZE 256 +#define RR_ALLOC_HEADROOM 64 + +/* GSM 04.08 RR-SAP header */ +struct gsm48_rr_hdr { + uint32_t msg_type; /* RR-* primitive */ + uint8_t cause; +}; + +/* GSM 04.07 9.1.1 */ +#define GSM48_RR_ST_IDLE 0 +#define GSM48_RR_ST_CONN_PEND 1 +#define GSM48_RR_ST_DEDICATED 2 +#define GSM48_RR_ST_REL_PEND 3 + +/* channel description */ +struct gsm48_rr_cd { + uint8_t tsc; + uint8_t h; /* using hopping */ + uint16_t arfcn; /* dedicated mode */ + uint8_t maio; + uint8_t hsn; + uint8_t chan_nr; /* type, slot, sub slot */ + uint8_t link_id; + uint8_t ta; /* timing advance */ + uint8_t mob_alloc_lv[9]; /* len + up to 64 bits */ + uint8_t start_t1, start_t2, start_t3; /* start. time */ +}; + +/* measurements */ +struct gsm48_rr_meas { + uint8_t rxlev_full; + uint8_t rxlev_sub; + uint8_t rxqual_full; + uint8_t rxqual_sub; + uint8_t dtx; + uint8_t ba; + uint8_t meas_valid; + uint8_t ncell_na; + uint8_t count; + uint8_t rxlev_nc[6]; + uint8_t bsic_nc[6]; + uint8_t bcch_f_nc[6]; +}; + +struct gsm48_cr_hist { + uint32_t fn; + uint8_t chan_req; + uint8_t valid; +}; + +/* RR sublayer instance */ +struct gsm48_rrlayer { + struct osmocom_ms *ms; + int state; + + /* queue for RSL-SAP message upwards */ + struct llist_head rsl_upqueue; + + /* queue for messages while RR connection is built up */ + struct llist_head downqueue; + + /* timers */ + struct timer_list t_rel_wait; /* wait for L2 to transmit UA */ + struct timer_list t3110; + struct timer_list t3122; + struct timer_list t3124; + struct timer_list t3126; + int t3126_value; +#ifndef TODO + struct timer_list temp_rach_ti; /* temporary timer */ +#endif + + /* states if RR-EST-REQ was used */ + uint8_t rr_est_req; + struct msgb *rr_est_msg; + + /* channel request states */ + uint8_t wait_assign; /* waiting for assignment state */ + uint8_t n_chan_req; /* number left, incl. current */ + uint8_t chan_req_val; /* current request value */ + uint8_t chan_req_mask; /* mask of random bits */ + + /* cr_hist must be signed and greater 8 bit, -1 = no value */ + struct gsm48_cr_hist cr_hist[3]; + + /* current channel descriptions */ + struct gsm48_rr_cd cd_now; + + /* current cipering */ + uint8_t cipher_on; + uint8_t cipher_type; /* 10.5.2.9 */ + + /* special states when changing channel */ + uint8_t hando_susp_state; + uint8_t assign_susp_state; + uint8_t resume_last_state; + struct gsm48_rr_cd cd_last; + + /* measurements */ + struct gsm48_rr_meas meas; + + /* BA range */ + uint8_t ba_ranges; + uint32_t ba_range[16]; +}; + +const char *get_rr_name(int value); +extern int gsm48_rr_init(struct osmocom_ms *ms); +extern int gsm48_rr_exit(struct osmocom_ms *ms); +int gsm48_rsl_dequeue(struct osmocom_ms *ms); +int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg); +struct msgb *gsm48_l3_msgb_alloc(void); +struct msgb *gsm48_rr_msgb_alloc(int msg_type); +int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, + uint16_t *mnc, uint16_t *lac); +int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm); +int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg); +int gsm48_rr_los(struct osmocom_ms *ms); +int gsm48_rr_rach_conf(struct osmocom_ms *ms, uint32_t fn); +extern const char *gsm48_rr_state_names[]; + +#endif /* _GSM48_RR_H */ diff --git a/src/host/layer23/include/osmocom/gsmtap_util.h b/src/host/layer23/include/osmocom/gsmtap_util.h new file mode 100644 index 00000000..30fc1267 --- /dev/null +++ b/src/host/layer23/include/osmocom/gsmtap_util.h @@ -0,0 +1,16 @@ +#ifndef _GSMTAP_UTIL_H +#define _GSMTAP_UTIL_H + +#include <stdint.h> + +/* convert RSL channel number to GSMTAP channel type */ +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id); + +/* receive a message from L1/L2 and put it in GSMTAP */ +int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, + uint32_t fn, int8_t signal_dbm, uint8_t snr, + const uint8_t *data, unsigned int len); + +int gsmtap_init(uint32_t dst_ip); + +#endif /* _GSMTAP_UTIL_H */ diff --git a/src/host/layer23/include/osmocom/l1ctl.h b/src/host/layer23/include/osmocom/l1ctl.h new file mode 100644 index 00000000..e5b91e7c --- /dev/null +++ b/src/host/layer23/include/osmocom/l1ctl.h @@ -0,0 +1,45 @@ +#ifndef osmocom_l1ctl_h +#define osmocom_l1ctl_h + +#include <osmocore/msgb.h> +#include <osmocom/osmocom_data.h> + +struct osmocom_ms; + +/* Receive incoming data from L1 using L1CTL format */ +int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg); + +/* Transmit L1CTL_DATA_REQ */ +int tx_ph_data_req(struct osmocom_ms *ms, struct msgb *msg, + uint8_t chan_nr, uint8_t link_id); + +/* Transmit L1CTL_RACH_REQ */ +int tx_ph_rach_req(struct osmocom_ms *ms); + +/* Transmit L1CTL_DM_EST_REQ */ +int tx_ph_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, + uint8_t chan_nr, uint8_t tsc, uint8_t tx_power); +int tx_ph_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, + uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc, + uint8_t tx_power); + +/* Transmit L1CTL_DM_REL_REQ */ +int tx_ph_dm_rel_req(struct osmocom_ms *ms); + +/* Transmit FBSB_REQ */ +int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn, + uint8_t flags, uint16_t timeout, uint8_t sync_info_idx, + uint8_t ccch_mode); + +int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode); + +int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len); + +/* Transmit L1CTL_RESET_REQ */ +int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type); + +/* Transmit L1CTL_PM_REQ */ +int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from, + uint16_t arfcm_to); + +#endif diff --git a/src/host/layer23/include/osmocom/l1l2_interface.h b/src/host/layer23/include/osmocom/l1l2_interface.h new file mode 100644 index 00000000..41403d87 --- /dev/null +++ b/src/host/layer23/include/osmocom/l1l2_interface.h @@ -0,0 +1,8 @@ +#ifndef _L1L2_INTERFACE_H +#define _L1L2_INTERFACE_H + +int layer2_open(struct osmocom_ms *ms, const char *socket_path); +int layer2_close(struct osmocom_ms *ms); +int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg); + +#endif /* _L1L2_INTERFACE_H */ diff --git a/src/host/layer23/include/osmocom/l23_app.h b/src/host/layer23/include/osmocom/l23_app.h new file mode 100644 index 00000000..1a228567 --- /dev/null +++ b/src/host/layer23/include/osmocom/l23_app.h @@ -0,0 +1,10 @@ +#ifndef _L23_APP_H +#define _L23_APP_H + +/* initialization, called once when starting the app, before entering + * select loop */ +extern int l23_app_init(struct osmocom_ms *ms); +extern int (*l23_app_work) (struct osmocom_ms *ms); +extern int (*l23_app_exit) (struct osmocom_ms *ms); + +#endif /* _L23_APP_H */ diff --git a/src/host/layer23/include/osmocom/lapdm.h b/src/host/layer23/include/osmocom/lapdm.h new file mode 100644 index 00000000..06ac5f89 --- /dev/null +++ b/src/host/layer23/include/osmocom/lapdm.h @@ -0,0 +1,94 @@ +#ifndef _OSMOCOM_LAPDM_H +#define _OSMOCOM_LAPDM_H + +#include <stdint.h> + +#include <osmocore/timer.h> +#include <osmocore/msgb.h> + +#include <l1a_l23_interface.h> + +enum lapdm_state { + LAPDm_STATE_NULL = 0, + LAPDm_STATE_IDLE, + LAPDm_STATE_SABM_SENT, + LAPDm_STATE_MF_EST, + LAPDm_STATE_TIMER_RECOV, + LAPDm_STATE_DISC_SENT, +}; + +struct lapdm_entity; +struct osmocom_ms; + +struct lapdm_msg_ctx { + struct lapdm_datalink *dl; + int lapdm_fmt; + uint8_t n201; + uint8_t chan_nr; + uint8_t link_id; + uint8_t addr; + uint8_t ctrl; +}; + +/* TS 04.06 / Section 3.5.2 */ +struct lapdm_datalink { + uint8_t V_send; /* seq nr of next I frame to be transmitted */ + uint8_t V_ack; /* last frame ACKed by peer */ + uint8_t N_send; /* ? set to V_send at Tx time*/ + uint8_t V_recv; /* seq nr of next I frame expected to be received */ + uint8_t N_recv; /* expected send seq nr of the next received I frame */ + uint32_t state; + int seq_err_cond; /* condition of sequence error */ + uint8_t own_busy, peer_busy; + struct timer_list t200; + uint8_t retrans_ctr; + struct llist_head send_queue; /* frames from L3 */ + struct msgb *send_buffer; /* current frame transmitting */ + int send_out; /* how much was sent from send_buffer */ + uint8_t tx_hist[8][200]; /* tx history buffer */ + int tx_length[8]; /* length in history buffer */ + struct llist_head tx_queue; /* frames to L1 */ + struct lapdm_msg_ctx mctx; /* context of established connection */ + struct msgb *rcv_buffer; /* buffer to assemble the received message */ + + struct lapdm_entity *entity; +}; + +enum lapdm_dl_sapi { + DL_SAPI0 = 0, + DL_SAPI3 = 1, + _NR_DL_SAPI +}; + +struct lapdm_entity { + struct lapdm_datalink datalink[_NR_DL_SAPI]; + int last_tx_dequeue; /* last entity that was dequeued */ + int tx_pending; /* currently a pending frame not confirmed by L1 */ + struct osmocom_ms *ms; +}; + +const char *get_rsl_name(int value); +extern const char *lapdm_state_names[]; + +/* initialize a LAPDm entity */ +void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms); + +/* deinitialize a LAPDm entity */ +void lapdm_exit(struct lapdm_entity *le); + +/* input into layer2 (from layer 1) */ +int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i); +int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le); + +/* input into layer2 (from layer 3) */ +int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms); + +/* sending messages up from L2 to L3 */ +int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms); + +typedef int (*osmol2_cb_t)(struct msgb *msg, struct osmocom_ms *ms); + +/* register message handler for messages that are sent from L2->L3 */ +int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb); + +#endif /* _OSMOCOM_LAPDM_H */ diff --git a/src/host/layer23/include/osmocom/layer3.h b/src/host/layer23/include/osmocom/layer3.h new file mode 100644 index 00000000..bf68102d --- /dev/null +++ b/src/host/layer23/include/osmocom/layer3.h @@ -0,0 +1,14 @@ +#ifndef _OSMOCOM_L3_H +#define _OSMOCOM_L3_H + +#include <osmocore/msgb.h> +#include <osmocom/osmocom_data.h> + +int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms); +int gsm48_rx_dcch(struct msgb *msg, struct osmocom_ms *ms); +int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms); + +/* Initialize layer3 for the MS, hook it to L2 */ +int layer3_init(struct osmocom_ms *ms); + +#endif diff --git a/src/host/layer23/include/osmocom/logging.h b/src/host/layer23/include/osmocom/logging.h new file mode 100644 index 00000000..1a11cf9d --- /dev/null +++ b/src/host/layer23/include/osmocom/logging.h @@ -0,0 +1,25 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#define DEBUG +#include <osmocore/logging.h> + +enum { + DRSL, + DRR, + DPLMN, + DCS, + DMM, + DCC, + DSMS, + DMNCC, + DMEAS, + DPAG, + DLAPDM, + DL1C, + DSUM, +}; + +extern const struct log_info log_info; + +#endif /* _LOGGING_H */ diff --git a/src/host/layer23/include/osmocom/mncc.h b/src/host/layer23/include/osmocom/mncc.h new file mode 100644 index 00000000..d1d4d38f --- /dev/null +++ b/src/host/layer23/include/osmocom/mncc.h @@ -0,0 +1,166 @@ +/* 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> + * (C) 2009 by 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. + * + */ + +#ifndef _MNCC_H +#define _MNCC_H + +#include <osmocore/linuxlist.h> +#include <osmocore/mncc.h> + +struct gsm_call { + struct llist_head entry; + + void *ms; + + uint32_t callref; + + uint8_t hold; /* call on hold */ + uint8_t ring; /* call is ringing/knocking */ +}; + +#define MNCC_SETUP_REQ 0x0101 +#define MNCC_SETUP_IND 0x0102 +#define MNCC_SETUP_RSP 0x0103 +#define MNCC_SETUP_CNF 0x0104 +#define MNCC_SETUP_COMPL_REQ 0x0105 +#define MNCC_SETUP_COMPL_IND 0x0106 +/* MNCC_REJ_* is perfomed via MNCC_REL_* */ +#define MNCC_CALL_CONF_IND 0x0107 +#define MNCC_CALL_PROC_REQ 0x0108 +#define MNCC_PROGRESS_REQ 0x0109 +#define MNCC_ALERT_REQ 0x010a +#define MNCC_ALERT_IND 0x010b +#define MNCC_NOTIFY_REQ 0x010c +#define MNCC_NOTIFY_IND 0x010d +#define MNCC_DISC_REQ 0x010e +#define MNCC_DISC_IND 0x010f +#define MNCC_REL_REQ 0x0110 +#define MNCC_REL_IND 0x0111 +#define MNCC_REL_CNF 0x0112 +#define MNCC_FACILITY_REQ 0x0113 +#define MNCC_FACILITY_IND 0x0114 +#define MNCC_START_DTMF_IND 0x0115 +#define MNCC_START_DTMF_RSP 0x0116 +#define MNCC_START_DTMF_REJ 0x0117 +#define MNCC_STOP_DTMF_IND 0x0118 +#define MNCC_STOP_DTMF_RSP 0x0119 +#define MNCC_MODIFY_REQ 0x011a +#define MNCC_MODIFY_IND 0x011b +#define MNCC_MODIFY_RSP 0x011c +#define MNCC_MODIFY_CNF 0x011d +#define MNCC_MODIFY_REJ 0x011e +#define MNCC_HOLD_IND 0x011f +#define MNCC_HOLD_CNF 0x0120 +#define MNCC_HOLD_REJ 0x0121 +#define MNCC_RETRIEVE_IND 0x0122 +#define MNCC_RETRIEVE_CNF 0x0123 +#define MNCC_RETRIEVE_REJ 0x0124 +#define MNCC_USERINFO_REQ 0x0125 +#define MNCC_USERINFO_IND 0x0126 +#define MNCC_REJ_REQ 0x0127 +#define MNCC_REJ_IND 0x0128 +#define MNCC_PROGRESS_IND 0x0129 +#define MNCC_CALL_PROC_IND 0x012a +#define MNCC_CALL_CONF_REQ 0x012b +#define MNCC_START_DTMF_REQ 0x012c +#define MNCC_STOP_DTMF_REQ 0x012d +#define MNCC_HOLD_REQ 0x012e +#define MNCC_RETRIEVE_REQ 0x012f + +#define MNCC_BRIDGE 0x0200 +#define MNCC_FRAME_RECV 0x0201 +#define MNCC_FRAME_DROP 0x0202 +#define MNCC_LCHAN_MODIFY 0x0203 + +#define GSM_TCHF_FRAME 0x0300 +#define GSM_TCHF_FRAME_EFR 0x0301 + +#define GSM_MAX_FACILITY 128 +#define GSM_MAX_SSVERSION 128 +#define GSM_MAX_USERUSER 128 + +#define MNCC_F_BEARER_CAP 0x0001 +#define MNCC_F_CALLED 0x0002 +#define MNCC_F_CALLING 0x0004 +#define MNCC_F_REDIRECTING 0x0008 +#define MNCC_F_CONNECTED 0x0010 +#define MNCC_F_CAUSE 0x0020 +#define MNCC_F_USERUSER 0x0040 +#define MNCC_F_PROGRESS 0x0080 +#define MNCC_F_EMERGENCY 0x0100 +#define MNCC_F_FACILITY 0x0200 +#define MNCC_F_SSVERSION 0x0400 +#define MNCC_F_CCCAP 0x0800 +#define MNCC_F_KEYPAD 0x1000 +#define MNCC_F_SIGNAL 0x2000 + +struct gsm_mncc { + /* context based information */ + u_int32_t msg_type; + u_int32_t callref; + + /* which fields are present */ + u_int32_t fields; + + /* data derived informations (MNCC_F_ based) */ + struct gsm_mncc_bearer_cap bearer_cap; + struct gsm_mncc_number called; + struct gsm_mncc_number calling; + struct gsm_mncc_number redirecting; + struct gsm_mncc_number connected; + struct gsm_mncc_cause cause; + struct gsm_mncc_progress progress; + struct gsm_mncc_useruser useruser; + struct gsm_mncc_facility facility; + struct gsm_mncc_cccap cccap; + struct gsm_mncc_ssversion ssversion; + struct { + int sup; + int inv; + } clir; + int signal; + + /* data derived information, not MNCC_F based */ + int keypad; + int more; + int notify; /* 0..127 */ + int emergency; + char imsi[16]; + + unsigned char lchan_mode; +}; + +struct gsm_data_frame { + u_int32_t msg_type; + u_int32_t callref; + unsigned char data[0]; +}; + +const char *get_mncc_name(int value); +int mncc_recv(struct osmocom_ms *ms, int msg_type, void *arg); +void mncc_set_cause(struct gsm_mncc *data, int loc, int val); + +#endif + diff --git a/src/host/layer23/include/osmocom/networks.h b/src/host/layer23/include/osmocom/networks.h new file mode 100644 index 00000000..e8c1b18e --- /dev/null +++ b/src/host/layer23/include/osmocom/networks.h @@ -0,0 +1,22 @@ +#ifndef _NETWORKS_H +#define _NETWORKS_H + +struct gsm_networks { + uint16_t mcc; + int16_t mnc; + const char *name; +}; + +int gsm_match_mcc(uint16_t mcc, char *imsi); +int gsm_match_mnc(uint16_t mcc, uint8_t mnc, char *imsi); +const char *gsm_print_mcc(uint16_t mcc); +const char *gsm_print_mnc(uint16_t mcc); +const char *gsm_get_mcc(uint16_t mcc); +const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc); +const char *gsm_imsi_mcc(char *imsi); +const char *gsm_imsi_mnc(char *imsi); +const uint16_t gsm_input_mcc(char *string); +const uint16_t gsm_input_mnc(char *string); + +#endif /* _NETWORKS_H */ + diff --git a/src/host/layer23/include/osmocom/osmocom_data.h b/src/host/layer23/include/osmocom/osmocom_data.h new file mode 100644 index 00000000..e4430fc7 --- /dev/null +++ b/src/host/layer23/include/osmocom/osmocom_data.h @@ -0,0 +1,80 @@ +#ifndef osmocom_data_h +#define osmocom_data_h + +#include <osmocore/select.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/write_queue.h> + +struct osmocom_ms; + +#include <osmocom/support.h> +#include <osmocom/settings.h> +#include <osmocom/subscriber.h> +#include <osmocom/lapdm.h> +#include <osmocom/gsm48_rr.h> +#include <osmocom/sysinfo.h> +#include <osmocom/gsm322.h> +#include <osmocom/gsm48_mm.h> +#include <osmocom/gsm48_cc.h> + +/* A layer2 entity */ +struct osmol2_entity { + struct lapdm_entity lapdm_dcch; + struct lapdm_entity lapdm_acch; + osmol2_cb_t msg_handler; +}; + +/* One Mobilestation for osmocom */ +struct osmocom_ms { + struct llist_head entity; + char name[32]; + struct write_queue wq; + uint16_t test_arfcn; + + struct gsm_support support; + + struct gsm_settings settings; + + struct gsm_subscriber subscr; + + struct osmol2_entity l2_entity; + + struct gsm48_rrlayer rrlayer; + struct gsm322_plmn plmn; + struct gsm322_cellsel cellsel; + struct gsm48_mmlayer mmlayer; + struct gsm48_cclayer cclayer; + struct llist_head trans_list; +}; + +enum osmobb_sig_subsys { + SS_L1CTL, +}; + +enum osmobb_meas_sig { + S_L1CTL_FBSB_ERR, + S_L1CTL_FBSB_RESP, + S_L1CTL_RESET, + S_L1CTL_PM_RES, + S_L1CTL_PM_DONE, + S_L1CTL_RACH_CONF, + S_L1CTL_CCCH_MODE_CONF, +}; + +struct osmobb_meas_res { + struct osmocom_ms *ms; + uint16_t band_arfcn; + uint8_t rx_lev; +}; + +struct osmobb_rach_conf { + struct osmocom_ms *ms; + uint32_t fn; +}; + +struct osmobb_ccch_mode_conf { + struct osmocom_ms *ms; + uint8_t ccch_mode; +}; + +#endif diff --git a/src/host/layer23/include/osmocom/rslms.h b/src/host/layer23/include/osmocom/rslms.h new file mode 100644 index 00000000..8f17b818 --- /dev/null +++ b/src/host/layer23/include/osmocom/rslms.h @@ -0,0 +1,23 @@ +#ifndef _OSMOCOM_RSLMS_H +#define _OSMOCOM_RSLMS_H + +#include <osmocore/msgb.h> +#include <osmocom/osmocom_data.h> + +/* From L3 into RSLMS (direction -> L2) */ + +/* Send a 'simple' RLL request to L2 */ +int rslms_tx_rll_req(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id); + +/* Send a RLL request (including L3 info) to L2 */ +int rslms_tx_rll_req_l3(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id, struct msgb *msg); + + +/* From L2 into RSLMS (direction -> L3) */ + +/* input function that L2 calls when sending messages up to L3 */ +//int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms); + +#endif /* _OSMOCOM_RSLMS_H */ diff --git a/src/host/layer23/include/osmocom/settings.h b/src/host/layer23/include/osmocom/settings.h new file mode 100644 index 00000000..95c53a83 --- /dev/null +++ b/src/host/layer23/include/osmocom/settings.h @@ -0,0 +1,34 @@ +#ifndef _settings_h +#define _settings_h + +struct gsm_settings { + /* IMEI */ + char imei[16]; + char imeisv[17]; + char imei_random; + + /* network search */ + int plmn_mode; /* PLMN_MODE_* */ + + /* SIM */ + int simtype; /* selects card on power on */ + char emergency_imsi[20]; /* just in case... */ + + /* test card simulator settings */ + char test_imsi[20]; /* just in case... */ + uint8_t test_barr; + uint8_t test_rplmn_valid; + uint16_t test_rplmn_mcc, test_rplmn_mnc; + uint8_t test_always; /* ...search hplmn... */ + + /* call related settings */ + uint8_t cw; /* set if call-waiting is allowed */ + uint8_t clip, clir; +}; + +int gsm_settings_init(struct osmocom_ms *ms); +char *gsm_check_imei(const char *imei, const char *sv); +int gsm_random_imei(struct gsm_settings *set); + +#endif /* _settings_h */ + diff --git a/src/host/layer23/include/osmocom/subscriber.h b/src/host/layer23/include/osmocom/subscriber.h new file mode 100644 index 00000000..09eab23c --- /dev/null +++ b/src/host/layer23/include/osmocom/subscriber.h @@ -0,0 +1,89 @@ +#ifndef _SUBSCRIBER_H +#define _SUBSCRIBER_H + +/* GSM 04.08 4.1.2.2 SIM update status */ +#define GSM_SIM_U0_NULL 0 +#define GSM_SIM_U1_UPDATED 1 +#define GSM_SIM_U2_NOT_UPDATED 2 +#define GSM_SIM_U3_ROAMING_NA 3 + +struct gsm_sub_plmn_list { + struct llist_head entry; + uint16_t mcc, mnc; +}; + +struct gsm_sub_plmn_na { + struct llist_head entry; + uint16_t mcc, mnc; + uint8_t cause; +}; + +#define GSM_IMSI_LENGTH 16 + +enum { + GSM_SIM_TYPE_NONE = 0, + GSM_SIM_TYPE_SLOT, + GSM_SIM_TYPE_TEST +}; + +struct gsm_subscriber { + struct osmocom_ms *ms; + + /* status */ + uint8_t sim_valid; /* sim inserted and valid */ + uint8_t ustate; /* update status */ + uint8_t imsi_attached; /* attached state */ + + /* LAI */ + uint8_t lai_valid; + uint16_t lai_mcc, lai_mnc, lai_lac; + + /* IMSI */ + char imsi[GSM_IMSI_LENGTH]; + + /* TMSI */ + uint8_t tmsi_valid; + uint32_t tmsi; + + /* key */ + uint8_t key_seq; /* ciphering key sequence number */ + uint8_t key[32]; /* up to 256 bit */ + + /* other */ + struct llist_head plmn_list; /* PLMN Selector field */ + struct llist_head plmn_na; /* not allowed PLMNs */ + uint8_t t6m_hplmn; /* timer for hplmn search */ + + /* special things */ + uint8_t always_search_hplmn; + /* search hplmn in other countries also (for test cards) */ + char sim_name[32]; /* name to load/save sim */ + + /* PLMN last registered */ + uint8_t plmn_valid; + uint16_t plmn_mcc, plmn_mnc; + + /* our access */ + uint8_t acc_barr; /* if we may access, if cell barred */ + uint16_t acc_class; /* bitmask of what we may access */ +}; + +int gsm_subscr_init(struct osmocom_ms *ms); +int gsm_subscr_exit(struct osmocom_ms *ms); +int gsm_subscr_testcard(struct osmocom_ms *ms); +int gsm_subscr_remove(struct osmocom_ms *ms); +void new_sim_ustate(struct gsm_subscriber *subscr, int state); +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc); +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc, uint8_t cause); +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc); +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv); +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv); +char *gsm_check_imsi(const char *imsi); + +#endif /* _SUBSCRIBER_H */ + diff --git a/src/host/layer23/include/osmocom/support.h b/src/host/layer23/include/osmocom/support.h new file mode 100644 index 00000000..ad1ccd7e --- /dev/null +++ b/src/host/layer23/include/osmocom/support.h @@ -0,0 +1,93 @@ +#ifndef _SUPPORT_H +#define _SUPPORT_H + +#define GSM_CIPHER_A5_1 0 +#define GSM_CIPHER_A5_2 1 +#define GSM_CIPHER_A5_3 2 +#define GSM_CIPHER_A5_4 3 +#define GSM_CIPHER_A5_5 4 +#define GSM_CIPHER_A5_6 5 +#define GSM_CIPHER_A5_7 6 +#define GSM_CIPHER_RESERVED 7 + +struct gsm_support { + struct osmocom_ms *ms; + + /* rf power capability */ + uint8_t pwr_lev_900; /* and < 900 */ + uint8_t pwr_lev_1800; /* DCS and PCS */ + /* controlled early classmark sending */ + uint8_t es_ind; + /* revision level */ + uint8_t rev_lev; + /* support of VGCS */ + uint8_t vgcs; + /* support of VBS */ + uint8_t vbs; + /* support of SMS */ + uint8_t sms_ptp; + /* screening indicator */ + uint8_t ss_ind; + /* pseudo synchronised capability */ + uint8_t ps_cap; + /* CM service prompt */ + uint8_t cmsp; + /* solsa support */ + uint8_t solsa; + /* location service support */ + uint8_t lcsva; + /* codec supprot */ + uint8_t a5_1; + uint8_t a5_2; + uint8_t a5_3; + uint8_t a5_4; + uint8_t a5_5; + uint8_t a5_6; + uint8_t a5_7; + /* radio support */ + uint8_t p_gsm; + uint8_t e_gsm; + uint8_t r_gsm; + uint8_t r_capa; + uint8_t low_capa; + uint8_t dcs_1800; + uint8_t dcs_capa; + uint8_t freq_map[128]; + /* multi slot support */ + uint8_t ms_sup; + /* ucs2 treatment */ + uint8_t ucs2_treat; + /* support extended measurements */ + uint8_t ext_meas; + /* support switched measurement capability */ + uint8_t meas_cap; + uint8_t sms_val; + uint8_t sm_val; + /* positioning method capability */ + uint8_t loc_serv; + uint8_t e_otd_ass; + uint8_t e_otd_based; + uint8_t gps_ass; + uint8_t gps_based; + uint8_t gps_conv; + + /* radio */ + int8_t min_rxlev_db; + uint8_t scan_to; + uint8_t sync_to; +}; + +struct gsm_support_scan_max { + uint16_t start; + uint16_t end; + uint16_t max; + uint16_t temp; +}; +extern struct gsm_support_scan_max gsm_sup_smax[]; + +void gsm_support_init(struct osmocom_ms *ms); +void gsm_support_dump(struct gsm_support *sup, + void (*print)(void *, const char *, ...), void *priv); + +#endif /* _SUPPORT_H */ + diff --git a/src/host/layer23/include/osmocom/sysinfo.h b/src/host/layer23/include/osmocom/sysinfo.h new file mode 100644 index 00000000..9a33bb97 --- /dev/null +++ b/src/host/layer23/include/osmocom/sysinfo.h @@ -0,0 +1,120 @@ +#ifndef _SYSINFO_H +#define _SYSINFO_H + +/* collection of system information of the current cell */ + +/* frequency mask flags of frequency type */ +#define FREQ_TYPE_SERV 0x01 /* frequency of the serving cell */ +#define FREQ_TYPE_HOPP 0x02 /* frequency used for channel hopping */ +#define FREQ_TYPE_NCELL 0x1c /* frequency of the neighbor cell */ +#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ +#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ +#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ +#define FREQ_TYPE_REP 0xe0 /* frequency to be reported */ +#define FREQ_TYPE_REP_5 0x20 /* sub channel of SI 5 */ +#define FREQ_TYPE_REP_5bis 0x40 /* sub channel of SI 5bis */ +#define FREQ_TYPE_REP_5ter 0x80 /* sub channel of SI 5ter */ + +/* structure of one frequency */ +struct gsm_sysinfo_freq { + /* if the frequency included in the sysinfo */ + uint8_t mask; +}; + +/* structure of all received system informations */ +struct gsm48_sysinfo { + /* flags of available information */ + uint8_t si1, si2, si2bis, si2ter, si3, + si4, si5, si5bis, si5ter, si6; + + /* memory maps to simply detect change in system info messages */ + uint8_t si1_msg[23]; + uint8_t si2_msg[23]; + uint8_t si2b_msg[23]; + uint8_t si2t_msg[23]; + uint8_t si3_msg[23]; + uint8_t si4_msg[23]; + uint8_t si5_msg[18]; + uint8_t si5b_msg[18]; + uint8_t si5t_msg[18]; + uint8_t si6_msg[18]; + + struct gsm_sysinfo_freq freq[1024]; /* all frequencies */ + uint16_t hopping[64]; /* hopping arfcn */ + uint8_t hopp_len; + + /* serving cell */ + uint16_t cell_id; + uint16_t mcc, mnc, lac; /* LAI */ + uint8_t max_retrans; /* decoded */ + uint8_t tx_integer; /* decoded */ + uint8_t reest_denied; /* 1 = denied */ + uint8_t cell_barr; /* 1 = barred */ + uint16_t class_barr; /* bit 10 is emergency */ + + /* si3 rest */ + uint8_t sp; + uint8_t sp_cbq; + uint8_t sp_cro; + uint8_t sp_to; + uint8_t sp_pt; + uint8_t po; + uint8_t po_value; + uint8_t si2ter_ind; + uint8_t ecsm; + uint8_t sched; + uint8_t sched_where; + uint8_t gi_ra_colour; + uint8_t gi_si13_pos; + + /* cell selection */ + int8_t ms_txpwr_max_ccch; + int8_t cell_resel_hyst_db; + int8_t rxlev_acc_min_db; + uint8_t neci; + uint8_t acs; + /* bcch options */ + uint8_t bcch_radio_link_timeout; + uint8_t bcch_dtx; + uint8_t bcch_pwrc; + /* sacch options */ + uint8_t sacch_radio_link_timeout; + uint8_t sacch_dtx; + uint8_t sacch_pwrc; + /* control channel */ + uint8_t ccch_conf; + uint8_t bs_ag_blks_res; + uint8_t att_allowed; + uint8_t pag_mf_periods; + int32_t t3212; /* real value in seconds */ + /* channel description */ + uint8_t tsc; + uint8_t h; /* using hopping */ + uint16_t arfcn; + uint8_t maio; + uint8_t hsn; + uint8_t chan_nr; /* type, slot, sub slot */ + + /* neighbor cell */ + uint8_t nb_ext_ind_si2; + uint8_t nb_ba_ind_si2; + uint8_t nb_ext_ind_si2bis; + uint8_t nb_ba_ind_si2bis; + uint8_t nb_multi_rep_si2ter; /* see GSM 05.08 8.4.3 */ + uint8_t nb_ext_ind_si5; + uint8_t nb_ba_ind_si5; + uint8_t nb_ext_ind_si5bis; + uint8_t nb_ba_ind_si5bis; + uint8_t nb_multi_rep_si5ter; + uint8_t nb_ncc_permitted; + uint8_t nb_max_retrans; /* decoded */ + uint8_t nb_tx_integer; /* decoded */ + uint8_t nb_reest_denied; /* 1 = denied */ + uint8_t nb_cell_barr; /* 1 = barred */ + uint16_t nb_class_barr; /* bit 10 is emergency */ +}; + +int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, + void (*print)(void *, const char *, ...), void *priv); + +#endif /* _SYSINFO_H */ diff --git a/src/host/layer23/include/osmocom/transaction.h b/src/host/layer23/include/osmocom/transaction.h new file mode 100644 index 00000000..4be82c19 --- /dev/null +++ b/src/host/layer23/include/osmocom/transaction.h @@ -0,0 +1,71 @@ +#ifndef _TRANSACT_H +#define _TRANSACT_H + +#include <osmocore/linuxlist.h> + +/* One transaction */ +struct gsm_trans { + /* Entry in list of all transactions */ + struct llist_head entry; + + /* The protocol within which we live */ + uint8_t protocol; + + /* The current transaction ID */ + uint8_t transaction_id; + + /* To whom we belong */ + struct osmocom_ms *ms; + + /* reference from MNCC or other application */ + uint32_t callref; + + /* if traffic channel receive was requested */ + int tch_recv; + + union { + struct { + + /* current call state */ + int state; + + /* most recent progress indicator */ + uint8_t prog_ind; + + /* current timer and message queue */ + int Tcurrent; /* current CC timer */ + int T308_second; /* used to send release again */ + struct timer_list timer; + struct gsm_mncc msg; /* stores setup/disconnect/release message */ + } cc; +#if 0 + struct { + uint8_t link_id; /* RSL Link ID to be used for this trans */ + int is_mt; /* is this a MO (0) or MT (1) transfer */ + enum gsm411_cp_state cp_state; + struct timer_list cp_timer; + + enum gsm411_rp_state rp_state; + + struct gsm_sms *sms; + } sms; +#endif + }; +}; + + + +struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, + uint8_t proto, uint8_t trans_id); +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, + uint32_t callref); + +struct gsm_trans *trans_alloc(struct osmocom_ms *ms, + uint8_t protocol, uint8_t trans_id, + uint32_t callref); +void trans_free(struct gsm_trans *trans); + +int trans_assign_trans_id(struct osmocom_ms *ms, + uint8_t protocol, uint8_t ti_flag); + +#endif diff --git a/src/host/layer23/include/osmocom/vty.h b/src/host/layer23/include/osmocom/vty.h new file mode 100644 index 00000000..f9e65a12 --- /dev/null +++ b/src/host/layer23/include/osmocom/vty.h @@ -0,0 +1,19 @@ +#ifndef OSMOCOM_VTY_H +#define OSMOCOM_VTY_H + +#include <osmocom/osmocom_data.h> +#include <osmocom/vty/vty.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/command.h> + +enum ms_vty_node { + MS_NODE = _LAST_OSMOVTY_NODE + 1, + TESTSIM_NODE, +}; + +enum node_type ms_vty_go_parent(struct vty *vty); +int ms_vty_init(void); +extern void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + +#endif + diff --git a/src/host/layer23/src/Makefile.am b/src/host/layer23/src/Makefile.am new file mode 100644 index 00000000..6b25352c --- /dev/null +++ b/src/host/layer23/src/Makefile.am @@ -0,0 +1,26 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) +#AM_LDFLAGS = $(LIBOSMOCORE_LIBS) + +noinst_LIBRARIES = liblayer23.a +liblayer23_a_SOURCES = l1l2_interface.c l1ctl.c gsmtap_util.c lapdm.c rslms.c \ + layer3.c logging.c bcch_scan.c settings.c \ + gsm48_cc.c transaction.c gsm48_mm.c gsm48_rr.c \ + gsm322.c support.c subscriber.c sysinfo.c networks.c \ + mnccms.c vty_interface.c + +bin_PROGRAMS = bcch_scan layer23 echo_test mobile + +bcch_scan_SOURCES = main.c app_bcch_scan.c +bcch_scan_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS) + +layer23_SOURCES = main.c app_phone.c +layer23_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS) + +echo_test_SOURCES = main.c app_echo_test.c +echo_test_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS) + +mobile_SOURCES = main.c app_mobile.c +mobile_LDADD = liblayer23.a $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) + + diff --git a/src/host/layer23/src/app_bcch_scan.c b/src/host/layer23/src/app_bcch_scan.c new file mode 100644 index 00000000..ce5ef9e9 --- /dev/null +++ b/src/host/layer23/src/app_bcch_scan.c @@ -0,0 +1,57 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/layer3.h> +#include <osmocom/lapdm.h> +#include <osmocom/gsmtap_util.h> +#include <osmocom/logging.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + ms = signal_data; + return fps_start(ms); + } + return 0; +} + +int l23_app_init(struct osmocom_ms *ms) +{ + /* don't do layer3_init() as we don't want an actualy L3 */ + fps_init(ms); + return register_signal_handler(SS_L1CTL, &signal_cb, NULL); +} diff --git a/src/host/layer23/src/app_echo_test.c b/src/host/layer23/src/app_echo_test.c new file mode 100644 index 00000000..285b80ab --- /dev/null +++ b/src/host/layer23/src/app_echo_test.c @@ -0,0 +1,56 @@ +/* TEST code, regularly transmit ECHO REQ packet to L1 */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/layer3.h> +#include <osmocom/lapdm.h> +#include <osmocom/gsmtap_util.h> +#include <osmocom/logging.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> + + +static struct { + struct timer_list timer; +} test_data; + +static void test_tmr_cb(void *data) +{ + struct osmocom_ms *ms = data; + + l1ctl_tx_echo_req(ms, 62); + bsc_schedule_timer(&test_data.timer, 1, 0); +} + +int l23_app_init(struct osmocom_ms *ms) +{ + test_data.timer.cb = &test_tmr_cb; + test_data.timer.data = ms; + + bsc_schedule_timer(&test_data.timer, 1, 0); + + return 0; +} diff --git a/src/host/layer23/src/app_mobile.c b/src/host/layer23/src/app_mobile.c new file mode 100644 index 00000000..52100e01 --- /dev/null +++ b/src/host/layer23/src/app_mobile.c @@ -0,0 +1,191 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by 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 <errno.h> +#include <signal.h> +#include <time.h> + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/l23_app.h> +#include <osmocom/gsm48_rr.h> +#include <osmocom/sysinfo.h> +#include <osmocom/lapdm.h> +#include <osmocom/gsmtap_util.h> +#include <osmocom/logging.h> +#include <osmocom/vty.h> +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/file.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> + +extern struct log_target *stderr_target; +static const char *config_file = "/etc/osmocom/osmocom.cfg"; +extern void *l23_ctx; +extern unsigned short vty_port; + +static int started = 0; + +int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg); + +int mobile_work(struct osmocom_ms *ms) +{ + int work = 0, w; + + do { + w = 0; + w |= gsm48_rsl_dequeue(ms); + w |= gsm48_rr_dequeue(ms); + w |= gsm48_mmxx_dequeue(ms); + w |= gsm48_mmr_dequeue(ms); + w |= gsm48_mmevent_dequeue(ms); + w |= gsm322_plmn_dequeue(ms); + w |= gsm322_cs_dequeue(ms); + w |= mncc_dequeue(ms); + if (w) + work = 1; + } while (w); + return work; +} + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + struct msgb *nmsg; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + if (started) + break; + started = 1; + ms = signal_data; + /* insert test card, if enabled */ + if (ms->settings.simtype == GSM_SIM_TYPE_TEST) + gsm_subscr_testcard(ms); + /* start PLMN + cell selection process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SWITCH_ON); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + } + return 0; +} + +int mobile_exit(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + if (!mm->power_off && started) { + struct msgb *nmsg; + + mm->power_off = 1; + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(mm->ms, nmsg); + + return -EBUSY; + } + + /* in case there is a lockup during exit */ + signal(SIGINT, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + + unregister_signal_handler(SS_L1CTL, &signal_cb, NULL); + gsm322_exit(ms); + gsm48_mm_exit(ms); + gsm48_rr_exit(ms); + gsm_subscr_exit(ms); + gsm48_cc_exit(ms); + + printf("Power off!\n"); + + return 0; +} + +static struct vty_app_info vty_info = { + .name = "OsmocomBB", + .version = PACKAGE_VERSION, + .go_parent_cb = ms_vty_go_parent, +}; + +int l23_app_init(struct osmocom_ms *ms) +{ + int rc; + struct telnet_connection dummy_conn; + +// log_parse_category_mask(stderr_target, "DRSL:DLAPDM:DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM"); + log_parse_category_mask(stderr_target, "DCS:DPLMN:DRR:DMM:DCC:DMNCC:DPAG:DSUM"); + + srand(time(NULL)); + + gsm_settings_init(ms); + gsm48_cc_init(ms); + gsm_support_init(ms); + gsm_subscr_init(ms); + gsm48_rr_init(ms); + gsm48_mm_init(ms); + INIT_LLIST_HEAD(&ms->trans_list); + ms->cclayer.mncc_recv = mncc_recv_mobile; + gsm322_init(ms); + + l23_app_work = mobile_work; + register_signal_handler(SS_L1CTL, &signal_cb, NULL); + l23_app_exit = mobile_exit; + + vty_init(&vty_info); + ms_vty_init(); + dummy_conn.priv = NULL; + rc = vty_read_config_file(config_file, &dummy_conn); + if (rc < 0) { + fprintf(stderr, "Failed to parse the config file: '%s'\n", + config_file); + fprintf(stderr, "Please check or create config file using: " + "'touch %s%s'\n", OSMOCOM_CONFDIR, config_file); + return rc; + } + telnet_init(l23_ctx, NULL, vty_port); + if (rc < 0) + return rc; + printf("VTY available on port %u.\n", vty_port); + + gsm_random_imei(&ms->settings); + + printf("Mobile initialized, please start phone now!\n"); + return 0; +} + diff --git a/src/host/layer23/src/app_phone.c b/src/host/layer23/src/app_phone.c new file mode 100644 index 00000000..de122f54 --- /dev/null +++ b/src/host/layer23/src/app_phone.c @@ -0,0 +1,60 @@ +/* "Application" code of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/layer3.h> +#include <osmocom/lapdm.h> +#include <osmocom/gsmtap_util.h> +#include <osmocom/logging.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/signal.h> + +static int signal_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_RESET: + ms = signal_data; + return l1ctl_tx_fbsb_req(ms, ms->test_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + CCCH_MODE_NONE); + break; + } + return 0; +} + + +int l23_app_init(struct osmocom_ms *ms) +{ + register_signal_handler(SS_L1CTL, &signal_cb, NULL); + return layer3_init(ms); +} diff --git a/src/host/layer23/src/bcch_scan.c b/src/host/layer23/src/bcch_scan.c new file mode 100644 index 00000000..c542129a --- /dev/null +++ b/src/host/layer23/src/bcch_scan.c @@ -0,0 +1,319 @@ +/* BCCH Scanning code for OsmocomBB */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <l1a_l23_interface.h> + +#include <osmocore/logging.h> +#include <osmocore/talloc.h> +#include <osmocore/signal.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/protocol/gsm_08_58.h> +#include <osmocore/rsl.h> + +#include <osmocom/l1ctl.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/lapdm.h> +#include <osmocom/logging.h> +#include <osmocom/gsmtap_util.h> + +/* somewhere in 05.08 */ +#define MAX_CELLS_IN_BA 32 + +/* Information about a single cell / BCCH */ +struct cell_info { + struct llist_head list; + + uint16_t band_arfcn; + uint8_t bsic; + uint8_t rxlev; + + struct { + uint16_t mcc; /* Mobile Country Code */ + uint16_t mnc; /* Mobile Network Code */ + uint16_t lac; /* Location Area Code */ + uint16_t rac; /* Routing Area Code */ + uint16_t cid; /* Cell ID */ + } id; + uint16_t ba_arfcn[MAX_CELLS_IN_BA]; + uint8_t ba_arfcn_num; + + struct { + int32_t fn_delta; /* delta to current L1 fn */ + int16_t qbit_delta; + int16_t afc_delta; + } l1_sync; +}; + +#define AFS_F_PM_DONE 0x01 +#define AFS_F_TESTED 0x02 +#define AFS_F_BCCH 0x04 +struct arfcn_state { + uint8_t rxlev; + uint8_t flags; +}; + +enum bscan_state { + BSCAN_S_NONE, + BSCAN_S_WAIT_DATA, + BSCAN_S_DONE, +}; + +enum fps_state { + FPS_S_NONE, + FPS_S_PM_GSM900, + FPS_S_PM_EGSM900, + FPS_S_PM_GSM1800, + FPS_S_BINFO, +}; + +struct full_power_scan { + /* Full Power Scan */ + enum fps_state fps_state; + struct arfcn_state arfcn_state[1024]; + + struct osmocom_ms *ms; + + /* BCCH info part */ + enum bscan_state state; + struct llist_head cell_list; + struct cell_info *cur_cell; + uint16_t cur_arfcn; + struct timer_list timer; +}; + +static struct full_power_scan fps; + +static int get_next_arfcn(struct full_power_scan *fps) +{ + unsigned int i; + uint8_t best_rxlev = 0; + int best_arfcn = -1; + + for (i = 0; i < ARRAY_SIZE(fps->arfcn_state); i++) { + struct arfcn_state *af = &fps->arfcn_state[i]; + /* skip ARFCN's where we don't have a PM */ + if (!(af->flags & AFS_F_PM_DONE)) + continue; + /* skip ARFCN's that we already tested */ + if (af->flags & AFS_F_TESTED) + continue; + /* if current arfcn_state is better than best so far, + * select it */ + if (af->rxlev > best_rxlev) { + best_rxlev = af->rxlev; + best_arfcn = i; + } + } + printf("arfcn=%d rxlev=%u\n", best_arfcn, best_rxlev); + return best_arfcn; +} + +static struct cell_info *cell_info_alloc(void) +{ + struct cell_info *ci = talloc_zero(NULL, struct cell_info); + return ci; +} + +static void cell_info_free(struct cell_info *ci) +{ + talloc_free(ci); +} + +/* start to scan for one ARFCN */ +static int _cinfo_start_arfcn(unsigned int band_arfcn) +{ + int rc; + + /* ask L1 to try to tune to new ARFCN */ + /* FIXME: decode band */ + rc = l1ctl_tx_fbsb_req(fps.ms, band_arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, CCCH_MODE_COMBINED); + if (rc < 0) + return rc; + + /* allocate new cell info structure */ + fps.cur_cell = cell_info_alloc(); + fps.cur_arfcn = band_arfcn; + fps.cur_cell->band_arfcn = band_arfcn; + /* FIXME: start timer in case we never get a sync */ + fps.state = BSCAN_S_WAIT_DATA; + bsc_schedule_timer(&fps.timer, 2, 0); + + return 0; +} + + +static void cinfo_next_cell(void *data) +{ + int rc; + + /* we've been waiting for BCCH info */ + fps.arfcn_state[fps.cur_arfcn].flags |= AFS_F_TESTED; + /* if there is a BCCH, we need to add the collected BCCH + * information to our list */ + + if (fps.arfcn_state[fps.cur_arfcn].flags & AFS_F_BCCH) + llist_add(&fps.cur_cell->list, &fps.cell_list); + else + cell_info_free(fps.cur_cell); + + rc = get_next_arfcn(&fps); + if (rc < 0) { + fps.state = BSCAN_S_DONE; + return; + } + /* start syncing to the next ARFCN */ + _cinfo_start_arfcn(rc); +} + +static void cinfo_timer_cb(void *data) +{ + switch (fps.state) { + case BSCAN_S_WAIT_DATA: + cinfo_next_cell(data); + break; + } +} + +/* Update cell_info for current cell with received BCCH info */ +static int rx_bcch_info(const uint8_t *data) +{ + struct cell_info *ci = fps.cur_cell; + struct gsm48_system_information_type_header *si_hdr; + si_hdr = (struct gsm48_system_information_type_header *) data;; + + /* we definitely have a BCCH on this channel */ + fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH; + + switch (si_hdr->system_information) { + case GSM48_MT_RR_SYSINFO_1: + /* FIXME: CA, RACH control */ + break; + case GSM48_MT_RR_SYSINFO_2: + /* FIXME: BA, NCC, RACH control */ + break; + case GSM48_MT_RR_SYSINFO_3: + /* FIXME: cell_id, LAI */ + break; + case GSM48_MT_RR_SYSINFO_4: + /* FIXME: LAI */ + break; + } + return 0; +} + +/* Update L1/SCH information (AFC/QBIT/FN offset, BSIC) */ +static int rx_sch_info() +{ + /* FIXME */ +} + +static int bscan_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct cell_info *ci = fps.cur_cell; + struct osmocom_ms *ms; + struct osmobb_meas_res *mr; + uint16_t arfcn; + int rc; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_PM_RES: + mr = signal_data; + /* check if PM result is for same MS */ + if (fps.ms != mr->ms) + return 0; + arfcn = mr->band_arfcn & 0x3ff; + /* update RxLev and notice that PM was done */ + fps.arfcn_state[arfcn].rxlev = mr->rx_lev; + fps.arfcn_state[arfcn].flags |= AFS_F_PM_DONE; + break; + case S_L1CTL_PM_DONE: + ms = signal_data; + switch (fps.fps_state) { + case FPS_S_PM_GSM900: + fps.fps_state = FPS_S_PM_EGSM900; + return l1ctl_tx_pm_req_range(ms, 955, 1023); + case FPS_S_PM_EGSM900: + fps.fps_state = FPS_S_PM_GSM1800; + return l1ctl_tx_pm_req_range(ms, 512, 885); + case FPS_S_PM_GSM1800: + /* power measurement has finished, we can start + * to actually iterate over the ARFCN's and try + * to sync to BCCHs */ + fps.fps_state = FPS_S_BINFO; + rc = get_next_arfcn(&fps); + if (rc < 0) { + fps.state = BSCAN_S_DONE; + return; + } + _cinfo_start_arfcn(rc); + break; + } + break; + case S_L1CTL_FBSB_RESP: + /* We actually got a FCCH/SCH burst */ +#if 0 + fps.arfcn_state[ci->band_arfcn].flags |= AFS_F_BCCH; + /* fallthrough */ +#else + break; +#endif + case S_L1CTL_FBSB_ERR: + /* We timed out, move on */ + if (fps.state == BSCAN_S_WAIT_DATA) + cinfo_next_cell(NULL); + break; + } + return 0; +} + +/* start the full power scan */ +int fps_start(struct osmocom_ms *ms) +{ + memset(&fps, 0, sizeof(fps)); + fps.ms = ms; + + fps.timer.cb = cinfo_timer_cb; + fps.timer.data = &fps; + + /* Start by scanning the good old GSM900 band */ + fps.fps_state = FPS_S_PM_GSM900; + return l1ctl_tx_pm_req_range(ms, 0, 124); +} + +int fps_init(void) +{ + return register_signal_handler(SS_L1CTL, &bscan_sig_cb, NULL); +} diff --git a/src/host/layer23/src/gsm322.c b/src/host/layer23/src/gsm322.c new file mode 100644 index 00000000..94e9caa2 --- /dev/null +++ b/src/host/layer23/src/gsm322.c @@ -0,0 +1,3497 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/signal.h> + +#include <osmocom/logging.h> +#include <osmocom/l1ctl.h> +#include <osmocom/file.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/networks.h> +#include <osmocom/vty.h> + +extern void *l23_ctx; + +static void gsm322_cs_timeout(void *arg); +static void gsm322_cs_loss(void *arg); +static int gsm322_cs_select(struct osmocom_ms *ms, int any); +static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg); + +#warning HACKING!!! +int hack; + +/* + * notes + */ + +/* Cell selection process + * + * The process depends on states and events (finites state machine). + * + * During states of cell selection or cell re-selection, the search for a cell + * is performed in two steps: + * + * 1. Measurement of received level of all relevant frequencies (rx-lev) + * + * 2. Receive system information messages of all relevant frequencies + * + * During this process, the results are stored in a list of all frequencies. + * This list is checked whenever a cell is selected. It depends on the results + * if the cell is 'suitable' and 'allowable' to 'camp' on. + * + * This list is also used to generate a list of available networks. + * + * The states are: + * + * - cs->list[0..1023].xxx for each cell, where + * - flags and rxlev_db are used to store outcome of cell scanning process + * - sysinfo pointing to sysinfo memory, allocated temporarily + * - cs->selected and cs->sel_* states of the current / last selected cell. + * + * + * There is a special state: GSM322_PLMN_SEARCH + * It is used to search for all cells, to find the HPLMN. This is triggered + * by a timer. Also it is used before selecting PLMN from list. + * + */ + +/* PLMN selection process + * + * The PLMN (Public Land Mobile Network = Operator's Network) has two different + * search processes: + * + * 1. Automatic search + * + * 2. Manual search + * + * The process depends on states and events (finites state machine). + * + */ + +/* File format of BA list: + * + * uint16_t mcc + * uint16_t mcc + * uint8_t freq[128]; + * where frequency 0 is bit 0 of first byte + * + * If not end-of-file, the next BA list is stored. + */ + +/* List of lists: + * + * * subscr->plmn_list + * + * The "PLMN Selector list" stores prefered networks to select during PLMN + * search process. This list is also stored in the SIM. + * + * * subscr->plmn_na + * + * The "forbidden PLMNs" list stores all networks that rejected us. The stored + * network will not be used when searching PLMN automatically. This list is + * also stored din the SIM. + * + * * plmn->forbidden_la + * + * The "forbidden LAs for roaming" list stores all location areas where roaming + * was not allowed. + * + * * cs->list[1024] + * + * This list stores measurements and cell informations during cell selection + * process. It can be used to speed up repeated cell selection. + * + * * cs->ba_list + * + * This list stores a map of frequencies used for a PLMN. If this lists exists + * for a PLMN, it helps to speedup cell scan process. + * + * * plmn->sorted_plmn + * + * This list is generated whenever a PLMN search is started and a list of PLMNs + * is required. It consists of home PLMN, PLMN Selector list, and PLMNs found + * during scan process. + */ + +/* + * event messages + */ + +static const struct value_string gsm322_event_names[] = { + { GSM322_EVENT_SWITCH_ON, "EVENT_SWITCH_ON" }, + { GSM322_EVENT_SWITCH_OFF, "EVENT_SWITCH_OFF" }, + { GSM322_EVENT_SIM_INSERT, "EVENT_SIM_INSERT" }, + { GSM322_EVENT_SIM_REMOVE, "EVENT_SIM_REMOVE" }, + { GSM322_EVENT_REG_FAILED, "EVENT_REG_FAILED" }, + { GSM322_EVENT_ROAMING_NA, "EVENT_ROAMING_NA" }, + { GSM322_EVENT_INVALID_SIM, "EVENT_INVALID_SIM" }, + { GSM322_EVENT_REG_SUCCESS, "EVENT_REG_SUCCESS" }, + { GSM322_EVENT_NEW_PLMN, "EVENT_NEW_PLMN" }, + { GSM322_EVENT_ON_PLMN, "EVENT_ON_PLMN" }, + { GSM322_EVENT_PLMN_SEARCH_START,"EVENT_PLMN_SEARCH_START" }, + { GSM322_EVENT_PLMN_SEARCH_END, "EVENT_PLMN_SEARCH_END" }, + { GSM322_EVENT_USER_RESEL, "EVENT_USER_RESEL" }, + { GSM322_EVENT_PLMN_AVAIL, "EVENT_PLMN_AVAIL" }, + { GSM322_EVENT_CHOSE_PLMN, "EVENT_CHOSE_PLMN" }, + { GSM322_EVENT_SEL_MANUAL, "EVENT_SEL_MANUAL" }, + { GSM322_EVENT_SEL_AUTO, "EVENT_SEL_AUTO" }, + { GSM322_EVENT_CELL_FOUND, "EVENT_CELL_FOUND" }, + { GSM322_EVENT_NO_CELL_FOUND, "EVENT_NO_CELL_FOUND" }, + { GSM322_EVENT_LEAVE_IDLE, "EVENT_LEAVE_IDLE" }, + { GSM322_EVENT_RET_IDLE, "EVENT_RET_IDLE" }, + { GSM322_EVENT_CELL_RESEL, "EVENT_CELL_RESEL" }, + { GSM322_EVENT_SYSINFO, "EVENT_SYSINFO" }, + { GSM322_EVENT_HPLMN_SEARCH, "EVENT_HPLMN_SEARCH" }, + { 0, NULL } +}; + +const char *get_event_name(int value) +{ + return get_value_string(gsm322_event_names, value); +} + + +/* allocate a 03.22 event message */ +struct msgb *gsm322_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm322_msg *gm; + + msg = msgb_alloc_headroom(sizeof(*gm), 0, "GSM 03.22 event"); + if (!msg) + return NULL; + + gm = (struct gsm322_msg *)msgb_put(msg, sizeof(*gm)); + gm->msg_type = msg_type; + + return msg; +} + +/* queue PLMN selection message */ +int gsm322_plmn_sendmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + msgb_enqueue(&plmn->event_queue, msg); + + return 0; +} + +/* queue cell selection message */ +int gsm322_cs_sendmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + msgb_enqueue(&cs->event_queue, msg); + + return 0; +} + +/* + * support + */ + +static int gsm322_sync_to_cell(struct gsm322_cellsel *cs) +{ + struct osmocom_ms *ms = cs->ms; + struct gsm48_sysinfo *s = cs->si; + + cs->ccch_state = GSM322_CCCH_ST_INIT; + if (s) { + if (s->ccch_conf == 1) { + LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode COMB\n"); + cs->ccch_mode = CCCH_MODE_COMBINED; + } else { + LOGP(DCS, LOGL_INFO, "Sysinfo, ccch mode NON-COMB\n"); + cs->ccch_mode = CCCH_MODE_NON_COMBINED; + } + } else { + LOGP(DCS, LOGL_INFO, "No sysinfo, ccch mode NONE\n"); + cs->ccch_mode = CCCH_MODE_NONE; + } +// printf("s->ccch_conf %d\n", cs->si->ccch_conf); + +// l1ctl_tx_reset_req(ms, L1CTL_RES_T_FULL); + return l1ctl_tx_fbsb_req(ms, cs->arfcn, + L1CTL_FBSB_F_FB01SB, 100, 0, + cs->ccch_mode); +} + +static void gsm322_unselect_cell(struct gsm322_cellsel *cs) +{ + cs->selected = 0; + cs->si = NULL; + memset(&cs->sel_si, 0, sizeof(cs->sel_si)); + cs->sel_mcc = cs->sel_mnc = cs->sel_lac = cs->sel_id = 0; +} + +/* print to DCS logging */ +static void print_dcs(void *priv, const char *fmt, ...) +{ + char buffer[1000]; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (buffer[0]) +// LOGP(DCS, LOGL_INFO, "%s", buffer); + printf("%s", buffer); +} + +/* del forbidden LA */ +int gsm322_del_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *la; + + llist_for_each_entry(la, &plmn->forbidden_la, entry) { + if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " + "LAs (mcc=%s, mnc=%s, lac=%04x)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc), lac); + llist_del(&la->entry); + talloc_free(la); + return 0; + } + } + + return -EINVAL; +} + +/* add forbidden LA */ +int gsm322_add_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, + uint16_t mnc, uint16_t lac, uint8_t cause) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *la; + + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden LAs " + "(mcc=%s, mnc=%s, lac=%04x)\n", gsm_print_mcc(mcc), + gsm_print_mnc(mnc), lac); + la = talloc_zero(l23_ctx, struct gsm322_la_list); + if (!la) + return -ENOMEM; + la->mcc = mcc; + la->mnc = mnc; + la->lac = lac; + la->cause = cause; + llist_add_tail(&la->entry, &plmn->forbidden_la); + + return 0; +} + +/* search forbidden LA */ +int gsm322_is_forbidden_la(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc, + uint16_t lac) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *la; + + llist_for_each_entry(la, &plmn->forbidden_la, entry) { + if (la->mcc == mcc && la->mnc == mnc && la->lac == lac) + return 1; + } + + return 0; +} + +/* search for PLMN in all BA lists */ +static struct gsm322_ba_list *gsm322_find_ba_list(struct gsm322_cellsel *cs, + uint16_t mcc, uint16_t mnc) +{ + struct gsm322_ba_list *ba, *ba_found = NULL; + + /* search for BA list */ + llist_for_each_entry(ba, &cs->ba_list, entry) { + if (ba->mcc == mcc + && ba->mnc == mnc) { + ba_found = ba; + break; + } + } + + return ba_found; +} + +/* search available PLMN */ +int gsm322_is_plmn_avail(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc) +{ + int i; + + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo + && cs->list[i].sysinfo->mcc == mcc + && cs->list[i].sysinfo->mnc == mnc) + return 1; + } + + return 0; +} + +/* search available HPLMN */ +int gsm322_is_hplmn_avail(struct gsm322_cellsel *cs, char *imsi) +{ + int i; + + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo + && gsm_match_mnc(cs->list[i].sysinfo->mcc, + cs->list[i].sysinfo->mnc, imsi)) + return 1; + } + + return 0; +} + +/* del forbidden LA */ +/* + * timer + */ + +/*plmn search timer event */ +static void plmn_timer_timeout(void *arg) +{ + struct gsm322_plmn *plmn = arg; + struct msgb *nmsg; + + LOGP(DPLMN, LOGL_INFO, "HPLMN search timer has fired.\n"); + + /* indicate PLMN selection T timeout */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_HPLMN_SEARCH); + if (!nmsg) + return; + gsm322_plmn_sendmsg(plmn->ms, nmsg); +} + +/* start plmn search timer */ +static void start_plmn_timer(struct gsm322_plmn *plmn, int secs) +{ + LOGP(DPLMN, LOGL_INFO, "Starting HPLMN search timer with %d minutes.\n", + secs / 60); + plmn->timer.cb = plmn_timer_timeout; + plmn->timer.data = plmn; + bsc_schedule_timer(&plmn->timer, secs, 0); +} + +/* stop plmn search timer */ +static void stop_plmn_timer(struct gsm322_plmn *plmn) +{ + if (bsc_timer_pending(&plmn->timer)) { + LOGP(DPLMN, LOGL_INFO, "Stopping pending timer.\n"); + bsc_del_timer(&plmn->timer); + } +} + +/* start cell selection timer */ +void start_cs_timer(struct gsm322_cellsel *cs, int sec, int micro) +{ + LOGP(DCS, LOGL_INFO, "Starting CS timer with %d seconds.\n", sec); + cs->timer.cb = gsm322_cs_timeout; + cs->timer.data = cs; + bsc_schedule_timer(&cs->timer, sec, micro); +} + +/* start loss timer */ +void start_loss_timer(struct gsm322_cellsel *cs, int sec, int micro) +{ + /* update timer */ + cs->timer.cb = gsm322_cs_loss; + cs->timer.data = cs; + if (bsc_timer_pending(&cs->timer)) { + struct timeval current_time; + unsigned long long currentTime; + + gettimeofday(¤t_time, NULL); + currentTime = current_time.tv_sec * 1000000LL + + current_time.tv_usec; + currentTime += sec * 1000000LL + micro; + cs->timer.timeout.tv_sec = currentTime / 1000000LL; + cs->timer.timeout.tv_usec = currentTime % 1000000LL; + + return; + } + + LOGP(DCS, LOGL_INFO, "Starting loss CS timer with %d seconds.\n", sec); + bsc_schedule_timer(&cs->timer, sec, micro); +} + +/* stop cell selection timer */ +static void stop_cs_timer(struct gsm322_cellsel *cs) +{ + if (bsc_timer_pending(&cs->timer)) { + LOGP(DCS, LOGL_INFO, "stopping pending CS timer.\n"); + bsc_del_timer(&cs->timer); + } +} + +/* + * state change + */ + +const char *plmn_a_state_names[] = { + "A0 null", + "A1 trying RPLMN", + "A2 on PLMN", + "A3 trying PLMN", + "A4 wait for PLMN to appear", + "A5 HPLMN search", + "A6 no SIM inserted" +}; + +const char *plmn_m_state_names[] = { + "M0 null", + "M1 trying RPLMN", + "M2 on PLMN", + "M3 not on PLMN", + "M4 trying PLMN", + "M5 no SIM inserted" +}; + +const char *cs_state_names[] = { + "C0 null", + "C1 normal cell selection", + "C2 stored cell selection", + "C3 camped normally", + "C4 normal cell re-selection", + "C5 choose cell", + "C6 any cell selection", + "C7 camped on any cell", + "C8 any cell re-selection", + "C9 choose any cell", + "PLMN search", + "HPLMN search" +}; + + +/* new automatic PLMN search state */ +static void new_a_state(struct gsm322_plmn *plmn, int state) +{ + if (plmn->ms->settings.plmn_mode != PLMN_MODE_AUTO) { + LOGP(DPLMN, LOGL_FATAL, "not in auto mode, please fix!\n"); + return; + } + + stop_plmn_timer(plmn); + + if (state < 0 || state >= (sizeof(plmn_a_state_names) / sizeof(char *))) + return; + + LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", + plmn_a_state_names[plmn->state], plmn_a_state_names[state]); + + plmn->state = state; +} + +/* new manual PLMN search state */ +static void new_m_state(struct gsm322_plmn *plmn, int state) +{ + if (plmn->ms->settings.plmn_mode != PLMN_MODE_MANUAL) { + LOGP(DPLMN, LOGL_FATAL, "not in manual mode, please fix!\n"); + return; + } + + if (state < 0 || state >= (sizeof(plmn_m_state_names) / sizeof(char *))) + return; + + LOGP(DPLMN, LOGL_INFO, "new state '%s' -> '%s'\n", + plmn_m_state_names[plmn->state], plmn_m_state_names[state]); + + plmn->state = state; +} + +/* new Cell selection state */ +static void new_c_state(struct gsm322_cellsel *cs, int state) +{ + if (state < 0 || state >= (sizeof(cs_state_names) / sizeof(char *))) + return; + + LOGP(DCS, LOGL_INFO, "new state '%s' -> '%s'\n", + cs_state_names[cs->state], cs_state_names[state]); + + /* stop cell selection timer, if running */ + stop_cs_timer(cs); + + /* stop scanning of power measurement */ + if (cs->powerscan) { +#ifdef TODO + stop power scanning +#endif + cs->powerscan = 0; + } + + cs->state = state; +} + +/* + * list of PLMNs + */ + +/* 4.4.3 create sorted list of PLMN + * + * the source of entries are + * + * - HPLMN + * - entries found in the SIM's PLMN Selector list + * - scanned PLMNs above -85 dB (random order) + * - scanned PLMNs below or equal -85 (by received level) + * + * NOTE: + * + * The list only includes networks found at last scan. + * + * The list always contains HPLMN if available, even if not used by PLMN + * search process at some conditions. + * + * The list contains all PLMNs even if not allowed, so entries have to be + * removed when selecting from the list. (In case we use manual cell selection, + * we need to provide non-allowed networks also.) + */ +static int gsm322_sort_list(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_list *sim_entry; + struct gsm_sub_plmn_na *na_entry; + struct llist_head temp_list; + struct gsm322_plmn_list *temp, *found; + struct llist_head *lh, *lh2; + int i, entries, move; + int8_t search_db = 0; + + /* flush list */ + llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) { + llist_del(lh); + talloc_free(lh); + } + + /* Create a temporary list of all networks */ + INIT_LLIST_HEAD(&temp_list); + for (i = 0; i <= 1023; i++) { + if (!(cs->list[i].flags & GSM322_CS_FLAG_TEMP_AA) + || !cs->list[i].sysinfo) + continue; + + /* search if network has multiple cells */ + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (temp->mcc == cs->list[i].sysinfo->mcc + && temp->mnc == cs->list[i].sysinfo->mnc) { + found = temp; + break; + } + } + /* update or create */ + if (found) { + if (cs->list[i].rxlev_db > found->rxlev_db) + found->rxlev_db = cs->list[i].rxlev_db; + } else { + temp = talloc_zero(l23_ctx, struct gsm322_plmn_list); + if (!temp) + return -ENOMEM; + temp->mcc = cs->list[i].sysinfo->mcc; + temp->mnc = cs->list[i].sysinfo->mnc; + temp->rxlev_db = cs->list[i].rxlev_db; + llist_add_tail(&temp->entry, &temp_list); + } + } + + /* move Home PLMN, if in list, else add it */ + if (subscr->sim_valid) { + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (gsm_match_mnc(temp->mcc, temp->mnc, subscr->imsi)) { + found = temp; + break; + } + } + if (found) { + /* move */ + llist_del(&found->entry); + llist_add_tail(&found->entry, &plmn->sorted_plmn); + } + } + + /* move entries if in SIM's PLMN Selector list */ + llist_for_each_entry(sim_entry, &subscr->plmn_list, entry) { + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (temp->mcc == sim_entry->mcc + && temp->mnc == sim_entry->mnc) { + found = temp; + break; + } + } + if (found) { + llist_del(&found->entry); + llist_add_tail(&found->entry, &plmn->sorted_plmn); + } + } + + /* move PLMN above -85 dBm in random order */ + entries = 0; + llist_for_each_entry(temp, &temp_list, entry) { + if (temp->rxlev_db > -85) + entries++; + } + while(entries) { + move = random() % entries; + i = 0; + llist_for_each_entry(temp, &temp_list, entry) { + if (temp->rxlev_db > -85) { + if (i == move) { + llist_del(&temp->entry); + llist_add_tail(&temp->entry, + &plmn->sorted_plmn); + break; + } + i++; + } + } + entries--; + } + + /* move ohter PLMN in decreasing order */ + while(1) { + found = NULL; + llist_for_each_entry(temp, &temp_list, entry) { + if (!found + || temp->rxlev_db > search_db) { + search_db = temp->rxlev_db; + found = temp; + } + } + if (!found) + break; + llist_del(&found->entry); + llist_add_tail(&found->entry, &plmn->sorted_plmn); + } + + /* mark forbidden PLMNs, if in list of forbidden networks */ + i = 0; + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { + llist_for_each_entry(na_entry, &subscr->plmn_na, entry) { + if (temp->mcc == na_entry->mcc + && temp->mnc == na_entry->mnc) { + temp->cause = na_entry->cause; + break; + } + } + LOGP(DPLMN, LOGL_INFO, "Creating Sorted PLMN list. " + "(%02d: mcc=%s mnc=%s allowed=%s rx-lev=%d)\n", + i, gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), (temp->cause) ? "no ":"yes", + temp->rxlev_db); + i++; + } + + gsm322_dump_sorted_plmn(ms); + + return 0; +} + +/* + * handler for automatic search + */ + +/* go On PLMN state */ +static int gsm322_a_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + + new_a_state(plmn, GSM322_A2_ON_PLMN); + + /* start timer, if on VPLMN of home country OR special case */ + if (!gsm_match_mnc(plmn->mcc, plmn->mnc, subscr->imsi) + && (subscr->always_search_hplmn + || gsm_match_mcc(plmn->mcc, subscr->imsi))) { + if (subscr->sim_valid && subscr->t6m_hplmn) + start_plmn_timer(plmn, subscr->t6m_hplmn * 360); + else + start_plmn_timer(plmn, 30 * 360); + } else + stop_plmn_timer(plmn); + + return 0; +} + +/* indicate selected PLMN */ +static int gsm322_a_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + return gsm322_a_go_on_plmn(ms, msg); +} + +/* no (more) PLMN in list */ +static int gsm322_a_no_more_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + int found; + + /* any allowable PLMN available? */ + plmn->mcc = plmn->mnc = 0; + found = gsm322_cs_select(ms, 0); + + /* if no PLMN in list */ + if (found < 0) { + LOGP(DPLMN, LOGL_INFO, "Not any PLNs allowable.\n"); + + new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); + + /* we must forward this, otherwhise "Any cell selection" + * will not start automatically. + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + + /* select first PLMN in list */ + plmn->mcc = cs->list[found].sysinfo->mcc; + plmn->mnc = cs->list[found].sysinfo->mnc; + + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s %s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + /* go On PLMN */ + return gsm322_a_indicate_selected(ms, msg); +} + +/* select first PLMN in list */ +static int gsm322_a_sel_first_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct gsm322_plmn_list *plmn_entry; + struct gsm322_plmn_list *plmn_first = NULL; + int i; + + /* generate list */ + gsm322_sort_list(ms); + + /* select first entry */ + i = 0; + llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { + /* if last selected PLMN was HPLMN, we skip that */ + if (gsm_match_mnc(plmn_entry->mcc, plmn_entry->mnc, + subscr->imsi) + && plmn_entry->mcc == plmn->mcc + && plmn_entry->mnc == plmn->mnc) { + LOGP(DPLMN, LOGL_INFO, "Skip HPLMN, because it was " + "previously selected.\n"); + i++; + continue; + } + /* select first allowed network */ + if (!plmn_entry->cause) { + plmn_first = plmn_entry; + break; + } + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it " + "not allowed (cause %d).\n", plmn_entry->mcc, + plmn_entry->mnc, plmn_entry->cause); + i++; + } + plmn->plmn_curr = i; + + /* if no PLMN in list */ + if (!plmn_first) { + LOGP(DPLMN, LOGL_INFO, "No PLMN in list.\n"); + gsm322_a_no_more_plmn(ms, msg); + + return 0; + } + + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " + "mnc=%s %s, %s)\n", plmn->plmn_curr, + gsm_print_mcc(plmn_first->mcc), gsm_print_mnc(plmn_first->mnc), + gsm_get_mcc(plmn_first->mcc), + gsm_get_mnc(plmn_first->mcc, plmn_first->mnc)); + + /* set current network */ + plmn->mcc = plmn_first->mcc; + plmn->mnc = plmn_first->mnc; + + new_a_state(plmn, GSM322_A3_TRYING_PLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* select next PLMN in list */ +static int gsm322_a_sel_next_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + struct gsm322_plmn_list *plmn_entry; + struct gsm322_plmn_list *plmn_next = NULL; + int i, ii; + + /* select next entry from list */ + i = 0; + ii = plmn->plmn_curr + 1; + llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { + /* skip previously selected networks */ + if (i < ii) { + i++; + continue; + } + /* select next allowed network */ + if (!plmn_entry->cause) { + plmn_next = plmn_entry; + break; + } + LOGP(DPLMN, LOGL_INFO, "Skip PLMN (%02d: mcc=%03d), because it " + "not allowed (cause %d).\n", plmn_entry->mcc, + plmn_entry->mnc, plmn_entry->cause); + i++; + } + plmn->plmn_curr = i; + + /* if no more PLMN in list */ + if (!plmn_next) { + LOGP(DPLMN, LOGL_INFO, "No more PLMN in list.\n"); + gsm322_a_no_more_plmn(ms, msg); + return 0; + } + + + LOGP(DPLMN, LOGL_INFO, "Selecting PLMN from list. (%02d: mcc=%s " + "mnc=%s %s, %s)\n", plmn->plmn_curr, + gsm_print_mcc(plmn_next->mcc), gsm_print_mnc(plmn_next->mnc), + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + /* set next network */ + plmn->mcc = plmn_next->mcc; + plmn->mnc = plmn_next->mnc; + + new_a_state(plmn, GSM322_A3_TRYING_PLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* User re-selection event */ +static int gsm322_a_user_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_plmn_list *plmn_entry; + struct gsm322_plmn_list *plmn_found = NULL; + + if (!subscr->sim_valid) { + return 0; + } + + /* try again later, if not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); + + return 0; + } + + /* search current PLMN in list */ + llist_for_each_entry(plmn_entry, &plmn->sorted_plmn, entry) { + if (plmn_entry->mcc == plmn->mcc + && plmn_entry->mnc == plmn->mnc) + plmn_found = plmn_entry; + break; + } + + /* abort if list is empty */ + if (!plmn_found) { + LOGP(DPLMN, LOGL_INFO, "Selected PLMN not in list, strange!\n"); + return 0; + } + + LOGP(DPLMN, LOGL_INFO, "Movin selected PLMN to the bottom of the list " + "and restarting PLMN search process.\n"); + + /* move entry to end of list */ + llist_del(&plmn_found->entry); + llist_add_tail(&plmn_found->entry, &plmn->sorted_plmn); + + /* select first PLMN in list */ + return gsm322_a_sel_first_plmn(ms, msg); +} + +/* PLMN becomes available */ +static int gsm322_a_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + + if (subscr->plmn_valid && subscr->plmn_mcc == gm->mcc + && subscr->plmn_mnc == gm->mnc) { + /* go On PLMN */ + plmn->mcc = gm->mcc; + plmn->mnc = gm->mnc; + LOGP(DPLMN, LOGL_INFO, "RPLMN became available.\n"); + return gsm322_a_go_on_plmn(ms, msg); + } else { + /* select first PLMN in list */ + LOGP(DPLMN, LOGL_INFO, "PLMN became available, start PLMN " + "search process.\n"); + return gsm322_a_sel_first_plmn(ms, msg); + } +} + +/* loss of radio coverage */ +static int gsm322_a_loss_of_radio(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + int found; + struct msgb *nmsg; + + /* any PLMN available */ + found = gsm322_cs_select(ms, 0); + + /* if PLMN in list */ + if (found >= 0) { + LOGP(DPLMN, LOGL_INFO, "PLMN available (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc( + cs->list[found].sysinfo->mcc), + gsm_print_mnc(cs->list[found].sysinfo->mnc), + gsm_get_mcc(cs->list[found].sysinfo->mcc), + gsm_get_mnc(cs->list[found].sysinfo->mcc, + cs->list[found].sysinfo->mnc)); + return gsm322_a_sel_first_plmn(ms, msg); + } + + LOGP(DPLMN, LOGL_INFO, "PLMN not available.\n"); + + plmn->mcc = plmn->mnc = 0; + + new_a_state(plmn, GSM322_A4_WAIT_FOR_PLMN); + + /* Tell cell selection process to handle "no cell found". */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched on OR SIM is inserted OR removed */ +static int gsm322_a_switch_on(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + LOGP(DSUM, LOGL_INFO, "SIM is removed\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n"); + new_a_state(plmn, GSM322_A6_NO_SIM); + + return 0; + } + + /* if there is a registered PLMN */ + if (subscr->plmn_valid) { + /* select the registered PLMN */ + plmn->mcc = subscr->plmn_mcc; + plmn->mnc = subscr->plmn_mnc; + + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + + new_a_state(plmn, GSM322_A1_TRYING_RPLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + + /* initiate search at cell selection */ + LOGP(DSUM, LOGL_INFO, "Search for network\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched off */ +static int gsm322_a_switch_off(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + new_a_state(plmn, GSM322_A0_NULL); + + return 0; +} + +static int gsm322_a_sim_insert(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n"); + return 0; +} + +/* SIM is removed */ +static int gsm322_a_sim_removed(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* indicate SIM remove to cell selection process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return gsm322_a_switch_on(ms, msg); +} + +/* location update response: "Roaming not allowed" */ +static int gsm322_a_roaming_na(struct osmocom_ms *ms, struct msgb *msg) +{ + /* store in list of forbidden LAs is done in gsm48* */ + + return gsm322_a_sel_first_plmn(ms, msg); +} + +/* On VPLMN of home country and timeout occurs */ +static int gsm322_a_hplmn_search_start(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + /* try again later, if not idle and not camping */ + if (rr->state != GSM48_RR_ST_IDLE + || cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DPLMN, LOGL_INFO, "Not camping, wait some more.\n"); + start_plmn_timer(plmn, 60); + + return 0; + } + + new_a_state(plmn, GSM322_A5_HPLMN_SEARCH); + + /* initiate search at cell selection */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* manual mode selected */ +static int gsm322_a_sel_manual(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* restart state machine */ + gsm322_a_switch_off(ms, msg); + ms->settings.plmn_mode = PLMN_MODE_MANUAL; + gsm322_m_switch_on(ms, msg); + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; +} + +/* + * handler for manual search + */ + +/* display PLMNs and to Not on PLMN */ +static int gsm322_m_display_plmns(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_sub_plmn_list *temp; + + /* generate list */ + gsm322_sort_list(ms); + + vty_notify(ms, NULL); + switch (msg_type) { + case GSM322_EVENT_REG_FAILED: + vty_notify(ms, "Failed to register to network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM322_EVENT_NO_CELL_FOUND: + vty_notify(ms, "No cell found for network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + case GSM322_EVENT_ROAMING_NA: + vty_notify(ms, "Roaming not allowed to network %s, %s " + "(%s, %s)\n", + gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + break; + } + + if (llist_empty(&plmn->sorted_plmn)) + vty_notify(ms, "Search network!\n"); + else { + vty_notify(ms, "Search or select from network:\n"); + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + vty_notify(ms, " Network %s, %s (%s, %s)\n", + gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), + gsm_get_mcc(temp->mcc), + gsm_get_mnc(temp->mcc, temp->mnc)); + } + + /* go Not on PLMN state */ + new_m_state(plmn, GSM322_M3_NOT_ON_PLMN); + + return 0; +} + +/* user starts reselection */ +static int gsm322_m_user_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + return 0; + } + + /* try again later, if not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DPLMN, LOGL_INFO, "Not idle, rejecting.\n"); + + return 0; + } + + /* initiate search at cell selection */ + vty_notify(ms, NULL); + vty_notify(ms, "Searching Network, please wait...\n"); + LOGP(DPLMN, LOGL_INFO, "User re-select, start PLMN search first.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched on OR SIM is inserted OR removed */ +static int gsm322_m_switch_on(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + + if (!subscr->sim_valid) { + LOGP(DSUM, LOGL_INFO, "SIM is removed\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on without SIM.\n"); + new_m_state(plmn, GSM322_M5_NO_SIM); + + return 0; + } + + /* if there is a registered PLMN */ + if (subscr->plmn_valid) { + struct msgb *nmsg; + + /* select the registered PLMN */ + plmn->mcc = subscr->plmn_mcc; + plmn->mnc = subscr->plmn_mnc; + + LOGP(DSUM, LOGL_INFO, "Start search of last registered PLMN " + "(mcc=%s mnc=%s %s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "Use RPLMN (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), + gsm_print_mnc(plmn->mnc), gsm_get_mcc(plmn->mcc), + gsm_get_mnc(plmn->mcc, plmn->mnc)); + + new_m_state(plmn, GSM322_M1_TRYING_RPLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; + } + + /* initiate search at cell selection */ + LOGP(DSUM, LOGL_INFO, "Search for network\n"); + LOGP(DPLMN, LOGL_INFO, "Switch on, start PLMN search first.\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Searching Network, please wait...\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_START); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* MS is switched off */ +static int gsm322_m_switch_off(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + stop_plmn_timer(plmn); + + new_m_state(plmn, GSM322_M0_NULL); + + return 0; +} + +static int gsm322_m_sim_insert(struct osmocom_ms *ms, struct msgb *msg) +{ + LOGP(DPLMN, LOGL_INFO, "SIM already inserted when switched on.\n"); + return 0; +} + +/* SIM is removed */ +static int gsm322_m_sim_removed(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + + stop_plmn_timer(plmn); + + /* indicate SIM remove to cell selection process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return gsm322_m_switch_on(ms, msg); +} + +/* go to On PLMN state */ +static int gsm322_m_go_on_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm_subscriber *subscr = &ms->subscr; + + /* if selected PLMN is in list of forbidden PLMNs */ + gsm_subscr_del_forbidden_plmn(subscr, plmn->mcc, plmn->mnc); + + /* set last registered PLMN */ + subscr->plmn_valid = 1; + subscr->plmn_mcc = plmn->mcc; + subscr->plmn_mnc = plmn->mnc; +#ifdef TODO + store on sim +#endif + + new_m_state(plmn, GSM322_M2_ON_PLMN); + + return 0; +} + +/* indicate selected PLMN */ +static int gsm322_m_indicate_selected(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + return gsm322_m_go_on_plmn(ms, msg); +} + +/* previously selected PLMN becomes available again */ +static int gsm322_m_plmn_avail(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + + new_m_state(plmn, GSM322_M1_TRYING_RPLMN); + + if (cs->mcc != plmn->mcc || cs->mnc != plmn->mnc) { + struct msgb *nmsg; + + LOGP(DPLMN, LOGL_INFO, "PLMN available, but currently not " + "selected, so start selection.\n"); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + } + + return 0; +} + +/* the user has selected given PLMN */ +static int gsm322_m_choose_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + struct msgb *nmsg; + + /* use user selection */ + plmn->mcc = gm->mcc; + plmn->mnc = gm->mnc; + + vty_notify(ms, NULL); + vty_notify(ms, "Selected Network: %s, %s\n", + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + LOGP(DPLMN, LOGL_INFO, "User selects PLMN. (mcc=%s mnc=%s " + "%s, %s)\n", gsm_print_mcc(plmn->mcc), gsm_print_mnc(plmn->mnc), + gsm_get_mcc(plmn->mcc), gsm_get_mnc(plmn->mcc, plmn->mnc)); + + new_m_state(plmn, GSM322_M4_TRYING_PLMN); + + /* indicate New PLMN */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NEW_PLMN); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* auto mode selected */ +static int gsm322_m_sel_auto(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* restart state machine */ + gsm322_m_switch_off(ms, msg); + ms->settings.plmn_mode = PLMN_MODE_AUTO; + gsm322_a_switch_on(ms, msg); + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_USER_PLMN_SEL); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; +} + +/* if no cell is found in other states than in *_TRYING_* states */ +static int gsm322_am_no_cell_found(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + + /* Tell cell selection process to handle "no cell found". */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_cs_sendmsg(ms, nmsg); + + return 0; +} + +/* + * cell scanning process + */ + +/* select a suitable and allowable cell */ +static int gsm322_cs_select(struct osmocom_ms *ms, int any) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_sysinfo *s; + int i, found = -1, power = 0; + uint8_t flags, mask; + uint16_t acc_class; + + /* set out access class depending on the cell selection type */ + if (any) { + acc_class = subscr->acc_class | 0x0400; /* add emergency */ + LOGP(DCS, LOGL_INFO, "Select using access class with Emergency " + "class.\n"); + } else { + acc_class = subscr->acc_class; + LOGP(DCS, LOGL_INFO, "Select using access class \n"); + } + + /* flags to match */ + mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL | GSM322_CS_FLAG_SYSINFO; + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) + mask |= GSM322_CS_FLAG_BA; + flags = mask; /* all masked flags are requied */ + + /* loop through all scanned frequencies and select cell */ + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~GSM322_CS_FLAG_TEMP_AA; + s = cs->list[i].sysinfo; + + /* channel has no informations for us */ + if (!s || (cs->list[i].flags & mask) != flags) { + continue; + } + + /* check C1 criteria not fullfilled */ + // TODO: C1 is also dependant on power class and max power + if (cs->list[i].rxlev_db < s->rxlev_acc_min_db) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: C1 criteria " + "not met. (rxlev=%d < min=%d)\n", i, + cs->list[i].rxlev_db, s->rxlev_acc_min_db); + continue; + } + + /* if cell is barred and we don't override */ + if (!subscr->acc_barr + && (cs->list[i].flags & GSM322_CS_FLAG_BARRED)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is " + "barred.\n", i); + continue; + } + + /* if cell is in list of forbidden LAs */ + if (!subscr->acc_barr + && (cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Cell is in " + "list of forbidden LAs. (mcc=%s mnc=%s " + "lai=%04x)\n", i, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + continue; + } + + /* if we have no access to the cell and we don't override */ + if (!subscr->acc_barr + && !(acc_class & (s->class_barr ^ 0xffff))) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: Class is " + "barred for out access. (access=%04x " + "barred=%04x)\n", i, acc_class, s->class_barr); + continue; + } + + /* store temporary available and allowable flag */ + cs->list[i].flags |= GSM322_CS_FLAG_TEMP_AA; + + /* if we search a specific PLMN, but it does not match */ + if (!any && cs->mcc && (cs->mcc != s->mcc + || cs->mnc != s->mnc)) { + LOGP(DCS, LOGL_INFO, "Skip frequency %d: PLMN of cell " + "does not match target PLMN. (mcc=%s " + "mnc=%s)\n", i, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc)); + continue; + } + + LOGP(DCS, LOGL_INFO, "Cell frequency %d: Cell found, (rxlev=%d " + "mcc=%s mnc=%s lac=%04x %s, %s)\n", i, + cs->list[i].rxlev_db, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac, gsm_get_mcc(s->mcc), + gsm_get_mnc(s->mcc, s->mnc)); + + /* find highest power cell */ + if (found < 0 || cs->list[i].rxlev_db > power) { + power = cs->list[i].rxlev_db; + found = i; + } + } + + if (found >= 0) + LOGP(DCS, LOGL_INFO, "Cell frequency %d selected.\n", found); + + return found; +} + +/* tune to first/next unscanned frequency and search for PLMN */ +static int gsm322_cs_scan(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i, j; + uint8_t mask, flags; + uint32_t weight = 0, test = cs->scan_state; + + /* search for strongest unscanned cell */ + mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL; + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) + mask |= GSM322_CS_FLAG_BA; + flags = mask; /* all masked flags are requied */ + for (i = 0; i <= 1023; i++) { + /* skip if band has enough frequencies scanned (3.2.1) */ + for (j = 0; gsm_sup_smax[j].max; j++) { + if (gsm_sup_smax[j].end > gsm_sup_smax[j].start) { + if (gsm_sup_smax[j].start >= i + && gsm_sup_smax[j].end <= i) + break; + } else { + if (gsm_sup_smax[j].end <= i + || gsm_sup_smax[j].start >= i) + break; + } + } + if (gsm_sup_smax[j].max) { + if (gsm_sup_smax[j].temp == gsm_sup_smax[j].max) + continue; + } + /* search for unscanned frequency */ + if ((cs->list[i].flags & mask) == flags) { + /* weight depends on the power level + * if it is the same, it depends on arfcn + */ + test = cs->list[i].rxlev_db + 128; + test = (test << 16) | i; + if (test >= cs->scan_state) + continue; + if (test > weight) + weight = test; + } + } + cs->scan_state = weight; + + if (!weight) + gsm322_dump_cs_list(cs, GSM322_CS_FLAG_SYSINFO, print_dcs, + NULL); + + /* special case for PLMN search */ + if (cs->state == GSM322_PLMN_SEARCH && !weight) { + struct msgb *nmsg; + + /* create AA flag */ + cs->mcc = cs->mnc = 0; + gsm322_cs_select(ms, 0); + + new_c_state(cs, GSM322_C0_NULL); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_SEARCH_END); + LOGP(DCS, LOGL_INFO, "PLMN search finished.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* special case for HPLMN search */ + if (cs->state == GSM322_HPLMN_SEARCH && !weight) { + struct msgb *nmsg; + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + LOGP(DCS, LOGL_INFO, "HPLMN search finished, no cell.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + cs->arfcn = cs->sel_arfcn; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency %d (rxlev " + "%d).\n", cs->arfcn, cs->list[cs->arfcn].rxlev_db); + hack = 5; + gsm322_sync_to_cell(cs); +// start_cs_timer(cs, ms->support.sync_to, 0); + + return 0; + } + + /* if all frequencies have been searched */ + if (!weight) { + struct msgb *nmsg; +#if 0 + int found, any = 0; + + LOGP(DCS, LOGL_INFO, "All frequencies scanned.\n"); + + /* just see, if we search for any cell */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + any = 1; + + found = gsm322_cs_select(ms, any); + + /* if found */ + if (found >= 0) { + struct gsm322_plmn *plmn = &ms->plmn; + + LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); + /* tune */ + cs->arfcn = found; + cs->si = cs->list[cs->arfcn].sysinfo; + hack = 5; + gsm322_sync_to_cell(cs); + + /* selected PLMN (manual) or any PLMN (auto) */ + switch (ms->settings.plmn_mode) { + case PLMN_MODE_AUTO: + if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + case PLMN_MODE_MANUAL: + if (plmn->state == GSM322_M3_NOT_ON_PLMN + && gsm322_is_plmn_avail(cs, plmn->mcc, + plmn->mnc)) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + } + + /* set selected cell */ + cs->selected = 1; + cs->sel_arfcn = cs->arfcn; + memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + + /* tell CS process about available cell */ + LOGP(DCS, LOGL_INFO, "Cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + } else { +#endif + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* tell CS process about no cell available */ + LOGP(DCS, LOGL_INFO, "No cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); +// } + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; + } + + /* NOTE: We might already have system information from previous + * scan. But we need recent informations, so we scan again! + */ + + /* Tune to frequency for a while, to receive broadcasts. */ + cs->arfcn = weight & 1023; + LOGP(DCS, LOGL_INFO, "Scanning frequency %d (rxlev %d).\n", cs->arfcn, + cs->list[cs->arfcn].rxlev_db); + hack = 5; + gsm322_sync_to_cell(cs); +// start_cs_timer(cs, ms->support.sync_to, 0); + + /* Allocate/clean system information. */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) + memset(cs->list[cs->arfcn].sysinfo, 0, + sizeof(struct gsm48_sysinfo)); + else + cs->list[cs->arfcn].sysinfo = talloc_zero(l23_ctx, + struct gsm48_sysinfo); + if (!cs->list[cs->arfcn].sysinfo) + exit(-ENOMEM); + cs->si = cs->list[cs->arfcn].sysinfo; + + /* increase scan counter for each maximum scan range */ + if (gsm_sup_smax[j].max) { + LOGP(DCS, LOGL_INFO, "%d frequencies left in band %d..%d\n", + gsm_sup_smax[j].max - gsm_sup_smax[j].temp, + gsm_sup_smax[j].start, gsm_sup_smax[j].end); + gsm_sup_smax[j].temp++; + } + + return 0; +} + +/* check if cell is now suitable and allowable */ +static int gsm322_cs_store(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *nmsg; + int found, any = 0; + + if (cs->state != GSM322_C2_STORED_CELL_SEL + && cs->state != GSM322_C1_NORMAL_CELL_SEL + && cs->state != GSM322_C6_ANY_CELL_SEL + && cs->state != GSM322_C4_NORMAL_CELL_RESEL + && cs->state != GSM322_C8_ANY_CELL_RESEL + && cs->state != GSM322_C5_CHOOSE_CELL + && cs->state != GSM322_C9_CHOOSE_ANY_CELL + && cs->state != GSM322_PLMN_SEARCH + && cs->state != GSM322_HPLMN_SEARCH) { + LOGP(DCS, LOGL_FATAL, "This must only happen during cell " + "(re-)selection, please fix!\n"); + return -EINVAL; + } + + /* store sysinfo */ + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_SYSINFO; + if (s->cell_barr + && !(cs->list[cs->arfcn].sysinfo && cs->list[cs->arfcn].sysinfo->sp && + cs->list[cs->arfcn].sysinfo->sp_cbq)) + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_BARRED; + else + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_BARRED; + +#if 0 + cs->list[cs->arfcn].min_db = s->rxlev_acc_min_db; + cs->list[cs->arfcn].class_barr = s->class_barr; + cs->list[cs->arfcn].max_pwr = s->ms_txpwr_max_ccch; +#endif + + /* store selected network */ + if (s->mcc) { +#if 0 + cs->list[cs->arfcn].mcc = s->mcc; + cs->list[cs->arfcn].mnc = s->mnc; + cs->list[cs->arfcn].lac = s->lac; +#endif + + if (gsm322_is_forbidden_la(ms, s->mcc, s->mnc, s->lac)) + cs->list[cs->arfcn].flags |= GSM322_CS_FLAG_FORBIDD; + else + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_FORBIDD; + } + + LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell found. (rxlev=%d " + "mcc=%s mnc=%s lac=%04x)\n", cs->arfcn, + cs->list[cs->arfcn].rxlev_db, gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + + /* special case for PLMN search */ + if (cs->state == GSM322_PLMN_SEARCH) + /* tune to next cell */ + return gsm322_cs_scan(ms); + + /* special case for HPLMN search */ + if (cs->state == GSM322_HPLMN_SEARCH) { + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + if (!gsm322_is_hplmn_avail(cs, subscr->imsi)) + /* tune to next cell */ + return gsm322_cs_scan(ms); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + LOGP(DCS, LOGL_INFO, "HPLMN search finished, cell found.\n"); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* just see, if we search for any cell */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL) + any = 1; + + found = gsm322_cs_select(ms, any); + + /* if not found */ + if (found < 0) { + LOGP(DCS, LOGL_INFO, "Cell not suitable and allowable.\n"); + /* tune to next cell */ + return gsm322_cs_scan(ms); + } + + LOGP(DCS, LOGL_INFO, "Tune to frequency %d.\n", found); + /* tune */ + cs->arfcn = found; + cs->si = cs->list[cs->arfcn].sysinfo; + hack = 5; + gsm322_sync_to_cell(cs); + + /* selected PLMN (manual) or any PLMN (auto) */ + switch (ms->settings.plmn_mode) { + case PLMN_MODE_AUTO: + if (plmn->state == GSM322_A4_WAIT_FOR_PLMN) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + case PLMN_MODE_MANUAL: + if (plmn->state == GSM322_M3_NOT_ON_PLMN + && gsm322_is_plmn_avail(cs, plmn->mcc, + plmn->mnc)) { + /* PLMN becomes available */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_PLMN_AVAIL); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + } + break; + } + + /* set selected cell */ + cs->selected = 1; + cs->sel_arfcn = cs->arfcn; + memcpy(&cs->sel_si, cs->si, sizeof(cs->sel_si)); + cs->sel_mcc = cs->si->mcc; + cs->sel_mnc = cs->si->mnc; + cs->sel_lac = cs->si->lac; + cs->sel_id = cs->si->cell_id; + + /* tell CS process about available cell */ + LOGP(DCS, LOGL_INFO, "Cell available.\n"); + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; +} + +/* process system information when returing to idle mode */ +struct gsm322_ba_list *gsm322_cs_sysinfo_sacch(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm322_ba_list *ba = NULL; + int i; + uint8_t freq[128]; + + /* collect system information received during dedicated mode */ + if (s->si5 + && (!s->nb_ext_ind_si5 + || (s->si5bis && s->nb_ext_ind_si5 && !s->nb_ext_ind_si5bis) + || (s->si5bis && s->si5ter && s->nb_ext_ind_si5 + && s->nb_ext_ind_si5bis))) { + /* find or create ba list */ + ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + if (!ba) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return NULL; + ba->mcc = s->mcc; + ba->mnc = s->mnc; + llist_add_tail(&ba->entry, &cs->ba_list); + } + /* update (add) ba list */ + memcpy(freq, ba->freq, sizeof(freq)); + for (i = 0; i <= 1023; i++) { + if ((s->freq[i].mask & FREQ_TYPE_REP)) + freq[i >> 3] |= (1 << (i & 7)); + } + if (!!memcmp(freq, ba->freq, sizeof(freq))) { + LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " + "%s, %s).\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + memcpy(ba->freq, freq, sizeof(freq)); + } + } + + return ba; +} + +/* store BA whenever a system informations changes */ +static int gsm322_store_ba_list(struct gsm322_cellsel *cs, + struct gsm48_sysinfo *s) +{ + struct gsm322_ba_list *ba; + int i; + uint8_t freq[128]; + + /* find or create ba list */ + ba = gsm322_find_ba_list(cs, s->mcc, s->mnc); + if (!ba) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return -ENOMEM; + ba->mcc = s->mcc; + ba->mnc = s->mnc; + llist_add_tail(&ba->entry, &cs->ba_list); + } + /* update ba list */ + memset(freq, 0, sizeof(freq)); + freq[cs->arfcn >> 3] |= (1 << (cs->arfcn & 7)); + for (i = 0; i <= 1023; i++) { + if ((s->freq[i].mask & + (FREQ_TYPE_SERV | FREQ_TYPE_NCELL))) + freq[i >> 3] |= (1 << (i & 7)); + } + if (!!memcmp(freq, ba->freq, sizeof(freq))) { + LOGP(DCS, LOGL_INFO, "New BA list (mcc=%s mnc=%s " + "%s, %s).\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + memcpy(ba->freq, freq, sizeof(freq)); + } + + return 0; +} + +/* process system information during camping on a cell */ +static int gsm322_c_camp_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + struct msgb *nmsg; + + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DCS, LOGL_INFO, "Ignoring in dedicated mode.\n"); + return -EBUSY; + } + + /* Store BA if we have full system info about cells and neigbor cells. + * Depending on the extended bit in the channel description, + * we require more or less system informations about neighbor cells + */ + if (s->mcc + && s->mnc + && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter) + && s->si1 + && s->si2 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) + gsm322_store_ba_list(cs, s); + + /* update sel_si, if all relevant system informations received */ + if (s->si1 && s->si2 && s->si3 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) { + if (cs->selected) { + LOGP(DCS, LOGL_INFO, "Sysinfo of selected cell is " + "updated.\n"); + memcpy(&cs->sel_si, s, sizeof(cs->sel_si)); + //gsm48_sysinfo_dump(s, print_dcs, NULL); + } + } + + /* check for barred cell */ + if (gm->sysinfo == GSM48_MT_RR_SYSINFO_1) { + /* check if cell becomes barred */ + if (!subscr->acc_barr && s->cell_barr + && !(cs->list[cs->arfcn].sysinfo + && cs->list[cs->arfcn].sysinfo->sp + && cs->list[cs->arfcn].sysinfo->sp_cbq)) { + LOGP(DCS, LOGL_INFO, "Cell becomes barred.\n"); + trigger_resel: + /* mark cell as unscanned */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + cs->arfcn); + talloc_free(cs->list[cs->arfcn].sysinfo); + cs->list[cs->arfcn].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + /* trigger reselection without queueing, + * because other sysinfo message may be queued + * before + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; + } + /* check if cell access becomes barred */ + if (!((subscr->acc_class & 0xfbff) + & (s->class_barr ^ 0xffff))) { + LOGP(DCS, LOGL_INFO, "Cell access becomes barred.\n"); + goto trigger_resel; + } + } + + /* check if MCC, MNC, LAC, cell ID changes */ + if (cs->sel_mcc != s->mcc || cs->sel_mnc != s->mnc + || cs->sel_lac != s->lac) { + LOGP(DCS, LOGL_NOTICE, "Cell changes location area. " + "This is not good!\n"); + goto trigger_resel; + } + if (cs->sel_id != s->cell_id) { + LOGP(DCS, LOGL_NOTICE, "Cell changes cell ID. " + "This is not good!\n"); + goto trigger_resel; + } + + return 0; +} + +/* process system information during channel scanning */ +static int gsm322_c_scan_sysinfo_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + + /* no sysinfo if we are not done with power scan */ + if (cs->powerscan) { + LOGP(DCS, LOGL_INFO, "Ignoring sysinfo during power scan.\n"); + return -EINVAL; + } + + /* Store BA if we have full system info about cells and neigbor cells. + * Depending on the extended bit in the channel description, + * we require more or less system informations about neighbor cells + */ + if (s->mcc + && s->mnc + && (gm->sysinfo == GSM48_MT_RR_SYSINFO_1 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2 + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2bis + || gm->sysinfo == GSM48_MT_RR_SYSINFO_2ter) + && s->si1 + && s->si2 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) + gsm322_store_ba_list(cs, s); + + /* all relevant system informations received */ + if (s->si1 && s->si2 && s->si3 + && (!s->nb_ext_ind_si2 + || (s->si2bis && s->nb_ext_ind_si2 && !s->nb_ext_ind_si2bis) + || (s->si2bis && s->si2ter && s->nb_ext_ind_si2 + && s->nb_ext_ind_si2bis))) { + LOGP(DCS, LOGL_INFO, "Received relevant sysinfo.\n"); + /* stop timer */ + stop_cs_timer(cs); + + //gsm48_sysinfo_dump(s, print_dcs, NULL); + + /* store sysinfo and continue scan */ + return gsm322_cs_store(ms); + } + + /* wait for more sysinfo or timeout */ + return 0; +} + +static void gsm322_cs_timeout(void *arg) +{ + struct gsm322_cellsel *cs = arg; + struct osmocom_ms *ms = cs->ms; + + LOGP(DCS, LOGL_INFO, "Cell selection failed.\n"); + + /* if we have no lock, we retry */ + if (cs->ccch_state != GSM322_CCCH_ST_SYNC) + LOGP(DCS, LOGL_INFO, "Sync timeout.\n"); + else + LOGP(DCS, LOGL_INFO, "Read timeout.\n"); + + LOGP(DCS, LOGL_INFO, "Scan frequency %d: Cell not found. (rxlev=%d)\n", + cs->arfcn, cs->list[cs->arfcn].rxlev_db); + + /* remove system information */ + cs->list[cs->arfcn].flags &= ~GSM322_CS_FLAG_SYSINFO; + if (cs->list[cs->arfcn].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", cs->arfcn); + talloc_free(cs->list[cs->arfcn].sysinfo); + cs->list[cs->arfcn].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + + /* tune to next cell */ + gsm322_cs_scan(ms); + + return; +} + +/* + * power scan process + */ + +/* search for block of unscanned frequencies and start scanning */ +static int gsm322_cs_powerscan(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i, s = -1, e; + uint8_t mask, flags; + + again: + + /* search for first frequency to scan */ + mask = GSM322_CS_FLAG_SUPPORT | GSM322_CS_FLAG_POWER; + flags = GSM322_CS_FLAG_SUPPORT; + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C5_CHOOSE_CELL) { + LOGP(DCS, LOGL_FATAL, "Scanning power for stored BA list.\n"); + mask |= GSM322_CS_FLAG_BA; + flags |= GSM322_CS_FLAG_BA; + } else + LOGP(DCS, LOGL_FATAL, "Scanning power for all frequencies.\n"); + for (i = 0; i <= 1023; i++) { + if ((cs->list[i].flags & mask) == flags) { + s = e = i; + break; + } + } + + /* if there is no more frequency, we can tune to that cell */ + if (s < 0) { + int found = 0; + + /* stop power level scanning */ + cs->powerscan = 0; + + /* check if not signal is found */ + for (i = 0; i <= 1023; i++) { + if ((cs->list[i].flags & GSM322_CS_FLAG_SIGNAL)) + found++; + } + if (!found) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "Found no frequency.\n"); + /* on normal cell selection, start over */ + if (cs->state == GSM322_C1_NORMAL_CELL_SEL) { + for (i = 0; i <= 1023; i++) { + /* clear flag that this was scanned */ + cs->list[i].flags &= + ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free " + "sysinfo arfcn=%d\n", + i); + talloc_free( + cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + /* no cell selected */ + gsm322_unselect_cell(cs); + goto again; + } + /* on other cell selection, indicate "no cell found" */ + /* NOTE: PLMN search process handles it. + * If not handled there, CS process gets indicated. + * If we would continue to process CS, then we might get + * our list of scanned cells disturbed. + */ + if (cs->state == GSM322_PLMN_SEARCH) + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_PLMN_SEARCH_END); + else + nmsg = gsm322_msgb_alloc( + GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* if HPLMN search, select last frequency */ + if (cs->state == GSM322_HPLMN_SEARCH) { + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + cs->arfcn = cs->sel_arfcn; + LOGP(DCS, LOGL_INFO, "Tuning back to frequency " + "%d (rxlev %d).\n", cs->arfcn, + cs->list[cs->arfcn].rxlev_db); + hack = 5; + gsm322_sync_to_cell(cs); +// start_cs_timer(cs, ms->support.sync_to, 0); + + } else + new_c_state(cs, GSM322_C0_NULL); + + return 0; + } + LOGP(DCS, LOGL_INFO, "Found %d frequencies.\n", found); + cs->scan_state = 0xffffffff; /* higher than high */ + /* clear counter of scanned frequencies of each range */ + for (i = 0; gsm_sup_smax[i].max; i++) + gsm_sup_smax[i].temp = 0; + return gsm322_cs_scan(ms); + } + + /* search last frequency to scan (en block) */ + e = i; + for (i = s + 1; i <= 1023; i++) { + if ((cs->list[i].flags & mask) == flags) + e = i; + else + break; + } + + LOGP(DCS, LOGL_INFO, "Scanning frequencies. (%d..%d)\n", s, e); + + /* start scan on radio interface */ + cs->powerscan = 1; +//#warning TESTING!!!! +//usleep(300000); + return l1ctl_tx_pm_req_range(ms, s, e); +} + +static int gsm322_l1_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct osmocom_ms *ms; + struct gsm322_cellsel *cs; + struct osmobb_meas_res *mr; + struct osmobb_rach_conf *rc; + int i; + int8_t rxlev_db; + + if (subsys != SS_L1CTL) + return 0; + + switch (signal) { + case S_L1CTL_PM_RES: + mr = signal_data; + ms = mr->ms; + cs = &ms->cellsel; + if (!cs->powerscan) + return -EINVAL; + i = mr->band_arfcn & 1023; + rxlev_db = mr->rx_lev - 110; + cs->list[i].rxlev_db = rxlev_db; + cs->list[i].flags |= GSM322_CS_FLAG_POWER; + if (rxlev_db >= ms->support.min_rxlev_db) { + cs->list[i].flags |= GSM322_CS_FLAG_SIGNAL; + LOGP(DCS, LOGL_INFO, "Found signal (frequency %d " + "rxlev %d)\n", i, cs->list[i].rxlev_db); + } + break; + case S_L1CTL_PM_DONE: + LOGP(DCS, LOGL_INFO, "Done with power scanning range.\n"); + ms = signal_data; + cs = &ms->cellsel; + if (!cs->powerscan) + return -EINVAL; + gsm322_cs_powerscan(ms); + break; + case S_L1CTL_FBSB_RESP: + ms = signal_data; + cs = &ms->cellsel; + if (cs->ccch_state == GSM322_CCCH_ST_INIT) { + LOGP(DCS, LOGL_INFO, "Channel synched.\n"); + cs->ccch_state = GSM322_CCCH_ST_SYNC; +#if 0 + stop_cs_timer(cs); + + /* in dedicated mode */ + if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) + return gsm48_rr_tx_rand_acc(ms, NULL); +#endif + + /* set timer for reading BCCH */ + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C1_NORMAL_CELL_SEL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C5_CHOOSE_CELL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL + || cs->state == GSM322_PLMN_SEARCH + || cs->state == GSM322_HPLMN_SEARCH) + start_cs_timer(cs, ms->support.scan_to, 0); + // TODO: timer depends on BCCH config + } + break; + case S_L1CTL_FBSB_ERR: + if (hack) { + ms = signal_data; + cs = &ms->cellsel; + gsm322_sync_to_cell(cs); + hack--; + LOGP(DCS, LOGL_INFO, "Channel sync error, try again.\n"); + break; + } + LOGP(DCS, LOGL_INFO, "Channel sync error.\n"); + ms = signal_data; + cs = &ms->cellsel; + + stop_cs_timer(cs); + if (cs->selected) + gsm322_cs_loss(cs); + else + gsm322_cs_timeout(cs); + break; + case S_L1CTL_RACH_CONF: + rc = signal_data; + ms = rc->ms; + gsm48_rr_rach_conf(ms, rc->fn); + break; + case S_L1CTL_RESET: + LOGP(DCS, LOGL_INFO, "Reset\n"); + break; + } + + return 0; +} + +static void gsm322_cs_loss(void *arg) +{ + struct gsm322_cellsel *cs = arg; + struct osmocom_ms *ms = cs->ms; + struct gsm48_rrlayer *rr = &ms->rrlayer; + + LOGP(DCS, LOGL_INFO, "Loss of CCCH.\n"); + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) { + if (rr->state == GSM48_RR_ST_IDLE) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "Trigger re-selection.\n"); + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CELL_RESEL); + if (!nmsg) + return; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + } else { + LOGP(DCS, LOGL_INFO, "Trigger RR abort.\n"); + gsm48_rr_los(ms); + /* be shure that nothing else is done after here + * because the function call above may cause + * to return from idle state and trigger cell re-sel. + */ + } + } + + return; +} + +/* + * handler for cell selection process + */ + +/* start PLMN search */ +static int gsm322_c_plmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_PLMN_SEARCH); + + /* mark all frequencies except our own BA to be scanned */ + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + gsm322_unselect_cell(cs); + } + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start HPLMN search */ +static int gsm322_c_hplmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_HPLMN_SEARCH); + + /* mark all frequencies except our own BA to be scanned */ + for (i = 0; i <= 1023; i++) { + if (i != cs->sel_arfcn + && (cs->list[i].flags & GSM322_CS_FLAG_SYSINFO) + && !(cs->list[i].flags & GSM322_CS_FLAG_BA)) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + + /* no cell selected */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start stored cell selection */ +static int gsm322_c_stored_cell_sel(struct osmocom_ms *ms, struct gsm322_ba_list *ba) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + new_c_state(cs, GSM322_C2_STORED_CELL_SEL); + + /* flag all frequencies that are in current band allocation */ + for (i = 0; i <= 1023; i++) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) + cs->list[i].flags |= GSM322_CS_FLAG_BA; + else + cs->list[i].flags &= ~GSM322_CS_FLAG_BA; + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start noraml cell selection */ +static int gsm322_c_normal_cell_sel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + int i; + + /* except for stored cell selection state, we weed to rescan ?? */ + if (cs->state != GSM322_C2_STORED_CELL_SEL) { + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + + new_c_state(cs, GSM322_C1_NORMAL_CELL_SEL); + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start any cell selection */ +static int gsm322_c_any_cell_sel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + /* in case we already tried any cell (re-)selection, power scan again */ + if (cs->state == GSM322_C0_NULL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) { + int i; + + for (i = 0; i <= 1023; i++) { + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", + i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + } + /* after re-selection, indicate no cell found */ + if (cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C8_ANY_CELL_RESEL) { + struct msgb *nmsg; + + /* tell that we have no cell found */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + } else { + new_c_state(cs, GSM322_C6_ANY_CELL_SEL); + } + + cs->mcc = cs->mnc = 0; + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start noraml cell re-selection */ +static int gsm322_c_normal_cell_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C4_NORMAL_CELL_RESEL); + + /* NOTE: We keep our scan info we have so far. + * This may cause a skip in power scan. */ + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start any cell re-selection */ +static int gsm322_c_any_cell_resel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C8_ANY_CELL_RESEL); + + /* NOTE: We keep our scan info we have so far. + * This may cause a skip in power scan. */ + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* create temporary ba range with given frequency ranges */ +struct gsm322_ba_list *gsm322_cs_ba_range(struct osmocom_ms *ms, + uint32_t *range, uint8_t ranges) +{ + static struct gsm322_ba_list ba; + uint16_t lower, higher; + + memset(&ba, 0, sizeof(ba)); + + while(ranges--) { + lower = *range & 1023; + higher = (*range >> 16) & 1023; + range++; + LOGP(DCS, LOGL_INFO, "Use BA range: %d..%d\n", lower, higher); + /* GSM 05.08 6.3 */ + while (1) { + ba.freq[lower >> 3] |= 1 << (lower & 7); + if (lower == higher) + break; + lower = (lower + 1) & 1023; + } + } + + return &ba; +} + +/* common part of gsm322_c_choose_cell and gsm322_c_choose_any_cell */ +static int gsm322_cs_choose(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_ba_list *ba = NULL; + int i; + + /* NOTE: The call to this function is synchron to RR layer, so + * we may access the BA range there. + */ + if (rr->ba_ranges) + ba = gsm322_cs_ba_range(ms, rr->ba_range, rr->ba_ranges); + else { + LOGP(DCS, LOGL_INFO, "No BA range(s), try sysinfo.\n"); + /* get and update BA of last received sysinfo 5* */ + ba = gsm322_cs_sysinfo_sacch(ms); + if (!ba) { + LOGP(DCS, LOGL_INFO, "No BA on sysinfo, try stored " + "BA list.\n"); + ba = gsm322_find_ba_list(cs, cs->sel_si.mcc, + cs->sel_si.mnc); + } + } + + if (!ba) { + struct msgb *nmsg; + + LOGP(DCS, LOGL_INFO, "No BA list to use.\n"); + + /* tell CS to start over */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_NO_CELL_FOUND); + if (!nmsg) + return -ENOMEM; + gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + + return 0; + } + + /* flag all frequencies that are in current band allocation */ + for (i = 0; i <= 1023; i++) { + if (cs->state == GSM322_C5_CHOOSE_CELL) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) { + cs->list[i].flags |= GSM322_CS_FLAG_BA; + } else { + cs->list[i].flags &= ~GSM322_CS_FLAG_BA; + } + } + cs->list[i].flags &= ~(GSM322_CS_FLAG_POWER + | GSM322_CS_FLAG_SIGNAL + | GSM322_CS_FLAG_SYSINFO); + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + } + + /* unset selected cell */ + gsm322_unselect_cell(cs); + + /* start power scan */ + return gsm322_cs_powerscan(ms); +} + +/* start 'Choose cell' after returning to idle mode */ +static int gsm322_c_choose_cell(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C5_CHOOSE_CELL); + + return gsm322_cs_choose(ms); +} + +/* start 'Choose any cell' after returning to idle mode */ +static int gsm322_c_choose_any_cell(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + new_c_state(cs, GSM322_C9_CHOOSE_ANY_CELL); + + return gsm322_cs_choose(ms); +} + +/* a new PLMN is selected by PLMN search process */ +static int gsm322_c_new_plmn(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_ba_list *ba; + + cs->mcc = plmn->mcc; + cs->mnc = plmn->mnc; + + LOGP(DSUM, LOGL_INFO, "Selecting network (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(cs->mcc), + gsm_print_mnc(cs->mnc), gsm_get_mcc(cs->mcc), + gsm_get_mnc(cs->mcc, cs->mnc)); + + /* search for BA list */ + ba = gsm322_find_ba_list(cs, plmn->mcc, plmn->mnc); + + if (ba) { + LOGP(DCS, LOGL_INFO, "Start stored cell selection.\n"); + return gsm322_c_stored_cell_sel(ms, ba); + } else { + LOGP(DCS, LOGL_INFO, "Start normal cell selection.\n"); + return gsm322_c_normal_cell_sel(ms, msg); + } +} + +/* a suitable cell was found, so we camp normally */ +static int gsm322_c_camp_normally(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + LOGP(DSUM, LOGL_INFO, "Camping normally on cell (arfcn=%d mcc=%s " + "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + + /* tell that we have selected a (new) cell */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + new_c_state(cs, GSM322_C3_CAMPED_NORMALLY); + + return 0; +} + +/* a not suitable cell was found, so we camp on any cell */ +static int gsm322_c_camp_any_cell(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *nmsg; + + LOGP(DSUM, LOGL_INFO, "Camping on any cell (arfcn=%d mcc=%s " + "mnc=%s %s, %s)\n", cs->sel_arfcn, gsm_print_mcc(cs->sel_mcc), + gsm_print_mnc(cs->sel_mnc), gsm_get_mcc(cs->sel_mcc), + gsm_get_mnc(cs->sel_mcc, cs->sel_mnc)); + + + /* tell that we have selected a (new) cell */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_CELL_SELECTED); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + new_c_state(cs, GSM322_C7_CAMPED_ANY_CELL); + + return 0; +} + +/* go connected mode */ +static int gsm322_c_conn_mode_1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + /* check for error */ + if (!cs->selected) + return -EINVAL; + cs->arfcn = cs->sel_arfcn; + + /* be sure to go to current camping frequency on return */ + LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn); + hack = 5; + gsm322_sync_to_cell(cs); + cs->si = cs->list[cs->arfcn].sysinfo; +//#warning TESTING!!!! +//usleep(300000); + + return 0; +} + +static int gsm322_c_conn_mode_2(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + + /* check for error */ + if (!cs->selected) + return -EINVAL; + cs->arfcn = cs->sel_arfcn; + + /* be sure to go to current camping frequency on return */ + LOGP(DCS, LOGL_INFO, "Going to camping frequency %d.\n", cs->arfcn); + hack = 5; + gsm322_sync_to_cell(cs); + cs->si = cs->list[cs->arfcn].sysinfo; + + return 0; +} + +/* switch on */ +static int gsm322_c_switch_on(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + /* if no SIM is is MS */ + if (!subscr->sim_valid) { + LOGP(DCS, LOGL_INFO, "Switch on without SIM.\n"); + return gsm322_c_any_cell_sel(ms, msg); + LOGP(DCS, LOGL_INFO, "Switch on with SIM inserted.\n"); + } + + /* stay in NULL state until PLMN is selected */ + + return 0; +} + +/* + * state machines + */ + +/* state machine for automatic PLMN selection events */ +static struct plmnastatelist { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} plmnastatelist[] = { + {SBIT(GSM322_A0_NULL), + GSM322_EVENT_SWITCH_ON, gsm322_a_switch_on}, + + {SBIT(GSM322_A0_NULL), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_a_sel_first_plmn}, + + {ALL_STATES, + GSM322_EVENT_SWITCH_OFF, gsm322_a_switch_off}, + + {SBIT(GSM322_A6_NO_SIM), + GSM322_EVENT_SIM_INSERT, gsm322_a_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_INSERT, gsm322_a_sim_insert}, + + {ALL_STATES, + GSM322_EVENT_SIM_REMOVE, gsm322_a_sim_removed}, + + {ALL_STATES, + GSM322_EVENT_INVALID_SIM, gsm322_a_sim_removed}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_REG_FAILED, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A1_TRYING_RPLMN) | SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_REG_SUCCESS, gsm322_a_indicate_selected}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_roaming_na}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_HPLMN_SEARCH, gsm322_a_hplmn_search_start}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_loss_of_radio}, + + {SBIT(GSM322_A2_ON_PLMN), + GSM322_EVENT_USER_RESEL, gsm322_a_user_resel}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_REG_FAILED, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A3_TRYING_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_sel_next_plmn}, + + {SBIT(GSM322_A5_HPLMN_SEARCH), + GSM322_EVENT_CELL_FOUND, gsm322_a_sel_first_plmn}, + + {SBIT(GSM322_A5_HPLMN_SEARCH), + GSM322_EVENT_NO_CELL_FOUND, gsm322_a_go_on_plmn}, + + {SBIT(GSM322_A4_WAIT_FOR_PLMN), + GSM322_EVENT_PLMN_AVAIL, gsm322_a_plmn_avail}, + + {ALL_STATES, + GSM322_EVENT_SEL_MANUAL, gsm322_a_sel_manual}, + + {ALL_STATES, + GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found}, +}; + +#define PLMNASLLEN \ + (sizeof(plmnastatelist) / sizeof(struct plmnastatelist)) + +static int gsm322_a_event(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + int rc; + int i; + + LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for automatic PLMN " + "selection in state '%s'\n", ms->name, get_event_name(msg_type), + plmn_a_state_names[plmn->state]); + /* find function for current state and message */ + for (i = 0; i < PLMNASLLEN; i++) + if ((msg_type == plmnastatelist[i].type) + && ((1 << plmn->state) & plmnastatelist[i].states)) + break; + if (i == PLMNASLLEN) { + LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n"); + return 0; + } + + rc = plmnastatelist[i].rout(ms, msg); + + return rc; +} + +/* state machine for manual PLMN selection events */ +static struct plmnmstatelist { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} plmnmstatelist[] = { + {SBIT(GSM322_M0_NULL), + GSM322_EVENT_SWITCH_ON, gsm322_m_switch_on}, + + {SBIT(GSM322_M0_NULL) | SBIT(GSM322_M3_NOT_ON_PLMN) | + SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_PLMN_SEARCH_END, gsm322_m_display_plmns}, + + {ALL_STATES, + GSM322_EVENT_SWITCH_OFF, gsm322_m_switch_off}, + + {SBIT(GSM322_M5_NO_SIM), + GSM322_EVENT_SIM_INSERT, gsm322_m_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_INSERT, gsm322_m_sim_insert}, + + {ALL_STATES, + GSM322_EVENT_SIM_REMOVE, gsm322_m_sim_removed}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN), + GSM322_EVENT_REG_SUCCESS, gsm322_m_indicate_selected}, + + {SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + + {SBIT(GSM322_M1_TRYING_RPLMN) | SBIT(GSM322_M2_ON_PLMN) | + SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_INVALID_SIM, gsm322_m_sim_removed}, + + {SBIT(GSM322_M3_NOT_ON_PLMN) | SBIT(GSM322_M2_ON_PLMN), + GSM322_EVENT_USER_RESEL, gsm322_m_user_resel}, + + {SBIT(GSM322_M3_NOT_ON_PLMN), + GSM322_EVENT_PLMN_AVAIL, gsm322_m_plmn_avail}, + + {SBIT(GSM322_M3_NOT_ON_PLMN), + GSM322_EVENT_CHOSE_PLMN, gsm322_m_choose_plmn}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_REG_SUCCESS, gsm322_m_go_on_plmn}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_REG_FAILED, gsm322_m_display_plmns}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_ROAMING_NA, gsm322_m_display_plmns}, + + {SBIT(GSM322_M4_TRYING_PLMN), + GSM322_EVENT_NO_CELL_FOUND, gsm322_m_display_plmns}, + + {ALL_STATES, + GSM322_EVENT_SEL_AUTO, gsm322_m_sel_auto}, + + {ALL_STATES, + GSM322_EVENT_NO_CELL_FOUND, gsm322_am_no_cell_found}, +}; + +#define PLMNMSLLEN \ + (sizeof(plmnmstatelist) / sizeof(struct plmnmstatelist)) + +static int gsm322_m_event(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + int rc; + int i; + + LOGP(DPLMN, LOGL_INFO, "(ms %s) Event '%s' for manual PLMN selection " + "in state '%s'\n", ms->name, get_event_name(msg_type), + plmn_m_state_names[plmn->state]); + /* find function for current state and message */ + for (i = 0; i < PLMNMSLLEN; i++) + if ((msg_type == plmnmstatelist[i].type) + && ((1 << plmn->state) & plmnmstatelist[i].states)) + break; + if (i == PLMNMSLLEN) { + LOGP(DPLMN, LOGL_NOTICE, "Event unhandled at this state.\n"); + return 0; + } + + rc = plmnmstatelist[i].rout(ms, msg); + + return rc; +} + +/* dequeue GSM 03.22 PLMN events */ +int gsm322_plmn_dequeue(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&plmn->event_queue))) { + /* send event to PLMN select process */ + if (ms->settings.plmn_mode == PLMN_MODE_AUTO) + gsm322_a_event(ms, msg); + else + gsm322_m_event(ms, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* state machine for channel selection events */ +static struct cellselstatelist { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} cellselstatelist[] = { + {ALL_STATES, + GSM322_EVENT_SWITCH_ON, gsm322_c_switch_on}, + + {ALL_STATES, + GSM322_EVENT_SIM_REMOVE, gsm322_c_any_cell_sel}, + + {ALL_STATES, + GSM322_EVENT_NEW_PLMN, gsm322_c_new_plmn}, + + {ALL_STATES, + GSM322_EVENT_PLMN_SEARCH_START, gsm322_c_plmn_search}, + + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | + SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL), + GSM322_EVENT_CELL_FOUND, gsm322_c_camp_normally}, + + {SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C6_ANY_CELL_SEL) | + SBIT(GSM322_C8_ANY_CELL_RESEL), + GSM322_EVENT_CELL_FOUND, gsm322_c_camp_any_cell}, + + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C6_ANY_CELL_SEL) | + SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | + SBIT(GSM322_C0_NULL), + GSM322_EVENT_NO_CELL_FOUND, gsm322_c_any_cell_sel}, + + {SBIT(GSM322_C2_STORED_CELL_SEL) | SBIT(GSM322_C5_CHOOSE_CELL) | + SBIT(GSM322_C4_NORMAL_CELL_RESEL), + GSM322_EVENT_NO_CELL_FOUND, gsm322_c_normal_cell_sel}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_1}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_LEAVE_IDLE, gsm322_c_conn_mode_2}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_RET_IDLE, gsm322_c_choose_cell}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_RET_IDLE, gsm322_c_choose_any_cell}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_CELL_RESEL, gsm322_c_normal_cell_resel}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_CELL_RESEL, gsm322_c_any_cell_resel}, + + {SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_CELL_FOUND, gsm322_c_normal_cell_sel}, + + {SBIT(GSM322_C1_NORMAL_CELL_SEL) | SBIT(GSM322_C2_STORED_CELL_SEL) | + SBIT(GSM322_C4_NORMAL_CELL_RESEL) | SBIT(GSM322_C5_CHOOSE_CELL) | + SBIT(GSM322_C9_CHOOSE_ANY_CELL) | SBIT(GSM322_C8_ANY_CELL_RESEL) | + SBIT(GSM322_C6_ANY_CELL_SEL) | SBIT(GSM322_PLMN_SEARCH), + GSM322_EVENT_SYSINFO, gsm322_c_scan_sysinfo_bcch}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY) | SBIT(GSM322_C7_CAMPED_ANY_CELL), + GSM322_EVENT_SYSINFO, gsm322_c_camp_sysinfo_bcch}, + + {SBIT(GSM322_C3_CAMPED_NORMALLY), + GSM322_EVENT_HPLMN_SEARCH, gsm322_c_hplmn_search}, +}; + +#define CELLSELSLLEN \ + (sizeof(cellselstatelist) / sizeof(struct cellselstatelist)) + +int gsm322_c_event(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm322_msg *gm = (struct gsm322_msg *) msg->data; + int msg_type = gm->msg_type; + int rc; + int i; + + LOGP(DCS, LOGL_INFO, "(ms %s) Event '%s' for Cell selection in state " + "'%s'\n", ms->name, get_event_name(msg_type), + cs_state_names[cs->state]); + /* find function for current state and message */ + for (i = 0; i < CELLSELSLLEN; i++) + if ((msg_type == cellselstatelist[i].type) + && ((1 << cs->state) & cellselstatelist[i].states)) + break; + if (i == CELLSELSLLEN) { + LOGP(DCS, LOGL_NOTICE, "Event unhandled at this state.\n"); + return 0; + } + + rc = cellselstatelist[i].rout(ms, msg); + + return rc; +} + +/* dequeue GSM 03.22 cell selection events */ +int gsm322_cs_dequeue(struct osmocom_ms *ms) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&cs->event_queue))) { + /* send event to cell selection process */ + gsm322_c_event(ms, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* + * dump lists + */ + +int gsm322_dump_sorted_plmn(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_plmn_list *temp; + + printf("MCC |MNC |allowed|rx-lev\n"); + printf("-------+-------+-------+-------\n"); + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) { + printf("%s |%s%s |%s |%d\n", gsm_print_mcc(temp->mcc), + gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", + (temp->cause) ? "no ":"yes", temp->rxlev_db); + } + + return 0; +} + +int gsm322_dump_cs_list(struct gsm322_cellsel *cs, uint8_t flags, + void (*print)(void *, const char *, ...), void *priv) +{ + int i; + struct gsm48_sysinfo *s; + + print(priv, "arfcn |rx-lev |MCC |MNC |LAC |cell ID|forb.LA|" + "prio |min-db |max-pwr\n"); + print(priv, "-------+-------+-------+-------+-------+-------+-------+" + "-------+-------+-------\n"); + for (i = 0; i <= 1023; i++) { + s = cs->list[i].sysinfo; + if (!s || !(cs->list[i].flags & flags)) + continue; + print(priv, "%4d |%4d |", i, cs->list[i].rxlev_db); + if ((cs->list[i].flags & GSM322_CS_FLAG_SYSINFO)) { + print(priv, "%s |%s%s |", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), + ((s->mnc & 0x00f) == 0x00f) ? " ":""); + print(priv, "0x%04x |0x%04x |", s->lac, s->cell_id); + if ((cs->list[i].flags & GSM322_CS_FLAG_FORBIDD)) + print(priv, "yes |"); + else + print(priv, "no |"); + if ((cs->list[i].flags & GSM322_CS_FLAG_BARRED)) + print(priv, "barred |"); + else { + if (cs->list[i].sysinfo->cell_barr) + print(priv, "low |"); + else + print(priv, "normal |"); + } + print(priv, "%4d |%4d\n", s->rxlev_acc_min_db, + s->ms_txpwr_max_ccch); + } else + print(priv, "n/a |n/a |n/a |n/a |n/a |" + "n/a |n/a |n/a\n"); + } + print(priv, "\n"); + + return 0; +} + +int gsm322_dump_forbidden_la(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_la_list *temp; + + print(priv, "MCC |MNC |LAC |cause\n"); + print(priv, "-------+-------+-------+-------\n"); + llist_for_each_entry(temp, &plmn->forbidden_la, entry) + print(priv, "%s |%s%s |0x%04x |#%d\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", + temp->lac, temp->cause); + + return 0; +} + +int gsm322_dump_ba_list(struct gsm322_cellsel *cs, uint16_t mcc, uint16_t mnc, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm322_ba_list *ba; + int i; + + llist_for_each_entry(ba, &cs->ba_list, entry) { + if (mcc && mnc && (mcc != ba->mcc || mnc != ba->mnc)) + continue; + print(priv, "Band Allocation of network: MCC %03d MNC %02d " + "(%s, %s)\n", ba->mcc, ba->mnc, gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + for (i = 0; i <= 1023; i++) { + if ((ba->freq[i >> 3] & (1 << (i & 7)))) + print(priv, " %d", i); + } + print(priv, "\n"); + } + + return 0; +} + +/* + * initialization + */ + +int gsm322_init(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + OSMOCOM_FILE *fp; + char suffix[] = ".ba"; + char filename[sizeof(ms->name) + strlen(suffix) + 1]; + int i; + struct gsm322_ba_list *ba; + uint8_t buf[4]; + + LOGP(DPLMN, LOGL_INFO, "init PLMN process\n"); + LOGP(DCS, LOGL_INFO, "init Cell Selection process\n"); + + memset(plmn, 0, sizeof(*plmn)); + memset(cs, 0, sizeof(*cs)); + plmn->ms = ms; + cs->ms = ms; + + /* set initial state */ + plmn->state = 0; + cs->state = 0; + ms->settings.plmn_mode = PLMN_MODE_AUTO; + + /* init lists */ + INIT_LLIST_HEAD(&plmn->event_queue); + INIT_LLIST_HEAD(&cs->event_queue); + INIT_LLIST_HEAD(&plmn->sorted_plmn); + INIT_LLIST_HEAD(&plmn->forbidden_la); + INIT_LLIST_HEAD(&cs->ba_list); + + /* set supported frequencies in cell selection list */ + for (i = 0; i <= 1023; i++) + if ((ms->support.freq_map[i >> 3] & (1 << (i & 7)))) + cs->list[i].flags |= GSM322_CS_FLAG_SUPPORT; + + /* read BA list */ + strcpy(filename, ms->name); + strcat(filename, suffix); + fp = osmocom_fopen(filename, "r"); + if (fp) { + int rc; + + while(!osmocom_feof(fp)) { + ba = talloc_zero(l23_ctx, struct gsm322_ba_list); + if (!ba) + return -ENOMEM; + rc = osmocom_fread(buf, 4, 1, fp); + if (!rc) { + talloc_free(ba); + break; + } + ba->mcc = (buf[0] << 8) | buf[1]; + ba->mnc = (buf[2] << 8) | buf[3]; + rc = osmocom_fread(ba->freq, sizeof(ba->freq), 1, fp); + if (!rc) { + talloc_free(ba); + break; + } + llist_add_tail(&ba->entry, &cs->ba_list); + LOGP(DCS, LOGL_INFO, "Read stored BA list (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + } + osmocom_fclose(fp); + } else + LOGP(DCS, LOGL_INFO, "No stored BA list\n"); + + register_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL); + + return 0; +} + +int gsm322_exit(struct osmocom_ms *ms) +{ + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct llist_head *lh, *lh2; + struct msgb *msg; + OSMOCOM_FILE *fp; + char suffix[] = ".ba"; + char filename[sizeof(ms->name) + strlen(suffix) + 1]; + struct gsm322_ba_list *ba; + uint8_t buf[4]; + int i; + + LOGP(DPLMN, LOGL_INFO, "exit PLMN process\n"); + LOGP(DCS, LOGL_INFO, "exit Cell Selection process\n"); + + unregister_signal_handler(SS_L1CTL, &gsm322_l1_signal, NULL); + + /* stop cell selection process (if any) */ + new_c_state(cs, GSM322_C0_NULL); + + /* stop timers */ + stop_cs_timer(cs); + stop_plmn_timer(plmn); + + /* flush sysinfo */ + for (i = 0; i <= 1023; i++) { + if (cs->list[i].sysinfo) { + LOGP(DCS, LOGL_INFO, "free sysinfo arfcn=%d\n", i); + talloc_free(cs->list[i].sysinfo); + cs->list[i].sysinfo = NULL; + } + cs->list[i].flags = 0; + } + + /* store BA list */ + strcpy(filename, ms->name); + strcat(filename, suffix); + fp = osmocom_fopen(filename, "w"); + if (fp) { + int rc; + + llist_for_each_entry(ba, &cs->ba_list, entry) { + buf[0] = ba->mcc >> 8; + buf[1] = ba->mcc & 0xff; + buf[2] = ba->mnc >> 8; + buf[3] = ba->mnc & 0xff; + rc = osmocom_fwrite(buf, 4, 1, fp); + rc = osmocom_fwrite(ba->freq, sizeof(ba->freq), 1, fp); + LOGP(DCS, LOGL_INFO, "Write stored BA list (mcc=%s " + "mnc=%s %s, %s)\n", gsm_print_mcc(ba->mcc), + gsm_print_mnc(ba->mnc), gsm_get_mcc(ba->mcc), + gsm_get_mnc(ba->mcc, ba->mnc)); + } + osmocom_fclose(fp); + } else + LOGP(DCS, LOGL_ERROR, "Failed to write BA list\n"); + + /* free lists */ + while ((msg = msgb_dequeue(&plmn->event_queue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&cs->event_queue))) + msgb_free(msg); + llist_for_each_safe(lh, lh2, &plmn->sorted_plmn) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &plmn->forbidden_la) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &cs->ba_list) { + llist_del(lh); + talloc_free(lh); + } + return 0; +} + + diff --git a/src/host/layer23/src/gsm48_cc.c b/src/host/layer23/src/gsm48_cc.c new file mode 100644 index 00000000..386fe80b --- /dev/null +++ b/src/host/layer23/src/gsm48_cc.c @@ -0,0 +1,2124 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocore/msgb.h> +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/talloc.h> + +#include <osmocom/logging.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/mncc.h> +#include <osmocom/transaction.h> +#include <osmocom/gsm48_cc.h> + +extern void *l23_ctx; + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); +static int gsm48_rel_null_free(struct gsm_trans *trans); +int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans, + u_int32_t callref, int location, int value); +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg); + +/* + * init + */ + +int gsm48_cc_init(struct osmocom_ms *ms) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + + cc->ms = ms; + + if (!cc->mncc_upqueue.next == 0) + return 0; + + LOGP(DCC, LOGL_INFO, "init Call Control\n"); + + INIT_LLIST_HEAD(&cc->mncc_upqueue); + + return 0; +} + +int gsm48_cc_exit(struct osmocom_ms *ms) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + struct gsm_trans *trans, *trans2; + struct msgb *msg; + + LOGP(DCC, LOGL_INFO, "exit Call Control processes for %s\n", ms->name); + + llist_for_each_entry_safe(trans, trans2, &ms->trans_list, entry) { + if (trans->protocol == GSM48_PDISC_CC) + LOGP(DCC, LOGL_NOTICE, "Free pendig CC-transaction.\n"); + trans_free(trans); + } + + while ((msg = msgb_dequeue(&cc->mncc_upqueue))) + msgb_free(msg); + + return 0; +} + +/* + * messages + */ + +/* names of MNCC-SAP */ +static const struct value_string gsm_mncc_names[] = { + { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, + { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, + { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, + { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, + { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, + { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, + { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, + { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, + { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, + { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, + { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, + { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, + { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, + { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, + { MNCC_DISC_IND, "MNCC_DISC_IND" }, + { MNCC_REL_REQ, "MNCC_REL_REQ" }, + { MNCC_REL_IND, "MNCC_REL_IND" }, + { MNCC_REL_CNF, "MNCC_REL_CNF" }, + { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, + { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, + { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, + { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, + { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, + { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, + { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, + { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, + { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, + { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, + { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, + { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, + { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, + { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, + { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, + { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, + { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, + { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, + { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, + { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, + { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, + { MNCC_REJ_IND, "MNCC_REJ_IND" }, + { MNCC_PROGRESS_IND, "MNCC_PROGRESS_IND" }, + { MNCC_CALL_PROC_IND, "MNCC_CALL_PROC_IND" }, + { MNCC_CALL_CONF_REQ, "MNCC_CALL_CONF_REQ" }, + { MNCC_START_DTMF_REQ, "MNCC_START_DTMF_REQ" }, + { MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" }, + { MNCC_HOLD_REQ, "MNCC_HOLD_REQ " }, + { MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" }, + { 0, NULL } +}; + +const char *get_mncc_name(int value) +{ + return get_value_string(gsm_mncc_names, value); +} + +/* push MMCC header and send to MM */ +static int gsm48_cc_to_mm(struct msgb *msg, struct gsm_trans *trans, + int msg_type) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_mmxx_hdr *mmh; + int emergency = 0; + + /* Add protocol type and transaction ID */ + gh->proto_discr = trans->protocol | (trans->transaction_id << 4); + + /* indicate emergency setup to MM layer */ + if (gh->msg_type == GSM48_MT_CC_EMERG_SETUP) + emergency = 1; + + /* push RR header */ + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = msg_type; + mmh->ref = trans->callref; + mmh->transaction_id = trans->transaction_id; + mmh->emergency = emergency; + + /* send message to MM */ + LOGP(DCC, LOGL_INFO, "Sending '%s' using %s (callref=%x, " + "transaction_id=%d)\n", gsm48_cc_msg_name(gh->msg_type), + get_mmxx_name(msg_type), trans->callref, trans->transaction_id); + return gsm48_mmxx_downmsg(trans->ms, msg); +} + +/* enqueue message to application (MNCC-SAP) */ +static int mncc_recvmsg(struct osmocom_ms *ms, struct gsm_trans *trans, + int msg_type, struct gsm_mncc *mncc) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + struct msgb *msg; + + if (trans) + LOGP(DCC, LOGL_INFO, "(ms %s ti %x) Sending '%s' to MNCC.\n", + ms->name, trans->transaction_id, + get_mncc_name(msg_type)); + else + LOGP(DCC, LOGL_INFO, "(ms %s ti -) Sending '%s' to MNCC.\n", + ms->name, get_mncc_name(msg_type)); + + mncc->msg_type = msg_type; + + msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); + if (!msg) + return -ENOMEM; + memcpy(msg->data, mncc, sizeof(struct gsm_mncc)); + msgb_enqueue(&cc->mncc_upqueue, msg); + + return 0; +} + +/* dequeue messages to layer 4 */ +int mncc_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_cclayer *cc = &ms->cclayer; + struct gsm_mncc *mncc; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&cc->mncc_upqueue))) { + mncc = (struct gsm_mncc *)msg->data; + if (cc->mncc_recv) + cc->mncc_recv(ms, mncc->msg_type, mncc); + work = 1; /* work done */ + talloc_free(msg); + } + + return work; +} + + +/* + * state transition + */ + +static void new_cc_state(struct gsm_trans *trans, int state) +{ + if (state > 31 || state < 0) + return; + + DEBUGP(DCC, "new state %s -> %s\n", + gsm48_cc_state_name(trans->cc.state), + gsm48_cc_state_name(state)); + + trans->cc.state = state; +} + +/* + * timers + */ + +/* timeout events of all timers */ +static void gsm48_cc_timeout(void *arg) +{ + struct gsm_trans *trans = arg; + int disconnect = 0, release = 0, abort = 1; + int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; + int mo_location = GSM48_CAUSE_LOC_PRN_S_LU; + int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; + int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; + struct gsm_mncc mo_rel, l4_rel; + + memset(&mo_rel, 0, sizeof(struct gsm_mncc)); + mo_rel.callref = trans->callref; + memset(&l4_rel, 0, sizeof(struct gsm_mncc)); + l4_rel.callref = trans->callref; + + LOGP(DCC, LOGL_INFO, "Timer T%x has fired.\n", trans->cc.Tcurrent); + + switch(trans->cc.Tcurrent) { + case 0x303: + /* abort if connection is not already esablished */ + if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND) + abort = 1; + else + release = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x305: + release = 1; + mo_cause = trans->cc.msg.cause.value; + mo_location = trans->cc.msg.cause.location; + break; + case 0x308: + if (!trans->cc.T308_second) { + /* restart T308 a second time */ + gsm48_cc_tx_release(trans, &trans->cc.msg); + trans->cc.T308_second = 1; + break; /* stay in release state */ + } + /* release MM conn, got NULL state, free trans */ + gsm48_rel_null_free(trans); + + return; + case 0x310: + disconnect = 1; + l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; + break; + case 0x313: + disconnect = 1; + /* unknown, did not find it in the specs */ + break; + default: + release = 1; + } + + if ((release || abort) && trans->callref) { + /* process release towards layer 4 */ + mncc_release_ind(trans->ms, trans, trans->callref, + l4_location, l4_cause); + } + + if (disconnect && trans->callref) { + /* process disconnect towards layer 4 */ + mncc_set_cause(&l4_rel, l4_location, l4_cause); + mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &l4_rel); + } + + /* process disconnect towards mobile station */ + if (disconnect || release || abort) { + mncc_set_cause(&mo_rel, mo_location, mo_cause); + mo_rel.cause.diag[0] = + ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; + mo_rel.cause.diag[1] = + ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; + mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; + mo_rel.cause.diag_len = 3; + + if (disconnect) + gsm48_cc_tx_disconnect(trans, &mo_rel); + if (release) + gsm48_cc_tx_release(trans, &mo_rel); + if (abort) { + /* release MM conn, got NULL state, free trans */ + gsm48_rel_null_free(trans); + } + } +} + +/* start various timers */ +static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, + int sec, int micro) +{ + LOGP(DCC, LOGL_INFO, "starting timer T%x with %d seconds\n", current, + sec); + trans->cc.timer.cb = gsm48_cc_timeout; + trans->cc.timer.data = trans; + bsc_schedule_timer(&trans->cc.timer, sec, micro); + trans->cc.Tcurrent = current; +} + +/* stop various timers */ +static void gsm48_stop_cc_timer(struct gsm_trans *trans) +{ + if (bsc_timer_pending(&trans->cc.timer)) { + LOGP(DCC, LOGL_INFO, "stopping pending timer T%x\n", + trans->cc.Tcurrent); + bsc_del_timer(&trans->cc.timer); + trans->cc.Tcurrent = 0; + } +} + +/* + * process handlers (misc) + */ + +/* Call Control Specific transaction release. + * gets called by trans_free, DO NOT CALL YOURSELF! + */ +void _gsm48_cc_trans_free(struct gsm_trans *trans) +{ + gsm48_stop_cc_timer(trans); + + /* send release to L4, if callref still exists */ + if (trans->callref) { + /* Ressource unavailable */ + mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + } + if (trans->cc.state != GSM_CSTATE_NULL) + new_cc_state(trans, GSM_CSTATE_NULL); +} + +/* release MM connection, go NULL state, free transaction */ +static int gsm48_rel_null_free(struct gsm_trans *trans) +{ + struct msgb *nmsg; + + /* release MM connection */ + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref, + trans->transaction_id); + if (!nmsg) + return -ENOMEM; + LOGP(DCC, LOGL_INFO, "Sending MMCC_REL_REQ\n"); + gsm48_mmxx_downmsg(trans->ms, nmsg); + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return 0; +} + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val) +{ + data->fields |= MNCC_F_CAUSE; + data->cause.location = loc; + data->cause.value = val; +} + +/* send release indication to upper layer */ +int mncc_release_ind(struct osmocom_ms *ms, struct gsm_trans *trans, + u_int32_t callref, int location, int value) +{ + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(rel)); + rel.callref = callref; + mncc_set_cause(&rel, location, value); + return mncc_recvmsg(ms, trans, MNCC_REL_IND, &rel); +} + +/* sending status message in response to unknown message */ +static int gsm48_cc_tx_status(struct gsm_trans *trans, int cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + uint8_t *cause_ie, *call_state_ie; + + LOGP(DCC, LOGL_INFO, "sending STATUS (cause %d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STATUS; + + cause_ie = msgb_put(nmsg, 3); + cause_ie[0] = 2; + cause_ie[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_PRN_S_LU; + cause_ie[2] = 0x80 | cause; + + call_state_ie = msgb_put(nmsg, 1); + call_state_ie[0] = 0xc0 | trans->cc.state; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* reply status enquiry */ +static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) +{ + LOGP(DCC, LOGL_INFO, "received STATUS ENQUIREY\n"); + + return gsm48_cc_tx_status(trans, GSM48_CC_CAUSE_RESP_STATUS_INQ); +} + +/* + * process handlers (mobile originating call establish) + */ + +/* on SETUP request from L4, init MM connection */ +static int gsm48_cc_init_mm(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm_mncc *data = arg; + struct gsm48_mmxx_hdr *nmmh; + + /* store setup message */ + memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); + + new_cc_state(trans, GSM_CSTATE_MM_CONNECTION_PEND); + + /* establish MM connection */ + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_REQ, trans->callref, + trans->transaction_id); + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *) nmsg->data; + if (data->emergency) + nmmh->emergency = 1; + LOGP(DCC, LOGL_INFO, "Sending MMCC_EST_REQ\n"); + return gsm48_mmxx_downmsg(trans->ms, nmsg); +} + +/* abort connection prior SETUP */ +static int gsm48_cc_abort_mm(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + + /* abort MM connection */ + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_REQ, trans->callref, + trans->transaction_id); + if (!nmsg) + return -ENOMEM; + LOGP(DCC, LOGL_INFO, "Sending MMCC_REL_REQ\n"); + gsm48_mmxx_downmsg(trans->ms, nmsg); + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return 0; +} + +/* setup message from upper layer */ +static int gsm48_cc_tx_setup(struct gsm_trans *trans) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm_mncc *setup = &trans->cc.msg; + int rc, transaction_id; + uint8_t *ie; + + LOGP(DCC, LOGL_INFO, "sending SETUP\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + /* transaction id must not be assigned */ + if (trans->transaction_id != 0xff) { /* unasssigned */ + LOGP(DCC, LOGL_NOTICE, "TX Setup with assigned transaction. " + "This is not allowed!\n"); + /* Temporarily out of order */ + rc = mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NORMAL_UNSPEC); + trans->callref = 0; + trans_free(trans); + return rc; + } + + /* Get free transaction_id */ + transaction_id = trans_assign_trans_id(trans->ms, GSM48_PDISC_CC, 0); + if (transaction_id < 0) { + /* no free transaction ID */ + rc = mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + trans->callref = 0; + trans_free(trans); + return rc; + } + trans->transaction_id = transaction_id; + + gh->msg_type = (setup->emergency) ? GSM48_MT_CC_EMERG_SETUP : + GSM48_MT_CC_SETUP; + + /* actually we have to start it when CM SERVICE REQUEST has been sent, + * but there is no primitive for that defined. i think it is ok to + * do it here rather than inventing MMCC-NOTIFY-IND. + */ + gsm48_start_cc_timer(trans, 0x303, GSM48_T303_MS); + + if (!setup->emergency) { + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 0, &setup->bearer_cap); + /* facility */ + if (setup->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &setup->facility); + /* called party BCD number */ + if (setup->fields & MNCC_F_CALLED) + gsm48_encode_called(nmsg, &setup->called); + /* user-user */ + if (setup->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &setup->useruser); + /* ss version */ + if (setup->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &setup->ssversion); + /* CLIR suppression */ + if (setup->clir.sup) { + ie = msgb_put(nmsg, 1); + ie[0] = GSM48_IE_CLIR_SUPP; + } + /* CLIR invocation */ + if (setup->clir.inv) { + ie = msgb_put(nmsg, 1); + ie[0] = GSM48_IE_CLIR_INVOC; + } + /* cc cap */ + if (setup->fields & MNCC_F_CCCAP) + gsm48_encode_cccap(nmsg, &setup->cccap); + } + + /* actually MM CONNECTION PENDING */ + new_cc_state(trans, GSM_CSTATE_INITIATED); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* progress is received from lower layer */ +static int gsm48_cc_rx_progress(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc progress; + + LOGP(DCC, LOGL_INFO, "received PROGRESS\n"); + + memset(&progress, 0, sizeof(struct gsm_mncc)); + progress.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_PROGR_IND, 0); + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + progress.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&progress.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + /* store last progress indicator */ + trans->cc.prog_ind = progress.progress.descr; + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + progress.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&progress.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + return mncc_recvmsg(trans->ms, trans, MNCC_PROGRESS_IND, &progress); +} + +/* call proceeding is received from lower layer */ +static int gsm48_cc_rx_call_proceeding(struct gsm_trans *trans, + struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc call_proc; + + LOGP(DCC, LOGL_INFO, "sending CALL PROCEEDING\n"); + + gsm48_stop_cc_timer(trans); + + memset(&call_proc, 0, sizeof(struct gsm_mncc)); + call_proc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); +#if 0 + /* repeat */ + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) + call_conf.repeat = 1; + if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) + call_conf.repeat = 2; +#endif + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + call_proc.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&call_proc.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + call_proc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&call_proc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + call_proc.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&call_proc.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + /* store last progress indicator */ + trans->cc.prog_ind = call_proc.progress.descr; + } + + /* start T310, if last progress indicator was 1 or 2 or 64 */ + if (trans->cc.prog_ind == 1 + || trans->cc.prog_ind == 2 + || trans->cc.prog_ind == 64) + gsm48_start_cc_timer(trans, 0x310, GSM48_T310_MS); + + new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); + + return mncc_recvmsg(trans->ms, trans, MNCC_CALL_PROC_IND, + &call_proc); +} + +/* alerting is received by the lower layer */ +static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc alerting; + + LOGP(DCC, LOGL_INFO, "sending ALERTING\n"); + + gsm48_stop_cc_timer(trans); + /* no T301 in MS call control */ + + memset(&alerting, 0, sizeof(struct gsm_mncc)); + alerting.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + alerting.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&alerting.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + alerting.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&alerting.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + alerting.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&alerting.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); + + return mncc_recvmsg(trans->ms, trans, MNCC_ALERT_IND, + &alerting); +} + +/* connect is received from lower layer */ +static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc connect; + + LOGP(DCC, LOGL_INFO, "received CONNECT\n"); + + gsm48_stop_cc_timer(trans); + + memset(&connect, 0, sizeof(struct gsm_mncc)); + connect.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + connect.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&connect.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* connected */ + if (TLVP_PRESENT(&tp, GSM48_IE_CONN_BCD)) { + connect.fields |= MNCC_F_CONNECTED; + gsm48_decode_connected(&connect.connected, + TLVP_VAL(&tp, GSM48_IE_CONN_BCD)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + connect.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&connect.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + connect.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&connect.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + /* ACTIVE state is set during this: */ + gsm48_cc_tx_connect_ack(trans, NULL); + + return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_CNF, &connect); +} + +/* connect ack message from upper layer */ +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending CONNECT ACKNOWLEDGE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT_ACK; + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* + * process handlers (mobile terminating call establish) + */ + +/* setup is received from lower layer */ +static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc setup; + + LOGP(DCC, LOGL_INFO, "sending SETUP\n"); + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + setup.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&setup.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + setup.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&setup.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + setup.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&setup.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* signal */ + if (TLVP_PRESENT(&tp, GSM48_IE_SIGNAL)) { + setup.fields |= MNCC_F_SIGNAL; + gsm48_decode_signal(&setup.signal, + TLVP_VAL(&tp, GSM48_IE_SIGNAL)-1); + } + /* calling party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLING_BCD)) { + setup.fields |= MNCC_F_CALLING; + gsm48_decode_calling(&setup.calling, + TLVP_VAL(&tp, GSM48_IE_CALLING_BCD)-1); + } + /* called party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { + setup.fields |= MNCC_F_CALLED; + gsm48_decode_called(&setup.called, + TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); + } + /* redirecting party bcd number */ + if (TLVP_PRESENT(&tp, GSM48_IE_REDIR_BCD)) { + setup.fields |= MNCC_F_REDIRECTING; + gsm48_decode_redirecting(&setup.redirecting, + TLVP_VAL(&tp, GSM48_IE_REDIR_BCD)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + setup.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&setup.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); + + /* indicate setup to MNCC */ + mncc_recvmsg(trans->ms, trans, MNCC_SETUP_IND, &setup); + + return 0; +} + +/* call conf message from upper layer */ +static int gsm48_cc_tx_call_conf(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *confirm = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending CALL CONFIRMED (proceeding)\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CALL_CONF; + + new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + + /* bearer capability */ + if (confirm->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(nmsg, 0, &confirm->bearer_cap); + /* cause */ + if (confirm->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &confirm->cause); + /* cc cap */ + if (confirm->fields & MNCC_F_CCCAP) + gsm48_encode_cccap(nmsg, &confirm->cccap); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* alerting message from upper layer */ +static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *alerting = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending ALERTING\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_ALERTING; + + /* facility */ + if (alerting->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &alerting->facility); + /* user-user */ + if (alerting->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &alerting->useruser); + /* ss version */ + if (alerting->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &alerting->ssversion); + + new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* connect message from upper layer */ +static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *connect = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending CONNECT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x313, GSM48_T313_MS); + + /* facility */ + if (connect->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &connect->facility); + /* user-user */ + if (connect->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &connect->useruser); + /* ss version */ + if (connect->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &connect->ssversion); + + new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* connect ack is received from lower layer */ +static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc connect_ack; + + LOGP(DCC, LOGL_INFO, "received CONNECT ACKNOWLEDGE\n"); + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = trans->callref; + return mncc_recvmsg(trans->ms, trans, MNCC_SETUP_COMPL_IND, + &connect_ack); +} + +/* + * process handlers (during active state) + */ + +/* notify message from upper layer */ +static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *notify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending NOTIFY\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_NOTIFY; + + /* notify */ + gsm48_encode_notify(nmsg, notify->notify); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* notify is received from lower layer */ +static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc notify; + + LOGP(DCC, LOGL_INFO, "received NOTIFY\n"); + + memset(¬ify, 0, sizeof(struct gsm_mncc)); + notify.callref = trans->callref; + /* notify */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of notify message error.\n"); + return -EINVAL; + } + gsm48_decode_notify(¬ify.notify, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_NOTIFY_IND, ¬ify); +} + +/* start dtmf message from upper layer */ +static int gsm48_cc_tx_start_dtmf(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending START DTMF\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF; + + /* keypad */ + gsm48_encode_keypad(nmsg, dtmf->keypad); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* start dtmf ack is received from lower layer */ +static int gsm48_cc_rx_start_dtmf_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + LOGP(DCC, LOGL_INFO, "received START DTMF ACKNOWLEDGE\n"); + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* keypad facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { + dtmf.fields |= MNCC_F_KEYPAD; + gsm48_decode_keypad(&dtmf.keypad, + TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); + } + + return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_RSP, &dtmf); +} + +/* start dtmf rej is received from lower layer */ +static int gsm48_cc_rx_start_dtmf_rej(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc dtmf; + + LOGP(DCC, LOGL_INFO, "received START DTMF REJECT\n"); + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + /* cause */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of dtmf reject message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&dtmf.cause, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_START_DTMF_REJ, &dtmf); +} + +/* stop dtmf message from upper layer */ +static int gsm48_cc_tx_stop_dtmf(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending STOP DTMF\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STOP_DTMF; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* stop dtmf ack is received from lower layer */ +static int gsm48_cc_rx_stop_dtmf_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + LOGP(DCC, LOGL_INFO, "received STOP DTMF ACKNOWLEDGE\n"); + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + + return mncc_recvmsg(trans->ms, trans, MNCC_STOP_DTMF_RSP, &dtmf); +} + +/* hold message from upper layer */ +static int gsm48_cc_tx_hold(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending HOLD\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* hold ack is received from lower layer */ +static int gsm48_cc_rx_hold_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc hold; + + LOGP(DCC, LOGL_INFO, "received HOLD ACKNOWLEDGE\n"); + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + + return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_CNF, &hold); +} + +/* hold rej is received from lower layer */ +static int gsm48_cc_rx_hold_rej(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc hold; + + LOGP(DCC, LOGL_INFO, "received HOLD REJECT\n"); + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + /* cause */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of hold reject message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&hold.cause, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_HOLD_REJ, &hold); +} + +/* retrieve message from upper layer */ +static int gsm48_cc_tx_retrieve(struct gsm_trans *trans, void *arg) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending RETRIEVE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR; + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* retrieve ack is received from lower layer */ +static int gsm48_cc_rx_retrieve_ack(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc retrieve; + + LOGP(DCC, LOGL_INFO, "received RETRIEVE ACKNOWLEDGE\n"); + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + + return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_CNF, &retrieve); +} + +/* retrieve rej is received from lower layer */ +static int gsm48_cc_rx_retrieve_rej(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc retrieve; + + LOGP(DCC, LOGL_INFO, "received RETRIEVE REJECT\n"); + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + /* cause */ + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of retrieve reject message " + "error.\n"); + return -EINVAL; + } + gsm48_decode_cause(&retrieve.cause, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_RETRIEVE_REJ, &retrieve); +} + +/* facility message from upper layer or from timer event */ +static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *fac = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending FACILITY\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_FACILITY; + + /* facility */ + gsm48_encode_facility(nmsg, 1, &fac->facility); + /* ss version */ + if (fac->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &fac->ssversion); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* facility is received from lower layer */ +static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc fac; + + LOGP(DCC, LOGL_INFO, "received FACILITY\n"); + + memset(&fac, 0, sizeof(struct gsm_mncc)); + fac.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of facility message " + "error.\n"); + return -EINVAL; + } + /* facility */ + gsm48_decode_facility(&fac.facility, gh->data); + + return mncc_recvmsg(trans->ms, trans, MNCC_FACILITY_IND, &fac); +} + +/* user info message from upper layer or from timer event */ +static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *user = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending USERINFO\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_USER_INFO; + + /* user-user */ + if (user->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 1, &user->useruser); + /* more data */ + if (user->more) + gsm48_encode_more(nmsg); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* user info is received from lower layer */ +static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc user; + + LOGP(DCC, LOGL_INFO, "received USERINFO\n"); + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of userinfo message " + "error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_USER_USER, 0); + /* user-user */ + gsm48_decode_useruser(&user.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + /* more data */ + if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) + user.more = 1; + + return mncc_recvmsg(trans->ms, trans, MNCC_USERINFO_IND, &user); +} + +/* modify message from upper layer or from timer event */ +static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending MODIFY\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY; + + gsm48_start_cc_timer(trans, 0x323, GSM48_T323_MS); + + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* modify complete is received from lower layer */ +static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, + struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc modify; + + LOGP(DCC, LOGL_INFO, "received MODIFY COMPLETE\n"); + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of modify complete message " + "error.\n"); + return -EINVAL; + } + /* bearer capability */ + gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_CNF, &modify); +} + +/* modify reject is received from lower layer */ +static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + LOGP(DCC, LOGL_INFO, "received MODIFY REJECT\n"); + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of modify reject message " + "error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + modify.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&modify.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_REJ, &modify); +} + +/* modify is received from lower layer */ +static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm_mncc modify; + + LOGP(DCC, LOGL_INFO, "received MODIFY\n"); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + if (payload_len < 1) { + LOGP(DCC, LOGL_NOTICE, "Short read of modify message error.\n"); + return -EINVAL; + } + /* bearer capability */ + gsm48_decode_bearer_cap(&modify.bearer_cap, gh->data); + + new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); + + return mncc_recvmsg(trans->ms, trans, MNCC_MODIFY_IND, &modify); +} + +/* modify complete message from upper layer or from timer event */ +static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending MODIFY COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; + + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* modify reject message from upper layer or from timer event */ +static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending MODIFY REJECT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; + + /* bearer capability */ + gsm48_encode_bearer_cap(nmsg, 1, &modify->bearer_cap); + /* cause */ + gsm48_encode_cause(nmsg, 1, &modify->cause); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* + * process handlers (call clearing) + */ + +static struct gsm_mncc_cause default_cause = { + .location = GSM48_CAUSE_LOC_PRN_S_LU, + .coding = 0, + .rec = 0, + .rec_val = 0, + .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, + .diag_len = 0, + .diag = { 0 }, +}; + +/* disconnect message from upper layer or from timer event */ +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *disc = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending DISCONNECT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_DISCONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x305, GSM48_T305_MS); + + /* cause */ + if (disc->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 1, &disc->cause); + else + gsm48_encode_cause(nmsg, 1, &default_cause); + + /* facility */ + if (disc->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &disc->facility); + /* progress */ + if (disc->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(nmsg, 0, &disc->progress); + /* user-user */ + if (disc->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &disc->useruser); + /* ss version */ + if (disc->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &disc->ssversion); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); + + return gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); +} + +/* release message from upper layer or from timer event */ +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending RELEASE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x308, GSM48_T308_MS); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &rel->useruser); + /* ss version */ + if (rel->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &rel->ssversion); + + trans->cc.T308_second = 0; + memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); + + if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) + new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); + + gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); + + /* release without sending MMCC_REL_REQ */ + new_cc_state(trans, GSM_CSTATE_NULL); + trans->callref = 0; + trans_free(trans); + + return 0; +} + +/* reject message from upper layer */ +static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *nmsg; + struct gsm48_hdr *gh; + + LOGP(DCC, LOGL_INFO, "sending RELEASE COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + gsm48_stop_cc_timer(trans); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(nmsg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(nmsg, 0, &rel->useruser); + /* ss version */ + if (rel->fields & MNCC_F_SSVERSION) + gsm48_encode_ssversion(nmsg, &rel->ssversion); + + /* release without sending MMCC_REL_REQ */ + new_cc_state(trans, GSM_CSTATE_NULL); + trans->callref = 0; + trans_free(trans); + + return 0; +} + +/* disconnect is received from lower layer */ +static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc disc; + + LOGP(DCC, LOGL_INFO, "received DISCONNECT\n"); + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, + GSM48_IE_CAUSE, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + disc.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&disc.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + disc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&disc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + disc.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&disc.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + disc.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&disc.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + /* store disconnect cause for T305 expiry */ + memcpy(&trans->cc.msg, &disc, sizeof(struct gsm_mncc)); + + return mncc_recvmsg(trans->ms, trans, MNCC_DISC_IND, &disc); +} + +/* release is received from lower layer */ +static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + + LOGP(DCC, LOGL_INFO, "received RELEASE\n"); + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { + /* release collision 5.4.5 */ + mncc_recvmsg(trans->ms, trans, MNCC_REL_CNF, &rel); + } else { + struct msgb *nmsg; + + /* forward cause only */ + LOGP(DCC, LOGL_INFO, "sending RELEASE COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + if (rel.fields & MNCC_F_CAUSE) + gsm48_encode_cause(nmsg, 0, &rel.cause); + + gsm48_cc_to_mm(nmsg, trans, GSM48_MMCC_DATA_REQ); + + /* release indication */ + mncc_recvmsg(trans->ms, trans, MNCC_REL_IND, &rel); + } + + /* release MM conn, got NULL state, free trans */ + return gsm48_rel_null_free(trans); +} + +/* release complete is received from lower layer */ +static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + + LOGP(DCC, LOGL_INFO, "received RELEASE COMPLETE\n"); + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + + if (trans->callref) { + switch (trans->cc.state) { + case GSM_CSTATE_CALL_PRESENT: + mncc_recvmsg(trans->ms, trans, + MNCC_REJ_IND, &rel); + break; + case GSM_CSTATE_RELEASE_REQ: + mncc_recvmsg(trans->ms, trans, + MNCC_REL_CNF, &rel); + break; + default: + mncc_recvmsg(trans->ms, trans, + MNCC_REL_IND, &rel); + } + } + + /* release MM conn, got NULL state, free trans */ + return gsm48_rel_null_free(trans); +} + +/* + * state machines + */ + +/* state trasitions for MNCC messages (upper layer) */ +static struct downstate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, void *arg); +} downstatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.1 */ + MNCC_SETUP_REQ, gsm48_cc_init_mm}, + + {SBIT(GSM_CSTATE_MM_CONNECTION_PEND), /* 5.2.1 */ + MNCC_REL_REQ, gsm48_cc_abort_mm}, + + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.1 */ + MNCC_CALL_CONF_REQ, gsm48_cc_tx_call_conf}, + + {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* 5.2.2.3.2 */ + MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, + + {SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | + SBIT(GSM_CSTATE_CALL_RECEIVED), /* 5.2.2.5 */ + MNCC_SETUP_RSP, gsm48_cc_tx_connect}, + + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */ + MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, + + {ALL_STATES, /* 5.5.7.1 */ + MNCC_START_DTMF_REQ, gsm48_cc_tx_start_dtmf}, + + {ALL_STATES, /* 5.5.7.3 */ + MNCC_STOP_DTMF_REQ, gsm48_cc_tx_stop_dtmf}, + + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_REQ, gsm48_cc_tx_hold}, + + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_REQ, gsm48_cc_tx_retrieve}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), + MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, + + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, + + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - + SBIT(GSM_CSTATE_RELEASE_REQ) - + SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.3.1 */ + MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, + + {SBIT(GSM_CSTATE_INITIATED), + MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - + SBIT(GSM_CSTATE_RELEASE_REQ), /* ??? */ + MNCC_REL_REQ, gsm48_cc_tx_release}, + + /* modify */ + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, + + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, + + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + +int mncc_send(struct osmocom_ms *ms, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + struct gsm_trans *trans; + int i, rc; + + /* Find callref */ + trans = trans_find_by_callref(ms, data->callref); + + if (!trans) { + /* check for SETUP message */ + if (msg_type != MNCC_SETUP_REQ) { + /* Invalid call reference */ + return mncc_release_ind(ms, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); + } + if (data->callref >= 0x40000000) { + LOGP(DCC, LOGL_FATAL, "MNCC ref wrong.\n"); + return mncc_release_ind(ms, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_TRANS_ID); + } + + /* Create transaction */ + trans = trans_alloc(ms, GSM48_PDISC_CC, 0xff, data->callref); + if (!trans) { + /* No memory or whatever */ + return mncc_release_ind(ms, NULL, data->callref, + GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + } + } + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << trans->cc.state) & downstatelist[i].states)) + break; + if (i == DOWNSLLEN) { + LOGP(DCC, LOGL_NOTICE, "Message unhandled at this " + "state.\n"); + return 0; + } + + rc = downstatelist[i].rout(trans, arg); + + return rc; +} + +/* state trasitions for call control messages (lower layer) */ +static struct datastate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, struct msgb *msg); +} datastatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.3 */ + GSM48_MT_CC_CALL_PROC, gsm48_cc_rx_call_proceeding}, + + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | + SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.4.1 */ + MNCC_PROGRESS_REQ, gsm48_cc_rx_progress}, + + {SBIT(GSM_CSTATE_INITIATED) | + SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.5 */ + GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, + + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | + SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.6 */ + GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, + + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ + GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, + + {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.6 */ + GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, + + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), /* 5.3.1 */ + GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, + + {ALL_STATES, /* 8.4 */ + GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, + + {ALL_STATES, /* 5.5.7.2 */ + GSM48_MT_CC_START_DTMF_ACK, gsm48_cc_rx_start_dtmf_ack}, + + {ALL_STATES, /* 5.5.7.2 */ + GSM48_MT_CC_START_DTMF_REJ, gsm48_cc_rx_start_dtmf_rej}, + + {ALL_STATES, /* 5.5.7.4 */ + GSM48_MT_CC_STOP_DTMF_ACK, gsm48_cc_rx_stop_dtmf_ack}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD_ACK, gsm48_cc_rx_hold_ack}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_HOLD_REJ, gsm48_cc_rx_hold_rej}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR_ACK, gsm48_cc_rx_retrieve_ack}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_RETR_REJ, gsm48_cc_rx_retrieve_rej}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL), + GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, + + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, + + /* clearing */ + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ) - + SBIT(GSM_CSTATE_DISCONNECT_IND), /* 5.4.4.1.1 */ + GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, + + {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.3.3 & 5.4.5!!!*/ + GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, + + {ALL_STATES, /* 5.4.4.1.3 */ + GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, + + /* modify */ + {SBIT(GSM_CSTATE_ACTIVE), + GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, + + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, + + {SBIT(GSM_CSTATE_MO_TERM_MODIFY), + GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, +}; + +#define DATASLLEN \ + (sizeof(datastatelist) / sizeof(struct datastate)) + +static int gsm48_cc_data_ind(struct gsm_trans *trans, struct msgb *msg) +{ + struct osmocom_ms *ms = trans->ms; + struct gsm48_hdr *gh = msgb_l3(msg); + int msg_type = gh->msg_type & 0xbf; + uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; + /* flip */ + int msg_supported = 0; /* determine, if message is supported at all */ + int i, rc; + + /* set transaction ID, if not already */ + trans->transaction_id = transaction_id; + + /* pull the MMCC header */ + msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); + + LOGP(DCC, LOGL_INFO, "(ms %s) Received '%s' in CC state %s\n", ms->name, + gsm48_cc_msg_name(msg_type), + gsm48_cc_state_name(trans->cc.state)); + + /* find function for current state and message */ + for (i = 0; i < DATASLLEN; i++) { + if (msg_type == datastatelist[i].type) + msg_supported = 1; + if ((msg_type == datastatelist[i].type) + && ((1 << trans->cc.state) & datastatelist[i].states)) + break; + } + if (i == DATASLLEN) { + if (msg_supported) { + LOGP(DCC, LOGL_NOTICE, "Message unhandled at this " + "state.\n"); + return gsm48_cc_tx_status(trans, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE); + } else { + LOGP(DCC, LOGL_NOTICE, "Message not supported.\n"); + return gsm48_cc_tx_status(trans, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); + } + } + + rc = datastatelist[i].rout(trans, msg); + + return rc; +} + +/* receive message from MM layer */ +int gsm48_rcv_cc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + struct gsm_trans *trans; + int rc = 0; + + trans = trans_find_by_callref(ms, mmh->ref); + if (!trans) { + trans = trans_alloc(ms, GSM48_PDISC_CC, mmh->transaction_id, + mmh->ref); + if (!trans) + return -ENOMEM; + } + + LOGP(DCC, LOGL_INFO, "(ms %s) Received '%s' in CC state %s\n", ms->name, + get_mmxx_name(msg_type), + gsm48_cc_state_name(trans->cc.state)); + + switch (msg_type) { + case GSM48_MMCC_EST_IND: + /* data included */ + rc = gsm48_cc_data_ind(trans, msg); + break; + case GSM48_MMCC_EST_CNF: + /* send setup after confirm */ + if (trans->cc.state == GSM_CSTATE_MM_CONNECTION_PEND) + rc = gsm48_cc_tx_setup(trans); + else + LOGP(DCC, LOGL_ERROR, "Oops, MMCC-EST-CONF in state " + "%d?\n", trans->cc.state); + break; + case GSM48_MMCC_ERR_IND: /* no supporting re-establishment */ + case GSM48_MMCC_REL_IND: + /* release L4, release transaction */ + mncc_release_ind(trans->ms, trans, trans->callref, + GSM48_CAUSE_LOC_PRN_S_LU, mmh->cause); + /* release without sending MMCC_REL_REQ */ + new_cc_state(trans, GSM_CSTATE_NULL); + trans->callref = 0; + trans_free(trans); + break; + case GSM48_MMCC_DATA_IND: + rc = gsm48_cc_data_ind(trans, msg); + break; + case GSM48_MMCC_UNIT_DATA_IND: + break; + case GSM48_MMCC_SYNC_IND: + break; + default: + LOGP(DCC, LOGL_NOTICE, "Message unhandled.\n"); + rc = -ENOTSUP; + } + + return rc; +} + diff --git a/src/host/layer23/src/gsm48_mm.c b/src/host/layer23/src/gsm48_mm.c new file mode 100644 index 00000000..4c00bbb7 --- /dev/null +++ b/src/host/layer23/src/gsm48_mm.c @@ -0,0 +1,4100 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/utils.h> +#include <osmocore/gsm48.h> +#include <osmocore/talloc.h> + +#include <osmocom/logging.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/gsm48_cc.h> +#include <osmocom/l23_app.h> +#include <osmocom/networks.h> + +extern void *l23_ctx; + +void mm_conn_free(struct gsm48_mm_conn *conn); +static int gsm48_rcv_rr(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_rcv_mmr(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg); +static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type); +static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms); +static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms); +static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg); +static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate); +static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg); + +/* + * notes + */ + +/* + * Notes on IMSI detach procedure: + * + * At the end of the procedure, the state of MM, RR, cell selection: No SIM. + * + * In MM IDLE state, cell available: RR is establised, IMSI detach specific + * procedure is performed. + * + * In MM IDLE state, no cell: State is silently changed to No SIM. + * + * During any MM connection state, or Wait for network command: All MM + * connections (if any) are released locally, and IMSI detach specific + * procedure is performed. + * + * During IMSI detach processing: Request of IMSI detach is ignored. + * + * Any other state: The special 'delay_detach' flag is set only. If set, at any + * state transition we will clear the flag and restart the procedure again. + * + * The procedure is not spec conform, but always succeeds. + * + */ + +/* Notes on Service states: + * + * There are two PLMN search states: + * + * - PLMN SEARCH NORMAL + * - PLMN SEARCH + * + * They are entered, if: (4.2.1.2) + * - ME is switched on + * - SIM is inserted + * - user has asked PLMN selection in certain Service states + * - coverage is lost in certain Service states + * - roaming is denied + * - (optionally see 4.2.1.2) + * + * PLMN SEARCH NORMAL state is then entered, if all these conditions are met: + * - SIM is valid + * - SIM state is U1 + * - SIM LAI valid + * - cell selected + * - cell == SIM LAI + * + * Otherwhise PLMN SEARCH is entered. + * + * During PLMN SEARCH NORMAL state: (4.2.2.5) + * - on expirery of T3211 or T3213: Perform location update, when back + * to NORMAL SERVICE state. + * - on expirery of T3212: Perform periodic location update, when back + * to NORMAL SERVICE state. + * - perform IMSI detach + * - perform MM connections + * - respond to paging (if possible) + * + * During PLMN SEARCH state: (4.2.2.6) + * - reject MM connection except for emergency calls + * + * + * The NO CELL AVAILABLE state is entered, if: + * - no cell found during PLMN search + * + * During NO CELL AVAILABLE state: + * - reject any MM connection + * + * + * The NO IMSI state is entered if: + * - SIM is invalid + * - and cell is selected during PLMN SEARCH states + * + * During NO IMSO state: (4.2.2.4) + * - reject MM connection except for emergency calls + * + * + * The LIMITED SERVICE state is entered if: + * - SIM is valid + * - and SIM state is U3 + * - and cell is selected + * + * During LIMITED SERVICE state: (4.2.2.3) + * - reject MM connection except for emergency calls + * - perform location update, if new LAI is entered + * + * + * The LOCATION UPDATE NEEDED state is entered if: + * - SIM is valid + * - and location update must be performed for any reason + * + * During LOCATION UPDATE NEEDED state: + * - reject MM connection except for emergency calls + * + * This state is left if location update is possible and directly enter + * state ATTEMPTING TO UPDATE and trigger location update. + * The function gsm48_mm_loc_upd_possible() is used to check this on state + * change. + * + * + * The ATTEMPTING TO UPDATE state is entered if: + * - SIM is valid + * - and SIM state is U2 + * - and cell is selected + * + * During ATTEMPTING TO UPDATE state: (4.2.2.2) + * - on expirery of T3211 or T3213: Perform location updated + * - on expirery of T3212: Perform location updated + * - on change of LAI: Perform location update + * - (abnormal cases unsupported) + * - accept MM connection for emergency calls + * - trigger location update on any other MM connection + * - respond to paging (with IMSI only, because in U2 TMSI is not valid) + * + * + * The NORMAL SERVICE state is entered if: + * - SIM is valid + * - and SIM state is U1 + * - and cell is selected + * - and SIM LAI == cell + * + * During NORMAL SERVICE state: (4.2.2.1) + * - on expirery of T3211 or T3213: Perform location updated + * - on expirery of T3212: Perform location updated + * - on change of LAI: Perform location update + * - perform IMSI detach + * - perform MM connections + * - respond to paging + * + * + * gsm48_mm_set_plmn_search() is used enter PLMN SEARCH or PLMN SEARCH NORMAL + * state. Depending on the conditions above, the appropiate state is selected. + * + * + * gsm48_mm_return_idle() is used to select the Service state when returning + * to MM IDLE state after cell reselection. + * + * + * If cell selection process indicates NO_CELL_FOUND: + * + * - NO CELL AVAILABLE state is entered, if not already. + * + * gsm48_mm_no_cell_found() is used to select the Service state. + * + * + * If cell selection process indicates CELL_SELECTED: + * + * - NO IMSI state is entered, if no SIM valid. + * - Otherwise NORMAL SERVICES state is entered, if + * SIM state is U1, SIM LAI == cell, IMSI is attached, T3212 not expired. + * - Otherwise NORMAL SERVICES state is entered, if + * SIM state is U1, SIM LAI == cell, attach not required, T3212 not expired. + * - Otherwise LIMITED SERVICE state is entered, if + * CS mode is automatic, cell is forbidden PLMN or forbidden LA. + * - Otherwise LIMITED SERVICE state is entered, if + * CS mode is manual, cell is not the selected one. + * - Otherwise LOCATION UPDATE NEEDED state is entered. + * + * gsm48_mm_cell_selected() is used to select the Service state. + * + */ + +/* + * support functions + */ + +/* decode network name */ +static int decode_network_name(char *name, int name_len, + const uint8_t *lv) +{ + uint8_t in_len = lv[0]; + int length, padding; + + name[0] = '\0'; + if (in_len < 1) + return -EINVAL; + + /* must be CB encoded */ + if ((lv[1] & 0x70) != 0x00) + return -ENOTSUP; + + padding = lv[1] & 0x03; + length = ((in_len - 1) * 8 - padding) / 7; + if (length <= 0) + return 0; + if (length >= name_len) + length = name_len - 1; + gsm_7bit_decode(name, lv + 2, length); + name[length] = '\0'; + + return length; +} + +/* encode 'mobile identity' */ +int gsm48_encode_mi(uint8_t *buf, struct msgb *msg, struct osmocom_ms *ms, + uint8_t mi_type) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *set = &ms->settings; + uint8_t *ie; + + switch(mi_type) { + case GSM_MI_TYPE_TMSI: + gsm48_generate_mid_from_tmsi(buf, subscr->tmsi); + break; + case GSM_MI_TYPE_IMSI: + gsm48_generate_mid_from_imsi(buf, subscr->imsi); + break; + case GSM_MI_TYPE_IMEI: + gsm48_generate_mid_from_imsi(buf, set->imei); + break; + case GSM_MI_TYPE_IMEISV: + gsm48_generate_mid_from_imsi(buf, set->imeisv); + break; + case GSM_MI_TYPE_NONE: + default: + buf[0] = GSM48_IE_MOBILE_ID; + buf[1] = 1; + buf[2] = 0xf0; + break; + } + /* alter MI type */ + buf[2] = (buf[2] & ~GSM_MI_TYPE_MASK) | mi_type; + + if (msg) { + /* MI as LV */ + ie = msgb_put(msg, 1 + buf[1]); + memcpy(ie, buf + 1, 1 + buf[1]); + } + + return 0; +} + +/* encode 'classmark 1' */ +int gsm48_encode_classmark1(struct gsm48_classmark1 *cm, uint8_t rev_lev, + uint8_t es_ind, uint8_t a5_1, uint8_t pwr_lev) +{ + memset(cm, 0, sizeof(*cm)); + cm->rev_lev = rev_lev; + cm->es_ind = es_ind; + cm->a5_1 = a5_1; + cm->pwr_lev = pwr_lev; + + return 0; +} + +/* + * timers + */ + +static void timeout_mm_t3210(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3210 (loc. upd. timeout) has fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3210, NULL); +} + +static void timeout_mm_t3211(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DSUM, LOGL_INFO, "Location update retry\n"); + LOGP(DMM, LOGL_INFO, "timer T3211 (loc. upd. retry delay) has fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3211, NULL); +} + +static void timeout_mm_t3212(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DSUM, LOGL_INFO, "Periodic location update\n"); + LOGP(DMM, LOGL_INFO, "timer T3212 (periodic loc. upd. delay) has " + "fired\n"); + + /* reset attempt counter when attempting to update (4.4.4.5) */ + if (mm->state == GSM48_MM_ST_MM_IDLE + && mm->substate == GSM48_MM_SST_ATTEMPT_UPDATE) + mm->lupd_attempt = 0; + + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3212, NULL); +} + +static void timeout_mm_t3213(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DSUM, LOGL_INFO, "Location update retry\n"); + LOGP(DMM, LOGL_INFO, "timer T3213 (delay after RA failure) has " + "fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3213, NULL); +} + +static void timeout_mm_t3230(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3230 (MM connection timeout) has " + "fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3230, NULL); +} + +static void timeout_mm_t3220(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3220 (IMSI detach keepalive) has " + "fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3220, NULL); +} + +static void timeout_mm_t3240(void *arg) +{ + struct gsm48_mmlayer *mm = arg; + + LOGP(DMM, LOGL_INFO, "timer T3240 (RR release timeout) has fired\n"); + gsm48_mm_ev(mm->ms, GSM48_MM_EVENT_TIMEOUT_T3240, NULL); +} + +static void start_mm_t3210(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3210 (loc. upd. timeout) with %d.%d " + "seconds\n", GSM_T3210_MS); + mm->t3210.cb = timeout_mm_t3210; + mm->t3210.data = mm; + bsc_schedule_timer(&mm->t3210, GSM_T3210_MS); +} + +static void start_mm_t3211(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3211 (loc. upd. retry delay) with " + "%d.%d seconds\n", GSM_T3211_MS); + mm->t3211.cb = timeout_mm_t3211; + mm->t3211.data = mm; + bsc_schedule_timer(&mm->t3211, GSM_T3211_MS); +} + +static void start_mm_t3212(struct gsm48_mmlayer *mm, int sec) +{ + /* don't start, if is not available */ + if (!sec) + return; + + LOGP(DMM, LOGL_INFO, "starting T3212 (periodic loc. upd. delay) with " + "%d seconds\n", sec); + mm->t3212.cb = timeout_mm_t3212; + mm->t3212.data = mm; + bsc_schedule_timer(&mm->t3212, sec, 0); +} + +static void start_mm_t3213(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3213 (delay after RA failure) with " + "%d.%d seconds\n", GSM_T3213_MS); + mm->t3213.cb = timeout_mm_t3213; + mm->t3213.data = mm; + bsc_schedule_timer(&mm->t3213, GSM_T3213_MS); +} + +static void start_mm_t3220(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3220 (IMSI detach keepalive) with " + "%d.%d seconds\n", GSM_T3220_MS); + mm->t3220.cb = timeout_mm_t3220; + mm->t3220.data = mm; + bsc_schedule_timer(&mm->t3220, GSM_T3220_MS); +} + +static void start_mm_t3230(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3230 (MM connection timeout) with " + "%d.%d seconds\n", GSM_T3230_MS); + mm->t3230.cb = timeout_mm_t3230; + mm->t3230.data = mm; + bsc_schedule_timer(&mm->t3230, GSM_T3230_MS); +} + +static void start_mm_t3240(struct gsm48_mmlayer *mm) +{ + LOGP(DMM, LOGL_INFO, "starting T3240 (RR release timeout) with %d.%d " + "seconds\n", GSM_T3240_MS); + mm->t3240.cb = timeout_mm_t3240; + mm->t3240.data = mm; + bsc_schedule_timer(&mm->t3240, GSM_T3240_MS); +} + +static void stop_mm_t3210(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3210)) { + LOGP(DMM, LOGL_INFO, "stopping pending (loc. upd. timeout) " + "timer T3210\n"); + bsc_del_timer(&mm->t3210); + } +} + +static void stop_mm_t3211(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3211)) { + LOGP(DMM, LOGL_INFO, "stopping pending (loc. upd. retry " + "delay) timer T3211\n"); + bsc_del_timer(&mm->t3211); + } +} + +static void stop_mm_t3212(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3212)) { + LOGP(DMM, LOGL_INFO, "stopping pending (periodic loc. upd. " + "delay) timer T3212\n"); + bsc_del_timer(&mm->t3212); + } +} + +static void stop_mm_t3213(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3213)) { + LOGP(DMM, LOGL_INFO, "stopping pending (delay after RA " + "failure) timer T3213\n"); + bsc_del_timer(&mm->t3213); + } +} + +static void stop_mm_t3220(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3220)) { + LOGP(DMM, LOGL_INFO, "stopping pending (IMSI detach keepalive) " + "timer T3220\n"); + bsc_del_timer(&mm->t3220); + } +} + +static void stop_mm_t3230(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3230)) { + LOGP(DMM, LOGL_INFO, "stopping pending (MM connection timeout) " + "timer T3230\n"); + bsc_del_timer(&mm->t3230); + } +} + +static void stop_mm_t3240(struct gsm48_mmlayer *mm) +{ + if (bsc_timer_pending(&mm->t3240)) { + LOGP(DMM, LOGL_INFO, "stopping pending (RR release timeout) " + "timer T3240\n"); + bsc_del_timer(&mm->t3240); + } +} + +static void stop_mm_t3241(struct gsm48_mmlayer *mm) +{ + /* not implemented, not required */ +} + +/* + * messages + */ + +/* names of MM events */ +static const struct value_string gsm48_mmevent_names[] = { + { GSM48_MM_EVENT_CELL_SELECTED, "MM_EVENT_CELL_SELECTED" }, + { GSM48_MM_EVENT_NO_CELL_FOUND, "MM_EVENT_NO_CELL_FOUND" }, + { GSM48_MM_EVENT_TIMEOUT_T3210, "MM_EVENT_TIMEOUT_T3210" }, + { GSM48_MM_EVENT_TIMEOUT_T3211, "MM_EVENT_TIMEOUT_T3211" }, + { GSM48_MM_EVENT_TIMEOUT_T3212, "MM_EVENT_TIMEOUT_T3212" }, + { GSM48_MM_EVENT_TIMEOUT_T3213, "MM_EVENT_TIMEOUT_T3213" }, + { GSM48_MM_EVENT_TIMEOUT_T3220, "MM_EVENT_TIMEOUT_T3220" }, + { GSM48_MM_EVENT_TIMEOUT_T3230, "MM_EVENT_TIMEOUT_T3230" }, + { GSM48_MM_EVENT_TIMEOUT_T3240, "MM_EVENT_TIMEOUT_T3240" }, + { GSM48_MM_EVENT_IMSI_DETACH, "MM_EVENT_IMSI_DETACH" }, + { GSM48_MM_EVENT_PAGING, "MM_EVENT_PAGING" }, + { GSM48_MM_EVENT_AUTH_RESPONSE, "MM_EVENT_AUTH_RESPONSE" }, + { GSM48_MM_EVENT_SYSINFO, "MM_EVENT_SYSINFO" }, + { GSM48_MM_EVENT_USER_PLMN_SEL, "MM_EVENT_USER_PLMN_SEL" }, + { 0, NULL } +}; + +const char *get_mmevent_name(int value) +{ + return get_value_string(gsm48_mmevent_names, value); +} + +/* names of MM-SAP */ +static const struct value_string gsm48_mm_msg_names[] = { + { GSM48_MT_MM_IMSI_DETACH_IND, "MT_MM_IMSI_DETACH_IND" }, + { GSM48_MT_MM_LOC_UPD_ACCEPT, "MT_MM_LOC_UPD_ACCEPT" }, + { GSM48_MT_MM_LOC_UPD_REJECT, "MT_MM_LOC_UPD_REJECT" }, + { GSM48_MT_MM_LOC_UPD_REQUEST, "MT_MM_LOC_UPD_REQUEST" }, + { GSM48_MT_MM_AUTH_REJ, "MT_MM_AUTH_REJ" }, + { GSM48_MT_MM_AUTH_REQ, "MT_MM_AUTH_REQ" }, + { GSM48_MT_MM_AUTH_RESP, "MT_MM_AUTH_RESP" }, + { GSM48_MT_MM_ID_REQ, "MT_MM_ID_REQ" }, + { GSM48_MT_MM_ID_RESP, "MT_MM_ID_RESP" }, + { GSM48_MT_MM_TMSI_REALL_CMD, "MT_MM_TMSI_REALL_CMD" }, + { GSM48_MT_MM_TMSI_REALL_COMPL, "MT_MM_TMSI_REALL_COMPL" }, + { GSM48_MT_MM_CM_SERV_ACC, "MT_MM_CM_SERV_ACC" }, + { GSM48_MT_MM_CM_SERV_REJ, "MT_MM_CM_SERV_REJ" }, + { GSM48_MT_MM_CM_SERV_ABORT, "MT_MM_CM_SERV_ABORT" }, + { GSM48_MT_MM_CM_SERV_REQ, "MT_MM_CM_SERV_REQ" }, + { GSM48_MT_MM_CM_SERV_PROMPT, "MT_MM_CM_SERV_PROMPT" }, + { GSM48_MT_MM_CM_REEST_REQ, "MT_MM_CM_REEST_REQ" }, + { GSM48_MT_MM_ABORT, "MT_MM_ABORT" }, + { GSM48_MT_MM_NULL, "MT_MM_NULL" }, + { GSM48_MT_MM_STATUS, "MT_MM_STATUS" }, + { GSM48_MT_MM_INFO, "MT_MM_INFO" }, + { 0, NULL } +}; + +const char *get_mm_name(int value) +{ + return get_value_string(gsm48_mm_msg_names, value); +} + +/* names of MMxx-SAP */ +static const struct value_string gsm48_mmxx_msg_names[] = { + { GSM48_MMCC_EST_REQ, "MMCC_EST_REQ" }, + { GSM48_MMCC_EST_IND, "MMCC_EST_IND" }, + { GSM48_MMCC_EST_CNF, "MMCC_EST_CNF" }, + { GSM48_MMCC_REL_REQ, "MMCC_REL_REQ" }, + { GSM48_MMCC_REL_IND, "MMCC_REL_IND" }, + { GSM48_MMCC_DATA_REQ, "MMCC_DATA_REQ" }, + { GSM48_MMCC_DATA_IND, "MMCC_DATA_IND" }, + { GSM48_MMCC_UNIT_DATA_REQ, "MMCC_UNIT_DATA_REQ" }, + { GSM48_MMCC_UNIT_DATA_IND, "MMCC_UNIT_DATA_IND" }, + { GSM48_MMCC_SYNC_IND, "MMCC_SYNC_IND" }, + { GSM48_MMCC_REEST_REQ, "MMCC_REEST_REQ" }, + { GSM48_MMCC_REEST_CNF, "MMCC_REEST_CNF" }, + { GSM48_MMCC_ERR_IND, "MMCC_ERR_IND" }, + { GSM48_MMCC_PROMPT_IND, "MMCC_PROMPT_IND" }, + { GSM48_MMCC_PROMPT_REJ, "MMCC_PROMPT_REJ" }, + { GSM48_MMSS_EST_REQ, "MMSS_EST_REQ" }, + { GSM48_MMSS_EST_IND, "MMSS_EST_IND" }, + { GSM48_MMSS_EST_CNF, "MMSS_EST_CNF" }, + { GSM48_MMSS_REL_REQ, "MMSS_REL_REQ" }, + { GSM48_MMSS_REL_IND, "MMSS_REL_IND" }, + { GSM48_MMSS_DATA_REQ, "MMSS_DATA_REQ" }, + { GSM48_MMSS_DATA_IND, "MMSS_DATA_IND" }, + { GSM48_MMSS_UNIT_DATA_REQ, "MMSS_UNIT_DATA_REQ" }, + { GSM48_MMSS_UNIT_DATA_IND, "MMSS_UNIT_DATA_IND" }, + { GSM48_MMSS_REEST_REQ, "MMSS_REEST_REQ" }, + { GSM48_MMSS_REEST_CNF, "MMSS_REEST_CNF" }, + { GSM48_MMSS_ERR_IND, "MMSS_ERR_IND" }, + { GSM48_MMSS_PROMPT_IND, "MMSS_PROMPT_IND" }, + { GSM48_MMSS_PROMPT_REJ, "MMSS_PROMPT_REJ" }, + { GSM48_MMSMS_EST_REQ, "MMSMS_EST_REQ" }, + { GSM48_MMSMS_EST_IND, "MMSMS_EST_IND" }, + { GSM48_MMSMS_EST_CNF, "MMSMS_EST_CNF" }, + { GSM48_MMSMS_REL_REQ, "MMSMS_REL_REQ" }, + { GSM48_MMSMS_REL_IND, "MMSMS_REL_IND" }, + { GSM48_MMSMS_DATA_REQ, "MMSMS_DATA_REQ" }, + { GSM48_MMSMS_DATA_IND, "MMSMS_DATA_IND" }, + { GSM48_MMSMS_UNIT_DATA_REQ, "MMSMS_UNIT_DATA_REQ" }, + { GSM48_MMSMS_UNIT_DATA_IND, "MMSMS_UNIT_DATA_IND" }, + { GSM48_MMSMS_REEST_REQ, "MMSMS_REEST_REQ" }, + { GSM48_MMSMS_REEST_CNF, "MMSMS_REEST_CNF" }, + { GSM48_MMSMS_ERR_IND, "MMSMS_ERR_IND" }, + { GSM48_MMSMS_PROMPT_IND, "MMSMS_PROMPT_IND" }, + { GSM48_MMSMS_PROMPT_REJ, "MMSMS_PROMPT_REJ" }, + { 0, NULL } +}; + +const char *get_mmxx_name(int value) +{ + return get_value_string(gsm48_mmxx_msg_names, value); +} + +/* names of MMR-SAP */ +static const struct value_string gsm48_mmr_msg_names[] = { + { GSM48_MMR_REG_REQ, "MMR_REG_REQ" }, + { GSM48_MMR_REG_CNF, "MMR_REG_CNF" }, + { GSM48_MMR_NREG_REQ, "MMR_NREG_REQ" }, + { GSM48_MMR_NREG_IND, "MMR_NREG_IND" }, + { 0, NULL } +}; + +const char *get_mmr_name(int value) +{ + return get_value_string(gsm48_mmr_msg_names, value); +} + +/* allocate GSM 04.08 message (MMxx-SAP) */ +struct msgb *gsm48_mmxx_msgb_alloc(int msg_type, uint32_t ref, + uint8_t transaction_id) +{ + struct msgb *msg; + struct gsm48_mmxx_hdr *mmh; + + msg = msgb_alloc_headroom(MMXX_ALLOC_SIZE+MMXX_ALLOC_HEADROOM, + MMXX_ALLOC_HEADROOM, "GSM 04.08 MMxx"); + if (!msg) + return NULL; + + mmh = (struct gsm48_mmxx_hdr *)msgb_put(msg, sizeof(*mmh)); + mmh->msg_type = msg_type; + mmh->ref = ref; + mmh->transaction_id = transaction_id; + + return msg; +} + +/* allocate MM event message */ +struct msgb *gsm48_mmevent_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm48_mm_event *mme; + + msg = msgb_alloc_headroom(sizeof(*mme), 0, "GSM 04.08 MM event"); + if (!msg) + return NULL; + + mme = (struct gsm48_mm_event *)msgb_put(msg, sizeof(*mme)); + mme->msg_type = msg_type; + + return msg; +} + +/* allocate MMR message */ +struct msgb *gsm48_mmr_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm48_mmr *mmr; + + msg = msgb_alloc_headroom(sizeof(*mmr), 0, "GSM 04.08 MMR"); + if (!msg) + return NULL; + + mmr = (struct gsm48_mmr *)msgb_put(msg, sizeof(*mmr)); + mmr->msg_type = msg_type; + + return msg; +} + +/* queue message (MMxx-SAP) */ +int gsm48_mmxx_upmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->mmxx_upqueue, msg); + + return 0; +} + +/* queue message (MMR-SAP) */ +int gsm48_mmr_downmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->mmr_downqueue, msg); + + return 0; +} + +/* queue MM event message */ +int gsm48_mmevent_msg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->event_queue, msg); + + return 0; +} + +/* dequeue messages (MMxx-SAP) */ +int gsm48_mmxx_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *msg; + struct gsm48_mmxx_hdr *mmh; + int work = 0; + + while ((msg = msgb_dequeue(&mm->mmxx_upqueue))) { + mmh = (struct gsm48_mmxx_hdr *) msg->data; + switch (mmh->msg_type & GSM48_MMXX_MASK) { + case GSM48_MMCC_CLASS: + gsm48_rcv_cc(ms, msg); + break; +#if 0 + case GSM48_MMSS_CLASS: + gsm48_rcv_ss(ms, msg); + break; + case GSM48_MMSMS_CLASS: + gsm48_rcv_sms(ms, msg); + break; +#endif + } + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* dequeue messages (MMR-SAP) */ +int gsm48_mmr_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *msg; + struct gsm48_mmr *mmr; + int work = 0; + + while ((msg = msgb_dequeue(&mm->mmr_downqueue))) { + mmr = (struct gsm48_mmr *) msg->data; + gsm48_rcv_mmr(ms, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* dequeue messages (RR-SAP) */ +int gsm48_rr_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&mm->rr_upqueue))) { + /* msg is freed there */ + gsm48_rcv_rr(ms, msg); + work = 1; /* work done */ + } + + return work; +} + +/* dequeue MM event messages */ +int gsm48_mmevent_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_event *mme; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&mm->event_queue))) { + mme = (struct gsm48_mm_event *) msg->data; + gsm48_mm_ev(ms, mme->msg_type, msg); + msgb_free(msg); + work = 1; /* work done */ + } + + return work; +} + +/* push RR header and send to RR */ +static int gsm48_mm_to_rr(struct osmocom_ms *ms, struct msgb *msg, + int msg_type, uint8_t cause) +{ + struct gsm48_rr_hdr *rrh; + + /* push RR header */ + msgb_push(msg, sizeof(struct gsm48_rr_hdr)); + rrh = (struct gsm48_rr_hdr *) msg->data; + rrh->msg_type = msg_type; + rrh->cause = cause; + + /* send message to RR */ + return gsm48_rr_downmsg(ms, msg); +} + +/* + * state transition + */ + +const char *gsm48_mm_state_names[] = { + "NULL", + "undefined 1", + "undefined 2", + "location updating initiated", + "undefined 4", + "wait for outgoing MM connection", + "MM connection active", + "IMSI detach initiated", + "process CM service prompt", + "wait for network command", + "location updating reject", + "undefined 11", + "undefined 12", + "wait for RR connection (location updating)", + "wait for RR connection (MM connection)", + "wait for RR connection (IMSI detach)", + "undefined 16", + "wait for re-establishment", + "wait for RR connection active", + "MM idle", + "wait for additional outgoing MM connection", + "MM_CONN_ACTIVE_VGCS", + "WAIT_RR_CONN_VGCS", + "location updating pending", + "IMSI detach pending", + "RR connection release not allowed" +}; + +const char *gsm48_mm_substate_names[] = { + "NULL", + "normal service", + "attempting to update", + "limited service", + "no IMSI", + "no cell available", + "location updating needed", + "PLMN search", + "PLMN search (normal)", + "RX_VGCS_NORMAL", + "RX_VGCS_LIMITED" +}; + +/* change state from LOCATION UPDATE NEEDED to ATTEMPTING TO UPDATE */ +static int gsm48_mm_loc_upd_possible(struct gsm48_mmlayer *mm) +{ + // TODO: check if really possible + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_ATTEMPT_UPDATE); + return gsm48_mm_loc_upd_normal(mm->ms, NULL); +} + +/* Set new MM state, also new substate in case of MM IDLE state. */ +static void new_mm_state(struct gsm48_mmlayer *mm, int state, int substate) +{ + /* IDLE -> IDLE */ + if (mm->state == GSM48_MM_ST_MM_IDLE && state == mm->state) + LOGP(DMM, LOGL_INFO, "new MM IDLE state %s -> %s\n", + gsm48_mm_substate_names[mm->substate], + gsm48_mm_substate_names[substate]); + /* IDLE -> non-IDLE */ + else if (mm->state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "new state MM IDLE, %s -> %s\n", + gsm48_mm_substate_names[mm->substate], + gsm48_mm_state_names[state]); + /* non-IDLE -> IDLE */ + else if (state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "new state %s -> MM IDLE, %s\n", + gsm48_mm_state_names[mm->state], + gsm48_mm_substate_names[substate]); + /* non-IDLE -> non-IDLE */ + else + LOGP(DMM, LOGL_INFO, "new state %s -> %s\n", + gsm48_mm_state_names[mm->state], + gsm48_mm_state_names[state]); + + /* remember most recent substate */ + if (mm->state == GSM48_MM_ST_MM_IDLE) + mm->mr_substate = mm->substate; + + mm->state = state; + mm->substate = substate; + + /* resend detach event, if flag is set */ + if (state == GSM48_MM_ST_MM_IDLE && mm->delay_detach) { + struct msgb *nmsg; + + mm->delay_detach = 0; + + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); + if (!nmsg) + return; + gsm48_mmevent_msg(mm->ms, nmsg); + } + + /* 4.4.2 start T3212 in MM IDLE mode if not started or has expired */ + if (state == GSM48_MM_ST_MM_IDLE + && (substate == GSM48_MM_SST_NORMAL_SERVICE + || substate == GSM48_MM_SST_ATTEMPT_UPDATE)) { + /* start periodic location update timer */ + if (!bsc_timer_pending(&mm->t3212)) + start_mm_t3212(mm, mm->t3212_value); + /* perform pending location update */ + if (mm->lupd_retry) { + LOGP(DMM, LOGL_INFO, "Loc. upd. pending (type %d)\n", + mm->lupd_type); + mm->lupd_retry = 0; + gsm48_mm_loc_upd(mm->ms, NULL); + /* must exit, because this function can be called + * recursively + */ + return; + } + if (mm->lupd_periodic) { + struct gsm48_sysinfo *s = &mm->ms->cellsel.sel_si; + + LOGP(DMM, LOGL_INFO, "Periodic loc. upd. pending " + "(type %d)\n", mm->lupd_type); + mm->lupd_periodic = 0; + if (s->t3212) + gsm48_mm_loc_upd_periodic(mm->ms, NULL); + else + LOGP(DMM, LOGL_INFO, "but not requred\n"); + /* must exit, because this function can be called + * recursively + */ + return; + } + } + + /* check if location update is possible */ + if (state == GSM48_MM_ST_MM_IDLE + && substate == GSM48_MM_SST_LOC_UPD_NEEDED) { + gsm48_mm_loc_upd_possible(mm); + /* must exit, because this function can be called recursively */ + return; + } +} + +/* return PLMN SEARCH or PLMN SEARCH NORMAL state */ +static int gsm48_mm_set_plmn_search(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + + /* SIM not inserted */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "no SIM.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* SIM not updated */ + if (subscr->ustate != GSM_SIM_U1_UPDATED) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "SIM not updated.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + if (!subscr->lai_valid) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "LAI in SIM not valid.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* no cell selected */ + if (!cs->selected) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "no cell selected.\n"); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* selected cell's LAI not equal to LAI stored on the sim */ + if (cs->sel_mcc != subscr->lai_mcc + || cs->sel_mnc != subscr->lai_mnc + || cs->sel_lac != subscr->lai_lac) { + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH state, because " + "LAI of selected cell (MCC %s MNC %s LAC 0x%04x) " + "!= LAI in SIM (MCC %s MNC %s LAC 0x%04x).\n", + gsm_print_mcc(cs->sel_mcc), gsm_print_mnc(cs->sel_mnc), + cs->sel_lac, gsm_print_mcc(subscr->lai_mcc), + gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac); + return GSM48_MM_SST_PLMN_SEARCH; + } + + /* SIM is updated in this LA */ + LOGP(DMM, LOGL_INFO, "Selecting PLMN SEARCH NORMAL state.\n"); + return GSM48_MM_SST_PLMN_SEARCH_NORMAL; +} + +/* 4.2.3 when returning to MM IDLE state, this function is called */ +static int gsm48_mm_return_idle(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + + /* 4.4.4.9 start T3211 when RR is released */ + if (mm->start_t3211) { + LOGP(DMM, LOGL_INFO, "Starting T3211 after RR release.\n"); + mm->start_t3211 = 0; + start_mm_t3211(mm); + } + + /* return from location update with "Roaming not allowed" */ + if (mm->state == GSM48_MM_ST_LOC_UPD_REJ && mm->lupd_rej_cause == 13) { + LOGP(DMM, LOGL_INFO, "Roaming not allowed as returning to " + "MM IDLE\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + gsm48_mm_set_plmn_search(ms)); + + return 0; + } + + /* no SIM present or invalid */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "SIM invalid as returning to MM IDLE\n"); + + /* stop periodic location updating */ + mm->lupd_pending = 0; + stop_mm_t3212(mm); /* 4.4.2 */ + + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_IMSI); + + return 0; + } + + /* selected cell equals the registered LAI */ + if (subscr->lai_valid + && cs->sel_mcc == subscr->lai_mcc + && cs->sel_mnc == subscr->lai_mnc + && cs->sel_lac == subscr->lai_lac) { + LOGP(DMM, LOGL_INFO, "We are in registered LAI as returning " + "to MM IDLE\n"); + /* if SIM not updated (abnormal case as described in 4.4.4.9) */ + if (subscr->ustate != GSM_SIM_U1_UPDATED) + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_ATTEMPT_UPDATE); + else + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + return 0; + } + + /* location update allowed */ + if (cs->state == GSM322_C3_CAMPED_NORMALLY) { + LOGP(DMM, LOGL_INFO, "We are camping normally as returning to " + "MM IDLE\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LOC_UPD_NEEDED); + } else { /* location update not allowed */ + LOGP(DMM, LOGL_INFO, "We are camping on any cell as returning " + "to MM IDLE\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + } + + return 0; +} + +/* 4.2.1.1 Service state PLMN SEARCH (NORMAL) is left if no cell found */ +static int gsm48_mm_no_cell_found(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_CELL_AVAIL); + + return 0; +} + +/* 4.2.1.1 Service state PLMN SEARCH (NORMAL) / NO CELL AVAILABLE is left + * if cell selected + */ +static int gsm48_mm_cell_selected(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm322_plmn *plmn = &ms->plmn; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm_settings *set = &ms->settings; + + /* no SIM is inserted */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "SIM invalid as cell is selected.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_NO_IMSI); + + return 0; + } + + /* SIM not updated in this LA */ + if (subscr->ustate == GSM_SIM_U1_UPDATED + && subscr->lai_valid + && cs->sel_mcc == subscr->lai_mcc + && cs->sel_mnc == subscr->lai_mnc + && cs->sel_lac == subscr->lai_lac + && !mm->lupd_periodic) { + if (subscr->imsi_attached) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Valid in location area.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + if (!s->att_allowed) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Attachment not required.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_NORMAL_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + /* else, continue */ + } + + /* PLMN mode auto and selected cell is forbidden */ + if (set->plmn_mode == PLMN_MODE_AUTO + && (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc) + || gsm322_is_forbidden_la(ms, cs->sel_mcc, cs->sel_mnc, + cs->sel_lac))) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Selected cell is forbidden.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* PLMN mode manual and selected cell not selected PLMN */ + if (set->plmn_mode == PLMN_MODE_MANUAL + && (plmn->mcc != cs->sel_mcc + || plmn->mnc != cs->sel_mnc)) { + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "Selected cell not found.\n"); + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + GSM48_MM_SST_LIMITED_SERVICE); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* other cases */ + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, GSM48_MM_SST_LOC_UPD_NEEDED); + + return 0; +} + +/* 4.2.1.2 Service state PLMN SEARCH (NORMAL) is entered */ +static int gsm48_mm_plmn_search(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, gsm48_mm_set_plmn_search(ms)); + + return 0; +} + +/* + * init and exit + */ + +/* initialize Mobility Management process */ +int gsm48_mm_init(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + memset(mm, 0, sizeof(*mm)); + mm->ms = ms; + + LOGP(DMM, LOGL_INFO, "init Mobility Management process\n"); + + /* 4.2.1.1 */ + mm->state = GSM48_MM_ST_MM_IDLE; + mm->substate = gsm48_mm_set_plmn_search(ms); + + /* init lists */ + INIT_LLIST_HEAD(&mm->mm_conn); + INIT_LLIST_HEAD(&mm->rr_upqueue); + INIT_LLIST_HEAD(&mm->mmxx_upqueue); + INIT_LLIST_HEAD(&mm->mmr_downqueue); + INIT_LLIST_HEAD(&mm->event_queue); + + return 0; +} + +/* exit MM process */ +int gsm48_mm_exit(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn; + struct msgb *msg; + + LOGP(DMM, LOGL_INFO, "exit Mobility Management process\n"); + + /* flush lists */ + while (!llist_empty(&mm->mm_conn)) { + conn = llist_entry(mm->mm_conn.next, + struct gsm48_mm_conn, list); + llist_del(&conn->list); + mm_conn_free(conn); + } + while ((msg = msgb_dequeue(&mm->rr_upqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&mm->mmxx_upqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&mm->mmr_downqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&mm->event_queue))) + msgb_free(msg); + + /* stop timers */ + stop_mm_t3210(mm); + stop_mm_t3211(mm); + stop_mm_t3212(mm); + stop_mm_t3213(mm); + stop_mm_t3220(mm); + stop_mm_t3230(mm); + stop_mm_t3240(mm); + + return 0; +} + +/* + * MM connection management + */ + +static const char *gsm48_mmxx_state_names[] = { + "IDLE", + "CONN_PEND", + "DEDICATED", + "CONN_SUSP", + "REESTPEND" +}; + +uint32_t mm_conn_new_ref = 1; + +/* new MM connection state */ +static void new_conn_state(struct gsm48_mm_conn *conn, int state) +{ + LOGP(DMM, LOGL_INFO, "(ref %d) new state %s -> %s\n", conn->ref, + gsm48_mmxx_state_names[conn->state], + gsm48_mmxx_state_names[state]); + conn->state = state; +} + +/* find MM connection by protocol+ID */ +struct gsm48_mm_conn *mm_conn_by_id(struct gsm48_mmlayer *mm, + uint8_t proto, uint8_t transaction_id) +{ + struct gsm48_mm_conn *conn; + + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->protocol == proto && + conn->transaction_id == transaction_id) + return conn; + } + return NULL; +} + +/* find MM connection by reference */ +struct gsm48_mm_conn *mm_conn_by_ref(struct gsm48_mmlayer *mm, + uint32_t ref) +{ + struct gsm48_mm_conn *conn; + + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->ref == ref) + return conn; + } + return NULL; +} + +/* create MM connection instance */ +static struct gsm48_mm_conn* mm_conn_new(struct gsm48_mmlayer *mm, + int proto, uint8_t transaction_id, uint32_t ref) +{ + struct gsm48_mm_conn *conn = talloc_zero(l23_ctx, struct gsm48_mm_conn); + + if (!conn) + return NULL; + + LOGP(DMM, LOGL_INFO, "New MM Connection (proto 0x%02x trans_id %d " + "ref %d)\n", proto, transaction_id, ref); + + conn->mm = mm; + conn->state = GSM48_MMXX_ST_IDLE; + conn->transaction_id = transaction_id; + conn->protocol = proto; + conn->ref = ref; + + llist_add(&conn->list, &mm->mm_conn); + + return conn; +} + +/* destroy MM connection instance */ +void mm_conn_free(struct gsm48_mm_conn *conn) +{ + LOGP(DMM, LOGL_INFO, "Freeing MM Connection\n"); + + new_conn_state(conn, GSM48_MMXX_ST_IDLE); + + llist_del(&conn->list); + + talloc_free(conn); +} + +/* support function to release pending/all ongoing MM connections */ +static int gsm48_mm_release_mm_conn(struct osmocom_ms *ms, int abort_any, + uint8_t cause, int error) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn, *conn2; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + if (abort_any) + LOGP(DMM, LOGL_INFO, "Release any MM Connection\n"); + else + LOGP(DMM, LOGL_INFO, "Release pending MM Connections\n"); + + /* release MM connection(s) */ + llist_for_each_entry_safe(conn, conn2, &mm->mm_conn, list) { + /* abort any OR the pending connection */ + if (abort_any || conn->state == GSM48_MMXX_ST_CONN_PEND) { + /* send MMxx-REL-IND */ + nmsg = NULL; + switch(conn->protocol) { + case GSM48_PDISC_CC: + nmsg = gsm48_mmxx_msgb_alloc( + error ? GSM48_MMCC_ERR_IND + : GSM48_MMCC_REL_IND, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_NC_SS: + nmsg = gsm48_mmxx_msgb_alloc( + error ? GSM48_MMSS_ERR_IND + : GSM48_MMSS_REL_IND, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_SMS: + nmsg = gsm48_mmxx_msgb_alloc( + error ? GSM48_MMSMS_ERR_IND + : GSM48_MMSMS_REL_IND, conn->ref, + conn->transaction_id); + break; + } + if (!nmsg) { + /* this should not happen */ + mm_conn_free(conn); + continue; /* skip if not of CC type */ + } + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = cause; + gsm48_mmxx_upmsg(ms, nmsg); + + mm_conn_free(conn); + } + } + return 0; +} + +/* + * process handlers (Common procedures) + */ + +/* sending MM STATUS message */ +static int gsm48_mm_tx_mm_status(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t *reject_cause; + + LOGP(DMM, LOGL_INFO, "MM STATUS (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + reject_cause = msgb_put(nmsg, 1); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_STATUS; + *reject_cause = cause; + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.1.2 sending TMSI REALLOCATION COMPLETE message */ +static int gsm48_mm_tx_tmsi_reall_cpl(struct osmocom_ms *ms) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + + LOGP(DMM, LOGL_INFO, "TMSI REALLOCATION COMPLETE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_TMSI_REALL_COMPL; + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.1 TMSI REALLOCATION COMMAND is received */ +static int gsm48_mm_rx_tmsi_realloc_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) gh->data; + uint8_t mi_type, *mi; + uint32_t tmsi; + + if (payload_len < sizeof(struct gsm48_loc_area_id) + 2) { + short_read: + LOGP(DMM, LOGL_NOTICE, "Short read of TMSI REALLOCATION " + "COMMAND message error.\n"); + return -EINVAL; + } + /* LAI */ + gsm48_decode_lai(lai, &subscr->lai_mcc, &subscr->lai_mnc, + &subscr->lai_lac); + /* MI */ + mi = gh->data + sizeof(struct gsm48_loc_area_id); + mi_type = mi[1] & GSM_MI_TYPE_MASK; + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + if (payload_len + sizeof(struct gsm48_loc_area_id) < 6 + || mi[0] < 5) + goto short_read; + memcpy(&tmsi, mi+2, 4); + subscr->tmsi = ntohl(tmsi); + subscr->tmsi_valid = 1; + LOGP(DMM, LOGL_INFO, "TMSI 0x%08x (%u) assigned.\n", + subscr->tmsi, subscr->tmsi); + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + case GSM_MI_TYPE_IMSI: + subscr->tmsi_valid = 0; + LOGP(DMM, LOGL_INFO, "TMSI removed.\n"); + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + default: + LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown MI " + "type %d.\n", mi_type); + gsm48_mm_tx_mm_status(ms, GSM48_REJECT_INCORRECT_MESSAGE); + + return 0; /* don't store in SIM */ + } + +#ifdef TODO + store / remove from sim +#endif + + return 0; +} + +#ifndef TODO +static int gsm48_mm_tx_auth_rsp(struct osmocom_ms *ms, struct msgb *msg); +#endif + +/* 4.3.2.2 AUTHENTICATION REQUEST is received */ +static int gsm48_mm_rx_auth_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct gsm48_auth_req *ar = (struct gsm48_auth_req *) gh->data; + + if (payload_len < sizeof(struct gsm48_auth_req)) { + LOGP(DMM, LOGL_NOTICE, "Short read of AUTHENTICATION REQUEST " + "message error.\n"); + return -EINVAL; + } + + /* SIM is not available */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "AUTHENTICATION REQUEST without SIM\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } + + LOGP(DMM, LOGL_INFO, "AUTHENTICATION REQUEST (seq %d)\n", ar->key_seq); + + /* key_seq and random */ +#ifdef TODO + new key to sim: + (..., ar->key_seq, ar->rand); +#else + /* Fake response */ + struct msgb *nmsg; + struct gsm48_mm_event *nmme; + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + nmme = (struct gsm48_mm_event *)msgb_put(nmsg, sizeof(*nmme)); + *((uint32_t *)nmme->sres) = 0x12345678; + gsm48_mm_tx_auth_rsp(ms, nmsg); + msgb_free(nmsg); +#endif + + /* wait for auth response event from SIM */ + return 0; +} + +/* 4.3.2.2 sending AUTHENTICATION RESPONSE */ +static int gsm48_mm_tx_auth_rsp(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mm_event *mme = (struct gsm48_mm_event *) msg->data; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t *sres; + + LOGP(DMM, LOGL_INFO, "AUTHENTICATION RESPONSE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_AUTH_RESP; + + /* SRES */ + sres = msgb_put(nmsg, 4); + memcpy(sres, mme->sres, 4); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.2.5 AUTHENTICATION REJECT is received */ +static int gsm48_mm_rx_auth_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "AUTHENTICATION REJECT\n"); + + stop_mm_t3212(mm); /* 4.4.2 */ + + /* SIM invalid */ + subscr->sim_valid = 0; + + /* TMSI and LAI invalid */ + subscr->lai_valid = 0; + subscr->tmsi_valid = 0; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA); + +#ifdef TODO + sim: delete tmsi, lai + sim: delete key seq number + sim: set update status +#endif + + /* abort IMSI detach procedure */ + if (mm->state == GSM48_MM_ST_IMSI_DETACH_INIT) { + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* abort RR connection */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *) msgb_put(nmsg, sizeof(*nrrh)); + nrrh->cause = GSM48_RR_CAUSE_NORMAL; + gsm48_rr_downmsg(ms, nmsg); + + /* CS process will trigger: return to MM IDLE / No SIM */ + return 0; + } + + return 0; +} + +/* 4.3.3.1 IDENTITY REQUEST is received */ +static int gsm48_mm_rx_id_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t mi_type; + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of IDENTITY REQUEST message " + "error.\n"); + return -EINVAL; + } + /* id type */ + mi_type = *gh->data; + + /* check if request can be fulfilled */ + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST without SIM\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } + if (mi_type == GSM_MI_TYPE_TMSI && !subscr->tmsi_valid) { + LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST of TMSI, but we have no " + "TMSI\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } + LOGP(DMM, LOGL_INFO, "IDENTITY REQUEST (mi_type %d)\n", mi_type); + + return gsm48_mm_tx_id_rsp(ms, mi_type); +} + +/* send IDENTITY RESPONSE message */ +static int gsm48_mm_tx_id_rsp(struct osmocom_ms *ms, uint8_t mi_type) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t buf[11]; + + LOGP(DMM, LOGL_INFO, "IDENTITY RESPONSE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_ID_RESP; + + /* MI */ + gsm48_encode_mi(buf, nmsg, ms, mi_type); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* 4.3.4.1 sending IMSI DETACH INDICATION message */ +static int gsm48_mm_tx_imsi_detach(struct osmocom_ms *ms, int rr_prim) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_support *sup = &ms->support; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + uint8_t pwr_lev; + uint8_t buf[11]; + struct gsm48_classmark1 cm; + + + LOGP(DMM, LOGL_INFO, "IMSI DETACH INDICATION\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_IMSI_DETACH_IND; + + /* classmark 1 */ + if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885) + pwr_lev = sup->pwr_lev_1800; + else + pwr_lev = sup->pwr_lev_900; + gsm48_encode_classmark1(&cm, sup->rev_lev, sup->es_ind, sup->a5_1, + pwr_lev); + msgb_v_put(nmsg, *((uint8_t *)&cm)); + /* MI */ + if (subscr->tmsi_valid) /* have TMSI ? */ + gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_TMSI); + else + gsm48_encode_mi(buf, nmsg, ms, GSM_MI_TYPE_IMSI); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, rr_prim, RR_EST_CAUSE_OTHER_SDCCH); +} + +/* detach has ended */ +static int gsm48_mm_imsi_detach_end(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + + LOGP(DMM, LOGL_INFO, "IMSI has been detached.\n"); + + /* stop IMSI detach timer (if running) */ + stop_mm_t3220(mm); + + /* update SIM */ +#ifdef TODO + sim: store BA list + sim: what else?: +#endif + + /* SIM invalid */ + subscr->sim_valid = 0; + + /* power off when IMSI is detached */ + if (mm->power_off) { + l23_app_exit(ms); + exit (0); + } + + /* send SIM remove event to gsm322 */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_REMOVE); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* CS process will trigger return to MM IDLE / No SIM */ + return 0; +} + +/* start an IMSI detach in MM IDLE */ +static int gsm48_mm_imsi_detach_start(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + + /* we may silently finish IMSI detach */ + if (!s->att_allowed || !subscr->imsi_attached) { + LOGP(DMM, LOGL_INFO, "IMSI detach not required.\n"); + + return gsm48_mm_imsi_detach_end(ms, msg); + } + LOGP(DMM, LOGL_INFO, "IMSI detach started (MM IDLE)\n"); + + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_IMSI_D, 0); + + /* establish RR and send IMSI detach */ + return gsm48_mm_tx_imsi_detach(ms, GSM48_RR_EST_REQ); +} + +/* IMSI detach has been sent, wait for RR release */ +static int gsm48_mm_imsi_detach_sent(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* start T3220 (4.3.4.1) */ + start_mm_t3220(mm); + + LOGP(DMM, LOGL_INFO, "IMSI detach started (Wait for RR release)\n"); + + new_mm_state(mm, GSM48_MM_ST_IMSI_DETACH_INIT, 0); + + return 0; +} + +/* release MM connection and proceed with IMSI detach */ +static int gsm48_mm_imsi_detach_release(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* release all connections */ + gsm48_mm_release_mm_conn(ms, 1, 16, 0); + + /* wait for release of RR */ + if (!s->att_allowed || !subscr->imsi_attached) { + LOGP(DMM, LOGL_INFO, "IMSI detach not required.\n"); + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + /* power off when IMSI is detached */ + if (mm->power_off) { + l23_app_exit(ms); + exit (0); + } + + return 0; + } + + /* send IMSI detach */ + gsm48_mm_tx_imsi_detach(ms, GSM48_RR_DATA_REQ); + + /* go to sent state */ + return gsm48_mm_imsi_detach_sent(ms, msg); +} + +/* ignore ongoing IMSI detach */ +static int gsm48_mm_imsi_detach_ignore(struct osmocom_ms *ms, struct msgb *msg) +{ + return 0; +} + +/* delay until state change (and then retry) */ +static int gsm48_mm_imsi_detach_delay(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "IMSI detach delayed.\n"); + + /* remember to detach later */ + mm->delay_detach = 1; + + return 0; +} + +/* 4.3.5.2 ABORT is received */ +static int gsm48_mm_rx_abort(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t reject_cause; + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of ABORT message error.\n"); + return -EINVAL; + } + + reject_cause = *gh->data; + + if (llist_empty(&mm->mm_conn)) { + LOGP(DMM, LOGL_NOTICE, "ABORT (cause #%d) while no MM " + "connection is established.\n", reject_cause); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_NOT_COMPATIBLE); + } else { + LOGP(DMM, LOGL_NOTICE, "ABORT (cause #%d) while MM connection " + "is established.\n", reject_cause); + /* stop MM connection timer */ + stop_mm_t3230(mm); + + gsm48_mm_release_mm_conn(ms, 1, 16, 0); + } + + if (reject_cause == GSM48_REJECT_ILLEGAL_ME) { + /* SIM invalid */ + subscr->sim_valid = 0; + + /* TMSI and LAI invalid */ + subscr->lai_valid = 0; + subscr->tmsi_valid = 0; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA); + +#ifdef TODO + sim: delete tmsi, lai + sim: delete key seq number + sim: apply update state +#endif + + /* CS process will trigger: return to MM IDLE / No SIM */ + return 0; + } + + return 0; +} + +/* 4.3.6.2 MM INFORMATION is received */ +static int gsm48_mm_rx_info(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + + if (payload_len < 0) { + LOGP(DMM, LOGL_NOTICE, "Short read of MM INFORMATION message " + "error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_mm_att_tlvdef, gh->data, payload_len, 0, 0); + + /* long name */ + if (TLVP_PRESENT(&tp, GSM48_IE_NAME_LONG)) { + decode_network_name(mm->name_long, sizeof(mm->name_long), + TLVP_VAL(&tp, GSM48_IE_NAME_LONG)-1); + } + /* short name */ + if (TLVP_PRESENT(&tp, GSM48_IE_NAME_SHORT)) { + decode_network_name(mm->name_short, sizeof(mm->name_short), + TLVP_VAL(&tp, GSM48_IE_NAME_SHORT)-1); + } + + return 0; +} + +/* + * process handlers for Location Update + IMSI attach (MM specific procedures) + */ + +/* 4.4.2 received sysinfo change event */ +static int gsm48_mm_sysinfo(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + + /* t3212 not changed in these states */ + if (mm->state == GSM48_MM_ST_MM_IDLE + && (mm->substate == GSM48_MM_SST_NO_CELL_AVAIL + || mm->substate == GSM48_MM_SST_LIMITED_SERVICE + || mm->substate == GSM48_MM_SST_PLMN_SEARCH + || mm->substate == GSM48_MM_SST_PLMN_SEARCH_NORMAL)) + return 0; + + /* new periodic location update timer timeout */ + if (s->t3212 && s->t3212 != mm->t3212_value) { + if (bsc_timer_pending(&mm->t3212)) { + int t; + struct timeval current_time; + + /* get rest time */ + gettimeofday(¤t_time, NULL); + t = mm->t3212.timeout.tv_sec - current_time.tv_sec; + if (t < 0) + t = 0; + LOGP(DMM, LOGL_INFO, "New T3212 while timer is running " + "(value %d rest %d)\n", s->t3212, t); + + /* rest time modulo given value */ + mm->t3212.timeout.tv_sec = current_time.tv_sec + + (t % s->t3212); + } else { + uint32_t rand = random(); + + LOGP(DMM, LOGL_INFO, "New T3212 while timer is not " + "running (value %d)\n", s->t3212); + + /* value between 0 and given value */ + start_mm_t3212(mm, rand % (s->t3212 + 1)); + } + mm->t3212_value = s->t3212; + } + + return 0; +} + +/* 4.4.4.1 (re)start location update + * + * this function is called by + * - normal location update + * - periodic location update + * - imsi attach (normal loc. upd. function) + * - retry timers (T3211 and T3213) + */ +static int gsm48_mm_loc_upd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + int msg_type; + + /* (re)start only if we still require location update */ + if (!mm->lupd_pending) { + LOGP(DMM, LOGL_INFO, "No loc. upd. pending.\n"); + return 0; + } + + /* must camp normally */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not camping normally.\n"); + msg_type = GSM322_EVENT_REG_FAILED; + stop: + LOGP(DSUM, LOGL_INFO, "Location update not possible\n"); + mm->lupd_pending = 0; + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(msg_type); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + return 0; + } + + /* if LAI is forbidden, don't start */ + if (gsm_subscr_is_forbidden_plmn(subscr, cs->sel_mcc, cs->sel_mnc)) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed PLMN.\n"); + msg_type = GSM322_EVENT_ROAMING_NA; + goto stop; + } + if (gsm322_is_forbidden_la(ms, cs->sel_mcc, + cs->sel_mnc, cs->sel_lac)) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed LA.\n"); + msg_type = GSM322_EVENT_ROAMING_NA; + goto stop; + } + + /* 4.4.4.9 if cell is barred, don't start */ + if ((!subscr->acc_barr && s->cell_barr) + || (!subscr->acc_barr && !((subscr->acc_class & 0xfbff) & + (s->class_barr ^ 0xffff)))) { + LOGP(DMM, LOGL_INFO, "Loc. upd. no access.\n"); + msg_type = GSM322_EVENT_ROAMING_NA; + goto stop; + } + + mm->lupd_mcc = cs->sel_mcc; + mm->lupd_mnc = cs->sel_mnc; + mm->lupd_lac = cs->sel_lac; + + LOGP(DSUM, LOGL_INFO, "Perform location update (MCC %s, MNC %s " + "LAC 0x%04x)\n", gsm_print_mcc(mm->lupd_mcc), + gsm_print_mnc(mm->lupd_mnc), mm->lupd_lac); + + return gsm48_mm_tx_loc_upd_req(ms); +} + +/* initiate a normal location update / imsi attach */ +static int gsm48_mm_loc_upd_normal(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &cs->sel_si; + struct msgb *nmsg; + + /* in case we already have a location update going on */ + if (mm->lupd_pending) { + LOGP(DMM, LOGL_INFO, "Loc. upd. already pending.\n"); + + return -EBUSY; + } + + /* no location update, if limited service */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not allowed.\n"); + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* if location update is not required */ + if (subscr->ustate == GSM_SIM_U1_UPDATED + && cs->selected + && cs->sel_mcc == subscr->lai_mcc + && cs->sel_mnc == subscr->lai_mnc + && cs->sel_lac == subscr->lai_lac + && (subscr->imsi_attached + || !s->att_allowed)) { + LOGP(DMM, LOGL_INFO, "Loc. upd. not required.\n"); + subscr->imsi_attached = 1; + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + return 0; + } + + /* 4.4.3 is attachment required? */ + if (subscr->ustate == GSM_SIM_U1_UPDATED + && cs->selected + && cs->sel_mcc == subscr->lai_mcc + && cs->sel_mnc == subscr->lai_mnc + && cs->sel_lac == subscr->lai_lac + && !subscr->imsi_attached + && s->att_allowed) { + /* do location update for IMSI attach */ + LOGP(DMM, LOGL_INFO, "Do Loc. upd. for IMSI attach.\n"); + mm->lupd_type = 2; + } else { + /* do normal location update */ + LOGP(DMM, LOGL_INFO, "Do normal Loc. upd.\n"); + mm->lupd_type = 0; + } + + /* start location update */ + mm->lupd_attempt = 0; + mm->lupd_pending = 1; + mm->lupd_ra_failure = 0; + + return gsm48_mm_loc_upd(ms, msg); +} + +/* initiate a periodic location update */ +static int gsm48_mm_loc_upd_periodic(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* in case we already have a location update going on */ + if (mm->lupd_pending) { + LOGP(DMM, LOGL_INFO, "Loc. upd. already pending.\n"); + return -EBUSY; + } + + /* start periodic location update */ + mm->lupd_type = 1; + mm->lupd_pending = 1; + mm->lupd_ra_failure = 0; + + return gsm48_mm_loc_upd(ms, msg); +} + +/* ignore location update */ +static int gsm48_mm_loc_upd_ignore(struct osmocom_ms *ms, struct msgb *msg) +{ + return 0; +} + +/* 9.2.15 send LOCATION UPDATING REQUEST message */ +static int gsm48_mm_tx_loc_upd_req(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_support *sup = &ms->support; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + struct gsm48_loc_upd_req *nlu; /* NOTE: mi_len is part of struct */ + uint8_t pwr_lev; + uint8_t buf[11]; + + LOGP(DMM, LOGL_INFO, "LOCATION UPDATING REQUEST\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + nlu = (struct gsm48_loc_upd_req *)msgb_put(nmsg, sizeof(*nlu)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST; + + /* location updating type */ + nlu->type = mm->lupd_type; + /* cipering key */ + nlu->key_seq = subscr->key_seq; + /* LAI (use last SIM stored LAI) */ + gsm48_generate_lai(&nlu->lai, + subscr->lai_mcc, subscr->lai_mnc, subscr->lai_lac); + /* classmark 1 */ + if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885) + pwr_lev = sup->pwr_lev_1800; + else + pwr_lev = sup->pwr_lev_900; + gsm48_encode_classmark1(&nlu->classmark1, sup->rev_lev, sup->es_ind, + sup->a5_1, pwr_lev); + /* MI */ + if (subscr->tmsi_valid) /* have TMSI ? */ + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI); + else + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI); + msgb_put(nmsg, buf[1]); /* length is part of nlu */ + memcpy(&nlu->mi_len, buf + 1, 1 + buf[1]); + + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_LUPD, 0); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_EST_REQ, RR_EST_CAUSE_LOC_UPD); +} + +/* 4.4.4.1 RR is esablised during location update */ +static int gsm48_mm_est_loc_upd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* start location update timer */ + start_mm_t3210(mm); + + new_mm_state(mm, GSM48_MM_ST_LOC_UPD_INIT, 0); + + return 0; +} + +/* 4.4.4.6 LOCATION UPDATING ACCEPT is received */ +static int gsm48_mm_rx_loc_upd_acc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_loc_area_id *lai = (struct gsm48_loc_area_id *) gh->data; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct msgb *nmsg; + + if (payload_len < sizeof(struct gsm48_loc_area_id)) { + short_read: + LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING ACCEPT " + "message error.\n"); + return -EINVAL; + } + tlv_parse(&tp, &gsm48_mm_att_tlvdef, + gh->data + sizeof(struct gsm48_loc_area_id), + payload_len - sizeof(struct gsm48_loc_area_id), 0, 0); + + /* update has finished */ + mm->lupd_pending = 0; + + /* RA was successfull */ + mm->lupd_ra_failure = 0; + + /* stop periodic location updating timer */ + stop_mm_t3212(mm); /* 4.4.2 */ + + /* LAI */ + subscr->lai_valid = 1; + gsm48_decode_lai(lai, &subscr->lai_mcc, &subscr->lai_mnc, + &subscr->lai_lac); + + /* stop location update timer */ + stop_mm_t3210(mm); + + /* reset attempt counter */ + mm->lupd_attempt = 0; + + /* mark SIM as attached */ + subscr->imsi_attached = 1; + + /* set the status in the sim to updated */ + new_sim_ustate(subscr, GSM_SIM_U1_UPDATED); +#ifdef TODO + sim: apply update state +#endif + + /* set last registered PLMN */ + subscr->plmn_valid = 1; + subscr->plmn_mcc = subscr->lai_mcc; + subscr->plmn_mnc = subscr->lai_mnc; +#ifdef TODO + sim: store plmn +#endif + + LOGP(DSUM, LOGL_INFO, "Location update accepted\n"); + LOGP(DMM, LOGL_INFO, "LOCATION UPDATING ACCEPT (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(subscr->lai_mcc), + gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac); + + /* remove LA from forbidden list */ + gsm322_del_forbidden_la(ms, subscr->lai_mcc, subscr->lai_mnc, + subscr->lai_lac); + + /* MI */ + if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) { + const uint8_t *mi; + uint8_t mi_type; + uint32_t tmsi; + + mi = TLVP_VAL(&tp, GSM48_IE_MOBILE_ID)-1; + if (mi[0] < 1) + goto short_read; + mi_type = mi[1] & GSM_MI_TYPE_MASK; + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + if (payload_len + sizeof(struct gsm48_loc_area_id) < 6 + || mi[0] < 5) + goto short_read; + memcpy(&tmsi, mi+2, 4); + subscr->tmsi = ntohl(tmsi); + subscr->tmsi_valid = 1; + LOGP(DMM, LOGL_INFO, "got TMSI 0x%08x (%u)\n", + subscr->tmsi, subscr->tmsi); +#ifdef TODO + sim: store tmsi +#endif + break; + case GSM_MI_TYPE_IMSI: + LOGP(DMM, LOGL_INFO, "TMSI removed\n"); + subscr->tmsi_valid = 0; +#ifdef TODO + sim: delete tmsi +#endif + /* send TMSI REALLOCATION COMPLETE */ + gsm48_mm_tx_tmsi_reall_cpl(ms); + break; + default: + LOGP(DMM, LOGL_NOTICE, "TMSI reallocation with unknown " + "MI type %d.\n", mi_type); + } + } + + /* send message to PLMN search process */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_SUCCESS); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* follow on proceed */ + if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) + LOGP(DMM, LOGL_NOTICE, "follow-on proceed not supported.\n"); + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + return 0; +} + +/* 4.4.4.7 LOCATION UPDATING REJECT is received */ +static int gsm48_mm_rx_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of LOCATION UPDATING REJECT " + "message error.\n"); + return -EINVAL; + } + + /* RA was successfull */ + mm->lupd_ra_failure = 0; + + /* stop periodic location updating timer */ + stop_mm_t3212(mm); /* 4.4.2 */ + + /* stop location update timer */ + stop_mm_t3210(mm); + + /* store until RR is released */ + mm->lupd_rej_cause = *gh->data; + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_LOC_UPD_REJ, 0); + + return 0; +} + +/* 4.4.4.7 RR is released after location update reject */ +static int gsm48_mm_rel_loc_upd_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *nmsg; + struct gsm322_msg *ngm; + + LOGP(DMM, LOGL_INFO, "Loc. upd. rejected (cause %d)\n", + mm->lupd_rej_cause); + + /* new status */ + switch (mm->lupd_rej_cause) { + case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: + case GSM48_REJECT_ILLEGAL_MS: + case GSM48_REJECT_ILLEGAL_ME: + /* reset attempt counter */ + mm->lupd_attempt = 0; + + /* SIM invalid */ + subscr->sim_valid = 0; + + // fall through + case GSM48_REJECT_PLMN_NOT_ALLOWED: + case GSM48_REJECT_LOC_NOT_ALLOWED: + case GSM48_REJECT_ROAMING_NOT_ALLOWED: + /* TMSI and LAI invalid */ + subscr->lai_valid = 0; + subscr->tmsi_valid = 0; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U3_ROAMING_NA); +#ifdef TODO + sim: delete tmsi, lai + sim: delete key seq number + sim: apply update state +#endif + /* update has finished */ + mm->lupd_pending = 0; + } + + /* send event to PLMN search process */ + switch(mm->lupd_rej_cause) { + case GSM48_REJECT_ROAMING_NOT_ALLOWED: + nmsg = gsm322_msgb_alloc(GSM322_EVENT_ROAMING_NA); + break; + case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: + case GSM48_REJECT_ILLEGAL_MS: + case GSM48_REJECT_ILLEGAL_ME: + nmsg = gsm322_msgb_alloc(GSM322_EVENT_INVALID_SIM); + break; + default: + nmsg = gsm322_msgb_alloc(GSM322_EVENT_REG_FAILED); + } + if (!nmsg) + return -ENOMEM; + ngm = (struct gsm322_msg *)nmsg->data; + ngm->reject = mm->lupd_rej_cause; + gsm322_plmn_sendmsg(ms, nmsg); + + /* forbidden list */ + switch (mm->lupd_rej_cause) { + case GSM48_REJECT_IMSI_UNKNOWN_IN_HLR: + LOGP(DSUM, LOGL_INFO, "Location update failed (IMSI unknown " + "in HLR)\n"); + break; + case GSM48_REJECT_ILLEGAL_MS: + LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal MS)\n"); + break; + case GSM48_REJECT_ILLEGAL_ME: + LOGP(DSUM, LOGL_INFO, "Location update failed (Illegal ME)\n"); + break; + case GSM48_REJECT_PLMN_NOT_ALLOWED: + gsm_subscr_add_forbidden_plmn(subscr, mm->lupd_mcc, + mm->lupd_mnc, mm->lupd_rej_cause); + LOGP(DSUM, LOGL_INFO, "Location update failed (PLMN not " + "allowed)\n"); + break; + case GSM48_REJECT_LOC_NOT_ALLOWED: + case GSM48_REJECT_ROAMING_NOT_ALLOWED: + gsm322_add_forbidden_la(ms, mm->lupd_mcc, mm->lupd_mnc, + mm->lupd_lac, mm->lupd_rej_cause); + LOGP(DSUM, LOGL_INFO, "Location update failed (LAI not " + "allowed)\n"); + break; + default: + /* 4.4.4.9 continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); + } + + /* CS proc triggers: return to IDLE, case 13 is also handled there */ + return 0; +} + +/* 4.2.2 delay a location update */ +static int gsm48_mm_loc_upd_delay_per(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "Schedule a pending periodic loc. upd.\n"); + mm->lupd_periodic = 1; + + return 0; +} + +/* delay a location update retry */ +static int gsm48_mm_loc_upd_delay_retry(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + LOGP(DMM, LOGL_INFO, "Schedule a pending periodic loc. upd.\n"); + mm->lupd_retry = 1; + + return 0; +} + +/* process failues as described in the lower part of 4.4.4.9 */ +static int gsm48_mm_loc_upd_failed(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + + LOGP(DSUM, LOGL_INFO, "Location update failed\n"); + + /* stop location update timer, if running */ + stop_mm_t3210(mm); + + if (subscr->ustate == GSM_SIM_U1_UPDATED + && mm->lupd_mcc == subscr->lai_mcc + && mm->lupd_mnc == subscr->lai_mnc + && mm->lupd_lac == subscr->lai_lac) { + if (mm->lupd_attempt < 4) { + LOGP(DSUM, LOGL_INFO, "Try location update later\n"); + LOGP(DMM, LOGL_INFO, "Loc. upd. failed, retry #%d\n", + mm->lupd_attempt); + + /* start update retry timer */ + start_mm_t3211(mm); + + /* CS process will trigger: return to MM IDLE */ + return 0; + } else + LOGP(DMM, LOGL_INFO, "Loc. upd. failed too often.\n"); + } + + /* TMSI and LAI invalid */ + subscr->lai_valid = 0; + subscr->tmsi_valid = 0; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U2_NOT_UPDATED); + +#ifdef TODO + sim: delete tmsi, lai + sim: delete key seq number + sim: set update status +#endif + + /* start update retry timer (RR connection is released) */ + if (mm->lupd_attempt < 4) { + mm->start_t3211 = 1; + LOGP(DSUM, LOGL_INFO, "Try location update later\n"); + } + + /* CS process will trigger: return to MM IDLE */ + return 0; +} + +/* abort a location update due to radio failure or release */ +static int gsm48_mm_rel_loc_upd_abort(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + + if (rrh->msg_type == GSM48_RR_REL_IND) { + LOGP(DMM, LOGL_INFO, "RR link released after loc. upd.\n"); + + /* continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); + } + + LOGP(DMM, LOGL_INFO, "Loc. upd. aborted by radio (cause #%d)\n", + rrh->cause); + + /* random access failure, but not two successive failures */ + if (rrh->cause == RR_REL_CAUSE_RA_FAILURE && !mm->lupd_ra_failure) { + mm->lupd_ra_failure = 1; + + /* start RA failure timer */ + start_mm_t3213(mm); + + return 0; + } + + /* RA was successfull or sent twice */ + mm->lupd_ra_failure = 0; + + /* continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); +} + +/* location update has timed out */ +static int gsm48_mm_loc_upd_timeout(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* abort RR connection */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *) msgb_put(nmsg, sizeof(*nrrh)); + nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER; + gsm48_rr_downmsg(ms, nmsg); + + /* continue with failure handling */ + return gsm48_mm_loc_upd_failed(ms, NULL); +} + +/* + * process handlers for MM connections + */ + +/* cm reestablish request message from upper layer */ +static int gsm48_mm_tx_cm_serv_req(struct osmocom_ms *ms, int rr_prim, + uint8_t cause, uint8_t cm_serv) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_settings *settings = &ms->settings; + struct msgb *nmsg; + struct gsm48_hdr *ngh; + struct gsm48_service_request *nsr; /* NOTE: includes MI length */ + uint8_t *cm2lv; + uint8_t buf[11]; + + LOGP(DMM, LOGL_INFO, "CM SERVICE REQUEST (cause %d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + nsr = (struct gsm48_service_request *)msgb_put(nmsg, sizeof(*nsr)); + cm2lv = (uint8_t *)&nsr->classmark; + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_CM_SERV_REQ; + + /* type and key */ + nsr->cm_service_type = cm_serv; + nsr->cipher_key_seq = subscr->key_seq; + /* classmark 2 */ + cm2lv[0] = sizeof(struct gsm48_classmark2); + gsm48_rr_enc_cm2(ms, (struct gsm48_classmark2 *)(cm2lv + 1)); + /* MI */ + if (!subscr->sim_valid) { /* have no SIM ? */ + if (settings->emergency_imsi[0]) + gsm48_generate_mid_from_imsi(buf, + settings->emergency_imsi); + else + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMEI); + } else if (subscr->tmsi_valid) /* have TMSI ? */ + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_TMSI); + else + gsm48_encode_mi(buf, NULL, ms, GSM_MI_TYPE_IMSI); + msgb_put(nmsg, buf[1]); /* length is part of nsr */ + memcpy(&nsr->mi_len, buf + 1, 1 + buf[1]); + /* prio is optional for eMLPP */ + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, rr_prim, cause); +} + +/* cm service abort message from upper layer */ +static int gsm48_mm_tx_cm_service_abort(struct osmocom_ms *ms) +{ + struct msgb *nmsg; + struct gsm48_hdr *ngh; + + LOGP(DMM, LOGL_INFO, "CM SERVICE ABORT\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + ngh = (struct gsm48_hdr *)msgb_put(nmsg, sizeof(*ngh)); + + ngh->proto_discr = GSM48_PDISC_MM; + ngh->msg_type = GSM48_MT_MM_CM_SERV_ABORT; + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, nmsg, GSM48_RR_DATA_REQ, 0); +} + +/* cm service acknowledge is received from lower layer */ +static int gsm48_mm_rx_cm_service_acc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return gsm48_mm_conn_go_dedic(ms); +} + +/* 9.2.6 CM SERVICE REJECT message received */ +static int gsm48_mm_rx_cm_service_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + uint8_t abort_any = 0; + uint8_t reject_cause; + + if (payload_len < 1) { + LOGP(DMM, LOGL_NOTICE, "Short read of cm service reject " + "message error.\n"); + return -EINVAL; + } + + /* reject cause */ + reject_cause = *gh->data; + + LOGP(DMM, LOGL_INFO, "CM SERVICE REJECT (cause %d)\n", reject_cause); + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* selection action on cause value */ + switch (reject_cause) { + case GSM48_REJECT_IMSI_UNKNOWN_IN_VLR: + case GSM48_REJECT_ILLEGAL_ME: + abort_any = 1; + + /* TMSI and LAI invalid */ + subscr->lai_valid = 0; + subscr->tmsi_valid = 0; + + /* key is invalid */ + subscr->key_seq = 7; + + /* update status */ + new_sim_ustate(subscr, GSM_SIM_U2_NOT_UPDATED); + +#ifdef TODO + sim: delete tmsi, lai + sim: delete key seq number + sim: set update status +#endif + + /* change to WAIT_NETWORK_CMD state impied by abort_any == 1 */ + + if (reject_cause == GSM48_REJECT_ILLEGAL_ME) + subscr->sim_valid = 0; + + break; + default: + /* state implied by the number of remaining connections */ + ; + } + + /* release MM connection(s) */ + gsm48_mm_release_mm_conn(ms, abort_any, 16, 0); + + /* state depends on the existance of remaining MM connections */ + if (llist_empty(&mm->mm_conn)) + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + else + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return 0; +} + +/* initiate an MM connection 4.5.1.1 + * + * this function is called when: + * - no RR connection exists + * - an RR connection exists, but this is the first MM connection + * - an RR connection exists, and there are already MM connection(s) + */ +static int gsm48_mm_init_mm(struct osmocom_ms *ms, struct msgb *msg, + int rr_prim) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + int emergency = 0; + uint8_t cause = 0, cm_serv = 0, proto = 0; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + struct gsm48_mm_conn *conn, *conn_found = NULL; + + /* reset loc. upd. counter on CM service request */ + mm->lupd_attempt = 0; + + /* find if there is already a pending connection */ + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->state == GSM48_MMXX_ST_CONN_PEND) { + conn_found = conn; + break; + } + } + + /* if pending connection */ + if (conn_found) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, but already have " + "pending MM Connection.\n"); + cause = 17; + reject: + nmsg = NULL; + switch(msg_type) { + case GSM48_MMCC_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, + mmh->ref, mmh->transaction_id); + break; + case GSM48_MMSS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, + mmh->ref, mmh->transaction_id); + break; + case GSM48_MMSMS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, + mmh->ref, mmh->transaction_id); + break; + } + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = cause; + gsm48_mmxx_upmsg(ms, nmsg); + + return -EBUSY; + } + /* in case of an emergency setup */ + if (msg_type == GSM48_MMCC_EST_REQ && mmh->emergency) + emergency = 1; + + /* if sim is not updated */ + if (!emergency && subscr->ustate != GSM_SIM_U1_UPDATED) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, but SIM not " + "updated.\n"); + cause = 21; + goto reject; + } + + /* current MM idle state + * (implies MM IDLE state, otherwise this function is not called) + */ + switch (mm->substate) { + case GSM48_MM_SST_NORMAL_SERVICE: + case GSM48_MM_SST_PLMN_SEARCH_NORMAL: + LOGP(DMM, LOGL_INFO, "Init MM Connection.\n"); + break; /* allow when normal */ + case GSM48_MM_SST_LOC_UPD_NEEDED: + case GSM48_MM_SST_ATTEMPT_UPDATE: + /* store mm request if attempting to update */ + if (!emergency) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, but " + "attempting to update.\n"); + cause = 21; + goto reject; + /* Some day implement delay and start loc upd. */ + } + break; + default: + /* reject if not emergency */ + if (!emergency) { + LOGP(DMM, LOGL_INFO, "Init MM Connection, not in " + "normal state.\n"); + cause = 21; + goto reject; + } + break; + } + + /* set cause, service, proto */ + switch(msg_type) { + case GSM48_MMCC_EST_REQ: + if (emergency) { + cause = RR_EST_CAUSE_EMERGENCY; + cm_serv = GSM48_CMSERV_EMERGENCY; + } else { + cause = RR_EST_CAUSE_ORIG_TCHF; + cm_serv = GSM48_CMSERV_MO_CALL_PACKET; + } + proto = GSM48_PDISC_CC; + break; + case GSM48_MMSS_EST_REQ: + cause = RR_EST_CAUSE_OTHER_SDCCH; + cm_serv = GSM48_CMSERV_SUP_SERV; + proto = GSM48_PDISC_NC_SS; + break; + case GSM48_MMSMS_EST_REQ: + cause = RR_EST_CAUSE_OTHER_SDCCH; + cm_serv = GSM48_CMSERV_SMS; + proto = GSM48_PDISC_SMS; + break; + } + + /* create MM connection instance */ + conn = mm_conn_new(mm, proto, mmh->transaction_id, mmh->ref); + if (!conn) + return -ENOMEM; + + new_conn_state(conn, GSM48_MMXX_ST_CONN_PEND); + + /* send CM SERVICE REQUEST */ + if (rr_prim) + return gsm48_mm_tx_cm_serv_req(ms, rr_prim, cause, cm_serv); + else + return 0; +} + +/* 4.5.1.1 a) MM connection request triggers RR connection */ +static int gsm48_mm_init_mm_no_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* start MM connection by requesting RR connection */ + rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_EST_REQ); + if (rc) + return rc; + + new_mm_state(mm, GSM48_MM_ST_WAIT_RR_CONN_MM_CON, 0); + + return 0; +} + +/* 4.5.1.1 a) RR is esablised during mm connection, wait for CM accepted */ +static int gsm48_mm_est_mm_con(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* 4.5.1.7 if there is no more MM connection */ + if (llist_empty(&mm->mm_conn)) { + LOGP(DMM, LOGL_INFO, "MM Connection, are already gone.\n"); + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + /* send abort */ + return gsm48_mm_tx_cm_service_abort(ms); + } + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_OUT_MM_CONN, 0); + + return 0; +} + +/* 4.5.1.1 b) MM connection request on existing RR connection */ +static int gsm48_mm_init_mm_first(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* start MM connection by sending data */ + rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_DATA_REQ); + if (rc) + return rc; + + /* stop "RR connection release not allowed" timer */ + stop_mm_t3241(mm); + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_OUT_MM_CONN, 0); + + return 0; +} + +/* 4.5.1.1 b) another MM connection request on existing RR connection */ +static int gsm48_mm_init_mm_more(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* start MM connection by sending data */ + rc = gsm48_mm_init_mm(ms, msg, GSM48_RR_DATA_REQ); + if (rc) + return rc; + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_ADD_OUT_MM_CON, 0); + + return 0; +} + +/* 4.5.1.1 b) delay on WAIT FOR NETWORK COMMAND state */ +static int gsm48_mm_init_mm_wait(struct osmocom_ms *ms, struct msgb *msg) +{ + /* reject */ + gsm48_mm_init_mm_reject(ms, msg); +#if 0 + this requires handling when leaving this state... + + struct gsm48_mmlayer *mm = &ms->mmlayer; + int rc; + + /* just create the MM connection in pending state */ + rc = gsm48_mm_init_mm(ms, msg, 0); + if (rc) + return rc; + + /* start MM connection timer */ + start_mm_t3230(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_ADD_OUT_MM_CON, 0); +#endif + + return 0; +} + +/* initiate an mm connection other cases */ +static int gsm48_mm_init_mm_reject(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + /* reject */ + nmsg = NULL; + switch(msg_type) { + case GSM48_MMCC_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_REL_IND, mmh->ref, + mmh->transaction_id); + break; + case GSM48_MMSS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_REL_IND, mmh->ref, + mmh->transaction_id); + break; + case GSM48_MMSMS_EST_REQ: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_REL_IND, mmh->ref, + mmh->transaction_id); + break; + } + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = 17; + gsm48_mmxx_upmsg(ms, nmsg); + + return 0; +} + +/* accepting pending connection, got dedicated mode + * + * this function is called: + * - when ciphering command is received + * - when cm service is accepted + */ +static int gsm48_mm_conn_go_dedic(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn, *conn_found = NULL; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + /* the first and only pending connection is the recent requested */ + llist_for_each_entry(conn, &mm->mm_conn, list) { + if (conn->state == GSM48_MMXX_ST_CONN_PEND) { + conn_found = conn; + break; + } + } + + /* if no pending connection (anymore) */ + if (!conn_found) { + LOGP(DMM, LOGL_INFO, "No pending MM Connection.\n"); + + return 0; + } + + new_conn_state(conn, GSM48_MMXX_ST_DEDICATED); + + /* send establishment confirm */ + nmsg = NULL; + switch(conn_found->protocol) { + case GSM48_PDISC_CC: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_EST_CNF, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_NC_SS: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSS_EST_CNF, conn->ref, + conn->transaction_id); + break; + case GSM48_PDISC_SMS: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMSMS_EST_CNF, conn->ref, + conn->transaction_id); + break; + } + if (!nmsg) + return -ENOMEM; + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = 17; + gsm48_mmxx_upmsg(ms, nmsg); + + return 0; +} + +/* a RR-SYNC-IND is received during MM connection establishment */ +static int gsm48_mm_sync_ind_wait(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + return gsm48_mm_conn_go_dedic(ms); +} + +/* a RR-SYNC-IND is received during MM connection active */ +static int gsm48_mm_sync_ind_active(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mm_conn *conn; + struct msgb *nmsg; + struct gsm48_mmxx_hdr *nmmh; + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* broadcast all MMCC connection(s) */ + llist_for_each_entry(conn, &mm->mm_conn, list) { + /* send MMCC-SYNC-IND */ + nmsg = NULL; + switch(conn->protocol) { + case GSM48_PDISC_CC: + nmsg = gsm48_mmxx_msgb_alloc(GSM48_MMCC_SYNC_IND, + conn->ref, conn->transaction_id); + break; + } + if (!nmsg) + continue; /* skip if not of CC type */ + nmmh = (struct gsm48_mmxx_hdr *)nmsg->data; + nmmh->cause = 17; + /* copy L3 message */ + nmsg->l3h = msgb_put(nmsg, msgb_l3len(msg)); + memcpy(nmsg->l3h, msg->l3h, msgb_l3len(msg)); + gsm48_mmxx_upmsg(ms, nmsg); + } + + return 0; +} + +/* 4.5.1.2 RR abort/release is received during MM connection establishment */ +static int gsm48_mm_abort_mm_con(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + int cause; + + /* this conversion is not of any standard */ + switch(rrh->cause) { + case RR_REL_CAUSE_NOT_AUTHORIZED: + case RR_REL_CAUSE_EMERGENCY_ONLY: + case RR_REL_CAUSE_TRY_LATER: + cause = 21; + break; + case RR_REL_CAUSE_NORMAL: + cause = 16; + break; + default: + cause = 47; + } + + /* stop MM connection timer */ + stop_mm_t3230(mm); + + /* release all connections */ + gsm48_mm_release_mm_conn(ms, 1, cause, 1); + + /* no RR connection, so we return to MM IDLE */ + if (mm->state == GSM48_MM_ST_WAIT_RR_CONN_MM_CON) + return gsm48_mm_return_idle(ms, NULL); + + /* CS process will trigger: return to MM IDLE */ + return 0; +} + +/* 4.5.1.2 timeout is received during MM connection establishment */ +static int gsm48_mm_timeout_mm_con(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + /* release pending connection */ + gsm48_mm_release_mm_conn(ms, 0, 102, 0); + + /* state depends on the existance of remaining MM connections */ + if (llist_empty(&mm->mm_conn)) { + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + } else + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return 0; +} + +/* respond to paging */ +static int gsm48_mm_est(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + return 0; +} + +/* send CM data */ +static int gsm48_mm_data(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + int msg_type = mmh->msg_type; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (!conn) { + LOGP(DMM, LOGL_INFO, "MMXX_DATA_REQ with unknown (already " + "released) ref=%d, sending MMXX_REL_IND\n", mmh->ref); + switch(msg_type & GSM48_MMXX_MASK) { + case GSM48_MMCC_CLASS: + mmh->msg_type = GSM48_MMCC_REL_IND; + break; + case GSM48_MMSS_CLASS: + mmh->msg_type = GSM48_MMSS_REL_IND; + break; + case GSM48_MMSMS_CLASS: + mmh->msg_type = GSM48_MMSMS_REL_IND; + break; + } + mmh->cause = 31; + + /* mirror message with REL_IND + cause */ + return gsm48_mmxx_upmsg(ms, msg); + } + + /* pull MM header */ + msgb_pull(msg, sizeof(struct gsm48_mmxx_hdr)); + + /* push RR header and send down */ + return gsm48_mm_to_rr(ms, msg, GSM48_RR_DATA_REQ, 0); +} + +/* release of MM connection (active state) */ +static int gsm48_mm_release_active(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + /* state depends on the existance of remaining MM connections */ + if (llist_empty(&mm->mm_conn)) { + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + } else + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + + return 0; +} + +/* release of MM connection (wait for additional state) */ +static int gsm48_mm_release_wait_add(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + return 0; +} + +/* release of MM connection (wait for active state) */ +static int gsm48_mm_release_wait_active(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + /* 4.5.1.7 if there is no MM connection during wait for active state */ + if (llist_empty(&mm->mm_conn)) { + LOGP(DMM, LOGL_INFO, "No MM Connection during 'wait for " + "active' state.\n"); + + /* start RR release timer */ + start_mm_t3240(mm); + + new_mm_state(mm, GSM48_MM_ST_WAIT_NETWORK_CMD, 0); + + /* send abort */ + return gsm48_mm_tx_cm_service_abort(ms); + } + + return 0; +} + +/* release of MM connection (wait for RR state) */ +static int gsm48_mm_release_wait_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + struct gsm48_mm_conn *conn; + + /* get connection, if not exist (anymore), release */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + mm_conn_free(conn); + + /* later, if RR connection is established, the CM SERIVE ABORT + * message will be sent + */ + return 0; +} + +/* abort RR connection (due to T3240) */ +static int gsm48_mm_abort_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + /* send abort to RR */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_ABORT_REQ); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *) msgb_put(nmsg, sizeof(*nrrh)); + nrrh->cause = GSM48_RR_CAUSE_ABNORMAL_TIMER; + gsm48_rr_downmsg(ms, nmsg); + + /* CS process will trigger: return to MM IDLE / No SIM */ + return 0; +} + +/* + * other processes + */ + +/* RR is released in other states */ +static int gsm48_mm_rel_other(struct osmocom_ms *ms, struct msgb *msg) +{ + /* CS process will trigger: return to MM IDLE */ + return 0; +} + +/* + * state machines + */ + +/* state trasitions for MMxx-SAP messages from upper layers */ +static struct downstate { + uint32_t states; + uint32_t substates; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} downstatelist[] = { + /* 4.2.2.1 Normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.2 Attempt to update / Loc. Upd. needed */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, /* emergency only */ + + /* 4.2.2.3 Limited service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.4 No IMSI */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_IMSI), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.5 PLMN search, normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.2.2.6 PLMN search */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_no_rr}, + + /* 4.5.1.1 MM Connection (EST) */ + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_RR_CONN_RELEASE_NA), ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_first}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_more}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_more}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_more}, + + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_wait}, + + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_wait}, + + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_wait}, + + {ALL_STATES, ALL_STATES, + GSM48_MMCC_EST_REQ, gsm48_mm_init_mm_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMSS_EST_REQ, gsm48_mm_init_mm_reject}, + + {ALL_STATES, ALL_STATES, + GSM48_MMSMS_EST_REQ, gsm48_mm_init_mm_reject}, + + /* 4.5.2.1 MM Connection (DATA) */ + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMCC_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSS_DATA_REQ, gsm48_mm_data}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSMS_DATA_REQ, gsm48_mm_data}, + + /* 4.5.2.1 MM Connection (REL) */ + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_active}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_add}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_active}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_active}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_active}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMCC_REL_REQ, gsm48_mm_release_wait_rr}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMSS_REL_REQ, gsm48_mm_release_wait_rr}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), ALL_STATES, + GSM48_MMSMS_REL_REQ, gsm48_mm_release_wait_rr}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + +int gsm48_mmxx_downmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_mmxx_hdr *mmh = (struct gsm48_mmxx_hdr *)msg->data; + int msg_type = mmh->msg_type; + struct gsm48_mm_conn *conn; + int i, rc; + + /* keep up to date with the transaction ID */ + conn = mm_conn_by_ref(mm, mmh->ref); + if (conn) + conn->transaction_id = mmh->transaction_id; + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state %s\n", + ms->name, get_mmxx_name(msg_type), + gsm48_mm_state_names[mm->state]); + if (mm->state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "-> substate %s\n", + gsm48_mm_substate_names[mm->substate]); + LOGP(DMM, LOGL_INFO, "-> callref %d, transaction_id %d\n", + mmh->ref, mmh->transaction_id); + + /* Find function for current state and message */ + for (i = 0; i < DOWNSLLEN; i++) + if ((msg_type == downstatelist[i].type) + && ((1 << mm->state) & downstatelist[i].states) + && ((1 << mm->substate) & downstatelist[i].substates)) + break; + if (i == DOWNSLLEN) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = downstatelist[i].rout(ms, msg); + + if (downstatelist[i].rout != gsm48_mm_data) + msgb_free(msg); + + return rc; +} + +/* state trasitions for radio ressource messages (lower layer) */ +static struct rrdatastate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} rrdatastatelist[] = { + /* paging */ + {SBIT(GSM48_MM_ST_MM_IDLE), + GSM48_RR_EST_IND, gsm48_mm_est}, + + /* imsi detach */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 */ + GSM48_RR_EST_CNF, gsm48_mm_imsi_detach_sent}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 (unsuc.) */ + GSM48_RR_REL_IND, gsm48_mm_imsi_detach_end}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D), /* 4.3.4.4 (lost) */ + GSM48_RR_ABORT_IND, gsm48_mm_imsi_detach_end}, + + /* location update */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.1 */ + GSM48_RR_EST_CNF, gsm48_mm_est_loc_upd}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT) | + SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.9 */ + GSM48_RR_REL_IND, gsm48_mm_rel_loc_upd_abort}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT) | + SBIT(GSM48_MM_ST_WAIT_RR_CONN_LUPD), /* 4.4.4.9 */ + GSM48_RR_ABORT_IND, gsm48_mm_rel_loc_upd_abort}, + + {SBIT(GSM48_MM_ST_LOC_UPD_REJ), /* 4.4.4.7 */ + GSM48_RR_REL_IND, gsm48_mm_rel_loc_upd_rej}, + + {SBIT(GSM48_MM_ST_LOC_UPD_REJ), /* 4.4.4.7 */ + GSM48_RR_ABORT_IND, gsm48_mm_rel_loc_upd_rej}, + + /* MM connection (EST) */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON), /* 4.5.1.1 */ + GSM48_RR_EST_CNF, gsm48_mm_est_mm_con}, + + /* MM connection (DATA) */ + {ALL_STATES, + GSM48_RR_DATA_IND, gsm48_mm_data_ind}, + + /* MM connection (SYNC) */ + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.1 */ + GSM48_RR_SYNC_IND, gsm48_mm_sync_ind_wait}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), + GSM48_RR_SYNC_IND, gsm48_mm_sync_ind_active}, + + /* MM connection (REL/ABORT) */ + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON) | + SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.2 */ + GSM48_RR_REL_IND, gsm48_mm_abort_mm_con}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_MM_CON) | + SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* 4.5.1.2 */ + GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con}, + + /* MM connection (REL/ABORT with re-establishment possibility) */ + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE), /* not supported */ + GSM48_RR_REL_IND, gsm48_mm_abort_mm_con}, + + {SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON), /* not supported */ + GSM48_RR_ABORT_IND, gsm48_mm_abort_mm_con}, + + /* other */ + {ALL_STATES, + GSM48_RR_REL_IND, gsm48_mm_rel_other}, + + {ALL_STATES, + GSM48_RR_ABORT_IND, gsm48_mm_rel_other}, +}; + +#define RRDATASLLEN \ + (sizeof(rrdatastatelist) / sizeof(struct rrdatastate)) + +static int gsm48_rcv_rr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *)msg->data; + int msg_type = rrh->msg_type; + int i, rc; + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' from RR in state %s\n", + ms->name, get_rr_name(msg_type), + gsm48_mm_state_names[mm->state]); + + /* find function for current state and message */ + for (i = 0; i < RRDATASLLEN; i++) + if ((msg_type == rrdatastatelist[i].type) + && ((1 << mm->state) & rrdatastatelist[i].states)) + break; + if (i == RRDATASLLEN) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = rrdatastatelist[i].rout(ms, msg); + + if (rrdatastatelist[i].rout != gsm48_mm_data_ind) + msgb_free(msg); + + return rc; +} + +/* state trasitions for mobile managemnt messages (lower layer) */ +static struct mmdatastate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} mmdatastatelist[] = { + {ALL_STATES, /* 4.3.1.2 */ + GSM48_MT_MM_TMSI_REALL_CMD, gsm48_mm_rx_tmsi_realloc_cmd}, + + {ALL_STATES, /* 4.3.2.2 */ + GSM48_MT_MM_AUTH_REQ, gsm48_mm_rx_auth_req}, + + {ALL_STATES, /* 4.3.2.5 */ + GSM48_MT_MM_AUTH_REJ, gsm48_mm_rx_auth_rej}, + + {ALL_STATES, /* 4.3.3.2 */ + GSM48_MT_MM_ID_REQ, gsm48_mm_rx_id_req}, + + {ALL_STATES, /* 4.3.5.2 */ + GSM48_MT_MM_ABORT, gsm48_mm_rx_abort}, + + {ALL_STATES, /* 4.3.6.2 */ + GSM48_MT_MM_INFO, gsm48_mm_rx_info}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT), /* 4.4.4.6 */ + GSM48_MT_MM_LOC_UPD_ACCEPT, gsm48_mm_rx_loc_upd_acc}, + + {SBIT(GSM48_MM_ST_LOC_UPD_INIT), /* 4.4.4.7 */ + GSM48_MT_MM_LOC_UPD_REJECT, gsm48_mm_rx_loc_upd_rej}, + + {ALL_STATES, /* 4.5.1.1 */ + GSM48_MT_MM_CM_SERV_ACC, gsm48_mm_rx_cm_service_acc}, + + {ALL_STATES, /* 4.5.1.1 */ + GSM48_MT_MM_CM_SERV_REJ, gsm48_mm_rx_cm_service_rej}, +}; + +#define MMDATASLLEN \ + (sizeof(mmdatastatelist) / sizeof(struct mmdatastate)) + +static int gsm48_mm_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + uint8_t msg_type = gh->msg_type & 0xbf; + struct gsm48_mmxx_hdr *mmh; + int msg_supported = 0; /* determine, if message is supported at all */ + int rr_prim = -1, rr_est = -1; /* no prim set */ + uint8_t skip_ind; + int i, rc; + + /* 9.2.19 */ + if (msg_type == GSM48_MT_MM_NULL) + return 0; + + /* pull the RR header */ + msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); + + /* create transaction (if not exists) and push message */ + switch (pdisc) { + case GSM48_PDISC_CC: + rr_prim = GSM48_MMCC_DATA_IND; + rr_est = GSM48_MMCC_EST_IND; + break; +#if 0 + case GSM48_PDISC_NC_SS: + rr_prim = GSM48_MMSS_DATA_IND; + rr_est = GSM48_MMSS_EST_IND; + break; + case GSM48_PDISC_SMS: + rr_prim = GSM48_MMSMS_DATA_IND; + rr_est = GSM48_MMSMS_EST_IND; + break; +#endif + } + if (rr_prim != -1) { + uint8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; + /* flip */ + struct gsm48_mm_conn *conn; + + /* find transaction, if any */ + conn = mm_conn_by_id(mm, pdisc, transaction_id); + + /* create MM connection instance */ + if (!conn) { + conn = mm_conn_new(mm, pdisc, transaction_id, + mm_conn_new_ref++); + rr_prim = rr_est; + } + if (!conn) + return -ENOMEM; + + /* push new header */ + msgb_push(msg, sizeof(struct gsm48_mmxx_hdr)); + mmh = (struct gsm48_mmxx_hdr *)msg->data; + mmh->msg_type = rr_prim; + mmh->ref = conn->ref; + + /* go MM CONN ACTIVE state */ + if (mm->state == GSM48_MM_ST_WAIT_NETWORK_CMD + || mm->state == GSM48_MM_ST_RR_CONN_RELEASE_NA) { + /* stop RR release timer */ + stop_mm_t3240(mm); + + /* stop "RR connection release not allowed" timer */ + stop_mm_t3241(mm); + + new_mm_state(mm, GSM48_MM_ST_MM_CONN_ACTIVE, 0); + } + } + + /* forward message */ + switch (pdisc) { + case GSM48_PDISC_MM: + skip_ind = (gh->proto_discr & 0xf0) >> 4; + + /* ignore if skip indicator is not B'0000' */ + if (skip_ind) + return 0; + break; /* follow the selection proceedure below */ + + case GSM48_PDISC_CC: + return gsm48_rcv_cc(ms, msg); + +#if 0 + case GSM48_PDISC_NC_SS: + return gsm48_rcv_ss(ms, msg); + + case GSM48_PDISC_SMS: + return gsm48_rcv_sms(ms, msg); +#endif + + default: + LOGP(DMM, LOGL_NOTICE, "Protocol type 0x%02x unsupported.\n", + pdisc); + msgb_free(msg); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); + } + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' in MM state %s\n", ms->name, + get_mm_name(msg_type), gsm48_mm_state_names[mm->state]); + + stop_mm_t3212(mm); /* 4.4.2 */ + + /* 11.2 re-start pending RR release timer */ + if (bsc_timer_pending(&mm->t3240)) { + stop_mm_t3240(mm); + start_mm_t3240(mm); + } + + /* find function for current state and message */ + for (i = 0; i < MMDATASLLEN; i++) { + if (msg_type == mmdatastatelist[i].type) + msg_supported = 1; + if ((msg_type == mmdatastatelist[i].type) + && ((1 << mm->state) & mmdatastatelist[i].states)) + break; + } + if (i == MMDATASLLEN) { + msgb_free(msg); + if (msg_supported) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this " + "state.\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_COMPATIBLE); + } else { + LOGP(DMM, LOGL_NOTICE, "Message not supported.\n"); + return gsm48_mm_tx_mm_status(ms, + GSM48_REJECT_MSG_TYPE_NOT_IMPLEMENTED); + } + } + + rc = mmdatastatelist[i].rout(ms, msg); + + msgb_free(msg); + + return rc; +} + +/* state trasitions for mobile management events */ +static struct eventstate { + uint32_t states; + uint32_t substates; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} eventstatelist[] = { + /* 4.2.3 return to MM IDLE */ + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, + + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_return_idle}, + + /* 4.2.2.1 Normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_periodic}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start}, + + /* 4.2.2.2 Attempt to update / Loc. upd. needed */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE) | + SBIT(GSM48_MM_SST_LOC_UPD_NEEDED), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* change */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_ATTEMPT_UPDATE), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_periodic}, + + /* 4.2.2.3 Limited service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_USER_PLMN_SEL, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_plmn_search}, /* 4.2.1.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_loc_upd_normal}, /* if allow. */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_LIMITED_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + /* 4.2.2.4 No IMSI */ + /* 4.2.2.5 PLMN search, normal service */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_TIMEOUT_T3211, gsm48_mm_loc_upd_delay_retry}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_delay_retry}, + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH_NORMAL), + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_start}, + + /* 4.2.2.6 PLMN search */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MM_EVENT_NO_CELL_FOUND, gsm48_mm_no_cell_found}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_PLMN_SEARCH), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + /* No cell available */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_CELL_AVAIL), + GSM48_MM_EVENT_CELL_SELECTED, gsm48_mm_cell_selected}, /* 4.2.1.1 */ + + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NO_CELL_AVAIL), + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_delay_per}, /* 4.4.2 */ + + /* IMSI detach in other cases */ + {SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, /* silently detach */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_end}, + + {SBIT(GSM48_MM_ST_WAIT_OUT_MM_CONN) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE) | + SBIT(GSM48_MM_ST_PROCESS_CM_SERV_P) | + SBIT(GSM48_MM_ST_WAIT_REEST) | + SBIT(GSM48_MM_ST_WAIT_ADD_OUT_MM_CON) | + SBIT(GSM48_MM_ST_MM_CONN_ACTIVE_VGCS) | + SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD), ALL_STATES, /* we can release */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_release}, + + {SBIT(GSM48_MM_ST_WAIT_RR_CONN_IMSI_D) | + SBIT(GSM48_MM_ST_IMSI_DETACH_INIT) | + SBIT(GSM48_MM_ST_IMSI_DETACH_PEND), ALL_STATES, /* ignore */ + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_ignore}, + + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_IMSI_DETACH, gsm48_mm_imsi_detach_delay}, + + {GSM48_MM_ST_IMSI_DETACH_INIT, ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3220, gsm48_mm_imsi_detach_end}, + + /* location update in other cases */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3212, gsm48_mm_loc_upd_ignore}, + + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3210, gsm48_mm_loc_upd_timeout}, + + {ALL_STATES - SBIT(GSM48_MM_ST_MM_IDLE), ALL_STATES, + GSM48_MM_EVENT_TIMEOUT_T3213, gsm48_mm_loc_upd_failed}, + /* 4.4.4.9 c) (but without retry) */ + + /* SYSINFO event */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_SYSINFO, gsm48_mm_sysinfo}, + + /* T3240 timed out */ + {SBIT(GSM48_MM_ST_WAIT_NETWORK_CMD) | + SBIT(GSM48_MM_ST_LOC_UPD_REJ), ALL_STATES, /* 4.4.4.8 */ + GSM48_MM_EVENT_TIMEOUT_T3240, gsm48_mm_abort_rr}, + + /* T3230 timed out */ + {SBIT(GSM48_MM_ST_MM_IDLE), SBIT(GSM48_MM_SST_NORMAL_SERVICE), + GSM48_MM_EVENT_TIMEOUT_T3230, gsm48_mm_timeout_mm_con}, + + /* SIM reports SRES */ + {ALL_STATES, ALL_STATES, /* 4.3.2.2 */ + GSM48_MM_EVENT_AUTH_RESPONSE, gsm48_mm_tx_auth_rsp}, + +#if 0 + /* change in classmark is reported */ + {ALL_STATES, ALL_STATES, + GSM48_MM_EVENT_CLASSMARK_CHG, gsm48_mm_classm_chg}, +#endif +}; + +#define EVENTSLLEN \ + (sizeof(eventstatelist) / sizeof(struct eventstate)) + +static int gsm48_mm_ev(struct osmocom_ms *ms, int msg_type, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + int i, rc; + + if (mm->state == GSM48_MM_ST_MM_IDLE) + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state " + "MM IDLE, %s\n", ms->name, get_mmevent_name(msg_type), + gsm48_mm_substate_names[mm->substate]); + else + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event in state " + "%s\n", ms->name, get_mmevent_name(msg_type), + gsm48_mm_state_names[mm->state]); + + /* Find function for current state and message */ + for (i = 0; i < EVENTSLLEN; i++) + if ((msg_type == eventstatelist[i].type) + && ((1 << mm->state) & eventstatelist[i].states) + && ((1 << mm->substate) & eventstatelist[i].substates)) + break; + if (i == EVENTSLLEN) { + LOGP(DMM, LOGL_NOTICE, "Message unhandled at this state.\n"); + return 0; + } + + rc = eventstatelist[i].rout(ms, msg); + + return rc; +} + +/* + * MM Register (SIM insert and remove) + */ + +/* register new SIM card and trigger attach */ +static int gsm48_mmr_reg_req(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + + /* schedule insertion of SIM */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SIM_INSERT); + if (!nmsg) + return -ENOMEM; + gsm322_plmn_sendmsg(ms, nmsg); + + /* 4.2.1.2 SIM is inserted in state NO IMSI */ + if (mm->state == GSM48_MM_ST_MM_IDLE + && mm->substate == GSM48_MM_SST_NO_IMSI) + new_mm_state(mm, GSM48_MM_ST_MM_IDLE, + gsm48_mm_set_plmn_search(ms)); + + return 0; +} + +/* trigger detach of sim card */ +static int gsm48_mmr_nreg_req(struct osmocom_ms *ms) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + struct msgb *nmsg; + + nmsg = gsm322_msgb_alloc(GSM48_MM_EVENT_IMSI_DETACH); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(mm->ms, nmsg); + + return 0; +} + +static int gsm48_rcv_mmr(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmr *mmr = (struct gsm48_mmr *)msg->data; + int msg_type = mmr->msg_type; + int rc = 0; + + LOGP(DMM, LOGL_INFO, "(ms %s) Received '%s' event\n", ms->name, + get_mmr_name(msg_type)); + switch(msg_type) { + case GSM48_MMR_REG_REQ: + rc = gsm48_mmr_reg_req(ms); + break; + case GSM48_MMR_NREG_REQ: + rc = gsm48_mmr_nreg_req(ms); + break; + default: + LOGP(DMM, LOGL_NOTICE, "Message unhandled.\n"); + } + + return rc; +} + + diff --git a/src/host/layer23/src/gsm48_rr.c b/src/host/layer23/src/gsm48_rr.c new file mode 100644 index 00000000..4a189f34 --- /dev/null +++ b/src/host/layer23/src/gsm48_rr.c @@ -0,0 +1,4232 @@ +#warning rr on MDL error handling (as specified in 04.08 / 04.06) +/* + * (C) 2010 by 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. + * + */ + +/* Very short description of some of the procedures: + * + * A radio ressource request causes sendig a channel request on RACH. + * After receiving of an immediate assignment the link will be establised. + * After the link is established, the dedicated mode is entered and confirmed. + * + * A Paging request also triggers the channel request as above... + * After the link is established, the dedicated mode is entered and indicated. + * + * During dedicated mode, messages are transferred. + * + * When an assignment command or a handover command is received, the current + * link is released. After release, the new channel is activated and the + * link is established again. After link is establised, pending messages from + * radio ressource are sent. + * + * When the assignment or handover fails, the old channel is activate and the + * link is established again. Also pending messages are sent. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <arpa/inet.h> + +#include <osmocore/msgb.h> +#include <osmocore/utils.h> +#include <osmocore/rsl.h> +#include <osmocore/gsm48.h> +#include <osmocore/bitvec.h> + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1l2_interface.h> +#include <osmocom/logging.h> +#include <osmocom/networks.h> +#include <osmocom/l1ctl.h> + +static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg); +static int gsm48_rr_dl_est(struct osmocom_ms *ms); + +/* + * support + */ + +#define MIN(a, b) ((a < b) ? a : b) + +int gsm48_decode_lai(struct gsm48_loc_area_id *lai, uint16_t *mcc, + uint16_t *mnc, uint16_t *lac) +{ + *mcc = ((lai->digits[0] & 0x0f) << 8) + | (lai->digits[0] & 0xf0) + | (lai->digits[1] & 0x0f); + *mnc = ((lai->digits[2] & 0x0f) << 8) + | (lai->digits[2] & 0xf0) + | ((lai->digits[1] & 0xf0) >> 4); + *lac = ntohs(lai->lac); + + return 0; +} + +static int gsm48_encode_chan_h0(struct gsm48_chan_desc *cd, uint8_t tsc, + uint16_t arfcn) +{ + cd->h0.tsc = tsc; + cd->h0.h = 0; + cd->h0.arfcn_low = arfcn & 0xff; + cd->h0.arfcn_high = arfcn >> 8; + + return 0; +} + +static int gsm48_encode_chan_h1(struct gsm48_chan_desc *cd, uint8_t tsc, + uint8_t maio, uint8_t hsn) +{ + cd->h1.tsc = tsc; + cd->h1.h = 1; + cd->h1.maio_low = maio & 0x03; + cd->h1.maio_high = maio >> 2; + cd->h1.hsn = hsn; + + return 0; +} + + +static int gsm48_decode_chan_h0(struct gsm48_chan_desc *cd, uint8_t *tsc, + uint16_t *arfcn) +{ + *tsc = cd->h0.tsc; + *arfcn = cd->h0.arfcn_low | (cd->h0.arfcn_high << 8); + + return 0; +} + +static int gsm48_decode_chan_h1(struct gsm48_chan_desc *cd, uint8_t *tsc, + uint8_t *maio, uint8_t *hsn) +{ + *tsc = cd->h1.tsc; + *maio = cd->h1.maio_low | (cd->h1.maio_high << 2); + *hsn = cd->h1.hsn; + + return 0; +} + +/* 10.5.2.38 decode Starting time IE */ +static int gsm48_decode_start_time(struct gsm48_rr_cd *cd, + struct gsm48_start_time *st) +{ + cd->start_t1 = st->t1; + cd->start_t2 = st->t2; + cd->start_t3 = (st->t3_high << 3) | st->t3_low; + + return 0; +} + +/* decode "BA Range" (10.5.2.1a) */ +static int gsm48_decode_ba_range(const uint8_t *ba, uint8_t ba_len, + uint32_t *range, uint8_t *ranges, int max_ranges) +{ + /* ba = pointer to IE without IE type and length octets + * ba_len = number of octets + * range = pointer to store decoded range + * ranges = number of ranges decoded + * max_ranges = maximum number of decoded ranges that can be stored + */ + uint16_t lower, higher; + int i, n, required_octets; + + /* find out how much ba ranges will be decoded */ + n = *ba++; + ba_len --; + required_octets = 5 * (n >> 1) + 3 * (n & 1); + if (required_octets > ba_len) { + LOGP(DRR, LOGL_NOTICE, "BA range IE too short: %d ranges " + "require %d octets, but only %d octets remain.\n", + n, required_octets, ba_len); + *ranges = 0; + return -EINVAL; + } + if (max_ranges > n) + LOGP(DRR, LOGL_NOTICE, "BA range %d exceed the maximum number " + "of ranges supported by this mobile (%d).\n", + n, max_ranges); + n = max_ranges; + + /* decode ranges */ + for (i = 0; i < n; i++) { + if (!(i & 1)) { + /* decode even range number */ + lower = *ba++ << 2; + lower |= (*ba >> 6); + higher = (*ba++ & 0x3f) << 4; + higher |= *ba >> 4; + } else { + lower = (*ba++ & 0x0f) << 6; + lower |= *ba >> 2; + higher = (*ba++ & 0x03) << 8; + higher |= *ba++; + /* decode odd range number */ + } + *range++ = (higher << 16) | lower; + } + *ranges = n; + + return 0; +} + +/* + * state transition + */ + +const char *gsm48_rr_state_names[] = { + "idle", + "connection pending", + "dedicated", + "release pending", +}; + +static void new_rr_state(struct gsm48_rrlayer *rr, int state) +{ + if (state < 0 || state >= + (sizeof(gsm48_rr_state_names) / sizeof(char *))) + return; + + LOGP(DRR, LOGL_INFO, "new state %s -> %s\n", + gsm48_rr_state_names[rr->state], gsm48_rr_state_names[state]); + + rr->state = state; + + if (state == GSM48_RR_ST_IDLE) { + struct msgb *msg, *nmsg; + + /* release dedicated mode, if any */ +// tx_ph_dm_rel_req(rr->ms); + l1ctl_tx_reset_req(rr->ms, L1CTL_RES_T_FULL); + /* free establish message, if any */ + rr->rr_est_req = 0; + if (rr->rr_est_msg) { + msgb_free(rr->rr_est_msg); + rr->rr_est_msg = NULL; + } + /* free all pending messages */ + while((msg = msgb_dequeue(&rr->downqueue))) + msgb_free(msg); + /* clear all descriptions of last channel */ + memset(&rr->cd_now, 0, sizeof(rr->cd_now)); + /* reset cipering */ + rr->cipher_on = 0; + /* tell cell selection process to return to idle mode + * NOTE: this must be sent unbuffered, because it will + * leave camping state, so it locks against subsequent + * establishment of dedicated channel, before the + * cell selection process returned to camping state + * again. (after cell reselection) + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_RET_IDLE); + if (!nmsg) + return; + gsm322_c_event(rr->ms, nmsg); + msgb_free(nmsg); + /* reset any BA range */ + rr->ba_ranges = 0; + } +} + +/* + * messages + */ + +/* names of RR-SAP */ +static const struct value_string gsm48_rr_msg_names[] = { + { GSM48_RR_EST_REQ, "RR_EST_REQ" }, + { GSM48_RR_EST_IND, "RR_EST_IND" }, + { GSM48_RR_EST_CNF, "RR_EST_CNF" }, + { GSM48_RR_REL_IND, "RR_REL_IND" }, + { GSM48_RR_SYNC_IND, "RR_SYNC_IND" }, + { GSM48_RR_DATA_REQ, "RR_DATA_REQ" }, + { GSM48_RR_DATA_IND, "RR_DATA_IND" }, + { GSM48_RR_UNIT_DATA_IND, "RR_UNIT_DATA_IND" }, + { GSM48_RR_ABORT_REQ, "RR_ABORT_REQ" }, + { GSM48_RR_ABORT_IND, "RR_ABORT_IND" }, + { GSM48_RR_ACT_REQ, "RR_ACT_REQ" }, + { 0, NULL } +}; + +const char *get_rr_name(int value) +{ + return get_value_string(gsm48_rr_msg_names, value); +} + +/* allocate GSM 04.08 layer 3 message */ +struct msgb *gsm48_l3_msgb_alloc(void) +{ + struct msgb *msg; + + msg = msgb_alloc_headroom(L3_ALLOC_SIZE+L3_ALLOC_HEADROOM, + L3_ALLOC_HEADROOM, "GSM 04.08 L3"); + if (!msg) + return NULL; + msg->l3h = msg->data; + + return msg; +} + +/* allocate GSM 04.08 message (RR-SAP) */ +struct msgb *gsm48_rr_msgb_alloc(int msg_type) +{ + struct msgb *msg; + struct gsm48_rr_hdr *rrh; + + msg = msgb_alloc_headroom(RR_ALLOC_SIZE+RR_ALLOC_HEADROOM, + RR_ALLOC_HEADROOM, "GSM 04.08 RR"); + if (!msg) + return NULL; + + rrh = (struct gsm48_rr_hdr *) msgb_put(msg, sizeof(*rrh)); + rrh->msg_type = msg_type; + + return msg; +} + +/* queue message (RR-SAP) */ +int gsm48_rr_upmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_mmlayer *mm = &ms->mmlayer; + + msgb_enqueue(&mm->rr_upqueue, msg); + + return 0; +} + +/* push rsl header and send (RSL-SAP) */ +static int gsm48_send_rsl(struct osmocom_ms *ms, uint8_t msg_type, + struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (!msg->l3h) { + printf("FIX l3h\n"); + exit (0); + } + rsl_rll_push_l3(msg, msg_type, rr->cd_now.chan_nr, + rr->cd_now.link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +/* enqueue messages (RSL-SAP) */ +static int gsm48_rx_rll(struct msgb *msg, struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + msgb_enqueue(&rr->rsl_upqueue, msg); + + return 0; +} + +/* input function that L2 calls when sending messages up to L3 */ +static int gsm48_rx_rsl(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = gsm48_rx_rll(msg, ms); + break; + default: + /* FIXME: implement this */ + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", + rslh->msg_discr); + msgb_free(msg); + rc = -EINVAL; + break; + } + + return rc; +} + +/* dequeue messages (RSL-SAP) */ +int gsm48_rsl_dequeue(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg; + int work = 0; + + while ((msg = msgb_dequeue(&rr->rsl_upqueue))) { + /* msg is freed there */ + gsm48_rcv_rsl(ms, msg); + work = 1; /* work done */ + } + + return work; +} + +/* + * timers handling + */ + +/* special timer to ensure that UA is sent before disconnecting channel */ +static void timeout_rr_t_rel_wait(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + + LOGP(DRR, LOGL_INFO, "L2 release timer has fired, done waiting\n"); + + /* return to idle now */ + new_rr_state(rr, GSM48_RR_ST_IDLE); +} + +/* 3.4.13.1.1: Timeout of T3110 */ +static void timeout_rr_t3110(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + struct msgb *nmsg; + uint8_t *mode; + + LOGP(DRR, LOGL_INFO, "timer T3110 has fired, release locally\n"); + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* disconnect the main signalling link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 1; /* local release */ + gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg); + + return; +} + +static void timeout_rr_t3122(void *arg) +{ + LOGP(DRR, LOGL_INFO, "timer T3122 has fired\n"); +} + +static void timeout_rr_t3126(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct osmocom_ms *ms = rr->ms; + + LOGP(DRR, LOGL_INFO, "timer T3126 has fired\n"); + if (rr->rr_est_req) { + struct msgb *msg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + struct gsm48_rr_hdr *rrh; + + LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); + if (!msg) + return; + rrh = (struct gsm48_rr_hdr *)msg->data; + rrh->cause = RR_REL_CAUSE_RA_FAILURE; + gsm48_rr_upmsg(ms, msg); + } + + new_rr_state(rr, GSM48_RR_ST_IDLE); +} + +static void start_rr_t_rel_wait(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T_rel_wait with %d seconds\n", sec); + rr->t_rel_wait.cb = timeout_rr_t_rel_wait; + rr->t_rel_wait.data = rr; + bsc_schedule_timer(&rr->t_rel_wait, sec, micro); +} + +static void start_rr_t3110(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3110 with %d seconds\n", sec); + rr->t3110.cb = timeout_rr_t3110; + rr->t3110.data = rr; + bsc_schedule_timer(&rr->t3110, sec, micro); +} + +static void start_rr_t3122(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3122 with %d seconds\n", sec); + rr->t3122.cb = timeout_rr_t3122; + rr->t3122.data = rr; + bsc_schedule_timer(&rr->t3122, sec, micro); +} + +static void start_rr_t3126(struct gsm48_rrlayer *rr, int sec, int micro) +{ + LOGP(DRR, LOGL_INFO, "starting T3126 with %d seconds\n", sec); + rr->t3126.cb = timeout_rr_t3126; + rr->t3126.data = rr; + bsc_schedule_timer(&rr->t3126, sec, micro); +} + +static void stop_rr_t_rel_wait(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t_rel_wait)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T_rel_wait\n"); + bsc_del_timer(&rr->t_rel_wait); + } +} + +static void stop_rr_t3110(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3110)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3110\n"); + bsc_del_timer(&rr->t3110); + } +} + +static void stop_rr_t3122(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3122)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3122\n"); + bsc_del_timer(&rr->t3122); + } +} + +static void stop_rr_t3126(struct gsm48_rrlayer *rr) +{ + if (bsc_timer_pending(&rr->t3126)) { + LOGP(DRR, LOGL_INFO, "stopping pending timer T3126\n"); + bsc_del_timer(&rr->t3126); + } +} + +/* + * status + */ + +/* send rr status request */ +static int gsm48_rr_tx_rr_status(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_rr_status *st; + + LOGP(DRR, LOGL_INFO, "RR STATUS (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + st = (struct gsm48_rr_status *) msgb_put(nmsg, sizeof(*st)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; + + /* rr cause */ + st->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* + * ciphering + */ + +/* send chiperhing mode complete */ +static int gsm48_rr_tx_cip_mode_cpl(struct osmocom_ms *ms, uint8_t cr) +{ + struct gsm_settings *set = &ms->settings; + struct msgb *nmsg; + struct gsm48_hdr *gh; + uint8_t buf[11], *tlv; + + LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMPLETE (cr %d)\n", cr); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CIPH_M_COMPL; + + /* MI */ + if (cr) { + gsm48_generate_mid_from_imsi(buf, set->imeisv); + tlv = msgb_put(nmsg, 2 + buf[1]); + memcpy(tlv, buf, 2 + buf[1]); + } + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* receive ciphering mode command */ +static int gsm48_rr_rx_cip_mode_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_support *sup = &ms->support; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_cip_mode_cmd *cm = (struct gsm48_cip_mode_cmd *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cm); + uint8_t sc, alg_id, cr; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of CIPHERING MODE COMMAND " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* cipher mode setting */ + sc = cm->sc; + alg_id = cm->alg_id; + /* cipher mode response */ + cr = cm->cr; + + if (sc) + LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, cr=%u)", + sc, cr); + else + LOGP(DRR, LOGL_INFO, "CIPHERING MODE COMMAND (sc=%u, " + "algo=A5/%d cr=%u)", sc, alg_id + 1, cr); + + /* 3.4.7.2 */ + if (rr->cipher_on && sc) { + LOGP(DRR, LOGL_INFO, "cipering already applied.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + + /* check if we actually support this cipher */ + if ((alg_id == GSM_CIPHER_A5_1 && !sup->a5_1) + || (alg_id == GSM_CIPHER_A5_2 && !sup->a5_2) + || (alg_id == GSM_CIPHER_A5_3 && !sup->a5_3) + || (alg_id == GSM_CIPHER_A5_4 && !sup->a5_4) + || (alg_id == GSM_CIPHER_A5_5 && !sup->a5_5) + || (alg_id == GSM_CIPHER_A5_6 && !sup->a5_6) + || (alg_id == GSM_CIPHER_A5_7 && !sup->a5_7)) + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_CHAN_MODE_UNACCT); + + /* change to ciphering */ +#ifdef TODO + rsl command to activate ciperhing +#endif + rr->cipher_on = sc, rr->cipher_type = alg_id; + + /* response */ + return gsm48_rr_tx_cip_mode_cpl(ms, cr); +} + +/* + * classmark + */ + +/* Encode "Classmark 3" (10.5.1.7) */ +static int gsm48_rr_enc_cm3(struct osmocom_ms *ms, uint8_t *buf, uint8_t *len) +{ + struct gsm_support *sup = &ms->support; + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = buf; + bv.data_len = 12; + + /* spare bit */ + bitvec_set_bit(&bv, 0); + /* band 3 supported */ + if (sup->dcs_1800) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* band 2 supported */ + if (sup->e_gsm || sup->r_gsm) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* band 1 supported */ + if (sup->p_gsm && !(sup->e_gsm || sup->r_gsm)) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* a5 bits */ + if (sup->a5_7) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + if (sup->a5_6) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + if (sup->a5_5) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + if (sup->a5_4) + bitvec_set_bit(&bv, ONE); + else + bitvec_set_bit(&bv, ZERO); + /* radio capability */ + if (sup->dcs_1800 && !sup->p_gsm && !(sup->e_gsm || sup->r_gsm)) { + /* dcs only */ + bitvec_set_uint(&bv, 0, 4); + bitvec_set_uint(&bv, sup->dcs_capa, 4); + } else + if (sup->dcs_1800 && (sup->p_gsm || (sup->e_gsm || sup->r_gsm))) { + /* dcs */ + bitvec_set_uint(&bv, sup->dcs_capa, 4); + /* low band */ + bitvec_set_uint(&bv, sup->low_capa, 4); + } else { + /* low band only */ + bitvec_set_uint(&bv, 0, 4); + bitvec_set_uint(&bv, sup->low_capa, 4); + } + /* r support */ + if (sup->r_gsm) { + bitvec_set_bit(&bv, ONE); + bitvec_set_uint(&bv, sup->r_capa, 3); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* multi slot support */ + if (sup->ms_sup) { + bitvec_set_bit(&bv, ONE); + bitvec_set_uint(&bv, sup->ms_sup, 5); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* ucs2 treatment */ + if (sup->ucs2_treat) { + bitvec_set_bit(&bv, ONE); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* support extended measurements */ + if (sup->ext_meas) { + bitvec_set_bit(&bv, ONE); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* support measurement capability */ + if (sup->meas_cap) { + bitvec_set_bit(&bv, ONE); + bitvec_set_uint(&bv, sup->sms_val, 4); + bitvec_set_uint(&bv, sup->sm_val, 4); + } else { + bitvec_set_bit(&bv, ZERO); + } + /* positioning method capability */ + if (sup->loc_serv) { + bitvec_set_bit(&bv, ONE); + bitvec_set_bit(&bv, sup->e_otd_ass == 1); + bitvec_set_bit(&bv, sup->e_otd_based == 1); + bitvec_set_bit(&bv, sup->gps_ass == 1); + bitvec_set_bit(&bv, sup->gps_based == 1); + bitvec_set_bit(&bv, sup->gps_conv == 1); + } else { + bitvec_set_bit(&bv, ZERO); + } + + /* partitial bytes will be completed */ + *len = (bv.cur_bit + 7) >> 3; + bitvec_spare_padding(&bv, (*len * 8) - 1); + + return 0; +} + +/* encode classmark 2 */ +int gsm48_rr_enc_cm2(struct osmocom_ms *ms, struct gsm48_classmark2 *cm) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_support *sup = &ms->support; + + if (rr->cd_now.arfcn >= 512 && rr->cd_now.arfcn <= 885) + cm->pwr_lev = sup->pwr_lev_1800; + else + cm->pwr_lev = sup->pwr_lev_900; + cm->a5_1 = sup->a5_1; + cm->es_ind = sup->es_ind; + cm->rev_lev = sup->rev_lev; + cm->fc = (sup->r_gsm || sup->e_gsm); + cm->vgcs = sup->vgcs; + cm->vbs = sup->vbs; + cm->sm_cap = sup->sms_ptp; + cm->ss_scr = sup->ss_ind; + cm->ps_cap = sup->ps_cap; + cm->a5_2 = sup->a5_2; + cm->a5_3 = sup->a5_3; + cm->cmsp = sup->cmsp; + cm->solsa = sup->solsa; + cm->lcsva_cap = sup->lcsva; + + return 0; +} + +/* send classmark change */ +static int gsm48_rr_tx_cm_change(struct osmocom_ms *ms) +{ + struct gsm_support *sup = &ms->support; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_cm_change *cc; + uint8_t cm3[14], *tlv; + + LOGP(DRR, LOGL_INFO, "CLASSMARK CHANGE\n"); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + cc = (struct gsm48_cm_change *) msgb_put(nmsg, sizeof(*cc)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CLSM_CHG; + + /* classmark 2 */ + cc->cm2_len = sizeof(cc->cm2); + gsm48_rr_enc_cm2(ms, &cc->cm2); + + /* classmark 3 */ + if (sup->dcs_1800 || sup->e_gsm || sup->r_gsm + || sup->a5_7 || sup->a5_6 || sup->a5_5 || sup->a5_4 + || sup->ms_sup + || sup->ucs2_treat + || sup->ext_meas || sup->meas_cap + || sup->loc_serv) { + cc->cm2.cm3 = 1; + cm3[0] = GSM48_IE_CLASSMARK3; + gsm48_rr_enc_cm3(ms, cm3 + 2, &cm3[1]); + tlv = msgb_put(nmsg, 2 + cm3[1]); + memcpy(tlv, cm3, 2 + cm3[1]); + } + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* receiving classmark enquiry */ +static int gsm48_rr_rx_cm_enq(struct osmocom_ms *ms, struct msgb *msg) +{ + /* send classmark */ + return gsm48_rr_tx_cm_change(ms); +} + +/* + * random access + */ + +/* temporary timer until we have time control over channnel request */ +/* TODO: turn this into a channel activation timeout, later */ +#define RSL_MT_CHAN_CNF 0x19 +#include <osmocom/l1ctl.h> +int gsm48_rr_rach_conf(struct osmocom_ms *ms, uint32_t fn) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RR"); + struct abis_rsl_rll_hdr *rllh = + (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rllh)); + + if (!rr->wait_assign) { + LOGP(DRR, LOGL_INFO, "RACH confirm ignored, not waiting for " + "assignment.\n"); + return 0; + } + LOGP(DRR, LOGL_INFO, "RACH confirm framenr=%u\n", fn); + rr->cr_hist[0].valid = 2; + rr->cr_hist[0].fn = fn; + + rllh->c.msg_type = RSL_MT_CHAN_CNF; + msg->l2h = (unsigned char *)rllh; + gsm48_rcv_rsl(ms, msg); + + return 0; +} + +/* start random access */ +static int gsm48_rr_chan_req(struct osmocom_ms *ms, int cause, int paging) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint8_t chan_req_val, chan_req_mask; + int rc; + + LOGP(DSUM, LOGL_INFO, "Establish radio link due to %s request\n", + (paging) ? "paging" : "mobility management"); + + /* ignore paging, if not camping */ + if (paging + && (!cs->selected || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL))) { + LOGP(DRR, LOGL_INFO, "Paging, but not camping, ignore.\n"); + return -EINVAL; + } + + /* tell cell selection process to leave idle mode + * NOTE: this must be sent unbuffered, because the state may not + * change until idle mode is left + */ + nmsg = gsm322_msgb_alloc(GSM322_EVENT_LEAVE_IDLE); + if (!nmsg) + return -ENOMEM; + rc = gsm322_c_event(ms, nmsg); + msgb_free(nmsg); + if (rc) { + if (paging) + return rc; + LOGP(DRR, LOGL_INFO, "Failed to leave IDLE mode.\n"); + goto undefined; + } + + /* 3.3.1.1.2 */ + new_rr_state(rr, GSM48_RR_ST_CONN_PEND); + + /* number of retransmissions (with first transmission) */ + rr->n_chan_req = s->max_retrans + 1; + +#warning HACK: always request SDCCH for test +cause = RR_EST_CAUSE_LOC_UPD; + /* generate CHAN REQ (9.1.8) */ + switch (cause) { + case RR_EST_CAUSE_EMERGENCY: + /* 101xxxxx */ + chan_req_mask = 0x1f; + chan_req_val = 0xa0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Emergency call)\n", + chan_req_val); + break; + case RR_EST_CAUSE_REESTAB_TCH_F: + chan_req_mask = 0x1f; + chan_req_val = 0xc0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (re-establish " + "TCH/F)\n", chan_req_val); + break; + case RR_EST_CAUSE_REESTAB_TCH_H: + if (s->neci) { + chan_req_mask = 0x03; + chan_req_val = 0x68; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H with NECI)\n", + chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xc0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H no NECI)\n", chan_req_val); + } + break; + case RR_EST_CAUSE_REESTAB_2_TCH_H: + if (s->neci) { + chan_req_mask = 0x03; + chan_req_val = 0x6c; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H+TCH/H with NECI)\n", + chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xc0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x " + "(re-establish TCH/H+TCH/H no NECI)\n", + chan_req_val); + } + break; + case RR_EST_CAUSE_ANS_PAG_ANY: + chan_req_mask = 0x1f; + chan_req_val = 0x80; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING " + "Any channel)\n", chan_req_val); + break; + case RR_EST_CAUSE_ANS_PAG_SDCCH: + chan_req_mask = 0x0f; + chan_req_val = 0x10; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING SDCCH)\n", + chan_req_val); + break; + case RR_EST_CAUSE_ANS_PAG_TCH_F: + /* ms supports no dual rate */ + chan_req_mask = 0x1f; + chan_req_val = 0x80; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/F)\n", + chan_req_val); + break; + case RR_EST_CAUSE_ANS_PAG_TCH_ANY: + /* ms supports no dual rate */ + chan_req_mask = 0x1f; + chan_req_val = 0x80; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (PAGING TCH/H or " + "TCH/F)\n", chan_req_val); + break; + case RR_EST_CAUSE_ORIG_TCHF: + /* ms supports no dual rate */ + chan_req_mask = 0x1f; + chan_req_val = 0xe0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Orig TCH/F)\n", + chan_req_val); + break; + case RR_EST_CAUSE_LOC_UPD: + if (s->neci) { + chan_req_mask = 0x0f; + chan_req_val = 0x00; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location " + "Update with NECI)\n", chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0x00; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (Location " + "Update no NECI)\n", chan_req_val); + } + break; + case RR_EST_CAUSE_OTHER_SDCCH: + if (s->neci) { + chan_req_mask = 0x0f; + chan_req_val = 0x01; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OHTER " + "with NECI)\n", chan_req_val); + } else { + chan_req_mask = 0x1f; + chan_req_val = 0xe0; + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: %02x (OTHER " + "no NECI)\n", chan_req_val); + } + break; + default: + if (!rr->rr_est_req) /* no request from MM */ + return -EINVAL; + + LOGP(DRR, LOGL_INFO, "CHANNEL REQUEST: with unknown " + "establishment cause: %d\n", cause); + undefined: + LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); + + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_UNDEFINED; + gsm48_rr_upmsg(ms, nmsg); + new_rr_state(rr, GSM48_RR_ST_IDLE); + return -EINVAL; + } + + /* store value, mask and history */ + rr->chan_req_val = chan_req_val; + rr->chan_req_mask = chan_req_mask; + rr->cr_hist[2].valid = 0; + rr->cr_hist[1].valid = 0; + rr->cr_hist[0].valid = 0; + + /* if channel is already active somehow */ + if (cs->ccch_state == GSM322_CCCH_ST_DATA) + return gsm48_rr_tx_rand_acc(ms, NULL); + + return 0; +} + +/* send first/next channel request in conn pend state */ +int gsm48_rr_tx_rand_acc(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; + struct msgb *nmsg; + struct l1ctl_info_ul *nul; + struct l1ctl_rach_req *nra; + int slots; + uint8_t chan_req; + + if (cs->ccch_state != GSM322_CCCH_ST_DATA) { + LOGP(DRR, LOGL_INFO, "CCCH channel activation failed.\n"); + + if (rr->rr_est_req) { + struct msgb *msg = + gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + struct gsm48_rr_hdr *rrh; + + LOGP(DSUM, LOGL_INFO, "Requesting channel failed\n"); + if (!msg) + return -ENOMEM; + rrh = (struct gsm48_rr_hdr *)msg->data; + rrh->cause = RR_REL_CAUSE_RA_FAILURE; + gsm48_rr_upmsg(ms, msg); + } + + new_rr_state(rr, GSM48_RR_ST_IDLE); + + return 0; + } + + if (rr->state == GSM48_RR_ST_IDLE) { + LOGP(DRR, LOGL_INFO, "MM already released RR.\n"); + + return 0; + } + + LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (requests left %d)\n", + rr->n_chan_req); + + if (!rr->n_chan_req) { + LOGP(DRR, LOGL_INFO, "Done with sending RANDOM ACCESS " + "bursts\n"); + if (!bsc_timer_pending(&rr->t3126)) + start_rr_t3126(rr, 5, 0); /* TODO improve! */ + return 0; + } + rr->n_chan_req--; + + if (!rr->wait_assign) { + /* first random acces, without delay of slots */ + slots = 0; + rr->wait_assign = 1; + } else { + /* subsequent random acces, with slots from table 3.1 */ + switch(s->tx_integer) { + case 3: case 8: case 14: case 50: + if (s->ccch_conf != 1) /* not combined CCCH */ + slots = 55; + else + slots = 41; + break; + case 4: case 9: case 16: + if (s->ccch_conf != 1) + slots = 76; + else + slots = 52; + break; + case 5: case 10: case 20: + if (s->ccch_conf != 1) + slots = 109; + else + slots = 58; + break; + case 6: case 11: case 25: + if (s->ccch_conf != 1) + slots = 163; + else + slots = 86; + break; + default: + if (s->ccch_conf != 1) + slots = 217; + else + slots = 115; + break; + } + } + + /* resend chan_req with new randiom */ +#ifdef TODO + nmsg = gsm48_rsl_msgb_alloc(); +#else + nmsg = msgb_alloc_headroom(64, 48, "RAND_ACC"); + struct l1ctl_hdr *l1h; + nmsg->l1h = msgb_put(nmsg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *) nmsg->l1h; + l1h->msg_type = L1CTL_RACH_REQ; + if (!nmsg) + return -ENOMEM; + nul = (struct l1ctl_info_ul *) msgb_put(nmsg, sizeof(*nul)); +#endif + nra = (struct l1ctl_rach_req *) msgb_put(nmsg, sizeof(*nra)); + chan_req = random(); + chan_req &= rr->chan_req_mask; + chan_req |= rr->chan_req_val; + nra->ra = chan_req; +#warning TODO + nra->mf_off = 1; // (random() % s->tx_integer) + slots; + nra->fn51 = (s->ccch_conf == 1) ? 27 : 50; + + LOGP(DRR, LOGL_INFO, "RANDOM ACCESS (Tx-integer %d combined %s " + "S(lots) %d ra 0x%02x offset %d)\n", s->tx_integer, + (s->ccch_conf == 1) ? "yes": "no", slots, nra->ra, nra->mf_off); + + /* shift history and store */ + memcpy(&(rr->cr_hist[2]), &(rr->cr_hist[1]), + sizeof(struct gsm48_cr_hist)); + memcpy(&(rr->cr_hist[1]), &(rr->cr_hist[0]), + sizeof(struct gsm48_cr_hist)); + rr->cr_hist[0].valid = 1; + rr->cr_hist[0].chan_req = chan_req; + +#ifdef TODO + add layer 1 conrols to RSL... + return gsm48_send_rsl(ms, RSL_MT_CHAN_REQ, nmsg); +#else +//#warning disabled! + return osmo_send_l1(ms, nmsg); +#endif +} + +/* + * system information + */ + +/* decode "Cell Channel Description" (10.5.2.1b) and other frequency lists */ +static int gsm48_decode_freq_list(struct gsm_support *sup, + struct gsm_sysinfo_freq *f, uint8_t *cd, uint8_t len, uint8_t mask, + uint8_t frqt) +{ + int i; + + /* NOTES: + * + * The Range format uses "SMOD" computation. + * e.g. "n SMOD m" equals "((n - 1) % m) + 1" + * A cascade of multiple SMOD computations is simpified: + * "(n SMOD m) SMOD o" equals "(((n - 1) % m) % o) + 1" + * + * The Range format uses 16 octets of data in SYSTEM INFORMATION. + * When used in dedicated messages, the length can be less. + * In this case the ranges are decoded for all frequencies that + * fit in the block of given length. + */ + + /* tabula rasa */ + for (i = 0; i < 1024; i++) + f[i].mask &= ~frqt; + + /* 00..XXX. */ + if ((cd[0] & 0xc0 & mask) == 0x00) { + /* Bit map 0 format */ + if (len < 16) + return -EINVAL; + for (i = 1; i <= 124; i++) + if ((cd[15 - ((i-1) >> 3)] & (1 << ((i-1) & 7)))) + f[i].mask |= frqt; + + return 0; + } + + /* only Bit map 0 format for P-GSM */ + if (sup->p_gsm && !sup->e_gsm && !sup->r_gsm && !sup->dcs_1800) + return 0; + + /* 10..0XX. */ + if ((cd[0] & 0xc8 & mask) == 0x80) { + /* Range 1024 format */ + uint16_t w[17]; /* 1..16 */ + struct gsm48_range_1024 *r = (struct gsm48_range_1024 *)cd; + + if (len < 2) + return -EINVAL; + memset(w, 0, sizeof(w)); + if (r->f0) + f[0].mask |= frqt; + w[1] = (r->w1_hi << 8) | r->w1_lo; + if (len >= 4) + w[2] = (r->w2_hi << 1) | r->w2_lo; + if (len >= 5) + w[3] = (r->w3_hi << 2) | r->w3_lo; + if (len >= 6) + w[4] = (r->w4_hi << 2) | r->w4_lo; + if (len >= 7) + w[5] = (r->w5_hi << 2) | r->w5_lo; + if (len >= 8) + w[6] = (r->w6_hi << 2) | r->w6_lo; + if (len >= 9) + w[7] = (r->w7_hi << 2) | r->w7_lo; + if (len >= 10) + w[8] = (r->w8_hi << 1) | r->w8_lo; + if (len >= 10) + w[9] = r->w9; + if (len >= 11) + w[10] = r->w10; + if (len >= 12) + w[11] = (r->w11_hi << 6) | r->w11_lo; + if (len >= 13) + w[12] = (r->w12_hi << 5) | r->w12_lo; + if (len >= 14) + w[13] = (r->w13_hi << 4) | r->w13_lo; + if (len >= 15) + w[14] = (r->w14_hi << 3) | r->w14_lo; + if (len >= 16) + w[15] = (r->w15_hi << 2) | r->w15_lo; + if (len >= 16) + w[16] = r->w16; + if (w[1]) + f[w[1]].mask |= frqt; + if (w[2]) + f[((w[1] - 512 + w[2] - 1) % 1023) + 1].mask |= frqt; + if (w[3]) + f[((w[1] + w[3] - 1) % 1023) + 1].mask |= frqt; + if (w[4]) + f[((w[1] - 512 + ((w[2] - 256 + w[4] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[5]) + f[((w[1] + ((w[3] - 256 - w[5] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[6]) + f[((w[1] - 512 + ((w[2] + w[6] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[7]) + f[((w[1] + ((w[3] + w[7] - 1) % 511)) % 1023) + 1].mask |= frqt; + if (w[8]) + f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + w[8] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[9]) + f[((w[1] + ((w[3] - 256 + ((w[5] - 128 + w[9] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[10]) + f[((w[1] - 512 + ((w[2] + ((w[6] - 128 + w[10] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[11]) + f[((w[1] + ((w[3] + ((w[7] - 128 + w[11] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[12]) + f[((w[1] - 512 + ((w[2] - 256 + ((w[4] + w[12] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[13]) + f[((w[1] + ((w[3] - 256 + ((w[5] + w[13] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[14]) + f[((w[1] - 512 + ((w[2] + ((w[6] + w[14] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[15]) + f[((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 255)) % 511)) % 1023) + 1].mask |= frqt; + if (w[16]) + f[((w[1] - 512 + ((w[2] - 256 + ((w[4] - 128 + ((w[8] - 64 + w[16] - 1) % 127)) % 255)) % 511)) % 1023) + 1].mask |= frqt; + + return 0; + } + /* 10..100. */ + if ((cd[0] & 0xce & mask) == 0x88) { + /* Range 512 format */ + uint16_t w[18]; /* 1..17 */ + struct gsm48_range_512 *r = (struct gsm48_range_512 *)cd; + + if (len < 4) + return -EINVAL; + memset(w, 0, sizeof(w)); + w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + w[1] = (r->w1_hi << 2) | r->w1_lo; + if (len >= 5) + w[2] = (r->w2_hi << 2) | r->w2_lo; + if (len >= 6) + w[3] = (r->w3_hi << 2) | r->w3_lo; + if (len >= 7) + w[4] = (r->w4_hi << 1) | r->w4_lo; + if (len >= 7) + w[5] = r->w5; + if (len >= 8) + w[6] = r->w6; + if (len >= 9) + w[7] = (r->w7_hi << 6) | r->w7_lo; + if (len >= 10) + w[8] = (r->w8_hi << 4) | r->w8_lo; + if (len >= 11) + w[9] = (r->w9_hi << 2) | r->w9_lo; + if (len >= 11) + w[10] = r->w10; + if (len >= 12) + w[11] = r->w11; + if (len >= 13) + w[12] = (r->w12_hi << 4) | r->w12_lo; + if (len >= 14) + w[13] = (r->w13_hi << 2) | r->w13_lo; + if (len >= 14) + w[14] = r->w14; + if (len >= 15) + w[15] = r->w15; + if (len >= 16) + w[16] = (r->w16_hi << 3) | r->w16_lo; + if (len >= 16) + w[17] = r->w17; + f[w[0]].mask |= frqt; + if (w[1]) + f[(w[0] + w[1]) % 1024].mask |= frqt; + if (w[2]) + f[(w[0] + ((w[1] - 256 + w[2] - 1) % 511) + 1) % 1024].mask |= frqt; + if (w[3]) + f[(w[0] + ((w[1] + w[3] - 1) % 511) + 1) % 1024].mask |= frqt; + if (w[4]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + w[4] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[5]) + f[(w[0] + ((w[1] + ((w[3] - 128 + w[5] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[6]) + f[(w[0] + ((w[1] - 256 + ((w[2] + w[6] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[7]) + f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[8]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + w[8] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[9]) + f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] - 64 + w[9] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[10]) + f[(w[0] + ((w[1] - 256 + ((w[2] + ((w[6] - 64 + w[10] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[11]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 64 + w[11] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[12]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] + w[12] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[13]) + f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] + w[13] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[14]) + f[(w[0] + ((w[1] - 256 + ((w[2] + ((w[6] + w[14] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[15]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[16]) + f[(w[0] + ((w[1] - 256 + ((w[2] - 128 + ((w[4] - 64 + ((w[8] - 32 + w[16] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + if (w[17]) + f[(w[0] + ((w[1] + ((w[3] - 128 + ((w[5] - 64 + ((w[9] - 32 + w[17] - 1) % 63)) % 127)) % 255)) % 511) + 1) % 1024].mask |= frqt; + + return 0; + } + /* 10..101. */ + if ((cd[0] & 0xce & mask) == 0x8a) { + /* Range 256 format */ + uint16_t w[22]; /* 1..21 */ + struct gsm48_range_256 *r = (struct gsm48_range_256 *)cd; + + if (len < 4) + return -EINVAL; + memset(w, 0, sizeof(w)); + w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + w[1] = (r->w1_hi << 1) | r->w1_lo; + if (len >= 4) + w[2] = r->w2; + if (len >= 5) + w[3] = r->w3; + if (len >= 6) + w[4] = (r->w4_hi << 5) | r->w4_lo; + if (len >= 7) + w[5] = (r->w5_hi << 3) | r->w5_lo; + if (len >= 8) + w[6] = (r->w6_hi << 1) | r->w6_lo; + if (len >= 8) + w[7] = r->w7; + if (len >= 9) + w[8] = (r->w8_hi << 4) | r->w8_lo; + if (len >= 10) + w[9] = (r->w9_hi << 1) | r->w9_lo; + if (len >= 10) + w[10] = r->w10; + if (len >= 11) + w[11] = (r->w11_hi << 3) | r->w11_lo; + if (len >= 11) + w[12] = r->w12; + if (len >= 12) + w[13] = r->w13; + if (len >= 13) + w[14] = r->w15; + if (len >= 13) + w[15] = (r->w14_hi << 2) | r->w14_lo; + if (len >= 14) + w[16] = (r->w16_hi << 3) | r->w16_lo; + if (len >= 14) + w[17] = r->w17; + if (len >= 15) + w[18] = r->w19; + if (len >= 15) + w[19] = (r->w18_hi << 3) | r->w18_lo; + if (len >= 16) + w[20] = (r->w20_hi << 3) | r->w20_lo; + if (len >= 16) + w[21] = r->w21; + f[w[0]].mask |= frqt; + if (w[1]) + f[(w[0] + w[1]) % 1024].mask |= frqt; + if (w[2]) + f[(w[0] + ((w[1] - 128 + w[2] - 1) % 255) + 1) % 1024].mask |= frqt; + if (w[3]) + f[(w[0] + ((w[1] + w[3] - 1) % 255) + 1) % 1024].mask |= frqt; + if (w[4]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + w[4] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[5]) + f[(w[0] + ((w[1] + ((w[3] - 64 + w[5] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[6]) + f[(w[0] + ((w[1] - 128 + ((w[2] + w[6] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[7]) + f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[8]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + w[8] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[9]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] - 32 + w[9] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[10]) + f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] - 32 + w[10] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[11]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 32 + w[11] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[12]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] + w[12] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[13]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] + w[13] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[14]) + f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] + w[14] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[15]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[16]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] - 32 + ((w[8] - 16 + w[16] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[17]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] - 32 + ((w[9] - 16 + w[17] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[18]) + f[(w[0] + ((w[1] - 128 + ((w[2] + ((w[6] - 32 + ((w[10] - 16 + w[18] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[19]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 32 + ((w[11] - 16 + w[19] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[20]) + f[(w[0] + ((w[1] - 128 + ((w[2] - 64 + ((w[4] + ((w[12] - 16 + w[20] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + if (w[21]) + f[(w[0] + ((w[1] + ((w[3] - 64 + ((w[5] + ((w[13] - 16 + w[21] - 1) % 31)) % 63)) % 127)) % 255) + 1) % 1024].mask |= frqt; + + return 0; + } + /* 10..110. */ + if ((cd[0] & 0xce & mask) == 0x8c) { + /* Range 128 format */ + uint16_t w[29]; /* 1..28 */ + struct gsm48_range_128 *r = (struct gsm48_range_128 *)cd; + + if (len < 3) + return -EINVAL; + memset(w, 0, sizeof(w)); + w[0] = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + w[1] = r->w1; + if (len >= 4) + w[2] = r->w2; + if (len >= 5) + w[3] = (r->w3_hi << 4) | r->w3_lo; + if (len >= 6) + w[4] = (r->w4_hi << 1) | r->w4_lo; + if (len >= 6) + w[5] = r->w5; + if (len >= 7) + w[6] = (r->w6_hi << 3) | r->w6_lo; + if (len >= 7) + w[7] = r->w7; + if (len >= 8) + w[8] = r->w8; + if (len >= 8) + w[9] = r->w9; + if (len >= 9) + w[10] = r->w10; + if (len >= 9) + w[11] = r->w11; + if (len >= 10) + w[12] = r->w12; + if (len >= 10) + w[13] = r->w13; + if (len >= 11) + w[14] = r->w14; + if (len >= 11) + w[15] = r->w15; + if (len >= 12) + w[16] = r->w16; + if (len >= 12) + w[17] = r->w17; + if (len >= 13) + w[18] = (r->w18_hi << 1) | r->w18_lo; + if (len >= 13) + w[19] = r->w19; + if (len >= 13) + w[20] = r->w20; + if (len >= 14) + w[21] = (r->w21_hi << 2) | r->w21_lo; + if (len >= 14) + w[22] = r->w22; + if (len >= 14) + w[23] = r->w23; + if (len >= 15) + w[24] = r->w24; + if (len >= 15) + w[25] = r->w25; + if (len >= 16) + w[26] = (r->w26_hi << 1) | r->w26_lo; + if (len >= 16) + w[27] = r->w27; + if (len >= 16) + w[28] = r->w28; + f[w[0]].mask |= frqt; + if (w[1]) + f[(w[0] + w[1]) % 1024].mask |= frqt; + if (w[2]) + f[(w[0] + ((w[1] - 64 + w[2] - 1) % 127) + 1) % 1024].mask |= frqt; + if (w[3]) + f[(w[0] + ((w[1] + w[3] - 1) % 127) + 1) % 1024].mask |= frqt; + if (w[4]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + w[4] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[5]) + f[(w[0] + ((w[1] + ((w[3] - 32 + w[5] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[6]) + f[(w[0] + ((w[1] - 64 + ((w[2] + w[6] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[7]) + f[(w[0] + ((w[1] + ((w[3] + w[7] - 1) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[8]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + w[8] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[9]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + w[9] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[10]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + w[10] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[11]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + w[11] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[12]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + w[12] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[13]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] + w[13] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[14]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] + w[14] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[15]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + w[15] - 1) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[16]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] - 8 + w[16] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[17]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + ((w[9] - 8 + w[17] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[18]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + ((w[10] - 8 + w[18] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[19]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + ((w[11] - 8 + w[19] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[20]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + ((w[12] - 8 + w[20] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[21]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] + ((w[13] - 8 + w[21] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[22]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] + ((w[14] - 8 + w[22] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[23]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] + ((w[15] - 8 + w[23] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[24]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] - 16 + ((w[8] + w[24] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[25]) + f[(w[0] + ((w[1] + ((w[3] - 32 + ((w[5] - 16 + ((w[9] + w[25] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[26]) + f[(w[0] + ((w[1] - 64 + ((w[2] + ((w[6] - 16 + ((w[10] + w[26] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[27]) + f[(w[0] + ((w[1] + ((w[3] + ((w[7] - 16 + ((w[11] + w[27] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + if (w[28]) + f[(w[0] + ((w[1] - 64 + ((w[2] - 32 + ((w[4] + ((w[12] + w[28] - 1) % 15)) % 31)) % 63)) % 127) + 1) % 1024].mask |= frqt; + + return 0; + } + /* 10..111. */ + if ((cd[0] & 0xce & mask) == 0x8e) { + /* Variable bitmap format (can be any length >= 3) */ + uint16_t orig = 0; + struct gsm48_var_bit *r = (struct gsm48_var_bit *)cd; + + if (len < 3) + return -EINVAL; + orig = (r->orig_arfcn_hi << 9) | (r->orig_arfcn_mid << 1) | r->orig_arfcn_lo; + f[orig].mask |= frqt; + for (i = 1; 2 + (i >> 3) < len; i++) + if ((cd[2 + (i >> 3)] & (0x80 >> (i & 7)))) + f[(orig + i) % 1024].mask |= frqt; + + return 0; + } + + return 0; +} + +/* decode "Cell Selection Parameters" (10.5.2.4) */ +static int gsm48_decode_cell_sel_param(struct gsm48_sysinfo *s, + struct gsm48_cell_sel_par *cs) +{ +#ifdef TODO + convert ms_txpwr_max_ccch dependant on the current frequenc and support + to the right powe level +#endif + s->ms_txpwr_max_ccch = cs->ms_txpwr_max_ccch; + s->cell_resel_hyst_db = cs->cell_resel_hyst * 2; + s->rxlev_acc_min_db = cs->rxlev_acc_min - 110; + s->neci = cs->neci; + s->acs = cs->acs; + + return 0; +} + +/* decode "Cell Options (BCCH)" (10.5.2.3) */ +static int gsm48_decode_cellopt_bcch(struct gsm48_sysinfo *s, + struct gsm48_cell_options *co) +{ + s->bcch_radio_link_timeout = (co->radio_link_timeout + 1) * 4; + s->bcch_dtx = co->dtx; + s->bcch_pwrc = co->pwrc; + + return 0; +} + +/* decode "Cell Options (SACCH)" (10.5.2.3a) */ +static int gsm48_decode_cellopt_sacch(struct gsm48_sysinfo *s, + struct gsm48_cell_options *co) +{ + s->sacch_radio_link_timeout = (co->radio_link_timeout + 1) * 4; + s->sacch_dtx = co->dtx; + s->sacch_pwrc = co->pwrc; + + return 0; +} + +/* decode "Control Channel Description" (10.5.2.11) */ +static int gsm48_decode_ccd(struct gsm48_sysinfo *s, + struct gsm48_control_channel_descr *cc) +{ + s->ccch_conf = cc->ccch_conf; + s->bs_ag_blks_res = cc->bs_ag_blks_res; + s->att_allowed = cc->att; + s->pag_mf_periods = cc->bs_pa_mfrms + 2; + s->t3212 = cc->t3212 * 360; /* convert deci-hours to seconds */ + + return 0; +} + +/* decode "Mobile Allocation" (10.5.2.21) */ +static int gsm48_decode_mobile_alloc(struct gsm48_sysinfo *s, + uint8_t *ma, uint8_t len, uint16_t *hopping, uint8_t *hopp_len, int si4) +{ + int i, j = 0; + uint16_t f[len << 3]; + + /* not more than 64 hopping indexes allowed in IE */ + if (len > 8) + return -EINVAL; + + /* tabula rasa */ + *hopp_len = 0; + if (si4) { + for (i = 0; i < 1024; i++) + s->freq[i].mask &= ~FREQ_TYPE_HOPP; + } + + /* generating list of all frequencies (1..1023,0) */ + for (i = 1; i <= 1024; i++) { + if ((s->freq[i & 1023].mask & FREQ_TYPE_SERV)) { + LOGP(DRR, LOGL_INFO, "Serving cell ARFCN #%d: %d\n", + j, i & 1023); + f[j++] = i & 1023; + if (j == (len << 3)) + break; + } + } + + /* fill hopping table with frequency index given by IE + * and set hopping type bits + */ + for (i = 0; i < (len << 3); i++) { + /* if bit is set, this frequency index is used for hopping */ + if ((ma[len - 1 - (i >> 3)] & (1 << (i & 7)))) { + LOGP(DRR, LOGL_INFO, "Hopping ARFCN: %d (bit %d)\n", + i, f[i]); + /* index higher than entries in list ? */ + if (i >= j) { + LOGP(DRR, LOGL_NOTICE, "Mobile Allocation " + "hopping index %d exceeds maximum " + "number of cell frequencies. (%d)\n", + i + 1, j); + break; + } + hopping[(*hopp_len)++] = f[i]; + if (si4) + s->freq[f[i]].mask |= FREQ_TYPE_HOPP; + } + } + + return 0; +} + +/* Rach Control decode tables */ +static uint8_t gsm48_max_retrans[4] = { + 1, 2, 4, 7 +}; +static uint8_t gsm48_tx_integer[16] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 25, 32, 50 +}; + +/* decode "RACH Control Parameter" (10.5.2.29) */ +static int gsm48_decode_rach_ctl_param(struct gsm48_sysinfo *s, + struct gsm48_rach_control *rc) +{ + s->reest_denied = rc->re; + s->cell_barr = rc->cell_bar; + s->tx_integer = gsm48_tx_integer[rc->tx_integer]; + s->max_retrans = gsm48_max_retrans[rc->max_trans]; + s->class_barr = (rc->t2 << 8) | rc->t3; + + return 0; +} +static int gsm48_decode_rach_ctl_neigh(struct gsm48_sysinfo *s, + struct gsm48_rach_control *rc) +{ + s->nb_reest_denied = rc->re; + s->nb_cell_barr = rc->cell_bar; + s->nb_tx_integer = gsm48_tx_integer[rc->tx_integer]; + s->nb_max_retrans = gsm48_max_retrans[rc->max_trans]; + s->nb_class_barr = (rc->t2 << 8) | rc->t3; + + return 0; +} + +/* decode "SI 1 Rest Octets" (10.5.2.32) */ +static int gsm48_decode_si1_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + return 0; +} + +/* decode "SI 3 Rest Octets" (10.5.2.34) */ +static int gsm48_decode_si3_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data_len = len; + bv.data = si; + + /* Optional Selection Parameters */ + if (bitvec_get_bit_high(&bv) == H) { + s->sp = 1; + s->sp_cbq = bitvec_get_uint(&bv, 1); + s->sp_cro = bitvec_get_uint(&bv, 6); + s->sp_to = bitvec_get_uint(&bv, 3); + s->sp_pt = bitvec_get_uint(&bv, 5); + } + /* Optional Power Offset */ + if (bitvec_get_bit_high(&bv) == H) { + s->po = 1; + s->po_value = bitvec_get_uint(&bv, 3); + } + /* System Onformation 2ter Indicator */ + if (bitvec_get_bit_high(&bv) == H) + s->si2ter_ind = 1; + /* Early Classark Sending Control */ + if (bitvec_get_bit_high(&bv) == H) + s->ecsm = 1; + /* Scheduling if and where */ + if (bitvec_get_bit_high(&bv) == H) { + s->sched = 1; + s->sched_where = bitvec_get_uint(&bv, 3); + } + /* GPRS Indicator */ + s->gi_ra_colour = bitvec_get_uint(&bv, 3); + s->gi_si13_pos = bitvec_get_uint(&bv, 1); + return 0; +} + +/* decode "SI 4 Rest Octets" (10.5.2.35) */ +static int gsm48_decode_si4_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + return 0; +} + +/* decode "SI 6 Rest Octets" (10.5.2.35a) */ +static int gsm48_decode_si6_rest(struct gsm48_sysinfo *s, uint8_t *si, + uint8_t len) +{ + return 0; +} + +/* send sysinfo event to other layers */ +static int gsm48_send_sysinfo(struct osmocom_ms *ms, uint8_t type) +{ + struct msgb *nmsg; + struct gsm322_msg *em; + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SYSINFO); + if (!nmsg) + return -ENOMEM; + em = (struct gsm322_msg *) nmsg->data; + em->sysinfo = type; + gsm322_cs_sendmsg(ms, nmsg); + + /* send timer info to location update process */ + nmsg = gsm48_mmevent_msgb_alloc(GSM48_MM_EVENT_SYSINFO); + if (!nmsg) + return -ENOMEM; + gsm48_mmevent_msg(ms, nmsg); + + return 0; +} + +/* receive "SYSTEM INFORMATION 1" message (9.1.31) */ +static int gsm48_rr_rx_sysinfo1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_1 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 1 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 1 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si1_msg, MIN(msgb_l3len(msg), sizeof(s->si1_msg)))) + return 0; + memcpy(s->si1_msg, si, MIN(msgb_l3len(msg), sizeof(s->si1_msg))); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 1\n"); + + /* Cell Channel Description */ + gsm48_decode_freq_list(&ms->support, s->freq, + si->cell_channel_description, + sizeof(si->cell_channel_description), 0xce, FREQ_TYPE_SERV); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_param(s, &si->rach_control); + /* SI 1 Rest Octets */ + if (payload_len) + gsm48_decode_si1_rest(s, si->rest_octets, payload_len); + + s->si1 = 1; + + return gsm48_send_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 2" message (9.1.32) */ +static int gsm48_rr_rx_sysinfo2(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_2 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2 " + "message.\n"); + return -EINVAL; + } +//printf("len = %d\n", MIN(msgb_l3len(msg), sizeof(s->si2_msg))); + + if (!memcmp(si, s->si2_msg, MIN(msgb_l3len(msg), sizeof(s->si2_msg)))) + return 0; + memcpy(s->si2_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2_msg))); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2\n"); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si2 = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si2 = (si->bcch_frequency_list[0] >> 5) & 1; + gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_NCELL_2); + /* NCC Permitted */ + s->nb_ncc_permitted = si->ncc_permitted; + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_neigh(s, &si->rach_control); + + s->si2 = 1; + + return gsm48_send_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 2bis" message (9.1.33) */ +static int gsm48_rr_rx_sysinfo2bis(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_2bis *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2bis" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2bis " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si2b_msg, MIN(msgb_l3len(msg), + sizeof(s->si2b_msg)))) + return 0; + memcpy(s->si2b_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2b_msg))); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2bis\n"); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si2bis = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si2bis = (si->bcch_frequency_list[0] >> 5) & 1; + gsm48_decode_freq_list(&ms->support, s->freq, + si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0x8e, + FREQ_TYPE_NCELL_2bis); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_neigh(s, &si->rach_control); + + s->si2bis = 1; + + return gsm48_send_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 2ter" message (9.1.34) */ +static int gsm48_rr_rx_sysinfo2ter(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_2ter *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 2ter" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 2ter " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si2t_msg, MIN(msgb_l3len(msg), + sizeof(s->si2t_msg)))) + return 0; + memcpy(s->si2t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si2t_msg))); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 2ter\n"); + + /* Neighbor Cell Description 2 */ + s->nb_multi_rep_si2ter = (si->ext_bcch_frequency_list[0] >> 6) & 3; + gsm48_decode_freq_list(&ms->support, s->freq, + si->ext_bcch_frequency_list, + sizeof(si->ext_bcch_frequency_list), 0x8e, + FREQ_TYPE_NCELL_2ter); + + s->si2ter = 1; + + return gsm48_send_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 3" message (9.1.35) */ +static int gsm48_rr_rx_sysinfo3(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_3 *si = msgb_l3(msg); + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 3 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 3 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si3_msg, MIN(msgb_l3len(msg), sizeof(s->si3_msg)))) + return 0; + memcpy(s->si3_msg, si, MIN(msgb_l3len(msg), sizeof(s->si3_msg))); + + /* Cell Identity */ + s->cell_id = ntohs(si->cell_identity); + /* LAI */ + gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac); + /* Control Channel Description */ + gsm48_decode_ccd(s, &si->control_channel_desc); + /* Cell Options (BCCH) */ + gsm48_decode_cellopt_bcch(s, &si->cell_options); + /* Cell Selection Parameters */ + gsm48_decode_cell_sel_param(s, &si->cell_sel_par); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_param(s, &si->rach_control); + /* SI 3 Rest Octets */ + if (payload_len >= 4) + gsm48_decode_si3_rest(s, si->rest_octets, payload_len); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 3 (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + + s->si3 = 1; + + if (cs->ccch_mode == CCCH_MODE_NONE) { + cs->ccch_mode = (s->ccch_conf == 1) ? CCCH_MODE_COMBINED : + CCCH_MODE_NON_COMBINED; + LOGP(DRR, LOGL_NOTICE, "Changing CCCH_MODE to %d\n", + cs->ccch_mode); + l1ctl_tx_ccch_mode_req(ms, cs->ccch_mode); + } + + return gsm48_send_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 4" message (9.1.36) */ +static int gsm48_rr_rx_sysinfo4(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_4 *si = msgb_l3(msg); + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si); + uint8_t *data = si->data; + struct gsm48_chan_desc *cd; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 4 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + short_read: + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 4 " + "message.\n"); + return -EINVAL; + } + + if (!s->si1) { + LOGP(DRR, LOGL_NOTICE, "Ignoring SYSTEM INFORMATION 4 " + "until SI 1 is received.\n"); + return -EBUSY; + } + + if (!memcmp(si, s->si4_msg, MIN(msgb_l3len(msg), sizeof(s->si4_msg)))) + return 0; + memcpy(s->si4_msg, si, MIN(msgb_l3len(msg), sizeof(s->si4_msg))); + + /* LAI */ + gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac); + /* Cell Selection Parameters */ + gsm48_decode_cell_sel_param(s, &si->cell_sel_par); + /* RACH Control Parameter */ + gsm48_decode_rach_ctl_param(s, &si->rach_control); + /* CBCH Channel Description */ + if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_CHAN_DESC) { + if (payload_len < 4) + goto short_read; + cd = (struct gsm48_chan_desc *) (data + 1); + if (cd->h0.h) { + s->h = 1; + gsm48_decode_chan_h1(cd, &s->tsc, &s->maio, &s->hsn); + } else { + s->h = 0; + gsm48_decode_chan_h0(cd, &s->tsc, &s->arfcn); + } + payload_len -= 4; + data += 4; + } + /* CBCH Mobile Allocation */ + if (payload_len >= 1 && data[0] == GSM48_IE_CBCH_MOB_AL) { + if (payload_len < 1 || payload_len < 2 + data[1]) + goto short_read; + gsm48_decode_mobile_alloc(s, data + 2, si->data[1], s->hopping, + &s->hopp_len, 1); + payload_len -= 2 + data[1]; + data += 2 + data[1]; + } + /* SI 4 Rest Octets */ + if (payload_len > 0) + gsm48_decode_si4_rest(s, data, payload_len); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 4 (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + + s->si4 = 1; + + return gsm48_send_sysinfo(ms, si->header.system_information); +} + +/* receive "SYSTEM INFORMATION 5" message (9.1.37) */ +static int gsm48_rr_rx_sysinfo5(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5 *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si5_msg, MIN(msgb_l3len(msg), sizeof(s->si5_msg)))) + return 0; + memcpy(s->si5_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5_msg))); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5\n"); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si5 = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si5 = (si->bcch_frequency_list[0] >> 5) & 1; + gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5); + + s->si5 = 1; + + return gsm48_send_sysinfo(ms, si->system_information); +} + +/* receive "SYSTEM INFORMATION 5bis" message (9.1.38) */ +static int gsm48_rr_rx_sysinfo5bis(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5bis *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5bis" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5bis " + "message.\n"); + return -EINVAL; + } + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5bis\n"); + + if (!memcmp(si, s->si5b_msg, MIN(msgb_l3len(msg), + sizeof(s->si5b_msg)))) + return 0; + memcpy(s->si5b_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5b_msg))); + + /* Neighbor Cell Description */ + s->nb_ext_ind_si5bis = (si->bcch_frequency_list[0] >> 6) & 1; + s->nb_ba_ind_si5bis = (si->bcch_frequency_list[0] >> 5) & 1; + gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5bis); + + s->si5bis = 1; + + return gsm48_send_sysinfo(ms, si->system_information); +} + +/* receive "SYSTEM INFORMATION 5ter" message (9.1.39) */ +static int gsm48_rr_rx_sysinfo5ter(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_5ter *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 5ter" + " ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 5ter " + "message.\n"); + return -EINVAL; + } + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 5ter\n"); + + if (!memcmp(si, s->si5t_msg, MIN(msgb_l3len(msg), + sizeof(s->si5t_msg)))) + return 0; + memcpy(s->si5t_msg, si, MIN(msgb_l3len(msg), sizeof(s->si5t_msg))); + + /* Neighbor Cell Description */ + gsm48_decode_freq_list(&ms->support, s->freq, si->bcch_frequency_list, + sizeof(si->bcch_frequency_list), 0xce, FREQ_TYPE_REP_5ter); + + s->si5ter = 1; + + return gsm48_send_sysinfo(ms, si->system_information); +} + +/* receive "SYSTEM INFORMATION 6" message (9.1.39) */ +static int gsm48_rr_rx_sysinfo6(struct osmocom_ms *ms, struct msgb *msg) +{ + /* NOTE: pseudo length is not in this structure, so we skip */ + struct gsm48_system_information_type_6 *si = msgb_l3(msg) + 1; + struct gsm48_sysinfo *s = ms->cellsel.si; + int payload_len = msgb_l3len(msg) - sizeof(*si) - 1; + + if (!s) { + LOGP(DRR, LOGL_INFO, "No cell selected, SYSTEM INFORMATION 6 " + "ignored\n"); + return -EINVAL; + } + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of SYSTEM INFORMATION 6 " + "message.\n"); + return -EINVAL; + } + + if (!memcmp(si, s->si6_msg, MIN(msgb_l3len(msg), sizeof(s->si6_msg)))) + return 0; + memcpy(s->si6_msg, si, MIN(msgb_l3len(msg), sizeof(s->si6_msg))); + + /* Cell Identity */ + if (s->si6 && s->cell_id != ntohs(si->cell_identity)) + LOGP(DRR, LOGL_INFO, "Cell ID on SI 6 differs from previous " + "read.\n"); + s->cell_id = ntohs(si->cell_identity); + /* LAI */ + gsm48_decode_lai(&si->lai, &s->mcc, &s->mnc, &s->lac); + /* Cell Options (SACCH) */ + gsm48_decode_cellopt_sacch(s, &si->cell_options); + /* NCC Permitted */ + s->nb_ncc_permitted = si->ncc_permitted; + /* SI 6 Rest Octets */ + if (payload_len >= 4) + gsm48_decode_si6_rest(s, si->rest_octets, payload_len); + + LOGP(DRR, LOGL_INFO, "New SYSTEM INFORMATION 6 (mcc %s mnc %s " + "lac 0x%04x)\n", gsm_print_mcc(s->mcc), + gsm_print_mnc(s->mnc), s->lac); + + s->si6 = 1; + + return gsm48_send_sysinfo(ms, si->system_information); +} + +/* + * paging + */ + +/* paging channel request */ +static int gsm48_rr_chan2cause[4] = { + RR_EST_CAUSE_ANS_PAG_ANY, + RR_EST_CAUSE_ANS_PAG_SDCCH, + RR_EST_CAUSE_ANS_PAG_TCH_F, + RR_EST_CAUSE_ANS_PAG_TCH_ANY +}; + +/* given LV of mobile identity is checked agains ms */ +static int gsm_match_mi(struct osmocom_ms *ms, uint8_t *mi) +{ + char imsi[16]; + uint32_t tmsi; + uint8_t mi_type; + + if (mi[0] < 1) + return 0; + mi_type = mi[1] & GSM_MI_TYPE_MASK; + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + if (mi[0] < 5) + return 0; + memcpy(&tmsi, mi+2, 4); + if (ms->subscr.tmsi == ntohl(tmsi) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", + ntohl(tmsi)); + + return 1; + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(tmsi)); + break; + case GSM_MI_TYPE_IMSI: + gsm48_mi_to_string(imsi, sizeof(imsi), mi + 1, mi[0]); + if (!strcmp(imsi, ms->subscr.imsi)) { + LOGP(DPAG, LOGL_INFO, "IMSI %s matches\n", imsi); + + return 1; + } else + LOGP(DPAG, LOGL_INFO, "IMSI %s (not for us)\n", imsi); + break; + default: + LOGP(DPAG, LOGL_NOTICE, "Paging with unsupported MI type %d.\n", + mi_type); + } + + return 0; +} + +/* 9.1.22 PAGING REQUEST 1 message received */ +static int gsm48_rr_rx_pag_req_1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_paging1 *pa = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*pa); + int chan_1, chan_2; + uint8_t *mi; + + /* empty paging request */ + if (payload_len >= 2 && (pa->data[1] & GSM_MI_TYPE_MASK) == 0) + return 0; + + /* 3.3.1.1.2: ignore paging while not camping on a cell */ + if (rr->state != GSM48_RR_ST_IDLE || !cs->selected + || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); + + return 0; + } + LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 1\n"); + + if (payload_len < 2) { + short_read: + LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 1 " + "message.\n"); + + return -EINVAL; + } + + /* channel needed */ + chan_1 = pa->cneed1; + chan_2 = pa->cneed2; + /* first MI */ + mi = pa->data; + if (payload_len < mi[0] + 1) + goto short_read; + if (gsm_match_mi(ms, mi) > 0) + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1); + /* second MI */ + payload_len -= mi[0] + 1; + mi = pa->data + mi[0] + 1; + if (payload_len < 2) + return 0; + if (mi[0] != GSM48_IE_MOBILE_ID) + return 0; + if (payload_len < mi[1] + 2) + goto short_read; + if (gsm_match_mi(ms, mi + 1) > 0) + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1); + + return 0; +} + +/* 9.1.23 PAGING REQUEST 2 message received */ +static int gsm48_rr_rx_pag_req_2(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_paging2 *pa = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*pa); + uint8_t *mi; + int chan_1, chan_2, chan_3; + + /* 3.3.1.1.2: ignore paging while not camping on a cell */ + if (rr->state != GSM48_RR_ST_IDLE || !cs->selected + || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); + + return 0; + } + LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 2\n"); + + if (payload_len < 0) { + short_read: + LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 2 " + "message .\n"); + + return -EINVAL; + } + + /* channel needed */ + chan_1 = pa->cneed1; + chan_2 = pa->cneed2; + /* first MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi1) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi1)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1); + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(pa->tmsi1)); + /* second MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi2) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi2)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1); + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(pa->tmsi2)); + /* third MI */ + mi = pa->data; + if (payload_len < 2) + return 0; + if (mi[0] != GSM48_IE_MOBILE_ID) + return 0; + if (payload_len < mi[1] + 2 + 1) /* must include "channel needed" */ + goto short_read; + chan_3 = mi[mi[1] + 2] & 0x03; /* channel needed */ + if (gsm_match_mi(ms, mi + 1) > 0) + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1); + + return 0; +} + +/* 9.1.24 PAGING REQUEST 3 message received */ +static int gsm48_rr_rx_pag_req_3(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_paging3 *pa = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*pa); + int chan_1, chan_2, chan_3, chan_4; + + /* 3.3.1.1.2: ignore paging while not camping on a cell */ + if (rr->state != GSM48_RR_ST_IDLE || !cs->selected + || (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL)) { + LOGP(DRR, LOGL_INFO, "PAGING ignored, we are not camping.\n"); + + return 0; + } + LOGP(DPAG, LOGL_INFO, "PAGING REQUEST 3\n"); + + if (payload_len < 0) { /* must include "channel needed", part of *pa */ + LOGP(DRR, LOGL_NOTICE, "Short read of PAGING REQUEST 3 " + "message .\n"); + + return -EINVAL; + } + + /* channel needed */ + chan_1 = pa->cneed1; + chan_2 = pa->cneed2; + chan_3 = pa->cneed3; + chan_4 = pa->cneed4; + /* first MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi1) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi1)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_1], 1); + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(pa->tmsi1)); + /* second MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi2) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi2)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_2], 1); + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(pa->tmsi2)); + /* thrid MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi3) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi3)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_3], 1); + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(pa->tmsi3)); + /* fourth MI */ + if (ms->subscr.tmsi == ntohl(pa->tmsi4) + && ms->subscr.tmsi_valid) { + LOGP(DPAG, LOGL_INFO, "TMSI %08x matches\n", ntohl(pa->tmsi4)); + return gsm48_rr_chan_req(ms, gsm48_rr_chan2cause[chan_4], 1); + } else + LOGP(DPAG, LOGL_INFO, "TMSI %08x (not for us)\n", + ntohl(pa->tmsi4)); + + return 0; +} + +/* + * (immediate) assignment + */ + +/* match request reference agains request history */ +static int gsm48_match_ra(struct osmocom_ms *ms, struct gsm48_req_ref *ref) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + int i; + struct gsm_time tm; + uint8_t ia_t1, ia_t2, ia_t3; + + for (i = 0; i < 3; i++) { + /* filter confirmed RACH requests only */ + if (rr->cr_hist[i].valid == 2 + && ref->ra == rr->cr_hist[i].chan_req) { + ia_t1 = ref->t1; + ia_t2 = ref->t2; + ia_t3 = (ref->t3_high << 3) | ref->t3_low; + gsm_fn2gsmtime(&tm, rr->cr_hist[i].fn); + if (ia_t1 == (tm.t1 & 0x1f) && ia_t2 == tm.t2 + && ia_t3 == tm.t3) { + LOGP(DRR, LOGL_INFO, "request %02x matches " + "(fn=%d,%d,%d)\n", ref->ra, ia_t1, + ia_t2, ia_t3); + return 1; + } else + LOGP(DRR, LOGL_INFO, "request %02x matches " + "but not frame number (IMM.ASS " + "fn=%d,%d,%d != RACH fn=%d,%d,%d)\n", + ref->ra, ia_t1, ia_t2, ia_t3, + tm.t1 & 0x1f, tm.t2, tm.t3); + } + } + + return 0; +} + +/* 9.1.18 IMMEDIATE ASSIGNMENT is received */ +static int gsm48_rr_rx_imm_ass(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_imm_ass *ia = msgb_l3(msg); + int ma_len = msgb_l3len(msg) - sizeof(*ia); + uint8_t ch_type, ch_subch, ch_ts; + struct gsm48_rr_cd cd; + uint8_t *st, st_len; + + memset(&cd, 0, sizeof(cd)); + + if (ma_len < 0 /* mobile allocation IE must be included */ + || ia->mob_alloc_len > ma_len) { /* short read of IE */ + LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " + "message.\n"); + return -EINVAL; + } + if (ia->mob_alloc_len > 8) { + LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE " + "ASSIGNMENT too large.\n"); + return -EINVAL; + } + + /* starting time */ + st_len = ma_len - ia->mob_alloc_len; + st = ia->mob_alloc + ia->mob_alloc_len; + if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) + gsm48_decode_start_time(&cd, (struct gsm48_start_time *)(st+1)); + + /* decode channel description */ + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT:\n"); + cd.chan_nr = ia->chan_desc.chan_nr; + rsl_dec_chan_nr(cd.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ia->chan_desc.h0.h) { + cd.h = 1; + gsm48_decode_chan_h1(&ia->chan_desc, &cd.tsc, &cd.maio, + &cd.hsn); + LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " + "MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ia->timing_advance, + ia->timing_advance * GSM_TA_CM / 100, + ia->req_ref.ra, ia->chan_desc.chan_nr, cd.maio, + cd.hsn, ch_ts, ch_subch, cd.tsc); + } else { + cd.h = 0; + gsm48_decode_chan_h0(&ia->chan_desc, &cd.tsc, &cd.arfcn); + LOGP(DRR, LOGL_INFO, " (ta %d/%dm ra 0x%02x chan_nr 0x%02x " + "ARFCN %u TS %u SS %u TSC %u)\n", + ia->timing_advance, + ia->timing_advance * GSM_TA_CM / 100, + ia->req_ref.ra, ia->chan_desc.chan_nr, cd.arfcn, + ch_ts, ch_subch, cd.tsc); + } + + /* 3.3.1.1.2: ignore assignment while idle */ + if (rr->state != GSM48_RR_ST_CONN_PEND || !rr->wait_assign) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + /* request ref */ + if (gsm48_match_ra(ms, &ia->req_ref)) { + /* channel description */ + memcpy(&rr->cd_now, &cd, sizeof(rr->cd_now)); + /* timing advance */ + rr->cd_now.ta = ia->timing_advance; + /* mobile allocation */ + memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, + ia->mob_alloc_len + 1); + rr->wait_assign = 0; + return gsm48_rr_dl_est(ms); + } + LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); + + return 0; +} + +/* 9.1.19 IMMEDIATE ASSIGNMENT EXTENDED is received */ +static int gsm48_rr_rx_imm_ass_ext(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_imm_ass_ext *ia = msgb_l3(msg); + int ma_len = msgb_l3len(msg) - sizeof(*ia); + uint8_t ch_type, ch_subch, ch_ts; + struct gsm48_rr_cd cd1, cd2; + uint8_t *st, st_len; + + memset(&cd1, 0, sizeof(cd1)); + memset(&cd2, 0, sizeof(cd2)); + + if (ma_len < 0 /* mobile allocation IE must be included */ + || ia->mob_alloc_len > ma_len) { /* short read of IE */ + LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " + "EXTENDED message.\n"); + return -EINVAL; + } + if (ia->mob_alloc_len > 4) { + LOGP(DRR, LOGL_NOTICE, "Moble allocation in IMMEDIATE " + "ASSIGNMENT EXTENDED too large.\n"); + return -EINVAL; + } + + /* starting time */ + st_len = ma_len - ia->mob_alloc_len; + st = ia->mob_alloc + ia->mob_alloc_len; + if (st_len >= 3 && st[0] == GSM48_IE_START_TIME) { + gsm48_decode_start_time(&cd1, + (struct gsm48_start_time *)(st+1)); + memcpy(&cd2, &cd1, sizeof(cd2)); + } + + /* decode channel description */ + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT EXTENDED:\n"); + cd2.chan_nr = ia->chan_desc1.chan_nr; + rsl_dec_chan_nr(cd1.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ia->chan_desc1.h0.h) { + cd1.h = 1; + gsm48_decode_chan_h1(&ia->chan_desc1, &cd1.tsc, &cd1.maio, + &cd1.hsn); + LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ia->timing_advance1, + ia->timing_advance1 * GSM_TA_CM / 100, + ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.maio, + cd1.hsn, ch_ts, ch_subch, cd1.tsc); + } else { + cd1.h = 0; + gsm48_decode_chan_h0(&ia->chan_desc1, &cd1.tsc, &cd1.arfcn); + LOGP(DRR, LOGL_INFO, " assignment 1 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n", + ia->timing_advance1, + ia->timing_advance1 * GSM_TA_CM / 100, + ia->req_ref1.ra, ia->chan_desc1.chan_nr, cd1.arfcn, + ch_ts, ch_subch, cd1.tsc); + } + cd2.chan_nr = ia->chan_desc2.chan_nr; + rsl_dec_chan_nr(cd2.chan_nr, &ch_type, &ch_subch, &ch_ts); + if (ia->chan_desc2.h0.h) { + cd2.h = 1; + gsm48_decode_chan_h1(&ia->chan_desc2, &cd2.tsc, &cd2.maio, + &cd2.hsn); + LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x MAIO %u HSN %u TS %u SS %u TSC %u)\n", + ia->timing_advance2, + ia->timing_advance2 * GSM_TA_CM / 100, + ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.maio, + cd2.hsn, ch_ts, ch_subch, cd2.tsc); + } else { + cd2.h = 0; + gsm48_decode_chan_h0(&ia->chan_desc2, &cd2.tsc, &cd2.arfcn); + LOGP(DRR, LOGL_INFO, " assignment 2 (ta %d/%dm ra 0x%02x " + "chan_nr 0x%02x ARFCN %u TS %u SS %u TSC %u)\n", + ia->timing_advance2, + ia->timing_advance2 * GSM_TA_CM / 100, + ia->req_ref2.ra, ia->chan_desc2.chan_nr, cd2.arfcn, + ch_ts, ch_subch, cd2.tsc); + } + + /* 3.3.1.1.2: ignore assignment while idle */ + if (rr->state != GSM48_RR_ST_CONN_PEND || !rr->wait_assign) { + LOGP(DRR, LOGL_INFO, "Not for us, no request.\n"); + return 0; + } + + /* request ref 1 */ + if (gsm48_match_ra(ms, &ia->req_ref1)) { + /* channel description */ + memcpy(&rr->cd_now, &cd1, sizeof(rr->cd_now)); + /* timing advance */ + rr->cd_now.ta = ia->timing_advance1; + /* mobile allocation */ + memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, + ia->mob_alloc_len + 1); + rr->wait_assign = 0; + return gsm48_rr_dl_est(ms); + } + /* request ref 1 */ + if (gsm48_match_ra(ms, &ia->req_ref2)) { + /* channel description */ + memcpy(&rr->cd_now, &cd2, sizeof(rr->cd_now)); + /* timing advance */ + rr->cd_now.ta = ia->timing_advance2; + /* mobile allocation */ + memcpy(&rr->cd_now.mob_alloc_lv, &ia->mob_alloc_len, + ia->mob_alloc_len + 1); + rr->wait_assign = 0; + return gsm48_rr_dl_est(ms); + } + LOGP(DRR, LOGL_INFO, "Request, but not for us.\n"); + + return 0; +} + +/* 9.1.20 IMMEDIATE ASSIGNMENT REJECT is received */ +static int gsm48_rr_rx_imm_ass_rej(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_imm_ass_rej *ia = msgb_l3(msg); + int payload_len = msgb_l3len(msg) - sizeof(*ia); + int i; + struct gsm48_req_ref *req_ref; + uint8_t t3122_value; + + /* 3.3.1.1.2: ignore assignment while idle */ + if (rr->state != GSM48_RR_ST_CONN_PEND || !rr->wait_assign) + return 0; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of IMMEDIATE ASSIGNMENT " + "REJECT message.\n"); + return -EINVAL; + } + + for (i = 0; i < 4; i++) { + /* request reference */ + req_ref = (struct gsm48_req_ref *) + (((uint8_t *)&ia->req_ref1) + i * 4); + LOGP(DRR, LOGL_INFO, "IMMEDIATE ASSIGNMENT REJECT " + "(ref 0x%02x)\n", req_ref->ra); + if (gsm48_match_ra(ms, req_ref)) { + /* wait indication */ + t3122_value = *(((uint8_t *)&ia->wait_ind1) + i * 4); + if (t3122_value) + start_rr_t3122(rr, t3122_value, 0); + /* start timer 3126 if not already */ + if (!bsc_timer_pending(&rr->t3126)) + start_rr_t3126(rr, 5, 0); /* TODO improve! */ + /* stop assignmnet requests */ + rr->n_chan_req = 0; + + /* wait until timer 3126 expires, then release + * or wait for channel assignment */ + return 0; + } + } + + return 0; +} + +/* 9.1.1 ADDITIONAL ASSIGMENT is received */ +static int gsm48_rr_rx_add_ass(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_add_ass *aa = (struct gsm48_add_ass *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*aa); + struct tlv_parsed tp; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of ADDITIONAL ASSIGNMENT " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, aa->data, payload_len, 0, 0); + + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); +} + +/* + * measturement reports + */ + +static int gsm48_rr_tx_meas_rep(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_rr_meas *meas = &rr->meas; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_meas_res *mr; + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + mr = (struct gsm48_meas_res *) msgb_put(nmsg, sizeof(*mr)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_MEAS_REP; + + /* measurement results */ + mr->rxlev_full = meas->rxlev_full; + mr->rxlev_sub = meas->rxlev_sub; + mr->rxqual_full = meas->rxqual_full; + mr->rxqual_sub = meas->rxqual_sub; + mr->dtx_used = meas->dtx; + mr->ba_used = meas->ba; + mr->meas_valid = meas->meas_valid; + if (meas->ncell_na) { + /* no results for serving cells */ + mr->no_nc_n_hi = 1; + mr->no_nc_n_lo = 3; + } else { + mr->no_nc_n_hi = meas->count >> 2; + mr->no_nc_n_lo = meas->count & 3; + } + mr->rxlev_nc1 = meas->rxlev_nc[0]; + mr->rxlev_nc2_hi = meas->rxlev_nc[1] >> 1; + mr->rxlev_nc2_lo = meas->rxlev_nc[1] & 1; + mr->rxlev_nc3_hi = meas->rxlev_nc[2] >> 2; + mr->rxlev_nc3_lo = meas->rxlev_nc[2] & 3; + mr->rxlev_nc4_hi = meas->rxlev_nc[3] >> 3; + mr->rxlev_nc4_lo = meas->rxlev_nc[3] & 7; + mr->rxlev_nc5_hi = meas->rxlev_nc[4] >> 4; + mr->rxlev_nc5_lo = meas->rxlev_nc[4] & 15; + mr->rxlev_nc6_hi = meas->rxlev_nc[5] >> 5; + mr->rxlev_nc6_lo = meas->rxlev_nc[5] & 31; + mr->bsic_nc1_hi = meas->bsic_nc[0] >> 3; + mr->bsic_nc1_lo = meas->bsic_nc[0] & 7; + mr->bsic_nc2_hi = meas->bsic_nc[1] >> 4; + mr->bsic_nc2_lo = meas->bsic_nc[1] & 15; + mr->bsic_nc3_hi = meas->bsic_nc[2] >> 5; + mr->bsic_nc3_lo = meas->bsic_nc[2] & 31; + mr->bsic_nc4 = meas->bsic_nc[3]; + mr->bsic_nc5 = meas->bsic_nc[4]; + mr->bsic_nc6 = meas->bsic_nc[5]; + mr->bcch_f_nc1 = meas->bcch_f_nc[0]; + mr->bcch_f_nc2 = meas->bcch_f_nc[1]; + mr->bcch_f_nc3 = meas->bcch_f_nc[2]; + mr->bcch_f_nc4 = meas->bcch_f_nc[3]; + mr->bcch_f_nc5_hi = meas->bcch_f_nc[4] >> 1; + mr->bcch_f_nc5_lo = meas->bcch_f_nc[4] & 1; + mr->bcch_f_nc6_hi = meas->bcch_f_nc[5] >> 2; + mr->bcch_f_nc6_lo = meas->bcch_f_nc[5] & 3; + + return gsm48_send_rsl(ms, RSL_MT_UNIT_DATA_REQ, nmsg); +} + +/* + * link establishment and release + */ + +/* process "Loss Of Signal" */ +int gsm48_rr_los(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint8_t *mode; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + LOGP(DSUM, LOGL_INFO, "Radio link lost signal\n"); + + /* stop T3211 if running */ + stop_rr_t3110(rr); + + switch(rr->state) { + case GSM48_RR_ST_CONN_PEND: + LOGP(DRR, LOGL_INFO, "LOS during RACH request\n"); + + /* stop pending RACH timer */ + stop_rr_t3126(rr); + break; + case GSM48_RR_ST_DEDICATED: + LOGP(DRR, LOGL_INFO, "LOS during dedicated mode, release " + "locally\n"); + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* release message */ + rel_local: + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 1; /* local release */ + /* start release */ + return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg); + case GSM48_RR_ST_REL_PEND: + LOGP(DRR, LOGL_INFO, "LOS during RR release procedure, release " + "locally\n"); + + /* stop pending RACH timer */ + stop_rr_t3110(rr); + + /* release locally */ + goto rel_local; + default: + /* this should not happen */ + LOGP(DRR, LOGL_ERROR, "LOS in IDLE state, ignoring\n"); + return -EINVAL; + } + + /* send inication to upper layer */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_LOST_SIGNAL; + gsm48_rr_upmsg(ms, nmsg); + + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* activate link and send establish request */ +static int gsm48_rr_dl_est(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_pag_rsp *pr; + uint8_t mi[11]; + uint8_t ch_type, ch_subch, ch_ts; + uint16_t ma[64]; + uint8_t ma_len; + + /* 3.3.1.1.3.1 */ + stop_rr_t3126(rr); + + if (rr->cd_now.h) { + gsm48_decode_mobile_alloc(s, rr->cd_now.mob_alloc_lv + 1, + rr->cd_now.mob_alloc_lv[0], ma, &ma_len, 0); + if (ma_len < 1) { + LOGP(DRR, LOGL_NOTICE, "Hopping, but no allocation\n"); + return -EINVAL; + } + } + + /* send DL_EST_REQ */ + if (rr->rr_est_msg) { + /* use queued message */ + nmsg = rr->rr_est_msg; + rr->rr_est_msg = 0; + LOGP(DRR, LOGL_INFO, "sending establish message\n"); + } else { + /* create paging response */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + pr = (struct gsm48_pag_rsp *) msgb_put(nmsg, sizeof(*pr)); + /* key sequence */ + pr->key_seq = subscr->key_seq; + /* classmark 2 */ + pr->cm2_len = sizeof(pr->cm2); + gsm48_rr_enc_cm2(ms, &pr->cm2); + /* mobile identity */ + if (ms->subscr.tmsi_valid) { + gsm48_generate_mid_from_tmsi(mi, subscr->tmsi); + LOGP(DRR, LOGL_INFO, "sending paging response with " + "TMSI\n"); + } else if (subscr->imsi[0]) { + gsm48_generate_mid_from_imsi(mi, subscr->imsi); + LOGP(DRR, LOGL_INFO, "sending paging response with " + "IMSI\n"); + } else { + mi[1] = 1; + mi[2] = 0xf0 | GSM_MI_TYPE_NONE; + LOGP(DRR, LOGL_INFO, "sending paging response without " + "TMSI/IMSI\n"); + } + msgb_put(nmsg, 1 + mi[1]); + memcpy(pr->data, mi + 1, 1 + mi[1]); + } + + /* activate channel */ +#ifdef TODO + RSL_MT_ to activate channel with all the cd_now informations +#else + rsl_dec_chan_nr(rr->cd_now.chan_nr, &ch_type, &ch_subch, &ch_ts); + if ((ch_type != RSL_CHAN_SDCCH8_ACCH + && ch_type != RSL_CHAN_SDCCH4_ACCH) || ch_ts > 4) { + printf("Channel type %d, subch %d, ts %d not supported, " + "exitting.\n", ch_type, ch_subch, ch_ts); + exit(-ENOTSUP); + } + if (rr->cd_now.h) + tx_ph_dm_est_req_h1(ms, rr->cd_now.maio, rr->cd_now.hsn, + ma, ma_len, rr->cd_now.chan_nr, rr->cd_now.tsc, 0); + else + tx_ph_dm_est_req_h0(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr, + rr->cd_now.tsc, 0); +#endif + + /* start establishmnet */ + return gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg); +} + +/* the link is established */ +static int gsm48_rr_estab_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint8_t *mode; + struct msgb *nmsg; + + /* if MM has releases before confirm, we start release */ + if (rr->state == GSM48_RR_ST_REL_PEND) { + LOGP(DRR, LOGL_INFO, "MM already released RR.\n"); + /* release message */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 0; /* normal release */ + /* start release */ + return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg); + } + + /* 3.3.1.1.4 */ + new_rr_state(rr, GSM48_RR_ST_DEDICATED); + + /* send confirm to upper layer */ + nmsg = gsm48_rr_msgb_alloc( + (rr->rr_est_req) ? GSM48_RR_EST_CNF : GSM48_RR_EST_IND); + if (!nmsg) + return -ENOMEM; + return gsm48_rr_upmsg(ms, nmsg); +} + +/* the link is released in pending state (by l2) */ +static int gsm48_rr_rel_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + LOGP(DSUM, LOGL_INFO, "Radio link is released\n"); + + /* send inication to upper layer */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_NORMAL; + gsm48_rr_upmsg(ms, nmsg); + + /* start release timer, so UA will be transmitted */ + start_rr_t_rel_wait(rr, 1, 500000); + + /* pending release */ + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + return 0; +} + +/* 9.1.7 CHANNEL RELEASE is received */ +static int gsm48_rr_rx_chan_rel(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_rel *cr = (struct gsm48_chan_rel *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*cr); + struct tlv_parsed tp; + struct msgb *nmsg; + uint8_t *mode; + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of CHANNEL RELEASE " + "message.\n"); + return gsm48_rr_tx_rr_status(ms, + GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, cr->data, payload_len, 0, 0); + + LOGP(DRR, LOGL_INFO, "channel release request with cause 0x%02x)\n", + cr->rr_cause); + + /* BA range */ + if (TLVP_PRESENT(&tp, GSM48_IE_BA_RANGE)) { + gsm48_decode_ba_range(TLVP_VAL(&tp, GSM48_IE_BA_RANGE), + *(TLVP_VAL(&tp, GSM48_IE_BA_RANGE) - 1), rr->ba_range, + &rr->ba_ranges, + sizeof(rr->ba_range) / sizeof(rr->ba_range[0])); + /* NOTE: the ranges are kept until IDLE state is returned + * (see new_rr_state) + */ + } + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* start T3110, so that two DISCs can be sent due to T200 timeout */ + start_rr_t3110(rr, 1, 500000); + + /* disconnect the main signalling link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 0; /* normal release */ + return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg); +} + +/* + * assignment and handover + */ + +/* 9.1.3 sending ASSIGNMENT COMPLETE */ +static int gsm48_rr_tx_ass_cpl(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_ass_cpl *ac; + + LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMPLETE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + ac = (struct gsm48_ass_cpl *) msgb_put(nmsg, sizeof(*ac)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_COMPL; + + /* RR_CAUSE */ + ac->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* 9.1.4 sending ASSIGNMENT FAILURE */ +static int gsm48_rr_tx_ass_fail(struct osmocom_ms *ms, uint8_t cause) +{ + struct msgb *nmsg; + struct gsm48_hdr *gh; + struct gsm48_ass_fail *af; + + LOGP(DRR, LOGL_INFO, "ASSIGNMENT FAILURE (cause #%d)\n", cause); + + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gh = (struct gsm48_hdr *) msgb_put(nmsg, sizeof(*gh)); + af = (struct gsm48_ass_fail *) msgb_put(nmsg, sizeof(*af)); + + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_COMPL; + + /* RR_CAUSE */ + af->rr_cause = cause; + + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, nmsg); +} + +/* 9.1.2 ASSIGNMENT COMMAND is received */ +static int gsm48_rr_rx_ass_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ +// struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_ass_cmd *ac = (struct gsm48_ass_cmd *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - sizeof(*ac); + struct tlv_parsed tp; + struct gsm48_rr_cd cd; + + LOGP(DRR, LOGL_INFO, "ASSIGNMENT COMMAND\n"); + + memset(&cd, 0, sizeof(cd)); + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of ASSIGNMENT COMMAND message.\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, ac->data, payload_len, 0, 0); + +#if 0 + /* channel description */ + memcpy(&cd.chan_desc, &ac->chan_desc, sizeof(chan_desc)); + /* power command */ + cd.power_command = ac->power_command; + /* frequency list, after timer */ + tlv_copy(&cd.fl, sizeof(fl_after), &tp, GSM48_IE_FRQLIST_AFTER); + /* cell channel description */ + tlv_copy(&cd.ccd, sizeof(ccd), &tp, GSM48_IE_CELL_CH_DESC); + /* multislot allocation */ + tlv_copy(&cd.multia, sizeof(ma), &tp, GSM48_IE_MSLOT_DESC); + /* channel mode */ + tlv_copy(&cd.chanmode, sizeof(chanmode), &tp, GSM48_IE_CHANMODE_1); + /* mobile allocation, after time */ + tlv_copy(&cd.moba_after, sizeof(moba_after), &tp, GSM48_IE_MOB_AL_AFTER); + /* starting time */ + tlv_copy(&cd.start, sizeof(start), &tp, GSM_IE_START_TIME); + /* frequency list, before time */ + tlv_copy(&cd.fl_before, sizeof(fl_before), &tp, GSM48_IE_FRQLIST_BEFORE); + /* channel description, before time */ + tlv_copy(&cd.chan_desc_before, sizeof(cd_before), &tp, GSM48_IE_CHDES_1_BEFORE); + /* frequency channel sequence, before time */ + tlv_copy(&cd.fcs_before, sizeof(fcs_before), &tp, GSM48_IE_FRQSEQ_BEFORE); + /* mobile allocation, before time */ + tlv_copy(&cd.moba_before, sizeof(moba_before), &tp, GSM48_IE_MOB_AL_BEFORE); + /* cipher mode setting */ + if (TLVP_PRESENT(&tp, GSM48_IE_CIP_MODE_SET)) + cd.cipher = *TLVP_VAL(&tp, GSM48_IE_CIP_MODE_SET); + else + cd.cipher = 0; + + if (no CA) { + LOGP(DRR, LOGL_INFO, "No current cell allocation available.\n"); + return gsm48_rr_tx_ass_fail(ms, GSM48_GSM48_RR_CAUSE_NO_CELL_ALLOC_A); + } + + if (not supported) { + LOGP(DRR, LOGL_INFO, "New channel is not supported.\n"); + return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_CHAN_MODE_UNACCEPT); + } + + if (freq not supported) { + LOGP(DRR, LOGL_INFO, "New frequency is not supported.\n"); + return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL); + } + + /* store current channel descriptions, to return in case of failure */ + memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd)); + /* copy new description */ + memcpy(&rr->chan_desc, cd, sizeof(cd)); + + /* start suspension of current link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg); + + /* change into special assignment suspension state */ + rr->assign_susp_state = 1; + rr->resume_last_state = 0; +#else + return gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_FREQ_NOT_IMPL); +#endif + + return 0; +} + +/* + * radio ressource requests + */ + +/* establish request for dedicated mode */ +static int gsm48_rr_est_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm322_cellsel *cs = &ms->cellsel; + struct gsm48_sysinfo *s = cs->si; + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t cause; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + uint16_t acc_class; + + /* 3.3.1.1.3.2 */ + if (bsc_timer_pending(&rr->t3122)) { + if (rrh->cause != RR_EST_CAUSE_EMERGENCY) { + LOGP(DRR, LOGL_INFO, "T3122 running, rejecting!\n"); + cause = RR_REL_CAUSE_T3122; + reject: + LOGP(DSUM, LOGL_INFO, "Establishing radio link not " + "possible\n"); + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = cause; + return gsm48_rr_upmsg(ms, nmsg); + } + LOGP(DRR, LOGL_INFO, "T3122 running, but emergency call\n"); + stop_rr_t3122(rr); + } + + /* if state is not idle */ + if (rr->state != GSM48_RR_ST_IDLE) { + LOGP(DRR, LOGL_INFO, "We are not IDLE yet, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* cell selected */ + if (!cs->selected) { + LOGP(DRR, LOGL_INFO, "No cell selected, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* check if camping */ + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && rrh->cause != RR_EST_CAUSE_EMERGENCY) { + LOGP(DRR, LOGL_INFO, "Not camping normally, rejecting!\n"); + cause = RR_REL_CAUSE_EMERGENCY_ONLY; + goto reject; + } + if (cs->state != GSM322_C3_CAMPED_NORMALLY + && cs->state != GSM322_C7_CAMPED_ANY_CELL) { + LOGP(DRR, LOGL_INFO, "Not camping, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* check for relevant informations */ + if (!s->si3) { + LOGP(DRR, LOGL_INFO, "Not enough SI, rejecting!\n"); + cause = RR_REL_CAUSE_TRY_LATER; + goto reject; + } + + /* 3.3.1.1.1 */ + if (!subscr->acc_barr && s->cell_barr) { + LOGP(DRR, LOGL_INFO, "Cell barred, rejecting!\n"); + cause = RR_REL_CAUSE_NOT_AUTHORIZED; + goto reject; + } + if (rrh->cause == RR_EST_CAUSE_EMERGENCY) + acc_class = subscr->acc_class | 0x0400; + else + acc_class = subscr->acc_class & 0xfbff; + if (!subscr->acc_barr && !(acc_class & (s->class_barr ^ 0xffff))) { + LOGP(DRR, LOGL_INFO, "Cell barred for our access class (access " + "%04x barred %04x)!\n", acc_class, s->class_barr); + cause = RR_REL_CAUSE_NOT_AUTHORIZED; + goto reject; + } + + /* requested by RR */ + rr->rr_est_req = 1; + + /* clone and store REQUEST message */ + if (!gh) { + LOGP(DRR, LOGL_ERROR, "Error, missing l3 message\n"); + return -EINVAL; + } + rr->rr_est_msg = gsm48_l3_msgb_alloc(); + if (!rr->rr_est_msg) + return -ENOMEM; + memcpy(msgb_put(rr->rr_est_msg, msgb_l3len(msg)), + msgb_l3(msg), msgb_l3len(msg)); + + /* request channel */ + return gsm48_rr_chan_req(ms, rrh->cause, 0); +} + +/* send all queued messages down to layer 2 */ +static int gsm48_rr_dequeue_down(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg; + + while((msg = msgb_dequeue(&rr->downqueue))) { + LOGP(DRR, LOGL_INFO, "Sending queued message.\n"); + gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg); + } + + return 0; +} + +/* 3.4.2 transfer data in dedicated mode */ +static int gsm48_rr_data_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (rr->state != GSM48_RR_ST_DEDICATED) { + msgb_free(msg); + return -EINVAL; + } + + /* pull RR header */ + msgb_pull(msg, sizeof(struct gsm48_rr_hdr)); + + /* queue message, during handover or assignment procedure */ + if (rr->hando_susp_state || rr->assign_susp_state) { + LOGP(DRR, LOGL_INFO, "Queueing message during suspend.\n"); + msgb_enqueue(&rr->downqueue, msg); + return 0; + } + + /* forward message */ + return gsm48_send_rsl(ms, RSL_MT_DATA_REQ, msg); +} + +/* + * data indications from data link + */ + +/* 3.4.2 data from layer 2 to RR and upper layer*/ +static int gsm48_rr_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_rr_hdr *rrh; + uint8_t pdisc = gh->proto_discr & 0x0f; + + if (pdisc == GSM48_PDISC_RR) { + int rc = -EINVAL; + uint8_t skip_ind = (gh->proto_discr & 0xf0) >> 4; + + /* ignore if skip indicator is not B'0000' */ + if (skip_ind) + return 0; + + switch(gh->msg_type) { + case GSM48_MT_RR_ADD_ASS: + rc = gsm48_rr_rx_add_ass(ms, msg); + break; + case GSM48_MT_RR_ASS_CMD: + rc = gsm48_rr_rx_ass_cmd(ms, msg); + break; +#if 0 + case GSM48_MT_RR_CIP_MODE_CMD: + rc = gsm48_rr_rx_cip_mode_cmd(ms, msg); + break; +#endif + case GSM48_MT_RR_CLSM_ENQ: + rc = gsm48_rr_rx_cm_enq(ms, msg); + break; +#if 0 + case GSM48_MT_RR_HANDO_CMD: + rc = gsm48_rr_rx_hando_cmd(ms, msg); + break; + case GSM48_MT_RR_FREQ_REDEF: + rc = gsm48_rr_rx_freq_redef(ms, msg); + break; +#endif + case GSM48_MT_RR_CHAN_REL: + rc = gsm48_rr_rx_chan_rel(ms, msg); + break; + default: + LOGP(DRR, LOGL_NOTICE, "Message type 0x%02x unknown.\n", + gh->msg_type); + + /* status message */ + gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_MSG_TYPE_N); + } + + msgb_free(msg); + return rc; + } + + /* pull off RSL header up to L3 message */ + msgb_pull(msg, (long)msgb_l3(msg) - (long)msg->data); + + /* push RR header */ + msgb_push(msg, sizeof(struct gsm48_rr_hdr)); + rrh = (struct gsm48_rr_hdr *)msg->data; + rrh->msg_type = GSM48_RR_DATA_IND; + + return gsm48_rr_upmsg(ms, msg); +} + +/* receive BCCH at RR layer */ +static int gsm48_rr_rx_bcch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_1: + return gsm48_rr_rx_sysinfo1(ms, msg); + case GSM48_MT_RR_SYSINFO_2: + return gsm48_rr_rx_sysinfo2(ms, msg); + case GSM48_MT_RR_SYSINFO_2bis: + return gsm48_rr_rx_sysinfo2bis(ms, msg); + case GSM48_MT_RR_SYSINFO_2ter: + return gsm48_rr_rx_sysinfo2ter(ms, msg); + case GSM48_MT_RR_SYSINFO_3: + return gsm48_rr_rx_sysinfo3(ms, msg); + case GSM48_MT_RR_SYSINFO_4: + return gsm48_rr_rx_sysinfo4(ms, msg); + default: +#if 0 + LOGP(DRR, LOGL_NOTICE, "BCCH message type 0x%02x not sup.\n", + sih->system_information); +#endif + return -EINVAL; + } +} + +/* receive CCCH at RR layer */ +static int gsm48_rr_rx_pch_agch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + switch (sih->system_information) { + case GSM48_MT_RR_PAG_REQ_1: + return gsm48_rr_rx_pag_req_1(ms, msg); + case GSM48_MT_RR_PAG_REQ_2: + return gsm48_rr_rx_pag_req_2(ms, msg); + case GSM48_MT_RR_PAG_REQ_3: + return gsm48_rr_rx_pag_req_3(ms, msg); + + case GSM48_MT_RR_IMM_ASS: + return gsm48_rr_rx_imm_ass(ms, msg); + case GSM48_MT_RR_IMM_ASS_EXT: + return gsm48_rr_rx_imm_ass_ext(ms, msg); + case GSM48_MT_RR_IMM_ASS_REJ: + return gsm48_rr_rx_imm_ass_rej(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "CCCH message type 0x%02x unknown.\n", + sih->system_information); + return -EINVAL; + } +} + +/* receive ACCH at RR layer */ +static int gsm48_rr_rx_acch(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + + switch (sih->system_information) { + case GSM48_MT_RR_SYSINFO_5: + return gsm48_rr_rx_sysinfo5(ms, msg); + case GSM48_MT_RR_SYSINFO_5bis: + return gsm48_rr_rx_sysinfo5bis(ms, msg); + case GSM48_MT_RR_SYSINFO_5ter: + return gsm48_rr_rx_sysinfo5ter(ms, msg); + case GSM48_MT_RR_SYSINFO_6: + return gsm48_rr_rx_sysinfo6(ms, msg); + default: + LOGP(DRR, LOGL_NOTICE, "ACCH message type 0x%02x unknown.\n", + sih->system_information); + return -EINVAL; + } +} + +/* unit data from layer 2 to RR layer */ +static int gsm48_rr_unit_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm322_cellsel *cs = &ms->cellsel; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + uint8_t ch_type, ch_subch, ch_ts; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EIO; + } + msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); + + if (cs->ccch_state != GSM322_CCCH_ST_SYNC + && cs->ccch_state != GSM322_CCCH_ST_DATA) + return -EINVAL; + + /* when camping, start/reset loss timer */ + if (cs->state == GSM322_C3_CAMPED_NORMALLY + || cs->state == GSM322_C7_CAMPED_ANY_CELL) { + struct gsm48_sysinfo *s = &ms->cellsel.sel_si; +#ifdef TODO + set radio link timeout on layer 1 + it is the number of subsequent BCCH blocks. (about 1/4 seconds) +#else + start_loss_timer(cs, s->bcch_radio_link_timeout / 4, 0); +#endif + } + + /* temporary moved here until confirm is fixed */ + if (cs->ccch_state != GSM322_CCCH_ST_DATA) { + LOGP(DCS, LOGL_INFO, "Channel provides data.\n"); + cs->ccch_state = GSM322_CCCH_ST_DATA; + + /* in dedicated mode */ + if (ms->rrlayer.state == GSM48_RR_ST_CONN_PEND) + return gsm48_rr_tx_rand_acc(ms, NULL); + + /* set timer for reading BCCH */ + if (cs->state == GSM322_C2_STORED_CELL_SEL + || cs->state == GSM322_C1_NORMAL_CELL_SEL + || cs->state == GSM322_C6_ANY_CELL_SEL + || cs->state == GSM322_C4_NORMAL_CELL_RESEL + || cs->state == GSM322_C8_ANY_CELL_RESEL + || cs->state == GSM322_C5_CHOOSE_CELL + || cs->state == GSM322_C9_CHOOSE_ANY_CELL + || cs->state == GSM322_PLMN_SEARCH + || cs->state == GSM322_HPLMN_SEARCH) + start_cs_timer(cs, ms->support.scan_to, 0); + // TODO: timer depends on BCCH config + } + + rsl_dec_chan_nr(rllh->chan_nr, &ch_type, &ch_subch, &ch_ts); + switch (ch_type) { + case RSL_CHAN_PCH_AGCH: + return gsm48_rr_rx_pch_agch(ms, msg); + case RSL_CHAN_BCCH: + return gsm48_rr_rx_bcch(ms, msg); + case RSL_CHAN_SDCCH4_ACCH: + return gsm48_rr_rx_acch(ms, msg); + case RSL_CHAN_SDCCH8_ACCH: + return gsm48_rr_rx_acch(ms, msg); + default: + LOGP(DRSL, LOGL_NOTICE, "RSL with chan_nr 0x%02x unknown.\n", + rllh->chan_nr); + return -EINVAL; + } +} + +/* 3.4.13.3 RR abort in dedicated mode (also in conn. pending mode) */ +static int gsm48_rr_abort_req(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + uint8_t *mode; + + /* stop pending RACH timer */ + stop_rr_t3126(rr); + + /* release "normally" if we are in dedicated mode */ + if (rr->state == GSM48_RR_ST_DEDICATED) { + struct msgb *nmsg; + + LOGP(DRR, LOGL_INFO, "Abort in dedicated state, send release " + "to layer 2.\n"); + + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + /* release message */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + mode = msgb_put(nmsg, 2); + mode[0] = RSL_IE_RELEASE_MODE; + mode[1] = 0; /* normal release */ + return gsm48_send_rsl(ms, RSL_MT_REL_REQ, nmsg); + } + + LOGP(DRR, LOGL_INFO, "Abort in connection pending state, return to " + "idle state.\n"); + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_REL_PEND); + + return 0; +} + +/* release confirm in dedicated mode */ +static int gsm48_rr_susp_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + if (rr->hando_susp_state || rr->assign_susp_state) { + struct msgb *nmsg; + + /* change radio to new channel */ +//todo tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr, +// rr->cd_now.tsc); + + /* send DL-ESTABLISH REQUEST */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gsm48_send_rsl(ms, RSL_MT_EST_REQ, nmsg); + +#ifdef TODO + /* trigger RACH */ + if (rr->hando_susp_state) { + gsm48_rr_tx_hando_access(ms); + rr->hando_acc_left = 3; + } +#endif + } + return 0; +} + +/* release confirm */ +static int gsm48_rr_rel_cnf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + struct gsm48_rr_hdr *nrrh; + + LOGP(DSUM, LOGL_INFO, "Requesting channel aborted\n"); + + /* stop T3211 if running */ + stop_rr_t3110(rr); + + /* send release indication */ + nmsg = gsm48_rr_msgb_alloc(GSM48_RR_REL_IND); + if (!nmsg) + return -ENOMEM; + nrrh = (struct gsm48_rr_hdr *)nmsg->data; + nrrh->cause = RR_REL_CAUSE_NORMAL; + gsm48_rr_upmsg(ms, nmsg); + + /* return idle */ + new_rr_state(rr, GSM48_RR_ST_IDLE); + return 0; +} + +/* + * state machines + */ + +/* state trasitions for link layer messages (lower layer) */ +static struct dldatastate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} dldatastatelist[] = { + /* data transfer */ + {SBIT(GSM48_RR_ST_IDLE) | + SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED) | + SBIT(GSM48_RR_ST_REL_PEND), + RSL_MT_UNIT_DATA_IND, gsm48_rr_unit_data_ind}, + + {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */ + RSL_MT_DATA_IND, gsm48_rr_data_ind}, + + /* esablish */ + {SBIT(GSM48_RR_ST_CONN_PEND), /* 3.3.1.1.2 */ + RSL_MT_CHAN_CNF, gsm48_rr_tx_rand_acc}, + + {SBIT(GSM48_RR_ST_IDLE) | + SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_REL_PEND), + RSL_MT_EST_CONF, gsm48_rr_estab_cnf}, + +#if 0 + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_EST_CONF, gsm48_rr_estab_cnf_dedicated}, + + {SBIT(GSM_RRSTATE), + RSL_MT_CONNECT_CNF, gsm48_rr_connect_cnf}, + +#endif + + /* release */ + {SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_REL_IND, gsm48_rr_rel_ind}, + + {SBIT(GSM48_RR_ST_REL_PEND), + RSL_MT_REL_CONF, gsm48_rr_rel_cnf}, + + /* suspenion */ + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_SUSP_CONF, gsm48_rr_susp_cnf_dedicated}, + +#if 0 + {SBIT(GSM48_RR_ST_DEDICATED), + RSL_MT_CHAN_CNF, gsm48_rr_rand_acc_cnf_dedicated}, + + {SBIT(GSM_RRSTATE), + RSL_MT_MDL_ERROR_IND, gsm48_rr_mdl_error_ind}, +#endif +}; + +#define DLDATASLLEN \ + (sizeof(dldatastatelist) / sizeof(struct dldatastate)) + +static int gsm48_rcv_rsl(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int msg_type = rllh->c.msg_type; + int i; + int rc; + + if (msg_type != RSL_MT_UNIT_DATA_IND) { + LOGP(DRSL, LOGL_INFO, "(ms %s) Received '%s' from L2 in state " + "%s\n", ms->name, get_rsl_name(msg_type), + gsm48_rr_state_names[rr->state]); + } + + /* find function for current state and message */ + for (i = 0; i < DLDATASLLEN; i++) + if ((msg_type == dldatastatelist[i].type) + && ((1 << rr->state) & dldatastatelist[i].states)) + break; + if (i == DLDATASLLEN) { + LOGP(DRSL, LOGL_NOTICE, "RSLms message unhandled\n"); + msgb_free(msg); + return 0; + } + + rc = dldatastatelist[i].rout(ms, msg); + + /* free msgb unless it is forwarded */ + if (dldatastatelist[i].rout != gsm48_rr_data_ind) + msgb_free(msg); + + return rc; +} + +/* state trasitions for RR-SAP messages from up */ +static struct rrdownstate { + uint32_t states; + int type; + int (*rout) (struct osmocom_ms *ms, struct msgb *msg); +} rrdownstatelist[] = { + /* NOTE: If not IDLE, it is rejected there. */ + {ALL_STATES, /* 3.3.1.1 */ + GSM48_RR_EST_REQ, gsm48_rr_est_req}, + + {SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.2 */ + GSM48_RR_DATA_REQ, gsm48_rr_data_req}, + + {SBIT(GSM48_RR_ST_CONN_PEND) | + SBIT(GSM48_RR_ST_DEDICATED), /* 3.4.13.3 */ + GSM48_RR_ABORT_REQ, gsm48_rr_abort_req}, + +#if 0 + {SBIT(GSM48_RR_ST_DEDICATED), + GSM48_RR_ACT_REQ, gsm48_rr_act_req}, +#endif +}; + +#define RRDOWNSLLEN \ + (sizeof(rrdownstatelist) / sizeof(struct rrdownstate)) + +int gsm48_rr_downmsg(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct gsm48_rr_hdr *rrh = (struct gsm48_rr_hdr *) msg->data; + int msg_type = rrh->msg_type; + int i; + int rc; + + LOGP(DRR, LOGL_INFO, "(ms %s) Message '%s' received in state %s\n", + ms->name, get_rr_name(msg_type), + gsm48_rr_state_names[rr->state]); + + /* find function for current state and message */ + for (i = 0; i < RRDOWNSLLEN; i++) + if ((msg_type == rrdownstatelist[i].type) + && ((1 << rr->state) & rrdownstatelist[i].states)) + break; + if (i == RRDOWNSLLEN) { + LOGP(DRR, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = rrdownstatelist[i].rout(ms, msg); + + /* free msgb uless it is forwarded */ + if (rrdownstatelist[i].rout != gsm48_rr_data_req) + msgb_free(msg); + + return rc; +} + +/* + * init/exit + */ + +int gsm48_rr_init(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + + memset(rr, 0, sizeof(*rr)); + rr->ms = ms; + + LOGP(DRR, LOGL_INFO, "init Radio Ressource process\n"); + + INIT_LLIST_HEAD(&rr->rsl_upqueue); + INIT_LLIST_HEAD(&rr->downqueue); + /* downqueue is handled here, so don't add_work */ + + osmol2_register_handler(ms, &gsm48_rx_rsl); + + return 0; +} + +int gsm48_rr_exit(struct osmocom_ms *ms) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *msg; + + LOGP(DRR, LOGL_INFO, "exit Radio Ressource process\n"); + + /* flush queues */ + while ((msg = msgb_dequeue(&rr->rsl_upqueue))) + msgb_free(msg); + while ((msg = msgb_dequeue(&rr->downqueue))) + msgb_free(msg); + + if (rr->rr_est_msg) { + msgb_free(rr->rr_est_msg); + rr->rr_est_msg = NULL; + } + + stop_rr_t_rel_wait(rr); + stop_rr_t3110(rr); + stop_rr_t3122(rr); + stop_rr_t3126(rr); + + return 0; +} + +#if 0 + +the process above is complete +------------------------------------------------------------------------------ +incomplete + + + + + + + + + + + + + + + + + + + +todo: + +stop timers on abort +wird beim abbruch immer der gepufferte cm-service-request entfernt, auch beim verschicken?: +measurement reports +todo rr_sync_ind when receiving ciph, re ass, channel mode modify + +todo change procedures, release procedure + +static int gsm48_rr_act_req(struct osmocom_ms *ms, struct gsm48_rr *rrmsg) +{ +} + + +} + +/* memcopy of LV of given IE from tlv_parsed structure */ +static int tlv_copy(void *dest, int dest_len, struct tlv_parsed *tp, uint8_t ie) +{ + uint8_t *lv = dest; + uint8_t len; + + if (dest_len < 1) + return -EINVAL; + lv[0] = 0; + + if (!TLVP_PRESENT(tp, ie)) + return 0; + + len = TLVP_LEN(tp, ie); + if (len < 1) + return 0; + if (len + 1 > dest_len) + return -ENOMEM; + + memcpy(dest, TLVP_VAL(tp, ie) - 1, len + 1); + return 0; +} + + +/* decode "Cell Description" (10.5.2.2) */ +static int gsm48_decode_cell_desc(struct gsm48_cell_desc *cd, uint16_t *arfcn, uint8_t *ncc uint8_t *bcc) +{ + *arfcn = (cd->bcch_hi << 8) + cd->bcch_lo; + *ncc = cd->ncc; + *bcc = cd->bcc; +} + +/* decode "Power Command" (10.5.2.28) and (10.5.2.28a) */ +static int gsm48_decode_power_cmd_acc(struct gsm48_power_cmd *pc, uint8_t *power_level uint8_t *atc) +{ + *power_level = pc->power_level; + if (atc) /* only in case of 10.5.2.28a */ + *atc = pc->atc; +} + +/* decode "Synchronization Indication" (10.5.2.39) */ +static int gsm48_decode_power_cmd_acc(struct gsm48_rrlayer *rr, struct gsm48_rr_sync_ind *si) +{ + rr->ho_sync_ind = si->si; + rr->ho_rot = si->rot; + rr->ho_nci = si->nci; +} + +/* receiving HANDOVER COMMAND message (9.1.15) */ +static int gsm48_rr_rx_hando_cmd(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = ms->rrlayer; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_ho_cmd *ho = (struct gsm48_ho_cmd *)gh->data; + int payload_len = msgb_l3len(msg) - sizeof(*gh) - wirklich sizeof(*ho); + struct tlv_parsed tp; + struct gsm48_rr_cd cd; + struct msgb *nmsg; + + memset(&cd, 0, sizeof(cd)); + + if (payload_len < 0) { + LOGP(DRR, LOGL_NOTICE, "Short read of HANDOVER COMMAND message.\n"); + return gsm48_rr_tx_rr_status(ms, GSM48_RR_CAUSE_PROT_ERROR_UNSPC); + } + tlv_parse(&tp, &gsm48_rr_att_tlvdef, ho->data, payload_len, 0, 0); + + /* decode Cell Description */ + gsm_decode_cell_desc(&ho->cell_desc, &cd.bcch_arfcn, &cd.ncc, &cd.bcc); + /* Channel Description */ + memcpy(&rr->chan_desc.chan_desc, ho->chan_desc, 3); + /* Handover Reference */ + rr->hando_ref = ho->ho_ref; + /* Power Command and access type */ + gsm_decode_power_cmd_acc((struct gsm48_power_cmd *)&ho->power_command, + &cd.power_level, cd.atc); + /* Synchronization Indication */ + if (TLVP_PRESENT(&tp, GSM48_IE_SYNC_IND)) + gsm48_decode_sync_ind(rr, + TLVP_VAL(&tp, GSM48_IE_SYNC_IND)-1, &cd); + /* Frequency Sort List */ + if (TLVP_PRESENT(&tp, GSM48_IE_FREQ_SHORT_LIST)) + gsm48_decode_freq_list(&ms->support, s->freq, + TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST), + *(TLVP_VAL(&tp, GSM48_IE_FREQ_SHORT_LIST)-1), + 0xce, FREQ_TYPE_SERV); + + +today: more IE parsing + + /* store current channel descriptions, to return in case of failure */ + memcpy(&rr->chan_last, &rr->chan_desc, sizeof(*cd)); + /* copy new description */ + memcpy(&rr->chan_desc, cd, sizeof(cd)); + + /* start suspension of current link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + gsm48_send_rsl(ms, RSL_MT_SUSP_REQ, msg); + + /* change into special handover suspension state */ + rr->hando_susp_state = 1; + rr->resume_last_state = 0; + + return 0; +} + +static int gsm48_rr_estab_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) +{ + if (rr->hando_susp_state || rr->assign_susp_state) { + if (rr->resume_last_state) { + rr->resume_last_state = 0; + gsm48_rr_tx_ass_cpl(ms, GSM48_RR_CAUSE_NORMAL); + } else { + gsm48_rr_tx_ass_fail(ms, GSM48_RR_CAUSE_PROTO_ERR_UNSPEC); + } + /* transmit queued frames during ho / ass transition */ + gsm48_rr_dequeue_down(ms); + } + + return 0; +} + +static int gsm48_rr_connect_cnf(struct osmocom_ms *ms, struct msgbl *msg) +{ +} + +static int gsm48_rr_mdl_error_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = ms->rrlayer; + struct msgb *nmsg; + struct gsm_rr_hdr *nrrh; + + printing of the cause + + switch (msg->l3h[0]) { + case RLL_CAUSE_SEQ_ERR: + case RLL_CAUSE_UNSOL_DM_RESP_MF: + einige muessen ignoriert werden + andere gelten als release + } + + if (rr->hando_susp_state || rr->assign_susp_state) { + if (!rr->resume_last_state) { + rr->resume_last_state = 1; + + /* get old channel description */ + memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd)); + + /* change radio to old channel */ + tx_ph_dm_est_req(ms, rr->cd_now.arfcn, + rr->cd_now.chan_nr, rr->cd_now.tsc); + + /* re-establish old link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + return gsm48_send_rsl(ms, RSL_MT_RECON_REQ, nmsg); + } + rr->resume_last_state = 0; + } + + /* deactivate channel */ + tx_ph_dm_rel_req(ms, arfcn, rr->chan_desc.chan_desc.chan_nr); + + /* send abort ind to upper layer */ + nmsg = gsm48_mm_msgb_alloc(); + + if (!msg) + return -ENOMEM; + nrrh = (struct gsm_mm_hdr *)nmsg->data; + nrrh->msg_type = RR_ABORT_IND; + nrrh->cause = GSM_MM_CAUSE_LINK_FAILURE; + return gsm48_rr_upmsg(ms, msg); +} + +static void timeout_rr_t3124(void *arg) +{ + struct gsm48_rrlayer *rr = arg; + struct msgb *nmsg; + + /* stop sending more access bursts when timer expired */ + hando_acc_left = 0; + + /* get old channel description */ + memcpy(&rr->chan_desc, &rr->chan_last, sizeof(*cd)); + + /* change radio to old channel */ + tx_ph_dm_est_req(ms, rr->cd_now.arfcn, rr->cd_now.chan_nr, + rr->cd_now.tsc); + + /* re-establish old link */ + nmsg = gsm48_l3_msgb_alloc(); + if (!nmsg) + return -ENOMEM; + return gsm48_send_rsl(ms, RSL_MT_REEST_REQ, nmsg); + + todo +} + +/* send HANDOVER ACCESS burst (9.1.14) */ +static int gsm48_rr_tx_hando_access(struct osmocom_ms *ms) +{ + nmsg = msgb_alloc_headroom(20, 16, "HAND_ACCESS"); + if (!nmsg) + return -ENOMEM; + *msgb_put(nmsg, 1) = rr->hando_ref; + todo burst + return gsm48_send_rsl(ms, RSL_MT_RAND_ACC_REQ, nmsg); +} + +/* send next channel request in dedicated state */ +static int gsm48_rr_rand_acc_cnf_dedicated(struct osmocom_ms *ms, struct msgb *msg) +{ + struct gsm48_rrlayer *rr = &ms->rrlayer; + struct msgb *nmsg; + int s; + + if (!rr->hando_susp_state) { + LOGP(DRR, LOGL_NOTICE, "Random acces confirm, but not in handover state.\n"); + return 0; + } + + /* send up to four handover access bursts */ + if (rr->hando_acc_left) { + rr->hando_acc_left--; + gsm48_rr_tx_hando_access(ms); + return; + } + + /* start timer for sending next HANDOVER ACCESS bursts afterwards */ + if (!bsc_timer_pending(&rr->t3124)) { + if (allocated channel is SDCCH) + start_rr_t3124(rr, GSM_T3124_675); + else + start_rr_t3124(rr, GSM_T3124_320); + if (!rr->n_chan_req) { + start_rr_t3126(rr, 5, 0); /* TODO improve! */ + return 0; + } + rr->n_chan_req--; + + /* wait for PHYSICAL INFORMATION message or T3124 timeout */ + return 0; + +} + +#endif + + diff --git a/src/host/layer23/src/gsmtap_util.c b/src/host/layer23/src/gsmtap_util.c new file mode 100644 index 00000000..a63fc882 --- /dev/null +++ b/src/host/layer23/src/gsmtap_util.c @@ -0,0 +1,179 @@ +/* GSMTAP output for Osmocom Layer2 (will only work on the host PC) */ +/* + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/osmocom_data.h> +#include <osmocom/gsmtap_util.h> +#include <osmocore/logging.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/gsmtap.h> +#include <osmocore/msgb.h> +#include <osmocore/rsl.h> + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +static struct bsc_fd gsmtap_bfd = { .fd = -1 }; +static LLIST_HEAD(gsmtap_txqueue); + +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t link_id) +{ + uint8_t ret = GSMTAP_CHANNEL_UNKNOWN; + + switch (rsl_chantype) { + case RSL_CHAN_Bm_ACCHs: + ret = GSMTAP_CHANNEL_TCH_F; + break; + case RSL_CHAN_Lm_ACCHs: + ret = GSMTAP_CHANNEL_TCH_H; + break; + case RSL_CHAN_SDCCH4_ACCH: + ret = GSMTAP_CHANNEL_SDCCH4; + break; + case RSL_CHAN_SDCCH8_ACCH: + ret = GSMTAP_CHANNEL_SDCCH8; + break; + case RSL_CHAN_BCCH: + ret = GSMTAP_CHANNEL_BCCH; + break; + case RSL_CHAN_RACH: + ret = GSMTAP_CHANNEL_RACH; + break; + case RSL_CHAN_PCH_AGCH: + /* it could also be AGCH... */ + ret = GSMTAP_CHANNEL_PCH; + break; + } + + if (link_id & 0x40) + ret |= GSMTAP_CHANNEL_ACCH; + + return ret; +} + +/* receive a message from L1/L2 and put it in GSMTAP */ +int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, + uint32_t fn, int8_t signal_dbm, uint8_t snr, + const uint8_t *data, unsigned int len) +{ + struct msgb *msg; + struct gsmtap_hdr *gh; + uint8_t *dst; + + /* gsmtap was never initialized, so don't try to send anything */ + if (gsmtap_bfd.fd == -1) + return 0; + + msg = msgb_alloc(sizeof(*gh) + len, "gsmtap_tx"); + if (!msg) + return -ENOMEM; + + gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->version = GSMTAP_VERSION; + gh->hdr_len = sizeof(*gh)/4; + gh->type = GSMTAP_TYPE_UM; + gh->timeslot = ts; + gh->sub_slot = ss; + gh->arfcn = htons(arfcn); + gh->snr_db = snr; + gh->signal_dbm = signal_dbm; + gh->frame_number = htonl(fn); + gh->sub_type = chan_type; + gh->antenna_nr = 0; + + dst = msgb_put(msg, len); + memcpy(dst, data, len); + + msgb_enqueue(&gsmtap_txqueue, msg); + gsmtap_bfd.when |= BSC_FD_WRITE; + + return 0; +} + +/* Callback from select layer if we can write to the socket */ +static int gsmtap_fd_cb(struct bsc_fd *fd, unsigned int flags) +{ + struct msgb *msg; + int rc; + + if (!(flags & BSC_FD_WRITE)) + return 0; + + msg = msgb_dequeue(&gsmtap_txqueue); + if (!msg) { + /* no more messages in the queue, disable READ cb */ + gsmtap_bfd.when = 0; + return 0; + } + rc = write(gsmtap_bfd.fd, msg->data, msg->len); + if (rc < 0) { + perror("writing msgb to gsmtap fd"); + msgb_free(msg); + return rc; + } + if (rc != msg->len) { + perror("short write to gsmtap fd"); + msgb_free(msg); + return -EIO; + } + + msgb_free(msg); + return 0; +} + +int gsmtap_init(uint32_t dst_ip) +{ + int rc; + struct sockaddr_in sin; + + sin.sin_family = AF_INET; + sin.sin_port = htons(GSMTAP_UDP_PORT); + sin.sin_addr.s_addr = htonl(dst_ip); + + /* FIXME: create socket */ + rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (rc < 0) { + perror("creating UDP socket"); + return rc; + } + gsmtap_bfd.fd = rc; + rc = connect(rc, (struct sockaddr *)&sin, sizeof(sin)); + if (rc < 0) { + perror("connecting UDP socket"); + close(gsmtap_bfd.fd); + gsmtap_bfd.fd = 0; + return rc; + } + + gsmtap_bfd.when = BSC_FD_WRITE; + gsmtap_bfd.cb = gsmtap_fd_cb; + gsmtap_bfd.data = NULL; + + return bsc_register_fd(&gsmtap_bfd); +} diff --git a/src/host/layer23/src/l1ctl.c b/src/host/layer23/src/l1ctl.c new file mode 100644 index 00000000..8d5e5693 --- /dev/null +++ b/src/host/layer23/src/l1ctl.c @@ -0,0 +1,577 @@ +/* Layer1 control code, talking L1CTL protocol with L1 on the phone */ + +/* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <arpa/inet.h> + +#include <l1a_l23_interface.h> + +#include <osmocore/signal.h> +#include <osmocore/logging.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/protocol/gsm_08_58.h> +#include <osmocore/rsl.h> + +#include <osmocom/l1ctl.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/l1l2_interface.h> +#include <osmocom/lapdm.h> +#include <osmocom/logging.h> +#include <osmocom/gsmtap_util.h> + +static struct msgb *osmo_l1_alloc(uint8_t msg_type) +{ + struct l1ctl_hdr *l1h; + struct msgb *msg = msgb_alloc_headroom(256, 4, "osmo_l1"); + + if (!msg) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate memory.\n"); + return NULL; + } + + msg->l1h = msgb_put(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = msg_type; + + return msg; +} + + +static int osmo_make_band_arfcn(struct osmocom_ms *ms, uint16_t arfcn) +{ + /* TODO: Include the band */ + return arfcn; +} + +static int rx_l1_fbsb_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl; + struct l1ctl_fbsb_conf *sb; + struct gsm_time tm; + + if (msgb_l3len(msg) < sizeof(*dl) + sizeof(*sb)) { + LOGP(DL1C, LOGL_ERROR, "FBSB RESP: MSG too short %u\n", + msgb_l3len(msg)); + return -1; + } + + dl = (struct l1ctl_info_dl *) msg->l1h; + sb = (struct l1ctl_fbsb_conf *) dl->payload; + + printf("snr=%04x, arfcn=%u result=%u\n", dl->snr, ntohs(dl->band_arfcn), + sb->result); + + if (sb->result != 0) { + LOGP(DL1C, LOGL_ERROR, "FBSB RESP: result=%u\n", sb->result); + dispatch_signal(SS_L1CTL, S_L1CTL_FBSB_ERR, ms); + return 0; + } + + gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr)); + DEBUGP(DL1C, "SCH: SNR: %u TDMA: (%.4u/%.2u/%.2u) bsic: %d\n", + dl->snr, tm.t1, tm.t2, tm.t3, sb->bsic); + dispatch_signal(SS_L1CTL, S_L1CTL_FBSB_RESP, ms); + + return 0; +} + +static int rx_l1_rach_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl; + struct osmobb_rach_conf rc; + + if (msgb_l3len(msg) < sizeof(*dl)) { + LOGP(DL1C, LOGL_ERROR, "RACH CONF: MSG too short %u\n", + msgb_l3len(msg)); + return -1; + } + + dl = (struct l1ctl_info_dl *) msg->l1h; + + rc.fn = htonl(dl->frame_nr); + rc.ms = ms; + dispatch_signal(SS_L1CTL, S_L1CTL_RACH_CONF, &rc); + + return 0; +} + +char *chan_nr2string(uint8_t chan_nr) +{ + static char str[20]; + uint8_t cbits = chan_nr >> 3; + + str[0] = '\0'; + + if (cbits == 0x01) + sprintf(str, "TCH/F"); + else if ((cbits & 0x1e) == 0x02) + sprintf(str, "TCH/H(%u)", cbits & 0x01); + else if ((cbits & 0x1c) == 0x04) + sprintf(str, "SDCCH/4(%u)", cbits & 0x03); + else if ((cbits & 0x18) == 0x08) + sprintf(str, "SDCCH/8(%u)", cbits & 0x07); + else if (cbits == 0x10) + sprintf(str, "BCCH"); + else if (cbits == 0x11) + sprintf(str, "RACH"); + else if (cbits == 0x12) + sprintf(str, "PCH/AGCH"); + else + sprintf(str, "UNKNOWN"); + + return str; +} + +/* Receive L1CTL_DATA_IND (Data Indication from L1) */ +static int rx_ph_data_ind(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl, dl_cpy; + struct l1ctl_data_ind *ccch; + struct lapdm_entity *le; + uint8_t chan_type, chan_ts, chan_ss; + uint8_t gsmtap_chan_type; + struct gsm_time tm; + + if (msgb_l3len(msg) < sizeof(*ccch)) { + LOGP(DL1C, LOGL_ERROR, "MSG too short Data Ind: %u\n", + msgb_l3len(msg)); + msgb_free(msg); + return -1; + } + + dl = (struct l1ctl_info_dl *) msg->l1h; + msg->l2h = dl->payload; + ccch = (struct l1ctl_data_ind *) msg->l2h; + + gsm_fn2gsmtime(&tm, ntohl(dl->frame_nr)); + rsl_dec_chan_nr(dl->chan_nr, &chan_type, &chan_ss, &chan_ts); + DEBUGP(DL1C, "%s (%.4u/%.2u/%.2u) %s\n", + chan_nr2string(dl->chan_nr), tm.t1, tm.t2, tm.t3, + hexdump(ccch->data, sizeof(ccch->data))); + + if (dl->num_biterr) { + LOGP(DL1C, LOGL_NOTICE, "Dropping frame with %u bit errors\n", + dl->num_biterr); + return 0; + } + + /* send CCCH data via GSMTAP */ + gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, dl->link_id); + gsmtap_sendmsg(ntohs(dl->band_arfcn), chan_ts, gsmtap_chan_type, chan_ss, + tm.fn, dl->rx_level-110, dl->snr, ccch->data, + sizeof(ccch->data)); + + /* determine LAPDm entity based on SACCH or not */ + if (dl->link_id & 0x40) + le = &ms->l2_entity.lapdm_acch; + else + le = &ms->l2_entity.lapdm_dcch; + /* make local stack copy of l1ctl_info_dl, as LAPDm will + * overwrite skb hdr */ + memcpy(&dl_cpy, dl, sizeof(dl_cpy)); + + /* pull the L1 header from the msgb */ + msgb_pull(msg, msg->l2h - (msg->l1h-sizeof(struct l1ctl_hdr))); + msg->l1h = NULL; + + /* send it up into LAPDm */ + l2_ph_data_ind(msg, le, &dl_cpy); + + return 0; +} + +/* Receive L1CTL_DATA_CONF (Data Confirm from L1) */ +static int rx_ph_data_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_info_dl *dl; + struct lapdm_entity *le; + + dl = (struct l1ctl_info_dl *) msg->l1h; + + /* determine LAPDm entity based on SACCH or not */ + if (dl->link_id & 0x40) + le = &ms->l2_entity.lapdm_acch; + else + le = &ms->l2_entity.lapdm_dcch; + + /* send it up into LAPDm */ + l2_ph_data_conf(msg, le); + + return 0; +} + +/* Transmit L1CTL_DATA_REQ */ +int tx_ph_data_req(struct osmocom_ms *ms, struct msgb *msg, + uint8_t chan_nr, uint8_t link_id) +{ + struct l1ctl_hdr *l1h; + struct l1ctl_info_ul *l1i_ul; + uint8_t chan_type, chan_ts, chan_ss; + uint8_t gsmtap_chan_type; + + DEBUGP(DL1C, "(%s)\n", hexdump(msg->l2h, msgb_l2len(msg))); + + if (msgb_l2len(msg) > 23) { + LOGP(DL1C, LOGL_ERROR, "L1 cannot handle message length " + "> 23 (%u)\n", msgb_l2len(msg)); + msgb_free(msg); + return -EINVAL; + } else if (msgb_l2len(msg) < 23) + LOGP(DL1C, LOGL_ERROR, "L1 message length < 23 (%u) " + "doesn't seem right!\n", msgb_l2len(msg)); + + /* send copy via GSMTAP */ + rsl_dec_chan_nr(chan_nr, &chan_type, &chan_ss, &chan_ts); + gsmtap_chan_type = chantype_rsl2gsmtap(chan_type, link_id); + gsmtap_sendmsg(0|0x4000, chan_ts, gsmtap_chan_type, chan_ss, + 0, 127, 255, msg->l2h, msgb_l2len(msg)); + + /* prepend uplink info header */ + l1i_ul = (struct l1ctl_info_ul *) msgb_push(msg, sizeof(*l1i_ul)); + + l1i_ul->chan_nr = chan_nr; + l1i_ul->link_id = link_id; + + /* FIXME: where to get this from? */ + l1i_ul->tx_power = 0; + + /* prepend l1 header */ + msg->l1h = msgb_push(msg, sizeof(*l1h)); + l1h = (struct l1ctl_hdr *) msg->l1h; + l1h->msg_type = L1CTL_DATA_REQ; + + return osmo_send_l1(ms, msg); +} + +/* Transmit FBSB_REQ */ +int l1ctl_tx_fbsb_req(struct osmocom_ms *ms, uint16_t arfcn, + uint8_t flags, uint16_t timeout, uint8_t sync_info_idx, + uint8_t ccch_mode) +{ + struct msgb *msg; + struct l1ctl_fbsb_req *req; + + msg = osmo_l1_alloc(L1CTL_FBSB_REQ); + if (!msg) + return -1; + + req = (struct l1ctl_fbsb_req *) msgb_put(msg, sizeof(*req)); + req->band_arfcn = htons(osmo_make_band_arfcn(ms, arfcn)); + req->timeout = htons(timeout); + /* Threshold when to consider FB_MODE1: 4kHz - 1kHz */ + req->freq_err_thresh1 = htons(4000 - 1000); + /* Threshold when to consider SCH: 1kHz - 200Hz */ + req->freq_err_thresh2 = htons(1000 - 200); + /* not used yet! */ + req->num_freqerr_avg = 3; + req->flags = flags; + req->sync_info_idx = sync_info_idx; + req->ccch_mode = ccch_mode; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_CCCH_MODE_REQ */ +int l1ctl_tx_ccch_mode_req(struct osmocom_ms *ms, uint8_t ccch_mode) +{ + struct msgb *msg; + struct l1ctl_ccch_mode_req *req; + + msg = osmo_l1_alloc(L1CTL_CCCH_MODE_REQ); + if (!msg) + return -1; + + req = (struct l1ctl_ccch_mode_req *) msgb_put(msg, sizeof(*req)); + req->ccch_mode = ccch_mode; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_RACH_REQ */ +int tx_ph_rach_req(struct osmocom_ms *ms) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_rach_req *req; + static uint8_t i = 0; + + msg = osmo_l1_alloc(L1CTL_RACH_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "RACH Req.\n"); + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + req = (struct l1ctl_rach_req *) msgb_put(msg, sizeof(*req)); + req->ra = i++; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_DM_EST_REQ */ +int tx_ph_dm_est_req_h0(struct osmocom_ms *ms, uint16_t band_arfcn, + uint8_t chan_nr, uint8_t tsc, uint8_t tx_power) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_dm_est_req *req; + + msg = osmo_l1_alloc(L1CTL_DM_EST_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "Tx Dedic.Mode Est Req (arfcn=%u, chan_nr=0x%02x)\n", + band_arfcn, chan_nr); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = chan_nr; + ul->link_id = 0; + ul->tx_power = tx_power; + + req = (struct l1ctl_dm_est_req *) msgb_put(msg, sizeof(*req)); + req->tsc = tsc; + req->h = 0; + req->h0.band_arfcn = htons(band_arfcn); + + return osmo_send_l1(ms, msg); +} + +int tx_ph_dm_est_req_h1(struct osmocom_ms *ms, uint8_t maio, uint8_t hsn, + uint16_t *ma, uint8_t ma_len, uint8_t chan_nr, uint8_t tsc, + uint8_t tx_power) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + struct l1ctl_dm_est_req *req; + int i; + + msg = osmo_l1_alloc(L1CTL_DM_EST_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "Tx Dedic.Mode Est Req (maio=%u, hsn=%u, " + "chan_nr=0x%02x)\n", maio, hsn, chan_nr); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + ul->chan_nr = chan_nr; + ul->link_id = 0; + ul->tx_power = tx_power; + + req = (struct l1ctl_dm_est_req *) msgb_put(msg, sizeof(*req)); + req->tsc = tsc; + req->h = 1; + req->h1.maio = maio; + req->h1.hsn = hsn; + req->h1.n = ma_len; + for (i = 0; i < ma_len; i++) + req->h1.ma[i] = htons(ma[i]); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_DM_REL_REQ */ +int tx_ph_dm_rel_req(struct osmocom_ms *ms) +{ + struct msgb *msg; + struct l1ctl_info_ul *ul; + + msg = osmo_l1_alloc(L1CTL_DM_REL_REQ); + if (!msg) + return -1; + + DEBUGP(DL1C, "Tx Dedic.Mode Rel Req\n"); + + ul = (struct l1ctl_info_ul *) msgb_put(msg, sizeof(*ul)); + + return osmo_send_l1(ms, msg); +} + +int l1ctl_tx_echo_req(struct osmocom_ms *ms, unsigned int len) +{ + struct msgb *msg; + uint8_t *data; + unsigned int i; + + msg = osmo_l1_alloc(L1CTL_ECHO_REQ); + if (!msg) + return -1; + + data = msgb_put(msg, len); + for (i = 0; i < len; i++) + data[i] = i % 8; + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_PM_REQ */ +int l1ctl_tx_pm_req_range(struct osmocom_ms *ms, uint16_t arfcn_from, + uint16_t arfcn_to) +{ + struct msgb *msg; + struct l1ctl_pm_req *pm; + + msg = osmo_l1_alloc(L1CTL_PM_REQ); + if (!msg) + return -1; + + printf("Tx PM Req (%u-%u)\n", arfcn_from, arfcn_to); + pm = (struct l1ctl_pm_req *) msgb_put(msg, sizeof(*pm)); + pm->type = 1; + pm->range.band_arfcn_from = htons(arfcn_from); + pm->range.band_arfcn_to = htons(arfcn_to); + + return osmo_send_l1(ms, msg); +} + +/* Transmit L1CTL_RESET_REQ */ +int l1ctl_tx_reset_req(struct osmocom_ms *ms, uint8_t type) +{ + struct msgb *msg; + struct l1ctl_reset *res; + + msg = osmo_l1_alloc(L1CTL_RESET_REQ); + if (!msg) + return -1; + + printf("Tx Reset Req (%u)\n", type); + res = (struct l1ctl_reset *) msgb_put(msg, sizeof(*res)); + res->type = type; + + return osmo_send_l1(ms, msg); +} + +/* Receive L1CTL_RESET_IND */ +static int rx_l1_reset(struct osmocom_ms *ms) +{ + printf("Layer1 Reset.\n"); + dispatch_signal(SS_L1CTL, S_L1CTL_RESET, ms); + + return 0; +} + +/* Receive L1CTL_PM_CONF */ +static int rx_l1_pm_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct l1ctl_pm_conf *pmr; + + for (pmr = (struct l1ctl_pm_conf *) msg->l1h; + (uint8_t *) pmr < msg->tail; pmr++) { + struct osmobb_meas_res mr; + DEBUGP(DL1C, "PM MEAS: ARFCN: %4u RxLev: %3d %3d\n", + ntohs(pmr->band_arfcn), pmr->pm[0], pmr->pm[1]); + mr.band_arfcn = ntohs(pmr->band_arfcn); + mr.rx_lev = (pmr->pm[0] + pmr->pm[1]) / 2; + mr.ms = ms; + dispatch_signal(SS_L1CTL, S_L1CTL_PM_RES, &mr); + } + return 0; +} + +/* Receive L1CTL_MODE_CONF */ +static int rx_l1_ccch_mode_conf(struct osmocom_ms *ms, struct msgb *msg) +{ + struct osmobb_ccch_mode_conf mc; + struct l1ctl_ccch_mode_conf *conf; + + if (msgb_l3len(msg) < sizeof(*conf)) { + LOGP(DL1C, LOGL_ERROR, "MODE CONF: MSG too short %u\n", + msgb_l3len(msg)); + return -1; + } + + conf = (struct l1ctl_ccch_mode_conf *) msg->l1h; + + printf("mode=%u\n", conf->ccch_mode); + + mc.ccch_mode = conf->ccch_mode; + mc.ms = ms; + dispatch_signal(SS_L1CTL, S_L1CTL_CCCH_MODE_CONF, &mc); + + return 0; +} + +/* Receive incoming data from L1 using L1CTL format */ +int l1ctl_recv(struct osmocom_ms *ms, struct msgb *msg) +{ + int rc = 0; + struct l1ctl_hdr *l1h; + struct l1ctl_info_dl *dl; + + if (msgb_l2len(msg) < sizeof(*dl)) { + LOGP(DL1C, LOGL_ERROR, "Short Layer2 message: %u\n", + msgb_l2len(msg)); + msgb_free(msg); + return -1; + } + + l1h = (struct l1ctl_hdr *) msg->l1h; + + /* move the l1 header pointer to point _BEHIND_ l1ctl_hdr, + as the l1ctl header is of no interest to subsequent code */ + msg->l1h = l1h->data; + + switch (l1h->msg_type) { + case L1CTL_FBSB_CONF: + rc = rx_l1_fbsb_conf(ms, msg); + msgb_free(msg); + break; + case L1CTL_DATA_IND: + rc = rx_ph_data_ind(ms, msg); + break; + case L1CTL_DATA_CONF: + rc = rx_ph_data_conf(ms, msg); + break; + case L1CTL_RESET_IND: + case L1CTL_RESET_CONF: + rc = rx_l1_reset(ms); + msgb_free(msg); + break; + case L1CTL_PM_CONF: + rc = rx_l1_pm_conf(ms, msg); + msgb_free(msg); + if (l1h->flags & L1CTL_F_DONE) + dispatch_signal(SS_L1CTL, S_L1CTL_PM_DONE, ms); + break; + case L1CTL_RACH_CONF: + rc = rx_l1_rach_conf(ms, msg); + msgb_free(msg); + break; + case L1CTL_CCCH_MODE_CONF: + rc = rx_l1_ccch_mode_conf(ms, msg); + msgb_free(msg); + break; + default: + fprintf(stderr, "Unknown MSG: %u\n", l1h->msg_type); + msgb_free(msg); + break; + } + + return rc; +} diff --git a/src/host/layer23/src/l1l2_interface.c b/src/host/layer23/src/l1l2_interface.c new file mode 100644 index 00000000..3c359e84 --- /dev/null +++ b/src/host/layer23/src/l1l2_interface.c @@ -0,0 +1,167 @@ +/* Layer 1 socket interface of layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/logging.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#define GSM_L2_LENGTH 256 + +extern int quit; + +static int layer2_read(struct bsc_fd *fd) +{ + struct msgb *msg; + u_int16_t len; + int rc; + + msg = msgb_alloc(GSM_L2_LENGTH, "Layer2"); + if (!msg) { + LOGP(DL1C, LOGL_ERROR, "Failed to allocate msg.\n"); + return -ENOMEM; + } + + rc = read(fd->fd, &len, sizeof(len)); + if (rc < sizeof(len)) { + fprintf(stderr, "Layer2 socket failed\n"); + if (rc >= 0) + rc = -EIO; + quit = rc; + return rc; + } + + len = ntohs(len); + if (len > GSM_L2_LENGTH) { + LOGP(DL1C, LOGL_ERROR, "Length is too big: %u\n", len); + msgb_free(msg); + return -EINVAL; + } + + + msg->l1h = msgb_put(msg, len); + rc = read(fd->fd, msg->l1h, msgb_l1len(msg)); + if (rc != msgb_l1len(msg)) { + LOGP(DL1C, LOGL_ERROR, "Can not read data: len=%d rc=%d " + "errno=%d\n", len, rc, errno); + msgb_free(msg); + return rc; + } + + l1ctl_recv((struct osmocom_ms *) fd->data, msg); + + return 0; +} + +static int layer2_write(struct bsc_fd *fd, struct msgb *msg) +{ + int rc; + + rc = write(fd->fd, msg->data, msg->len); + if (rc != msg->len) { + LOGP(DL1C, LOGL_ERROR, "Failed to write data: rc: %d\n", rc); + return rc; + } + + return 0; +} + +int layer2_open(struct osmocom_ms *ms, const char *socket_path) +{ + int rc; + struct sockaddr_un local; + + ms->wq.bfd.fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (ms->wq.bfd.fd < 0) { + fprintf(stderr, "Failed to create unix domain socket.\n"); + return ms->wq.bfd.fd; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + rc = connect(ms->wq.bfd.fd, (struct sockaddr *) &local, + sizeof(local.sun_family) + strlen(local.sun_path)); + if (rc < 0) { + fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path); + close(ms->wq.bfd.fd); + return rc; + } + + write_queue_init(&ms->wq, 100); + ms->wq.bfd.data = ms; + ms->wq.bfd.when = BSC_FD_READ; + ms->wq.read_cb = layer2_read; + ms->wq.write_cb = layer2_write; + + rc = bsc_register_fd(&ms->wq.bfd); + if (rc != 0) { + fprintf(stderr, "Failed to register fd.\n"); + return rc; + } + + return 0; +} + +int layer2_close(struct osmocom_ms *ms) +{ + close(ms->wq.bfd.fd); + bsc_unregister_fd(&ms->wq.bfd); + + return 0; +} + +int osmo_send_l1(struct osmocom_ms *ms, struct msgb *msg) +{ + uint16_t *len; + + DEBUGP(DL1C, "Sending: '%s'\n", hexdump(msg->data, msg->len)); + + if (msg->l1h != msg->data) + LOGP(DL1C, LOGL_ERROR, "Message L1 header != Message Data\n"); + + /* prepend 16bit length before sending */ + len = (uint16_t *) msgb_push(msg, sizeof(*len)); + *len = htons(msg->len - sizeof(*len)); + + if (write_queue_enqueue(&ms->wq, msg) != 0) { + LOGP(DL1C, LOGL_ERROR, "Failed to enqueue msg.\n"); + msgb_free(msg); + return -1; + } + + return 0; +} + + diff --git a/src/host/layer23/src/lapdm.c b/src/host/layer23/src/lapdm.c new file mode 100644 index 00000000..eff7aabe --- /dev/null +++ b/src/host/layer23/src/lapdm.c @@ -0,0 +1,2126 @@ +/* GSM LAPDm (TS 04.06) implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by 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. + * + */ + +/* Notes on Buffering: rcv_buffer, tx_queue, tx_hist, send_buffer, send_queue + * + * RX data is stored in the rcv_buffer (pointer). If the message is complete, it + * is removed from rcv_buffer pointer and forwarded to L3. If the RX data is + * received while there is an incomplete rcv_buffer, it is appended to it. + * + * TX data is stored in the send_queue first. When transmitting a frame, + * the first message in the send_queue is moved to the send_buffer. There it + * resides until all fragments are acknowledged. Fragments to be sent by I + * frames are stored in the tx_hist buffer for resend, if required. Also the + * current fragment is copied into the tx_queue. There it resides until it is + * forwarded to layer 1. + * + * In case we have SAPI 0, we only have a window size of 1, so the unack- + * nowledged message resides always in the send_buffer. In case of a suspend, + * it can be written back to the first position of the send_queue. + * + * The layer 1 normally sends a PH-READY-TO-SEND. But because we use + * asynchronous transfer between layer 1 and layer 2 (serial link), we must + * send a frame before layer 1 reaches the right timeslot to send it. So we + * move the tx_queue to layer 1 when there is not already a pending frame, and + * wait until acknowledge after the frame has been sent. If we receive an + * acknowledge, we can send the next frame from the buffer, if any. + * + * The moving of tx_queue to layer 1 may also trigger T200, if desired. Also it + * will trigger next I frame, if possible. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocore/logging.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/utils.h> +#include <osmocore/rsl.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/protocol/gsm_08_58.h> + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/lapdm.h> +#include <osmocom/logging.h> + +#include <l1a_l23_interface.h> + +/* TS 04.06 Figure 4 / Section 3.2 */ +#define LAPDm_LPD_NORMAL 0 +#define LAPDm_LPD_SMSCB 1 +#define LAPDm_SAPI_NORMAL 0 +#define LAPDm_SAPI_SMS 3 +#define LAPDm_ADDR(lpd, sapi, cr) ((((lpd) & 0x3) << 5) | (((sapi) & 0x7) << 2) | (((cr) & 0x1) << 1) | 0x1) + +#define LAPDm_ADDR_SAPI(addr) (((addr) >> 2) & 0x7) +#define LAPDm_ADDR_CR(addr) (((addr) >> 1) & 0x1) +#define LAPDm_ADDR_EA(addr) ((addr) & 0x1) + +/* TS 04.06 Table 3 / Section 3.4.3 */ +#define LAPDm_CTRL_I(nr, ns, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((ns) & 0x7) << 1)) +#define LAPDm_CTRL_S(nr, s, p) ((((nr) & 0x7) << 5) | (((p) & 0x1) << 4) | (((s) & 0x3) << 2) | 0x1) +#define LAPDm_CTRL_U(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3) + +#define LAPDm_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0) +#define LAPDm_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1) +#define LAPDm_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3) + +#define LAPDm_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3) +#define LAPDm_CTRL_PF_BIT(ctrl) (((ctrl) >> 4) & 0x1) + +#define LAPDm_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2) + +#define LAPDm_CTRL_I_Ns(ctrl) (((ctrl) & 0xE) >> 1) +#define LAPDm_CTRL_Nr(ctrl) (((ctrl) & 0xE0) >> 5) + +/* TS 04.06 Table 4 / Section 3.8.1 */ +#define LAPDm_U_SABM 0x7 +#define LAPDm_U_DM 0x3 +#define LAPDm_U_UI 0x0 +#define LAPDm_U_DISC 0x8 +#define LAPDm_U_UA 0xC + +#define LAPDm_S_RR 0x0 +#define LAPDm_S_RNR 0x1 +#define LAPDm_S_REJ 0x2 + +#define LAPDm_LEN(len) ((len << 2) | 0x1) +#define LAPDm_MORE 0x2 + +/* TS 04.06 Section 5.8.3 */ +#define N201_AB_SACCH 18 +#define N201_AB_SDCCH 20 +#define N201_AB_FACCH 20 +#define N201_Bbis 23 +#define N201_Bter_SACCH 21 +#define N201_Bter_SDCCH 23 +#define N201_Bter_FACCH 23 +#define N201_B4 19 + +/* 5.8.2.1 N200 during establish and release */ +#define N200_EST_REL 5 +/* 5.8.2.1 N200 during timer recovery state */ +#define N200_TR_SACCH 5 +#define N200_TR_SDCCH 23 +#define N200_TR_FACCH_FR 34 +#define N200_TR_EFACCH_FR 48 +#define N200_TR_FACCH_HR 29 +/* FIXME: this depends on chan type */ +#define N200 N200_TR_SACCH + +#define CR_MS2BS_CMD 0 +#define CR_MS2BS_RESP 1 +#define CR_BS2MS_CMD 1 +#define CR_BS2MS_RESP 0 + +/* Set T200 to 1 Second (OpenBTS uses 900ms) */ +#define T200 1, 0 + +/* k value for each SAPI */ +static uint8_t k_sapi[] = {1, 1, 1, 1, 1, 1, 1, 1}; + +enum lapdm_format { + LAPDm_FMT_A, + LAPDm_FMT_B, + LAPDm_FMT_Bbis, + LAPDm_FMT_Bter, + LAPDm_FMT_B4, +}; + +static void lapdm_t200_cb(void *data); +static int rslms_send_i(struct lapdm_msg_ctx *mctx); + +/* UTILITY FUNCTIONS */ + +static inline uint8_t inc_mod8(uint8_t x) +{ + return (x + 1) & 7; +} + +static inline uint8_t add_mod8(uint8_t x, uint8_t y) +{ + return (x + y) & 7; +} + +static inline uint8_t sub_mod8(uint8_t x, uint8_t y) +{ + return (x - y) & 7; /* handle negative results correctly */ +} + +static void lapdm_dl_init(struct lapdm_datalink *dl, + struct lapdm_entity *entity) +{ + memset(dl, 0, sizeof(*dl)); + INIT_LLIST_HEAD(&dl->send_queue); + INIT_LLIST_HEAD(&dl->tx_queue); + dl->state = LAPDm_STATE_IDLE; + dl->t200.data = dl; + dl->t200.cb = &lapdm_t200_cb; + dl->entity = entity; +} + +void lapdm_init(struct lapdm_entity *le, struct osmocom_ms *ms) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(le->datalink); i++) + lapdm_dl_init(&le->datalink[i], le); + + le->ms = ms; +} + +static void lapdm_dl_flush_send(struct lapdm_datalink *dl) +{ + struct msgb *msg; + + /* Flush send-queue */ + while ((msg = msgb_dequeue(&dl->send_queue))) + msgb_free(msg); + + /* Clear send-buffer */ + if (dl->send_buffer) { + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; + } +} + +static void lapdm_dl_flush_tx(struct lapdm_datalink *dl) +{ + struct msgb *msg; + unsigned int i; + + while ((msg = msgb_dequeue(&dl->tx_queue))) + msgb_free(msg); + for (i = 0; i < 8; i++) + dl->tx_length[i] = 0; +} + +void lapdm_exit(struct lapdm_entity *le) +{ + unsigned int i; + struct lapdm_datalink *dl; + + for (i = 0; i < ARRAY_SIZE(le->datalink); i++) { + dl = &le->datalink[i]; + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + if (dl->rcv_buffer) + msgb_free(dl->rcv_buffer); + } +} + +static void lapdm_dl_newstate(struct lapdm_datalink *dl, uint32_t state) +{ + LOGP(DLAPDM, LOGL_INFO, "new state %s -> %s\n", + lapdm_state_names[dl->state], lapdm_state_names[state]); + + dl->state = state; +} + +static struct lapdm_datalink *datalink_for_sapi(struct lapdm_entity *le, uint8_t sapi) +{ + switch (sapi) { + case LAPDm_SAPI_NORMAL: + return &le->datalink[0]; + case LAPDm_SAPI_SMS: + return &le->datalink[1]; + default: + return NULL; + } +} + +/* remove the L2 header from a MSGB */ +static inline unsigned char *msgb_pull_l2h(struct msgb *msg) +{ + unsigned char *ret = msgb_pull(msg, msg->l3h - msg->l2h); + msg->l2h = NULL; + return ret; +} + +/* Append padding (if required) */ +static void lapdm_pad_msgb(struct msgb *msg, uint8_t n201) +{ + int pad_len = n201 - msgb_l2len(msg); + uint8_t *data; + + if (pad_len < 0) { + LOGP(DLAPDM, LOGL_ERROR, + "cannot pad message that is already too big!\n"); + return; + } + + data = msgb_put(msg, pad_len); + memset(data, 0x2B, pad_len); +} + +/* write a frame into the tx queue */ +static int tx_ph_data_enqueue(struct lapdm_datalink *dl, struct msgb *msg, + uint8_t chan_nr, uint8_t link_id, uint8_t n201) +{ + struct lapdm_entity *le = dl->entity; + struct osmocom_ms *ms = le->ms; + + /* if there is a pending message, queue it */ + if (le->tx_pending) { + *msgb_push(msg, 1) = n201; + *msgb_push(msg, 1) = link_id; + *msgb_push(msg, 1) = chan_nr; + msgb_enqueue(&dl->tx_queue, msg); + return -EBUSY; + } + + /* send the frame now */ + le->tx_pending = 1; +#if 0 +printf("-> tx chan_nr 0x%x link_id 0x%x len %d data", chan_nr, link_id, msgb_l2len(msg)); +int i; +for (i = 0; i < msgb_l2len(msg); i++) + printf(" %02x", msg->l2h[i]); +printf("\n"); +#endif + lapdm_pad_msgb(msg, n201); + return tx_ph_data_req(ms, msg, chan_nr, link_id); +} + +/* get next frame from the tx queue. because the ms has multiple datalinks, + * each datalink's queue is read round-robin. + */ +int l2_ph_data_conf(struct msgb *msg, struct lapdm_entity *le) +{ + struct osmocom_ms *ms = le->ms; + struct lapdm_datalink *dl; + int last = le->last_tx_dequeue; + int i = last, n = ARRAY_SIZE(le->datalink); + uint8_t chan_nr, link_id, n201; + + /* we may send again */ + le->tx_pending = 0; + +#if 0 +printf("-> tx confirm\n"); +#endif + /* free confirm message */ + msgb_free(msg); + + /* round-robin dequeue */ + do { + /* next */ + i = (i + 1) % n; + dl = &le->datalink[i]; + if ((msg = msgb_dequeue(&dl->tx_queue))) + break; + } while (i != last); + + /* no message in all queues */ + if (!msg) + return 0; + + /* Pull chan_nr and link_id */ + chan_nr = *msg->data; + msgb_pull(msg, 1); + link_id = *msg->data; + msgb_pull(msg, 1); + n201 = *msg->data; + msgb_pull(msg, 1); + + /* Set last dequeue position */ + le->last_tx_dequeue = i; + + /* Pad the frame, we can transmit now */ + le->tx_pending = 1; +#if 0 +printf("-> more tx chan_nr 0x%x link_id 0x%x len %d data", chan_nr, link_id, msgb_l2len(msg)); +for (i = 0; i < msgb_l2len(msg); i++) + printf(" %02x", msg->l2h[i]); +printf("\n"); +#endif + lapdm_pad_msgb(msg, n201); + return tx_ph_data_req(ms, msg, chan_nr, link_id); +} + +/* Take a Bbis format message from L1 and create RSLms UNIT DATA IND */ +static int send_rslms_rll_l3(uint8_t msg_type, struct lapdm_msg_ctx *mctx, + struct msgb *msg) +{ + /* Add the RSL + RLL header */ + rsl_rll_push_l3(msg, msg_type, mctx->chan_nr, mctx->link_id, 1); + + /* send off the RSLms message to L3 */ + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +static int send_rll_simple(uint8_t msg_type, struct lapdm_msg_ctx *mctx) +{ + struct msgb *msg; + + msg = rsl_rll_simple(msg_type, mctx->chan_nr, mctx->link_id, 1); + + /* send off the RSLms message to L3 */ + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +static int rsl_rll_error(uint8_t cause, struct lapdm_msg_ctx *mctx) +{ + struct msgb *msg; + + LOGP(DLAPDM, LOGL_NOTICE, "sending MDL-ERROR-IND %d\n", cause); + msg = rsl_rll_simple(RSL_MT_ERROR_IND, mctx->chan_nr, mctx->link_id, 1); + msg->l2h = msgb_put(msg, sizeof(struct abis_rsl_rll_hdr) + 1); + msg->l2h[0] = cause; + return rslms_sendmsg(msg, mctx->dl->entity->ms); +} + +static int check_length_ind(struct lapdm_msg_ctx *mctx, uint8_t length_ind) +{ + if (!(length_ind & 0x01)) { + /* G.4.1 If the EL bit is set to "0", an MDL-ERROR-INDICATION + * primitive with cause "frame not implemented" is sent to the + * mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, + "we don't support multi-octet length\n"); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + return 0; +} + +static int lapdm_send_resend(struct lapdm_datalink *dl) +{ + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm resend"); + int length; + + /* Resend SABM/DISC from tx_hist */ + length = dl->tx_length[0]; + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, dl->tx_hist[dl->V_send], length); + + return tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr, dl->mctx.link_id, + dl->mctx.n201); +} + +static int lapdm_send_ua(struct lapdm_msg_ctx *mctx, uint8_t len, uint8_t *data) +{ + uint8_t sapi = mctx->link_id & 7; + uint8_t f_bit = LAPDm_CTRL_PF_BIT(mctx->ctrl); + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm UA"); + msg->l2h = msgb_put(msg, 3 + len); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UA, f_bit); + msg->l2h[2] = LAPDm_LEN(len); + if (len) + memcpy(msg->l2h + 3, data, len); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_dm(struct lapdm_msg_ctx *mctx) +{ + uint8_t sapi = mctx->link_id & 7; + uint8_t f_bit = LAPDm_CTRL_PF_BIT(mctx->ctrl); + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm DM"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_DM, f_bit); + msg->l2h[2] = 0; + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_rr(struct lapdm_msg_ctx *mctx, uint8_t f_bit) +{ + uint8_t sapi = mctx->link_id & 7; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RR"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_RR, f_bit); + msg->l2h[2] = LAPDm_LEN(0); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_rnr(struct lapdm_msg_ctx *mctx, uint8_t f_bit) +{ + uint8_t sapi = mctx->link_id & 7; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm RNR"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_RNR, f_bit); + msg->l2h[2] = LAPDm_LEN(0); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +static int lapdm_send_rej(struct lapdm_msg_ctx *mctx, uint8_t f_bit) +{ + uint8_t sapi = mctx->link_id & 7; + struct msgb *msg = msgb_alloc_headroom(23+10, 10, "LAPDm REJ"); + msg->l2h = msgb_put(msg, 3); + + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_RESP); + msg->l2h[1] = LAPDm_CTRL_S(mctx->dl->V_recv, LAPDm_S_REJ, f_bit); + msg->l2h[2] = LAPDm_LEN(0); + + return tx_ph_data_enqueue(mctx->dl, msg, mctx->chan_nr, mctx->link_id, + mctx->n201); +} + +/* Timer callback on T200 expiry */ +static void lapdm_t200_cb(void *data) +{ + struct lapdm_datalink *dl = data; + + LOGP(DLAPDM, LOGL_INFO, "lapdm_t200_cb(%p) state=%u\n", dl, dl->state); + + switch (dl->state) { + case LAPDm_STATE_SABM_SENT: + /* 5.4.1.3 */ + if (dl->retrans_ctr + 1 >= N200_EST_REL + 1) { + /* send RELEASE INDICATION to L3 */ + send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + /* send MDL ERROR INIDCATION to L3 */ + rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx); + /* go back to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* NOTE: we must not change any other states or buffers + * and queues, since we may reconnect after handover + * failure. the buffered messages is replaced there */ + break; + } + /* retransmit SABM command */ + lapdm_send_resend(dl); + /* increment re-transmission counter */ + dl->retrans_ctr++; + /* restart T200 (PH-READY-TO-SEND) */ + bsc_schedule_timer(&dl->t200, T200); + break; + case LAPDm_STATE_DISC_SENT: + /* 5.4.4.3 */ + if (dl->retrans_ctr + 1 >= N200_EST_REL + 1) { + /* send RELEASE INDICATION to L3 */ + send_rll_simple(RSL_MT_REL_CONF, &dl->mctx); + /* send MDL ERROR INIDCATION to L3 */ + rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx); + /* go back to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* NOTE: we must not change any other states or buffers + * and queues, since we may reconnect after handover + * failure. the buffered messages is replaced there */ + break; + } + /* retransmit DISC command */ + lapdm_send_resend(dl); + /* increment re-transmission counter */ + dl->retrans_ctr++; + /* restart T200 (PH-READY-TO-SEND) */ + bsc_schedule_timer(&dl->t200, T200); + break; + case LAPDm_STATE_MF_EST: + /* 5.5.7 */ + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_TIMER_RECOV); + /* fall through */ + case LAPDm_STATE_TIMER_RECOV: + dl->retrans_ctr++; + if (dl->retrans_ctr < N200) { + /* retransmit I frame (V_s-1) with P=1, if any */ + if (dl->tx_length[dl->V_send - 1]) { + struct msgb *msg; + int length; + + LOGP(DLAPDM, LOGL_INFO, "retransmit last frame " + "V(S)=%d\n", dl->V_send - 1); + /* Create I frame (segment) from tx_hist */ + length = dl->tx_length[dl->V_send - 1]; + msg = msgb_alloc_headroom(23+10, 10, "LAPDm I"); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, dl->tx_hist[dl->V_send - 1], + length); + msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, + dl->V_send - 1, 1); /* P=1 */ + tx_ph_data_enqueue(dl, msg, dl->mctx.chan_nr, + dl->mctx.link_id, dl->mctx.n201); + } else { + /* OR send appropriate supervision frame with P=1 */ + if (!dl->own_busy && !dl->seq_err_cond) { + lapdm_send_rr(&dl->mctx, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + lapdm_send_rnr(&dl->mctx, 1); + } else { + LOGP(DLAPDM, LOGL_INFO, "unhandled, " + "pls. fix\n"); + } + } + /* restart T200 (PH-READY-TO-SEND) */ + bsc_schedule_timer(&dl->t200, T200); + } else { + /* send MDL ERROR INIDCATION to L3 */ + rsl_rll_error(RLL_CAUSE_T200_EXPIRED, &dl->mctx); + } + break; + default: + LOGP(DLAPDM, LOGL_INFO, "T200 expired in unexpected " + "dl->state %u\n", dl->state); + } +} + +/* 5.5.3.1: Common function to acknowlege frames up to the given N(R) value */ +static void lapdm_acknowledge(struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t nr = LAPDm_CTRL_Nr(mctx->ctrl); + int s = 0, rej = 0, t200_reset = 0; + int i; + + /* supervisory frame ? */ + if (LAPDm_CTRL_is_S(mctx->ctrl)) + s = 1; + /* REJ frame ? */ + if (s && LAPDm_CTRL_S_BITS(mctx->ctrl) == LAPDm_S_REJ) + rej = 1; + + /* Flush all transmit buffers of acknowledged frames */ + for (i = dl->V_ack; i != nr; i = inc_mod8(i)) { + if (dl->tx_length[i]) { + dl->tx_length[i] = 0; + LOGP(DLAPDM, LOGL_INFO, "ack frame %d\n", i); + } + } + + if (dl->state != LAPDm_STATE_TIMER_RECOV) { + /* When not in the timer recovery condition, the data + * link layer entity shall reset the timer T200 on + * receipt of a valid I frame with N(R) higher than V(A), + * or an REJ with an N(R) equal to V(A). */ + if ((!rej && nr != dl->V_ack) + || (rej && nr == dl->V_ack)) { + LOGP(DLAPDM, LOGL_INFO, "reset t200\n"); + t200_reset = 1; + bsc_del_timer(&dl->t200); + /* 5.5.3.1 Note 1 + 2 imply timer recovery cond. */ + } + /* 5.7.4: N(R) sequence error + * N(R) is called valid, if and only if + * (N(R)-V(A)) mod 8 <= (V(S)-V(A)) mod 8. + */ + if (sub_mod8(nr, dl->V_ack) > sub_mod8(dl->V_send, dl->V_ack)) { + LOGP(DLAPDM, LOGL_NOTICE, "N(R) sequence error\n"); + rsl_rll_error(RLL_CAUSE_SEQ_ERR, mctx); + } + } + + /* V(A) shall be set to the value of N(R) */ + dl->V_ack = nr; + + /* If T200 has been reset by the receipt of an I, RR or RNR frame, + * and if there are outstanding I frames, restart T200 */ + if (t200_reset && !rej) { + if (dl->tx_length[dl->V_send - 1]) { + LOGP(DLAPDM, LOGL_INFO, "start T200, due to unacked I " + "frame(s)\n"); + bsc_schedule_timer(&dl->t200, T200); + } + } +} + +/* L1 -> L2 */ + +/* Receive a LAPDm U (Unnumbered) message from L1 */ +static int lapdm_rx_u(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t length; + int rc; + int rsl_msg; + + switch (LAPDm_CTRL_U_BITS(mctx->ctrl)) { + case LAPDm_U_SABM: + rsl_msg = RSL_MT_EST_IND; + + LOGP(DLAPDM, LOGL_INFO, "SABM received\n"); + /* 5.7.1 */ + dl->seq_err_cond = 0; + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "SABM response error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + /* G.4.5 If SABM is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if ((msg->l2h[2] & LAPDm_MORE) || length + 3 > mctx->n201) { + LOGP(DLAPDM, LOGL_NOTICE, "SABM too large error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx); + return -EIO; + } + + /* Must be Format B */ + rc = check_length_ind(mctx, msg->l2h[2]); + if (rc < 0) { + msgb_free(msg); + return rc; + } + switch (dl->state) { + case LAPDm_STATE_IDLE: + /* Set chan_nr and link_id for established connection */ + memset(&dl->mctx, 0, sizeof(dl->mctx)); + dl->mctx.dl = dl; + dl->mctx.chan_nr = mctx->chan_nr; + dl->mctx.link_id = mctx->link_id; + break; + case LAPDm_STATE_MF_EST: + if (length == 0) { + rsl_msg = RSL_MT_EST_CONF; + break; + } + LOGP(DLAPDM, LOGL_INFO, "SABM command, multiple " + "frame established state\n"); + /* check for contention resoultion */ + if (dl->tx_hist[0][2] >> 2) { + LOGP(DLAPDM, LOGL_NOTICE, "SABM not allowed " + "during contention resolution\n"); + rsl_rll_error(RLL_CAUSE_SABM_INFO_NOTALL, mctx); + } + msgb_free(msg); + return 0; + case LAPDm_STATE_DISC_SENT: + /* 5.4.6.2 send DM with F=P */ + lapdm_send_dm(mctx); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_CONF, mctx); + default: + lapdm_send_ua(mctx, length, msg->l2h + 3); + msgb_free(msg); + return 0; + } + /* send UA response */ + lapdm_send_ua(mctx, length, msg->l2h + 3); + /* set Vs, Vr and Va to 0 */ + dl->V_send = dl->V_recv = dl->V_ack = 0; + /* clear tx_hist */ + dl->tx_length[0] = 0; + /* enter multiple-frame-established state */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* send notification to L3 */ + if (length == 0) { + /* 5.4.1.2 Normal establishment procedures */ + rc = send_rll_simple(rsl_msg, mctx); + msgb_free(msg); + } else { + /* 5.4.1.4 Contention resolution establishment */ + msg->l3h = msg->l2h + 3; + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3(rsl_msg, mctx, msg); + } + break; + case LAPDm_U_DM: + LOGP(DLAPDM, LOGL_INFO, "DM received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) { + LOGP(DLAPDM, LOGL_NOTICE, "DM command error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + if (!LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + /* 5.4.1.2 DM responses with the F bit set to "0" + * shall be ignored. + */ + msgb_free(msg); + return 0; + } + switch (dl->state) { + case LAPDm_STATE_SABM_SENT: + break; + case LAPDm_STATE_MF_EST: + if (LAPDm_CTRL_PF_BIT(mctx->ctrl) == 1) { + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM " + "response\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP, mctx); + } else { + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM " + "response, multiple frame established " + "state\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP_MF, mctx); + } + msgb_free(msg); + return 0; + case LAPDm_STATE_TIMER_RECOV: + /* DM is normal in case PF = 1 */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl) == 0) { + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM " + "response, multiple frame established " + "state\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_DM_RESP_MF, mctx); + msgb_free(msg); + return 0; + } + break; + case LAPDm_STATE_DISC_SENT: + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + rc = send_rll_simple(RSL_MT_REL_CONF, mctx); + msgb_free(msg); + return 0; + case LAPDm_STATE_IDLE: + /* 5.4.5 all other frame types shall be discarded */ + default: + LOGP(DLAPDM, LOGL_INFO, "unsolicited DM response! " + "(discarding)\n"); + msgb_free(msg); + return 0; + } + /* reset T200 */ + bsc_del_timer(&dl->t200); + rc = send_rll_simple(RSL_MT_REL_IND, mctx); + msgb_free(msg); + break; + case LAPDm_U_UI: + LOGP(DLAPDM, LOGL_INFO, "UI received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "UI indicates response " + "error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + /* FIXME: G.4.5 If UI is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + + if (mctx->lapdm_fmt == LAPDm_FMT_B4) { + length = N201_B4; + msg->l3h = msg->l2h + 2; + } else { + rc = check_length_ind(mctx, msg->l2h[2]); + if (rc < 0) { + msgb_free(msg); + return rc; + } + length = msg->l2h[2] >> 2; + msg->l3h = msg->l2h + 3; + } + /* do some length checks */ + if (length == 0) { + /* 5.3.3 UI frames received with the length indicator + * set to "0" shall be ignored + */ + LOGP(DLAPDM, LOGL_INFO, "length=0 (discarding)\n"); + msgb_free(msg); + return 0; + } + switch (LAPDm_ADDR_SAPI(mctx->addr)) { + case LAPDm_SAPI_NORMAL: + case LAPDm_SAPI_SMS: + break; + default: + /* 5.3.3 UI frames with invalid SAPI values shall be + * discarded + */ + LOGP(DLAPDM, LOGL_INFO, "sapi=%u (discarding)\n", + LAPDm_ADDR_SAPI(mctx->addr)); + msgb_free(msg); + return 0; + } + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, mctx, msg); + break; + case LAPDm_U_DISC: + rsl_msg = RSL_MT_REL_IND; + + LOGP(DLAPDM, LOGL_INFO, "DISC received\n"); + /* flush buffers */ + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + /* 5.7.1 */ + dl->seq_err_cond = 0; + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "DISC response error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + length = msg->l2h[2] >> 2; + if (length > 0 || msg->l2h[2] & 0x02) { + /* G.4.4 If a DISC or DM frame is received with L>0 or + * with the M bit set to "1", an MDL-ERROR-INDICATION + * primitive with cause "U frame with incorrect + * parameters" is sent to the mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, + "U frame iwth incorrect parameters "); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx); + return -EIO; + } + msgb_free(msg); + switch (dl->state) { + case LAPDm_STATE_IDLE: + LOGP(DLAPDM, LOGL_INFO, "DISC in idle state\n"); + /* send DM with F=P */ + return lapdm_send_dm(mctx); + case LAPDm_STATE_SABM_SENT: + LOGP(DLAPDM, LOGL_INFO, "DISC in SABM state\n"); + /* 5.4.6.2 send DM with F=P */ + lapdm_send_dm(mctx); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + return send_rll_simple(RSL_MT_REL_IND, mctx); + case LAPDm_STATE_MF_EST: + case LAPDm_STATE_TIMER_RECOV: + LOGP(DLAPDM, LOGL_INFO, "DISC in est state\n"); + break; + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_INFO, "DISC in disc state\n"); + rsl_msg = RSL_MT_REL_CONF; + break; + default: + lapdm_send_ua(mctx, length, msg->l2h + 3); + return 0; + } + /* send UA response */ + lapdm_send_ua(mctx, length, msg->l2h + 3); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* enter idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* send notification to L3 */ + rc = send_rll_simple(rsl_msg, mctx); + break; + case LAPDm_U_UA: + LOGP(DLAPDM, LOGL_INFO, "UA received\n"); + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) { + LOGP(DLAPDM, LOGL_NOTICE, "UA indicates command " + "error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + /* G.4.5 If UA is received with L>N201 or with M bit + * set, AN MDL-ERROR-INDICATION is sent to MM. + */ + if ((msg->l2h[2] & LAPDm_MORE) || length + 3 > mctx->n201) { + LOGP(DLAPDM, LOGL_NOTICE, "UA too large error\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_UFRM_INC_PARAM, mctx); + return -EIO; + } + + if (!LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + /* 5.4.1.2 A UA response with the F bit set to "0" + * shall be ignored. + */ + LOGP(DLAPDM, LOGL_INFO, "F=0 (discarding)\n"); + msgb_free(msg); + return 0; + } + switch (dl->state) { + case LAPDm_STATE_SABM_SENT: + break; + case LAPDm_STATE_MF_EST: + case LAPDm_STATE_TIMER_RECOV: + LOGP(DLAPDM, LOGL_INFO, "unsolicited UA response! " + "(discarding)\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_UA_RESP, mctx); + msgb_free(msg); + return 0; + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_INFO, "UA in disconnect state\n"); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + rc = send_rll_simple(RSL_MT_REL_CONF, mctx); + msgb_free(msg); + return 0; + case LAPDm_STATE_IDLE: + /* 5.4.5 all other frame types shall be discarded */ + default: + LOGP(DLAPDM, LOGL_INFO, "unsolicited UA response! " + "(discarding)\n"); + msgb_free(msg); + return 0; + } + LOGP(DLAPDM, LOGL_INFO, "UA in SABM state\n"); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* compare UA with SABME if contention resolution is applied */ + if (dl->tx_hist[0][2] >> 2) { + rc = check_length_ind(mctx, msg->l2h[2]); + if (rc < 0) { + rc = send_rll_simple(RSL_MT_REL_IND, mctx); + msgb_free(msg); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + return 0; + } + length = msg->l2h[2] >> 2; + if (length != (dl->tx_hist[0][2] >> 2) + || !!memcmp(dl->tx_hist[0] + 3, msg->l2h + 3, + length)) { + LOGP(DLAPDM, LOGL_INFO, "UA response " + "mismatches\n"); + rc = send_rll_simple(RSL_MT_REL_IND, mctx); + msgb_free(msg); + /* go to idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + return 0; + } + } + /* set Vs, Vr and Va to 0 */ + dl->V_send = dl->V_recv = dl->V_ack = 0; + /* clear tx_hist */ + dl->tx_length[0] = 0; + /* enter multiple-frame-established state */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* send outstanding frames, if any (resume / reconnect) */ + rslms_send_i(mctx); + /* send notification to L3 */ + rc = send_rll_simple(RSL_MT_EST_CONF, mctx); + msgb_free(msg); + break; + default: + /* G.3.1 */ + LOGP(DLAPDM, LOGL_NOTICE, "Unnumbered frame not allowed.\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + return rc; +} + +/* Receive a LAPDm S (Supervisory) message from L1 */ +static int lapdm_rx_s(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t length; + + length = msg->l2h[2] >> 2; + if (length > 0 || msg->l2h[2] & 0x02) { + /* G.4.3 If a supervisory frame is received with L>0 or + * with the M bit set to "1", an MDL-ERROR-INDICATION + * primitive with cause "S frame with incorrect + * parameters" is sent to the mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, + "S frame with incorrect parameters\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_SFRM_INC_PARAM, mctx); + return -EIO; + } + /* 5.4.2.2: Inidcate error on supervisory reponse F=1 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + LOGP(DLAPDM, LOGL_NOTICE, "S frame response with F=1 error\n"); + rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx); + } + + switch (dl->state) { + case LAPDm_STATE_IDLE: + /* if P=1, respond DM with F=1 (5.2.2) */ + /* 5.4.5 all other frame types shall be discarded */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) + lapdm_send_dm(mctx); /* F=P */ + /* fall though */ + case LAPDm_STATE_SABM_SENT: + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_NOTICE, "S frame ignored in this state\n"); + msgb_free(msg); + return 0; + } + switch (LAPDm_CTRL_S_BITS(mctx->ctrl)) { + case LAPDm_S_RR: + LOGP(DLAPDM, LOGL_INFO, "RR received\n"); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); + + /* 5.5.3.2 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGP(DLAPDM, LOGL_NOTICE, "RR frame command with polling bit set and we are not busy, so we reply with RR frame\n"); + lapdm_send_rr(mctx, 1); + /* NOTE: In case of sequence error condition, + * the REJ frame has been transmitted when + * entering the condition, so it has not be + * done here + */ + } else if (dl->own_busy) { + LOGP(DLAPDM, LOGL_NOTICE, "RR frame command with polling bit set and we are busy, so we reply with RR frame\n"); + lapdm_send_rnr(mctx, 1); + } + } + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx); + + break; + case LAPDm_S_RNR: + LOGP(DLAPDM, LOGL_INFO, "RNR received\n"); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); + + /* 5.5.5 */ + /* Set peer receiver busy condition */ + dl->peer_busy = 1; + + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD) { + if (!dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "RNR poll " + "command and we are not busy, " + "so we reply with RR final " + "response\n"); + /* Send RR with F=1 */ + lapdm_send_rr(mctx, 1); + } else { + LOGP(DLAPDM, LOGL_INFO, "RNR poll " + "command and we are busy, so " + "we reply with RNR final " + "response\n"); + /* Send RNR with F=1 */ + lapdm_send_rnr(mctx, 1); + } + } else if (dl->state == LAPDm_STATE_TIMER_RECOV) { + LOGP(DLAPDM, LOGL_INFO, "RNR poll response " + "and we in timer recovery state, so " + "we leave that state\n"); + /* Clear timer recovery condition */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* V(S) to the N(R) in the RNR frame */ + dl->V_send = LAPDm_CTRL_Nr(mctx->ctrl); + } + } else + LOGP(DLAPDM, LOGL_INFO, "RNR not polling/final state " + "received\n"); + + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx); + + break; + case LAPDm_S_REJ: + LOGP(DLAPDM, LOGL_INFO, "REJ received\n"); + /* 5.5.3.1: Acknowlege all tx frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); + + /* 5.5.4.1 */ + if (dl->state != LAPDm_STATE_TIMER_RECOV) { + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* 5.5.3.2 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command not in timer recovery " + "state and not in own busy " + "condition received, so we " + "respond with RR final " + "response\n"); + lapdm_send_rr(mctx, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command not in timer recovery " + "state and in own busy " + "condition received, so we " + "respond with RNR final " + "response\n"); + lapdm_send_rnr(mctx, 1); + } + } else + LOGP(DLAPDM, LOGL_INFO, "REJ response or not " + "polling command not in timer recovery " + "state received\n"); + /* send MDL ERROR INIDCATION to L3 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + rsl_rll_error(RLL_CAUSE_UNSOL_SPRV_RESP, mctx); + } + + } else if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll response in timer " + "recovery state received\n"); + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* Clear timer recovery condition */ + lapdm_dl_newstate(dl, LAPDm_STATE_MF_EST); + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + } else { + /* Clear an existing peer receiver busy condition */ + dl->peer_busy = 0; + /* V(S) and V(A) to the N(R) in the REJ frame */ + dl->V_send = dl->V_ack = LAPDm_CTRL_Nr(mctx->ctrl); + /* 5.5.3.2 */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_CMD + && LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + if (!dl->own_busy && !dl->seq_err_cond) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command in timer recovery " + "state and not in own busy " + "condition received, so we " + "respond with RR final " + "response\n"); + lapdm_send_rr(mctx, 1); + /* NOTE: In case of sequence error + * condition, the REJ frame has been + * transmitted when entering the + * condition, so it has not be done + * here + */ + } else if (dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "REJ poll " + "command in timer recovery " + "state and in own busy " + "condition received, so we " + "respond with RNR final " + "response\n"); + lapdm_send_rnr(mctx, 1); + } + } else + LOGP(DLAPDM, LOGL_INFO, "REJ response or not " + "polling command in timer recovery " + "state received\n"); + } + + /* FIXME: 5.5.4.2 2) */ + + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx); + + break; + default: + /* G.3.1 */ + LOGP(DLAPDM, LOGL_NOTICE, "Supervisory frame not allowed.\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + msgb_free(msg); + return 0; +} + +/* Receive a LAPDm I (Information) message from L1 */ +static int lapdm_rx_i(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + //uint8_t nr = LAPDm_CTRL_Nr(mctx->ctrl); + uint8_t ns = LAPDm_CTRL_I_Ns(mctx->ctrl); + uint8_t length; + int rc; + + LOGP(DLAPDM, LOGL_NOTICE, "I received\n"); + + /* G.2.2 Wrong value of the C/R bit */ + if (LAPDm_ADDR_CR(mctx->addr) == CR_BS2MS_RESP) { + LOGP(DLAPDM, LOGL_NOTICE, "I frame response not allowed\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + length = msg->l2h[2] >> 2; + if (length == 0 || length + 3 > mctx->n201) { + /* G.4.2 If the length indicator of an I frame is set + * to a numerical value L>N201 or L=0, an MDL-ERROR-INDICATION + * primitive with cause "I frame with incorrect length" + * is sent to the mobile management entity. */ + LOGP(DLAPDM, LOGL_NOTICE, "I frame length not allowed\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_IFRM_INC_LEN, mctx); + return -EIO; + } + + /* G.4.2 If the numerical value of L is L<N201 and the M + * bit is set to "1", then an MDL-ERROR-INDICATION primitive with + * cause "I frame with incorrect use of M bit" is sent to the + * mobile management entity. */ + if ((msg->l2h[2] & LAPDm_MORE) && length + 3 < mctx->n201) { + LOGP(DLAPDM, LOGL_NOTICE, "I frame with M bit too short\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_IFRM_INC_MBITS, mctx); + return -EIO; + } + + switch (dl->state) { + case LAPDm_STATE_IDLE: + /* if P=1, respond DM with F=1 (5.2.2) */ + /* 5.4.5 all other frame types shall be discarded */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) + lapdm_send_dm(mctx); /* F=P */ + /* fall though */ + case LAPDm_STATE_SABM_SENT: + case LAPDm_STATE_DISC_SENT: + LOGP(DLAPDM, LOGL_NOTICE, "I frame ignored in this state\n"); + msgb_free(msg); + return 0; + } + + /* 5.7.1: N(s) sequence error */ + if (ns != dl->V_recv) { + LOGP(DLAPDM, LOGL_NOTICE, "N(S) sequence error: N(S)=%u, " + "V(R)=%u\n", ns, dl->V_recv); + /* discard data */ + msgb_free(msg); + if (!dl->seq_err_cond) { + /* FIXME: help me understand what exactly todo here + dl->seq_err_cond = 1; + */ + lapdm_send_rej(mctx, LAPDm_CTRL_PF_BIT(mctx->ctrl)); + } else { + } + return -EIO; + } + dl->seq_err_cond = 0; + + /* Increment receiver state */ + dl->V_recv = inc_mod8(dl->V_recv); + LOGP(DLAPDM, LOGL_NOTICE, "incrementing V(R) to %u\n", dl->V_recv); + + /* 5.5.3.1: Acknowlege all transmitted frames up the the N(R)-1 */ + lapdm_acknowledge(mctx); /* V(A) is also set here */ + + /* Only if we are not in own receiver busy condition */ + if (!dl->own_busy) { + /* if the frame carries a complete segment */ + if (!(msg->l2h[2] & LAPDm_MORE) + && !dl->rcv_buffer) { + LOGP(DLAPDM, LOGL_INFO, "message in single I frame\n"); + /* send a DATA INDICATION to L3 */ + msg->l3h = msg->l2h + 3; + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx, msg); + } else { + /* create rcv_buffer */ + if (!dl->rcv_buffer) { + LOGP(DLAPDM, LOGL_INFO, "message in multiple I " + "frames (first message)\n"); + dl->rcv_buffer = msgb_alloc_headroom(200+10, 10, + "LAPDm RX"); + dl->rcv_buffer->l3h = dl->rcv_buffer->data; + } + /* concat. rcv_buffer */ + if (msgb_l3len(dl->rcv_buffer) + length > 200) { + LOGP(DLAPDM, LOGL_NOTICE, "Received frame " + "overflow!\n"); + } else { + memcpy(msgb_put(dl->rcv_buffer, length), + msg->l2h + 3, length); + } + /* if the last segment was received */ + if (!(msg->l2h[2] & LAPDm_MORE)) { + LOGP(DLAPDM, LOGL_INFO, "message in multiple I " + "frames (next message)\n"); + rc = send_rslms_rll_l3(RSL_MT_DATA_IND, mctx, + dl->rcv_buffer); + dl->rcv_buffer = NULL; + } else + LOGP(DLAPDM, LOGL_INFO, "message in multiple I " + "frames (last message)\n"); + msgb_free(msg); + + } + } else + LOGP(DLAPDM, LOGL_INFO, "I frame ignored during own receiver " + "busy condition\n"); + + /* Check for P bit */ + if (LAPDm_CTRL_PF_BIT(mctx->ctrl)) { + /* 5.5.2.1 */ + /* check if we are not in own receiver busy */ + if (!dl->own_busy) { + LOGP(DLAPDM, LOGL_INFO, "we are not busy, send RR\n"); + /* Send RR with F=1 */ + rc = lapdm_send_rr(mctx, 1); + } else { + LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n"); + /* Send RNR with F=1 */ + rc = lapdm_send_rnr(mctx, 1); + } + } else { + /* 5.5.2.2 */ + /* check if we are not in own receiver busy */ + if (!dl->own_busy) { + /* NOTE: V(R) is already set above */ + rc = rslms_send_i(mctx); + if (rc) { + LOGP(DLAPDM, LOGL_INFO, "we are not busy and " + "have no pending data, send RR\n"); + /* Send RR with F=0 */ + return lapdm_send_rr(mctx, 0); + } + } else { + LOGP(DLAPDM, LOGL_INFO, "we are busy, send RNR\n"); + /* Send RNR with F=0 */ + rc = lapdm_send_rnr(mctx, 0); + } + } + + /* Send message, if possible due to acknowledged data */ + rslms_send_i(mctx); + + return rc; +} + +/* Receive a LAPDm message from L1 */ +static int lapdm_ph_data_ind(struct msgb *msg, struct lapdm_msg_ctx *mctx) +{ + int rc; + +#if 0 +printf("-> rx chan_nr 0x%x link_id 0x%x len %d data", mctx->chan_nr, mctx->link_id, msgb_l2len(msg)); +int i; +for (i = 0; i < msgb_l2len(msg); i++) + printf(" %02x", msg->l2h[i]); +printf("\n"); +#endif + /* G.2.3 EA bit set to "0" is not allowed in GSM */ + if (!LAPDm_ADDR_EA(mctx->addr)) { + LOGP(DLAPDM, LOGL_NOTICE, "EA bit 0 is not allowed in GSM\n"); + msgb_free(msg); + rsl_rll_error(RLL_CAUSE_FRM_UNIMPL, mctx); + return -EINVAL; + } + + if (LAPDm_CTRL_is_U(mctx->ctrl)) + rc = lapdm_rx_u(msg, mctx); + else if (LAPDm_CTRL_is_S(mctx->ctrl)) + rc = lapdm_rx_s(msg, mctx); + else if (LAPDm_CTRL_is_I(mctx->ctrl)) + rc = lapdm_rx_i(msg, mctx); + else { + LOGP(DLAPDM, LOGL_NOTICE, "unknown LAPDm format\n"); + msgb_free(msg); + rc = -EINVAL; + } + return rc; +} + +/* input into layer2 (from layer 1) */ +int l2_ph_data_ind(struct msgb *msg, struct lapdm_entity *le, struct l1ctl_info_dl *l1i) +{ + uint8_t cbits = l1i->chan_nr >> 3; + uint8_t sapi = l1i->link_id & 7; + struct lapdm_msg_ctx mctx; + int rc = 0; + + /* when we reach here, we have a msgb with l2h pointing to the raw + * 23byte mac block. The l1h has already been purged. */ + + mctx.dl = datalink_for_sapi(le, sapi); + mctx.chan_nr = l1i->chan_nr; + mctx.link_id = l1i->link_id; + mctx.addr = mctx.ctrl = 0; + + /* G.2.1 No action schall be taken on frames containing an unallocated + * SAPI. + */ + if (!mctx.dl) { + LOGP(DLAPDM, LOGL_NOTICE, "Received frame for unsupported " + "SAPI %d!\n", sapi); + return -EINVAL; + msgb_free(msg); + return -EIO; + } + + /* check for L1 chan_nr/link_id and determine LAPDm hdr format */ + if (cbits == 0x10 || cbits == 0x12) { + /* Format Bbis is used on BCCH and CCCH(PCH, NCH and AGCH) */ + mctx.lapdm_fmt = LAPDm_FMT_Bbis; + mctx.n201 = N201_Bbis; + } else { + if (mctx.link_id & 0x40) { + /* It was received from network on SACCH, thus + * lapdm_fmt must be B4 */ + mctx.lapdm_fmt = LAPDm_FMT_B4; + mctx.n201 = N201_B4; + LOGP(DLAPDM, LOGL_INFO, "fmt=B4\n"); + /* SACCH frames have a two-byte L1 header that + * OsmocomBB L1 doesn't strip */ + msgb_pull(msg, 2); + msg->l2h += 2; + } else { + mctx.lapdm_fmt = LAPDm_FMT_B; + LOGP(DLAPDM, LOGL_INFO, "fmt=B\n"); + mctx.n201 = 23; // FIXME: select correct size by chan. + } + } + + switch (mctx.lapdm_fmt) { + case LAPDm_FMT_A: + case LAPDm_FMT_B: + case LAPDm_FMT_B4: + mctx.addr = msg->l2h[0]; + if (!(mctx.addr & 0x01)) { + LOGP(DLAPDM, LOGL_ERROR, "we don't support " + "multibyte addresses (discarding)\n"); + msgb_free(msg); + return -EINVAL; + } + mctx.ctrl = msg->l2h[1]; + /* obtain SAPI from address field */ + mctx.link_id |= LAPDm_ADDR_SAPI(mctx.addr); + rc = lapdm_ph_data_ind(msg, &mctx); + break; + case LAPDm_FMT_Bter: + /* FIXME */ + msgb_free(msg); + break; + case LAPDm_FMT_Bbis: + /* directly pass up to layer3 */ + LOGP(DLAPDM, LOGL_INFO, "fmt=Bbis UI\n"); + msg->l3h = msg->l2h; + msgb_pull_l2h(msg); + rc = send_rslms_rll_l3(RSL_MT_UNIT_DATA_IND, &mctx, msg); + break; + default: + msgb_free(msg); + } + + return rc; +} + +/* L3 -> L2 / RSLMS -> LAPDm */ + +/* L3 requests establishment of data link */ +static int rslms_rx_rll_est_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = rllh->link_id & 7; + struct tlv_parsed tv; + uint8_t length; + uint8_t n201 = 23; //FIXME + + /* Set chan_nr and link_id for established connection */ + memset(&dl->mctx, 0, sizeof(dl->mctx)); + dl->mctx.dl = dl; + dl->mctx.n201 = n201; + dl->mctx.chan_nr = chan_nr; + dl->mctx.link_id = link_id; + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + /* contention resolution establishment procedure */ + if (sapi != 0) { + /* According to clause 6, the contention resolution + * procedure is only permitted with SAPI value 0 */ + LOGP(DLAPDM, LOGL_ERROR, "SAPI != 0 but contention" + "resolution (discarding)\n"); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + } + /* transmit a SABM command with the P bit set to "1". The SABM + * command shall contain the layer 3 message unit */ + length = TLVP_LEN(&tv, RSL_IE_L3_INFO); + LOGP(DLAPDM, LOGL_INFO, "perform establishment with content " + "(SAMB)\n"); + } else { + /* normal establishment procedure */ + length = 0; + LOGP(DLAPDM, LOGL_INFO, "perform normal establishm. (SAMB)\n"); + } + + /* check if the layer3 message length exceeds N201 */ + if (length + 3 > 21) { /* FIXME: do we know the channel N201? */ + LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) " + "(discarding)\n", length + 3, 21); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + } + + /* Flush send-queue */ + /* Clear send-buffer */ + lapdm_dl_flush_send(dl); + + /* Discard partly received L3 message */ + if (dl->rcv_buffer) { + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + } + + /* Remove RLL header from msgb */ + msgb_pull_l2h(msg); + + /* Push LAPDm header on msgb */ + msg->l2h = msgb_push(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_SABM, 1); + msg->l2h[2] = LAPDm_LEN(length); + /* Transmit-buffer carries exactly one segment */ + memcpy(dl->tx_hist[0], msg->l2h, 3 + length); + dl->tx_length[0] = 3 + length; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT); + + /* Tramsmit and start T200 */ + bsc_schedule_timer(&dl->t200, T200); + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201); +} + +/* L3 requests transfer of unnumbered information */ +static int rslms_rx_rll_udata_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = link_id & 7; + struct tlv_parsed tv; + int length; + uint8_t n201 = 23; //FIXME + + /* check if the layer3 message length exceeds N201 */ + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + + length = TLVP_LEN(&tv, RSL_IE_L3_INFO); + /* check if the layer3 message length exceeds N201 */ + if (length + 3 > 18) { /* FIXME: do we know the channel N201? */ + LOGP(DLAPDM, LOGL_ERROR, "frame too large: %d > N201(%d) " + "(discarding)\n", length + 3, 18); + msgb_free(msg); + return -EIO; + } + + LOGP(DLAPDM, LOGL_INFO, "sending unit data\n"); + + /* Remove RLL header from msgb */ + msgb_pull_l2h(msg); + + /* Push LAPDm header on msgb */ + msg->l2h = msgb_push(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_UI, 0); + msg->l2h[2] = LAPDm_LEN(length); + // FIXME: short L2 header support + + /* Tramsmit */ + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201); +} + +/* L3 requests transfer of acknowledged information */ +static int rslms_rx_rll_data_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + LOGP(DLAPDM, LOGL_ERROR, "data request without message error\n"); + msgb_free(msg); + return -EINVAL; + } + + LOGP(DLAPDM, LOGL_INFO, "writing message to send-queue\n"); + + /* Remove the RSL/RLL header */ + msgb_pull_l2h(msg); + + /* Write data into the send queue */ + msgb_enqueue(&dl->send_queue, msg); + + /* Send message, if possible */ + rslms_send_i(&dl->mctx); + return 0; +} + +/* Send next I frame from queued/buffered data */ +static int rslms_send_i(struct lapdm_msg_ctx *mctx) +{ + struct lapdm_datalink *dl = mctx->dl; + uint8_t chan_nr = mctx->chan_nr; + uint8_t link_id = mctx->link_id; + uint8_t sapi = link_id & 7; + int k = k_sapi[sapi]; + struct msgb *msg; + int length, left; + int rc = -1; /* we sent nothing */ + + next_frame: + + if (dl->peer_busy) { + LOGP(DLAPDM, LOGL_INFO, "peer busy, not sending\n"); + return rc; + } + + if (dl->state == LAPDm_STATE_TIMER_RECOV) { + LOGP(DLAPDM, LOGL_INFO, "timer recovery, not sending\n"); + return rc; + } + + /* If the send state variable V(S) is equal to V(A) plus k + * (where k is the maximum number of outstanding I frames - see + * subclause 5.8.4), the data link layer entity shall not transmit any + * new I frames, but shall retransmit an I frame as a result + * of the error recovery procedures as described in subclauses 5.5.4 and + * 5.5.7. */ + if (dl->V_send == add_mod8(dl->V_ack, k)) { + LOGP(DLAPDM, LOGL_INFO, "k frames outstanding, not sending " + "more (k=%u V(S)=%u V(A)=%u)\n", k, dl->V_send, + dl->V_ack); + return rc; + } + + /* if we have no tx_hist yet, we create it */ + if (!dl->tx_length[dl->V_send]) { + /* Get next message into send-buffer, if any */ + if (!dl->send_buffer) { + next_message: + dl->send_out = 0; + dl->send_buffer = msgb_dequeue(&dl->send_queue); + /* No more data to be sent */ + if (!dl->send_buffer) + return rc; + LOGP(DLAPDM, LOGL_INFO, "get message from " + "send-queue\n"); + } + + /* How much is left in the send-buffer? */ + left = msgb_l3len(dl->send_buffer) - dl->send_out; + /* Segment, if data exceeds N201 */ + length = left; + if (length > mctx->n201 - 3) + length = mctx->n201 - 3; +printf("msg-len %d sent %d left %d N201 %d length %d first byte %02x\n", msgb_l3len(dl->send_buffer), dl->send_out, left, mctx->n201, length, dl->send_buffer->l3h[0]); + /* If message in send-buffer is completely sent */ + if (left == 0) { + msgb_free(dl->send_buffer); + dl->send_buffer = NULL; + goto next_message; + } + + LOGP(DLAPDM, LOGL_INFO, "send I frame %sV(S)=%d\n", + (left > length) ? "segment " : "", dl->V_send); + + /* Create I frame (segment) and transmit-buffer content */ + msg = msgb_alloc_headroom(23+10, 10, "LAPDm I"); + msg->l2h = msgb_put(msg, 3 + length); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, dl->V_send, 0); + msg->l2h[2] = LAPDm_LEN(length); + if (left > length) + msg->l2h[2] |= LAPDm_MORE; + memcpy(msg->l2h + 3, dl->send_buffer->l3h + dl->send_out, + length); + memcpy(dl->tx_hist[dl->V_send], msg->l2h, 3 + length); + dl->tx_length[dl->V_send] = 3 + length; + /* Add length to track how much is already in the tx buffer */ + dl->send_out += length; + } else { + LOGP(DLAPDM, LOGL_INFO, "resend I frame from tx buffer " + "V(S)=%d\n", dl->V_send); + + /* Create I frame (segment) from tx_hist */ + length = dl->tx_length[dl->V_send]; + msg = msgb_alloc_headroom(23+10, 10, "LAPDm I"); + msg->l2h = msgb_put(msg, length); + memcpy(msg->l2h, dl->tx_hist[dl->V_send], length); + msg->l2h[1] = LAPDm_CTRL_I(dl->V_recv, dl->V_send, 0); + } + + /* The value of the send state variable V(S) shall be incremented by 1 + * at the end of the transmission of the I frame */ + dl->V_send = inc_mod8(dl->V_send); + + /* If timer T200 is not running at the time right before transmitting a + * frame, when the PH-READY-TO-SEND primitive is received from the + * physical layer., it shall be set. */ + if (!bsc_timer_pending(&dl->t200)) + bsc_schedule_timer(&dl->t200, T200); + + tx_ph_data_enqueue(dl, msg, chan_nr, link_id, mctx->n201); + + rc = 0; /* we sent something */ + goto next_frame; +} + +/* L3 requests suspension of data link */ +static int rslms_rx_rll_susp_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t sapi = rllh->link_id & 7; + + if (sapi != 0) { + LOGP(DLAPDM, LOGL_ERROR, "SAPI != 0 while suspending\n"); + msgb_free(msg); + return -EINVAL; + } + + LOGP(DLAPDM, LOGL_INFO, "perform suspension\n"); + + /* put back the send-buffer to the send-queue (first position) */ + if (dl->send_buffer) { + llist_add(&dl->send_buffer->list, &dl->send_queue); + dl->send_buffer = NULL; + } + + /* Clear transmit and send buffer, if any */ + lapdm_dl_flush_tx(dl); + lapdm_dl_flush_send(dl); + + msgb_free(msg); + + return send_rll_simple(RSL_MT_SUSP_CONF, &dl->mctx); +} + +/* L3 requests resume of data link */ +static int rslms_rx_rll_res_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = rllh->link_id & 7; + struct tlv_parsed tv; + uint8_t length; + uint8_t n201 = 23; //FIXME + + /* Set chan_nr and link_id for established connection */ + memset(&dl->mctx, 0, sizeof(dl->mctx)); + dl->mctx.dl = dl; + dl->mctx.n201 = n201; + dl->mctx.chan_nr = chan_nr; + dl->mctx.link_id = link_id; + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + LOGP(DLAPDM, LOGL_ERROR, "resume without message error\n"); + msgb_free(msg); + return send_rll_simple(RSL_MT_REL_IND, &dl->mctx); + } + length = TLVP_LEN(&tv, RSL_IE_L3_INFO); + + LOGP(DLAPDM, LOGL_INFO, "perform re-establishment (SAMB)\n"); + + /* Replace message in the send-buffer (reconnect) */ + if (dl->send_buffer) + msgb_free(dl->send_buffer); + dl->send_buffer = msg; + + /* Discard partly received L3 message */ + if (dl->rcv_buffer) { + msgb_free(dl->rcv_buffer); + dl->rcv_buffer = NULL; + } + + /* Create new msgb (old one is now free) */ + msg = msgb_alloc_headroom(23+10, 10, "LAPDm SABM"); + msg->l2h = msgb_put(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_SABM, 1); + msg->l2h[2] = LAPDm_LEN(0); + /* Transmit-buffer carries exactly one segment */ + memcpy(dl->tx_hist[0], msg->l2h, 3); + dl->tx_length[0] = 3; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_SABM_SENT); + + /* Tramsmit and start T200 */ + bsc_schedule_timer(&dl->t200, T200); + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, n201); +} + +/* L3 requests release of data link */ +static int rslms_rx_rll_rel_req(struct msgb *msg, struct lapdm_datalink *dl) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + uint8_t chan_nr = rllh->chan_nr; + uint8_t link_id = rllh->link_id; + uint8_t sapi = rllh->link_id & 7; + uint8_t mode = 0; + + /* get release mode */ + if (rllh->data[0] == RSL_IE_RELEASE_MODE) + mode = rllh->data[1] & 1; + + /* local release */ + if (mode) { + LOGP(DLAPDM, LOGL_INFO, "perform local release\n"); + msgb_free(msg); + /* reset Timer T200 */ + bsc_del_timer(&dl->t200); + /* enter idle state */ + lapdm_dl_newstate(dl, LAPDm_STATE_IDLE); + /* send notification to L3 */ + return send_rll_simple(RSL_MT_REL_CONF, &dl->mctx); + } else + LOGP(DLAPDM, LOGL_INFO, "perform normal release (DISC)\n"); + + /* Create new msgb */ + msgb_pull_l2h(msg); + + /* Push LAPDm header on msgb */ + msg->l2h = msgb_push(msg, 3); + msg->l2h[0] = LAPDm_ADDR(LAPDm_LPD_NORMAL, sapi, CR_MS2BS_CMD); + msg->l2h[1] = LAPDm_CTRL_U(LAPDm_U_DISC, 1); + msg->l2h[2] = LAPDm_LEN(0); + /* Transmit-buffer carries exactly one segment */ + memcpy(dl->tx_hist[0], msg->l2h, 3); + dl->tx_length[0] = 3; + + /* Set states */ + dl->own_busy = dl->peer_busy = 0; + dl->retrans_ctr = 0; + lapdm_dl_newstate(dl, LAPDm_STATE_DISC_SENT); + + /* Tramsmit and start T200 */ + bsc_schedule_timer(&dl->t200, T200); + return tx_ph_data_enqueue(dl, msg, chan_nr, link_id, dl->mctx.n201); +} + +/* L3 requests release in idle state */ +static int rslms_rx_rll_rel_req_idle(struct msgb *msg, struct lapdm_datalink *dl) +{ + msgb_free(msg); + + /* send notification to L3 */ + return send_rll_simple(RSL_MT_REL_CONF, &dl->mctx); +} + +/* Names for Radio Link Layer Management */ +static const struct value_string rsl_msg_names[] = { + { RSL_MT_DATA_REQ, "RSL_MT_DATA_REQ" }, + { RSL_MT_DATA_IND, "RSL_MT_DATA_IND" }, + { RSL_MT_ERROR_IND, "RSL_MT_ERROR_IND" }, + { RSL_MT_EST_REQ, "RSL_MT_EST_REQ" }, + { RSL_MT_EST_CONF, "RSL_MT_EST_CONF" }, + { RSL_MT_EST_IND, "RSL_MT_EST_IND" }, + { RSL_MT_EST_IND, "RSL_MT_REL_REQ" }, + { RSL_MT_REL_REQ, "RSL_MT_REL_REQ" }, + { RSL_MT_REL_CONF, "RSL_MT_REL_CONF" }, + { RSL_MT_REL_IND, "RSL_MT_REL_IND" }, + { RSL_MT_UNIT_DATA_REQ, "RSL_MT_UNIT_DATA_REQ" }, + { RSL_MT_UNIT_DATA_IND, "RSL_MT_UNIT_DATA_IND" }, + { RSL_MT_SUSP_REQ, "RSL_MT_SUSP_REQ" }, + { RSL_MT_SUSP_CONF, "RSL_MT_SUSP_CONF" }, + { RSL_MT_RES_REQ, "RSL_MT_RES_REQ" }, + { RSL_MT_RECON_REQ, "RSL_MT_RECON_REQ" }, + { 0, NULL } +}; + +const char *get_rsl_name(int value) +{ + return get_value_string(rsl_msg_names, value); +} + +const char *lapdm_state_names[] = { + "LAPDm_STATE_NULL", + "LAPDm_STATE_IDLE", + "LAPDm_STATE_SABM_SENT", + "LAPDm_STATE_MF_EST", + "LAPDm_STATE_TIMER_RECOV", + "LAPDm_STATE_DISC_SENT", +}; + +/* statefull handling for RSLms RLL messages from L3 */ +static struct l2downstate { + uint32_t states; + int type; + int (*rout) (struct msgb *msg, struct lapdm_datalink *dl); +} l2downstatelist[] = { + /* create and send UI command */ + {ALL_STATES, + RSL_MT_UNIT_DATA_REQ, rslms_rx_rll_udata_req}, + + /* create and send SABM command */ + {SBIT(LAPDm_STATE_IDLE), + RSL_MT_EST_REQ, rslms_rx_rll_est_req}, + + /* create and send I command */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_DATA_REQ, rslms_rx_rll_data_req}, + + /* suspend datalink */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_SUSP_REQ, rslms_rx_rll_susp_req}, + + /* create and send SABM command (resume) */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_RES_REQ, rslms_rx_rll_res_req}, + + /* create and send SABM command (reconnect) */ + {SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_RECON_REQ, rslms_rx_rll_res_req}, + + /* create and send DISC command */ + {SBIT(LAPDm_STATE_SABM_SENT) | + SBIT(LAPDm_STATE_MF_EST) | + SBIT(LAPDm_STATE_TIMER_RECOV), + RSL_MT_REL_REQ, rslms_rx_rll_rel_req}, + + /* release in idle state */ + {SBIT(LAPDm_STATE_IDLE), + RSL_MT_REL_REQ, rslms_rx_rll_rel_req_idle}, + + /* FIXME: create and send DISC command */ +}; + +#define L2DOWNSLLEN \ + (sizeof(l2downstatelist) / sizeof(struct l2downstate)) + +/* incoming RSLms RLL message from L3 */ +static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int msg_type = rllh->c.msg_type; + uint8_t sapi = rllh->link_id & 7; + struct lapdm_entity *le; + struct lapdm_datalink *dl; + int i, supported = 0; + int rc = 0; + + if (rllh->link_id & 0x40) + le = &ms->l2_entity.lapdm_acch; + else + le = &ms->l2_entity.lapdm_dcch; + + /* G.2.1 No action schall be taken on frames containing an unallocated + * SAPI. + */ + dl = datalink_for_sapi(le, sapi); + if (!dl) { + LOGP(DRSL, LOGL_ERROR, "No instance for SAPI %d!\n", sapi); + return -EINVAL; + } + + LOGP(DRSL, LOGL_INFO, "(ms %s) RLL Message '%s' received in state %s\n", + ms->name, get_rsl_name(msg_type), lapdm_state_names[dl->state]); + + /* find function for current state and message */ + for (i = 0; i < L2DOWNSLLEN; i++) { + if (msg_type == l2downstatelist[i].type) + supported = 1; + if ((msg_type == l2downstatelist[i].type) + && ((1 << dl->state) & l2downstatelist[i].states)) + break; + } + if (!supported) { + LOGP(DRSL, LOGL_NOTICE, "Message unsupported.\n"); + msgb_free(msg); + return 0; + } + if (i == L2DOWNSLLEN) { + LOGP(DRSL, LOGL_NOTICE, "Message unhandled at this state.\n"); + msgb_free(msg); + return 0; + } + + rc = l2downstatelist[i].rout(msg, dl); + + return rc; +} + +/* input into layer2 (from layer 3) */ +int rslms_recvmsg(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = rslms_rx_rll(msg, ms); + break; + default: + LOGP(DRSL, LOGL_ERROR, "unknown RSLms message " + "discriminator 0x%02x", rslh->msg_discr); + msgb_free(msg); + return -EINVAL; + } + + return rc; +} + +/* input function that L2 calls when sending messages up to L3 */ +int rslms_sendmsg(struct msgb *msg, struct osmocom_ms *ms) +{ + if (!ms->l2_entity.msg_handler) { + msgb_free(msg); + return -EIO; + } + + /* call the layer2 message handler that is registered */ + return ms->l2_entity.msg_handler(msg, ms); +} + +/* register message handler for messages that are sent from L2->L3 */ +int osmol2_register_handler(struct osmocom_ms *ms, osmol2_cb_t cb) +{ + ms->l2_entity.msg_handler = cb; + + return 0; +} diff --git a/src/host/layer23/src/layer3.c b/src/host/layer23/src/layer3.c new file mode 100644 index 00000000..978ea104 --- /dev/null +++ b/src/host/layer23/src/layer3.c @@ -0,0 +1,235 @@ +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocore/msgb.h> +#include <osmocore/rsl.h> +#include <osmocore/tlv.h> +#include <osmocore/protocol/gsm_04_08.h> + +#include <osmocom/logging.h> +#include <osmocom/lapdm.h> +#include <osmocom/rslms.h> +#include <osmocom/layer3.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> + +static int ccch_mode = CCCH_MODE_NONE; + +static void dump_bcch(struct osmocom_ms *ms, uint8_t tc, const uint8_t *data) +{ + struct gsm48_system_information_type_header *si_hdr; + si_hdr = (struct gsm48_system_information_type_header *) data; + + /* GSM 05.02 §6.3.1.3 Mapping of BCCH data */ + switch (si_hdr->system_information) { + case GSM48_MT_RR_SYSINFO_1: + fprintf(stderr, "\tSI1"); +#ifdef BCCH_TC_CHECK + if (tc != 0) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_2: + fprintf(stderr, "\tSI2"); +#ifdef BCCH_TC_CHECK + if (tc != 1) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_3: + fprintf(stderr, "\tSI3"); +#ifdef BCCH_TC_CHECK + if (tc != 2 && tc != 6) + fprintf(stderr, " on wrong TC"); +#endif + if (ccch_mode == CCCH_MODE_NONE) { + struct gsm48_system_information_type_3 *si3 = + (struct gsm48_system_information_type_3 *)data; + + if (si3->control_channel_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) + ccch_mode = CCCH_MODE_COMBINED; + else + ccch_mode = CCCH_MODE_NON_COMBINED; + + l1ctl_tx_ccch_mode_req(ms, ccch_mode); + } + break; + case GSM48_MT_RR_SYSINFO_4: + fprintf(stderr, "\tSI4"); +#ifdef BCCH_TC_CHECK + if (tc != 3 && tc != 7) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_5: + fprintf(stderr, "\tSI5"); + break; + case GSM48_MT_RR_SYSINFO_6: + fprintf(stderr, "\tSI6"); + break; + case GSM48_MT_RR_SYSINFO_7: + fprintf(stderr, "\tSI7"); +#ifdef BCCH_TC_CHECK + if (tc != 7) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_8: + fprintf(stderr, "\tSI8"); +#ifdef BCCH_TC_CHECK + if (tc != 3) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_9: + fprintf(stderr, "\tSI9"); +#ifdef BCCH_TC_CHECK + if (tc != 4) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_13: + fprintf(stderr, "\tSI13"); +#ifdef BCCH_TC_CHECK + if (tc != 4 && tc != 0) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_16: + fprintf(stderr, "\tSI16"); +#ifdef BCCH_TC_CHECK + if (tc != 6) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_17: + fprintf(stderr, "\tSI17"); +#ifdef BCCH_TC_CHECK + if (tc != 2) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_2bis: + fprintf(stderr, "\tSI2bis"); +#ifdef BCCH_TC_CHECK + if (tc != 5) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_2ter: + fprintf(stderr, "\tSI2ter"); +#ifdef BCCH_TC_CHECK + if (tc != 5 && tc != 4) + fprintf(stderr, " on wrong TC"); +#endif + break; + case GSM48_MT_RR_SYSINFO_5bis: + fprintf(stderr, "\tSI5bis"); + break; + case GSM48_MT_RR_SYSINFO_5ter: + fprintf(stderr, "\tSI5ter"); + break; + default: + fprintf(stderr, "\tUnknown SI"); + break; + }; + + fprintf(stderr, "\n"); +} + + +/* send location updating request * (as part of RSLms EST IND / + LAPDm SABME) */ +static int gsm48_tx_loc_upd_req(struct osmocom_ms *ms, uint8_t chan_nr) +{ + struct msgb *msg = msgb_alloc_headroom(256, 16, "loc_upd_req"); + struct gsm48_hdr *gh; + struct gsm48_loc_upd_req *lu_r; + + DEBUGP(DMM, "chan_nr=%u\n", chan_nr); + + msg->l3h = msgb_put(msg, sizeof(*gh)); + gh = (struct gsm48_hdr *) msg->l3h; + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_REQUEST; + lu_r = (struct gsm48_loc_upd_req *) msgb_put(msg, sizeof(*lu_r)); + lu_r->type = GSM48_LUPD_IMSI_ATT; + lu_r->key_seq = 0; + /* FIXME: set LAI and CM1 */ + /* FIXME: set MI */ + lu_r->mi_len = 0; + + return rslms_tx_rll_req_l3(ms, RSL_MT_EST_REQ, chan_nr, 0, msg); +} + +static int gsm48_rx_imm_ass(struct msgb *msg, struct osmocom_ms *ms) +{ + struct gsm48_imm_ass *ia = msgb_l3(msg); + uint8_t ch_type, ch_subch, ch_ts; + uint16_t arfcn; + + rsl_dec_chan_nr(ia->chan_desc.chan_nr, &ch_type, &ch_subch, &ch_ts); + arfcn = ia->chan_desc.h0.arfcn_low | (ia->chan_desc.h0.arfcn_high << 8); + + DEBUGP(DRR, "GSM48 IMM ASS (ra=0x%02x, chan_nr=0x%02x, " + "ARFCN=%u, TS=%u, SS=%u, TSC=%u) ", ia->req_ref.ra, + ia->chan_desc.chan_nr, arfcn, ch_ts, ch_subch, + ia->chan_desc.h0.tsc); + + /* FIXME: compare RA and GSM time with when we sent RACH req */ + + /* check if we can support this type of channel at the moment */ + if (ch_type != RSL_CHAN_SDCCH4_ACCH || ch_ts != 0 || + ia->chan_desc.h0.h == 1) { + DEBUGPC(DRR, "UNSUPPORTED!\n"); + return 0; + } + + /* request L1 to go to dedicated mode on assigned channel */ + tx_ph_dm_est_req_h0(ms, arfcn, ia->chan_desc.chan_nr, + ia->chan_desc.h0.tsc, 0); + + /* request L2 to establish the SAPI0 connection */ + gsm48_tx_loc_upd_req(ms, ia->chan_desc.chan_nr); + + DEBUGPC(DRR, "\n"); + + return 0; +} + +int gsm48_rx_ccch(struct msgb *msg, struct osmocom_ms *ms) +{ + struct gsm48_system_information_type_header *sih = msgb_l3(msg); + int rc = 0; + + if (sih->rr_protocol_discriminator != GSM48_PDISC_RR) + LOGP(DRR, LOGL_ERROR, "PCH pdisc != RR\n"); + + switch (sih->system_information) { + case GSM48_MT_RR_PAG_REQ_1: + case GSM48_MT_RR_PAG_REQ_2: + case GSM48_MT_RR_PAG_REQ_3: + /* FIXME: implement decoding of paging request */ + break; + case GSM48_MT_RR_IMM_ASS: + rc = gsm48_rx_imm_ass(msg, ms); + break; + default: + LOGP(DRR, LOGL_NOTICE, "unknown PCH/AGCH type 0x%02x\n", + sih->system_information); + rc = -EINVAL; + } + + return rc; +} + +int gsm48_rx_bcch(struct msgb *msg, struct osmocom_ms *ms) +{ + /* FIXME: we have lost the gsm frame time until here, need to store it + * in some msgb context */ + //dump_bcch(dl->time.tc, ccch->data); + dump_bcch(ms, 0, msg->l3h); + + return 0; +} diff --git a/src/host/layer23/src/logging.c b/src/host/layer23/src/logging.c new file mode 100644 index 00000000..82195327 --- /dev/null +++ b/src/host/layer23/src/logging.c @@ -0,0 +1,112 @@ +/* Logging/Debug support of the layer2/3 stack */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <osmocore/utils.h> +#include <osmocore/logging.h> +#include <osmocom/logging.h> + +static const struct log_info_cat default_categories[] = { + [DRSL] = { + .name = "DRSL", + .description = "Radio Signalling Link (MS)", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DCS] = { + .name = "DCS", + .description = "Cell selection", + .color = "\033[34m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DPLMN] = { + .name = "DPLMN", + .description = "PLMN selection", + .color = "\033[32m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DRR] = { + .name = "DRR", + .description = "Radio Resource", + .color = "\033[1;34m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMM] = { + .name = "DMM", + .description = "Mobility Management", + .color = "\033[1;32m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DCC] = { + .name = "DCC", + .description = "Call Control", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSMS] = { + .name = "DSMS", + .description = "Short Message Service", + .color = "\033[1;37m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMNCC] = { + .name = "DMNCC", + .description = "Mobile Network Call Control", + .color = "\033[1;37m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "MEasurement Reporting", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DPAG] = { + .name = "DPAG", + .description = "Paging", + .color = "\033[33m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DLAPDM] = { + .name = "DLAPDM", + .description = "LAPDm Layer2", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DL1C] = { + .name = "DL1C", + .description = "Layer 1 Control", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSUM] = { + .name = "DSUM", + .description = "Summary of Process", + .color = "\033[1;37m", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +const struct log_info log_info = { + .filter_fn = NULL, + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + diff --git a/src/host/layer23/src/main.c b/src/host/layer23/src/main.c new file mode 100644 index 00000000..1816e150 --- /dev/null +++ b/src/host/layer23/src/main.c @@ -0,0 +1,212 @@ +/* Main method of the layer2/3 stack */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> +#include <osmocom/l1l2_interface.h> +#include <osmocom/layer3.h> +#include <osmocom/lapdm.h> +#include <osmocom/gsmtap_util.h> +#include <osmocom/logging.h> +#include <osmocom/l23_app.h> + +#include <osmocore/msgb.h> +#include <osmocore/talloc.h> +#include <osmocore/select.h> +#include <osmocore/linuxlist.h> + +#include <arpa/inet.h> + +#define _GNU_SOURCE +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +struct log_target *stderr_target; + +void *l23_ctx = NULL; +static char *socket_path = "/tmp/osmocom_l2"; +struct llist_head ms_list; +static struct osmocom_ms *ms = NULL; +static uint32_t gsmtap_ip = 0; +unsigned short vty_port = 4247; +int (*l23_app_work) (struct osmocom_ms *ms) = NULL; +int (*l23_app_exit) (struct osmocom_ms *ms) = NULL; +int quit = 0; + +const char *openbsc_copyright = + "Copyright (C) 2008-2010 ...\n" + "Contributions by ...\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.\n"; + +static void print_usage(const char *app) +{ + printf("Usage: %s\n", app); +} + +static void print_help() +{ + printf(" Some help...\n"); + printf(" -h --help this text\n"); + printf(" -s --socket /tmp/osmocom_l2. Path to the unix " + "domain socket\n"); + printf(" -a --arfcn NR The ARFCN to be used for layer2.\n"); + printf(" -i --gsmtap-ip The destination IP used for GSMTAP.\n"); + printf(" -v --vty-port The VTY port number to telnet to. " + "(default %u)\n", vty_port); + printf(" -d --debug Change debug flags.\n"); +} + +static void handle_options(int argc, char **argv) +{ + struct sockaddr_in gsmtap; + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"socket", 1, 0, 's'}, + {"arfcn", 1, 0, 'a'}, + {"gsmtap-ip", 1, 0, 'i'}, + {"vty-port", 1, 0, 'v'}, + {"debug", 1, 0, 'd'}, + {0, 0, 0, 0}, + }; + + c = getopt_long(argc, argv, "hs:a:i:v:d:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(argv[0]); + print_help(); + exit(0); + break; + case 's': + socket_path = talloc_strdup(l23_ctx, optarg); + break; + case 'a': + ms->test_arfcn = atoi(optarg); + break; + case 'i': + if (!inet_aton(optarg, &gsmtap.sin_addr)) { + perror("inet_aton"); + exit(2); + } + gsmtap_ip = ntohl(gsmtap.sin_addr.s_addr); + break; + case 'v': + vty_port = atoi(optarg); + break; + case 'd': + log_parse_category_mask(stderr_target, optarg); + break; + default: + break; + } + } +} + +void sighandler(int sigset) +{ + int rc = 0; + + if (sigset == SIGHUP || sigset == SIGPIPE) + return; + + fprintf(stderr, "Signal %d recevied.\n", sigset); + if (l23_app_exit) + rc = l23_app_exit(ms); + + if (rc != -EBUSY) + exit (0); +} + +int main(int argc, char **argv) +{ + int rc; + + printf("%s\n", openbsc_copyright); + + INIT_LLIST_HEAD(&ms_list); + log_init(&log_info); + stderr_target = log_target_create_stderr(); + log_add_target(stderr_target); + log_set_all_filter(stderr_target, 1); + + l23_ctx = talloc_named_const(NULL, 1, "layer2 context"); + + ms = talloc_zero(l23_ctx, struct osmocom_ms); + if (!ms) { + fprintf(stderr, "Failed to allocate MS\n"); + exit(1); + } + llist_add_tail(&ms->entity, &ms_list); + + sprintf(ms->name, "1"); + + ms->test_arfcn = 871; + + handle_options(argc, argv); + + rc = layer2_open(ms, socket_path); + if (rc < 0) { + fprintf(stderr, "Failed during layer2_open()\n"); + exit(1); + } + + lapdm_init(&ms->l2_entity.lapdm_dcch, ms); + lapdm_init(&ms->l2_entity.lapdm_acch, ms); + + rc = l23_app_init(ms); + if (rc < 0) + exit(1); + + if (gsmtap_ip) { + rc = gsmtap_init(gsmtap_ip); + if (rc < 0) { + fprintf(stderr, "Failed during gsmtap_init()\n"); + exit(1); + } + } + + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGTERM, sighandler); + signal(SIGPIPE, sighandler); + + while (!quit) { + if (l23_app_work) + l23_app_work(ms); + bsc_select_main(0); + } + + return 0; +} diff --git a/src/host/layer23/src/mnccms.c b/src/host/layer23/src/mnccms.c new file mode 100644 index 00000000..dd08b7c8 --- /dev/null +++ b/src/host/layer23/src/mnccms.c @@ -0,0 +1,458 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <osmocore/talloc.h> + +#include <osmocom/logging.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/mncc.h> +#include <osmocom/vty.h> + +void *l23_ctx; +static int new_callref = 1; +static LLIST_HEAD(call_list); + +/* + * support functions + */ + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val); + +static void free_call(struct gsm_call *call) +{ + llist_del(&call->entry); + DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); + talloc_free(call); +} + + +struct gsm_call *get_call_ref(uint32_t callref) +{ + struct gsm_call *callt; + + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) + return callt; + } + return NULL; +} + +/* + * MNCCms dummy application + */ + +/* this is a minimal implementation as required by GSM 04.08 */ +int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + uint32_t callref = data->callref; + struct gsm_mncc rel; + + if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF) + return 0; + + LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call\n"); + + /* reject, as we don't support Calls */ + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = callref; + mncc_set_cause(&rel, GSM48_CAUSE_LOC_USER, + GSM48_CC_CAUSE_INCOMPAT_DEST); + + return mncc_send(ms, MNCC_REL_REQ, &rel); +} + +/* + * MNCCms basic call application + */ + +int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) +{ + struct gsm_mncc *data = arg; + struct gsm_call *call = get_call_ref(data->callref); + struct gsm_mncc mncc; + uint8_t cause; + + /* call does not exist */ + if (!call && msg_type != MNCC_SETUP_IND) { + LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call " + "(callref %d)\n", data->callref); + if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF) + return 0; + cause = GSM48_CC_CAUSE_INCOMPAT_DEST; + release: + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = data->callref; + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_USER, cause); + return mncc_send(ms, MNCC_REL_REQ, &mncc); + } + + /* setup without call */ + if (!call) { + call = talloc_zero(l23_ctx, struct gsm_call); + if (!call) + return -ENOMEM; + call->callref = new_callref++; + llist_add_tail(&call->entry, &call_list); + } + + switch (msg_type) { + case MNCC_DISC_IND: + vty_notify(ms, NULL); + switch (data->cause.value) { + case GSM48_CC_CAUSE_UNASSIGNED_NR: + vty_notify(ms, "Call: Number not assigned\n"); + break; + case GSM48_CC_CAUSE_NO_ROUTE: + vty_notify(ms, "Call: Destination unreachable\n"); + break; + case GSM48_CC_CAUSE_NORM_CALL_CLEAR: + vty_notify(ms, "Call: Remote hangs up\n"); + break; + case GSM48_CC_CAUSE_USER_BUSY: + vty_notify(ms, "Call: Remote busy\n"); + break; + case GSM48_CC_CAUSE_USER_NOTRESPOND: + vty_notify(ms, "Call: Remote not responding\n"); + break; + case GSM48_CC_CAUSE_USER_ALERTING_NA: + vty_notify(ms, "Call: Remote not answering\n"); + break; + case GSM48_CC_CAUSE_CALL_REJECTED: + vty_notify(ms, "Call has been rejected\n"); + break; + case GSM48_CC_CAUSE_NUMBER_CHANGED: + vty_notify(ms, "Call: Number changed\n"); + break; + case GSM48_CC_CAUSE_PRE_EMPTION: + vty_notify(ms, "Call: Cleared due to pre-emption\n"); + break; + case GSM48_CC_CAUSE_DEST_OOO: + vty_notify(ms, "Call: Remote out of order\n"); + break; + case GSM48_CC_CAUSE_INV_NR_FORMAT: + vty_notify(ms, "Call: Number invalid or imcomplete\n"); + break; + case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN: + vty_notify(ms, "Call: No channel available\n"); + break; + case GSM48_CC_CAUSE_NETWORK_OOO: + vty_notify(ms, "Call: Network out of order\n"); + break; + case GSM48_CC_CAUSE_TEMP_FAILURE: + vty_notify(ms, "Call: Temporary failure\n"); + break; + case GSM48_CC_CAUSE_SWITCH_CONG: + vty_notify(ms, "Congestion\n"); + break; + default: + vty_notify(ms, "Call has been disconnected\n"); + } + LOGP(DMNCC, LOGL_INFO, "Call has been disconnected " + "(cause %d)\n", data->cause.value); + if ((data->fields & MNCC_F_PROGRESS) + && data->progress.descr == 8) { + vty_notify(ms, "Please hang up!\n"); + break; + } + free_call(call); + cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + goto release; + case MNCC_REL_IND: + case MNCC_REL_CNF: + vty_notify(ms, NULL); + if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED) + vty_notify(ms, "Call has been rejected\n"); + else + vty_notify(ms, "Call has been released\n"); + LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n", + data->cause.value); + free_call(call); + break; + case MNCC_CALL_PROC_IND: + vty_notify(ms, NULL); + vty_notify(ms, "Call is proceeding\n"); + LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n"); + break; + case MNCC_ALERT_IND: + vty_notify(ms, NULL); + vty_notify(ms, "Call is aleriting\n"); + LOGP(DMNCC, LOGL_INFO, "Call is alerting\n"); + break; + case MNCC_SETUP_CNF: + vty_notify(ms, NULL); + vty_notify(ms, "Call is answered\n"); + LOGP(DMNCC, LOGL_INFO, "Call is answered\n"); + break; + case MNCC_SETUP_IND: + vty_notify(ms, NULL); + if (!llist_empty(&call_list) && !ms->settings.cw) { + vty_notify(ms, "Incomming call rejected while busy\n"); + LOGP(DMNCC, LOGL_INFO, "Incomming call but busy\n"); + cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR; + goto release; + } + if (!data->calling.present || !data->calling.number[0]) + vty_notify(ms, "Incomming call\n"); + else if (data->calling.type == 1) + vty_notify(ms, "Incomming call from +%s\n", + data->calling.number); + else if (data->calling.type == 2) + vty_notify(ms, "Incomming call from 0-%s\n", + data->calling.number); + else + vty_notify(ms, "Incomming call from %s\n", + data->calling.number); + LOGP(DMNCC, LOGL_INFO, "Incomming call\n"); + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc_send(ms, MNCC_CALL_CONF_REQ, &mncc); + if (llist_empty(&call_list)) + LOGP(DMNCC, LOGL_INFO, "Ring!\n"); + else { + LOGP(DMNCC, LOGL_INFO, "Knock!\n"); + call->hold = 1; + } + call->ring = 1; + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc_send(ms, MNCC_ALERT_REQ, &mncc); + break; + case MNCC_HOLD_CNF: + vty_notify(ms, NULL); + vty_notify(ms, "Call is on hold\n"); + LOGP(DMNCC, LOGL_INFO, "Call is on hold\n"); + call->hold = 1; + break; + case MNCC_HOLD_REJ: + vty_notify(ms, NULL); + vty_notify(ms, "Call hold was rejected\n"); + LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n"); + break; + case MNCC_RETRIEVE_CNF: + vty_notify(ms, NULL); + vty_notify(ms, "Call is retrieved\n"); + LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n"); + call->hold = 0; + break; + case MNCC_RETRIEVE_REJ: + vty_notify(ms, NULL); + vty_notify(ms, "Call retrieve was rejected\n"); + LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n"); + break; + default: + LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n", + msg_type); + return -EINVAL; + } + + return 0; +} + +int mncc_call(struct osmocom_ms *ms, char *number) +{ + struct gsm_call *call; + struct gsm_mncc setup; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + vty_notify(ms, NULL); + vty_notify(ms, "Please put active call on hold " + "first!\n"); + LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n"); + return -EBUSY; + } + } + + call = talloc_zero(l23_ctx, struct gsm_call); + if (!call) + return -ENOMEM; + call->callref = new_callref++; + llist_add_tail(&call->entry, &call_list); + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = call->callref; + + if (!strncasecmp(number, "emerg", 5)) { + LOGP(DMNCC, LOGL_INFO, "Make emergency call\n"); + /* emergency */ + setup.emergency = 1; + } else { + LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number); + /* called number */ + setup.fields |= MNCC_F_CALLED; + strncpy(setup.called.number, number, + sizeof(setup.called.number) - 1); + + /* bearer capability (mandatory) */ + setup.fields |= MNCC_F_BEARER_CAP; + setup.bearer_cap.coding = 0; + setup.bearer_cap.radio = 1; + setup.bearer_cap.speech_ctm = 0; + setup.bearer_cap.speech_ver[0] = 0; + setup.bearer_cap.speech_ver[1] = -1; /* end of list */ + setup.bearer_cap.transfer = 0; + setup.bearer_cap.mode = 0; + if (ms->settings.clir) + setup.clir.sup = 1; + else if (ms->settings.clip) + setup.clir.inv = 1; + } + + return mncc_send(ms, MNCC_SETUP_REQ, &setup); +} + +int mncc_hangup(struct osmocom_ms *ms) +{ + struct gsm_call *call, *found = NULL; + struct gsm_mncc disc; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + found = call; + break; + } + } + if (!found) { + LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No active call\n"); + return -EINVAL; + } + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = found->callref; + mncc_set_cause(&disc, GSM48_CAUSE_LOC_USER, + GSM48_CC_CAUSE_NORM_CALL_CLEAR); + return mncc_send(ms, MNCC_DISC_REQ, &disc); +} + +int mncc_answer(struct osmocom_ms *ms) +{ + struct gsm_call *call, *alerting = NULL; + struct gsm_mncc rsp; + int active = 0; + + llist_for_each_entry(call, &call_list, entry) { + if (call->ring) + alerting = call; + if (!call->hold) + active = 1; + } + if (!alerting) { + LOGP(DMNCC, LOGL_INFO, "No call alerting\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No alerting call\n"); + return -EBUSY; + } + if (active) { + LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Please put active call on hold first!\n"); + return -EBUSY; + } + alerting->ring = 0; + + memset(&rsp, 0, sizeof(struct gsm_mncc)); + rsp.callref = alerting->callref; + return mncc_send(ms, MNCC_SETUP_RSP, &rsp); +} + +int mncc_hold(struct osmocom_ms *ms) +{ + struct gsm_call *call, *found = NULL; + struct gsm_mncc hold; + + llist_for_each_entry(call, &call_list, entry) { + if (!call->hold) { + found = call; + break; + } + } + if (!found) { + LOGP(DMNCC, LOGL_INFO, "No active call to hold\n"); + vty_notify(ms, NULL); + vty_notify(ms, "No active call\n"); + return -EINVAL; + } + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = found->callref; + return mncc_send(ms, MNCC_HOLD_REQ, &hold); +} + +int mncc_retrieve(struct osmocom_ms *ms, int number) +{ + struct gsm_call *call; + struct gsm_mncc retr; + int holdnum = 0, active = 0, i = 0; + + llist_for_each_entry(call, &call_list, entry) { + if (call->hold) + holdnum++; + if (!call->hold) + active = 1; + } + if (active) { + LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n"); + vty_notify(ms, NULL); + vty_notify(ms, "Hold active call first!\n"); + return -EINVAL; + } + if (holdnum == 0) { + vty_notify(ms, NULL); + vty_notify(ms, "No call on hold!\n"); + return -EINVAL; + } + if (holdnum > 1 && number <= 0) { + vty_notify(ms, NULL); + vty_notify(ms, "Select call 1..%d\n", holdnum); + return -EINVAL; + } + if (holdnum == 1 && number <= 0) + number = 1; + if (number > holdnum) { + vty_notify(ms, NULL); + vty_notify(ms, "Given number %d out of range!\n", number); + vty_notify(ms, "Select call 1..%d\n", holdnum); + return -EINVAL; + } + + llist_for_each_entry(call, &call_list, entry) { + i++; + if (i == number) + break; + } + + memset(&retr, 0, sizeof(struct gsm_mncc)); + retr.callref = call->callref; + return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr); +} + + + + diff --git a/src/host/layer23/src/networks.c b/src/host/layer23/src/networks.c new file mode 100644 index 00000000..ae14d80c --- /dev/null +++ b/src/host/layer23/src/networks.c @@ -0,0 +1,1824 @@ +#include <string.h> +#include <stdint.h> +#include <stdio.h> + +#include <osmocom/networks.h> + +/* list of networks */ + +struct gsm_networks gsm_networks[] = { + { 0x001, -1, "Test" }, + { 0x001, 0x01f, "Test" }, + { 0x412, -1, "Afghanistan" }, + { 0x412, 0x01f, "AWCC" }, + { 0x412, 0x20f, "Roshan" }, + { 0x412, 0x40f, "Areeba" }, + { 0x412, 0x50f, "Etisalat" }, + { 0x276, -1, "Albania" }, + { 0x276, 0x01f, "AMC" }, + { 0x276, 0x02f, "Vodafone" }, + { 0x276, 0x03f, "Eagle Mobile" }, + { 0x603, -1, "Algeria" }, + { 0x603, 0x01f, "Mobilis" }, + { 0x603, 0x02f, "Djezzy" }, + { 0x603, 0x03f, "Nedjma" }, + { 0x213, -1, "Andorra" }, + { 0x213, 0x03f, "Mobiland" }, + { 0x631, -1, "Angola" }, + { 0x631, 0x02f, "UNITEL" }, + { 0x365, -1, "Anguilla" }, + { 0x365, 0x010, "Weblinks Limited" }, + { 0x365, 0x840, "Cable & Wireless" }, + { 0x344, -1, "Antigua and Barbuda" }, + { 0x344, 0x030, "APUA" }, + { 0x344, 0x920, "Lime" }, + { 0x338, 0x050, "Digicel" }, + { 0x722, -1, "Argentina" }, + { 0x722, 0x010, "Movistar" }, + { 0x722, 0x020, "Nextel" }, + { 0x722, 0x070, "Movistar" }, + { 0x722, 0x310, "Claro" }, + { 0x722, 0x320, "Claro" }, + { 0x722, 0x330, "Claro" }, + { 0x722, 0x340, "Personal" }, + { 0x722, 0x350, "Hutchinson (PORT HABLE)" }, + { 0x283, -1, "Armenia" }, + { 0x283, 0x01f, "Beeline" }, + { 0x283, 0x05f, "VivaCell-MTS" }, + { 0x283, 0x10f, "Orange" }, + { 0x363, -1, "Aruba" }, + { 0x363, 0x01f, "SETAR" }, + { 0x363, 0x02f, "Digicel" }, + { 0x505, -1, "Australia" }, + { 0x505, 0x01f, "Telstra" }, + { 0x505, 0x02f, "Optus" }, + { 0x505, 0x03f, "Vodafone" }, + { 0x505, 0x04f, "Department of Defence" }, + { 0x505, 0x05f, "Ozitel" }, + { 0x505, 0x06f, "Vodafone" }, + { 0x505, 0x08f, "One.Tel" }, + { 0x505, 0x09f, "Airnet" }, + { 0x505, 0x12f, "3" }, + { 0x505, 0x13f, "Railcorp" }, + { 0x505, 0x14f, "AAPT" }, + { 0x505, 0x15f, "3GIS" }, + { 0x505, 0x16f, "Unknown" }, + { 0x505, 0x21f, "SOUL" }, + { 0x505, 0x24f, "Advanced Communications Technologies Pty. Ltd." }, + { 0x505, 0x38f, "Crazy John's" }, + { 0x505, 0x71f, "Telstra" }, + { 0x505, 0x72f, "Telstra" }, + { 0x505, 0x88f, "Localstar Holding Pty. Ltd." }, + { 0x505, 0x90f, "Optus" }, + { 0x505, 0x99f, "One.Tel" }, + { 0x232, -1, "Austria" }, + { 0x232, 0x01f, "A1" }, + { 0x232, 0x03f, "T-Mobile" }, + { 0x232, 0x05f, "Orange" }, + { 0x232, 0x07f, "tele.ring" }, + { 0x232, 0x10f, "3" }, + { 0x232, 0x11f, "bob" }, + { 0x232, 0x12f, "yesss" }, + { 0x232, 0x14f, "3" }, + { 0x232, 0x15f, "Barablu" }, + { 0x232, 0x16f, "3" }, + { 0x400, -1, "Azerbaijan" }, + { 0x400, 0x01f, "Azercell" }, + { 0x400, 0x02f, "Bakcell" }, + { 0x400, 0x03f, "FONEX" }, + { 0x400, 0x04f, "Nar Mobile" }, + { 0x364, -1, "Bahamas" }, + { 0x364, 0x390, "BaTelCo" }, + { 0x426, -1, "Bahrain" }, + { 0x426, 0x01f, "Batelco" }, + { 0x426, 0x02f, "zain BH" }, + { 0x426, 0x04f, "VIVA" }, + { 0x470, -1, "Bangladesh" }, + { 0x470, 0x01f, "Grameenphone" }, + { 0x470, 0x02f, "Robi" }, + { 0x470, 0x03f, "Banglalink" }, + { 0x470, 0x04f, "TeleTalk" }, + { 0x470, 0x05f, "Citycell" }, + { 0x470, 0x06f, "Warid" }, + { 0x470, 0x07f, "WTBL" }, + { 0x342, -1, "Barbados" }, + { 0x342, 0x600, "bmobile" }, + { 0x342, 0x750, "Digicel" }, + { 0x342, 0x820, "Sunbeach Communications" }, + { 0x257, -1, "Belarus" }, + { 0x257, 0x01f, "velcom" }, + { 0x257, 0x02f, "MTS" }, + { 0x257, 0x04f, "life:)" }, + { 0x257, 0x03f, "DIALLOG" }, + { 0x206, -1, "Belgium" }, + { 0x206, 0x01f, "Proximus" }, + { 0x206, 0x02f, "SNCB GSM-R" }, + { 0x206, 0x10f, "Mobistar" }, + { 0x206, 0x20f, "BASE" }, + { 0x702, -1, "Belize" }, + { 0x702, 0x67f, "Belize Telemedia" }, + { 0x702, 0x68f, "International Telecommunications Ltd." }, + { 0x702, 0x00f, "Smart" }, + { 0x616, -1, "Benin" }, + { 0x616, 0x01f, "Libercom" }, + { 0x616, 0x02f, "Moov" }, + { 0x616, 0x03f, "MTN" }, + { 0x616, 0x04f, "BBCOM" }, + { 0x616, 0x05f, "Glo" }, + { 0x350, -1, "Bermuda" }, + { 0x350, 0x01f, "Digicel Bermuda" }, + { 0x350, 0x02f, "Mobility" }, + { 0x338, 0x050, "Digicel Bermuda" }, + { 0x310, 0x00f, "Cellular One" }, + { 0x402, -1, "Bhutan" }, + { 0x402, 0x11f, "B-Mobile" }, + { 0x402, 0x77f, "TashiCell" }, + { 0x736, -1, "Bolivia" }, + { 0x736, 0x01f, "Nuevatel" }, + { 0x736, 0x02f, "Entel" }, + { 0x736, 0x03f, "Tigo" }, + { 0x218, -1, "Bosnia and Herzegovina" }, + { 0x218, 0x03f, "HT-ERONET" }, + { 0x218, 0x05f, "m:tel" }, + { 0x218, 0x90f, "BH Mobile" }, + { 0x652, -1, "Botswana" }, + { 0x652, 0x01f, "Mascom" }, + { 0x652, 0x02f, "Orange" }, + { 0x652, 0x04f, "BTC Mobile" }, + { 0x724, -1, "Brazil" }, + { 0x724, 0x02f, "TIM" }, + { 0x724, 0x03f, "TIM" }, + { 0x724, 0x04f, "TIM" }, + { 0x724, 0x05f, "Claro" }, + { 0x724, 0x06f, "Vivo" }, + { 0x724, 0x07f, "CTBC Celular" }, + { 0x724, 0x08f, "TIM" }, + { 0x724, 0x10f, "Vivo" }, + { 0x724, 0x11f, "Vivo" }, + { 0x724, 0x15f, "Sercomtel" }, + { 0x724, 0x16f, "Oi / Brasil Telecom" }, + { 0x724, 0x23f, "Vivo" }, + { 0x724, 0x24f, "Oi / Amazonia Celular" }, + { 0x724, 0x31f, "Oi" }, + { 0x724, 0x32f, "CTBC Celular" }, + { 0x724, 0x33f, "CTBC Celular" }, + { 0x724, 0x34f, "CTBC Celular" }, + { 0x724, 0x37f, "aeiou" }, + { 0x724, 0x00f, "Nextel" }, + { 0x348, -1, "British Virgin Islands" }, + { 0x348, 0x170, "Cable & Wireless" }, + { 0x348, 0x570, "CCT Boatphone" }, + { 0x528, -1, "Brunei" }, + { 0x528, 0x01f, "Jabatan Telekom" }, + { 0x528, 0x02f, "B-Mobile" }, + { 0x528, 0x11f, "DSTCom" }, + { 0x284, -1, "Bulgaria" }, + { 0x284, 0x01f, "M-Tel" }, + { 0x284, 0x03f, "Vivacom" }, + { 0x284, 0x05f, "GLOBUL" }, + { 0x613, -1, "Burkina Faso" }, + { 0x613, 0x01f, "Onatel" }, + { 0x613, 0x02f, "Zain" }, + { 0x613, 0x03f, "Telecel Faso" }, + { 0x642, -1, "Burundi" }, + { 0x642, 0x01f, "Spacetel" }, + { 0x642, 0x02f, "Africell" }, + { 0x642, 0x03f, "Telecel" }, + { 0x642, 0x82f, "Onatel" }, +// { 0x642, ?, "LACELL SU" }, + { 0x456, -1, "Cambodia" }, + { 0x456, 0x01f, "Mobitel" }, + { 0x456, 0x02f, "hello" }, + { 0x456, 0x03f, "S Telecom" }, + { 0x456, 0x04f, "qb" }, + { 0x456, 0x05f, "Star-Cell" }, + { 0x456, 0x06f, "Smart" }, + { 0x456, 0x18f, "Mfone" }, +// { 0x456, ?, "Excell" }, + { 0x456, 0x09f, "Beeline" }, + { 0x456, 0x08f, "Metfone" }, + { 0x624, -1, "Cameroon" }, + { 0x624, 0x01f, "MTN Cameroon" }, + { 0x624, 0x02f, "Orange" }, + { 0x302, -1, "Canada" }, + { 0x302, 0x220, "Telus" }, + { 0x302, 0x221, "Telus" }, + { 0x302, 0x290, "Airtel Wireless" }, + { 0x302, 0x350, "FIRST" }, + { 0x302, 0x360, "MiKe" }, + { 0x302, 0x361, "Telus" }, + { 0x302, 0x370, "Fido" }, + { 0x302, 0x380, "DMTS" }, + { 0x302, 0x490, "WIND Mobile" }, + { 0x302, 0x500, "Videotron" }, + { 0x302, 0x510, "Videotron" }, + { 0x302, 0x610, "Bell" }, + { 0x302, 0x620, "ICE Wireless" }, + { 0x302, 0x640, "Bell" }, + { 0x302, 0x651, "Bell" }, + { 0x302, 0x652, "BC Tel Mobility (Telus)" }, + { 0x302, 0x653, "Telus" }, + { 0x302, 0x655, "MTS" }, + { 0x302, 0x656, "TBay" }, + { 0x302, 0x657, "Telus" }, + { 0x302, 0x680, "SaskTel" }, + { 0x302, 0x701, "MB Tel Mobility" }, + { 0x302, 0x702, "MT&T Mobility (Aliant)" }, + { 0x302, 0x703, "New Tel Mobility (Aliant)" }, + { 0x302, 0x710, "Globalstar" }, + { 0x302, 0x720, "Rogers Wireless" }, + { 0x302, 0x780, "SaskTel" }, + { 0x302, 0x880, "Bell / Telus" }, + { 0x625, -1, "Cape Verde" }, + { 0x625, 0x01f, "CVMOVEL" }, + { 0x625, 0x02f, "T+" }, + { 0x346, -1, "Cayman Islands" }, + { 0x346, 0x140, "Cable & Wireless" }, + { 0x338, 0x050, "Digicel" }, + { 0x623, -1, "Central African Republic" }, + { 0x623, 0x01f, "CTP" }, + { 0x623, 0x02f, "TC" }, + { 0x623, 0x03f, "Orange" }, + { 0x623, 0x04f, "Nationlink" }, + { 0x622, -1, "Chad" }, + { 0x622, 0x01f, "Zain" }, + { 0x622, 0x02f, "Tchad Mobile" }, + { 0x622, 0x03f, "TIGO - Millicom" }, + { 0x622, 0x02f, "TAWALI" }, + { 0x622, 0x04f, "Salam" }, + { 0x730, -1, "Chile" }, + { 0x730, 0x01f, "Entel" }, + { 0x730, 0x02f, "movistar" }, + { 0x730, 0x03f, "Claro" }, + { 0x730, 0x04f, "Nextel" }, + { 0x730, 0x10f, "Entel" }, + { 0x730, 0x99f, "WILL" }, + { 0x460, -1, "China" }, + { 0x460, 0x00f, "China Mobile" }, + { 0x460, 0x01f, "China Unicom" }, + { 0x460, 0x02f, "China Mobile" }, + { 0x460, 0x03f, "China Telecom" }, + { 0x460, 0x05f, "China Telecom" }, + { 0x460, 0x06f, "China Unicom" }, + { 0x460, 0x20f, "China TIETONG" }, + { 0x732, -1, "Colombia" }, + { 0x732, 0x001, "Colombia Telecomunicaciones S.A." }, + { 0x732, 0x002, "Edatel" }, + { 0x732, 0x101, "Comcel" }, + { 0x732, 0x102, "movistar" }, + { 0x732, 0x103, "Tigo" }, + { 0x732, 0x111, "Tigo" }, + { 0x732, 0x12f, "movistar" }, + { 0x654, -1, "Comoros" }, + { 0x654, 0x01f, "HURI - SNPT" }, + { 0x629, -1, "Republic of the Congo" }, + { 0x629, 0x01f, "Zain" }, + { 0x629, 0x10f, "Libertis Telecom" }, +// { 0x629, ?, "Warid Telecom" }, + { 0x548, -1, "Cook Islands" }, + { 0x548, 0x01f, "Telecom Cook" }, + { 0x712, -1, "Costa Rica" }, + { 0x712, 0x01f, "ICE" }, + { 0x712, 0x02f, "ICE" }, + { 0x712, 0x03f, "ICE" }, + { 0x219, -1, "Croatia" }, + { 0x219, 0x01f, "T-Mobile" }, + { 0x219, 0x02f, "Tele2" }, + { 0x219, 0x10f, "VIPnet" }, + { 0x368, -1, "Cuba" }, + { 0x368, 0x01f, "ETECSA" }, + { 0x280, -1, "Cyprus" }, + { 0x280, 0x01f, "Cytamobile-Vodafone" }, + { 0x280, 0x10f, "MTN" }, + { 0x230, -1, "Czech Republic" }, + { 0x230, 0x01f, "T-Mobile" }, + { 0x230, 0x02f, "O2" }, + { 0x230, 0x03f, "Vodafone" }, + { 0x230, 0x04f, "U:fon" }, + { 0x230, 0x98f, "S?DC s.o." }, + { 0x230, 0x99f, "Vodafone" }, + { 0x630, -1, "Democratic Republic of the Congo" }, + { 0x630, 0x01f, "Vodacom" }, + { 0x630, 0x02f, "Zain" }, + { 0x630, 0x04f, "Cellco" }, + { 0x630, 0x05f, "Supercell" }, + { 0x630, 0x86f, "CCT" }, + { 0x630, 0x89f, "SAIT Telecom" }, +// { 0x630, ?, "Africell" }, + { 0x238, -1, "Denmark" }, + { 0x238, 0x01f, "TDC" }, + { 0x238, 0x02f, "Telenor" }, + { 0x238, 0x03f, "MIGway A/S" }, + { 0x238, 0x05f, "ApS KBUS" }, + { 0x238, 0x06f, "3" }, + { 0x238, 0x07f, "Barablu Mobile Ltd." }, + { 0x238, 0x09f, "Dansk Beredskabskommunikation A/S" }, + { 0x238, 0x10f, "TDC" }, + { 0x238, 0x11f, "Dansk Beredskabskommunikation A/S" }, + { 0x238, 0x12f, "Lycamobile Denmark Ltd" }, + { 0x238, 0x20f, "Telia" }, + { 0x238, 0x30f, "Telia" }, + { 0x238, 0x40f, "Ericsson Danmark A/S" }, + { 0x238, 0x77f, "Telenor" }, + { 0x638, -1, "Djibouti" }, + { 0x638, 0x01f, "Evatis" }, + { 0x366, -1, "Dominica" }, + { 0x366, 0x020, "Digicel" }, + { 0x366, 0x110, "Cable & Wireless" }, + { 0x370, -1, "Dominican Republic" }, + { 0x370, 0x01f, "Orange" }, + { 0x370, 0x02f, "Claro" }, + { 0x370, 0x03f, "Tricom" }, + { 0x370, 0x04f, "Viva" }, + { 0x514, -1, "East Timor" }, + { 0x514, 0x02f, "Timor Telecom" }, + { 0x740, -1, "Ecuador" }, + { 0x740, 0x00f, "Movistar" }, + { 0x740, 0x01f, "Porta" }, + { 0x740, 0x02f, "Alegro" }, + { 0x602, -1, "Egypt" }, + { 0x602, 0x01f, "Mobinil" }, + { 0x602, 0x02f, "Vodafone" }, + { 0x602, 0x03f, "Etisalat" }, + { 0x706, -1, "El Salvador" }, + { 0x706, 0x01f, "CTE Telecom Personal" }, + { 0x706, 0x02f, "digicel" }, + { 0x706, 0x03f, "Telemovil EL Salvador" }, + { 0x706, 0x04f, "movistar" }, + { 0x706, 0x10f, "Claro" }, + { 0x627, -1, "Equatorial Guinea" }, + { 0x627, 0x01f, "Orange GQ" }, + { 0x627, 0x03f, "Hits GQ" }, + { 0x657, -1, "Eritrea" }, + { 0x657, 0x01f, "Eritel" }, + { 0x248, -1, "Estonia" }, + { 0x248, 0x01f, "EMT" }, + { 0x248, 0x02f, "Elisa" }, + { 0x248, 0x03f, "Tele 2" }, + { 0x248, 0x04f, "OY Top Connect" }, + { 0x248, 0x05f, "AS Bravocom Mobiil" }, + { 0x248, 0x06f, "OY ViaTel" }, + { 0x636, -1, "Ethiopia" }, + { 0x636, 0x01f, "ETMTN" }, + { 0x288, -1, "Faroe Islands" }, + { 0x288, 0x01f, "Faroese Telecom" }, + { 0x288, 0x02f, "Vodafone" }, + { 0x542, -1, "Fiji" }, + { 0x542, 0x01f, "Vodafone" }, + { 0x542, 0x02f, "Digicel" }, + { 0x244, -1, "Finland" }, + { 0x244, 0x03f, "DNA" }, + { 0x244, 0x05f, "Elisa" }, + { 0x244, 0x07f, "Nokia" }, + { 0x244, 0x08f, "Unknown" }, + { 0x244, 0x10f, "TDC Oy" }, + { 0x244, 0x12f, "DNA" }, + { 0x244, 0x14f, "AMT" }, + { 0x244, 0x21f, "Saunalahti" }, + { 0x244, 0x29f, "Scnl Truphone" }, + { 0x244, 0x91f, "Sonera" }, + { 0x208, -1, "France" }, + { 0x208, 0x00f, "Orange" }, + { 0x208, 0x01f, "Orange" }, + { 0x208, 0x02f, "Orange" }, + { 0x208, 0x05f, "Globalstar Europe" }, + { 0x208, 0x06f, "Globalstar Europe" }, + { 0x208, 0x07f, "Globalstar Europe" }, + { 0x208, 0x10f, "SFR" }, + { 0x208, 0x11f, "SFR" }, + { 0x208, 0x13f, "SFR" }, + { 0x208, 0x20f, "Bouygues" }, + { 0x208, 0x21f, "Bouygues" }, + { 0x208, 0x22f, "Transatel Mobile" }, + { 0x208, 0x88f, "Bouygues" }, + { 0x628, -1, "Gabon" }, + { 0x628, 0x01f, "Libertis" }, + { 0x628, 0x02f, "Moov (Telecel) Gabon S.A." }, + { 0x628, 0x03f, "Zain" }, + { 0x607, -1, "Gambia" }, + { 0x607, 0x01f, "Gamcel" }, + { 0x607, 0x02f, "Africel" }, + { 0x607, 0x03f, "Comium" }, + { 0x607, 0x04f, "QCell" }, + { 0x282, -1, "Georgia" }, + { 0x282, 0x01f, "Geocell" }, + { 0x282, 0x02f, "MagtiCom" }, + { 0x282, 0x03f, "Iberiatel" }, + { 0x282, 0x04f, "Beeline" }, + { 0x289, 0x67f, "Aquafon" }, + { 0x289, 0x88f, "A-Mobile" }, + { 0x262, -1, "Germany" }, + { 0x262, 0x01f, "T-Mobile" }, + { 0x262, 0x02f, "Vodafone" }, + { 0x262, 0x03f, "E-Plus" }, + { 0x262, 0x04f, "Vodafone" }, + { 0x262, 0x05f, "E-Plus" }, + { 0x262, 0x06f, "T-Mobile" }, + { 0x262, 0x07f, "O2" }, + { 0x262, 0x08f, "O2" }, + { 0x262, 0x09f, "Vodafone" }, + { 0x262, 0x10f, "DB Systel GSM-R" }, + { 0x262, 0x11f, "O2" }, + { 0x262, 0x12f, "Dolphin Telecom" }, + { 0x262, 0x13f, "Mobilcom Multimedia" }, + { 0x262, 0x14f, "Group 3G UMTS" }, + { 0x262, 0x15f, "Airdata" }, + { 0x262, 0x16f, "Vistream" }, + { 0x262, 0x42f, "OpenBSC" }, + { 0x262, 0x60f, "DB Telematik" }, + { 0x262, 0x76f, "Siemens AG" }, + { 0x262, 0x77f, "E-Plus" }, + { 0x262, 0x901, "Debitel" }, + { 0x620, -1, "Ghana" }, + { 0x620, 0x01f, "MTN" }, + { 0x620, 0x02f, "Vodafone" }, + { 0x620, 0x03f, "tiGO" }, + { 0x620, 0x04f, "Kasapa / Hutchison Telecom" }, + { 0x620, 0x06f, "Zain" }, + { 0x266, -1, "Gibraltar" }, + { 0x266, 0x01f, "GibTel" }, + { 0x266, 0x06f, "CTS Mobile" }, + { 0x202, -1, "Greece" }, + { 0x202, 0x01f, "Cosmote" }, + { 0x202, 0x05f, "Vodafone" }, + { 0x202, 0x09f, "Wind" }, + { 0x202, 0x10f, "Wind" }, + { 0x290, -1, "Greenland" }, + { 0x290, 0x01f, "TELE Greenland A/S" }, + { 0x352, -1, "Grenada" }, + { 0x352, 0x030, "Digicel" }, + { 0x352, 0x110, "Cable & Wireless" }, + { 0x340, -1, "Guadeloupe" }, + { 0x340, 0x01f, "Orange" }, + { 0x340, 0x02f, "Outremer" }, + { 0x340, 0x03f, "Telcell" }, + { 0x340, 0x08f, "MIO GSM" }, + { 0x340, 0x20f, "Digicel" }, + { 0x310, -1, "Guam" }, + { 0x310, 0x032, "IT&E Wireless" }, + { 0x310, 0x033, "Guam Telephone Authority" }, + { 0x310, 0x140, "mPulse" }, + { 0x310, 0x370, "Guamcell" }, + { 0x311, 0x250, "i CAN_GSM" }, + { 0x310, 0x470, "Guamcell" }, + { 0x704, -1, "Guatemala" }, + { 0x704, 0x01f, "Claro" }, + { 0x704, 0x02f, "Comcel / Tigo" }, + { 0x704, 0x03f, "movistar" }, +// { 0x704, ?, "digicel" }, + { 0x234, -1, "Guernsey" }, + { 0x234, 0x55f, "Sure Mobile" }, + { 0x234, 0x50f, "Wave Telecom" }, + { 0x234, 0x03f, "Airtel Vodafone" }, + { 0x611, -1, "Guinea" }, + { 0x611, 0x01f, "Spacetel" }, + { 0x611, 0x02f, "Lagui" }, + { 0x611, 0x03f, "Telecel Guinee" }, + { 0x611, 0x04f, "MTN" }, + { 0x632, -1, "Guinea-Bissau" }, + { 0x632, 0x02f, "Areeba" }, + { 0x632, 0x03f, "Orange" }, + { 0x738, -1, "Guyana" }, + { 0x738, 0x01f, "Digicel" }, + { 0x738, 0x02f, "GT&T Cellink Plus" }, + { 0x372, -1, "Haiti" }, + { 0x372, 0x01f, "Comcel / Voila" }, + { 0x338, 0x050, "Digicel" }, + { 0x708, -1, "Honduras" }, + { 0x708, 0x01f, "Claro" }, + { 0x708, 0x02f, "Celtel / Tigo" }, + { 0x708, 0x30f, "Hondutel" }, + { 0x708, 0x40f, "DIGICEL" }, + { 0x454, -1, "Hong Kong" }, + { 0x454, 0x00f, "1O1O and One2Free" }, + { 0x454, 0x01f, "CITIC Telecom 1616" }, + { 0x454, 0x02f, "CSL Limited" }, + { 0x454, 0x03f, "3 (3G)" }, + { 0x454, 0x04f, "3 DualBand (2G)" }, + { 0x454, 0x05f, "3 CDMA" }, + { 0x454, 0x06f, "SmarTone-Vodafone" }, + { 0x454, 0x07f, "China Unicom (Hong Kong) Limited" }, + { 0x454, 0x08f, "Trident" }, + { 0x454, 0x09f, "China Motion Telecom" }, + { 0x454, 0x10f, "New World Mobility" }, + { 0x454, 0x11f, "China-Hongkong Telecom" }, + { 0x454, 0x12f, "CMCC HK" }, + { 0x454, 0x14f, "Hutchison Telecom" }, + { 0x454, 0x15f, "SmarTone Mobile Communications Limited" }, + { 0x454, 0x16f, "PCCW Mobile (2G)" }, + { 0x454, 0x17f, "SmarTone Mobile Communications Limited" }, + { 0x454, 0x18f, "CSL Limited" }, + { 0x454, 0x19f, "PCCW Mobile (3G)" }, + { 0x454, 0x29f, "PCCW Mobile (CDMA)" }, + { 0x274, -1, "Iceland" }, + { 0x274, 0x01f, "Siminn" }, + { 0x274, 0x02f, "Vodafone" }, + { 0x274, 0x03f, "Vodafone" }, + { 0x274, 0x04f, "Viking" }, + { 0x274, 0x06f, "N?ll n?u ehf" }, + { 0x274, 0x07f, "IceCell" }, + { 0x274, 0x08f, "On-waves" }, + { 0x274, 0x11f, "Nova" }, + { 0x404, -1, "India" }, + { 0x404, 0x01f, "Vodafone IN" }, + { 0x404, 0x02f, "AirTel" }, + { 0x404, 0x04f, "IDEA" }, + { 0x404, 0x05f, "Vodafone IN" }, + { 0x404, 0x07f, "IDEA" }, + { 0x404, 0x09f, "Reliance" }, + { 0x404, 0x10f, "AirTel" }, + { 0x404, 0x11f, "Vodafone IN" }, + { 0x404, 0x12f, "IDEA" }, + { 0x404, 0x13f, "Vodafone IN" }, + { 0x404, 0x14f, "IDEA" }, + { 0x404, 0x15f, "Vodafone IN" }, + { 0x404, 0x17f, "AIRCEL" }, + { 0x404, 0x19f, "IDEA" }, + { 0x404, 0x20f, "Vodafone IN" }, + { 0x404, 0x21f, "Loop Mobile" }, + { 0x404, 0x22f, "IDEA" }, + { 0x404, 0x24f, "IDEA" }, + { 0x404, 0x27f, "Vodafone IN" }, + { 0x404, 0x28f, "AIRCEL" }, + { 0x404, 0x29f, "AIRCEL" }, + { 0x404, 0x30f, "Vodafone IN" }, + { 0x404, 0x31f, "AirTel" }, + { 0x404, 0x34f, "CellOne" }, + { 0x404, 0x36f, "Reliance" }, + { 0x404, 0x37f, "Aircel" }, + { 0x404, 0x38f, "CellOne" }, + { 0x404, 0x41f, "Aircel" }, + { 0x404, 0x42f, "Aircel" }, + { 0x404, 0x44f, "IDEA" }, + { 0x404, 0x45f, "Airtel" }, + { 0x404, 0x51f, "CellOne" }, + { 0x404, 0x52f, "Reliance" }, + { 0x404, 0x53f, "CellOne" }, + { 0x404, 0x54f, "CellOne" }, + { 0x404, 0x55f, "CellOne" }, + { 0x404, 0x56f, "IDEA" }, + { 0x404, 0x57f, "CellOne" }, + { 0x404, 0x58f, "CellOne" }, + { 0x404, 0x59f, "CellOne" }, + { 0x404, 0x60f, "Vodafone IN" }, + { 0x404, 0x62f, "CellOne" }, + { 0x404, 0x64f, "CellOne" }, + { 0x404, 0x66f, "CellOne" }, + { 0x404, 0x67f, "Reliance GSM" }, + { 0x404, 0x68f, "DOLPHIN" }, + { 0x404, 0x69f, "DOLPHIN" }, + { 0x404, 0x72f, "CellOne" }, + { 0x404, 0x74f, "CellOne" }, + { 0x404, 0x76f, "CellOne" }, + { 0x404, 0x78f, "Idea Cellular Ltd" }, + { 0x404, 0x80f, "BSNL MOBILE" }, + { 0x404, 0x81f, "CellOne" }, + { 0x404, 0x82f, "Idea" }, + { 0x404, 0x83f, "Reliance Smart GSM" }, + { 0x404, 0x84f, "Vodafone IN" }, + { 0x404, 0x85f, "Reliance" }, + { 0x404, 0x86f, "Vodafone IN" }, + { 0x404, 0x90f, "AirTel" }, + { 0x404, 0x91f, "AIRCEL" }, + { 0x404, 0x92f, "AirTel" }, + { 0x404, 0x93f, "AirTel" }, + { 0x404, 0x96f, "AirTel" }, + { 0x405, 0x05f, "Reliance" }, + { 0x405, 0x10f, "Reliance" }, + { 0x405, 0x13f, "Reliance" }, + { 0x405, 0x025, "TATA DOCOMO" }, + { 0x405, 0x026, "TATA DOCOMO" }, + { 0x405, 0x027, "TATA DOCOMO" }, + { 0x405, 0x029, "TATA DOCOMO" }, + { 0x405, 0x030, "TATA DOCOMO" }, + { 0x405, 0x031, "TATA DOCOMO" }, + { 0x405, 0x032, "TATA DOCOMO" }, + { 0x405, 0x034, "TATA DOCOMO" }, + { 0x405, 0x035, "TATA DOCOMO" }, + { 0x405, 0x036, "TATA DOCOMO" }, + { 0x405, 0x037, "TATA DOCOMO" }, + { 0x405, 0x038, "TATA DOCOMO" }, + { 0x405, 0x039, "TATA DOCOMO" }, + { 0x405, 0x041, "TATA DOCOMO" }, + { 0x405, 0x042, "TATA DOCOMO" }, + { 0x405, 0x043, "TATA DOCOMO" }, + { 0x405, 0x044, "TATA DOCOMO" }, + { 0x405, 0x045, "TATA DOCOMO" }, + { 0x405, 0x046, "TATA DOCOMO" }, + { 0x405, 0x047, "TATA DOCOMO" }, + { 0x405, 0x51f, "AirTel" }, + { 0x405, 0x52f, "AirTel" }, + { 0x405, 0x54f, "AirTel" }, + { 0x405, 0x56f, "AirTel" }, + { 0x405, 0x66f, "Vodafone IN" }, + { 0x405, 0x70f, "IDEA" }, + { 0x405, 0x750, "Vodafone IN" }, + { 0x405, 0x751, "Vodafone IN" }, + { 0x405, 0x752, "Vodafone IN" }, + { 0x405, 0x753, "Vodafone IN" }, + { 0x405, 0x754, "Vodafone IN" }, + { 0x405, 0x755, "Vodafone IN" }, + { 0x405, 0x756, "Vodafone IN" }, + { 0x405, 0x799, "IDEA" }, + { 0x405, 0x800, "AIRCEL" }, + { 0x405, 0x801, "AIRCEL" }, + { 0x405, 0x802, "AIRCEL" }, + { 0x405, 0x803, "AIRCEL" }, + { 0x405, 0x804, "AIRCEL" }, + { 0x405, 0x805, "AIRCEL" }, + { 0x405, 0x806, "AIRCEL" }, + { 0x405, 0x807, "AIRCEL" }, + { 0x405, 0x808, "AIRCEL" }, + { 0x405, 0x809, "AIRCEL" }, + { 0x405, 0x810, "AIRCEL" }, + { 0x405, 0x811, "AIRCEL" }, + { 0x405, 0x812, "AIRCEL" }, + { 0x405, 0x819, "Uninor" }, + { 0x405, 0x818, "[Uninor]" }, + { 0x405, 0x820, "Uninor" }, + { 0x405, 0x821, "Uninor" }, + { 0x405, 0x822, "Uninor" }, + { 0x405, 0x880, "Uninor" }, + { 0x405, 0x824, "Videocon Datacom" }, + { 0x405, 0x834, "Videocon Datacom" }, + { 0x405, 0x844, "Uninor" }, + { 0x405, 0x845, "IDEA" }, + { 0x405, 0x848, "IDEA" }, + { 0x405, 0x850, "IDEA" }, + { 0x405, 0x855, "Loop Mobile" }, + { 0x405, 0x864, "Loop Mobile" }, + { 0x405, 0x865, "Loop Mobile" }, + { 0x405, 0x875, "Uninor" }, + { 0x405, 0x881, "S Tel" }, + { 0x405, 0x912, "Etisalat DB" }, + { 0x405, 0x913, "Etisalat DB" }, + { 0x405, 0x917, "Etisalat DB" }, + { 0x405, 0x929, "Uninor" }, + { 0x510, -1, "Indonesia" }, + { 0x510, 0x00f, "PSN" }, + { 0x510, 0x01f, "INDOSAT" }, + { 0x510, 0x03f, "StarOne" }, + { 0x510, 0x07f, "TelkomFlexi" }, + { 0x510, 0x08f, "AXIS" }, + { 0x510, 0x09f, "SMART" }, + { 0x510, 0x10f, "Telkomsel" }, + { 0x510, 0x11f, "XL" }, + { 0x510, 0x20f, "TELKOMMobile" }, + { 0x510, 0x21f, "IM3" }, + { 0x510, 0x27f, "Ceria" }, + { 0x510, 0x28f, "Fren/Hepi" }, + { 0x510, 0x89f, "3" }, + { 0x510, 0x99f, "Esia " }, + { 0x432, -1, "Iran" }, + { 0x432, 0x11f, "MCI" }, + { 0x432, 0x14f, "TKC" }, + { 0x432, 0x19f, "MTCE" }, + { 0x432, 0x32f, "Taliya" }, + { 0x432, 0x35f, "Irancell" }, + { 0x418, -1, "Iraq" }, + { 0x418, 0x20f, "Zain IQ" }, + { 0x418, 0x30f, "Zain IQ" }, + { 0x418, 0x05f, "Asia Cell" }, + { 0x418, 0x40f, "Korek" }, + { 0x418, 0x08f, "SanaTel" }, +// { 0x418, ?, "IRAQNA" }, + { 0x272, -1, "Ireland" }, + { 0x272, 0x01f, "Vodafone" }, + { 0x272, 0x02f, "O2" }, + { 0x272, 0x03f, "Meteor" }, + { 0x272, 0x04f, "Access Telecom" }, + { 0x272, 0x05f, "3" }, + { 0x272, 0x07f, "Eircom" }, + { 0x272, 0x09f, "Clever Communications" }, + { 0x234, -1, "Isle of Man" }, + { 0x234, 0x58f, "Pronto GSM" }, + { 0x425, -1, "Israel" }, + { 0x425, 0x01f, "Orange" }, + { 0x425, 0x02f, "Cellcom" }, + { 0x425, 0x03f, "Pelephone" }, + { 0x425, 0x77f, "Mirs" }, + { 0x222, -1, "Italy" }, + { 0x222, 0x01f, "TIM" }, + { 0x222, 0x02f, "Elsacom" }, + { 0x222, 0x10f, "Vodafone" }, + { 0x222, 0x30f, "RFI" }, + { 0x222, 0x77f, "IPSE 2000" }, + { 0x222, 0x88f, "Wind" }, + { 0x222, 0x98f, "Blu" }, + { 0x222, 0x99f, "3 Italia" }, + { 0x612, -1, "Ivory Coast" }, + { 0x612, 0x01f, "Cora de Comstar" }, + { 0x612, 0x02f, "Moov" }, + { 0x612, 0x03f, "Orange" }, + { 0x612, 0x04f, "KoZ" }, + { 0x612, 0x05f, "MTN" }, + { 0x612, 0x06f, "ORICEL" }, + { 0x338, -1, "Jamaica" }, + { 0x338, 0x020, "LIME (formerly known as Cable & Wireless)" }, + { 0x338, 0x050, "Digicel" }, + { 0x338, 0x070, "Claro" }, + { 0x338, 0x180, "LIME (formerly known as Cable & Wireless)" }, + { 0x440, -1, "Japan" }, + { 0x440, 0x00f, "eMobile" }, + { 0x440, 0x01f, "NTT docomo" }, + { 0x440, 0x02f, "NTT docomo" }, + { 0x440, 0x03f, "NTT docomo" }, + { 0x440, 0x04f, "SoftBank" }, + { 0x440, 0x06f, "SoftBank" }, + { 0x440, 0x07f, "KDDI" }, + { 0x440, 0x08f, "KDDI" }, + { 0x440, 0x09f, "NTT docomo" }, + { 0x440, 0x10f, "NTT docomo" }, + { 0x440, 0x11f, "NTT docomo" }, + { 0x440, 0x12f, "NTT docomo" }, + { 0x440, 0x13f, "NTT docomo" }, + { 0x440, 0x14f, "NTT docomo" }, + { 0x440, 0x15f, "NTT docomo" }, + { 0x440, 0x16f, "NTT docomo" }, + { 0x440, 0x17f, "NTT docomo" }, + { 0x440, 0x18f, "NTT docomo" }, + { 0x440, 0x19f, "NTT docomo" }, + { 0x440, 0x20f, "SoftBank" }, + { 0x440, 0x21f, "NTT docomo" }, + { 0x440, 0x22f, "NTT docomo" }, + { 0x440, 0x23f, "DoCoMo" }, + { 0x440, 0x24f, "DoCoMo" }, + { 0x440, 0x25f, "DoCoMo" }, + { 0x440, 0x26f, "DoCoMo" }, + { 0x440, 0x27f, "DoCoMo" }, + { 0x440, 0x28f, "DoCoMo" }, + { 0x440, 0x29f, "DoCoMo" }, + { 0x440, 0x30f, "DoCoMo" }, + { 0x440, 0x31f, "DoCoMo" }, + { 0x440, 0x32f, "DoCoMo" }, + { 0x440, 0x33f, "DoCoMo" }, + { 0x440, 0x34f, "DoCoMo" }, + { 0x440, 0x35f, "DoCoMo" }, + { 0x440, 0x36f, "DoCoMo" }, + { 0x440, 0x37f, "DoCoMo" }, + { 0x440, 0x38f, "DoCoMo" }, + { 0x440, 0x39f, "DoCoMo" }, + { 0x440, 0x40f, "SoftBank" }, + { 0x440, 0x41f, "SoftBank" }, + { 0x440, 0x42f, "SoftBank" }, + { 0x440, 0x43f, "SoftBank" }, + { 0x440, 0x44f, "SoftBank" }, + { 0x440, 0x45f, "SoftBank" }, + { 0x440, 0x46f, "SoftBank" }, + { 0x440, 0x47f, "SoftBank" }, + { 0x440, 0x48f, "SoftBank" }, + { 0x440, 0x49f, "DoCoMo" }, + { 0x440, 0x50f, "KDDI" }, + { 0x440, 0x51f, "KDDI" }, + { 0x440, 0x52f, "KDDI" }, + { 0x440, 0x53f, "KDDI" }, + { 0x440, 0x54f, "KDDI" }, + { 0x440, 0x55f, "KDDI" }, + { 0x440, 0x56f, "KDDI" }, + { 0x440, 0x58f, "DoCoMo" }, + { 0x440, 0x60f, "DoCoMo" }, + { 0x440, 0x61f, "DoCoMo" }, + { 0x440, 0x62f, "DoCoMo" }, + { 0x440, 0x63f, "DoCoMo" }, + { 0x440, 0x64f, "DoCoMo" }, + { 0x440, 0x65f, "DoCoMo" }, + { 0x440, 0x66f, "DoCoMo" }, + { 0x440, 0x67f, "DoCoMo" }, + { 0x440, 0x68f, "DoCoMo" }, + { 0x440, 0x69f, "DoCoMo" }, + { 0x440, 0x70f, "au" }, + { 0x440, 0x71f, "KDDI" }, + { 0x440, 0x72f, "KDDI" }, + { 0x440, 0x73f, "KDDI" }, + { 0x440, 0x74f, "KDDI" }, + { 0x440, 0x75f, "KDDI" }, + { 0x440, 0x76f, "KDDI" }, + { 0x440, 0x77f, "KDDI" }, + { 0x440, 0x78f, "Okinawa Cellular Telephone" }, + { 0x440, 0x79f, "KDDI" }, + { 0x440, 0x80f, "TU-KA" }, + { 0x440, 0x81f, "TU-KA" }, + { 0x440, 0x82f, "TU-KA" }, + { 0x440, 0x83f, "TU-KA" }, + { 0x440, 0x84f, "TU-KA" }, + { 0x440, 0x85f, "TU-KA" }, + { 0x440, 0x86f, "TU-KA" }, + { 0x440, 0x87f, "DoCoMo" }, + { 0x440, 0x88f, "KDDI" }, + { 0x440, 0x89f, "KDDI" }, + { 0x440, 0x90f, "SoftBank" }, + { 0x440, 0x92f, "SoftBank" }, + { 0x440, 0x93f, "SoftBank" }, + { 0x440, 0x94f, "SoftBank" }, + { 0x440, 0x95f, "SoftBank" }, + { 0x440, 0x96f, "SoftBank" }, + { 0x440, 0x97f, "SoftBank" }, + { 0x440, 0x98f, "SoftBank" }, + { 0x440, 0x99f, "DoCoMo" }, + { 0x234, -1, "Jersey" }, + { 0x234, 0x50f, "JT-Wave" }, + { 0x234, 0x55f, "Sure Mobile" }, + { 0x234, 0x03f, "Airtel Vodafone" }, + { 0x416, -1, "Jordan" }, + { 0x416, 0x01f, "zain JO" }, + { 0x416, 0x02f, "XPress Telecom" }, + { 0x416, 0x03f, "Umniah" }, + { 0x416, 0x77f, "Orange" }, + { 0x401, -1, "Kazakhstan" }, + { 0x401, 0x01f, "Beeline" }, + { 0x401, 0x02f, "Kcell" }, + { 0x401, 0x07f, "Dalacom" }, + { 0x401, 0x77f, "Mobile Telecom Service" }, + { 0x639, -1, "Kenya" }, + { 0x639, 0x02f, "Safaricom" }, + { 0x639, 0x03f, "Zain" }, + { 0x639, 0x07f, "Orange Kenya" }, + { 0x545, -1, "Kiribati" }, + { 0x545, 0x09f, "Kiribati Frigate" }, + { 0x467, -1, "North Korea" }, + { 0x467, 0x193, "SUN NET" }, + { 0x450, -1, "South Korea" }, + { 0x450, 0x02f, "KT" }, + { 0x450, 0x03f, "Power 017" }, + { 0x450, 0x04f, "KT" }, + { 0x450, 0x05f, "SKT" }, + { 0x450, 0x06f, "LGT" }, + { 0x450, 0x08f, "KT SHOW" }, + { 0x212, -1, "Kosovo" }, + { 0x212, 0x01f, "Vala" }, + { 0x293, 0x41f, "iPKO" }, + { 0x293, 0x41f, "D3 Mobile" }, + { 0x212, 0x01f, "Z Mobile" }, + { 0x419, -1, "Kuwait" }, + { 0x419, 0x02f, "zain KW" }, + { 0x419, 0x03f, "Wataniya" }, + { 0x419, 0x04f, "Viva" }, + { 0x437, -1, "Kyrgyzstan" }, + { 0x437, 0x01f, "Beeline" }, + { 0x437, 0x05f, "MegaCom" }, + { 0x437, 0x09f, "O!" }, + { 0x457, -1, "Laos" }, + { 0x457, 0x01f, "LaoTel" }, + { 0x457, 0x02f, "ETL" }, + { 0x457, 0x03f, "Unitel" }, + { 0x457, 0x08f, "Tigo" }, + { 0x247, -1, "Latvia" }, + { 0x247, 0x01f, "LMT" }, + { 0x247, 0x02f, "Tele2" }, + { 0x247, 0x03f, "TRIATEL" }, + { 0x247, 0x05f, "Bite" }, + { 0x247, 0x06f, "Rigatta" }, + { 0x247, 0x07f, "MTS" }, + { 0x247, 0x08f, "IZZI" }, + { 0x247, 0x09f, "Camel Mobile" }, + { 0x415, -1, "Lebanon" }, + { 0x415, 0x01f, "Alfa" }, + { 0x415, 0x03f, "MTC-Touch" }, + { 0x651, -1, "Lesotho" }, + { 0x651, 0x01f, "Vodacom" }, + { 0x651, 0x02f, "Econet Ezin-cel" }, + { 0x618, -1, "Liberia" }, + { 0x618, 0x01f, "Lonestar Cell" }, + { 0x618, 0x04f, "Comium" }, + { 0x618, 0x20f, "LIBTELCO" }, + { 0x606, -1, "Libya" }, + { 0x606, 0x00f, "Libyana" }, + { 0x606, 0x01f, "Madar" }, + { 0x295, -1, "Liechtenstein" }, + { 0x295, 0x01f, "Swisscom" }, + { 0x295, 0x02f, "Orange" }, + { 0x295, 0x05f, "FL1" }, + { 0x295, 0x77f, "Tele 2" }, + { 0x246, -1, "Lithuania" }, + { 0x246, 0x01f, "Omnitel" }, + { 0x246, 0x02f, "BITE" }, + { 0x246, 0x03f, "Tele 2" }, + { 0x270, -1, "Luxembourg" }, + { 0x270, 0x01f, "LuxGSM" }, + { 0x270, 0x77f, "Tango" }, + { 0x270, 0x99f, "Orange" }, + { 0x455, -1, "Macau" }, + { 0x455, 0x00f, "SmarTone" }, + { 0x455, 0x01f, "CTM" }, + { 0x455, 0x02f, "China Telecom" }, + { 0x455, 0x03f, "3" }, + { 0x455, 0x04f, "CTM" }, + { 0x455, 0x05f, "3" }, + { 0x294, -1, "Republic of Macedonia" }, + { 0x294, 0x01f, "T-Mobile MK" }, + { 0x294, 0x02f, "ONE" }, + { 0x294, 0x03f, "Vip MK" }, + { 0x646, -1, "Madagascar" }, + { 0x646, 0x01f, "Zain" }, + { 0x646, 0x02f, "Orange" }, + { 0x646, 0x03f, "Sacel" }, + { 0x646, 0x04f, "Telma" }, + { 0x650, -1, "Malawi" }, + { 0x650, 0x01f, "TNM" }, + { 0x650, 0x10f, "Zain" }, + { 0x502, -1, "Malaysia" }, + { 0x502, 0x12f, "Maxis" }, + { 0x502, 0x13f, "Celcom" }, + { 0x502, 0x16f, "DiGi" }, + { 0x502, 0x17f, "Maxis" }, + { 0x502, 0x18f, "U Mobile" }, + { 0x502, 0x19f, "Celcom" }, + { 0x472, -1, "Maldives" }, + { 0x472, 0x01f, "Dhiraagu" }, + { 0x472, 0x02f, "Wataniya" }, + { 0x610, -1, "Mali" }, + { 0x610, 0x01f, "Malitel" }, + { 0x610, 0x02f, "Orange" }, + { 0x278, -1, "Malta" }, + { 0x278, 0x01f, "Vodafone" }, + { 0x278, 0x21f, "GO" }, + { 0x278, 0x77f, "Melita" }, + { 0x000, -1, "Marshall Islands" }, +// { 0x000, ?, "?" }, + { 0x340, -1, "Martinique" }, + { 0x340, 0x01f, "Orange" }, + { 0x340, 0x02f, "Outremer" }, + { 0x340, 0x20f, "Digicel" }, + { 0x609, -1, "Mauritania" }, + { 0x609, 0x01f, "Mattel" }, + { 0x609, 0x10f, "Mauritel" }, + { 0x617, -1, "Mauritius" }, + { 0x617, 0x01f, "Orange" }, + { 0x617, 0x02f, "MTML" }, + { 0x617, 0x10f, "Emtel" }, + { 0x334, -1, "Mexico" }, + { 0x334, 0x01f, "Nextel" }, + { 0x334, 0x02f, "Telcel" }, + { 0x334, 0x03f, "movistar" }, + { 0x334, 0x04f, "Iusacell / Unefon" }, + { 0x550, -1, "Federated States of Micronesia" }, + { 0x550, 0x01f, "FSM Telecom" }, + { 0x259, -1, "Moldova" }, + { 0x259, 0x01f, "Orange" }, + { 0x259, 0x02f, "Moldcell" }, + { 0x259, 0x03f, "IDC" }, + { 0x259, 0x03f, "Unit?" }, + { 0x259, 0x04f, "Eventis" }, + { 0x259, 0x05f, "Unit?" }, + { 0x212, -1, "Monaco" }, + { 0x212, 0x01f, "Office des Telephones" }, + { 0x428, -1, "Mongolia" }, + { 0x428, 0x99f, "MobiCom" }, + { 0x428, 0x88f, "Unitel" }, + { 0x428, 0x91f, "Skytel" }, + { 0x428, 0x98f, "G.Mobile" }, + { 0x297, -1, "Montenegro" }, + { 0x297, 0x01f, "Telenor" }, + { 0x297, 0x02f, "T-Mobile" }, + { 0x297, 0x03f, "m:tel CG" }, + { 0x604, -1, "Morocco" }, + { 0x604, 0x00f, "Moditel" }, + { 0x604, 0x01f, "IAM" }, + { 0x604, 0x02f, "INWI" }, + { 0x605, 0x03f, "yassine" }, + { 0x643, -1, "Mozambique" }, + { 0x643, 0x01f, "mCel" }, + { 0x643, 0x04f, "Vodacom" }, + { 0x414, -1, "Myanmar" }, + { 0x414, 0x01f, "MPT" }, + { 0x649, -1, "Namibia" }, + { 0x649, 0x01f, "MTC" }, + { 0x649, 0x02f, "switch" }, + { 0x649, 0x03f, "Leo" }, + { 0x536, -1, "Nauru" }, + { 0x429, -1, "Nepal" }, + { 0x429, 0x01f, "Namaste / NT Mobile" }, + { 0x429, 0x02f, "Ncell" }, + { 0x429, 0x03f, "Sky/C-Phone" }, + { 0x204, -1, "Netherlands" }, + { 0x204, 0x01f, "OneFoon" }, + { 0x204, 0x02f, "Tele2" }, + { 0x204, 0x03f, "Blyk" }, + { 0x204, 0x04f, "Vodafone" }, + { 0x204, 0x05f, "Elephant Talk" }, + { 0x204, 0x06f, "Barablu Mobile" }, + { 0x204, 0x07f, "Teleena" }, + { 0x204, 0x08f, "KPN" }, + { 0x204, 0x09f, "Lycamobile" }, + { 0x204, 0x10f, "KPN" }, + { 0x204, 0x12f, "Telfort" }, + { 0x204, 0x14f, "6Gmobile" }, + { 0x204, 0x16f, "T-Mobile" }, + { 0x204, 0x18f, "Telfort" }, + { 0x204, 0x20f, "Orange Nederland" }, + { 0x204, 0x21f, "NS Railinfrabeheer B.V." }, + { 0x204, 0x67f, "RadioAccess" }, + { 0x204, 0x69f, "KPN Mobile" }, + { 0x362, -1, "Netherlands Antilles" }, + { 0x362, 0x51f, "Telcell" }, + { 0x362, 0x69f, "Digicel" }, + { 0x362, 0x91f, "UTS" }, + { 0x362, 0x00f, "East Caribbean Cellular" }, + { 0x362, 0x00f, "Antiliano Por N.V." }, + { 0x362, 0x95f, "MIO" }, + { 0x362, 0x94f, "Bay?s" }, + { 0x546, -1, "New Caledonia" }, + { 0x546, 0x01f, "Mobilis" }, + { 0x530, -1, "New Zealand" }, + { 0x530, 0x00f, "Telecom" }, + { 0x530, 0x01f, "Vodafone" }, + { 0x530, 0x02f, "Telecom" }, + { 0x530, 0x03f, "Woosh" }, + { 0x530, 0x04f, "TelstraClear" }, + { 0x530, 0x05f, "XT Mobile Network" }, + { 0x530, 0x12f, "360" }, + { 0x530, 0x24f, "2degrees" }, + { 0x710, -1, "Nicaragua" }, + { 0x710, 0x21f, "Claro" }, + { 0x710, 0x30f, "movistar" }, + { 0x710, 0x73f, "SERCOM" }, + { 0x614, -1, "Niger" }, + { 0x614, 0x01f, "SahelCom" }, + { 0x614, 0x02f, "Zain" }, + { 0x614, 0x03f, "Telecel" }, + { 0x614, 0x04f, "Orange" }, + { 0x621, -1, "Nigeria" }, + { 0x621, 0x20f, "Zain" }, + { 0x621, 0x30f, "MTN" }, + { 0x621, 0x40f, "M-Tel" }, + { 0x621, 0x50f, "Glo" }, + { 0x621, 0x60f, "Etisalat" }, + { 0x242, -1, "Norway" }, + { 0x242, 0x01f, "Telenor" }, + { 0x242, 0x02f, "NetCom" }, + { 0x242, 0x03f, "Teletopia" }, + { 0x242, 0x04f, "Tele2" }, + { 0x242, 0x05f, "Network Norway" }, + { 0x242, 0x06f, "Ice" }, + { 0x242, 0x07f, "Ventelo" }, + { 0x242, 0x08f, "TDC Mobil AS" }, + { 0x242, 0x09f, "Barablu Mobile Norway Ltd" }, + { 0x242, 0x20f, "Jernbaneverket AS" }, + { 0x422, -1, "Oman" }, + { 0x422, 0x02f, "Oman Mobile" }, + { 0x422, 0x03f, "Nawras" }, + { 0x410, -1, "Pakistan" }, + { 0x410, 0x01f, "Mobilink" }, + { 0x410, 0x03f, "Ufone" }, + { 0x410, 0x04f, "Zong" }, + { 0x410, 0x06f, "Telenor" }, + { 0x410, 0x07f, "Warid" }, + { 0x552, -1, "Palau" }, + { 0x552, 0x01f, "PNCC" }, + { 0x552, 0x80f, "Palau Mobile" }, + { 0x423, -1, "Palestinian Authority" }, + { 0x423, 0x05f, "Jawwal" }, + { 0x423, 0x06f, "Wataniya" }, + { 0x714, -1, "Panama" }, + { 0x714, 0x01f, "Cable & Wireless" }, + { 0x714, 0x02f, "movistar" }, + { 0x714, 0x04f, "Digicel" }, + { 0x714, 0x03f, "Claro" }, + { 0x537, -1, "Papua New Guinea" }, + { 0x537, 0x01f, "B-Mobile" }, + { 0x537, 0x03f, "Digicel" }, + { 0x744, -1, "Paraguay" }, + { 0x744, 0x01f, "VOX" }, + { 0x744, 0x02f, "Claro" }, + { 0x744, 0x04f, "Tigo" }, + { 0x744, 0x05f, "Personal" }, + { 0x716, -1, "Peru" }, + { 0x716, 0x06f, "movistar" }, + { 0x716, 0x10f, "Claro" }, + { 0x716, 0x17f, "NEXTEL" }, + { 0x515, -1, "Philippines" }, + { 0x515, 0x01f, "Islacom" }, + { 0x515, 0x02f, "Globe" }, + { 0x515, 0x03f, "Smart" }, + { 0x515, 0x05f, "Sun" }, + { 0x515, 0x11f, "PLDT via ACeS Philippines" }, + { 0x515, 0x18f, "Cure" }, + { 0x515, 0x88f, "Nextel" }, + { 0x260, -1, "Poland" }, + { 0x260, 0x01f, "Plus" }, + { 0x260, 0x02f, "Era" }, + { 0x260, 0x03f, "Orange" }, + { 0x260, 0x04f, "Netia S.A." }, + { 0x260, 0x05f, "Polska Telefonia Kom?rkowa Centertel Sp. z o.o." }, + { 0x260, 0x06f, "Play" }, + { 0x260, 0x07f, "Netia" }, + { 0x260, 0x08f, "E-Telko Sp. z o.o." }, + { 0x260, 0x09f, "Telekomunikacja Kolejowa Sp. z o.o." }, + { 0x260, 0x10f, "Sferia" }, + { 0x260, 0x11f, "Nordisk Polska" }, + { 0x260, 0x12f, "Cyfrowy Polsat" }, + { 0x260, 0x13f, "Sferia" }, + { 0x260, 0x14f, "Sferia" }, + { 0x260, 0x15f, "CenterNet" }, + { 0x260, 0x16f, "Mobyland" }, + { 0x260, 0x17f, "Aero2" }, + { 0x268, -1, "Portugal" }, + { 0x268, 0x01f, "Vodafone" }, + { 0x268, 0x03f, "Optimus" }, + { 0x268, 0x06f, "TMN" }, + { 0x268, 0x21f, "Zapp" }, + { 0x330, -1, "Puerto Rico" }, + { 0x330, 0x11f, "Claro" }, + { 0x427, -1, "Qatar" }, + { 0x427, 0x01f, "Qatarnet" }, + { 0x427, 0x02f, "Vodafone Qatar" }, + { 0x647, -1, "R&?union" }, + { 0x647, 0x00f, "Orange" }, + { 0x647, 0x02f, "Outremer" }, + { 0x647, 0x10f, "SFR Reunion" }, + { 0x226, -1, "Romania" }, + { 0x226, 0x01f, "Vodafone" }, + { 0x226, 0x02f, "Romtelecom" }, + { 0x226, 0x03f, "Cosmote" }, + { 0x226, 0x04f, "Cosmote" }, + { 0x226, 0x05f, "Digi.Mobil" }, + { 0x226, 0x06f, "Cosmote" }, + { 0x226, 0x10f, "Orange" }, + { 0x250, -1, "Russian Federation" }, + { 0x250, 0x01f, "MTS" }, + { 0x250, 0x02f, "MegaFon" }, + { 0x250, 0x03f, "NCC" }, + { 0x250, 0x04f, "Sibchallenge" }, + { 0x250, 0x05f, "ETK" }, + { 0x250, 0x06f, "Skylink" }, + { 0x250, 0x07f, "SMARTS" }, + { 0x250, 0x09f, "Skylink" }, + { 0x250, 0x10f, "DTC" }, + { 0x250, 0x11f, "Orensot" }, + { 0x250, 0x12f, "Baykalwestcom" }, + { 0x250, 0x12f, "Akos" }, + { 0x250, 0x13f, "KUGSM" }, + { 0x250, 0x15f, "SMARTS" }, + { 0x250, 0x16f, "NTC" }, + { 0x250, 0x17f, "Utel" }, + { 0x250, 0x19f, "INDIGO" }, + { 0x250, 0x20f, "Tele2" }, + { 0x250, 0x23f, "Mobicom - Novosibirsk" }, + { 0x250, 0x28f, "Beeline" }, + { 0x250, 0x35f, "MOTIV" }, + { 0x250, 0x38f, "Tambov GSM" }, + { 0x250, 0x39f, "Utel" }, + { 0x250, 0x44f, "Stavtelesot / North Caucasian GSM" }, + { 0x250, 0x92f, "Primtelefon" }, + { 0x250, 0x93f, "Telecom XXI" }, + { 0x250, 0x99f, "Beeline" }, +// { 0x250, ?, "SkyLink/MTS/the Moscow Cellular communication" }, + { 0x635, -1, "Rwanda" }, + { 0x635, 0x10f, "MTN" }, + { 0x635, 0x13f, "Tigo" }, + { 0x356, -1, "Saint Kitts and Nevis" }, + { 0x356, 0x050, "Digicel" }, + { 0x356, 0x110, "Cable & Wireless" }, + { 0x358, -1, "Saint Lucia" }, + { 0x358, 0x050, "Digicel" }, + { 0x358, 0x110, "Cable & Wireless" }, + { 0x308, -1, "Saint Pierre and Miquelon" }, + { 0x308, 0x01f, "Ameris" }, + { 0x360, -1, "Saint Vincent and the Grenadines" }, + { 0x360, 0x070, "Digicel" }, + { 0x360, 0x100, "Cingular Wireless" }, + { 0x360, 0x110, "Cable & Wireless" }, + { 0x549, -1, "Samoa" }, + { 0x549, 0x01f, "Digicel" }, + { 0x549, 0x27f, "SamoaTel" }, + { 0x292, -1, "San Marino" }, + { 0x292, 0x01f, "PRIMA" }, + { 0x626, -1, "Sao Tome and Principe" }, + { 0x626, 0x01f, "CSTmovel" }, + { 0x420, -1, "Saudi Arabia" }, + { 0x420, 0x01f, "Al Jawal" }, + { 0x420, 0x03f, "Mobily" }, + { 0x420, 0x07f, "EAE" }, + { 0x420, 0x04f, "Zain SA" }, + { 0x608, -1, "Senegal" }, + { 0x608, 0x01f, "Orange (telecommunications)" }, + { 0x608, 0x02f, "Tigo" }, + { 0x608, 0x03f, "Expresso" }, + { 0x220, -1, "Serbia" }, + { 0x220, 0x01f, "Telenor" }, + { 0x220, 0x03f, "mt:s" }, + { 0x220, 0x05f, "VIP" }, + { 0x633, -1, "Seychelles" }, + { 0x633, 0x01f, "Cable & Wireless" }, + { 0x633, 0x02f, "Mediatech International" }, + { 0x633, 0x10f, "Airtel" }, + { 0x619, -1, "Sierra Leone" }, + { 0x619, 0x01f, "Zain" }, + { 0x619, 0x02f, "Millicom" }, + { 0x619, 0x03f, "Datatel" }, + { 0x619, 0x04f, "Comium" }, + { 0x619, 0x05f, "Africell" }, + { 0x619, 0x25f, "Mobitel" }, +// { 0x619, ?, "LeoneCel" }, + { 0x525, -1, "Singapore" }, + { 0x525, 0x01f, "SingTel" }, + { 0x525, 0x02f, "SingTel-G18" }, + { 0x525, 0x03f, "M1" }, + { 0x525, 0x05f, "StarHub" }, + { 0x525, 0x12f, "Digital Trunked Radio Network" }, + { 0x231, -1, "Slovakia" }, + { 0x231, 0x01f, "Orange" }, + { 0x231, 0x02f, "T-Mobile" }, + { 0x231, 0x03f, "Unient Communications" }, + { 0x231, 0x04f, "T-Mobile" }, + { 0x231, 0x05f, "Mobile Entertainment Company" }, + { 0x231, 0x06f, "O2" }, + { 0x231, 0x99f, "?SR" }, + { 0x293, -1, "Slovenia" }, + { 0x293, 0x40f, "Si.mobil" }, + { 0x293, 0x41f, "Mobitel" }, + { 0x293, 0x64f, "T-2" }, + { 0x293, 0x70f, "Tu?mobil" }, + { 0x540, -1, "Solomon Islands" }, + { 0x637, -1, "Somalia" }, + { 0x637, 0x01f, "Telesom" }, + { 0x637, 0x04f, "Somafone" }, + { 0x637, 0x10f, "Nationlink" }, + { 0x637, 0x25f, "Hormuud" }, + { 0x637, 0x30f, "Golis" }, + { 0x637, 0x82f, "Telcom" }, + { 0x655, -1, "South Africa" }, + { 0x655, 0x01f, "Vodacom" }, + { 0x655, 0x06f, "Sentech" }, + { 0x655, 0x07f, "Cell C" }, + { 0x655, 0x10f, "MTN" }, + { 0x655, 0x11f, "SAPS Gauteng" }, + { 0x655, 0x13f, "Neotel" }, + { 0x655, 0x21f, "Cape Town Metropolitan Council" }, + { 0x655, 0x30f, "Bokamoso Consortium" }, + { 0x655, 0x31f, "Karabo Telecoms (Pty) Ltd." }, + { 0x655, 0x32f, "Ilizwi Telecommunications" }, + { 0x655, 0x33f, "Thinta Thinta Telecommunications" }, + { 0x655, 0x02f, "Telkom" }, + { 0x214, -1, "Spain" }, + { 0x214, 0x01f, "Vodafone" }, + { 0x214, 0x03f, "Orange" }, + { 0x214, 0x04f, "Yoigo" }, + { 0x214, 0x05f, "TME" }, + { 0x214, 0x06f, "Vodafone" }, + { 0x214, 0x07f, "movistar" }, + { 0x214, 0x08f, "Euskaltel" }, + { 0x214, 0x09f, "Orange" }, + { 0x214, 0x15f, "BT" }, + { 0x214, 0x16f, "TeleCable" }, + { 0x214, 0x17f, "M?bil R" }, + { 0x214, 0x18f, "ONO" }, + { 0x214, 0x19f, "Simyo" }, + { 0x214, 0x21f, "Jazztel" }, + { 0x214, 0x22f, "DigiMobil" }, + { 0x214, 0x23f, "Barablu" }, + { 0x413, -1, "Sri Lanka" }, + { 0x413, 0x01f, "Mobitel" }, + { 0x413, 0x02f, "Dialog" }, + { 0x413, 0x03f, "Etisalat" }, + { 0x413, 0x05f, "Airtel" }, + { 0x413, 0x08f, "Hutch" }, + { 0x413, 0x00f, "RTEC Mobile" }, + { 0x634, -1, "Sudan" }, + { 0x634, 0x01f, "Zain SD" }, + { 0x634, 0x02f, "MTN" }, + { 0x634, 0x05f, "Vivacell" }, + { 0x746, -1, "Suriname" }, + { 0x746, 0x05f, "Telesur" }, + { 0x653, -1, "Swaziland" }, + { 0x653, 0x10f, "Swazi MTN" }, + { 0x240, -1, "Sweden" }, + { 0x240, 0x01f, "Telia" }, + { 0x240, 0x02f, "3" }, + { 0x240, 0x03f, "Ice.net" }, + { 0x240, 0x04f, "3G Infrastructure Services" }, + { 0x240, 0x05f, "Sweden 3G" }, + { 0x240, 0x06f, "Telenor" }, + { 0x240, 0x07f, "Tele2" }, + { 0x240, 0x08f, "Telenor" }, + { 0x240, 0x09f, "djuice" }, + { 0x240, 0x10f, "Spring Mobil" }, + { 0x240, 0x11f, "Lindholmen Science Park" }, + { 0x240, 0x12f, "Barablu Mobile Scandinavia" }, + { 0x240, 0x13f, "Ventelo Sverige" }, + { 0x240, 0x14f, "TDC Mobil" }, + { 0x240, 0x15f, "Wireless Maingate Nordic" }, + { 0x240, 0x16f, "42IT" }, + { 0x240, 0x17f, "G?talandsn?tet" }, + { 0x240, 0x20f, "Wireless Maingate Message Services" }, + { 0x240, 0x21f, "MobiSir" }, + { 0x240, 0x25f, "DigiTelMobile" }, + { 0x228, -1, "Switzerland" }, + { 0x228, 0x01f, "Swisscom" }, + { 0x228, 0x02f, "Sunrise" }, + { 0x228, 0x03f, "Orange" }, + { 0x228, 0x05f, "Togewanet AG (Comfone)" }, + { 0x228, 0x06f, "SBB AG" }, + { 0x228, 0x07f, "IN&Phone" }, + { 0x228, 0x08f, "Tele2" }, + { 0x228, 0x50f, "3G Mobile AG" }, + { 0x228, 0x51f, "BebbiCell AG" }, + { 0x417, -1, "Syria" }, + { 0x417, 0x01f, "Syriatel" }, + { 0x417, 0x02f, "MTN" }, + { 0x466, -1, "Taiwan" }, + { 0x466, 0x01f, "FarEasTone" }, + { 0x466, 0x02f, "APTG" }, + { 0x466, 0x06f, "Tuntex" }, + { 0x466, 0x11f, "Chunghwa LDM" }, + { 0x466, 0x88f, "KG Telecom" }, + { 0x466, 0x89f, "VIBO" }, + { 0x466, 0x92f, "Chungwa" }, + { 0x466, 0x93f, "MobiTai" }, + { 0x466, 0x97f, "Taiwan Mobile" }, + { 0x466, 0x99f, "TransAsia" }, + { 0x436, -1, "Tajikistan" }, + { 0x436, 0x01f, "Tcell" }, + { 0x436, 0x02f, "Indigo" }, + { 0x436, 0x03f, "MLT" }, + { 0x436, 0x04f, "Babilon-M" }, + { 0x436, 0x05f, "Beeline" }, + { 0x640, -1, "Tanzania" }, + { 0x640, 0x06f, "SasaTel" }, + { 0x640, 0x02f, "tiGO" }, + { 0x640, 0x03f, "Zantel" }, + { 0x640, 0x04f, "Vodacom" }, + { 0x640, 0x05f, "Zain" }, + { 0x520, -1, "Thailand" }, + { 0x520, 0x00f, "Hutch" }, + { 0x520, 0x01f, "AIS" }, + { 0x520, 0x02f, "CAT CDMA" }, + { 0x520, 0x10f, "?" }, + { 0x520, 0x15f, "Thai Mobile" }, + { 0x520, 0x15f, "TOT 3G" }, + { 0x520, 0x18f, "dtac" }, + { 0x520, 0x23f, "AIS GSM 1800" }, + { 0x520, 0x99f, "True Move" }, + { 0x520, 0x00f, "WE PCT" }, + { 0x615, -1, "Togo" }, + { 0x615, 0x01f, "Togo Cell" }, + { 0x615, 0x03f, "Moov" }, + { 0x539, -1, "Tonga" }, + { 0x539, 0x01f, "Tonga Communications Corporation" }, + { 0x539, 0x43f, "Shoreline Communication" }, + { 0x539, 0x88f, "Digicel" }, + { 0x374, -1, "Trinidad and Tobago" }, + { 0x374, 0x12f, "bmobile" }, + { 0x374, 0x13f, "Digicel" }, + { 0x605, -1, "Tunisia" }, + { 0x605, 0x01f, "Orange" }, + { 0x605, 0x02f, "Tunicell" }, + { 0x605, 0x03f, "Tunisiana" }, + { 0x286, -1, "Turkey" }, + { 0x286, 0x01f, "Turkcell" }, + { 0x286, 0x02f, "Vodafone" }, + { 0x286, 0x03f, "Avea" }, + { 0x286, 0x04f, "Aycell" }, + { 0x438, -1, "Turkmenistan" }, + { 0x438, 0x01f, "MTS" }, + { 0x438, 0x02f, "TM-Cell" }, + { 0x376, -1, "Turks and Caicos Islands" }, + { 0x376, 0x350, "C&W" }, + { 0x376, 0x352, "Islandcom" }, + { 0x338, 0x05f, "Digicel" }, + { 0x553, -1, "Tuvalu" }, + { 0x553, 0x01f, "TTC" }, + { 0x641, -1, "Uganda" }, + { 0x641, 0x01f, "Zain" }, + { 0x641, 0x10f, "MTN" }, + { 0x641, 0x11f, "Uganda Telecom Ltd." }, + { 0x641, 0x22f, "Warid Telecom" }, + { 0x641, 0x14f, "Orange" }, + { 0x255, -1, "Ukraine" }, + { 0x255, 0x01f, "MTS" }, + { 0x255, 0x02f, "Beeline" }, + { 0x255, 0x03f, "Kyivstar" }, + { 0x255, 0x04f, "IT" }, + { 0x255, 0x05f, "Golden Telecom" }, + { 0x255, 0x06f, "life:)" }, + { 0x255, 0x07f, "Ukrtelecom" }, + { 0x255, 0x21f, "PEOPLEnet" }, + { 0x255, 0x23f, "CDMA Ukraine" }, + { 0x424, -1, "United Arab Emirates" }, + { 0x424, 0x02f, "Etisalat" }, + { 0x424, 0x03f, "du" }, + { 0x234, -1, "United Kingdom" }, + { 0x234, 0x00f, "BT" }, + { 0x234, 0x01f, "UK01" }, + { 0x234, 0x02f, "O2" }, + { 0x234, 0x03f, "Airtel-Vodafone" }, + { 0x234, 0x04f, "FMS Solutions Ltd" }, + { 0x234, 0x07f, "Cable and Wireless UK" }, + { 0x234, 0x08f, "OnePhone Ltd" }, + { 0x234, 0x10f, "O2" }, + { 0x234, 0x11f, "O2" }, + { 0x234, 0x12f, "Railtrack" }, + { 0x234, 0x14f, "Hay Systems Ltd" }, + { 0x234, 0x15f, "Vodafone" }, + { 0x234, 0x16f, "Opal Telecom Ltd" }, + { 0x234, 0x18f, "Cloud9" }, + { 0x234, 0x19f, "Teleware" }, + { 0x234, 0x20f, "3" }, + { 0x234, 0x22f, "RoutoMessaging" }, + { 0x234, 0x25f, "Truphone" }, + { 0x234, 0x30f, "T-Mobile" }, + { 0x234, 0x31f, "Virgin" }, + { 0x234, 0x32f, "Virgin" }, + { 0x234, 0x33f, "Orange" }, + { 0x234, 0x34f, "Orange" }, + { 0x234, 0x50f, "JT-Wave" }, + { 0x234, 0x55f, "Cable & Wireless Guernsey / Sure Mobile (Jersey)" }, + { 0x234, 0x58f, "Manx Telecom" }, + { 0x234, 0x75f, "Inquam" }, + { 0x234, 0x77f, "BT" }, + { 0x200, -1, "United States of America" }, + { 0x200, 0x053, "Virgin Mobile US" }, + { 0x200, 0x054, "Alltel US" }, + { 0x200, 0x066, "U.S. Cellular" }, + { 0x310, 0x00f, "nTelos" }, + { 0x310, 0x000, "Mid-Tex Cellular" }, + { 0x310, 0x004, "Verizon" }, + { 0x310, 0x010, "MCI" }, + { 0x310, 0x012, "Verizon" }, + { 0x310, 0x013, "MobileTel" }, + { 0x310, 0x014, "Testing" }, + { 0x310, 0x016, "Cricket Communications" }, + { 0x310, 0x017, "North Sight Communications Inc." }, + { 0x310, 0x020, "Union Telephone Company" }, + { 0x310, 0x026, "T-Mobile" }, + { 0x310, 0x030, "Centennial" }, + { 0x310, 0x034, "Airpeak" }, + { 0x310, 0x038, "AT&T" }, + { 0x310, 0x040, "Concho" }, + { 0x310, 0x046, "SIMMETRY" }, + { 0x310, 0x060, "Consolidated Telcom" }, + { 0x310, 0x070, "Highland Cellular" }, + { 0x310, 0x080, "Corr" }, + { 0x310, 0x090, "AT&T" }, + { 0x310, 0x100, "Plateau Wireless" }, + { 0x310, 0x110, "PTI Pacifica" }, + { 0x310, 0x120, "Sprint" }, + { 0x310, 0x150, "AT&T" }, + { 0x310, 0x160, "T-Mobile" }, + { 0x310, 0x170, "T-Mobile" }, + { 0x310, 0x180, "West Central" }, + { 0x310, 0x190, "Dutch Harbor" }, + { 0x310, 0x200, "T-Mobile" }, + { 0x310, 0x210, "T-Mobile" }, + { 0x310, 0x220, "T-Mobile" }, + { 0x310, 0x230, "T-Mobile" }, + { 0x310, 0x240, "T-Mobile" }, + { 0x310, 0x250, "T-Mobile" }, + { 0x310, 0x260, "T-Mobile" }, + { 0x310, 0x270, "T-Mobile" }, + { 0x310, 0x280, "T-Mobile" }, + { 0x310, 0x290, "T-Mobile" }, + { 0x310, 0x300, "iSmart Mobile" }, + { 0x310, 0x310, "T-Mobile" }, + { 0x310, 0x311, "Farmers Wireless" }, + { 0x310, 0x320, "Cellular One" }, + { 0x310, 0x330, "T-Mobile" }, + { 0x310, 0x340, "Westlink" }, + { 0x310, 0x350, "Carolina Phone" }, + { 0x310, 0x380, "AT&T Mobility" }, + { 0x310, 0x390, "Cellular One of East Texas" }, + { 0x310, 0x400, "i CAN_GSM" }, + { 0x310, 0x410, "AT&T" }, + { 0x310, 0x420, "Cincinnati Bell" }, + { 0x310, 0x430, "Alaska Digitel" }, + { 0x310, 0x440, "Cellular One" }, + { 0x310, 0x450, "Viaero" }, + { 0x310, 0x460, "Simmetry" }, + { 0x310, 0x480, "Choice Phone" }, + { 0x310, 0x490, "T-Mobile" }, + { 0x310, 0x500, "Alltel" }, + { 0x310, 0x510, "Airtel" }, + { 0x310, 0x520, "VeriSign" }, + { 0x310, 0x530, "West Virginia Wireless" }, + { 0x310, 0x540, "Oklahoma Western" }, + { 0x310, 0x560, "AT&T" }, + { 0x310, 0x570, "Cellular One" }, + { 0x310, 0x580, "T-Mobile" }, + { 0x310, 0x590, "Alltel" }, + { 0x310, 0x610, "Epic Touch" }, + { 0x310, 0x620, "Coleman County Telecom" }, + { 0x310, 0x630, "AmeriLink PCS" }, + { 0x310, 0x640, "Airadigm" }, + { 0x310, 0x650, "Jasper" }, + { 0x310, 0x660, "T-Mobile" }, + { 0x310, 0x670, "Northstar" }, + { 0x310, 0x680, "AT&T" }, + { 0x310, 0x690, "Conestoga" }, + { 0x310, 0x730, "SeaMobile" }, + { 0x310, 0x740, "Convey" }, + { 0x310, 0x760, "Panhandle" }, + { 0x310, 0x770, "i wireless" }, + { 0x310, 0x780, "Airlink PCS" }, + { 0x310, 0x790, "PinPoint" }, + { 0x310, 0x800, "T-Mobile" }, + { 0x310, 0x830, "Caprock" }, + { 0x310, 0x850, "Aeris" }, + { 0x310, 0x870, "PACE" }, + { 0x310, 0x880, "Advantage" }, + { 0x310, 0x890, "Unicel" }, + { 0x310, 0x900, "Mid-Rivers Wireless" }, + { 0x310, 0x910, "First Cellular" }, + { 0x310, 0x940, "Iris Wireless LLC" }, + { 0x310, 0x950, "XIT Wireless" }, + { 0x310, 0x960, "Plateau Wireless" }, + { 0x310, 0x970, "Globalstar" }, + { 0x310, 0x980, "AT&T Mobility" }, + { 0x310, 0x990, "AT&T Mobility" }, + { 0x311, 0x000, "Mid-Tex Cellular" }, + { 0x311, 0x010, "Chariton Valley" }, + { 0x311, 0x020, "Missouri RSA 5 Partnership" }, + { 0x311, 0x030, "Indigo Wireless" }, + { 0x311, 0x040, "Commnet Wireless" }, + { 0x311, 0x050, "Wikes Cellular" }, + { 0x311, 0x060, "Farmers Cellular" }, + { 0x311, 0x070, "Easterbrooke" }, + { 0x311, 0x080, "Pine Cellular" }, + { 0x311, 0x090, "Long Lines Wireless" }, + { 0x311, 0x100, "High Plains Wireless" }, + { 0x311, 0x110, "High Plains Wireless" }, + { 0x311, 0x120, "Choice Phone" }, + { 0x311, 0x130, "Cell One Amarillo" }, + { 0x311, 0x140, "Sprocket" }, + { 0x311, 0x150, "Wilkes Cellular" }, + { 0x311, 0x160, "Endless Mountains Wireless" }, + { 0x311, 0x170, "PetroCom" }, + { 0x311, 0x180, "Cingular Wireless" }, + { 0x311, 0x190, "Cellular Properties" }, + { 0x311, 0x210, "Farmers Cellular" }, + { 0x316, 0x010, "Nextel" }, + { 0x316, 0x011, "Southern Communications Services" }, + { 0x748, -1, "Uruguay" }, + { 0x748, 0x00f, "Ancel" }, + { 0x748, 0x01f, "Ancel" }, + { 0x748, 0x07f, "Movistar" }, + { 0x748, 0x10f, "Claro" }, + { 0x434, -1, "Uzbekistan" }, + { 0x434, 0x01f, "Buztel" }, + { 0x434, 0x02f, "Uzmacom" }, + { 0x434, 0x04f, "Beeline" }, + { 0x434, 0x05f, "Ucell" }, + { 0x434, 0x06f, "Perfectum Mobile" }, + { 0x434, 0x07f, "MTS" }, + { 0x541, -1, "Vanuatu" }, + { 0x541, 0x01f, "SMILE" }, + { 0x225, -1, "Vatican" }, + { 0x734, -1, "Venezuela" }, + { 0x734, 0x01f, "Digitel" }, + { 0x734, 0x02f, "Digitel" }, + { 0x734, 0x03f, "Digitel" }, + { 0x734, 0x04f, "movistar" }, + { 0x734, 0x06f, "Movilnet" }, + { 0x452, -1, "Vietnam" }, + { 0x452, 0x01f, "MobiFone" }, + { 0x452, 0x02f, "Vinaphone" }, + { 0x452, 0x03f, "S-Fone" }, + { 0x452, 0x04f, "Viettel Mobile" }, + { 0x452, 0x05f, "Vietnamobile" }, + { 0x452, 0x06f, "E-Mobile" }, + { 0x452, 0x07f, "Beeline VN" }, + { 0x421, -1, "Yemen" }, + { 0x421, 0x01f, "SabaFon" }, + { 0x421, 0x02f, "MTN" }, + { 0x421, 0x03f, "Yemen Mobile" }, + { 0x421, 0x04f, "HiTS-UNITEL" }, + { 0x645, -1, "Zambia" }, + { 0x645, 0x01f, "Zain" }, + { 0x645, 0x02f, "MTN" }, + { 0x645, 0x03f, "ZAMTEL" }, + { 0x648, -1, "Zimbabwe" }, + { 0x648, 0x01f, "Net*One" }, + { 0x648, 0x03f, "Telecel" }, + { 0x648, 0x04f, "Econet" }, + { 0x901, -1, "International" }, + { 0x901, 0x01f, "ICO" }, + { 0x901, 0x02f, "Sense Communications International" }, + { 0x901, 0x03f, "Iridium" }, + { 0x901, 0x04f, "Globalstar" }, + { 0x901, 0x05f, "Thuraya RMSS Network" }, + { 0x901, 0x06f, "Thuraya Satellite Telecommunications Company" }, + { 0x901, 0x07f, "Ellipso" }, + { 0x901, 0x08f, "" }, + { 0x901, 0x09f, "Tele1 Europe" }, + { 0x901, 0x10f, "ACeS" }, + { 0x901, 0x11f, "Inmarsat" }, + { 0x901, 0x12f, "MCP" }, + { 0x901, 0x13f, "GSM.AQ" }, + { 0x901, 0x14f, "AeroMobile AS" }, + { 0x901, 0x15f, "OnAir Switzerland Sarl" }, + { 0x901, 0x16f, "Jasper Systems" }, + { 0x901, 0x17f, "Navitas" }, + { 0x901, 0x18f, "Cellular @Sea" }, + { 0x901, 0x19f, "Vodafone Malta Maritime" }, + { 0x901, 0x21f, "Seanet" }, + { 0x901, 0x24f, "iNum" }, + { 0x901, 0x29f, "Telenor" }, + { 0, 0, NULL } +}; + +/* GSM 03.22 Annex A */ +int gsm_match_mcc(uint16_t mcc, char *imsi) +{ + uint16_t sim_mcc; + + sim_mcc = ((imsi[0] - '0') << 8) + + ((imsi[1] - '0') << 4) + + imsi[2] - '0'; + + return (mcc == sim_mcc); +} + +/* GSM 03.22 Annex A */ +int gsm_match_mnc(uint16_t mcc, uint8_t mnc, char *imsi) +{ + uint16_t sim_mnc; + + /* 1. SIM-MCC = BCCH-MCC */ + if (!gsm_match_mcc(mcc, imsi)) + return 0; + + /* 2. 3rd digit of BCCH-MNC is not 0xf */ + if ((mnc & 0x00f) != 0x00f) { + /* 3. 3 digit SIM-MNC = BCCH-MNC */ + sim_mnc = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + imsi[5] - '0'; + + return (mnc == sim_mnc); + } + + /* 4. BCCH-MCC in the range 310-316 */ + if (mcc >= 310 && mcc <= 316) { + /* 5. 3rd diit of SIM-MNC is 0 */ + if (imsi[5] != 0) + return 0; + } + + /* 6. 1st 2 digits of SIM-MNC and BCCH-MNC match */ + sim_mnc = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + 0x00f; + + return (mnc == sim_mnc); +} + +const char *gsm_print_mcc(uint16_t mcc) +{ + static char string[5] = "000"; + + snprintf(string, 4, "%03x", mcc); + return string; +} + +const char *gsm_print_mnc(uint16_t mnc) +{ + static char string[7]; + + /* invalid format: return hex value */ + if ((mnc & 0xf000) + || (mnc & 0x0f00) > 0x0900 + || (mnc & 0x00f0) > 0x0090 + || ((mnc & 0x000f) > 0x0009 && (mnc & 0x000f) < 0x000f)) { + snprintf(string, 6, "0x%03x", mnc); + return string; + } + + /* two digits */ + if ((mnc & 0x000f) == 0x000f) { + snprintf(string, 6, "%02x", mnc >> 4); + return string; + } + + /* three digits */ + snprintf(string, 6, "%03x", mnc); + return string; +} + +const uint16_t gsm_input_mcc(char *string) +{ + uint16_t mcc; + + if (strlen(string) != 3) + return 0; + if (string[0] < '0' || string [0] > '9' + || string[1] < '0' || string [1] > '9' + || string[2] < '0' || string [2] > '9') + return 0; + + mcc = ((string[0] - '0') << 8) + | ((string[1] - '0') << 4) + | ((string[2] - '0')); + + return mcc; +} + +const uint16_t gsm_input_mnc(char *string) +{ + uint16_t mnc = 0; + + if (strlen(string) == 2) { + if (string[0] < '0' || string [0] > '9' + || string[1] < '0' || string [1] > '9') + return 0; + + mnc = ((string[0] - '0') << 8) + | ((string[1] - '0') << 4) + | 0x00f; + } else + if (strlen(string) == 3) { + if (string[0] < '0' || string [0] > '9' + || string[1] < '0' || string [1] > '9' + || string[2] < '0' || string [2] > '9') + return 0; + + mnc = ((string[0] - '0') << 8) + | ((string[1] - '0') << 4) + | ((string[2] - '0')); + } + + return mnc; +} + +const char *gsm_get_mcc(uint16_t mcc) +{ + int i; + + for (i = 0; gsm_networks[i].name; i++) + if (gsm_networks[i].mnc < 0 && gsm_networks[i].mcc == mcc) + return gsm_networks[i].name; + + return gsm_print_mcc(mcc); +} + +const char *gsm_get_mnc(uint16_t mcc, uint16_t mnc) +{ + int i; + + for (i = 0; gsm_networks[i].name; i++) + if (gsm_networks[i].mcc == mcc && gsm_networks[i].mnc == mnc) + return gsm_networks[i].name; + + return gsm_print_mnc(mnc); +} + +/* get MCC from IMSI */ +const char *gsm_imsi_mcc(char *imsi) +{ + int i, found = 0; + uint16_t mcc; + + mcc = ((imsi[0] - '0') << 8) + | ((imsi[1] - '0') << 4) + | ((imsi[2] - '0')); + + for (i = 0; gsm_networks[i].name; i++) { + if (gsm_networks[i].mcc == mcc) { + found = 1; + break; + } + } + if (found == 0) + return "Unknown"; + + return gsm_networks[i].name; +} + +/* get MNC from IMSI */ +const char *gsm_imsi_mnc(char *imsi) +{ + int i, found = 0, position = 0; + uint16_t mcc, mnc2, mnc3; + + mcc = ((imsi[0] - '0') << 8) + | ((imsi[1] - '0') << 4) + | ((imsi[2] - '0')); + mnc2 = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + 0x00f; + mnc3 = ((imsi[3] - '0') << 8) + + ((imsi[4] - '0') << 4) + + imsi[5] - '0'; + + for (i = 0; gsm_networks[i].name; i++) { + if (gsm_networks[i].mcc != mcc) + continue; + if ((gsm_networks[i].mnc & 0x00f) == 0x00f) { + if (mnc2 == gsm_networks[i].mnc) { + found++; + position = i; + } + } else { + if (mnc3 == gsm_networks[i].mnc) { + found++; + position = i; + } + } + } + + if (found == 0) + return "Unknown"; + if (found > 1) + return "Ambiguous"; + return gsm_networks[position].name; +} + + diff --git a/src/host/layer23/src/rslms.c b/src/host/layer23/src/rslms.c new file mode 100644 index 00000000..f8e15663 --- /dev/null +++ b/src/host/layer23/src/rslms.c @@ -0,0 +1,156 @@ +/* RSLms - GSM 08.58 like protocol between L2 and L3 of GSM Um interface */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocore/msgb.h> +#include <osmocore/rsl.h> +#include <osmocore/tlv.h> +#include <osmocore/protocol/gsm_04_08.h> + +#include <osmocom/logging.h> +#include <osmocom/lapdm.h> +#include <osmocom/rslms.h> +#include <osmocom/layer3.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/l1ctl.h> + +/* Send a 'simple' RLL request to L2 */ +int rslms_tx_rll_req(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id) +{ + struct msgb *msg; + + msg = rsl_rll_simple(msg_type, chan_nr, link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +/* Send a RLL request (including L3 info) to L2 */ +int rslms_tx_rll_req_l3(struct osmocom_ms *ms, uint8_t msg_type, + uint8_t chan_nr, uint8_t link_id, struct msgb *msg) +{ + rsl_rll_push_l3(msg, msg_type, chan_nr, link_id, 1); + + return rslms_recvmsg(msg, ms); +} + +static int ccch_enabled = 0; +static int rach_count = 0; + +static int rslms_rx_udata_ind(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + struct tlv_parsed tv; + int rc = 0; + + DEBUGP(DRSL, "RSLms UNIT DATA IND chan_nr=0x%02x link_id=0x%02x\n", + rllh->chan_nr, rllh->link_id); + + rsl_tlv_parse(&tv, rllh->data, msgb_l2len(msg)-sizeof(*rllh)); + if (!TLVP_PRESENT(&tv, RSL_IE_L3_INFO)) { + DEBUGP(DRSL, "UNIT_DATA_IND without L3 INFO ?!?\n"); + return -EIO; + } + msg->l3h = (uint8_t *) TLVP_VAL(&tv, RSL_IE_L3_INFO); + + if (rllh->chan_nr == RSL_CHAN_PCH_AGCH) { + rc = gsm48_rx_ccch(msg, ms); + ccch_enabled = 1; + } else if (rllh->chan_nr == RSL_CHAN_BCCH) { + rc = gsm48_rx_bcch(msg, ms); + if (ccch_enabled && (rach_count < 2)) { + tx_ph_rach_req(ms); + rach_count++; + } + } + + return rc; +} + +static int rslms_rx_rll(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int rc = 0; + + switch (rllh->c.msg_type) { + case RSL_MT_DATA_IND: + DEBUGP(DRSL, "RSLms DATA IND\n"); + /* FIXME: implement this */ + break; + case RSL_MT_UNIT_DATA_IND: + rc = rslms_rx_udata_ind(msg, ms); + break; + case RSL_MT_EST_IND: + DEBUGP(DRSL, "RSLms EST IND\n"); + /* FIXME: implement this */ + break; + case RSL_MT_EST_CONF: + DEBUGP(DRSL, "RSLms EST CONF\n"); + /* FIXME: implement this */ + break; + case RSL_MT_REL_CONF: + DEBUGP(DRSL, "RSLms REL CONF\n"); + /* FIXME: implement this */ + break; + case RSL_MT_ERROR_IND: + DEBUGP(DRSL, "RSLms ERR IND\n"); + /* FIXME: implement this */ + break; + default: + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms message type " + "0x%02x\n", rllh->c.msg_type); + rc = -EINVAL; + break; + } + msgb_free(msg); + return rc; +} + +/* input function that L2 calls when sending messages up to L3 */ +static int layer3_from_layer2(struct msgb *msg, struct osmocom_ms *ms) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + int rc = 0; + + switch (rslh->msg_discr & 0xfe) { + case ABIS_RSL_MDISC_RLL: + rc = rslms_rx_rll(msg, ms); + break; + default: + /* FIXME: implement this */ + LOGP(DRSL, LOGL_NOTICE, "unknown RSLms msg_discr 0x%02x\n", + rslh->msg_discr); + msgb_free(msg); + rc = -EINVAL; + break; + } + + return rc; +} + +int layer3_init(struct osmocom_ms *ms) +{ + return osmol2_register_handler(ms, &layer3_from_layer2); +} diff --git a/src/host/layer23/src/settings.c b/src/host/layer23/src/settings.c new file mode 100644 index 00000000..096b3db7 --- /dev/null +++ b/src/host/layer23/src/settings.c @@ -0,0 +1,87 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <errno.h> +#include <string.h> +#include <osmocore/talloc.h> + +#include <osmocom/logging.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/networks.h> + +int gsm_settings_init(struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + + /* IMEI */ + sprintf(set->imei, "000000000000000"); + sprintf(set->imeisv, "0000000000000000"); + + /* test sim */ + strcpy(set->test_imsi, "001010000000000"); + set->test_rplmn_mcc = set->test_rplmn_mnc = 1; + + return 0; +} + +char *gsm_check_imei(const char *imei, const char *sv) +{ + int i; + + if (!imei || strlen(imei) != 15) + return "IMEI must have 15 digits!"; + + for (i = 0; i < strlen(imei); i++) { + if (imei[i] < '0' || imei[i] > '9') + return "IMEI must have digits 0 to 9 only!"; + } + + if (!sv || strlen(sv) != 1) + return "Software version must have 1 digit!"; + + if (sv[0] < '0' || sv[0] > '9') + return "Software version must have digits 0 to 9 only!"; + + return NULL; +} + +int gsm_random_imei(struct gsm_settings *set) +{ + int digits = set->imei_random; + char rand[16]; + + if (digits <= 0) + return 0; + if (digits > 15) + digits = 15; + + sprintf(rand, "%08ld", random() % 100000000); + sprintf(rand + 8, "%07ld", random() % 10000000); + + strcpy(set->imei + 15 - digits, rand + 15 - digits); + strncpy(set->imeisv, set->imei, 15); + + return 0; +} + + + diff --git a/src/host/layer23/src/subscriber.c b/src/host/layer23/src/subscriber.c new file mode 100644 index 00000000..80ea5a43 --- /dev/null +++ b/src/host/layer23/src/subscriber.c @@ -0,0 +1,319 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <errno.h> +#include <string.h> +#include <osmocore/talloc.h> + +#include <osmocom/logging.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/networks.h> + +void *l23_ctx; + +int gsm_subscr_init(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + + memset(subscr, 0, sizeof(*subscr)); + subscr->ms = ms; + + /* set key invalid */ + subscr->key_seq = 7; + + /* init lists */ + INIT_LLIST_HEAD(&subscr->plmn_list); + INIT_LLIST_HEAD(&subscr->plmn_na); + + return 0; +} + +int gsm_subscr_exit(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct llist_head *lh, *lh2; + + /* flush lists */ + llist_for_each_safe(lh, lh2, &subscr->plmn_list) { + llist_del(lh); + talloc_free(lh); + } + llist_for_each_safe(lh, lh2, &subscr->plmn_na) { + llist_del(lh); + talloc_free(lh); + } + + return 0; +} + +/* Attach test card, no sim must be present */ +int gsm_subscr_testcard(struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *msg; + char *error; + + if (subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card " + "is detached.\n"); + return -EBUSY; + } + + error = gsm_check_imsi(set->test_imsi); + if (error) { + LOGP(DMM, LOGL_ERROR, "%s\n", error); + return -EINVAL; + } + + /* reset subscriber */ + gsm_subscr_exit(ms); + gsm_subscr_init(ms); + + sprintf(subscr->sim_name, "test"); + // TODO: load / save SIM to file system + subscr->sim_valid = 1; + subscr->ustate = GSM_SIM_U2_NOT_UPDATED; + subscr->acc_barr = set->test_barr; /* we may access barred cell */ + subscr->acc_class = 0xffff; /* we have any access class */ + subscr->plmn_valid = set->test_rplmn_valid; + subscr->plmn_mcc = set->test_rplmn_mcc; + subscr->plmn_mnc = set->test_rplmn_mnc; + subscr->always_search_hplmn = set->test_always; + subscr->t6m_hplmn = 1; /* try to find home network every 6 min */ + strcpy(subscr->imsi, set->test_imsi); + + LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s,%s)\n", + ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi), + gsm_imsi_mnc(subscr->imsi)); + + /* insert card */ + msg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ); + if (!msg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, msg); + + return 0; +} + +/* Detach card */ +int gsm_subscr_remove(struct osmocom_ms *ms) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct msgb *msg; + + if (!subscr->sim_valid) { + LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n"); + return -EINVAL; + } + + /* remove card */ + msg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ); + if (!msg) + return -ENOMEM; + gsm48_mmr_downmsg(ms, msg); + + return 0; +} + +static const char *subscr_ustate_names[] = { + "U0_NULL", + "U1_UPDATED", + "U2_NOT_UPDATED", + "U3_ROAMING_NA" +}; + +/* change to new U state */ +void new_sim_ustate(struct gsm_subscriber *subscr, int state) +{ + LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name, + subscr_ustate_names[subscr->ustate], + subscr_ustate_names[state]); + + subscr->ustate = state; +} + +/* del forbidden PLMN */ +int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc) +{ + struct gsm_sub_plmn_na *na; + + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (na->mcc == mcc && na->mnc == mnc) { + LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden " + "PLMNs (mcc=%s, mnc=%s)\n", + gsm_print_mcc(mcc), gsm_print_mnc(mnc)); + llist_del(&na->entry); + talloc_free(na); +#ifdef TODO + update plmn not allowed list on sim +#endif + return 0; + } + } + + return -EINVAL; +} + +/* add forbidden PLMN */ +int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc, uint8_t cause) +{ + struct gsm_sub_plmn_na *na; + + /* don't add Home PLMN */ + if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi)) + return -EINVAL; + + LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs " + "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc)); + na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na); + if (!na) + return -ENOMEM; + na->mcc = mcc; + na->mnc = mnc; + na->cause = cause; + llist_add_tail(&na->entry, &subscr->plmn_na); + +#ifdef TODO + update plmn not allowed list on sim +#endif + + return 0; +} + +/* search forbidden PLMN */ +int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc, + uint16_t mnc) +{ + struct gsm_sub_plmn_na *na; + + llist_for_each_entry(na, &subscr->plmn_na, entry) { + if (na->mcc == mcc && na->mnc == mnc) + return 1; + } + + return 0; +} + +int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms, + void (*print)(void *, const char *, ...), void *priv) +{ + struct gsm_subscriber *subscr = &ms->subscr; + struct gsm_sub_plmn_na *temp; + + print(priv, "MCC |MNC |cause\n"); + print(priv, "-------+-------+-------\n"); + llist_for_each_entry(temp, &subscr->plmn_na, entry) + print(priv, "%s |%s%s |#%d\n", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause); + + return 0; +} + +/* dump subscriber */ +void gsm_subscr_dump(struct gsm_subscriber *subscr, + void (*print)(void *, const char *, ...), void *priv) +{ + int i; + struct gsm_sub_plmn_list *plmn_list; + struct gsm_sub_plmn_na *plmn_na; + + print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name); + + if (!subscr->sim_valid) { + print(priv, " No SIM present.\n"); + return; + } + + print(priv, " IMSI: %s\n", subscr->imsi); + print(priv, " Status: %s IMSI %s", subscr_ustate_names[subscr->ustate], + (subscr->imsi_attached) ? "attached" : "detached"); + if (subscr->tmsi_valid) + print(priv, " TSMI %08x", subscr->tmsi); + if (subscr->lai_valid) + print(priv, " LAI: MCC %s MNC %s LAC 0x%04x (%s, %s)\n", + gsm_print_mcc(subscr->lai_mcc), + gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac, + gsm_get_mcc(subscr->lai_mcc), + gsm_get_mnc(subscr->lai_mcc, subscr->lai_mnc)); + else + print(priv, " LAI: invalid\n"); + if (subscr->key_seq != 7) { + print(priv, " Key: sequence %d "); + for (i = 0; i < sizeof(subscr->key); i++) + print(priv, " %02x", subscr->key[i]); + print(priv, "\n"); + } + if (subscr->plmn_valid) + print(priv, " Current PLMN: MCC %s MNC %s (%s, %s)\n", + gsm_print_mcc(subscr->plmn_mcc), + gsm_print_mnc(subscr->plmn_mnc), + gsm_get_mcc(subscr->plmn_mcc), + gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc)); + print(priv, " Access barred cells: %s\n", + (subscr->acc_barr) ? "yes" : "no"); + print(priv, " Access classes:"); + for (i = 0; i < 16; i++) + if ((subscr->acc_class & (1 << i))) + print(priv, " C%d", i); + print(priv, "\n"); + if (!llist_empty(&subscr->plmn_list)) { + print(priv, " List of preferred PLMNs:\n"); + print(priv, " MCC |MNC\n"); + print(priv, " -------+-------\n"); + llist_for_each_entry(plmn_list, &subscr->plmn_list, entry) + print(priv, " %s |%s\n", + gsm_print_mcc(plmn_list->mcc), + gsm_print_mnc(plmn_list->mnc)); + } + if (!llist_empty(&subscr->plmn_na)) { + print(priv, " List of forbidden PLMNs:\n"); + print(priv, " MCC |MNC |cause\n"); + print(priv, " -------+-------+-------\n"); + llist_for_each_entry(plmn_na, &subscr->plmn_na, entry) + print(priv, " %s |%s%s |#%d\n", + gsm_print_mcc(plmn_na->mcc), + gsm_print_mnc(plmn_na->mnc), + ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"", + plmn_na->cause); + } +} + +char *gsm_check_imsi(const char *imsi) +{ + int i; + + if (!imsi || strlen(imsi) != 15) + return "IMSI must have 15 digits!"; + + for (i = 0; i < strlen(imsi); i++) { + if (imsi[i] < '0' || imsi[i] > '9') + return "IMSI must have digits 0 to 9 only!"; + } + + return NULL; +} + + diff --git a/src/host/layer23/src/support.c b/src/host/layer23/src/support.c new file mode 100644 index 00000000..74476c07 --- /dev/null +++ b/src/host/layer23/src/support.c @@ -0,0 +1,155 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <string.h> + +#include <osmocom/osmocom_data.h> + +void gsm_support_init(struct osmocom_ms *ms) +{ + struct gsm_support *sup = &ms->support; + int i; + + memset(sup, 0, sizeof(*sup)); + sup->ms = ms; + + /* rf power capability */ + sup->pwr_lev_900 = 3; /* CLASS 4: Handheld 2W */ + sup->pwr_lev_1800 = 0; /* CLASS 1: Handheld 1W */ + /* controlled early classmark sending */ + sup->es_ind = 0; /* no */ + /* revision level */ + sup->rev_lev = 1; /* phase 2 mobile station */ + /* support of VGCS */ + sup->vgcs = 0; /* no */ + /* support of VBS */ + sup->vbs = 0; /* no */ + /* support of SMS */ + sup->sms_ptp = 1; /* yes */ + /* screening indicator */ + sup->ss_ind = 1; /* phase 2 error handling */ + /* pseudo synchronised capability */ + sup->ps_cap = 0; /* no */ + /* CM service prompt */ + sup->cmsp = 0; /* no */ + /* solsa support */ + sup->solsa = 0; /* no */ + /* location service support */ + sup->lcsva = 0; /* no */ + sup->loc_serv = 0; /* no */ + /* codec supprot */ + sup->a5_1 = 0; /* currently not */ + sup->a5_2 = 0; + sup->a5_3 = 0; + sup->a5_4 = 0; + sup->a5_5 = 0; + sup->a5_6 = 0; + sup->a5_7 = 0; + /* radio support */ + sup->p_gsm = 1; /* P-GSM only */ + sup->e_gsm = 1; /* E-GSM */ + sup->r_gsm = 1; /* R-GSM */ + sup->r_capa = 0; + sup->low_capa = 4; /* p,e,r power class */ + sup->dcs_1800 = 1; + /* set supported frequencies */ + if (sup->e_gsm || sup->r_gsm) + sup->freq_map[0] |= 1; + if (sup->p_gsm || sup->e_gsm || sup->r_gsm) + for(i = 1; i <= 124; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + if (sup->dcs_1800) + for(i = 512; i <= 885; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + if (sup->e_gsm) + for(i = 975; i <= 1023; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); +// for(i = 978; i <= 978; i++) +// sup->freq_map[i >> 3] |= (1 << (i & 7)); + if (sup->r_gsm) + for(i = 955; i <= 1023; i++) + sup->freq_map[i >> 3] |= (1 << (i & 7)); + sup->dcs_capa = 1; /* dcs power class */ + /* multi slot support */ + sup->ms_sup = 0; /* no */ + /* ucs2 treatment */ + sup->ucs2_treat = 0; /* default */ + /* support extended measurements */ + sup->ext_meas = 0; /* no */ + /* support switched measurement capability */ + sup->meas_cap = 0; /* no */ + //sup->sms_val = ; + //sup->sm_val = ; + + /* radio */ + sup->min_rxlev_db = -100; // TODO + sup->sync_to = 6; /* how long to wait sync (0.9 s) */ + sup->scan_to = 4; /* how long to wait for all sysinfos (>=4 s) */ +} + +/* (3.2.1) maximum channels to scan within each band */ +struct gsm_support_scan_max gsm_sup_smax[] = { + { 259, 293, 15, 0 }, /* GSM 450 */ + { 306, 340, 15, 0 }, /* GSM 480 */ + { 438, 511, 25, 0 }, + { 128, 251, 30, 0 }, + { 955, 124, 30, 0 }, + { 512, 885, 40, 0 }, /* DCS 1800 */ + { 0, 0, 0, 0 } +}; + +/* dump support */ +void gsm_support_dump(struct gsm_support *sup, + void (*print)(void *, const char *, ...), void *priv) +{ + print(priv, "Supported features of MS '%s':\n", sup->ms->name); + if (sup->r_gsm) + print(priv, " R-GSM"); + if (sup->e_gsm || sup->r_gsm) + print(priv, " E-GSM"); + if (sup->p_gsm || sup->p_gsm || sup->r_gsm) + print(priv, " P-GSM"); + if (sup->dcs_1800) + print(priv, " DCS1800"); + print(priv, " (Phase %d mobile station)\n", sup->rev_lev + 1); + print(priv, " CECS : %s\n", (sup->es_ind) ? "yes" : "no"); + print(priv, " VGCS : %s\n", (sup->vgcs) ? "yes" : "no"); + print(priv, " VBS : %s\n", (sup->vbs) ? "yes" : "no"); + print(priv, " SMS : %s\n", (sup->sms_ptp) ? "yes" : "no"); + print(priv, " SS_IND : %s\n", (sup->ss_ind) ? "yes" : "no"); + print(priv, " PS_CAP : %s\n", (sup->ps_cap) ? "yes" : "no"); + print(priv, " CMSP : %s\n", (sup->cmsp) ? "yes" : "no"); + print(priv, " SoLSA : %s\n", (sup->solsa) ? "yes" : "no"); + print(priv, " LCSVA : %s\n", (sup->lcsva) ? "yes" : "no"); + print(priv, " LOC_SERV : %s\n", (sup->loc_serv) ? "yes" : "no"); + print(priv, " A5/1 : %s\n", (sup->a5_1) ? "yes" : "no"); + print(priv, " A5/2 : %s\n", (sup->a5_2) ? "yes" : "no"); + print(priv, " A5/3 : %s\n", (sup->a5_3) ? "yes" : "no"); + print(priv, " A5/4 : %s\n", (sup->a5_4) ? "yes" : "no"); + print(priv, " A5/5 : %s\n", (sup->a5_5) ? "yes" : "no"); + print(priv, " A5/6 : %s\n", (sup->a5_6) ? "yes" : "no"); + print(priv, " A5/7 : %s\n", (sup->a5_7) ? "yes" : "no"); + print(priv, " A5/1 : %s\n", (sup->a5_1) ? "yes" : "no"); + print(priv, " Min RXLEV: %d\n", sup->min_rxlev_db); +} + diff --git a/src/host/layer23/src/sysinfo.c b/src/host/layer23/src/sysinfo.c new file mode 100644 index 00000000..ca62eb68 --- /dev/null +++ b/src/host/layer23/src/sysinfo.c @@ -0,0 +1,178 @@ +/* + * (C) 2010 by 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 <stdint.h> +#include <string.h> + +#include <osmocom/osmocom_data.h> +#include <osmocom/networks.h> + +int gsm48_sysinfo_dump(struct gsm48_sysinfo *s, uint16_t arfcn, + void (*print)(void *, const char *, ...), void *priv) +{ + char buffer[80]; + int i, j; + + /* available sysinfos */ + print(priv, "ARFCN = %d\n", arfcn); + print(priv, "Available SYSTEM INFORMATIONS ="); + if (s->si1) + print(priv, " 1"); + if (s->si2) + print(priv, " 2"); + if (s->si2bis) + print(priv, " 2bis"); + if (s->si2ter) + print(priv, " 2ter"); + if (s->si3) + print(priv, " 3"); + if (s->si4) + print(priv, " 4"); + if (s->si5) + print(priv, " 5"); + if (s->si5bis) + print(priv, " 5bis"); + if (s->si5ter) + print(priv, " 5ter"); + if (s->si6) + print(priv, " 6"); + print(priv, "\n"); + print(priv, "\n"); + + /* frequency map */ + for (i = 0; i < 1024; i += 64) { + if (i < 10) + sprintf(buffer, " %d ", i); + else if (i < 100) + sprintf(buffer, " %d ", i); + else + sprintf(buffer, " %d ", i); + for (j = 0; j < 64; j++) { + if ((s->freq[i+j].mask & FREQ_TYPE_SERV)) + buffer[j + 5] = 'S'; + else if ((s->freq[i+j].mask & FREQ_TYPE_HOPP)) + buffer[j + 5] = 'H'; + else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL_2)) + buffer[j + 5] = 'N'; + else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL_2bis)) + buffer[j + 5] = 'b'; + else if ((s->freq[i+j].mask & FREQ_TYPE_NCELL_2ter)) + buffer[j + 5] = 't'; + else if ((s->freq[i+j].mask & FREQ_TYPE_REP_5)) + buffer[j + 5] = 'R'; + else if ((s->freq[i+j].mask & FREQ_TYPE_REP_5bis)) + buffer[j + 5] = 'b'; + else if ((s->freq[i+j].mask & FREQ_TYPE_REP_5ter)) + buffer[j + 5] = 't'; + else + buffer[j + 5] = '.'; + } + sprintf(buffer + 69, " %d", i + 63); + print(priv, "%s\n", buffer); + } + print(priv, " S = serv. cell H = hopping seq. N,b,t = neigh. cells " + "R,b,t = cells to rep.\n\n"); + + /* serving cell */ + print(priv, "Serving Cell:\n"); + print(priv, " MCC = %03d MNC = %02d LAC = 0x%04x Cell ID = 0x%04x " + "(%s, %s)\n", s->mcc, s->mnc, s->lac, s->cell_id, + gsm_get_mcc(s->mcc), gsm_get_mnc(s->mcc, s->mnc)); + print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n", + s->max_retrans, s->tx_integer, + (s->reest_denied) ? "denied" : "allowed"); + print(priv, " Cell barred = %s barred classes =", + (s->cell_barr ? "yes" : "no")); + for (i = 0; i < 16; i++) { + if ((s->class_barr & (1 << i))) + print(priv, " C%d", i); + } + print(priv, "\n"); + if (s->sp) + print(priv, " CBQ = %d CRO = %d TEMP_OFFSET = %d " + "PENALTY_TIME = %d\n", s->sp_cbq, s->sp_cro, s->sp_to, + s->sp_pt); + print(priv, "\n"); + + /* neighbor cell */ + print(priv, "Neighbor Cell:\n"); + print(priv, " MAX_RETRANS = %d TX_INTEGER = %d re-establish = %s\n", + s->nb_max_retrans, s->nb_tx_integer, + (s->nb_reest_denied) ? "denied" : "allowed"); + print(priv, " Cell barred = %s barred classes =", + (s->nb_cell_barr ? "yes" : "no")); + for (i = 0; i < 16; i++) { + if ((s->nb_class_barr & (1 << i))) + print(priv, " C%d", i); + } + print(priv, "\n"); + print(priv, "\n"); + + /* cell selection */ + print(priv, "MX_TXPWR_MAX_CCCH = %d CRH = %d RXLEV_MIN = %d " + "NECI = %d ACS = %d\n", s->ms_txpwr_max_ccch, + s->cell_resel_hyst_db, s->rxlev_acc_min_db, s->neci, s->acs); + + /* bcch options */ + print(priv, "BCCH link timeout = %d DTX = %d PWRC = %d\n", + s->bcch_radio_link_timeout, s->bcch_dtx, s->bcch_pwrc); + + /* sacch options */ + print(priv, "SACCH link timeout = %d DTX = %d PWRC = %d\n", + s->sacch_radio_link_timeout, s->sacch_dtx, s->sacch_pwrc); + + /* control channel */ + switch(s->ccch_conf) { + case 0: + print(priv, "CCCH Config = 1 CCCH"); + break; + case 1: + print(priv, "CCCH Config = 1 CCCH + SDCCH"); + break; + case 2: + print(priv, "CCCH Config = 2 CCCH"); + break; + case 4: + print(priv, "CCCH Config = 3 CCCH"); + break; + case 6: + print(priv, "CCCH Config = 4 CCCH"); + break; + default: + print(priv, "CCCH Config = reserved"); + } + print(priv, " BS-PA-MFMS = %d Attachment = %s\n", + s->pag_mf_periods, (s->att_allowed) ? "allowed" : "denied"); + print(priv, "BS-AG_BLKS_RES = %d\n", s->bs_ag_blks_res); + + /* channel description */ + if (s->h) + print(priv, "chan_nr = 0x%02x TSC = %d MAIO = %d HSN = %d\n", + s->chan_nr, s->tsc, s->maio, s->hsn); + else + print(priv, "chan_nr = 0x%02x TSC = %d ARFCN = %d\n", + s->chan_nr, s->tsc, s->arfcn); + print(priv, "\n"); + + return 0; +} + diff --git a/src/host/layer23/src/telnet_interface.c b/src/host/layer23/src/telnet_interface.c new file mode 100644 index 00000000..c7b06f72 --- /dev/null +++ b/src/host/layer23/src/telnet_interface.c @@ -0,0 +1,240 @@ +/* minimalistic telnet/ms 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 <osmocom/telnet_interface.h> +#include <osmocore/talloc.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); + +void *l23_ctx; +static void *tall_telnet_ctx; + +/* per ms data */ +static int telnet_new_connection(struct bsc_fd *fd, unsigned int what); + +static struct bsc_fd server_socket = { + .when = BSC_FD_READ, + .cb = telnet_new_connection, + .priv_nr = 0, +}; + +int telnet_init(struct osmocom_ms *ms, int port) { + struct sockaddr_in sock_addr; + int fd, on = 1; + + tall_telnet_ctx = talloc_named_const(l23_ctx, 1, + "telnet_connection"); + + ms_vty_init(ms); + + fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (fd < 0) { + fprintf(stderr, "Telnet interface socket creation failed\n"); + return -1; + } + + 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) { + fprintf(stderr, "Telnet interface failed to bind\n"); + return -1; + } + + if (listen(fd, 0) < 0) { + fprintf(stderr, "Telnet interface failed to listen\n"); + return -1; + } + + server_socket.data = ms; + server_socket.fd = fd; + bsc_register_fd(&server_socket); + + return 0; +} + +static void print_welcome(int fd) { + int ret; + static char *msg = + "Welcome to the Osmocom Control interface\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); + + if (conn->dbg) { +// debug_del_target(conn->dbg); + talloc_free(conn->dbg); + } + + llist_del(&conn->entry); + talloc_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); + } + + /* vty might have been closed from vithin vty_read() */ + if (!conn->vty) + return rc; + + 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) { + fprintf(stderr, "telnet accept failed\n"); + return -1; + } + + + connection = talloc_zero(tall_telnet_ctx, struct telnet_connection); + connection->ms = (struct osmocom_ms*)fd->data; + connection->fd.data = connection; + connection->fd.fd = new_connection; + connection->fd.when = BSC_FD_READ; + connection->fd.cb = client_data; + 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) { + fprintf(stderr, "couldn't create VTY\n"); + 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; + + if (vty->type != VTY_TERM) + return; + + switch (event) { + case VTY_READ: + bfd->when |= BSC_FD_READ; + break; + case VTY_WRITE: + bfd->when |= BSC_FD_WRITE; + break; + case VTY_CLOSED: + /* vty layer is about to free() vty */ + connection->vty = NULL; + telnet_close_client(bfd); + break; + default: + break; + } +} + +void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) +{ + struct telnet_connection *connection; + char buffer[1000]; + va_list args; + struct vty *vty; + + if (fmt) { + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (!buffer[0]) + return; + } + + llist_for_each_entry(connection, &active_connections, entry) { + vty = connection->vty; + if (!vty) + continue; + if (!fmt) { + vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name, + VTY_NEWLINE); + continue; + } + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE); + buffer[strlen(buffer)] = '\n'; + } else + vty_out(vty, "%% %s", buffer); + } +} + diff --git a/src/host/layer23/src/transaction.c b/src/host/layer23/src/transaction.c new file mode 100644 index 00000000..fbce57b6 --- /dev/null +++ b/src/host/layer23/src/transaction.c @@ -0,0 +1,143 @@ +/* GSM 04.07 Transaction handling */ + +/* (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 <stdint.h> + +#include <osmocore/talloc.h> +#include <osmocore/timer.h> +#include <osmocore/msgb.h> + +#include <osmocom/osmocom_data.h> +#include <osmocom/mncc.h> +#include <osmocom/transaction.h> +#include <osmocom/logging.h> + +extern void *l23_ctx; + +void _gsm48_cc_trans_free(struct gsm_trans *trans); + +struct gsm_trans *trans_find_by_id(struct osmocom_ms *ms, + uint8_t proto, uint8_t trans_id) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol == proto && + trans->transaction_id == trans_id) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_find_by_callref(struct osmocom_ms *ms, + uint32_t callref) +{ + struct gsm_trans *trans; + + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->callref == callref) + return trans; + } + return NULL; +} + +struct gsm_trans *trans_alloc(struct osmocom_ms *ms, + uint8_t protocol, uint8_t trans_id, + uint32_t callref) +{ + struct gsm_trans *trans; + + trans = talloc_zero(l23_ctx, struct gsm_trans); + if (!trans) + return NULL; + + DEBUGP(DCC, "ms %s allocetes transaction (proto %d trans_id %d " + "callref %d mem %p)\n", ms->name, protocol, trans_id, callref, + trans); + + trans->ms = ms; + + trans->protocol = protocol; + trans->transaction_id = trans_id; + trans->callref = callref; + + llist_add_tail(&trans->entry, &ms->trans_list); + + return trans; +} + +void trans_free(struct gsm_trans *trans) +{ + switch (trans->protocol) { + case GSM48_PDISC_CC: + _gsm48_cc_trans_free(trans); + break; +#if 0 + case GSM48_PDISC_SS: + _gsm411_ss_trans_free(trans); + break; + case GSM48_PDISC_SMS: + _gsm411_sms_trans_free(trans); + break; +#endif + } + + DEBUGP(DCC, "ms %s frees transaction (mem %p)\n", trans->ms->name, + trans); + + llist_del(&trans->entry); + + talloc_free(trans); +} + +/* allocate an unused transaction ID + * in the given protocol using the ti_flag specified */ +int trans_assign_trans_id(struct osmocom_ms *ms, + uint8_t protocol, uint8_t ti_flag) +{ + struct gsm_trans *trans; + unsigned int used_tid_bitmask = 0; + int i, j, h; + + if (ti_flag) + ti_flag = 0x8; + + /* generate bitmask of already-used TIDs for this (proto) */ + llist_for_each_entry(trans, &ms->trans_list, entry) { + if (trans->protocol != protocol || + trans->transaction_id == 0xff) + continue; + used_tid_bitmask |= (1 << trans->transaction_id); + } + + /* find a new one, trying to go in a 'circular' pattern */ + for (h = 6; h > 0; h--) + if (used_tid_bitmask & (1 << (h | ti_flag))) + break; + for (i = 0; i < 7; i++) { + j = ((h + i) % 7) | ti_flag; + if ((used_tid_bitmask & (1 << j)) == 0) + return j; + } + + return -1; +} + diff --git a/src/host/layer23/src/vty_interface.c b/src/host/layer23/src/vty_interface.c new file mode 100644 index 00000000..7d2a7111 --- /dev/null +++ b/src/host/layer23/src/vty_interface.c @@ -0,0 +1,997 @@ +/* + * (C) 2010 by 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 <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <sys/types.h> + +#include <osmocom/vty.h> +#include <osmocom/vty/telnet_interface.h> + +#include <osmocore/gsm48.h> +#include <osmocom/osmocom_data.h> +#include <osmocom/networks.h> +#include <osmocom/mncc.h> +#include <osmocom/transaction.h> + +int mncc_call(struct osmocom_ms *ms, char *number); +int mncc_hangup(struct osmocom_ms *ms); +int mncc_answer(struct osmocom_ms *ms); +int mncc_hold(struct osmocom_ms *ms); +int mncc_retrieve(struct osmocom_ms *ms, int number); + +extern struct llist_head ms_list; +extern struct llist_head active_connections; + +struct cmd_node ms_node = { + MS_NODE, + "%s(ms)#", + 1 +}; + +struct cmd_node testsim_node = { + TESTSIM_NODE, + "%s(test-sim)#", + 1 +}; + +static void print_vty(void *priv, const char *fmt, ...) +{ + char buffer[1000]; + struct vty *vty = priv; + va_list args; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (buffer[0]) { + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%s%s", buffer, VTY_NEWLINE); + } else + vty_out(vty, "%s", buffer); + } +} + +static struct osmocom_ms *get_ms(const char *name, struct vty *vty) +{ + struct osmocom_ms *ms; + + llist_for_each_entry(ms, &ms_list, entity) { + if (!strcmp(ms->name, name)) + return ms; + } + vty_out(vty, "MS name '%s' does not exits.%s", name, VTY_NEWLINE); + + return NULL; +} + +DEFUN(show_ms, show_ms_cmd, "show ms", + SHOW_STR "Display available MS entities\n") +{ + struct osmocom_ms *ms; + + llist_for_each_entry(ms, &ms_list, entity) { + struct gsm_settings *set = &ms->settings; + + vty_out(vty, "MS NAME: %s%s", ms->name, VTY_NEWLINE); + vty_out(vty, " IMEI: %s%s", set->imei, VTY_NEWLINE); + vty_out(vty, " IMEISV: %s%s", set->imeisv, VTY_NEWLINE); + if (set->imei_random) + vty_out(vty, " IMEI generation: random (%d trailing " + "digits)%s", set->imei_random, VTY_NEWLINE); + else + vty_out(vty, " IMEI generation: fixed%s", VTY_NEWLINE); + vty_out(vty, " network selection mode: %s%s", + (set->plmn_mode == PLMN_MODE_AUTO) + ? "automatic" : "manual", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(show_support, show_support_cmd, "show support [ms_name]", + SHOW_STR "Display information about MS support\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_support_dump(&ms->support, print_vty, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + gsm_support_dump(&ms->support, print_vty, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +static void gsm_states_dump(struct osmocom_ms *ms, struct vty *vty) +{ + struct gsm_trans *trans; + + vty_out(vty, "Current state of MS '%s'%s", ms->name, VTY_NEWLINE); + if (ms->settings.plmn_mode == PLMN_MODE_AUTO) + vty_out(vty, " automatic network selection: %s%s", + plmn_a_state_names[ms->plmn.state], VTY_NEWLINE); + else + vty_out(vty, " manual network selection: %s%s", + plmn_m_state_names[ms->plmn.state], VTY_NEWLINE); + vty_out(vty, " cell selection: %s%s", + cs_state_names[ms->cellsel.state], VTY_NEWLINE); + vty_out(vty, " radio ressource layer: %s%s", + gsm48_rr_state_names[ms->rrlayer.state], VTY_NEWLINE); + vty_out(vty, " mobility management layer: %s", + gsm48_mm_state_names[ms->mmlayer.state]); + if (ms->mmlayer.state == GSM48_MM_ST_MM_IDLE) + vty_out(vty, ", %s", + gsm48_mm_substate_names[ms->mmlayer.substate]); + vty_out(vty, "%s", VTY_NEWLINE); + llist_for_each_entry(trans, &ms->trans_list, entry) { + vty_out(vty, " call control: %s%s", + gsm48_cc_state_name(trans->cc.state), VTY_NEWLINE); + } +} + +DEFUN(show_states, show_states_cmd, "show states [ms_name]", + SHOW_STR "Display current states of given MS\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_states_dump(ms, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + gsm_states_dump(ms, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(show_subscr, show_subscr_cmd, "show subscriber [ms_name]", + SHOW_STR "Display information about subscriber\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + if (argc) { + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + gsm_subscr_dump(&ms->subscr, print_vty, vty); + } else { + llist_for_each_entry(ms, &ms_list, entity) { + gsm_subscr_dump(&ms->subscr, print_vty, vty); + vty_out(vty, "%s", VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +DEFUN(show_cell, show_cell_cmd, "show cell MS_NAME", + SHOW_STR "Display information about received cells\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm322_dump_cs_list(&ms->cellsel, GSM322_CS_FLAG_SYSINFO, print_vty, + vty); + + return CMD_SUCCESS; +} + +DEFUN(show_cell_si, show_cell_si_cmd, "show cell MS_NAME <0-1023>", + SHOW_STR "Display information about received cell\n" + "Name of MS (see \"show ms\")\nRadio frequency number") +{ + struct osmocom_ms *ms; + int i; + struct gsm48_sysinfo *s; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + i = atoi(argv[1]); + if (i < 0 || i > 1023) { + vty_out(vty, "Given ARFCN '%s' not in range (0..1023)%s", + argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + s = ms->cellsel.list[i].sysinfo; + if (!s) { + vty_out(vty, "Given ARFCN '%s' has no sysinfo available%s", + argv[1], VTY_NEWLINE); + return CMD_SUCCESS; + } + + gsm48_sysinfo_dump(s, i, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_ba, show_ba_cmd, "show ba MS_NAME [mcc] [mnc]", + SHOW_STR "Display information about band allocations\n" + "Name of MS (see \"show ms\")\nMobile Country Code\n" + "Mobile Network Code") +{ + struct osmocom_ms *ms; + uint16_t mcc = 0, mnc = 0; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (argc >= 3) { + mcc = gsm_input_mcc((char *)argv[1]); + mnc = gsm_input_mnc((char *)argv[2]); + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + gsm322_dump_ba_list(&ms->cellsel, mcc, mnc, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_forb_plmn, show_forb_plmn_cmd, "show forbidden plmn MS_NAME", + SHOW_STR "Display information about forbidden cells / networks\n" + "Display forbidden PLMNs\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm_subscr_dump_forbidden_plmn(ms, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(show_forb_la, show_forb_la_cmd, "show forbidden location-area MS_NAME", + SHOW_STR "Display information about forbidden cells / networks\n" + "Display forbidden location areas\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + gsm322_dump_forbidden_la(ms, print_vty, vty); + + return CMD_SUCCESS; +} + +DEFUN(insert_test, insert_test_cmd, "insert testcard MS_NAME [mcc] [mnc]", + "Insert ...\nInsert test card\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code") +{ + struct osmocom_ms *ms; + uint16_t mcc = 1, mnc = 1; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (ms->subscr.sim_valid) { + vty_out(vty, "Sim already presend, remove first!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc >= 3) { + mcc = gsm_input_mcc((char *)argv[1]); + mnc = gsm_input_mnc((char *)argv[2]); + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + gsm_subscr_testcard(ms); + + return CMD_SUCCESS; +} + +DEFUN(remove_sim, remove_sim_cmd, "remove sim MS_NAME", + "Remove ...\nRemove SIM card\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + if (!ms->subscr.sim_valid) { + vty_out(vty, "No Sim inserted!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + gsm_subscr_remove(ms); + + return CMD_SUCCESS; +} + +DEFUN(network_select, network_select_cmd, "network select MS_NAME MCC MNC", + "Select ...\nSelect Network\nName of MS (see \"show ms\")\n" + "Mobile Country Code\nMobile Network Code") +{ + struct osmocom_ms *ms; + struct gsm322_plmn *plmn; + struct msgb *nmsg; + struct gsm322_msg *ngm; + struct gsm322_plmn_list *temp; + uint16_t mcc = gsm_input_mcc((char *)argv[1]), + mnc = gsm_input_mnc((char *)argv[2]); + int found = 0; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + plmn = &ms->plmn; + + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + if (temp->mcc == mcc && temp->mnc == mnc) + found = 1; + if (!found) { + vty_out(vty, "Network not in list!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_CHOSE_PLMN); + if (!nmsg) + return CMD_WARNING; + ngm = (struct gsm322_msg *) nmsg->data; + ngm->mcc = mcc; + ngm->mnc = mnc; + gsm322_plmn_sendmsg(ms, nmsg); + + return CMD_SUCCESS; +} + +DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)", + "Make a call\nName of MS (see \"show ms\")\nPhone number to call\n" + "Make an emergency call\nAnswer an incomming call\nHangup a call\n" + "Hold current active call\n") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + switch (argv[1][0]) { + case 'a': + mncc_answer(ms); + break; + case 'h': + if (argv[1][1] == 'a') + mncc_hangup(ms); + else + mncc_hold(ms); + break; + default: + mncc_call(ms, (char *)argv[1]); + } + + return CMD_SUCCESS; +} + +DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [number]", + "Make a call\nName of MS (see \"show ms\")\n" + "Retrieve call on hold\nNumber of call to retrieve") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + mncc_retrieve(ms, (argc > 1) ? atoi(argv[1]) : 0); + + return CMD_SUCCESS; +} + +DEFUN(network_show, network_show_cmd, "network show MS_NAME", + "Network ...\nShow results of network search (again)\n" + "Name of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + struct gsm322_plmn *plmn; + struct gsm322_plmn_list *temp; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + plmn = &ms->plmn; + + if (ms->settings.plmn_mode != PLMN_MODE_AUTO + && plmn->state != GSM322_M3_NOT_ON_PLMN) { + vty_out(vty, "Start network search first!%s", VTY_NEWLINE); + return CMD_WARNING; + } + + llist_for_each_entry(temp, &plmn->sorted_plmn, entry) + vty_out(vty, " Network %s, %s (%s, %s)%s", + gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc), + gsm_get_mcc(temp->mcc), + gsm_get_mnc(temp->mcc, temp->mnc), VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(network_search, network_search_cmd, "network search MS_NAME", + "Network ...\nTrigger network search\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + struct msgb *nmsg; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + nmsg = gsm322_msgb_alloc(GSM322_EVENT_USER_RESEL); + if (!nmsg) + return CMD_WARNING; + gsm322_plmn_sendmsg(ms, nmsg); + + return CMD_SUCCESS; +} + +/* per MS config */ +DEFUN(cfg_ms, cfg_ms_cmd, "ms MS_NAME", + "Select a mobile station to configure\nName of MS (see \"show ms\")") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + vty->index = ms; + vty->node = MS_NODE; + + return CMD_SUCCESS; +} + +static void config_write_ms_single(struct vty *vty, struct osmocom_ms *ms) +{ + struct gsm_settings *set = &ms->settings; + + vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE); + switch(ms->settings.simtype) { + case GSM_SIM_TYPE_NONE: + vty_out(vty, " sim none%s", VTY_NEWLINE); + break; + case GSM_SIM_TYPE_SLOT: + vty_out(vty, " sim slot%s", VTY_NEWLINE); + break; + case GSM_SIM_TYPE_TEST: + vty_out(vty, " sim test%s", VTY_NEWLINE); + break; + } + vty_out(vty, " network-selection-mode %s%s", (set->plmn_mode + == PLMN_MODE_AUTO) ? "auto" : "manual", VTY_NEWLINE); + vty_out(vty, " imei %s %s%s", set->imei, + set->imeisv + strlen(set->imei), VTY_NEWLINE); + if (set->imei_random) + vty_out(vty, " imei-random %d%s", set->imei_random, + VTY_NEWLINE); + else + vty_out(vty, " imei-fixed%s", VTY_NEWLINE); + vty_out(vty, " emergency-imsi %s%s", (ms->settings.emergency_imsi[0]) ? + ms->settings.emergency_imsi : "none", VTY_NEWLINE); + vty_out(vty, " %scall-waiting%s", (set->cw) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " %sclip%s", (set->clip) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " %sclir%s", (set->clir) ? "" : "no ", VTY_NEWLINE); + vty_out(vty, " test-sim%s", VTY_NEWLINE); + vty_out(vty, " imsi %s%s", ms->settings.test_imsi, VTY_NEWLINE); + vty_out(vty, " %sbarred-access%s", (set->test_barr) ? "" : "no ", + VTY_NEWLINE); + if (ms->settings.test_rplmn_valid) + vty_out(vty, " rplmn %s %s%s", + gsm_print_mcc(ms->settings.test_rplmn_mcc), + gsm_print_mnc(ms->settings.test_rplmn_mnc), + VTY_NEWLINE); + else + vty_out(vty, " no rplmn%s", VTY_NEWLINE); + vty_out(vty, " hplmn-search %s%s", (set->test_always) ? "everywhere" + : "foreign-country", VTY_NEWLINE); + vty_out(vty, " exit%s", VTY_NEWLINE); + vty_out(vty, "exit%s", VTY_NEWLINE); + vty_out(vty, "!%s", VTY_NEWLINE); +} + +static int config_write_ms(struct vty *vty) +{ + struct osmocom_ms *ms; + + llist_for_each_entry(ms, &ms_list, entity) + config_write_ms_single(vty, ms); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_mode, cfg_ms_mode_cmd, "network-selection-mode (auto|manual)", + "Set network selection mode\nAutomatic network selection\n" + "Manual network selection") +{ + struct osmocom_ms *ms = vty->index; + struct msgb *nmsg; + + if (!ms->plmn.state) { + if (argv[0][0] == 'a') + ms->settings.plmn_mode = PLMN_MODE_AUTO; + else + ms->settings.plmn_mode = PLMN_MODE_MANUAL; + + return CMD_SUCCESS; + } + if (argv[0][0] == 'a') + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_AUTO); + else + nmsg = gsm322_msgb_alloc(GSM322_EVENT_SEL_MANUAL); + if (!nmsg) + return CMD_WARNING; + gsm322_plmn_sendmsg(ms, nmsg); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei, cfg_ms_imei_cmd, "imei IMEI [SV]", + "Set IMEI (enter without control digit)\n15 Digits IMEI\n" + "Software version digit") +{ + struct osmocom_ms *ms = vty->index; + char *error, *sv = "0"; + + if (argc >= 2) + sv = (char *)argv[1]; + + error = gsm_check_imei(argv[0], sv); + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + + strcpy(ms->settings.imei, argv[0]); + strcpy(ms->settings.imeisv, argv[0]); + strcpy(ms->settings.imeisv + 15, sv); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei_fixed, cfg_ms_imei_fixed_cmd, "imei-fixed", + "Use fixed IMEI on every power on") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.imei_random = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_imei_random, cfg_ms_imei_random_cmd, "imei-random <0-15>", + "Use random IMEI on every power on\n" + "Number of trailing digits to randomize") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.imei_random = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_emerg_imsi, cfg_ms_emerg_imsi_cmd, "emergency-imsi (none|IMSI)", + "Use IMSI for emergency calls\n" + "Use IMSI of SIM or IMEI for emergency calls\n15 digits IMSI") +{ + struct osmocom_ms *ms = vty->index; + char *error; + + if (argv[0][0] == 'n') { + ms->settings.emergency_imsi[0] = '\0'; + return CMD_SUCCESS; + } + + error = gsm_check_imsi(argv[0]); + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + strcpy(ms->settings.emergency_imsi, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_cw, cfg_ms_no_cw_cmd, "no call-waiting", + NO_STR "Disallow waiting calls") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.cw = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_cw, cfg_ms_cw_cmd, "call-waiting", + "Allow waiting calls") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.cw = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_clip, cfg_ms_clip_cmd, "clip", + "Force caller ID presentation") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.clip = 1; + ms->settings.clir = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_clir, cfg_ms_clir_cmd, "clir", + "Force caller ID restriction") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.clip = 0; + ms->settings.clir = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_clip, cfg_ms_no_clip_cmd, "no clip", + NO_STR "Disable forcing of caller ID presentation") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.clip = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_clir, cfg_ms_no_clir_cmd, "no clir", + NO_STR "Disable forcing of caller ID restriction") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.clir = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ms_sim, cfg_ms_sim_cmd, "sim (none|test)", + "Set sim card type when powering on\nNo sim interted\n" + "Test sim inserted") +{ + struct osmocom_ms *ms = vty->index; + + switch (argv[0][0]) { + case 'n': + ms->settings.simtype = GSM_SIM_TYPE_NONE; + break; + case 's': + ms->settings.simtype = GSM_SIM_TYPE_SLOT; + break; + case 't': + ms->settings.simtype = GSM_SIM_TYPE_TEST; + break; + } + + return CMD_SUCCESS; +} + +/* per MS config */ +DEFUN(cfg_testsim, cfg_testsim_cmd, "test-sim", + "Configure test SIM emulation") +{ + vty->node = TESTSIM_NODE; + + return CMD_SUCCESS; +} + +static int config_write_dummy(struct vty *vty) +{ + return CMD_SUCCESS; +} + +DEFUN(cfg_test_imsi, cfg_test_imsi_cmd, "imsi IMSI", + "Set IMSI on test card\n15 digits IMSI") +{ + struct osmocom_ms *ms = vty->index; + char *error = gsm_check_imsi(argv[0]); + + if (error) { + vty_out(vty, "%s%s", error, VTY_NEWLINE); + return CMD_WARNING; + } + + strcpy(ms->settings.test_imsi, argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_barr, cfg_test_barr_cmd, "barred-access", + "Allow access to barred cells") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.test_barr = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_no_barr, cfg_test_no_barr_cmd, "no barred-access", + NO_STR "Deny access to barred cells") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.test_barr = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_no_rplmn, cfg_test_no_rplmn_cmd, "no rplmn", + NO_STR "Unset Registered PLMN") +{ + struct osmocom_ms *ms = vty->index; + + ms->settings.test_rplmn_valid = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_rplmn, cfg_test_rplmn_cmd, "rplmn MCC MNC", + "Set Registered PLMN\nMobile Country Code\nMobile Network Code") +{ + struct osmocom_ms *ms = vty->index; + uint16_t mcc = gsm_input_mcc((char *)argv[0]), + mnc = gsm_input_mnc((char *)argv[1]); + + if (!mcc) { + vty_out(vty, "Given MCC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!mnc) { + vty_out(vty, "Given MNC invalid%s", VTY_NEWLINE); + return CMD_WARNING; + } + ms->settings.test_rplmn_valid = 1; + ms->settings.test_rplmn_mcc = mcc; + ms->settings.test_rplmn_mnc = mnc; + + return CMD_SUCCESS; +} + +DEFUN(cfg_test_hplmn, cfg_test_hplmn_cmd, "hplmn-search (everywhere|foreign-country)", + "Set Home PLMN search mode\n" + "Search for HPLMN when on any other network\n" + "Search for HPLMN when in a different country") +{ + struct osmocom_ms *ms = vty->index; + + switch (argv[0][0]) { + case 'e': + ms->settings.test_always = 1; + break; + case 'f': + ms->settings.test_always = 0; + break; + } + + return CMD_SUCCESS; +} + +enum node_type ms_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case MS_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case TESTSIM_NODE: + vty->node = MS_NODE; + break; + default: + vty->node = CONFIG_NODE; + } + + return vty->node; +} + +/* Down vty node level. */ +gDEFUN(ournode_exit, + ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + switch (vty->node) { + case MS_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case TESTSIM_NODE: + vty->node = MS_NODE; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* End of configuration. */ +gDEFUN(ournode_end, + ournode_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 VTY_NODE: + case MS_NODE: + case TESTSIM_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; + default: + break; + } + return CMD_SUCCESS; +} + +int ms_vty_init(void) +{ + install_element_ve(&show_ms_cmd); + install_element_ve(&show_subscr_cmd); + install_element_ve(&show_support_cmd); + install_element_ve(&show_states_cmd); + install_element_ve(&show_cell_cmd); + install_element_ve(&show_cell_si_cmd); + install_element_ve(&show_ba_cmd); + install_element_ve(&show_forb_la_cmd); + install_element_ve(&show_forb_plmn_cmd); + + install_element(ENABLE_NODE, &insert_test_cmd); + install_element(ENABLE_NODE, &remove_sim_cmd); + install_element(ENABLE_NODE, &network_search_cmd); + install_element(ENABLE_NODE, &network_show_cmd); + install_element(ENABLE_NODE, &network_select_cmd); + install_element(ENABLE_NODE, &call_cmd); + install_element(ENABLE_NODE, &call_retr_cmd); + + install_element(CONFIG_NODE, &cfg_ms_cmd); + install_element(CONFIG_NODE, &ournode_end_cmd); + install_node(&ms_node, config_write_ms); + install_default(MS_NODE); + install_element(MS_NODE, &ournode_exit_cmd); + install_element(MS_NODE, &ournode_end_cmd); + install_element(MS_NODE, &cfg_ms_mode_cmd); + install_element(MS_NODE, &cfg_ms_imei_cmd); + install_element(MS_NODE, &cfg_ms_imei_fixed_cmd); + install_element(MS_NODE, &cfg_ms_imei_random_cmd); + install_element(MS_NODE, &cfg_ms_emerg_imsi_cmd); + install_element(MS_NODE, &cfg_ms_cw_cmd); + install_element(MS_NODE, &cfg_ms_no_cw_cmd); + install_element(MS_NODE, &cfg_ms_clip_cmd); + install_element(MS_NODE, &cfg_ms_clir_cmd); + install_element(MS_NODE, &cfg_ms_no_clip_cmd); + install_element(MS_NODE, &cfg_ms_no_clir_cmd); + install_element(MS_NODE, &cfg_ms_sim_cmd); + + install_element(MS_NODE, &cfg_testsim_cmd); + install_node(&testsim_node, config_write_dummy); + install_default(TESTSIM_NODE); + install_element(TESTSIM_NODE, &ournode_exit_cmd); + install_element(TESTSIM_NODE, &ournode_end_cmd); + install_element(TESTSIM_NODE, &cfg_test_imsi_cmd); + install_element(TESTSIM_NODE, &cfg_test_barr_cmd); + install_element(TESTSIM_NODE, &cfg_test_no_barr_cmd); + install_element(TESTSIM_NODE, &cfg_test_no_rplmn_cmd); + install_element(TESTSIM_NODE, &cfg_test_rplmn_cmd); + install_element(TESTSIM_NODE, &cfg_test_hplmn_cmd); + + return 0; +} + +void vty_notify(struct osmocom_ms *ms, const char *fmt, ...) +{ + struct telnet_connection *connection; + char buffer[1000]; + va_list args; + struct vty *vty; + + if (fmt) { + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + buffer[sizeof(buffer) - 1] = '\0'; + va_end(args); + + if (!buffer[0]) + return; + } + + llist_for_each_entry(connection, &active_connections, entry) { + vty = connection->vty; + if (!vty) + continue; + if (!fmt) { + vty_out(vty, "%s%% (MS %s)%s", VTY_NEWLINE, ms->name, + VTY_NEWLINE); + continue; + } + if (buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + vty_out(vty, "%% %s%s", buffer, VTY_NEWLINE); + buffer[strlen(buffer)] = '\n'; + } else + vty_out(vty, "%% %s", buffer); + } +} + diff --git a/src/host/osmocon/.gitignore b/src/host/osmocon/.gitignore new file mode 100644 index 00000000..7c58aae3 --- /dev/null +++ b/src/host/osmocon/.gitignore @@ -0,0 +1,10 @@ +*.o +osmocon +*.id* +*.nam +*.til +*.dump +*.bin +*.log +.version +.tarball-version diff --git a/src/host/osmocon/COPYING b/src/host/osmocon/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/host/osmocon/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/src/host/osmocon/Makefile.am b/src/host/osmocon/Makefile.am new file mode 100644 index 00000000..8b0d4bf3 --- /dev/null +++ b/src/host/osmocon/Makefile.am @@ -0,0 +1,21 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 + +# versioning magic +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) + +sbin_PROGRAMS = osmocon osmoload + +# FIXME: sercomm needs to move into libosmocore or another shared lib +INCLUDES += -I../../target/firmware/include/comm -I../../target/firmware/apps -DHOST_BUILD +osmocon_SOURCES = osmocon.c tpu_debug.c ../../target/firmware/comm/sercomm.c +osmocon_LDADD = $(LIBOSMOCORE_LIBS) + +osmoload_SOURCE = osmoload.c ../../target/firmware/comm/sercomm.c +osmoload_LDADD = $(LIBOSMOCORE_LIBS) diff --git a/src/host/osmocon/configure.ac b/src/host/osmocon/configure.ac new file mode 100644 index 00000000..41308003 --- /dev/null +++ b/src/host/osmocon/configure.ac @@ -0,0 +1,25 @@ +dnl Process this file with autoconf to produce a configure script +AC_INIT([osmocon], + m4_esyscmd([./git-version-gen .tarball-version]), + [baseband-devel@lists.osmocom.org]) + +AM_INIT_AUTOMAKE([dist-bzip2]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL + +dnl checks for libraries +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore) + +dnl checks for header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures and compiler characteristics + +AC_OUTPUT( + Makefile) diff --git a/src/host/osmocon/git-version-gen b/src/host/osmocon/git-version-gen new file mode 100755 index 00000000..652fac68 --- /dev/null +++ b/src/host/osmocon/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='osmocon_v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + osmocon_[0-9]*) ;; + osmocon_v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/;s/^osmocon_//'`; +else + v="UNKNOWN" +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/host/osmocon/memdump_convert.pl b/src/host/osmocon/memdump_convert.pl new file mode 100755 index 00000000..3d18a0b3 --- /dev/null +++ b/src/host/osmocon/memdump_convert.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +my $num_line = 0; +my $num_hex = 0; +my $oldaddr; + +while (my $line = <STDIN>) { + chomp($line); + $num_line++; + my (@hex) = $line =~ /(\w{8}): (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8}) (\w{8})/; + my $addr = hex(shift @hex); + if ($addr != 0 && $addr != $oldaddr + 0x20) { + printf(STDERR "gap of %u between 0x%08x and 0x%08x\n%s\n", + $addr - $oldaddr, $addr, $oldaddr, $line); + } + foreach my $h (@hex) { + $num_hex++; + # poor mans endian conversion + my ($a, $b, $c, $d) = $h =~/(\w\w)(\w\w)(\w\w)(\w\w)/; + my $h_reorder = $d . $c . $b . $a; + # convert into actual binary number + my $tmp = pack('H8', $h_reorder); + syswrite(STDOUT, $tmp, 4); + } + $oldaddr = $addr; +} + +printf(STDERR "num lines/num hex: %u/%u\n", $num_line, $num_hex); + diff --git a/src/host/osmocon/osmocon.c b/src/host/osmocon/osmocon.c new file mode 100644 index 00000000..1f4d9ca8 --- /dev/null +++ b/src/host/osmocon/osmocon.c @@ -0,0 +1,1507 @@ +/* osmocon */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by Steve Markgraf <steve@steve-m.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 <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <fcntl.h> +#include <errno.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> + +#include <sercomm.h> + +#include <osmocore/linuxlist.h> +#include <osmocore/select.h> +#include <osmocore/talloc.h> +#include <osmocore/timer.h> + +#include <arpa/inet.h> + +#define MODEM_BAUDRATE B115200 +#define MAX_DNLOAD_SIZE 0xFFFF +#define MAX_HDR_SIZE 128 +#define MAGIC_OFFSET 0x3be2 + +#define BEACON_INTERVAL 50000 +#define ROMLOAD_INIT_BAUDRATE B19200 +#define ROMLOAD_DL_BAUDRATE B115200 +#define ROMLOAD_BLOCK_HDR_LEN 10 +#define ROMLOAD_ADDRESS 0x820000 + +#define MTK_INIT_BAUDRATE B19200 +#define MTK_ADDRESS 0x40001400 +#define MTK_BLOCK_SIZE 1024 + +struct tool_server *tool_server_for_dlci[256]; + +/** + * a connection from some other tool + */ +struct tool_connection { + struct tool_server *server; + struct llist_head entry; + struct bsc_fd fd; +}; + +/** + * server for a tool + */ +struct tool_server { + struct bsc_fd bfd; + uint8_t dlci; + struct llist_head connections; +}; + + +enum dnload_state { + WAITING_PROMPT1, + WAITING_PROMPT2, + DOWNLOADING, +}; + +enum romload_state { + WAITING_IDENTIFICATION, + WAITING_PARAM_ACK, + SENDING_BLOCKS, + SENDING_LAST_BLOCK, + LAST_BLOCK_SENT, + WAITING_BLOCK_ACK, + WAITING_CHECKSUM_ACK, + WAITING_BRANCH_ACK, + FINISHED, +}; + +enum mtk_state { + MTK_INIT_1, + MTK_INIT_2, + MTK_INIT_3, + MTK_INIT_4, + MTK_WAIT_WRITE_ACK, + MTK_WAIT_ADDR_ACK, + MTK_WAIT_SIZE_ACK, + MTK_SENDING_BLOCKS, + MTK_WAIT_BRANCH_CMD_ACK, + MTK_WAIT_BRANCH_ADDR_ACK, + MTK_FINISHED, +}; + +enum dnload_mode { + MODE_C123, + MODE_C123xor, + MODE_C140, + MODE_C140xor, + MODE_C155, + MODE_ROMLOAD, + MODE_MTK, +}; + +struct dnload { + enum dnload_state state; + enum romload_state romload_state; + enum mtk_state mtk_state; + enum dnload_mode mode; + struct bsc_fd serial_fd; + char *filename; + + int print_hdlc; + + /* data to be downloaded */ + uint8_t *data; + int data_len; + + uint8_t *write_ptr; + + /* romload: block to be downloaded */ + uint8_t *block; + int block_len; + uint8_t block_number; + uint16_t block_payload_size; + int romload_dl_checksum; + uint8_t *block_ptr; + uint8_t load_address[4]; + + uint8_t mtk_send_size[4]; + int block_count; + int echo_bytecount; + + struct tool_server layer2_server; + struct tool_server loader_server; +}; + + +static struct dnload dnload; +static struct timer_list tick_timer; + +/* Compal ramloader specific */ +static const uint8_t phone_prompt1[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x01, 0x40 }; +static const uint8_t dnload_cmd[] = { 0x1b, 0xf6, 0x02, 0x00, 0x52, 0x01, 0x53 }; +static const uint8_t phone_prompt2[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x02, 0x43 }; +static const uint8_t phone_ack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 }; +static const uint8_t phone_nack_magic[]= { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x57 }; +static const uint8_t phone_nack[] = { 0x1b, 0xf6, 0x02, 0x00, 0x45, 0x53, 0x16 }; +static const uint8_t ftmtool[] = { 0x66, 0x74, 0x6d, 0x74, 0x6f, 0x6f, 0x6c }; +static const uint8_t phone_magic[] = { 0x31, 0x30, 0x30, 0x33 }; /* "1003" */ + +/* The C123 has a hard-coded check inside the ramloader that requires the + * following bytes to be always the first four bytes of the image */ +static const uint8_t data_hdr_c123[] = { 0xee, 0x4c, 0x9f, 0x63 }; + +/* The C155 doesn't have some strange restriction on what the first four bytes + * have to be, but it starts the ramloader in THUMB mode. We use the following + * four bytes to switch back to ARM mode: + 800100: 4778 bx pc + 800102: 46c0 nop ; (mov r8, r8) + */ +static const uint8_t data_hdr_c155[] = { 0x78, 0x47, 0xc0, 0x46 }; + +/* Calypso romloader specific */ +static const uint8_t romload_ident_cmd[] = { 0x3c, 0x69 }; /* <i */ +static const uint8_t romload_abort_cmd[] = { 0x3c, 0x61 }; /* <a */ +static const uint8_t romload_write_cmd[] = { 0x3c, 0x77 }; /* <w */ +static const uint8_t romload_checksum_cmd[] = { 0x3c, 0x63 }; /* <c */ +static const uint8_t romload_branch_cmd[] = { 0x3c, 0x62 }; /* <b */ +static const uint8_t romload_ident_ack[] = { 0x3e, 0x69 }; /* >i */ +static const uint8_t romload_param_ack[] = { 0x3e, 0x70 }; /* >p */ +static const uint8_t romload_param_nack[] = { 0x3e, 0x50 }; /* >P */ +static const uint8_t romload_block_ack[] = { 0x3e, 0x77 }; /* >w */ +static const uint8_t romload_block_nack[] = { 0x3e, 0x57 }; /* >W */ +static const uint8_t romload_checksum_ack[] = { 0x3e, 0x63 }; /* >c */ +static const uint8_t romload_checksum_nack[] = { 0x3e, 0x43 }; /* >C */ +static const uint8_t romload_branch_ack[] = { 0x3e, 0x62 }; /* >b */ +static const uint8_t romload_branch_nack[] = { 0x3e, 0x42 }; /* >B */ + +/* romload_param: {"<p", uint8_t baudrate, uint8_t dpll, uint16_t memory_config, + * uint8_t strobe_af, uint32_t uart_timeout} */ + +static const uint8_t romload_param[] = { 0x3c, 0x70, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* MTK romloader specific */ +static const uint8_t mtk_init_cmd[] = { 0xa0, 0x0a, 0x50, 0x05 }; +static const uint8_t mtk_init_resp[] = { 0x5f, 0xf5, 0xaf, 0xfa }; +static const uint8_t mtk_command[] = { 0xa1, 0xa2, 0xa4, 0xa8 }; + +/* FIXME: this routine is more or less what openbsc/src/rs232:rs232_setup() + * does, we should move it to libosmocore at some point */ +static int serial_init(const char *serial_port) +{ + int rc, serial_fd, v24; + struct termios tio; + + serial_fd = open(serial_port, O_RDWR | O_NOCTTY | O_NDELAY); + if (serial_fd < 0) { + perror("cannot open serial port"); + return serial_fd; + } + + //fcntl(serial_fd, F_SETFL, 0); + + /* Configure serial interface */ + rc = tcgetattr(serial_fd, &tio); + if (rc < 0) { + perror("tcgetattr()"); + return rc; + } + cfsetispeed(&tio, MODEM_BAUDRATE); + cfsetospeed(&tio, MODEM_BAUDRATE); + tio.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS); + tio.c_cflag |= (CREAD | CLOCAL | CS8); + 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 | ONLCR); + rc = tcsetattr(serial_fd, TCSANOW, &tio); + if (rc < 0) { + perror("tcsetattr()"); + return rc; + } + + /* set ready to read/write */ + v24 = TIOCM_DTR | TIOCM_RTS; + rc = ioctl(serial_fd, TIOCMBIS, &v24); + if (rc < 0) { + perror("ioctl(TIOCMBIS)"); + return rc; + } + + return serial_fd; +} + +static int serial_set_baudrate(speed_t baudrate) +{ + int rc; + struct termios tio; + + rc = tcgetattr(dnload.serial_fd.fd, &tio); + if (rc < 0) { + perror("tcgetattr()"); + return rc; + } + cfsetispeed(&tio, baudrate); + cfsetospeed(&tio, baudrate); + + rc = tcsetattr(dnload.serial_fd.fd, TCSANOW, &tio); + return rc; +} + +static void beacon_timer_cb(void *p) +{ + int rc; + + if (dnload.romload_state == WAITING_IDENTIFICATION) { + printf("Sending Calypso romloader beacon...\n"); + rc = write(dnload.serial_fd.fd, romload_ident_cmd, + sizeof(romload_ident_cmd)); + + if (!(rc == sizeof(romload_ident_cmd))) + printf("Error sending identification beacon\n"); + + bsc_schedule_timer(p, 0, BEACON_INTERVAL); + } +} + +static void mtk_timer_cb(void *p) +{ + int rc; + + if (dnload.mtk_state == MTK_INIT_1) { + printf("Sending MTK romloader beacon...\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[0], 1); + + if (!(rc == 1)) + printf("Error sending identification beacon\n"); + + bsc_schedule_timer(p, 0, BEACON_INTERVAL); + } +} + +/* Read the to-be-downloaded file, prepend header and length, append XOR sum */ +int read_file(const char *filename) +{ + int fd, rc, i; + struct stat st; + const uint8_t *hdr = NULL; + int payload_size; + int hdr_len = 0; + uint8_t *file_data; + uint16_t tot_len; + uint8_t nibble; + uint8_t running_xor = 0x02; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("opening file"); + exit(1); + } + + rc = fstat(fd, &st); + if (st.st_size > MAX_DNLOAD_SIZE) { + fprintf(stderr, "The maximum file size is 64kBytes (%u bytes)\n", + MAX_DNLOAD_SIZE); + return -EFBIG; + } + + if (dnload.data) { + free(dnload.data); + dnload.data = NULL; + } + + if (dnload.mode == MODE_C140 || dnload.mode == MODE_C140xor) { + if (st.st_size < (MAGIC_OFFSET + sizeof(phone_magic))) + payload_size = MAGIC_OFFSET + sizeof(phone_magic); + else { + printf("\nThe filesize is larger than 15kb, code on " + "the magic address will be overwritten!\nUse " + "loader.bin and upload the application with " + "osmoload instead!\n\n"); + payload_size = st.st_size; + } + } else + payload_size = st.st_size; + + dnload.data = malloc(MAX_HDR_SIZE + payload_size); + + if (!dnload.data) { + close(fd); + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + + /* copy in the header, if any */ + switch (dnload.mode) { + case MODE_C155: + hdr = data_hdr_c155; + hdr_len = sizeof(data_hdr_c155); + break; + case MODE_C140: + case MODE_C140xor: + case MODE_C123: + case MODE_C123xor: + hdr = data_hdr_c123; + hdr_len = sizeof(data_hdr_c123); + break; + case MODE_ROMLOAD: + break; + default: + break; + } + + if (hdr && hdr_len) + memcpy(dnload.data, hdr, hdr_len); + + /* 2 bytes for length + header */ + file_data = dnload.data + 2 + hdr_len; + + /* write the length, keep running XOR */ + tot_len = hdr_len + payload_size; + nibble = tot_len >> 8; + dnload.data[0] = nibble; + running_xor ^= nibble; + nibble = tot_len & 0xff; + dnload.data[1] = nibble; + running_xor ^= nibble; + + if (hdr_len && hdr) { + memcpy(dnload.data+2, hdr, hdr_len); + + for (i = 0; i < hdr_len; i++) + running_xor ^= hdr[i]; + } + + rc = read(fd, file_data, st.st_size); + if (rc < 0) { + perror("error reading file\n"); + free(dnload.data); + dnload.data = NULL; + close(fd); + return -EIO; + } + if (rc < st.st_size) { + free(dnload.data); + dnload.data = NULL; + close(fd); + fprintf(stderr, "Short read of file (%d < %d)\n", + rc, (int)st.st_size); + return -EIO; + } + + close(fd); + + dnload.data_len = (file_data+payload_size) - dnload.data; + + /* fill memory between data end and magic, add magic */ + if(dnload.mode == MODE_C140 || dnload.mode == MODE_C140xor) { + if (st.st_size < MAGIC_OFFSET) + memset(file_data + st.st_size, 0x00, + payload_size - st.st_size); + memcpy(dnload.data + MAGIC_OFFSET, phone_magic, + sizeof(phone_magic)); + } + + /* calculate XOR sum */ + for (i = 0; i < payload_size; i++) + running_xor ^= file_data[i]; + + dnload.data[dnload.data_len++] = running_xor; + + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + + printf("read_file(%s): file_size=%u, hdr_len=%u, dnload_len=%u\n", + filename, (int)st.st_size, hdr_len, dnload.data_len); + + return 0; +} + +static void hexdump(const uint8_t *data, unsigned int len) +{ + int n; + + for (n=0; n < len; n++) + printf("%02x ", data[n]); + printf(" "); + for (n=0; n < len; n++) + if (isprint(data[n])) + putchar(data[n]); + else + putchar('.'); + printf("\n"); +} + +static int romload_prepare_block(void) +{ + int rc, i; + + int block_checksum = 5; + int remaining_bytes; + int fill_bytes; + uint8_t *block_data; + uint32_t block_address; + + dnload.block_len = ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size; + + /* if first block, allocate memory */ + if (!dnload.block_number) { + dnload.block = malloc(dnload.block_len); + if (!dnload.block) { + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + dnload.romload_dl_checksum = 0; + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + } + + block_address = ROMLOAD_ADDRESS + + (dnload.block_number * dnload.block_payload_size); + + /* prepare our block header (10 bytes) */ + memcpy(dnload.block, romload_write_cmd, sizeof(romload_write_cmd)); + dnload.block[2] = 0x01; /* block index */ + /* should normally be the block number, but hangs when sending !0x01 */ + dnload.block[3] = 0x01; /* dnload.block_number+1 */ + dnload.block[4] = (dnload.block_payload_size >> 8) & 0xff; + dnload.block[5] = dnload.block_payload_size & 0xff; + dnload.block[6] = (block_address >> 24) & 0xff; + dnload.block[7] = (block_address >> 16) & 0xff; + dnload.block[8] = (block_address >> 8) & 0xff; + dnload.block[9] = block_address & 0xff; + + block_data = dnload.block + ROMLOAD_BLOCK_HDR_LEN; + dnload.write_ptr = dnload.data + 2 + + (dnload.block_payload_size * dnload.block_number); + + remaining_bytes = dnload.data_len - 3 - + (dnload.block_payload_size * dnload.block_number); + + memcpy(block_data, dnload.write_ptr, dnload.block_payload_size); + + if (remaining_bytes <= dnload.block_payload_size) { + fill_bytes = (dnload.block_payload_size - remaining_bytes); + printf("Preparing the last block, filling %i bytes,", + fill_bytes); + memset(block_data + remaining_bytes, 0x00, fill_bytes); + dnload.romload_state = SENDING_LAST_BLOCK; + } else { + dnload.romload_state = SENDING_BLOCKS; + printf("Preparing block %i,", dnload.block_number+1); + } + + /* block checksum is lsb of ~(5 + block_size_lsb + all bytes of + * block_address + all data bytes) */ + for (i = 5; i < ROMLOAD_BLOCK_HDR_LEN + dnload.block_payload_size; i++) + block_checksum += dnload.block[i]; + + /* checksum is lsb of ~(sum of LSBs of all block checksums) */ + printf(" block checksum is 0x%02x \n", ~(block_checksum) & 0xff); + dnload.romload_dl_checksum += ~(block_checksum) & 0xff; + + /* initialize block pointer to start of block */ + dnload.block_ptr = dnload.block; + + dnload.block_number++; + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + return rc; +} + +static int mtk_prepare_block(void) +{ + int rc, i; + + int remaining_bytes; + int fill_bytes; + uint8_t *block_data; + uint8_t tmp_byteswap; + uint32_t tmp_size; + + dnload.block_len = MTK_BLOCK_SIZE; + dnload.echo_bytecount = 0; + + /* if first block, allocate memory */ + if (!dnload.block_number) { + dnload.block = malloc(dnload.block_len); + if (!dnload.block) { + fprintf(stderr, "No memory\n"); + return -ENOMEM; + } + + /* calculate the number of blocks we need to send */ + dnload.block_count = (dnload.data_len-3) / MTK_BLOCK_SIZE; + /* add one more block if no multiple of blocksize */ + if((dnload.data_len-3) % MTK_BLOCK_SIZE) + dnload.block_count++; + + /* divide by 2, since we have to tell the mtk loader the size + * as count of uint16 (odd transfer sizes are not possible) */ + tmp_size = (dnload.block_count * MTK_BLOCK_SIZE)/2; + dnload.mtk_send_size[0] = (tmp_size >> 24) & 0xff; + dnload.mtk_send_size[1] = (tmp_size >> 16) & 0xff; + dnload.mtk_send_size[2] = (tmp_size >> 8) & 0xff; + dnload.mtk_send_size[3] = tmp_size & 0xff; + + /* initialize write pointer to start of data */ + dnload.write_ptr = dnload.data; + } + + block_data = dnload.block; + dnload.write_ptr = dnload.data + 2 + + (dnload.block_len * dnload.block_number); + + remaining_bytes = dnload.data_len - 3 - + (dnload.block_len * dnload.block_number); + + memcpy(block_data, dnload.write_ptr, MTK_BLOCK_SIZE); + + if (remaining_bytes <= MTK_BLOCK_SIZE) { + fill_bytes = (MTK_BLOCK_SIZE - remaining_bytes); + printf("Preparing the last block, filling %i bytes\n", + fill_bytes); + memset(block_data + remaining_bytes, 0x00, fill_bytes); + dnload.romload_state = SENDING_LAST_BLOCK; + } else { + dnload.romload_state = SENDING_BLOCKS; + printf("Preparing block %i\n", dnload.block_number+1); + } + + /* for the mtk romloader we need to swap MSB <-> LSB */ + for (i = 0; i < dnload.block_len; i += 2) { + tmp_byteswap = dnload.block[i]; + dnload.block[i] = dnload.block[i+1]; + dnload.block[i+1] = tmp_byteswap; + } + + /* initialize block pointer to start of block */ + dnload.block_ptr = dnload.block; + + dnload.block_number++; + return rc; +} + +static int handle_write_block(void) +{ + int bytes_left, write_len, rc; + + printf("handle_write_block(): "); + + if (dnload.block_ptr >= dnload.block + dnload.block_len) { + printf("Block %i finished\n", dnload.block_number); + dnload.write_ptr = dnload.data; + dnload.serial_fd.when &= ~BSC_FD_WRITE; + if (dnload.romload_state == SENDING_LAST_BLOCK) { + dnload.romload_state = LAST_BLOCK_SENT; + printf("Finished, sent %i blocks in total\n", + dnload.block_number); + } else { + dnload.romload_state = WAITING_BLOCK_ACK; + } + + return 0; + } + + /* try to write a maximum of block_len bytes */ + bytes_left = (dnload.block + dnload.block_len) - dnload.block_ptr; + write_len = dnload.block_len; + if (bytes_left < dnload.block_len) + write_len = bytes_left; + + rc = write(dnload.serial_fd.fd, dnload.block_ptr, write_len); + if (rc < 0) { + perror("Error during write"); + return rc; + } + + dnload.block_ptr += rc; + + printf("%u bytes (%tu/%u)\n", rc, dnload.block_ptr - dnload.block, + dnload.block_len); + + return 0; +} + +#define WRITE_BLOCK 4096 + +static int handle_write_dnload(void) +{ + int bytes_left, write_len, rc; + uint8_t xor_init = 0x02; + + printf("handle_write(): "); + if (dnload.write_ptr == dnload.data) { + /* no bytes have been transferred yet */ + switch (dnload.mode) { + case MODE_C155: + case MODE_C140xor: + case MODE_C123xor: + rc = write(dnload.serial_fd.fd, &xor_init, 1); + break; + default: + break; + } + } else if (dnload.write_ptr >= dnload.data + dnload.data_len) { + printf("finished\n"); + dnload.write_ptr = dnload.data; + dnload.serial_fd.when &= ~BSC_FD_WRITE; + return 1; + } + + /* try to write a maximum of WRITE_BLOCK bytes */ + bytes_left = (dnload.data + dnload.data_len) - dnload.write_ptr; + write_len = WRITE_BLOCK; + if (bytes_left < WRITE_BLOCK) + write_len = bytes_left; + + rc = write(dnload.serial_fd.fd, dnload.write_ptr, write_len); + if (rc < 0) { + perror("Error during write"); + return rc; + } + + dnload.write_ptr += rc; + + printf("%u bytes (%tu/%u)\n", rc, dnload.write_ptr - dnload.data, + dnload.data_len); + + return 0; +} + +static int handle_sercomm_write(void) +{ + uint8_t c; + + if (sercomm_drv_pull(&c) != 0) { + if (write(dnload.serial_fd.fd, &c, 1) != 1) + perror("short write"); + } else + dnload.serial_fd.when &= ~BSC_FD_WRITE; + + return 0; +} + +static int handle_write(void) +{ + /* TODO: simplify this again (global state: downloading, sercomm) */ + switch (dnload.mode) { + case MODE_ROMLOAD: + switch (dnload.romload_state) { + case SENDING_BLOCKS: + case SENDING_LAST_BLOCK: + return handle_write_block(); + default: + return handle_sercomm_write(); + } + break; + case MODE_MTK: + switch (dnload.mtk_state) { + case MTK_SENDING_BLOCKS: + return handle_write_block(); + default: + return handle_sercomm_write(); + } + break; + default: + switch (dnload.state) { + case DOWNLOADING: + return handle_write_dnload(); + default: + return handle_sercomm_write(); + } + } + + return 0; +} + +static uint8_t buffer[sizeof(phone_prompt1)]; +static uint8_t *bufptr = buffer; + +static void hdlc_send_to_phone(uint8_t dlci, uint8_t *data, int len) +{ + struct msgb *msg; + uint8_t *dest; + + printf("hdlc_send_to_phone(dlci=%u): ", dlci); + hexdump(data, len); + + if (len > 512) { + fprintf(stderr, "Too much data to send. %u\n", len); + return; + } + + /* push the message into the stack */ + msg = sercomm_alloc_msgb(512); + if (!msg) { + fprintf(stderr, "Failed to create data for the frame.\n"); + return; + } + + /* copy the data */ + dest = msgb_put(msg, len); + memcpy(dest, data, len); + + sercomm_sendmsg(dlci, msg); + + dnload.serial_fd.when |= BSC_FD_WRITE; +} + +static void hdlc_console_cb(uint8_t dlci, struct msgb *msg) +{ + int rc; + + rc = write(1, msg->data, msg->len); + msgb_free(msg); +} + +static void hdlc_tool_cb(uint8_t dlci, struct msgb *msg) +{ + struct tool_server *srv = tool_server_for_dlci[dlci]; + + if(srv) { + struct tool_connection *con; + u_int16_t *len; + + len = (u_int16_t *) msgb_push(msg, 2); + *len = htons(msg->len - sizeof(*len)); + + llist_for_each_entry(con, &srv->connections, entry) { + if (write(con->fd.fd, msg->data, msg->len) != msg->len) { + fprintf(stderr, + "Failed to write msg to the socket..\n"); + continue; + } + } + } + + msgb_free(msg); +} + +static void print_hdlc(uint8_t *buffer, int length) +{ + int i; + + for (i = 0; i < length; ++i) + if (sercomm_drv_rx_char(buffer[i]) == 0) + printf("Dropping sample '%c'\n", buffer[i]); +} + +static int handle_buffer(int buf_used_len) +{ + int nbytes, buf_left; + + buf_left = buf_used_len - (bufptr - buffer); + if (buf_left <= 0) { + memmove(buffer, buffer+1, buf_used_len-1); + bufptr -= 1; + buf_left = 1; + } + + nbytes = read(dnload.serial_fd.fd, bufptr, buf_left); + if (nbytes <= 0) + return nbytes; + + if (!dnload.print_hdlc) { + printf("got %i bytes from modem, ", nbytes); + printf("data looks like: "); + hexdump(bufptr, nbytes); + } else { + print_hdlc(bufptr, nbytes); + } + + return nbytes; +} + +/* Compal ramloader */ +static int handle_read(void) +{ + int rc, nbytes; + + nbytes = handle_buffer(sizeof(buffer)); + if (nbytes <= 0) + return nbytes; + + if (!memcmp(buffer, phone_prompt1, sizeof(phone_prompt1))) { + printf("Received PROMPT1 from phone, responding with CMD\n"); + dnload.print_hdlc = 0; + dnload.state = WAITING_PROMPT2; + rc = write(dnload.serial_fd.fd, dnload_cmd, sizeof(dnload_cmd)); + + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", + dnload.filename, rc); + exit(1); + } + } else if (!memcmp(buffer, phone_prompt2, sizeof(phone_prompt2))) { + printf("Received PROMPT2 from phone, starting download\n"); + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + dnload.state = DOWNLOADING; + } else if (!memcmp(buffer, phone_ack, sizeof(phone_ack))) { + printf("Received DOWNLOAD ACK from phone, your code is" + " running now!\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + dnload.print_hdlc = 1; + } else if (!memcmp(buffer, phone_nack, sizeof(phone_nack))) { + printf("Received DOWNLOAD NACK from phone, something went" + " wrong :(\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } else if (!memcmp(buffer, phone_nack_magic, sizeof(phone_nack_magic))) { + printf("Received MAGIC NACK from phone, you need to" + " have \"1003\" at 0x803ce0\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } else if (!memcmp(buffer, ftmtool, sizeof(ftmtool))) { + printf("Received FTMTOOL from phone, ramloader has aborted\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.state = WAITING_PROMPT1; + dnload.write_ptr = dnload.data; + } + bufptr += nbytes; + + return nbytes; +} + +/* "Calypso non-secure romloader" */ +static int handle_read_romload(void) +{ + int rc, nbytes, buf_used_len; + + /* virtually limit buffer length for romloader, since responses + * are shorter and vary in length */ + + switch (dnload.romload_state) { + case WAITING_PARAM_ACK: + buf_used_len = 4; /* ">p" + uint16_t len */ + break; + case WAITING_CHECKSUM_ACK: + buf_used_len = 3; /* ">c" + uint8_t checksum */ + break; + case FINISHED: + buf_used_len = sizeof(buffer); + break; + default: + buf_used_len = 2; /* ">*" */ + } + + nbytes = handle_buffer(buf_used_len); + if (nbytes <= 0) + return nbytes; + + switch (dnload.romload_state) { + case WAITING_IDENTIFICATION: + if (memcmp(buffer, romload_ident_ack, + sizeof(romload_ident_ack))) + break; + + printf("Received ident ack from phone, sending " + "parameter sequence\n"); + dnload.print_hdlc = 1; + dnload.romload_state = WAITING_PARAM_ACK; + rc = write(dnload.serial_fd.fd, romload_param, + sizeof(romload_param)); + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", + dnload.filename, rc); + exit(1); + } + break; + case WAITING_PARAM_ACK: + if (memcmp(buffer, romload_param_ack, + sizeof(romload_param_ack))) + break; + + printf("Received parameter ack from phone, " + "starting download\n"); + serial_set_baudrate(ROMLOAD_DL_BAUDRATE); + + /* using the max blocksize the phone tells us */ + dnload.block_payload_size = ((buffer[3] << 8) + buffer[2]); + printf("Used blocksize for download is %i bytes\n", + dnload.block_payload_size); + dnload.block_payload_size -= ROMLOAD_BLOCK_HDR_LEN; + dnload.romload_state = SENDING_BLOCKS; + dnload.block_number = 0; + romload_prepare_block(); + bufptr -= 2; + break; + case WAITING_BLOCK_ACK: + case LAST_BLOCK_SENT: + if (!memcmp(buffer, romload_block_ack, + sizeof(romload_block_ack))) { + printf("Received block ack from phone\n"); + if (dnload.romload_state == LAST_BLOCK_SENT) { + /* send the checksum */ + uint8_t final_checksum = + (~(dnload.romload_dl_checksum) & 0xff); + printf("Sending checksum: 0x%02x \n", + final_checksum); + rc = write(dnload.serial_fd.fd, + romload_checksum_cmd, + sizeof(romload_checksum_cmd)); + rc = write(dnload.serial_fd.fd, + &final_checksum, 1); + dnload.romload_state = WAITING_CHECKSUM_ACK; + } else + romload_prepare_block(); + } else if (!memcmp(buffer, romload_block_nack, + sizeof(romload_block_nack))) { + printf("Received block nack from phone, " + "something went wrong, aborting\n"); + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + dnload.romload_state = WAITING_IDENTIFICATION; + bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL); + } + break; + case WAITING_CHECKSUM_ACK: + if (!memcmp(buffer, romload_checksum_ack, + sizeof(romload_checksum_ack))) { + printf("Checksum on phone side matches, " + "let's branch to your code\n"); + printf("Branching to 0x%08x\n", ROMLOAD_ADDRESS); + + rc = write(dnload.serial_fd.fd, romload_branch_cmd, + sizeof(romload_branch_cmd)); + rc = write(dnload.serial_fd.fd, &dnload.load_address, + sizeof(dnload.load_address)); + dnload.romload_state = WAITING_BRANCH_ACK; + bufptr -= 1; + } else if (!memcmp(buffer, romload_checksum_nack, + sizeof(romload_checksum_nack))) { + printf("Checksum on phone side (0x%02x) doesn't " + "match ours, aborting\n", ~buffer[2]); + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + dnload.romload_state = WAITING_IDENTIFICATION; + bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL); + bufptr -= 1; + } + break; + case WAITING_BRANCH_ACK: + if (!memcmp(buffer, romload_branch_ack, + sizeof(romload_branch_ack))) { + printf("Received branch ack, your code is running now!\n"); + dnload.serial_fd.when = BSC_FD_READ; + dnload.romload_state = FINISHED; + dnload.write_ptr = dnload.data; + dnload.print_hdlc = 1; + } else if (!memcmp(buffer, romload_branch_nack, + sizeof(romload_branch_nack))) { + printf("Received branch nack, aborting\n"); + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + dnload.romload_state = WAITING_IDENTIFICATION; + bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL); + } + break; + default: + break; + } + + bufptr += nbytes; + return nbytes; +} + +/* MTK romloader */ +static int handle_read_mtk(void) +{ + int rc, nbytes, buf_used_len; + + switch (dnload.mtk_state) { + case MTK_WAIT_ADDR_ACK: + case MTK_WAIT_SIZE_ACK: + case MTK_WAIT_BRANCH_ADDR_ACK: + buf_used_len = 4; + break; + case MTK_FINISHED: + buf_used_len = sizeof(buffer); + break; + default: + buf_used_len = 1; + } + + nbytes = handle_buffer(buf_used_len); + if (nbytes <= 0) + return nbytes; + + switch (dnload.mtk_state) { + case MTK_INIT_1: + if (!(buffer[0] == mtk_init_resp[0])) + break; + dnload.mtk_state = MTK_INIT_2; + printf("Received init magic byte 1\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[1], 1); + break; + case MTK_INIT_2: + if (!(buffer[0] == mtk_init_resp[1])) + break; + dnload.mtk_state = MTK_INIT_3; + printf("Received init magic byte 2\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[2], 1); + break; + case MTK_INIT_3: + if (!(buffer[0] == mtk_init_resp[2])) + break; + dnload.mtk_state = MTK_INIT_4; + printf("Received init magic byte 3\n"); + rc = write(dnload.serial_fd.fd, &mtk_init_cmd[3], 1); + break; + case MTK_INIT_4: + if (!(buffer[0] == mtk_init_resp[3])) + break; + dnload.mtk_state = MTK_WAIT_WRITE_ACK; + printf("Received init magic byte 4, requesting write\n"); + rc = write(dnload.serial_fd.fd, &mtk_command[0], 1); + break; + case MTK_WAIT_WRITE_ACK: + if (!(buffer[0] == mtk_command[0])) + break; + dnload.mtk_state = MTK_WAIT_ADDR_ACK; + printf("Received write ack, sending load address\n"); + + rc = write(dnload.serial_fd.fd, &dnload.load_address, + sizeof(dnload.load_address)); + break; + case MTK_WAIT_ADDR_ACK: + if (memcmp(buffer, dnload.load_address, + sizeof(dnload.load_address))) + break; + printf("Received address ack from phone, sending loadsize\n"); + /* re-read file */ + rc = read_file(dnload.filename); + if (rc < 0) { + fprintf(stderr, "read_file(%s) failed with %d\n", + dnload.filename, rc); + exit(1); + } + dnload.block_number = 0; + mtk_prepare_block(); + dnload.mtk_state = MTK_WAIT_SIZE_ACK; + rc = write(dnload.serial_fd.fd, &dnload.mtk_send_size, + sizeof(dnload.mtk_send_size)); + break; + case MTK_WAIT_SIZE_ACK: + if (memcmp(buffer, dnload.mtk_send_size, + sizeof(dnload.mtk_send_size))) + break; + printf("Received size ack\n"); + dnload.print_hdlc = 1; + dnload.mtk_state = MTK_SENDING_BLOCKS; + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + bufptr -= 3; + break; + case MTK_SENDING_BLOCKS: + if (!(buffer[0] == dnload.block[dnload.echo_bytecount])) + printf("Warning: Byte %i of Block %i doesn't match," + " check your serial connection!\n", + dnload.echo_bytecount, dnload.block_number); + dnload.echo_bytecount++; + + if ((dnload.echo_bytecount+1) > MTK_BLOCK_SIZE) { + if ( dnload.block_number == dnload.block_count) { + rc = write(dnload.serial_fd.fd, + &mtk_command[3], 1); + printf("Sending branch command\n"); + dnload.print_hdlc = 0; + dnload.mtk_state = MTK_WAIT_BRANCH_CMD_ACK; + break; + } + printf("Received Block %i preparing next block\n", + dnload.block_number); + mtk_prepare_block(); + dnload.serial_fd.when = BSC_FD_READ | BSC_FD_WRITE; + } + break; + case MTK_WAIT_BRANCH_CMD_ACK: + if (!(buffer[0] == mtk_command[3])) + break; + dnload.mtk_state = MTK_WAIT_BRANCH_ADDR_ACK; + printf("Received branch command ack, sending address\n"); + + rc = write(dnload.serial_fd.fd, &dnload.load_address, + sizeof(dnload.load_address)); + break; + case MTK_WAIT_BRANCH_ADDR_ACK: + if (memcmp(buffer, dnload.load_address, + sizeof(dnload.load_address))) + break; + printf("Received branch address ack, code should run now\n"); + + /* uncomment this once we have working uart driver & sercomm */ + //dnload.mtk_state = MTK_FINISHED; + //dnload.serial_fd.when = BSC_FD_READ; + //dnload.print_hdlc = 1; + break; + default: + break; + } + + bufptr += nbytes; + return nbytes; +} + +static int serial_read(struct bsc_fd *fd, unsigned int flags) +{ + int rc; + if (flags & BSC_FD_READ) { + switch (dnload.mode) { + case MODE_ROMLOAD: + rc = handle_read_romload(); + break; + case MODE_MTK: + rc = handle_read_mtk(); + break; + default: + rc = handle_read(); + break; + } + if (rc == 0) + exit(2); + } + + if (flags & BSC_FD_WRITE) { + rc = handle_write(); + if (rc == 1) + dnload.state = WAITING_PROMPT1; + } + return 0; +} + +static int parse_mode(const char *arg) +{ + if (!strcasecmp(arg, "c123")) + return MODE_C123; + else if (!strcasecmp(arg, "c123xor")) + return MODE_C123xor; + else if (!strcasecmp(arg, "c140")) + return MODE_C140; + else if (!strcasecmp(arg, "c140xor")) + return MODE_C140xor; + else if (!strcasecmp(arg, "c155")) + return MODE_C155; + else if (!strcasecmp(arg, "romload")) + return MODE_ROMLOAD; + else if (!strcasecmp(arg, "mtk")) + return MODE_MTK; + + return -1; +} + +#define HELP_TEXT \ + "[ -v | -h ] [ -p /dev/ttyXXXX ] [ -s /tmp/osmocom_l2 ]\n" \ + "\t\t[ -l /tmp/osmocom_loader ]\n" \ + "\t\t[ -m {c123,c123xor,c140,c140xor,c155,romload,mtk} ]\n" \ + "\t\t file.bin\n\n" \ + "* Open serial port /dev/ttyXXXX (connected to your phone)\n" \ + "* Perform handshaking with the ramloader in the phone\n" \ + "* Download file.bin to the attached phone (base address 0x00800100)\n" + +static int usage(const char *name) +{ + printf("Usage: %s ", name); + printf(HELP_TEXT); + exit(2); +} + +static int version(const char *name) +{ + printf("%s version %s\n", name, PACKAGE_VERSION); + exit(2); +} + +static int un_tool_read(struct bsc_fd *fd, unsigned int flags) +{ + int rc, c; + u_int16_t length = 0xffff; + u_int8_t buf[4096]; + struct tool_connection *con = (struct tool_connection *)fd->data; + + c = 0; + while(c < 2) { + rc = read(fd->fd, &buf + c, 2 - c); + if(rc == 0) { + // disconnect + goto close; + } + if(rc < 0) { + if(errno == EAGAIN) { + continue; + } + fprintf(stderr, "Err from socket: %s\n", strerror(errno)); + goto close; + } + c += rc; + } + + length = ntohs(*(u_int16_t*)buf); + + c = 0; + while(c < length) { + rc = read(fd->fd, &buf + c, length - c); + if(rc == 0) { + // disconnect + goto close; + } + if(rc < 0) { + if(errno == EAGAIN) { + continue; + } + fprintf(stderr, "Err from socket: %s\n", strerror(errno)); + goto close; + } + c += rc; + } + + hdlc_send_to_phone(con->server->dlci, buf, length); + + return 0; +close: + + close(fd->fd); + bsc_unregister_fd(fd); + llist_del(&con->entry); + talloc_free(con); + return -1; +} + +/* accept a new connection */ +static int tool_accept(struct bsc_fd *fd, unsigned int flags) +{ + struct tool_server *srv = (struct tool_server *)fd->data; + struct tool_connection *con; + struct sockaddr_un un_addr; + socklen_t len; + int rc; + + len = sizeof(un_addr); + rc = accept(fd->fd, (struct sockaddr *) &un_addr, &len); + if (rc < 0) { + fprintf(stderr, "Failed to accept a new connection.\n"); + return -1; + } + + con = talloc_zero(NULL, struct tool_connection); + if (!con) { + fprintf(stderr, "Failed to create tool connection.\n"); + return -1; + } + + con->server = srv; + + con->fd.fd = rc; + con->fd.when = BSC_FD_READ; + con->fd.cb = un_tool_read; + con->fd.data = con; + if (bsc_register_fd(&con->fd) != 0) { + fprintf(stderr, "Failed to register the fd.\n"); + return -1; + } + + llist_add(&con->entry, &srv->connections); + return 0; +} + +/* + * Register and start a tool server + */ +static int register_tool_server(struct tool_server *ts, + const char *path, + uint8_t dlci) +{ + struct bsc_fd *bfd = &ts->bfd; + struct sockaddr_un local; + unsigned int namelen; + int rc; + + bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (bfd->fd < 0) { + fprintf(stderr, "Failed to create Unix Domain Socket.\n"); + return -1; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); + if (rc != 0) { + fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", + local.sun_path); + return -1; + } + + if (listen(bfd->fd, 0) != 0) { + fprintf(stderr, "Failed to listen.\n"); + return -1; + } + + bfd->when = BSC_FD_READ; + bfd->cb = tool_accept; + bfd->data = ts; + + ts->dlci = dlci; + INIT_LLIST_HEAD(&ts->connections); + + tool_server_for_dlci[dlci] = ts; + + sercomm_register_rx_cb(dlci, hdlc_tool_cb); + + if (bsc_register_fd(bfd) != 0) { + fprintf(stderr, "Failed to register the bfd.\n"); + return -1; + } + + return 0; +} + +extern void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg); + +int main(int argc, char **argv) +{ + int opt, flags; + uint32_t tmp_load_address = 0; + const char *serial_dev = "/dev/ttyUSB1"; + const char *layer2_un_path = "/tmp/osmocom_l2"; + const char *loader_un_path = "/tmp/osmocom_loader"; + + dnload.mode = MODE_C123; + + while ((opt = getopt(argc, argv, "hl:p:m:s:v")) != -1) { + switch (opt) { + case 'p': + serial_dev = optarg; + break; + case 'm': + dnload.mode = parse_mode(optarg); + if (dnload.mode < 0) + usage(argv[0]); + break; + case 's': + layer2_un_path = optarg; + break; + case 'l': + loader_un_path = optarg; + break; + case 'v': + version(argv[0]); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + if (argc <= optind) { + fprintf(stderr, "You have to specify the filename\n"); + usage(argv[0]); + } + + dnload.filename = argv[optind]; + + dnload.serial_fd.fd = serial_init(serial_dev); + if (dnload.serial_fd.fd < 0) { + fprintf(stderr, "Cannot open serial device %s\n", serial_dev); + exit(1); + } + + if (bsc_register_fd(&dnload.serial_fd) != 0) { + fprintf(stderr, "Failed to register the serial.\n"); + exit(1); + } + + /* Set serial socket to non-blocking mode of operation */ + flags = fcntl(dnload.serial_fd.fd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(dnload.serial_fd.fd, F_SETFL, flags); + + dnload.serial_fd.when = BSC_FD_READ; + dnload.serial_fd.cb = serial_read; + + /* initialize the HDLC layer */ + sercomm_init(); + sercomm_register_rx_cb(SC_DLCI_CONSOLE, hdlc_console_cb); + sercomm_register_rx_cb(SC_DLCI_DEBUG, hdlc_tpudbg_cb); + + /* unix domain socket handling */ + if (register_tool_server(&dnload.layer2_server, layer2_un_path, + SC_DLCI_L1A_L23) != 0) + exit(1); + + if (register_tool_server(&dnload.loader_server, loader_un_path, + SC_DLCI_LOADER) != 0) + exit(1); + + /* if in romload mode, start our beacon timer */ + if (dnload.mode == MODE_ROMLOAD) { + tmp_load_address = ROMLOAD_ADDRESS; + serial_set_baudrate(ROMLOAD_INIT_BAUDRATE); + tick_timer.cb = &beacon_timer_cb; + tick_timer.data = &tick_timer; + bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL); + } + else if (dnload.mode == MODE_MTK) { + tmp_load_address = MTK_ADDRESS; + serial_set_baudrate(MTK_INIT_BAUDRATE); + tick_timer.cb = &mtk_timer_cb; + tick_timer.data = &tick_timer; + bsc_schedule_timer(&tick_timer, 0, BEACON_INTERVAL); + } + + dnload.load_address[0] = (tmp_load_address >> 24) & 0xff; + dnload.load_address[1] = (tmp_load_address >> 16) & 0xff; + dnload.load_address[2] = (tmp_load_address >> 8) & 0xff; + dnload.load_address[3] = tmp_load_address & 0xff; + + while (1) + bsc_select_main(0); + + close(dnload.serial_fd.fd); + + exit(0); +} diff --git a/src/host/osmocon/osmoload.c b/src/host/osmocon/osmoload.c new file mode 100644 index 00000000..ecee8b36 --- /dev/null +++ b/src/host/osmocon/osmoload.c @@ -0,0 +1,198 @@ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> + +#include <arpa/inet.h> + +#include <sys/socket.h> +#include <sys/un.h> + +#include <osmocore/msgb.h> +#include <osmocore/select.h> + +#include <loader/protocol.h> + +#define MSGB_MAX 256 + +#define DEFAULT_SOCKET "/tmp/osmocom_loader" + +static struct bsc_fd connection; + +static int usage(const char *name) +{ + printf("\nUsage: %s [ -v | -h ] [ -m {c123,c155} ] [ -l /tmp/osmocom_loader ] COMMAND\n", name); + exit(2); +} + +static int version(const char *name) +{ + //printf("\n%s version %s\n", name, VERSION); + exit(2); +} + +static void hexdump(const uint8_t *data, unsigned int len) +{ + const uint8_t *bufptr = data; + int n; + + for (n=0; n < len; n++, bufptr++) + printf("%02x ", *bufptr); + printf("\n"); +} + +static void +loader_send_request(struct msgb *msg) { + int rc; + u_int16_t len = htons(msg->len); + + printf("Sending %d bytes ", msg->len); + hexdump(msg->data, msg->len); + + rc = write(connection.fd, &len, sizeof(len)); + if(rc != sizeof(len)) { + fprintf(stderr, "Error writing.\n"); + exit(2); + } + + rc = write(connection.fd, msg->data, msg->len); + if(rc != msg->len) { + fprintf(stderr, "Error writing.\n"); + exit(2); + } +} + +static void +loader_handle_reply(struct msgb *msg) { + printf("Received "); + hexdump(msg->data, msg->len); +} + +static int +loader_read_cb(struct bsc_fd *fd, unsigned int flags) { + struct msgb *msg; + u_int16_t len; + int rc; + + msg = msgb_alloc(MSGB_MAX, "loader"); + if (!msg) { + fprintf(stderr, "Failed to allocate msg.\n"); + return -1; + } + + rc = read(fd->fd, &len, sizeof(len)); + if (rc < sizeof(len)) { + fprintf(stderr, "Short read. Error.\n"); + exit(2); + } + + if (ntohs(len) > MSGB_MAX) { + fprintf(stderr, "Length is too big: %u\n", ntohs(len)); + msgb_free(msg); + return -1; + } + + /* blocking read for the poor... we can starve in here... */ + msg->l2h = msgb_put(msg, ntohs(len)); + rc = read(fd->fd, msg->l2h, msgb_l2len(msg)); + if (rc != msgb_l2len(msg)) { + fprintf(stderr, "Can not read data: rc: %d errno: %d\n", rc, errno); + msgb_free(msg); + return -1; + } + + loader_handle_reply(msg); + + msgb_free(msg); + + return 0; +} + +static void +loader_connect(const char *socket_path) { + int rc; + struct sockaddr_un local; + struct bsc_fd *conn = &connection; + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, socket_path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + + conn->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (conn->fd < 0) { + fprintf(stderr, "Failed to create unix domain socket.\n"); + exit(1); + } + + rc = connect(conn->fd, (struct sockaddr *) &local, + sizeof(local.sun_family) + strlen(local.sun_path)); + if (rc < 0) { + fprintf(stderr, "Failed to connect to '%s'.\n", local.sun_path); + exit(1); + } + + conn->when = BSC_FD_READ; + conn->cb = loader_read_cb; + conn->data = NULL; + + if (bsc_register_fd(conn) != 0) { + fprintf(stderr, "Failed to register fd.\n"); + exit(1); + } +} + +static void +loader_command(char *name, int cmdc, char **cmdv) { + if(!cmdc) { + usage(name); + } + + char *cmd = cmdv[0]; + + printf("Command %s\n", cmd); + + if(!strcmp(cmd, "ping")) { + struct msgb *msg = msgb_alloc(MSGB_MAX, "loader"); + msgb_put_u8(msg, LOADER_PING); + msgb_put_u8(msg, 0); + loader_send_request(msg); + msgb_free(msg); + } else { + printf("Unknown command '%s'\n", cmd); + usage(name); + } +} + +int +main(int argc, char **argv) { + int opt; + char *loader_un_path = "/tmp/osmocom_loader"; + + while((opt = getopt(argc, argv, "hl:m:v")) != -1) { + switch(opt) { + case 'l': + loader_un_path = optarg; + break; + case 'm': + puts("model selection not implemented"); + exit(2); + break; + case 'v': + version(argv[0]); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + loader_connect(loader_un_path); + + loader_command(argv[0], argc - optind, argv + optind); + + return 0; +} diff --git a/src/host/osmocon/tpu_debug.c b/src/host/osmocon/tpu_debug.c new file mode 100644 index 00000000..f54bd40f --- /dev/null +++ b/src/host/osmocon/tpu_debug.c @@ -0,0 +1,138 @@ +/* Calypso TPU debugger, displays and decodes TPU instruction RAM */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#include <osmocore/msgb.h> + +/* TPU disassembler begin */ + +static const char *tpu_instr_name[] = { + [0] = "SLEEP", + [1] = "AT", + [2] = "OFFSET", + [3] = "SYNCHRO", + [4] = "MOVE", + [5] = "WAIT", + [6] = "UNDEFINED6", + [7] = "UNDEFINED7", +}; + +static const char *tpu_addr_name[0x1f] = { + [0] = "TSP_CTLR1", + [1] = "TSP_CTRL2", + [4] = "TSP_TX_1", + [3] = "TSP_TX_2", + [2] = "TSP_TX_3", + [5] = "TSP_TX_4", + [6] = "TSPACT_L", + [7] = "TSPACT_H", + [9] = "TSP_SET1", + [0xa] = "TSP_SET2", + [0xb] = "TSP_SET3", + [0x10] = "DSP_INT_PG", + [0x11] = "GAUGING_EN", +}; + +static uint8_t tpu_reg_cache[0x1f]; +static uint16_t tpu_qbit; + +static void tpu_show_instr(uint16_t tpu) +{ + uint16_t instr = tpu >> 13; + uint16_t param = tpu & 0x1fff; + uint16_t addr, data, bitlen; + uint32_t tsp_data; + + tpu_qbit++; + + printf("\t %04u %04x %s ", tpu_qbit, tpu, tpu_instr_name[instr]); + switch (instr) { + case 0: + tpu_qbit = 0; + default: + break; + case 1: + tpu_qbit = param; + printf("%u ", param); + break; + case 5: + tpu_qbit += param; + printf("%u ", param); + break; + case 2: + case 3: + printf("%u ", param); + break; + case 4: + addr = param & 0x1f; + data = param >> 5; + tpu_reg_cache[addr] = data; + printf("%10s=0x%04x ", tpu_addr_name[addr], data); + switch (addr) { + case 0: + bitlen = (data & 0x1f) + 1; + printf("DEV_IDX=%u, BITLEN=%u ", data >> 5, bitlen); + if (bitlen <= 8) { + tsp_data = tpu_reg_cache[4]; + printf(" TSP_DATA=0x%02x ", tsp_data); + } else if (bitlen <= 16) { + tsp_data = tpu_reg_cache[3]; + tsp_data |= tpu_reg_cache[4] << 8; + printf(" TSP_DATA=0x%04x ", tsp_data); + } else if (bitlen <= 24) { + tsp_data = tpu_reg_cache[2]; + tsp_data |= tpu_reg_cache[3] << 8; + tsp_data |= tpu_reg_cache[4] << 16; + printf(" TSP_DATA=0x%06x ", tsp_data); + } else { + tsp_data = tpu_reg_cache[5]; + tsp_data |= tpu_reg_cache[2] << 8; + tsp_data |= tpu_reg_cache[3] << 16; + tsp_data |= tpu_reg_cache[4] << 24; + printf(" TSP_DATA=0x%08x ", tsp_data); + } + break; + case 1: + if (data & 0x01) + printf("READ "); + if (data & 0x02) + printf("WRITE "); + break; + } + } + printf("\n"); +} + +void hdlc_tpudbg_cb(uint8_t dlci, struct msgb *msg) +{ + uint32_t *fn = (uint32_t *) msg->data; + uint16_t *tpu; + + printf("TPU FN %u\n", *fn); + for (tpu = (uint16_t *) (msg->data + 4); tpu < (uint16_t *) msg->tail; tpu++) + tpu_show_instr(*tpu); + + msgb_free(msg); +} diff --git a/src/host/rita_pll/rita_pll.pl b/src/host/rita_pll/rita_pll.pl new file mode 100755 index 00000000..7de1aec4 --- /dev/null +++ b/src/host/rita_pll/rita_pll.pl @@ -0,0 +1,111 @@ +#!/usr/bin/perl + +sub pll_rx($$$$$) { + my ($a, $b, $p, $r, $l) = @_; + + return (($b*$p+$a)/($r*$l))*26; +} + +sub pll_rx_low_band($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 65; my $l = 4; + return pll_rx($a, $b, $p, $r, $l); +} + +sub pll_rx_high_band($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 65; my $l = 2; + return pll_rx($a, $b, $p, $r, $l); +} + +sub pll_tx_gsm850_1($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 55; my $l = 4; my $m = 26; + + my $left = ((1/$l) - (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_gsm850_2($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 30; my $l = 4; my $m = 52; + + my $left = ((1/$l) - (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_gsm900($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 35; my $l = 4; my $m = 52; + + my $left = ((1/$l) + (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub pll_tx_high($$) { + my ($a, $b) = @_; + my $p = 64; my $r = 70; my $l = 2; my $m = 26; + + my $left = ((1/$l) + (1/$m)); + my $right = (($b*$p+$a)/$r); + + return $left * $right * 26; +} + +sub hr() { + printf("======================================================================\n"); +} + +printf("PLL Rx Low Band:\n"); +for (my $b = 135; $b <= 150; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_low_band($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Rx High Band:\n"); +for (my $b = 141; $b <= 155; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_rx_high_band($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM850_1\n"); +for (my $b = 128; $b <= 130; $b++) { + for (my $a = 0; $a <= 62; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_1($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM850_2\n"); +for (my $b = 65; $b <= 66; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm850_2($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM900\n"); +for (my $b = 68; $b <= 71; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_gsm900($a, $b), $a, $b); + } +} + +hr(); +printf("PLL Tx GSM1800/1900\n"); +for (my $b = 133; $b <= 149; $b++) { + for (my $a = 0; $a <= 63; $a++) { + printf("Fout=%4.2f (A=%03u, B=%03u)\n", pll_tx_high($a, $b), $a, $b); + } +} + diff --git a/src/host/rita_pll/rita_pll.txt b/src/host/rita_pll/rita_pll.txt new file mode 100644 index 00000000..cac2cac2 --- /dev/null +++ b/src/host/rita_pll/rita_pll.txt @@ -0,0 +1,3625 @@ +PLL Rx Low Band: +Fout=864.00 (A=000, B=135) +Fout=864.10 (A=001, B=135) +Fout=864.20 (A=002, B=135) +Fout=864.30 (A=003, B=135) +Fout=864.40 (A=004, B=135) +Fout=864.50 (A=005, B=135) +Fout=864.60 (A=006, B=135) +Fout=864.70 (A=007, B=135) +Fout=864.80 (A=008, B=135) +Fout=864.90 (A=009, B=135) +Fout=865.00 (A=010, B=135) +Fout=865.10 (A=011, B=135) +Fout=865.20 (A=012, B=135) +Fout=865.30 (A=013, B=135) +Fout=865.40 (A=014, B=135) +Fout=865.50 (A=015, B=135) +Fout=865.60 (A=016, B=135) +Fout=865.70 (A=017, B=135) +Fout=865.80 (A=018, B=135) +Fout=865.90 (A=019, B=135) +Fout=866.00 (A=020, B=135) +Fout=866.10 (A=021, B=135) +Fout=866.20 (A=022, B=135) +Fout=866.30 (A=023, B=135) +Fout=866.40 (A=024, B=135) +Fout=866.50 (A=025, B=135) +Fout=866.60 (A=026, B=135) +Fout=866.70 (A=027, B=135) +Fout=866.80 (A=028, B=135) +Fout=866.90 (A=029, B=135) +Fout=867.00 (A=030, B=135) +Fout=867.10 (A=031, B=135) +Fout=867.20 (A=032, B=135) +Fout=867.30 (A=033, B=135) +Fout=867.40 (A=034, B=135) +Fout=867.50 (A=035, B=135) +Fout=867.60 (A=036, B=135) +Fout=867.70 (A=037, B=135) +Fout=867.80 (A=038, B=135) +Fout=867.90 (A=039, B=135) +Fout=868.00 (A=040, B=135) +Fout=868.10 (A=041, B=135) +Fout=868.20 (A=042, B=135) +Fout=868.30 (A=043, B=135) +Fout=868.40 (A=044, B=135) +Fout=868.50 (A=045, B=135) +Fout=868.60 (A=046, B=135) +Fout=868.70 (A=047, B=135) +Fout=868.80 (A=048, B=135) +Fout=868.90 (A=049, B=135) +Fout=869.00 (A=050, B=135) +Fout=869.10 (A=051, B=135) +Fout=869.20 (A=052, B=135) +Fout=869.30 (A=053, B=135) +Fout=869.40 (A=054, B=135) +Fout=869.50 (A=055, B=135) +Fout=869.60 (A=056, B=135) +Fout=869.70 (A=057, B=135) +Fout=869.80 (A=058, B=135) +Fout=869.90 (A=059, B=135) +Fout=870.00 (A=060, B=135) +Fout=870.10 (A=061, B=135) +Fout=870.20 (A=062, B=135) +Fout=870.40 (A=000, B=136) +Fout=870.50 (A=001, B=136) +Fout=870.60 (A=002, B=136) +Fout=870.70 (A=003, B=136) +Fout=870.80 (A=004, B=136) +Fout=870.90 (A=005, B=136) +Fout=871.00 (A=006, B=136) +Fout=871.10 (A=007, B=136) +Fout=871.20 (A=008, B=136) +Fout=871.30 (A=009, B=136) +Fout=871.40 (A=010, B=136) +Fout=871.50 (A=011, B=136) +Fout=871.60 (A=012, B=136) +Fout=871.70 (A=013, B=136) +Fout=871.80 (A=014, B=136) +Fout=871.90 (A=015, B=136) +Fout=872.00 (A=016, B=136) +Fout=872.10 (A=017, B=136) +Fout=872.20 (A=018, B=136) +Fout=872.30 (A=019, B=136) +Fout=872.40 (A=020, B=136) +Fout=872.50 (A=021, B=136) +Fout=872.60 (A=022, B=136) +Fout=872.70 (A=023, B=136) +Fout=872.80 (A=024, B=136) +Fout=872.90 (A=025, B=136) +Fout=873.00 (A=026, B=136) +Fout=873.10 (A=027, B=136) +Fout=873.20 (A=028, B=136) +Fout=873.30 (A=029, B=136) +Fout=873.40 (A=030, B=136) +Fout=873.50 (A=031, B=136) +Fout=873.60 (A=032, B=136) +Fout=873.70 (A=033, B=136) +Fout=873.80 (A=034, B=136) +Fout=873.90 (A=035, B=136) +Fout=874.00 (A=036, B=136) +Fout=874.10 (A=037, B=136) +Fout=874.20 (A=038, B=136) +Fout=874.30 (A=039, B=136) +Fout=874.40 (A=040, B=136) +Fout=874.50 (A=041, B=136) +Fout=874.60 (A=042, B=136) +Fout=874.70 (A=043, B=136) +Fout=874.80 (A=044, B=136) +Fout=874.90 (A=045, B=136) +Fout=875.00 (A=046, B=136) +Fout=875.10 (A=047, B=136) +Fout=875.20 (A=048, B=136) +Fout=875.30 (A=049, B=136) +Fout=875.40 (A=050, B=136) +Fout=875.50 (A=051, B=136) +Fout=875.60 (A=052, B=136) +Fout=875.70 (A=053, B=136) +Fout=875.80 (A=054, B=136) +Fout=875.90 (A=055, B=136) +Fout=876.00 (A=056, B=136) +Fout=876.10 (A=057, B=136) +Fout=876.20 (A=058, B=136) +Fout=876.30 (A=059, B=136) +Fout=876.40 (A=060, B=136) +Fout=876.50 (A=061, B=136) +Fout=876.60 (A=062, B=136) +Fout=876.80 (A=000, B=137) +Fout=876.90 (A=001, B=137) +Fout=877.00 (A=002, B=137) +Fout=877.10 (A=003, B=137) +Fout=877.20 (A=004, B=137) +Fout=877.30 (A=005, B=137) +Fout=877.40 (A=006, B=137) +Fout=877.50 (A=007, B=137) +Fout=877.60 (A=008, B=137) +Fout=877.70 (A=009, B=137) +Fout=877.80 (A=010, B=137) +Fout=877.90 (A=011, B=137) +Fout=878.00 (A=012, B=137) +Fout=878.10 (A=013, B=137) +Fout=878.20 (A=014, B=137) +Fout=878.30 (A=015, B=137) +Fout=878.40 (A=016, B=137) +Fout=878.50 (A=017, B=137) +Fout=878.60 (A=018, B=137) +Fout=878.70 (A=019, B=137) +Fout=878.80 (A=020, B=137) +Fout=878.90 (A=021, B=137) +Fout=879.00 (A=022, B=137) +Fout=879.10 (A=023, B=137) +Fout=879.20 (A=024, B=137) +Fout=879.30 (A=025, B=137) +Fout=879.40 (A=026, B=137) +Fout=879.50 (A=027, B=137) +Fout=879.60 (A=028, B=137) +Fout=879.70 (A=029, B=137) +Fout=879.80 (A=030, B=137) +Fout=879.90 (A=031, B=137) +Fout=880.00 (A=032, B=137) +Fout=880.10 (A=033, B=137) +Fout=880.20 (A=034, B=137) +Fout=880.30 (A=035, B=137) +Fout=880.40 (A=036, B=137) +Fout=880.50 (A=037, B=137) +Fout=880.60 (A=038, B=137) +Fout=880.70 (A=039, B=137) +Fout=880.80 (A=040, B=137) +Fout=880.90 (A=041, B=137) +Fout=881.00 (A=042, B=137) +Fout=881.10 (A=043, B=137) +Fout=881.20 (A=044, B=137) +Fout=881.30 (A=045, B=137) +Fout=881.40 (A=046, B=137) +Fout=881.50 (A=047, B=137) +Fout=881.60 (A=048, B=137) +Fout=881.70 (A=049, B=137) +Fout=881.80 (A=050, B=137) +Fout=881.90 (A=051, B=137) +Fout=882.00 (A=052, B=137) +Fout=882.10 (A=053, B=137) +Fout=882.20 (A=054, B=137) +Fout=882.30 (A=055, B=137) +Fout=882.40 (A=056, B=137) +Fout=882.50 (A=057, B=137) +Fout=882.60 (A=058, B=137) +Fout=882.70 (A=059, B=137) +Fout=882.80 (A=060, B=137) +Fout=882.90 (A=061, B=137) +Fout=883.00 (A=062, B=137) +Fout=883.20 (A=000, B=138) +Fout=883.30 (A=001, B=138) +Fout=883.40 (A=002, B=138) +Fout=883.50 (A=003, B=138) +Fout=883.60 (A=004, B=138) +Fout=883.70 (A=005, B=138) +Fout=883.80 (A=006, B=138) +Fout=883.90 (A=007, B=138) +Fout=884.00 (A=008, B=138) +Fout=884.10 (A=009, B=138) +Fout=884.20 (A=010, B=138) +Fout=884.30 (A=011, B=138) +Fout=884.40 (A=012, B=138) +Fout=884.50 (A=013, B=138) +Fout=884.60 (A=014, B=138) +Fout=884.70 (A=015, B=138) +Fout=884.80 (A=016, B=138) +Fout=884.90 (A=017, B=138) +Fout=885.00 (A=018, B=138) +Fout=885.10 (A=019, B=138) +Fout=885.20 (A=020, B=138) +Fout=885.30 (A=021, B=138) +Fout=885.40 (A=022, B=138) +Fout=885.50 (A=023, B=138) +Fout=885.60 (A=024, B=138) +Fout=885.70 (A=025, B=138) +Fout=885.80 (A=026, B=138) +Fout=885.90 (A=027, B=138) +Fout=886.00 (A=028, B=138) +Fout=886.10 (A=029, B=138) +Fout=886.20 (A=030, B=138) +Fout=886.30 (A=031, B=138) +Fout=886.40 (A=032, B=138) +Fout=886.50 (A=033, B=138) +Fout=886.60 (A=034, B=138) +Fout=886.70 (A=035, B=138) +Fout=886.80 (A=036, B=138) +Fout=886.90 (A=037, B=138) +Fout=887.00 (A=038, B=138) +Fout=887.10 (A=039, B=138) +Fout=887.20 (A=040, B=138) +Fout=887.30 (A=041, B=138) +Fout=887.40 (A=042, B=138) +Fout=887.50 (A=043, B=138) +Fout=887.60 (A=044, B=138) +Fout=887.70 (A=045, B=138) +Fout=887.80 (A=046, B=138) +Fout=887.90 (A=047, B=138) +Fout=888.00 (A=048, B=138) +Fout=888.10 (A=049, B=138) +Fout=888.20 (A=050, B=138) +Fout=888.30 (A=051, B=138) +Fout=888.40 (A=052, B=138) +Fout=888.50 (A=053, B=138) +Fout=888.60 (A=054, B=138) +Fout=888.70 (A=055, B=138) +Fout=888.80 (A=056, B=138) +Fout=888.90 (A=057, B=138) +Fout=889.00 (A=058, B=138) +Fout=889.10 (A=059, B=138) +Fout=889.20 (A=060, B=138) +Fout=889.30 (A=061, B=138) +Fout=889.40 (A=062, B=138) +Fout=889.60 (A=000, B=139) +Fout=889.70 (A=001, B=139) +Fout=889.80 (A=002, B=139) +Fout=889.90 (A=003, B=139) +Fout=890.00 (A=004, B=139) +Fout=890.10 (A=005, B=139) +Fout=890.20 (A=006, B=139) +Fout=890.30 (A=007, B=139) +Fout=890.40 (A=008, B=139) +Fout=890.50 (A=009, B=139) +Fout=890.60 (A=010, B=139) +Fout=890.70 (A=011, B=139) +Fout=890.80 (A=012, B=139) +Fout=890.90 (A=013, B=139) +Fout=891.00 (A=014, B=139) +Fout=891.10 (A=015, B=139) +Fout=891.20 (A=016, B=139) +Fout=891.30 (A=017, B=139) +Fout=891.40 (A=018, B=139) +Fout=891.50 (A=019, B=139) +Fout=891.60 (A=020, B=139) +Fout=891.70 (A=021, B=139) +Fout=891.80 (A=022, B=139) +Fout=891.90 (A=023, B=139) +Fout=892.00 (A=024, B=139) +Fout=892.10 (A=025, B=139) +Fout=892.20 (A=026, B=139) +Fout=892.30 (A=027, B=139) +Fout=892.40 (A=028, B=139) +Fout=892.50 (A=029, B=139) +Fout=892.60 (A=030, B=139) +Fout=892.70 (A=031, B=139) +Fout=892.80 (A=032, B=139) +Fout=892.90 (A=033, B=139) +Fout=893.00 (A=034, B=139) +Fout=893.10 (A=035, B=139) +Fout=893.20 (A=036, B=139) +Fout=893.30 (A=037, B=139) +Fout=893.40 (A=038, B=139) +Fout=893.50 (A=039, B=139) +Fout=893.60 (A=040, B=139) +Fout=893.70 (A=041, B=139) +Fout=893.80 (A=042, B=139) +Fout=893.90 (A=043, B=139) +Fout=894.00 (A=044, B=139) +Fout=894.10 (A=045, B=139) +Fout=894.20 (A=046, B=139) +Fout=894.30 (A=047, B=139) +Fout=894.40 (A=048, B=139) +Fout=894.50 (A=049, B=139) +Fout=894.60 (A=050, B=139) +Fout=894.70 (A=051, B=139) +Fout=894.80 (A=052, B=139) +Fout=894.90 (A=053, B=139) +Fout=895.00 (A=054, B=139) +Fout=895.10 (A=055, B=139) +Fout=895.20 (A=056, B=139) +Fout=895.30 (A=057, B=139) +Fout=895.40 (A=058, B=139) +Fout=895.50 (A=059, B=139) +Fout=895.60 (A=060, B=139) +Fout=895.70 (A=061, B=139) +Fout=895.80 (A=062, B=139) +Fout=896.00 (A=000, B=140) +Fout=896.10 (A=001, B=140) +Fout=896.20 (A=002, B=140) +Fout=896.30 (A=003, B=140) +Fout=896.40 (A=004, B=140) +Fout=896.50 (A=005, B=140) +Fout=896.60 (A=006, B=140) +Fout=896.70 (A=007, B=140) +Fout=896.80 (A=008, B=140) +Fout=896.90 (A=009, B=140) +Fout=897.00 (A=010, B=140) +Fout=897.10 (A=011, B=140) +Fout=897.20 (A=012, B=140) +Fout=897.30 (A=013, B=140) +Fout=897.40 (A=014, B=140) +Fout=897.50 (A=015, B=140) +Fout=897.60 (A=016, B=140) +Fout=897.70 (A=017, B=140) +Fout=897.80 (A=018, B=140) +Fout=897.90 (A=019, B=140) +Fout=898.00 (A=020, B=140) +Fout=898.10 (A=021, B=140) +Fout=898.20 (A=022, B=140) +Fout=898.30 (A=023, B=140) +Fout=898.40 (A=024, B=140) +Fout=898.50 (A=025, B=140) +Fout=898.60 (A=026, B=140) +Fout=898.70 (A=027, B=140) +Fout=898.80 (A=028, B=140) +Fout=898.90 (A=029, B=140) +Fout=899.00 (A=030, B=140) +Fout=899.10 (A=031, B=140) +Fout=899.20 (A=032, B=140) +Fout=899.30 (A=033, B=140) +Fout=899.40 (A=034, B=140) +Fout=899.50 (A=035, B=140) +Fout=899.60 (A=036, B=140) +Fout=899.70 (A=037, B=140) +Fout=899.80 (A=038, B=140) +Fout=899.90 (A=039, B=140) +Fout=900.00 (A=040, B=140) +Fout=900.10 (A=041, B=140) +Fout=900.20 (A=042, B=140) +Fout=900.30 (A=043, B=140) +Fout=900.40 (A=044, B=140) +Fout=900.50 (A=045, B=140) +Fout=900.60 (A=046, B=140) +Fout=900.70 (A=047, B=140) +Fout=900.80 (A=048, B=140) +Fout=900.90 (A=049, B=140) +Fout=901.00 (A=050, B=140) +Fout=901.10 (A=051, B=140) +Fout=901.20 (A=052, B=140) +Fout=901.30 (A=053, B=140) +Fout=901.40 (A=054, B=140) +Fout=901.50 (A=055, B=140) +Fout=901.60 (A=056, B=140) +Fout=901.70 (A=057, B=140) +Fout=901.80 (A=058, B=140) +Fout=901.90 (A=059, B=140) +Fout=902.00 (A=060, B=140) +Fout=902.10 (A=061, B=140) +Fout=902.20 (A=062, B=140) +Fout=902.40 (A=000, B=141) +Fout=902.50 (A=001, B=141) +Fout=902.60 (A=002, B=141) +Fout=902.70 (A=003, B=141) +Fout=902.80 (A=004, B=141) +Fout=902.90 (A=005, B=141) +Fout=903.00 (A=006, B=141) +Fout=903.10 (A=007, B=141) +Fout=903.20 (A=008, B=141) +Fout=903.30 (A=009, B=141) +Fout=903.40 (A=010, B=141) +Fout=903.50 (A=011, B=141) +Fout=903.60 (A=012, B=141) +Fout=903.70 (A=013, B=141) +Fout=903.80 (A=014, B=141) +Fout=903.90 (A=015, B=141) +Fout=904.00 (A=016, B=141) +Fout=904.10 (A=017, B=141) +Fout=904.20 (A=018, B=141) +Fout=904.30 (A=019, B=141) +Fout=904.40 (A=020, B=141) +Fout=904.50 (A=021, B=141) +Fout=904.60 (A=022, B=141) +Fout=904.70 (A=023, B=141) +Fout=904.80 (A=024, B=141) +Fout=904.90 (A=025, B=141) +Fout=905.00 (A=026, B=141) +Fout=905.10 (A=027, B=141) +Fout=905.20 (A=028, B=141) +Fout=905.30 (A=029, B=141) +Fout=905.40 (A=030, B=141) +Fout=905.50 (A=031, B=141) +Fout=905.60 (A=032, B=141) +Fout=905.70 (A=033, B=141) +Fout=905.80 (A=034, B=141) +Fout=905.90 (A=035, B=141) +Fout=906.00 (A=036, B=141) +Fout=906.10 (A=037, B=141) +Fout=906.20 (A=038, B=141) +Fout=906.30 (A=039, B=141) +Fout=906.40 (A=040, B=141) +Fout=906.50 (A=041, B=141) +Fout=906.60 (A=042, B=141) +Fout=906.70 (A=043, B=141) +Fout=906.80 (A=044, B=141) +Fout=906.90 (A=045, B=141) +Fout=907.00 (A=046, B=141) +Fout=907.10 (A=047, B=141) +Fout=907.20 (A=048, B=141) +Fout=907.30 (A=049, B=141) +Fout=907.40 (A=050, B=141) +Fout=907.50 (A=051, B=141) +Fout=907.60 (A=052, B=141) +Fout=907.70 (A=053, B=141) +Fout=907.80 (A=054, B=141) +Fout=907.90 (A=055, B=141) +Fout=908.00 (A=056, B=141) +Fout=908.10 (A=057, B=141) +Fout=908.20 (A=058, B=141) +Fout=908.30 (A=059, B=141) +Fout=908.40 (A=060, B=141) +Fout=908.50 (A=061, B=141) +Fout=908.60 (A=062, B=141) +Fout=908.80 (A=000, B=142) +Fout=908.90 (A=001, B=142) +Fout=909.00 (A=002, B=142) +Fout=909.10 (A=003, B=142) +Fout=909.20 (A=004, B=142) +Fout=909.30 (A=005, B=142) +Fout=909.40 (A=006, B=142) +Fout=909.50 (A=007, B=142) +Fout=909.60 (A=008, B=142) +Fout=909.70 (A=009, B=142) +Fout=909.80 (A=010, B=142) +Fout=909.90 (A=011, B=142) +Fout=910.00 (A=012, B=142) +Fout=910.10 (A=013, B=142) +Fout=910.20 (A=014, B=142) +Fout=910.30 (A=015, B=142) +Fout=910.40 (A=016, B=142) +Fout=910.50 (A=017, B=142) +Fout=910.60 (A=018, B=142) +Fout=910.70 (A=019, B=142) +Fout=910.80 (A=020, B=142) +Fout=910.90 (A=021, B=142) +Fout=911.00 (A=022, B=142) +Fout=911.10 (A=023, B=142) +Fout=911.20 (A=024, B=142) +Fout=911.30 (A=025, B=142) +Fout=911.40 (A=026, B=142) +Fout=911.50 (A=027, B=142) +Fout=911.60 (A=028, B=142) +Fout=911.70 (A=029, B=142) +Fout=911.80 (A=030, B=142) +Fout=911.90 (A=031, B=142) +Fout=912.00 (A=032, B=142) +Fout=912.10 (A=033, B=142) +Fout=912.20 (A=034, B=142) +Fout=912.30 (A=035, B=142) +Fout=912.40 (A=036, B=142) +Fout=912.50 (A=037, B=142) +Fout=912.60 (A=038, B=142) +Fout=912.70 (A=039, B=142) +Fout=912.80 (A=040, B=142) +Fout=912.90 (A=041, B=142) +Fout=913.00 (A=042, B=142) +Fout=913.10 (A=043, B=142) +Fout=913.20 (A=044, B=142) +Fout=913.30 (A=045, B=142) +Fout=913.40 (A=046, B=142) +Fout=913.50 (A=047, B=142) +Fout=913.60 (A=048, B=142) +Fout=913.70 (A=049, B=142) +Fout=913.80 (A=050, B=142) +Fout=913.90 (A=051, B=142) +Fout=914.00 (A=052, B=142) +Fout=914.10 (A=053, B=142) +Fout=914.20 (A=054, B=142) +Fout=914.30 (A=055, B=142) +Fout=914.40 (A=056, B=142) +Fout=914.50 (A=057, B=142) +Fout=914.60 (A=058, B=142) +Fout=914.70 (A=059, B=142) +Fout=914.80 (A=060, B=142) +Fout=914.90 (A=061, B=142) +Fout=915.00 (A=062, B=142) +Fout=915.20 (A=000, B=143) +Fout=915.30 (A=001, B=143) +Fout=915.40 (A=002, B=143) +Fout=915.50 (A=003, B=143) +Fout=915.60 (A=004, B=143) +Fout=915.70 (A=005, B=143) +Fout=915.80 (A=006, B=143) +Fout=915.90 (A=007, B=143) +Fout=916.00 (A=008, B=143) +Fout=916.10 (A=009, B=143) +Fout=916.20 (A=010, B=143) +Fout=916.30 (A=011, B=143) +Fout=916.40 (A=012, B=143) +Fout=916.50 (A=013, B=143) +Fout=916.60 (A=014, B=143) +Fout=916.70 (A=015, B=143) +Fout=916.80 (A=016, B=143) +Fout=916.90 (A=017, B=143) +Fout=917.00 (A=018, B=143) +Fout=917.10 (A=019, B=143) +Fout=917.20 (A=020, B=143) +Fout=917.30 (A=021, B=143) +Fout=917.40 (A=022, B=143) +Fout=917.50 (A=023, B=143) +Fout=917.60 (A=024, B=143) +Fout=917.70 (A=025, B=143) +Fout=917.80 (A=026, B=143) +Fout=917.90 (A=027, B=143) +Fout=918.00 (A=028, B=143) +Fout=918.10 (A=029, B=143) +Fout=918.20 (A=030, B=143) +Fout=918.30 (A=031, B=143) +Fout=918.40 (A=032, B=143) +Fout=918.50 (A=033, B=143) +Fout=918.60 (A=034, B=143) +Fout=918.70 (A=035, B=143) +Fout=918.80 (A=036, B=143) +Fout=918.90 (A=037, B=143) +Fout=919.00 (A=038, B=143) +Fout=919.10 (A=039, B=143) +Fout=919.20 (A=040, B=143) +Fout=919.30 (A=041, B=143) +Fout=919.40 (A=042, B=143) +Fout=919.50 (A=043, B=143) +Fout=919.60 (A=044, B=143) +Fout=919.70 (A=045, B=143) +Fout=919.80 (A=046, B=143) +Fout=919.90 (A=047, B=143) +Fout=920.00 (A=048, B=143) +Fout=920.10 (A=049, B=143) +Fout=920.20 (A=050, B=143) +Fout=920.30 (A=051, B=143) +Fout=920.40 (A=052, B=143) +Fout=920.50 (A=053, B=143) +Fout=920.60 (A=054, B=143) +Fout=920.70 (A=055, B=143) +Fout=920.80 (A=056, B=143) +Fout=920.90 (A=057, B=143) +Fout=921.00 (A=058, B=143) +Fout=921.10 (A=059, B=143) +Fout=921.20 (A=060, B=143) +Fout=921.30 (A=061, B=143) +Fout=921.40 (A=062, B=143) +Fout=921.60 (A=000, B=144) +Fout=921.70 (A=001, B=144) +Fout=921.80 (A=002, B=144) +Fout=921.90 (A=003, B=144) +Fout=922.00 (A=004, B=144) +Fout=922.10 (A=005, B=144) +Fout=922.20 (A=006, B=144) +Fout=922.30 (A=007, B=144) +Fout=922.40 (A=008, B=144) +Fout=922.50 (A=009, B=144) +Fout=922.60 (A=010, B=144) +Fout=922.70 (A=011, B=144) +Fout=922.80 (A=012, B=144) +Fout=922.90 (A=013, B=144) +Fout=923.00 (A=014, B=144) +Fout=923.10 (A=015, B=144) +Fout=923.20 (A=016, B=144) +Fout=923.30 (A=017, B=144) +Fout=923.40 (A=018, B=144) +Fout=923.50 (A=019, B=144) +Fout=923.60 (A=020, B=144) +Fout=923.70 (A=021, B=144) +Fout=923.80 (A=022, B=144) +Fout=923.90 (A=023, B=144) +Fout=924.00 (A=024, B=144) +Fout=924.10 (A=025, B=144) +Fout=924.20 (A=026, B=144) +Fout=924.30 (A=027, B=144) +Fout=924.40 (A=028, B=144) +Fout=924.50 (A=029, B=144) +Fout=924.60 (A=030, B=144) +Fout=924.70 (A=031, B=144) +Fout=924.80 (A=032, B=144) +Fout=924.90 (A=033, B=144) +Fout=925.00 (A=034, B=144) +Fout=925.10 (A=035, B=144) +Fout=925.20 (A=036, B=144) +Fout=925.30 (A=037, B=144) +Fout=925.40 (A=038, B=144) +Fout=925.50 (A=039, B=144) +Fout=925.60 (A=040, B=144) +Fout=925.70 (A=041, B=144) +Fout=925.80 (A=042, B=144) +Fout=925.90 (A=043, B=144) +Fout=926.00 (A=044, B=144) +Fout=926.10 (A=045, B=144) +Fout=926.20 (A=046, B=144) +Fout=926.30 (A=047, B=144) +Fout=926.40 (A=048, B=144) +Fout=926.50 (A=049, B=144) +Fout=926.60 (A=050, B=144) +Fout=926.70 (A=051, B=144) +Fout=926.80 (A=052, B=144) +Fout=926.90 (A=053, B=144) +Fout=927.00 (A=054, B=144) +Fout=927.10 (A=055, B=144) +Fout=927.20 (A=056, B=144) +Fout=927.30 (A=057, B=144) +Fout=927.40 (A=058, B=144) +Fout=927.50 (A=059, B=144) +Fout=927.60 (A=060, B=144) +Fout=927.70 (A=061, B=144) +Fout=927.80 (A=062, B=144) +Fout=928.00 (A=000, B=145) +Fout=928.10 (A=001, B=145) +Fout=928.20 (A=002, B=145) +Fout=928.30 (A=003, B=145) +Fout=928.40 (A=004, B=145) +Fout=928.50 (A=005, B=145) +Fout=928.60 (A=006, B=145) +Fout=928.70 (A=007, B=145) +Fout=928.80 (A=008, B=145) +Fout=928.90 (A=009, B=145) +Fout=929.00 (A=010, B=145) +Fout=929.10 (A=011, B=145) +Fout=929.20 (A=012, B=145) +Fout=929.30 (A=013, B=145) +Fout=929.40 (A=014, B=145) +Fout=929.50 (A=015, B=145) +Fout=929.60 (A=016, B=145) +Fout=929.70 (A=017, B=145) +Fout=929.80 (A=018, B=145) +Fout=929.90 (A=019, B=145) +Fout=930.00 (A=020, B=145) +Fout=930.10 (A=021, B=145) +Fout=930.20 (A=022, B=145) +Fout=930.30 (A=023, B=145) +Fout=930.40 (A=024, B=145) +Fout=930.50 (A=025, B=145) +Fout=930.60 (A=026, B=145) +Fout=930.70 (A=027, B=145) +Fout=930.80 (A=028, B=145) +Fout=930.90 (A=029, B=145) +Fout=931.00 (A=030, B=145) +Fout=931.10 (A=031, B=145) +Fout=931.20 (A=032, B=145) +Fout=931.30 (A=033, B=145) +Fout=931.40 (A=034, B=145) +Fout=931.50 (A=035, B=145) +Fout=931.60 (A=036, B=145) +Fout=931.70 (A=037, B=145) +Fout=931.80 (A=038, B=145) +Fout=931.90 (A=039, B=145) +Fout=932.00 (A=040, B=145) +Fout=932.10 (A=041, B=145) +Fout=932.20 (A=042, B=145) +Fout=932.30 (A=043, B=145) +Fout=932.40 (A=044, B=145) +Fout=932.50 (A=045, B=145) +Fout=932.60 (A=046, B=145) +Fout=932.70 (A=047, B=145) +Fout=932.80 (A=048, B=145) +Fout=932.90 (A=049, B=145) +Fout=933.00 (A=050, B=145) +Fout=933.10 (A=051, B=145) +Fout=933.20 (A=052, B=145) +Fout=933.30 (A=053, B=145) +Fout=933.40 (A=054, B=145) +Fout=933.50 (A=055, B=145) +Fout=933.60 (A=056, B=145) +Fout=933.70 (A=057, B=145) +Fout=933.80 (A=058, B=145) +Fout=933.90 (A=059, B=145) +Fout=934.00 (A=060, B=145) +Fout=934.10 (A=061, B=145) +Fout=934.20 (A=062, B=145) +Fout=934.40 (A=000, B=146) +Fout=934.50 (A=001, B=146) +Fout=934.60 (A=002, B=146) +Fout=934.70 (A=003, B=146) +Fout=934.80 (A=004, B=146) +Fout=934.90 (A=005, B=146) +Fout=935.00 (A=006, B=146) +Fout=935.10 (A=007, B=146) +Fout=935.20 (A=008, B=146) +Fout=935.30 (A=009, B=146) +Fout=935.40 (A=010, B=146) +Fout=935.50 (A=011, B=146) +Fout=935.60 (A=012, B=146) +Fout=935.70 (A=013, B=146) +Fout=935.80 (A=014, B=146) +Fout=935.90 (A=015, B=146) +Fout=936.00 (A=016, B=146) +Fout=936.10 (A=017, B=146) +Fout=936.20 (A=018, B=146) +Fout=936.30 (A=019, B=146) +Fout=936.40 (A=020, B=146) +Fout=936.50 (A=021, B=146) +Fout=936.60 (A=022, B=146) +Fout=936.70 (A=023, B=146) +Fout=936.80 (A=024, B=146) +Fout=936.90 (A=025, B=146) +Fout=937.00 (A=026, B=146) +Fout=937.10 (A=027, B=146) +Fout=937.20 (A=028, B=146) +Fout=937.30 (A=029, B=146) +Fout=937.40 (A=030, B=146) +Fout=937.50 (A=031, B=146) +Fout=937.60 (A=032, B=146) +Fout=937.70 (A=033, B=146) +Fout=937.80 (A=034, B=146) +Fout=937.90 (A=035, B=146) +Fout=938.00 (A=036, B=146) +Fout=938.10 (A=037, B=146) +Fout=938.20 (A=038, B=146) +Fout=938.30 (A=039, B=146) +Fout=938.40 (A=040, B=146) +Fout=938.50 (A=041, B=146) +Fout=938.60 (A=042, B=146) +Fout=938.70 (A=043, B=146) +Fout=938.80 (A=044, B=146) +Fout=938.90 (A=045, B=146) +Fout=939.00 (A=046, B=146) +Fout=939.10 (A=047, B=146) +Fout=939.20 (A=048, B=146) +Fout=939.30 (A=049, B=146) +Fout=939.40 (A=050, B=146) +Fout=939.50 (A=051, B=146) +Fout=939.60 (A=052, B=146) +Fout=939.70 (A=053, B=146) +Fout=939.80 (A=054, B=146) +Fout=939.90 (A=055, B=146) +Fout=940.00 (A=056, B=146) +Fout=940.10 (A=057, B=146) +Fout=940.20 (A=058, B=146) +Fout=940.30 (A=059, B=146) +Fout=940.40 (A=060, B=146) +Fout=940.50 (A=061, B=146) +Fout=940.60 (A=062, B=146) +Fout=940.80 (A=000, B=147) +Fout=940.90 (A=001, B=147) +Fout=941.00 (A=002, B=147) +Fout=941.10 (A=003, B=147) +Fout=941.20 (A=004, B=147) +Fout=941.30 (A=005, B=147) +Fout=941.40 (A=006, B=147) +Fout=941.50 (A=007, B=147) +Fout=941.60 (A=008, B=147) +Fout=941.70 (A=009, B=147) +Fout=941.80 (A=010, B=147) +Fout=941.90 (A=011, B=147) +Fout=942.00 (A=012, B=147) +Fout=942.10 (A=013, B=147) +Fout=942.20 (A=014, B=147) +Fout=942.30 (A=015, B=147) +Fout=942.40 (A=016, B=147) +Fout=942.50 (A=017, B=147) +Fout=942.60 (A=018, B=147) +Fout=942.70 (A=019, B=147) +Fout=942.80 (A=020, B=147) +Fout=942.90 (A=021, B=147) +Fout=943.00 (A=022, B=147) +Fout=943.10 (A=023, B=147) +Fout=943.20 (A=024, B=147) +Fout=943.30 (A=025, B=147) +Fout=943.40 (A=026, B=147) +Fout=943.50 (A=027, B=147) +Fout=943.60 (A=028, B=147) +Fout=943.70 (A=029, B=147) +Fout=943.80 (A=030, B=147) +Fout=943.90 (A=031, B=147) +Fout=944.00 (A=032, B=147) +Fout=944.10 (A=033, B=147) +Fout=944.20 (A=034, B=147) +Fout=944.30 (A=035, B=147) +Fout=944.40 (A=036, B=147) +Fout=944.50 (A=037, B=147) +Fout=944.60 (A=038, B=147) +Fout=944.70 (A=039, B=147) +Fout=944.80 (A=040, B=147) +Fout=944.90 (A=041, B=147) +Fout=945.00 (A=042, B=147) +Fout=945.10 (A=043, B=147) +Fout=945.20 (A=044, B=147) +Fout=945.30 (A=045, B=147) +Fout=945.40 (A=046, B=147) +Fout=945.50 (A=047, B=147) +Fout=945.60 (A=048, B=147) +Fout=945.70 (A=049, B=147) +Fout=945.80 (A=050, B=147) +Fout=945.90 (A=051, B=147) +Fout=946.00 (A=052, B=147) +Fout=946.10 (A=053, B=147) +Fout=946.20 (A=054, B=147) +Fout=946.30 (A=055, B=147) +Fout=946.40 (A=056, B=147) +Fout=946.50 (A=057, B=147) +Fout=946.60 (A=058, B=147) +Fout=946.70 (A=059, B=147) +Fout=946.80 (A=060, B=147) +Fout=946.90 (A=061, B=147) +Fout=947.00 (A=062, B=147) +Fout=947.20 (A=000, B=148) +Fout=947.30 (A=001, B=148) +Fout=947.40 (A=002, B=148) +Fout=947.50 (A=003, B=148) +Fout=947.60 (A=004, B=148) +Fout=947.70 (A=005, B=148) +Fout=947.80 (A=006, B=148) +Fout=947.90 (A=007, B=148) +Fout=948.00 (A=008, B=148) +Fout=948.10 (A=009, B=148) +Fout=948.20 (A=010, B=148) +Fout=948.30 (A=011, B=148) +Fout=948.40 (A=012, B=148) +Fout=948.50 (A=013, B=148) +Fout=948.60 (A=014, B=148) +Fout=948.70 (A=015, B=148) +Fout=948.80 (A=016, B=148) +Fout=948.90 (A=017, B=148) +Fout=949.00 (A=018, B=148) +Fout=949.10 (A=019, B=148) +Fout=949.20 (A=020, B=148) +Fout=949.30 (A=021, B=148) +Fout=949.40 (A=022, B=148) +Fout=949.50 (A=023, B=148) +Fout=949.60 (A=024, B=148) +Fout=949.70 (A=025, B=148) +Fout=949.80 (A=026, B=148) +Fout=949.90 (A=027, B=148) +Fout=950.00 (A=028, B=148) +Fout=950.10 (A=029, B=148) +Fout=950.20 (A=030, B=148) +Fout=950.30 (A=031, B=148) +Fout=950.40 (A=032, B=148) +Fout=950.50 (A=033, B=148) +Fout=950.60 (A=034, B=148) +Fout=950.70 (A=035, B=148) +Fout=950.80 (A=036, B=148) +Fout=950.90 (A=037, B=148) +Fout=951.00 (A=038, B=148) +Fout=951.10 (A=039, B=148) +Fout=951.20 (A=040, B=148) +Fout=951.30 (A=041, B=148) +Fout=951.40 (A=042, B=148) +Fout=951.50 (A=043, B=148) +Fout=951.60 (A=044, B=148) +Fout=951.70 (A=045, B=148) +Fout=951.80 (A=046, B=148) +Fout=951.90 (A=047, B=148) +Fout=952.00 (A=048, B=148) +Fout=952.10 (A=049, B=148) +Fout=952.20 (A=050, B=148) +Fout=952.30 (A=051, B=148) +Fout=952.40 (A=052, B=148) +Fout=952.50 (A=053, B=148) +Fout=952.60 (A=054, B=148) +Fout=952.70 (A=055, B=148) +Fout=952.80 (A=056, B=148) +Fout=952.90 (A=057, B=148) +Fout=953.00 (A=058, B=148) +Fout=953.10 (A=059, B=148) +Fout=953.20 (A=060, B=148) +Fout=953.30 (A=061, B=148) +Fout=953.40 (A=062, B=148) +Fout=953.60 (A=000, B=149) +Fout=953.70 (A=001, B=149) +Fout=953.80 (A=002, B=149) +Fout=953.90 (A=003, B=149) +Fout=954.00 (A=004, B=149) +Fout=954.10 (A=005, B=149) +Fout=954.20 (A=006, B=149) +Fout=954.30 (A=007, B=149) +Fout=954.40 (A=008, B=149) +Fout=954.50 (A=009, B=149) +Fout=954.60 (A=010, B=149) +Fout=954.70 (A=011, B=149) +Fout=954.80 (A=012, B=149) +Fout=954.90 (A=013, B=149) +Fout=955.00 (A=014, B=149) +Fout=955.10 (A=015, B=149) +Fout=955.20 (A=016, B=149) +Fout=955.30 (A=017, B=149) +Fout=955.40 (A=018, B=149) +Fout=955.50 (A=019, B=149) +Fout=955.60 (A=020, B=149) +Fout=955.70 (A=021, B=149) +Fout=955.80 (A=022, B=149) +Fout=955.90 (A=023, B=149) +Fout=956.00 (A=024, B=149) +Fout=956.10 (A=025, B=149) +Fout=956.20 (A=026, B=149) +Fout=956.30 (A=027, B=149) +Fout=956.40 (A=028, B=149) +Fout=956.50 (A=029, B=149) +Fout=956.60 (A=030, B=149) +Fout=956.70 (A=031, B=149) +Fout=956.80 (A=032, B=149) +Fout=956.90 (A=033, B=149) +Fout=957.00 (A=034, B=149) +Fout=957.10 (A=035, B=149) +Fout=957.20 (A=036, B=149) +Fout=957.30 (A=037, B=149) +Fout=957.40 (A=038, B=149) +Fout=957.50 (A=039, B=149) +Fout=957.60 (A=040, B=149) +Fout=957.70 (A=041, B=149) +Fout=957.80 (A=042, B=149) +Fout=957.90 (A=043, B=149) +Fout=958.00 (A=044, B=149) +Fout=958.10 (A=045, B=149) +Fout=958.20 (A=046, B=149) +Fout=958.30 (A=047, B=149) +Fout=958.40 (A=048, B=149) +Fout=958.50 (A=049, B=149) +Fout=958.60 (A=050, B=149) +Fout=958.70 (A=051, B=149) +Fout=958.80 (A=052, B=149) +Fout=958.90 (A=053, B=149) +Fout=959.00 (A=054, B=149) +Fout=959.10 (A=055, B=149) +Fout=959.20 (A=056, B=149) +Fout=959.30 (A=057, B=149) +Fout=959.40 (A=058, B=149) +Fout=959.50 (A=059, B=149) +Fout=959.60 (A=060, B=149) +Fout=959.70 (A=061, B=149) +Fout=959.80 (A=062, B=149) +Fout=960.00 (A=000, B=150) +Fout=960.10 (A=001, B=150) +Fout=960.20 (A=002, B=150) +Fout=960.30 (A=003, B=150) +Fout=960.40 (A=004, B=150) +Fout=960.50 (A=005, B=150) +Fout=960.60 (A=006, B=150) +Fout=960.70 (A=007, B=150) +Fout=960.80 (A=008, B=150) +Fout=960.90 (A=009, B=150) +Fout=961.00 (A=010, B=150) +Fout=961.10 (A=011, B=150) +Fout=961.20 (A=012, B=150) +Fout=961.30 (A=013, B=150) +Fout=961.40 (A=014, B=150) +Fout=961.50 (A=015, B=150) +Fout=961.60 (A=016, B=150) +Fout=961.70 (A=017, B=150) +Fout=961.80 (A=018, B=150) +Fout=961.90 (A=019, B=150) +Fout=962.00 (A=020, B=150) +Fout=962.10 (A=021, B=150) +Fout=962.20 (A=022, B=150) +Fout=962.30 (A=023, B=150) +Fout=962.40 (A=024, B=150) +Fout=962.50 (A=025, B=150) +Fout=962.60 (A=026, B=150) +Fout=962.70 (A=027, B=150) +Fout=962.80 (A=028, B=150) +Fout=962.90 (A=029, B=150) +Fout=963.00 (A=030, B=150) +Fout=963.10 (A=031, B=150) +Fout=963.20 (A=032, B=150) +Fout=963.30 (A=033, B=150) +Fout=963.40 (A=034, B=150) +Fout=963.50 (A=035, B=150) +Fout=963.60 (A=036, B=150) +Fout=963.70 (A=037, B=150) +Fout=963.80 (A=038, B=150) +Fout=963.90 (A=039, B=150) +Fout=964.00 (A=040, B=150) +Fout=964.10 (A=041, B=150) +Fout=964.20 (A=042, B=150) +Fout=964.30 (A=043, B=150) +Fout=964.40 (A=044, B=150) +Fout=964.50 (A=045, B=150) +Fout=964.60 (A=046, B=150) +Fout=964.70 (A=047, B=150) +Fout=964.80 (A=048, B=150) +Fout=964.90 (A=049, B=150) +Fout=965.00 (A=050, B=150) +Fout=965.10 (A=051, B=150) +Fout=965.20 (A=052, B=150) +Fout=965.30 (A=053, B=150) +Fout=965.40 (A=054, B=150) +Fout=965.50 (A=055, B=150) +Fout=965.60 (A=056, B=150) +Fout=965.70 (A=057, B=150) +Fout=965.80 (A=058, B=150) +Fout=965.90 (A=059, B=150) +Fout=966.00 (A=060, B=150) +Fout=966.10 (A=061, B=150) +Fout=966.20 (A=062, B=150) +====================================================================== +PLL Rx High Band: +Fout=1804.80 (A=000, B=141) +Fout=1805.00 (A=001, B=141) +Fout=1805.20 (A=002, B=141) +Fout=1805.40 (A=003, B=141) +Fout=1805.60 (A=004, B=141) +Fout=1805.80 (A=005, B=141) +Fout=1806.00 (A=006, B=141) +Fout=1806.20 (A=007, B=141) +Fout=1806.40 (A=008, B=141) +Fout=1806.60 (A=009, B=141) +Fout=1806.80 (A=010, B=141) +Fout=1807.00 (A=011, B=141) +Fout=1807.20 (A=012, B=141) +Fout=1807.40 (A=013, B=141) +Fout=1807.60 (A=014, B=141) +Fout=1807.80 (A=015, B=141) +Fout=1808.00 (A=016, B=141) +Fout=1808.20 (A=017, B=141) +Fout=1808.40 (A=018, B=141) +Fout=1808.60 (A=019, B=141) +Fout=1808.80 (A=020, B=141) +Fout=1809.00 (A=021, B=141) +Fout=1809.20 (A=022, B=141) +Fout=1809.40 (A=023, B=141) +Fout=1809.60 (A=024, B=141) +Fout=1809.80 (A=025, B=141) +Fout=1810.00 (A=026, B=141) +Fout=1810.20 (A=027, B=141) +Fout=1810.40 (A=028, B=141) +Fout=1810.60 (A=029, B=141) +Fout=1810.80 (A=030, B=141) +Fout=1811.00 (A=031, B=141) +Fout=1811.20 (A=032, B=141) +Fout=1811.40 (A=033, B=141) +Fout=1811.60 (A=034, B=141) +Fout=1811.80 (A=035, B=141) +Fout=1812.00 (A=036, B=141) +Fout=1812.20 (A=037, B=141) +Fout=1812.40 (A=038, B=141) +Fout=1812.60 (A=039, B=141) +Fout=1812.80 (A=040, B=141) +Fout=1813.00 (A=041, B=141) +Fout=1813.20 (A=042, B=141) +Fout=1813.40 (A=043, B=141) +Fout=1813.60 (A=044, B=141) +Fout=1813.80 (A=045, B=141) +Fout=1814.00 (A=046, B=141) +Fout=1814.20 (A=047, B=141) +Fout=1814.40 (A=048, B=141) +Fout=1814.60 (A=049, B=141) +Fout=1814.80 (A=050, B=141) +Fout=1815.00 (A=051, B=141) +Fout=1815.20 (A=052, B=141) +Fout=1815.40 (A=053, B=141) +Fout=1815.60 (A=054, B=141) +Fout=1815.80 (A=055, B=141) +Fout=1816.00 (A=056, B=141) +Fout=1816.20 (A=057, B=141) +Fout=1816.40 (A=058, B=141) +Fout=1816.60 (A=059, B=141) +Fout=1816.80 (A=060, B=141) +Fout=1817.00 (A=061, B=141) +Fout=1817.20 (A=062, B=141) +Fout=1817.60 (A=000, B=142) +Fout=1817.80 (A=001, B=142) +Fout=1818.00 (A=002, B=142) +Fout=1818.20 (A=003, B=142) +Fout=1818.40 (A=004, B=142) +Fout=1818.60 (A=005, B=142) +Fout=1818.80 (A=006, B=142) +Fout=1819.00 (A=007, B=142) +Fout=1819.20 (A=008, B=142) +Fout=1819.40 (A=009, B=142) +Fout=1819.60 (A=010, B=142) +Fout=1819.80 (A=011, B=142) +Fout=1820.00 (A=012, B=142) +Fout=1820.20 (A=013, B=142) +Fout=1820.40 (A=014, B=142) +Fout=1820.60 (A=015, B=142) +Fout=1820.80 (A=016, B=142) +Fout=1821.00 (A=017, B=142) +Fout=1821.20 (A=018, B=142) +Fout=1821.40 (A=019, B=142) +Fout=1821.60 (A=020, B=142) +Fout=1821.80 (A=021, B=142) +Fout=1822.00 (A=022, B=142) +Fout=1822.20 (A=023, B=142) +Fout=1822.40 (A=024, B=142) +Fout=1822.60 (A=025, B=142) +Fout=1822.80 (A=026, B=142) +Fout=1823.00 (A=027, B=142) +Fout=1823.20 (A=028, B=142) +Fout=1823.40 (A=029, B=142) +Fout=1823.60 (A=030, B=142) +Fout=1823.80 (A=031, B=142) +Fout=1824.00 (A=032, B=142) +Fout=1824.20 (A=033, B=142) +Fout=1824.40 (A=034, B=142) +Fout=1824.60 (A=035, B=142) +Fout=1824.80 (A=036, B=142) +Fout=1825.00 (A=037, B=142) +Fout=1825.20 (A=038, B=142) +Fout=1825.40 (A=039, B=142) +Fout=1825.60 (A=040, B=142) +Fout=1825.80 (A=041, B=142) +Fout=1826.00 (A=042, B=142) +Fout=1826.20 (A=043, B=142) +Fout=1826.40 (A=044, B=142) +Fout=1826.60 (A=045, B=142) +Fout=1826.80 (A=046, B=142) +Fout=1827.00 (A=047, B=142) +Fout=1827.20 (A=048, B=142) +Fout=1827.40 (A=049, B=142) +Fout=1827.60 (A=050, B=142) +Fout=1827.80 (A=051, B=142) +Fout=1828.00 (A=052, B=142) +Fout=1828.20 (A=053, B=142) +Fout=1828.40 (A=054, B=142) +Fout=1828.60 (A=055, B=142) +Fout=1828.80 (A=056, B=142) +Fout=1829.00 (A=057, B=142) +Fout=1829.20 (A=058, B=142) +Fout=1829.40 (A=059, B=142) +Fout=1829.60 (A=060, B=142) +Fout=1829.80 (A=061, B=142) +Fout=1830.00 (A=062, B=142) +Fout=1830.40 (A=000, B=143) +Fout=1830.60 (A=001, B=143) +Fout=1830.80 (A=002, B=143) +Fout=1831.00 (A=003, B=143) +Fout=1831.20 (A=004, B=143) +Fout=1831.40 (A=005, B=143) +Fout=1831.60 (A=006, B=143) +Fout=1831.80 (A=007, B=143) +Fout=1832.00 (A=008, B=143) +Fout=1832.20 (A=009, B=143) +Fout=1832.40 (A=010, B=143) +Fout=1832.60 (A=011, B=143) +Fout=1832.80 (A=012, B=143) +Fout=1833.00 (A=013, B=143) +Fout=1833.20 (A=014, B=143) +Fout=1833.40 (A=015, B=143) +Fout=1833.60 (A=016, B=143) +Fout=1833.80 (A=017, B=143) +Fout=1834.00 (A=018, B=143) +Fout=1834.20 (A=019, B=143) +Fout=1834.40 (A=020, B=143) +Fout=1834.60 (A=021, B=143) +Fout=1834.80 (A=022, B=143) +Fout=1835.00 (A=023, B=143) +Fout=1835.20 (A=024, B=143) +Fout=1835.40 (A=025, B=143) +Fout=1835.60 (A=026, B=143) +Fout=1835.80 (A=027, B=143) +Fout=1836.00 (A=028, B=143) +Fout=1836.20 (A=029, B=143) +Fout=1836.40 (A=030, B=143) +Fout=1836.60 (A=031, B=143) +Fout=1836.80 (A=032, B=143) +Fout=1837.00 (A=033, B=143) +Fout=1837.20 (A=034, B=143) +Fout=1837.40 (A=035, B=143) +Fout=1837.60 (A=036, B=143) +Fout=1837.80 (A=037, B=143) +Fout=1838.00 (A=038, B=143) +Fout=1838.20 (A=039, B=143) +Fout=1838.40 (A=040, B=143) +Fout=1838.60 (A=041, B=143) +Fout=1838.80 (A=042, B=143) +Fout=1839.00 (A=043, B=143) +Fout=1839.20 (A=044, B=143) +Fout=1839.40 (A=045, B=143) +Fout=1839.60 (A=046, B=143) +Fout=1839.80 (A=047, B=143) +Fout=1840.00 (A=048, B=143) +Fout=1840.20 (A=049, B=143) +Fout=1840.40 (A=050, B=143) +Fout=1840.60 (A=051, B=143) +Fout=1840.80 (A=052, B=143) +Fout=1841.00 (A=053, B=143) +Fout=1841.20 (A=054, B=143) +Fout=1841.40 (A=055, B=143) +Fout=1841.60 (A=056, B=143) +Fout=1841.80 (A=057, B=143) +Fout=1842.00 (A=058, B=143) +Fout=1842.20 (A=059, B=143) +Fout=1842.40 (A=060, B=143) +Fout=1842.60 (A=061, B=143) +Fout=1842.80 (A=062, B=143) +Fout=1843.20 (A=000, B=144) +Fout=1843.40 (A=001, B=144) +Fout=1843.60 (A=002, B=144) +Fout=1843.80 (A=003, B=144) +Fout=1844.00 (A=004, B=144) +Fout=1844.20 (A=005, B=144) +Fout=1844.40 (A=006, B=144) +Fout=1844.60 (A=007, B=144) +Fout=1844.80 (A=008, B=144) +Fout=1845.00 (A=009, B=144) +Fout=1845.20 (A=010, B=144) +Fout=1845.40 (A=011, B=144) +Fout=1845.60 (A=012, B=144) +Fout=1845.80 (A=013, B=144) +Fout=1846.00 (A=014, B=144) +Fout=1846.20 (A=015, B=144) +Fout=1846.40 (A=016, B=144) +Fout=1846.60 (A=017, B=144) +Fout=1846.80 (A=018, B=144) +Fout=1847.00 (A=019, B=144) +Fout=1847.20 (A=020, B=144) +Fout=1847.40 (A=021, B=144) +Fout=1847.60 (A=022, B=144) +Fout=1847.80 (A=023, B=144) +Fout=1848.00 (A=024, B=144) +Fout=1848.20 (A=025, B=144) +Fout=1848.40 (A=026, B=144) +Fout=1848.60 (A=027, B=144) +Fout=1848.80 (A=028, B=144) +Fout=1849.00 (A=029, B=144) +Fout=1849.20 (A=030, B=144) +Fout=1849.40 (A=031, B=144) +Fout=1849.60 (A=032, B=144) +Fout=1849.80 (A=033, B=144) +Fout=1850.00 (A=034, B=144) +Fout=1850.20 (A=035, B=144) +Fout=1850.40 (A=036, B=144) +Fout=1850.60 (A=037, B=144) +Fout=1850.80 (A=038, B=144) +Fout=1851.00 (A=039, B=144) +Fout=1851.20 (A=040, B=144) +Fout=1851.40 (A=041, B=144) +Fout=1851.60 (A=042, B=144) +Fout=1851.80 (A=043, B=144) +Fout=1852.00 (A=044, B=144) +Fout=1852.20 (A=045, B=144) +Fout=1852.40 (A=046, B=144) +Fout=1852.60 (A=047, B=144) +Fout=1852.80 (A=048, B=144) +Fout=1853.00 (A=049, B=144) +Fout=1853.20 (A=050, B=144) +Fout=1853.40 (A=051, B=144) +Fout=1853.60 (A=052, B=144) +Fout=1853.80 (A=053, B=144) +Fout=1854.00 (A=054, B=144) +Fout=1854.20 (A=055, B=144) +Fout=1854.40 (A=056, B=144) +Fout=1854.60 (A=057, B=144) +Fout=1854.80 (A=058, B=144) +Fout=1855.00 (A=059, B=144) +Fout=1855.20 (A=060, B=144) +Fout=1855.40 (A=061, B=144) +Fout=1855.60 (A=062, B=144) +Fout=1856.00 (A=000, B=145) +Fout=1856.20 (A=001, B=145) +Fout=1856.40 (A=002, B=145) +Fout=1856.60 (A=003, B=145) +Fout=1856.80 (A=004, B=145) +Fout=1857.00 (A=005, B=145) +Fout=1857.20 (A=006, B=145) +Fout=1857.40 (A=007, B=145) +Fout=1857.60 (A=008, B=145) +Fout=1857.80 (A=009, B=145) +Fout=1858.00 (A=010, B=145) +Fout=1858.20 (A=011, B=145) +Fout=1858.40 (A=012, B=145) +Fout=1858.60 (A=013, B=145) +Fout=1858.80 (A=014, B=145) +Fout=1859.00 (A=015, B=145) +Fout=1859.20 (A=016, B=145) +Fout=1859.40 (A=017, B=145) +Fout=1859.60 (A=018, B=145) +Fout=1859.80 (A=019, B=145) +Fout=1860.00 (A=020, B=145) +Fout=1860.20 (A=021, B=145) +Fout=1860.40 (A=022, B=145) +Fout=1860.60 (A=023, B=145) +Fout=1860.80 (A=024, B=145) +Fout=1861.00 (A=025, B=145) +Fout=1861.20 (A=026, B=145) +Fout=1861.40 (A=027, B=145) +Fout=1861.60 (A=028, B=145) +Fout=1861.80 (A=029, B=145) +Fout=1862.00 (A=030, B=145) +Fout=1862.20 (A=031, B=145) +Fout=1862.40 (A=032, B=145) +Fout=1862.60 (A=033, B=145) +Fout=1862.80 (A=034, B=145) +Fout=1863.00 (A=035, B=145) +Fout=1863.20 (A=036, B=145) +Fout=1863.40 (A=037, B=145) +Fout=1863.60 (A=038, B=145) +Fout=1863.80 (A=039, B=145) +Fout=1864.00 (A=040, B=145) +Fout=1864.20 (A=041, B=145) +Fout=1864.40 (A=042, B=145) +Fout=1864.60 (A=043, B=145) +Fout=1864.80 (A=044, B=145) +Fout=1865.00 (A=045, B=145) +Fout=1865.20 (A=046, B=145) +Fout=1865.40 (A=047, B=145) +Fout=1865.60 (A=048, B=145) +Fout=1865.80 (A=049, B=145) +Fout=1866.00 (A=050, B=145) +Fout=1866.20 (A=051, B=145) +Fout=1866.40 (A=052, B=145) +Fout=1866.60 (A=053, B=145) +Fout=1866.80 (A=054, B=145) +Fout=1867.00 (A=055, B=145) +Fout=1867.20 (A=056, B=145) +Fout=1867.40 (A=057, B=145) +Fout=1867.60 (A=058, B=145) +Fout=1867.80 (A=059, B=145) +Fout=1868.00 (A=060, B=145) +Fout=1868.20 (A=061, B=145) +Fout=1868.40 (A=062, B=145) +Fout=1868.80 (A=000, B=146) +Fout=1869.00 (A=001, B=146) +Fout=1869.20 (A=002, B=146) +Fout=1869.40 (A=003, B=146) +Fout=1869.60 (A=004, B=146) +Fout=1869.80 (A=005, B=146) +Fout=1870.00 (A=006, B=146) +Fout=1870.20 (A=007, B=146) +Fout=1870.40 (A=008, B=146) +Fout=1870.60 (A=009, B=146) +Fout=1870.80 (A=010, B=146) +Fout=1871.00 (A=011, B=146) +Fout=1871.20 (A=012, B=146) +Fout=1871.40 (A=013, B=146) +Fout=1871.60 (A=014, B=146) +Fout=1871.80 (A=015, B=146) +Fout=1872.00 (A=016, B=146) +Fout=1872.20 (A=017, B=146) +Fout=1872.40 (A=018, B=146) +Fout=1872.60 (A=019, B=146) +Fout=1872.80 (A=020, B=146) +Fout=1873.00 (A=021, B=146) +Fout=1873.20 (A=022, B=146) +Fout=1873.40 (A=023, B=146) +Fout=1873.60 (A=024, B=146) +Fout=1873.80 (A=025, B=146) +Fout=1874.00 (A=026, B=146) +Fout=1874.20 (A=027, B=146) +Fout=1874.40 (A=028, B=146) +Fout=1874.60 (A=029, B=146) +Fout=1874.80 (A=030, B=146) +Fout=1875.00 (A=031, B=146) +Fout=1875.20 (A=032, B=146) +Fout=1875.40 (A=033, B=146) +Fout=1875.60 (A=034, B=146) +Fout=1875.80 (A=035, B=146) +Fout=1876.00 (A=036, B=146) +Fout=1876.20 (A=037, B=146) +Fout=1876.40 (A=038, B=146) +Fout=1876.60 (A=039, B=146) +Fout=1876.80 (A=040, B=146) +Fout=1877.00 (A=041, B=146) +Fout=1877.20 (A=042, B=146) +Fout=1877.40 (A=043, B=146) +Fout=1877.60 (A=044, B=146) +Fout=1877.80 (A=045, B=146) +Fout=1878.00 (A=046, B=146) +Fout=1878.20 (A=047, B=146) +Fout=1878.40 (A=048, B=146) +Fout=1878.60 (A=049, B=146) +Fout=1878.80 (A=050, B=146) +Fout=1879.00 (A=051, B=146) +Fout=1879.20 (A=052, B=146) +Fout=1879.40 (A=053, B=146) +Fout=1879.60 (A=054, B=146) +Fout=1879.80 (A=055, B=146) +Fout=1880.00 (A=056, B=146) +Fout=1880.20 (A=057, B=146) +Fout=1880.40 (A=058, B=146) +Fout=1880.60 (A=059, B=146) +Fout=1880.80 (A=060, B=146) +Fout=1881.00 (A=061, B=146) +Fout=1881.20 (A=062, B=146) +Fout=1881.60 (A=000, B=147) +Fout=1881.80 (A=001, B=147) +Fout=1882.00 (A=002, B=147) +Fout=1882.20 (A=003, B=147) +Fout=1882.40 (A=004, B=147) +Fout=1882.60 (A=005, B=147) +Fout=1882.80 (A=006, B=147) +Fout=1883.00 (A=007, B=147) +Fout=1883.20 (A=008, B=147) +Fout=1883.40 (A=009, B=147) +Fout=1883.60 (A=010, B=147) +Fout=1883.80 (A=011, B=147) +Fout=1884.00 (A=012, B=147) +Fout=1884.20 (A=013, B=147) +Fout=1884.40 (A=014, B=147) +Fout=1884.60 (A=015, B=147) +Fout=1884.80 (A=016, B=147) +Fout=1885.00 (A=017, B=147) +Fout=1885.20 (A=018, B=147) +Fout=1885.40 (A=019, B=147) +Fout=1885.60 (A=020, B=147) +Fout=1885.80 (A=021, B=147) +Fout=1886.00 (A=022, B=147) +Fout=1886.20 (A=023, B=147) +Fout=1886.40 (A=024, B=147) +Fout=1886.60 (A=025, B=147) +Fout=1886.80 (A=026, B=147) +Fout=1887.00 (A=027, B=147) +Fout=1887.20 (A=028, B=147) +Fout=1887.40 (A=029, B=147) +Fout=1887.60 (A=030, B=147) +Fout=1887.80 (A=031, B=147) +Fout=1888.00 (A=032, B=147) +Fout=1888.20 (A=033, B=147) +Fout=1888.40 (A=034, B=147) +Fout=1888.60 (A=035, B=147) +Fout=1888.80 (A=036, B=147) +Fout=1889.00 (A=037, B=147) +Fout=1889.20 (A=038, B=147) +Fout=1889.40 (A=039, B=147) +Fout=1889.60 (A=040, B=147) +Fout=1889.80 (A=041, B=147) +Fout=1890.00 (A=042, B=147) +Fout=1890.20 (A=043, B=147) +Fout=1890.40 (A=044, B=147) +Fout=1890.60 (A=045, B=147) +Fout=1890.80 (A=046, B=147) +Fout=1891.00 (A=047, B=147) +Fout=1891.20 (A=048, B=147) +Fout=1891.40 (A=049, B=147) +Fout=1891.60 (A=050, B=147) +Fout=1891.80 (A=051, B=147) +Fout=1892.00 (A=052, B=147) +Fout=1892.20 (A=053, B=147) +Fout=1892.40 (A=054, B=147) +Fout=1892.60 (A=055, B=147) +Fout=1892.80 (A=056, B=147) +Fout=1893.00 (A=057, B=147) +Fout=1893.20 (A=058, B=147) +Fout=1893.40 (A=059, B=147) +Fout=1893.60 (A=060, B=147) +Fout=1893.80 (A=061, B=147) +Fout=1894.00 (A=062, B=147) +Fout=1894.40 (A=000, B=148) +Fout=1894.60 (A=001, B=148) +Fout=1894.80 (A=002, B=148) +Fout=1895.00 (A=003, B=148) +Fout=1895.20 (A=004, B=148) +Fout=1895.40 (A=005, B=148) +Fout=1895.60 (A=006, B=148) +Fout=1895.80 (A=007, B=148) +Fout=1896.00 (A=008, B=148) +Fout=1896.20 (A=009, B=148) +Fout=1896.40 (A=010, B=148) +Fout=1896.60 (A=011, B=148) +Fout=1896.80 (A=012, B=148) +Fout=1897.00 (A=013, B=148) +Fout=1897.20 (A=014, B=148) +Fout=1897.40 (A=015, B=148) +Fout=1897.60 (A=016, B=148) +Fout=1897.80 (A=017, B=148) +Fout=1898.00 (A=018, B=148) +Fout=1898.20 (A=019, B=148) +Fout=1898.40 (A=020, B=148) +Fout=1898.60 (A=021, B=148) +Fout=1898.80 (A=022, B=148) +Fout=1899.00 (A=023, B=148) +Fout=1899.20 (A=024, B=148) +Fout=1899.40 (A=025, B=148) +Fout=1899.60 (A=026, B=148) +Fout=1899.80 (A=027, B=148) +Fout=1900.00 (A=028, B=148) +Fout=1900.20 (A=029, B=148) +Fout=1900.40 (A=030, B=148) +Fout=1900.60 (A=031, B=148) +Fout=1900.80 (A=032, B=148) +Fout=1901.00 (A=033, B=148) +Fout=1901.20 (A=034, B=148) +Fout=1901.40 (A=035, B=148) +Fout=1901.60 (A=036, B=148) +Fout=1901.80 (A=037, B=148) +Fout=1902.00 (A=038, B=148) +Fout=1902.20 (A=039, B=148) +Fout=1902.40 (A=040, B=148) +Fout=1902.60 (A=041, B=148) +Fout=1902.80 (A=042, B=148) +Fout=1903.00 (A=043, B=148) +Fout=1903.20 (A=044, B=148) +Fout=1903.40 (A=045, B=148) +Fout=1903.60 (A=046, B=148) +Fout=1903.80 (A=047, B=148) +Fout=1904.00 (A=048, B=148) +Fout=1904.20 (A=049, B=148) +Fout=1904.40 (A=050, B=148) +Fout=1904.60 (A=051, B=148) +Fout=1904.80 (A=052, B=148) +Fout=1905.00 (A=053, B=148) +Fout=1905.20 (A=054, B=148) +Fout=1905.40 (A=055, B=148) +Fout=1905.60 (A=056, B=148) +Fout=1905.80 (A=057, B=148) +Fout=1906.00 (A=058, B=148) +Fout=1906.20 (A=059, B=148) +Fout=1906.40 (A=060, B=148) +Fout=1906.60 (A=061, B=148) +Fout=1906.80 (A=062, B=148) +Fout=1907.20 (A=000, B=149) +Fout=1907.40 (A=001, B=149) +Fout=1907.60 (A=002, B=149) +Fout=1907.80 (A=003, B=149) +Fout=1908.00 (A=004, B=149) +Fout=1908.20 (A=005, B=149) +Fout=1908.40 (A=006, B=149) +Fout=1908.60 (A=007, B=149) +Fout=1908.80 (A=008, B=149) +Fout=1909.00 (A=009, B=149) +Fout=1909.20 (A=010, B=149) +Fout=1909.40 (A=011, B=149) +Fout=1909.60 (A=012, B=149) +Fout=1909.80 (A=013, B=149) +Fout=1910.00 (A=014, B=149) +Fout=1910.20 (A=015, B=149) +Fout=1910.40 (A=016, B=149) +Fout=1910.60 (A=017, B=149) +Fout=1910.80 (A=018, B=149) +Fout=1911.00 (A=019, B=149) +Fout=1911.20 (A=020, B=149) +Fout=1911.40 (A=021, B=149) +Fout=1911.60 (A=022, B=149) +Fout=1911.80 (A=023, B=149) +Fout=1912.00 (A=024, B=149) +Fout=1912.20 (A=025, B=149) +Fout=1912.40 (A=026, B=149) +Fout=1912.60 (A=027, B=149) +Fout=1912.80 (A=028, B=149) +Fout=1913.00 (A=029, B=149) +Fout=1913.20 (A=030, B=149) +Fout=1913.40 (A=031, B=149) +Fout=1913.60 (A=032, B=149) +Fout=1913.80 (A=033, B=149) +Fout=1914.00 (A=034, B=149) +Fout=1914.20 (A=035, B=149) +Fout=1914.40 (A=036, B=149) +Fout=1914.60 (A=037, B=149) +Fout=1914.80 (A=038, B=149) +Fout=1915.00 (A=039, B=149) +Fout=1915.20 (A=040, B=149) +Fout=1915.40 (A=041, B=149) +Fout=1915.60 (A=042, B=149) +Fout=1915.80 (A=043, B=149) +Fout=1916.00 (A=044, B=149) +Fout=1916.20 (A=045, B=149) +Fout=1916.40 (A=046, B=149) +Fout=1916.60 (A=047, B=149) +Fout=1916.80 (A=048, B=149) +Fout=1917.00 (A=049, B=149) +Fout=1917.20 (A=050, B=149) +Fout=1917.40 (A=051, B=149) +Fout=1917.60 (A=052, B=149) +Fout=1917.80 (A=053, B=149) +Fout=1918.00 (A=054, B=149) +Fout=1918.20 (A=055, B=149) +Fout=1918.40 (A=056, B=149) +Fout=1918.60 (A=057, B=149) +Fout=1918.80 (A=058, B=149) +Fout=1919.00 (A=059, B=149) +Fout=1919.20 (A=060, B=149) +Fout=1919.40 (A=061, B=149) +Fout=1919.60 (A=062, B=149) +Fout=1920.00 (A=000, B=150) +Fout=1920.20 (A=001, B=150) +Fout=1920.40 (A=002, B=150) +Fout=1920.60 (A=003, B=150) +Fout=1920.80 (A=004, B=150) +Fout=1921.00 (A=005, B=150) +Fout=1921.20 (A=006, B=150) +Fout=1921.40 (A=007, B=150) +Fout=1921.60 (A=008, B=150) +Fout=1921.80 (A=009, B=150) +Fout=1922.00 (A=010, B=150) +Fout=1922.20 (A=011, B=150) +Fout=1922.40 (A=012, B=150) +Fout=1922.60 (A=013, B=150) +Fout=1922.80 (A=014, B=150) +Fout=1923.00 (A=015, B=150) +Fout=1923.20 (A=016, B=150) +Fout=1923.40 (A=017, B=150) +Fout=1923.60 (A=018, B=150) +Fout=1923.80 (A=019, B=150) +Fout=1924.00 (A=020, B=150) +Fout=1924.20 (A=021, B=150) +Fout=1924.40 (A=022, B=150) +Fout=1924.60 (A=023, B=150) +Fout=1924.80 (A=024, B=150) +Fout=1925.00 (A=025, B=150) +Fout=1925.20 (A=026, B=150) +Fout=1925.40 (A=027, B=150) +Fout=1925.60 (A=028, B=150) +Fout=1925.80 (A=029, B=150) +Fout=1926.00 (A=030, B=150) +Fout=1926.20 (A=031, B=150) +Fout=1926.40 (A=032, B=150) +Fout=1926.60 (A=033, B=150) +Fout=1926.80 (A=034, B=150) +Fout=1927.00 (A=035, B=150) +Fout=1927.20 (A=036, B=150) +Fout=1927.40 (A=037, B=150) +Fout=1927.60 (A=038, B=150) +Fout=1927.80 (A=039, B=150) +Fout=1928.00 (A=040, B=150) +Fout=1928.20 (A=041, B=150) +Fout=1928.40 (A=042, B=150) +Fout=1928.60 (A=043, B=150) +Fout=1928.80 (A=044, B=150) +Fout=1929.00 (A=045, B=150) +Fout=1929.20 (A=046, B=150) +Fout=1929.40 (A=047, B=150) +Fout=1929.60 (A=048, B=150) +Fout=1929.80 (A=049, B=150) +Fout=1930.00 (A=050, B=150) +Fout=1930.20 (A=051, B=150) +Fout=1930.40 (A=052, B=150) +Fout=1930.60 (A=053, B=150) +Fout=1930.80 (A=054, B=150) +Fout=1931.00 (A=055, B=150) +Fout=1931.20 (A=056, B=150) +Fout=1931.40 (A=057, B=150) +Fout=1931.60 (A=058, B=150) +Fout=1931.80 (A=059, B=150) +Fout=1932.00 (A=060, B=150) +Fout=1932.20 (A=061, B=150) +Fout=1932.40 (A=062, B=150) +Fout=1932.80 (A=000, B=151) +Fout=1933.00 (A=001, B=151) +Fout=1933.20 (A=002, B=151) +Fout=1933.40 (A=003, B=151) +Fout=1933.60 (A=004, B=151) +Fout=1933.80 (A=005, B=151) +Fout=1934.00 (A=006, B=151) +Fout=1934.20 (A=007, B=151) +Fout=1934.40 (A=008, B=151) +Fout=1934.60 (A=009, B=151) +Fout=1934.80 (A=010, B=151) +Fout=1935.00 (A=011, B=151) +Fout=1935.20 (A=012, B=151) +Fout=1935.40 (A=013, B=151) +Fout=1935.60 (A=014, B=151) +Fout=1935.80 (A=015, B=151) +Fout=1936.00 (A=016, B=151) +Fout=1936.20 (A=017, B=151) +Fout=1936.40 (A=018, B=151) +Fout=1936.60 (A=019, B=151) +Fout=1936.80 (A=020, B=151) +Fout=1937.00 (A=021, B=151) +Fout=1937.20 (A=022, B=151) +Fout=1937.40 (A=023, B=151) +Fout=1937.60 (A=024, B=151) +Fout=1937.80 (A=025, B=151) +Fout=1938.00 (A=026, B=151) +Fout=1938.20 (A=027, B=151) +Fout=1938.40 (A=028, B=151) +Fout=1938.60 (A=029, B=151) +Fout=1938.80 (A=030, B=151) +Fout=1939.00 (A=031, B=151) +Fout=1939.20 (A=032, B=151) +Fout=1939.40 (A=033, B=151) +Fout=1939.60 (A=034, B=151) +Fout=1939.80 (A=035, B=151) +Fout=1940.00 (A=036, B=151) +Fout=1940.20 (A=037, B=151) +Fout=1940.40 (A=038, B=151) +Fout=1940.60 (A=039, B=151) +Fout=1940.80 (A=040, B=151) +Fout=1941.00 (A=041, B=151) +Fout=1941.20 (A=042, B=151) +Fout=1941.40 (A=043, B=151) +Fout=1941.60 (A=044, B=151) +Fout=1941.80 (A=045, B=151) +Fout=1942.00 (A=046, B=151) +Fout=1942.20 (A=047, B=151) +Fout=1942.40 (A=048, B=151) +Fout=1942.60 (A=049, B=151) +Fout=1942.80 (A=050, B=151) +Fout=1943.00 (A=051, B=151) +Fout=1943.20 (A=052, B=151) +Fout=1943.40 (A=053, B=151) +Fout=1943.60 (A=054, B=151) +Fout=1943.80 (A=055, B=151) +Fout=1944.00 (A=056, B=151) +Fout=1944.20 (A=057, B=151) +Fout=1944.40 (A=058, B=151) +Fout=1944.60 (A=059, B=151) +Fout=1944.80 (A=060, B=151) +Fout=1945.00 (A=061, B=151) +Fout=1945.20 (A=062, B=151) +Fout=1945.60 (A=000, B=152) +Fout=1945.80 (A=001, B=152) +Fout=1946.00 (A=002, B=152) +Fout=1946.20 (A=003, B=152) +Fout=1946.40 (A=004, B=152) +Fout=1946.60 (A=005, B=152) +Fout=1946.80 (A=006, B=152) +Fout=1947.00 (A=007, B=152) +Fout=1947.20 (A=008, B=152) +Fout=1947.40 (A=009, B=152) +Fout=1947.60 (A=010, B=152) +Fout=1947.80 (A=011, B=152) +Fout=1948.00 (A=012, B=152) +Fout=1948.20 (A=013, B=152) +Fout=1948.40 (A=014, B=152) +Fout=1948.60 (A=015, B=152) +Fout=1948.80 (A=016, B=152) +Fout=1949.00 (A=017, B=152) +Fout=1949.20 (A=018, B=152) +Fout=1949.40 (A=019, B=152) +Fout=1949.60 (A=020, B=152) +Fout=1949.80 (A=021, B=152) +Fout=1950.00 (A=022, B=152) +Fout=1950.20 (A=023, B=152) +Fout=1950.40 (A=024, B=152) +Fout=1950.60 (A=025, B=152) +Fout=1950.80 (A=026, B=152) +Fout=1951.00 (A=027, B=152) +Fout=1951.20 (A=028, B=152) +Fout=1951.40 (A=029, B=152) +Fout=1951.60 (A=030, B=152) +Fout=1951.80 (A=031, B=152) +Fout=1952.00 (A=032, B=152) +Fout=1952.20 (A=033, B=152) +Fout=1952.40 (A=034, B=152) +Fout=1952.60 (A=035, B=152) +Fout=1952.80 (A=036, B=152) +Fout=1953.00 (A=037, B=152) +Fout=1953.20 (A=038, B=152) +Fout=1953.40 (A=039, B=152) +Fout=1953.60 (A=040, B=152) +Fout=1953.80 (A=041, B=152) +Fout=1954.00 (A=042, B=152) +Fout=1954.20 (A=043, B=152) +Fout=1954.40 (A=044, B=152) +Fout=1954.60 (A=045, B=152) +Fout=1954.80 (A=046, B=152) +Fout=1955.00 (A=047, B=152) +Fout=1955.20 (A=048, B=152) +Fout=1955.40 (A=049, B=152) +Fout=1955.60 (A=050, B=152) +Fout=1955.80 (A=051, B=152) +Fout=1956.00 (A=052, B=152) +Fout=1956.20 (A=053, B=152) +Fout=1956.40 (A=054, B=152) +Fout=1956.60 (A=055, B=152) +Fout=1956.80 (A=056, B=152) +Fout=1957.00 (A=057, B=152) +Fout=1957.20 (A=058, B=152) +Fout=1957.40 (A=059, B=152) +Fout=1957.60 (A=060, B=152) +Fout=1957.80 (A=061, B=152) +Fout=1958.00 (A=062, B=152) +Fout=1958.40 (A=000, B=153) +Fout=1958.60 (A=001, B=153) +Fout=1958.80 (A=002, B=153) +Fout=1959.00 (A=003, B=153) +Fout=1959.20 (A=004, B=153) +Fout=1959.40 (A=005, B=153) +Fout=1959.60 (A=006, B=153) +Fout=1959.80 (A=007, B=153) +Fout=1960.00 (A=008, B=153) +Fout=1960.20 (A=009, B=153) +Fout=1960.40 (A=010, B=153) +Fout=1960.60 (A=011, B=153) +Fout=1960.80 (A=012, B=153) +Fout=1961.00 (A=013, B=153) +Fout=1961.20 (A=014, B=153) +Fout=1961.40 (A=015, B=153) +Fout=1961.60 (A=016, B=153) +Fout=1961.80 (A=017, B=153) +Fout=1962.00 (A=018, B=153) +Fout=1962.20 (A=019, B=153) +Fout=1962.40 (A=020, B=153) +Fout=1962.60 (A=021, B=153) +Fout=1962.80 (A=022, B=153) +Fout=1963.00 (A=023, B=153) +Fout=1963.20 (A=024, B=153) +Fout=1963.40 (A=025, B=153) +Fout=1963.60 (A=026, B=153) +Fout=1963.80 (A=027, B=153) +Fout=1964.00 (A=028, B=153) +Fout=1964.20 (A=029, B=153) +Fout=1964.40 (A=030, B=153) +Fout=1964.60 (A=031, B=153) +Fout=1964.80 (A=032, B=153) +Fout=1965.00 (A=033, B=153) +Fout=1965.20 (A=034, B=153) +Fout=1965.40 (A=035, B=153) +Fout=1965.60 (A=036, B=153) +Fout=1965.80 (A=037, B=153) +Fout=1966.00 (A=038, B=153) +Fout=1966.20 (A=039, B=153) +Fout=1966.40 (A=040, B=153) +Fout=1966.60 (A=041, B=153) +Fout=1966.80 (A=042, B=153) +Fout=1967.00 (A=043, B=153) +Fout=1967.20 (A=044, B=153) +Fout=1967.40 (A=045, B=153) +Fout=1967.60 (A=046, B=153) +Fout=1967.80 (A=047, B=153) +Fout=1968.00 (A=048, B=153) +Fout=1968.20 (A=049, B=153) +Fout=1968.40 (A=050, B=153) +Fout=1968.60 (A=051, B=153) +Fout=1968.80 (A=052, B=153) +Fout=1969.00 (A=053, B=153) +Fout=1969.20 (A=054, B=153) +Fout=1969.40 (A=055, B=153) +Fout=1969.60 (A=056, B=153) +Fout=1969.80 (A=057, B=153) +Fout=1970.00 (A=058, B=153) +Fout=1970.20 (A=059, B=153) +Fout=1970.40 (A=060, B=153) +Fout=1970.60 (A=061, B=153) +Fout=1970.80 (A=062, B=153) +Fout=1971.20 (A=000, B=154) +Fout=1971.40 (A=001, B=154) +Fout=1971.60 (A=002, B=154) +Fout=1971.80 (A=003, B=154) +Fout=1972.00 (A=004, B=154) +Fout=1972.20 (A=005, B=154) +Fout=1972.40 (A=006, B=154) +Fout=1972.60 (A=007, B=154) +Fout=1972.80 (A=008, B=154) +Fout=1973.00 (A=009, B=154) +Fout=1973.20 (A=010, B=154) +Fout=1973.40 (A=011, B=154) +Fout=1973.60 (A=012, B=154) +Fout=1973.80 (A=013, B=154) +Fout=1974.00 (A=014, B=154) +Fout=1974.20 (A=015, B=154) +Fout=1974.40 (A=016, B=154) +Fout=1974.60 (A=017, B=154) +Fout=1974.80 (A=018, B=154) +Fout=1975.00 (A=019, B=154) +Fout=1975.20 (A=020, B=154) +Fout=1975.40 (A=021, B=154) +Fout=1975.60 (A=022, B=154) +Fout=1975.80 (A=023, B=154) +Fout=1976.00 (A=024, B=154) +Fout=1976.20 (A=025, B=154) +Fout=1976.40 (A=026, B=154) +Fout=1976.60 (A=027, B=154) +Fout=1976.80 (A=028, B=154) +Fout=1977.00 (A=029, B=154) +Fout=1977.20 (A=030, B=154) +Fout=1977.40 (A=031, B=154) +Fout=1977.60 (A=032, B=154) +Fout=1977.80 (A=033, B=154) +Fout=1978.00 (A=034, B=154) +Fout=1978.20 (A=035, B=154) +Fout=1978.40 (A=036, B=154) +Fout=1978.60 (A=037, B=154) +Fout=1978.80 (A=038, B=154) +Fout=1979.00 (A=039, B=154) +Fout=1979.20 (A=040, B=154) +Fout=1979.40 (A=041, B=154) +Fout=1979.60 (A=042, B=154) +Fout=1979.80 (A=043, B=154) +Fout=1980.00 (A=044, B=154) +Fout=1980.20 (A=045, B=154) +Fout=1980.40 (A=046, B=154) +Fout=1980.60 (A=047, B=154) +Fout=1980.80 (A=048, B=154) +Fout=1981.00 (A=049, B=154) +Fout=1981.20 (A=050, B=154) +Fout=1981.40 (A=051, B=154) +Fout=1981.60 (A=052, B=154) +Fout=1981.80 (A=053, B=154) +Fout=1982.00 (A=054, B=154) +Fout=1982.20 (A=055, B=154) +Fout=1982.40 (A=056, B=154) +Fout=1982.60 (A=057, B=154) +Fout=1982.80 (A=058, B=154) +Fout=1983.00 (A=059, B=154) +Fout=1983.20 (A=060, B=154) +Fout=1983.40 (A=061, B=154) +Fout=1983.60 (A=062, B=154) +Fout=1984.00 (A=000, B=155) +Fout=1984.20 (A=001, B=155) +Fout=1984.40 (A=002, B=155) +Fout=1984.60 (A=003, B=155) +Fout=1984.80 (A=004, B=155) +Fout=1985.00 (A=005, B=155) +Fout=1985.20 (A=006, B=155) +Fout=1985.40 (A=007, B=155) +Fout=1985.60 (A=008, B=155) +Fout=1985.80 (A=009, B=155) +Fout=1986.00 (A=010, B=155) +Fout=1986.20 (A=011, B=155) +Fout=1986.40 (A=012, B=155) +Fout=1986.60 (A=013, B=155) +Fout=1986.80 (A=014, B=155) +Fout=1987.00 (A=015, B=155) +Fout=1987.20 (A=016, B=155) +Fout=1987.40 (A=017, B=155) +Fout=1987.60 (A=018, B=155) +Fout=1987.80 (A=019, B=155) +Fout=1988.00 (A=020, B=155) +Fout=1988.20 (A=021, B=155) +Fout=1988.40 (A=022, B=155) +Fout=1988.60 (A=023, B=155) +Fout=1988.80 (A=024, B=155) +Fout=1989.00 (A=025, B=155) +Fout=1989.20 (A=026, B=155) +Fout=1989.40 (A=027, B=155) +Fout=1989.60 (A=028, B=155) +Fout=1989.80 (A=029, B=155) +Fout=1990.00 (A=030, B=155) +Fout=1990.20 (A=031, B=155) +Fout=1990.40 (A=032, B=155) +Fout=1990.60 (A=033, B=155) +Fout=1990.80 (A=034, B=155) +Fout=1991.00 (A=035, B=155) +Fout=1991.20 (A=036, B=155) +Fout=1991.40 (A=037, B=155) +Fout=1991.60 (A=038, B=155) +Fout=1991.80 (A=039, B=155) +Fout=1992.00 (A=040, B=155) +Fout=1992.20 (A=041, B=155) +Fout=1992.40 (A=042, B=155) +Fout=1992.60 (A=043, B=155) +Fout=1992.80 (A=044, B=155) +Fout=1993.00 (A=045, B=155) +Fout=1993.20 (A=046, B=155) +Fout=1993.40 (A=047, B=155) +Fout=1993.60 (A=048, B=155) +Fout=1993.80 (A=049, B=155) +Fout=1994.00 (A=050, B=155) +Fout=1994.20 (A=051, B=155) +Fout=1994.40 (A=052, B=155) +Fout=1994.60 (A=053, B=155) +Fout=1994.80 (A=054, B=155) +Fout=1995.00 (A=055, B=155) +Fout=1995.20 (A=056, B=155) +Fout=1995.40 (A=057, B=155) +Fout=1995.60 (A=058, B=155) +Fout=1995.80 (A=059, B=155) +Fout=1996.00 (A=060, B=155) +Fout=1996.20 (A=061, B=155) +Fout=1996.40 (A=062, B=155) +====================================================================== +PLL Tx GSM850_1 +Fout=819.20 (A=000, B=128) +Fout=819.30 (A=001, B=128) +Fout=819.40 (A=002, B=128) +Fout=819.50 (A=003, B=128) +Fout=819.60 (A=004, B=128) +Fout=819.70 (A=005, B=128) +Fout=819.80 (A=006, B=128) +Fout=819.90 (A=007, B=128) +Fout=820.00 (A=008, B=128) +Fout=820.10 (A=009, B=128) +Fout=820.20 (A=010, B=128) +Fout=820.30 (A=011, B=128) +Fout=820.40 (A=012, B=128) +Fout=820.50 (A=013, B=128) +Fout=820.60 (A=014, B=128) +Fout=820.70 (A=015, B=128) +Fout=820.80 (A=016, B=128) +Fout=820.90 (A=017, B=128) +Fout=821.00 (A=018, B=128) +Fout=821.10 (A=019, B=128) +Fout=821.20 (A=020, B=128) +Fout=821.30 (A=021, B=128) +Fout=821.40 (A=022, B=128) +Fout=821.50 (A=023, B=128) +Fout=821.60 (A=024, B=128) +Fout=821.70 (A=025, B=128) +Fout=821.80 (A=026, B=128) +Fout=821.90 (A=027, B=128) +Fout=822.00 (A=028, B=128) +Fout=822.10 (A=029, B=128) +Fout=822.20 (A=030, B=128) +Fout=822.30 (A=031, B=128) +Fout=822.40 (A=032, B=128) +Fout=822.50 (A=033, B=128) +Fout=822.60 (A=034, B=128) +Fout=822.70 (A=035, B=128) +Fout=822.80 (A=036, B=128) +Fout=822.90 (A=037, B=128) +Fout=823.00 (A=038, B=128) +Fout=823.10 (A=039, B=128) +Fout=823.20 (A=040, B=128) +Fout=823.30 (A=041, B=128) +Fout=823.40 (A=042, B=128) +Fout=823.50 (A=043, B=128) +Fout=823.60 (A=044, B=128) +Fout=823.70 (A=045, B=128) +Fout=823.80 (A=046, B=128) +Fout=823.90 (A=047, B=128) +Fout=824.00 (A=048, B=128) +Fout=824.10 (A=049, B=128) +Fout=824.20 (A=050, B=128) +Fout=824.30 (A=051, B=128) +Fout=824.40 (A=052, B=128) +Fout=824.50 (A=053, B=128) +Fout=824.60 (A=054, B=128) +Fout=824.70 (A=055, B=128) +Fout=824.80 (A=056, B=128) +Fout=824.90 (A=057, B=128) +Fout=825.00 (A=058, B=128) +Fout=825.10 (A=059, B=128) +Fout=825.20 (A=060, B=128) +Fout=825.30 (A=061, B=128) +Fout=825.40 (A=062, B=128) +Fout=825.60 (A=000, B=129) +Fout=825.70 (A=001, B=129) +Fout=825.80 (A=002, B=129) +Fout=825.90 (A=003, B=129) +Fout=826.00 (A=004, B=129) +Fout=826.10 (A=005, B=129) +Fout=826.20 (A=006, B=129) +Fout=826.30 (A=007, B=129) +Fout=826.40 (A=008, B=129) +Fout=826.50 (A=009, B=129) +Fout=826.60 (A=010, B=129) +Fout=826.70 (A=011, B=129) +Fout=826.80 (A=012, B=129) +Fout=826.90 (A=013, B=129) +Fout=827.00 (A=014, B=129) +Fout=827.10 (A=015, B=129) +Fout=827.20 (A=016, B=129) +Fout=827.30 (A=017, B=129) +Fout=827.40 (A=018, B=129) +Fout=827.50 (A=019, B=129) +Fout=827.60 (A=020, B=129) +Fout=827.70 (A=021, B=129) +Fout=827.80 (A=022, B=129) +Fout=827.90 (A=023, B=129) +Fout=828.00 (A=024, B=129) +Fout=828.10 (A=025, B=129) +Fout=828.20 (A=026, B=129) +Fout=828.30 (A=027, B=129) +Fout=828.40 (A=028, B=129) +Fout=828.50 (A=029, B=129) +Fout=828.60 (A=030, B=129) +Fout=828.70 (A=031, B=129) +Fout=828.80 (A=032, B=129) +Fout=828.90 (A=033, B=129) +Fout=829.00 (A=034, B=129) +Fout=829.10 (A=035, B=129) +Fout=829.20 (A=036, B=129) +Fout=829.30 (A=037, B=129) +Fout=829.40 (A=038, B=129) +Fout=829.50 (A=039, B=129) +Fout=829.60 (A=040, B=129) +Fout=829.70 (A=041, B=129) +Fout=829.80 (A=042, B=129) +Fout=829.90 (A=043, B=129) +Fout=830.00 (A=044, B=129) +Fout=830.10 (A=045, B=129) +Fout=830.20 (A=046, B=129) +Fout=830.30 (A=047, B=129) +Fout=830.40 (A=048, B=129) +Fout=830.50 (A=049, B=129) +Fout=830.60 (A=050, B=129) +Fout=830.70 (A=051, B=129) +Fout=830.80 (A=052, B=129) +Fout=830.90 (A=053, B=129) +Fout=831.00 (A=054, B=129) +Fout=831.10 (A=055, B=129) +Fout=831.20 (A=056, B=129) +Fout=831.30 (A=057, B=129) +Fout=831.40 (A=058, B=129) +Fout=831.50 (A=059, B=129) +Fout=831.60 (A=060, B=129) +Fout=831.70 (A=061, B=129) +Fout=831.80 (A=062, B=129) +Fout=832.00 (A=000, B=130) +Fout=832.10 (A=001, B=130) +Fout=832.20 (A=002, B=130) +Fout=832.30 (A=003, B=130) +Fout=832.40 (A=004, B=130) +Fout=832.50 (A=005, B=130) +Fout=832.60 (A=006, B=130) +Fout=832.70 (A=007, B=130) +Fout=832.80 (A=008, B=130) +Fout=832.90 (A=009, B=130) +Fout=833.00 (A=010, B=130) +Fout=833.10 (A=011, B=130) +Fout=833.20 (A=012, B=130) +Fout=833.30 (A=013, B=130) +Fout=833.40 (A=014, B=130) +Fout=833.50 (A=015, B=130) +Fout=833.60 (A=016, B=130) +Fout=833.70 (A=017, B=130) +Fout=833.80 (A=018, B=130) +Fout=833.90 (A=019, B=130) +Fout=834.00 (A=020, B=130) +Fout=834.10 (A=021, B=130) +Fout=834.20 (A=022, B=130) +Fout=834.30 (A=023, B=130) +Fout=834.40 (A=024, B=130) +Fout=834.50 (A=025, B=130) +Fout=834.60 (A=026, B=130) +Fout=834.70 (A=027, B=130) +Fout=834.80 (A=028, B=130) +Fout=834.90 (A=029, B=130) +Fout=835.00 (A=030, B=130) +Fout=835.10 (A=031, B=130) +Fout=835.20 (A=032, B=130) +Fout=835.30 (A=033, B=130) +Fout=835.40 (A=034, B=130) +Fout=835.50 (A=035, B=130) +Fout=835.60 (A=036, B=130) +Fout=835.70 (A=037, B=130) +Fout=835.80 (A=038, B=130) +Fout=835.90 (A=039, B=130) +Fout=836.00 (A=040, B=130) +Fout=836.10 (A=041, B=130) +Fout=836.20 (A=042, B=130) +Fout=836.30 (A=043, B=130) +Fout=836.40 (A=044, B=130) +Fout=836.50 (A=045, B=130) +Fout=836.60 (A=046, B=130) +Fout=836.70 (A=047, B=130) +Fout=836.80 (A=048, B=130) +Fout=836.90 (A=049, B=130) +Fout=837.00 (A=050, B=130) +Fout=837.10 (A=051, B=130) +Fout=837.20 (A=052, B=130) +Fout=837.30 (A=053, B=130) +Fout=837.40 (A=054, B=130) +Fout=837.50 (A=055, B=130) +Fout=837.60 (A=056, B=130) +Fout=837.70 (A=057, B=130) +Fout=837.80 (A=058, B=130) +Fout=837.90 (A=059, B=130) +Fout=838.00 (A=060, B=130) +Fout=838.10 (A=061, B=130) +Fout=838.20 (A=062, B=130) +====================================================================== +PLL Tx GSM850_2 +Fout=832.00 (A=000, B=065) +Fout=832.20 (A=001, B=065) +Fout=832.40 (A=002, B=065) +Fout=832.60 (A=003, B=065) +Fout=832.80 (A=004, B=065) +Fout=833.00 (A=005, B=065) +Fout=833.20 (A=006, B=065) +Fout=833.40 (A=007, B=065) +Fout=833.60 (A=008, B=065) +Fout=833.80 (A=009, B=065) +Fout=834.00 (A=010, B=065) +Fout=834.20 (A=011, B=065) +Fout=834.40 (A=012, B=065) +Fout=834.60 (A=013, B=065) +Fout=834.80 (A=014, B=065) +Fout=835.00 (A=015, B=065) +Fout=835.20 (A=016, B=065) +Fout=835.40 (A=017, B=065) +Fout=835.60 (A=018, B=065) +Fout=835.80 (A=019, B=065) +Fout=836.00 (A=020, B=065) +Fout=836.20 (A=021, B=065) +Fout=836.40 (A=022, B=065) +Fout=836.60 (A=023, B=065) +Fout=836.80 (A=024, B=065) +Fout=837.00 (A=025, B=065) +Fout=837.20 (A=026, B=065) +Fout=837.40 (A=027, B=065) +Fout=837.60 (A=028, B=065) +Fout=837.80 (A=029, B=065) +Fout=838.00 (A=030, B=065) +Fout=838.20 (A=031, B=065) +Fout=838.40 (A=032, B=065) +Fout=838.60 (A=033, B=065) +Fout=838.80 (A=034, B=065) +Fout=839.00 (A=035, B=065) +Fout=839.20 (A=036, B=065) +Fout=839.40 (A=037, B=065) +Fout=839.60 (A=038, B=065) +Fout=839.80 (A=039, B=065) +Fout=840.00 (A=040, B=065) +Fout=840.20 (A=041, B=065) +Fout=840.40 (A=042, B=065) +Fout=840.60 (A=043, B=065) +Fout=840.80 (A=044, B=065) +Fout=841.00 (A=045, B=065) +Fout=841.20 (A=046, B=065) +Fout=841.40 (A=047, B=065) +Fout=841.60 (A=048, B=065) +Fout=841.80 (A=049, B=065) +Fout=842.00 (A=050, B=065) +Fout=842.20 (A=051, B=065) +Fout=842.40 (A=052, B=065) +Fout=842.60 (A=053, B=065) +Fout=842.80 (A=054, B=065) +Fout=843.00 (A=055, B=065) +Fout=843.20 (A=056, B=065) +Fout=843.40 (A=057, B=065) +Fout=843.60 (A=058, B=065) +Fout=843.80 (A=059, B=065) +Fout=844.00 (A=060, B=065) +Fout=844.20 (A=061, B=065) +Fout=844.40 (A=062, B=065) +Fout=844.60 (A=063, B=065) +Fout=844.80 (A=000, B=066) +Fout=845.00 (A=001, B=066) +Fout=845.20 (A=002, B=066) +Fout=845.40 (A=003, B=066) +Fout=845.60 (A=004, B=066) +Fout=845.80 (A=005, B=066) +Fout=846.00 (A=006, B=066) +Fout=846.20 (A=007, B=066) +Fout=846.40 (A=008, B=066) +Fout=846.60 (A=009, B=066) +Fout=846.80 (A=010, B=066) +Fout=847.00 (A=011, B=066) +Fout=847.20 (A=012, B=066) +Fout=847.40 (A=013, B=066) +Fout=847.60 (A=014, B=066) +Fout=847.80 (A=015, B=066) +Fout=848.00 (A=016, B=066) +Fout=848.20 (A=017, B=066) +Fout=848.40 (A=018, B=066) +Fout=848.60 (A=019, B=066) +Fout=848.80 (A=020, B=066) +Fout=849.00 (A=021, B=066) +Fout=849.20 (A=022, B=066) +Fout=849.40 (A=023, B=066) +Fout=849.60 (A=024, B=066) +Fout=849.80 (A=025, B=066) +Fout=850.00 (A=026, B=066) +Fout=850.20 (A=027, B=066) +Fout=850.40 (A=028, B=066) +Fout=850.60 (A=029, B=066) +Fout=850.80 (A=030, B=066) +Fout=851.00 (A=031, B=066) +Fout=851.20 (A=032, B=066) +Fout=851.40 (A=033, B=066) +Fout=851.60 (A=034, B=066) +Fout=851.80 (A=035, B=066) +Fout=852.00 (A=036, B=066) +Fout=852.20 (A=037, B=066) +Fout=852.40 (A=038, B=066) +Fout=852.60 (A=039, B=066) +Fout=852.80 (A=040, B=066) +Fout=853.00 (A=041, B=066) +Fout=853.20 (A=042, B=066) +Fout=853.40 (A=043, B=066) +Fout=853.60 (A=044, B=066) +Fout=853.80 (A=045, B=066) +Fout=854.00 (A=046, B=066) +Fout=854.20 (A=047, B=066) +Fout=854.40 (A=048, B=066) +Fout=854.60 (A=049, B=066) +Fout=854.80 (A=050, B=066) +Fout=855.00 (A=051, B=066) +Fout=855.20 (A=052, B=066) +Fout=855.40 (A=053, B=066) +Fout=855.60 (A=054, B=066) +Fout=855.80 (A=055, B=066) +Fout=856.00 (A=056, B=066) +Fout=856.20 (A=057, B=066) +Fout=856.40 (A=058, B=066) +Fout=856.60 (A=059, B=066) +Fout=856.80 (A=060, B=066) +Fout=857.00 (A=061, B=066) +Fout=857.20 (A=062, B=066) +Fout=857.40 (A=063, B=066) +====================================================================== +PLL Tx GSM900 +Fout=870.40 (A=000, B=068) +Fout=870.60 (A=001, B=068) +Fout=870.80 (A=002, B=068) +Fout=871.00 (A=003, B=068) +Fout=871.20 (A=004, B=068) +Fout=871.40 (A=005, B=068) +Fout=871.60 (A=006, B=068) +Fout=871.80 (A=007, B=068) +Fout=872.00 (A=008, B=068) +Fout=872.20 (A=009, B=068) +Fout=872.40 (A=010, B=068) +Fout=872.60 (A=011, B=068) +Fout=872.80 (A=012, B=068) +Fout=873.00 (A=013, B=068) +Fout=873.20 (A=014, B=068) +Fout=873.40 (A=015, B=068) +Fout=873.60 (A=016, B=068) +Fout=873.80 (A=017, B=068) +Fout=874.00 (A=018, B=068) +Fout=874.20 (A=019, B=068) +Fout=874.40 (A=020, B=068) +Fout=874.60 (A=021, B=068) +Fout=874.80 (A=022, B=068) +Fout=875.00 (A=023, B=068) +Fout=875.20 (A=024, B=068) +Fout=875.40 (A=025, B=068) +Fout=875.60 (A=026, B=068) +Fout=875.80 (A=027, B=068) +Fout=876.00 (A=028, B=068) +Fout=876.20 (A=029, B=068) +Fout=876.40 (A=030, B=068) +Fout=876.60 (A=031, B=068) +Fout=876.80 (A=032, B=068) +Fout=877.00 (A=033, B=068) +Fout=877.20 (A=034, B=068) +Fout=877.40 (A=035, B=068) +Fout=877.60 (A=036, B=068) +Fout=877.80 (A=037, B=068) +Fout=878.00 (A=038, B=068) +Fout=878.20 (A=039, B=068) +Fout=878.40 (A=040, B=068) +Fout=878.60 (A=041, B=068) +Fout=878.80 (A=042, B=068) +Fout=879.00 (A=043, B=068) +Fout=879.20 (A=044, B=068) +Fout=879.40 (A=045, B=068) +Fout=879.60 (A=046, B=068) +Fout=879.80 (A=047, B=068) +Fout=880.00 (A=048, B=068) +Fout=880.20 (A=049, B=068) +Fout=880.40 (A=050, B=068) +Fout=880.60 (A=051, B=068) +Fout=880.80 (A=052, B=068) +Fout=881.00 (A=053, B=068) +Fout=881.20 (A=054, B=068) +Fout=881.40 (A=055, B=068) +Fout=881.60 (A=056, B=068) +Fout=881.80 (A=057, B=068) +Fout=882.00 (A=058, B=068) +Fout=882.20 (A=059, B=068) +Fout=882.40 (A=060, B=068) +Fout=882.60 (A=061, B=068) +Fout=882.80 (A=062, B=068) +Fout=883.00 (A=063, B=068) +Fout=883.20 (A=000, B=069) +Fout=883.40 (A=001, B=069) +Fout=883.60 (A=002, B=069) +Fout=883.80 (A=003, B=069) +Fout=884.00 (A=004, B=069) +Fout=884.20 (A=005, B=069) +Fout=884.40 (A=006, B=069) +Fout=884.60 (A=007, B=069) +Fout=884.80 (A=008, B=069) +Fout=885.00 (A=009, B=069) +Fout=885.20 (A=010, B=069) +Fout=885.40 (A=011, B=069) +Fout=885.60 (A=012, B=069) +Fout=885.80 (A=013, B=069) +Fout=886.00 (A=014, B=069) +Fout=886.20 (A=015, B=069) +Fout=886.40 (A=016, B=069) +Fout=886.60 (A=017, B=069) +Fout=886.80 (A=018, B=069) +Fout=887.00 (A=019, B=069) +Fout=887.20 (A=020, B=069) +Fout=887.40 (A=021, B=069) +Fout=887.60 (A=022, B=069) +Fout=887.80 (A=023, B=069) +Fout=888.00 (A=024, B=069) +Fout=888.20 (A=025, B=069) +Fout=888.40 (A=026, B=069) +Fout=888.60 (A=027, B=069) +Fout=888.80 (A=028, B=069) +Fout=889.00 (A=029, B=069) +Fout=889.20 (A=030, B=069) +Fout=889.40 (A=031, B=069) +Fout=889.60 (A=032, B=069) +Fout=889.80 (A=033, B=069) +Fout=890.00 (A=034, B=069) +Fout=890.20 (A=035, B=069) +Fout=890.40 (A=036, B=069) +Fout=890.60 (A=037, B=069) +Fout=890.80 (A=038, B=069) +Fout=891.00 (A=039, B=069) +Fout=891.20 (A=040, B=069) +Fout=891.40 (A=041, B=069) +Fout=891.60 (A=042, B=069) +Fout=891.80 (A=043, B=069) +Fout=892.00 (A=044, B=069) +Fout=892.20 (A=045, B=069) +Fout=892.40 (A=046, B=069) +Fout=892.60 (A=047, B=069) +Fout=892.80 (A=048, B=069) +Fout=893.00 (A=049, B=069) +Fout=893.20 (A=050, B=069) +Fout=893.40 (A=051, B=069) +Fout=893.60 (A=052, B=069) +Fout=893.80 (A=053, B=069) +Fout=894.00 (A=054, B=069) +Fout=894.20 (A=055, B=069) +Fout=894.40 (A=056, B=069) +Fout=894.60 (A=057, B=069) +Fout=894.80 (A=058, B=069) +Fout=895.00 (A=059, B=069) +Fout=895.20 (A=060, B=069) +Fout=895.40 (A=061, B=069) +Fout=895.60 (A=062, B=069) +Fout=895.80 (A=063, B=069) +Fout=896.00 (A=000, B=070) +Fout=896.20 (A=001, B=070) +Fout=896.40 (A=002, B=070) +Fout=896.60 (A=003, B=070) +Fout=896.80 (A=004, B=070) +Fout=897.00 (A=005, B=070) +Fout=897.20 (A=006, B=070) +Fout=897.40 (A=007, B=070) +Fout=897.60 (A=008, B=070) +Fout=897.80 (A=009, B=070) +Fout=898.00 (A=010, B=070) +Fout=898.20 (A=011, B=070) +Fout=898.40 (A=012, B=070) +Fout=898.60 (A=013, B=070) +Fout=898.80 (A=014, B=070) +Fout=899.00 (A=015, B=070) +Fout=899.20 (A=016, B=070) +Fout=899.40 (A=017, B=070) +Fout=899.60 (A=018, B=070) +Fout=899.80 (A=019, B=070) +Fout=900.00 (A=020, B=070) +Fout=900.20 (A=021, B=070) +Fout=900.40 (A=022, B=070) +Fout=900.60 (A=023, B=070) +Fout=900.80 (A=024, B=070) +Fout=901.00 (A=025, B=070) +Fout=901.20 (A=026, B=070) +Fout=901.40 (A=027, B=070) +Fout=901.60 (A=028, B=070) +Fout=901.80 (A=029, B=070) +Fout=902.00 (A=030, B=070) +Fout=902.20 (A=031, B=070) +Fout=902.40 (A=032, B=070) +Fout=902.60 (A=033, B=070) +Fout=902.80 (A=034, B=070) +Fout=903.00 (A=035, B=070) +Fout=903.20 (A=036, B=070) +Fout=903.40 (A=037, B=070) +Fout=903.60 (A=038, B=070) +Fout=903.80 (A=039, B=070) +Fout=904.00 (A=040, B=070) +Fout=904.20 (A=041, B=070) +Fout=904.40 (A=042, B=070) +Fout=904.60 (A=043, B=070) +Fout=904.80 (A=044, B=070) +Fout=905.00 (A=045, B=070) +Fout=905.20 (A=046, B=070) +Fout=905.40 (A=047, B=070) +Fout=905.60 (A=048, B=070) +Fout=905.80 (A=049, B=070) +Fout=906.00 (A=050, B=070) +Fout=906.20 (A=051, B=070) +Fout=906.40 (A=052, B=070) +Fout=906.60 (A=053, B=070) +Fout=906.80 (A=054, B=070) +Fout=907.00 (A=055, B=070) +Fout=907.20 (A=056, B=070) +Fout=907.40 (A=057, B=070) +Fout=907.60 (A=058, B=070) +Fout=907.80 (A=059, B=070) +Fout=908.00 (A=060, B=070) +Fout=908.20 (A=061, B=070) +Fout=908.40 (A=062, B=070) +Fout=908.60 (A=063, B=070) +Fout=908.80 (A=000, B=071) +Fout=909.00 (A=001, B=071) +Fout=909.20 (A=002, B=071) +Fout=909.40 (A=003, B=071) +Fout=909.60 (A=004, B=071) +Fout=909.80 (A=005, B=071) +Fout=910.00 (A=006, B=071) +Fout=910.20 (A=007, B=071) +Fout=910.40 (A=008, B=071) +Fout=910.60 (A=009, B=071) +Fout=910.80 (A=010, B=071) +Fout=911.00 (A=011, B=071) +Fout=911.20 (A=012, B=071) +Fout=911.40 (A=013, B=071) +Fout=911.60 (A=014, B=071) +Fout=911.80 (A=015, B=071) +Fout=912.00 (A=016, B=071) +Fout=912.20 (A=017, B=071) +Fout=912.40 (A=018, B=071) +Fout=912.60 (A=019, B=071) +Fout=912.80 (A=020, B=071) +Fout=913.00 (A=021, B=071) +Fout=913.20 (A=022, B=071) +Fout=913.40 (A=023, B=071) +Fout=913.60 (A=024, B=071) +Fout=913.80 (A=025, B=071) +Fout=914.00 (A=026, B=071) +Fout=914.20 (A=027, B=071) +Fout=914.40 (A=028, B=071) +Fout=914.60 (A=029, B=071) +Fout=914.80 (A=030, B=071) +Fout=915.00 (A=031, B=071) +Fout=915.20 (A=032, B=071) +Fout=915.40 (A=033, B=071) +Fout=915.60 (A=034, B=071) +Fout=915.80 (A=035, B=071) +Fout=916.00 (A=036, B=071) +Fout=916.20 (A=037, B=071) +Fout=916.40 (A=038, B=071) +Fout=916.60 (A=039, B=071) +Fout=916.80 (A=040, B=071) +Fout=917.00 (A=041, B=071) +Fout=917.20 (A=042, B=071) +Fout=917.40 (A=043, B=071) +Fout=917.60 (A=044, B=071) +Fout=917.80 (A=045, B=071) +Fout=918.00 (A=046, B=071) +Fout=918.20 (A=047, B=071) +Fout=918.40 (A=048, B=071) +Fout=918.60 (A=049, B=071) +Fout=918.80 (A=050, B=071) +Fout=919.00 (A=051, B=071) +Fout=919.20 (A=052, B=071) +Fout=919.40 (A=053, B=071) +Fout=919.60 (A=054, B=071) +Fout=919.80 (A=055, B=071) +Fout=920.00 (A=056, B=071) +Fout=920.20 (A=057, B=071) +Fout=920.40 (A=058, B=071) +Fout=920.60 (A=059, B=071) +Fout=920.80 (A=060, B=071) +Fout=921.00 (A=061, B=071) +Fout=921.20 (A=062, B=071) +Fout=921.40 (A=063, B=071) +====================================================================== +PLL Tx GSM1800/1900 +Fout=1702.40 (A=000, B=133) +Fout=1702.60 (A=001, B=133) +Fout=1702.80 (A=002, B=133) +Fout=1703.00 (A=003, B=133) +Fout=1703.20 (A=004, B=133) +Fout=1703.40 (A=005, B=133) +Fout=1703.60 (A=006, B=133) +Fout=1703.80 (A=007, B=133) +Fout=1704.00 (A=008, B=133) +Fout=1704.20 (A=009, B=133) +Fout=1704.40 (A=010, B=133) +Fout=1704.60 (A=011, B=133) +Fout=1704.80 (A=012, B=133) +Fout=1705.00 (A=013, B=133) +Fout=1705.20 (A=014, B=133) +Fout=1705.40 (A=015, B=133) +Fout=1705.60 (A=016, B=133) +Fout=1705.80 (A=017, B=133) +Fout=1706.00 (A=018, B=133) +Fout=1706.20 (A=019, B=133) +Fout=1706.40 (A=020, B=133) +Fout=1706.60 (A=021, B=133) +Fout=1706.80 (A=022, B=133) +Fout=1707.00 (A=023, B=133) +Fout=1707.20 (A=024, B=133) +Fout=1707.40 (A=025, B=133) +Fout=1707.60 (A=026, B=133) +Fout=1707.80 (A=027, B=133) +Fout=1708.00 (A=028, B=133) +Fout=1708.20 (A=029, B=133) +Fout=1708.40 (A=030, B=133) +Fout=1708.60 (A=031, B=133) +Fout=1708.80 (A=032, B=133) +Fout=1709.00 (A=033, B=133) +Fout=1709.20 (A=034, B=133) +Fout=1709.40 (A=035, B=133) +Fout=1709.60 (A=036, B=133) +Fout=1709.80 (A=037, B=133) +Fout=1710.00 (A=038, B=133) +Fout=1710.20 (A=039, B=133) +Fout=1710.40 (A=040, B=133) +Fout=1710.60 (A=041, B=133) +Fout=1710.80 (A=042, B=133) +Fout=1711.00 (A=043, B=133) +Fout=1711.20 (A=044, B=133) +Fout=1711.40 (A=045, B=133) +Fout=1711.60 (A=046, B=133) +Fout=1711.80 (A=047, B=133) +Fout=1712.00 (A=048, B=133) +Fout=1712.20 (A=049, B=133) +Fout=1712.40 (A=050, B=133) +Fout=1712.60 (A=051, B=133) +Fout=1712.80 (A=052, B=133) +Fout=1713.00 (A=053, B=133) +Fout=1713.20 (A=054, B=133) +Fout=1713.40 (A=055, B=133) +Fout=1713.60 (A=056, B=133) +Fout=1713.80 (A=057, B=133) +Fout=1714.00 (A=058, B=133) +Fout=1714.20 (A=059, B=133) +Fout=1714.40 (A=060, B=133) +Fout=1714.60 (A=061, B=133) +Fout=1714.80 (A=062, B=133) +Fout=1715.00 (A=063, B=133) +Fout=1715.20 (A=000, B=134) +Fout=1715.40 (A=001, B=134) +Fout=1715.60 (A=002, B=134) +Fout=1715.80 (A=003, B=134) +Fout=1716.00 (A=004, B=134) +Fout=1716.20 (A=005, B=134) +Fout=1716.40 (A=006, B=134) +Fout=1716.60 (A=007, B=134) +Fout=1716.80 (A=008, B=134) +Fout=1717.00 (A=009, B=134) +Fout=1717.20 (A=010, B=134) +Fout=1717.40 (A=011, B=134) +Fout=1717.60 (A=012, B=134) +Fout=1717.80 (A=013, B=134) +Fout=1718.00 (A=014, B=134) +Fout=1718.20 (A=015, B=134) +Fout=1718.40 (A=016, B=134) +Fout=1718.60 (A=017, B=134) +Fout=1718.80 (A=018, B=134) +Fout=1719.00 (A=019, B=134) +Fout=1719.20 (A=020, B=134) +Fout=1719.40 (A=021, B=134) +Fout=1719.60 (A=022, B=134) +Fout=1719.80 (A=023, B=134) +Fout=1720.00 (A=024, B=134) +Fout=1720.20 (A=025, B=134) +Fout=1720.40 (A=026, B=134) +Fout=1720.60 (A=027, B=134) +Fout=1720.80 (A=028, B=134) +Fout=1721.00 (A=029, B=134) +Fout=1721.20 (A=030, B=134) +Fout=1721.40 (A=031, B=134) +Fout=1721.60 (A=032, B=134) +Fout=1721.80 (A=033, B=134) +Fout=1722.00 (A=034, B=134) +Fout=1722.20 (A=035, B=134) +Fout=1722.40 (A=036, B=134) +Fout=1722.60 (A=037, B=134) +Fout=1722.80 (A=038, B=134) +Fout=1723.00 (A=039, B=134) +Fout=1723.20 (A=040, B=134) +Fout=1723.40 (A=041, B=134) +Fout=1723.60 (A=042, B=134) +Fout=1723.80 (A=043, B=134) +Fout=1724.00 (A=044, B=134) +Fout=1724.20 (A=045, B=134) +Fout=1724.40 (A=046, B=134) +Fout=1724.60 (A=047, B=134) +Fout=1724.80 (A=048, B=134) +Fout=1725.00 (A=049, B=134) +Fout=1725.20 (A=050, B=134) +Fout=1725.40 (A=051, B=134) +Fout=1725.60 (A=052, B=134) +Fout=1725.80 (A=053, B=134) +Fout=1726.00 (A=054, B=134) +Fout=1726.20 (A=055, B=134) +Fout=1726.40 (A=056, B=134) +Fout=1726.60 (A=057, B=134) +Fout=1726.80 (A=058, B=134) +Fout=1727.00 (A=059, B=134) +Fout=1727.20 (A=060, B=134) +Fout=1727.40 (A=061, B=134) +Fout=1727.60 (A=062, B=134) +Fout=1727.80 (A=063, B=134) +Fout=1728.00 (A=000, B=135) +Fout=1728.20 (A=001, B=135) +Fout=1728.40 (A=002, B=135) +Fout=1728.60 (A=003, B=135) +Fout=1728.80 (A=004, B=135) +Fout=1729.00 (A=005, B=135) +Fout=1729.20 (A=006, B=135) +Fout=1729.40 (A=007, B=135) +Fout=1729.60 (A=008, B=135) +Fout=1729.80 (A=009, B=135) +Fout=1730.00 (A=010, B=135) +Fout=1730.20 (A=011, B=135) +Fout=1730.40 (A=012, B=135) +Fout=1730.60 (A=013, B=135) +Fout=1730.80 (A=014, B=135) +Fout=1731.00 (A=015, B=135) +Fout=1731.20 (A=016, B=135) +Fout=1731.40 (A=017, B=135) +Fout=1731.60 (A=018, B=135) +Fout=1731.80 (A=019, B=135) +Fout=1732.00 (A=020, B=135) +Fout=1732.20 (A=021, B=135) +Fout=1732.40 (A=022, B=135) +Fout=1732.60 (A=023, B=135) +Fout=1732.80 (A=024, B=135) +Fout=1733.00 (A=025, B=135) +Fout=1733.20 (A=026, B=135) +Fout=1733.40 (A=027, B=135) +Fout=1733.60 (A=028, B=135) +Fout=1733.80 (A=029, B=135) +Fout=1734.00 (A=030, B=135) +Fout=1734.20 (A=031, B=135) +Fout=1734.40 (A=032, B=135) +Fout=1734.60 (A=033, B=135) +Fout=1734.80 (A=034, B=135) +Fout=1735.00 (A=035, B=135) +Fout=1735.20 (A=036, B=135) +Fout=1735.40 (A=037, B=135) +Fout=1735.60 (A=038, B=135) +Fout=1735.80 (A=039, B=135) +Fout=1736.00 (A=040, B=135) +Fout=1736.20 (A=041, B=135) +Fout=1736.40 (A=042, B=135) +Fout=1736.60 (A=043, B=135) +Fout=1736.80 (A=044, B=135) +Fout=1737.00 (A=045, B=135) +Fout=1737.20 (A=046, B=135) +Fout=1737.40 (A=047, B=135) +Fout=1737.60 (A=048, B=135) +Fout=1737.80 (A=049, B=135) +Fout=1738.00 (A=050, B=135) +Fout=1738.20 (A=051, B=135) +Fout=1738.40 (A=052, B=135) +Fout=1738.60 (A=053, B=135) +Fout=1738.80 (A=054, B=135) +Fout=1739.00 (A=055, B=135) +Fout=1739.20 (A=056, B=135) +Fout=1739.40 (A=057, B=135) +Fout=1739.60 (A=058, B=135) +Fout=1739.80 (A=059, B=135) +Fout=1740.00 (A=060, B=135) +Fout=1740.20 (A=061, B=135) +Fout=1740.40 (A=062, B=135) +Fout=1740.60 (A=063, B=135) +Fout=1740.80 (A=000, B=136) +Fout=1741.00 (A=001, B=136) +Fout=1741.20 (A=002, B=136) +Fout=1741.40 (A=003, B=136) +Fout=1741.60 (A=004, B=136) +Fout=1741.80 (A=005, B=136) +Fout=1742.00 (A=006, B=136) +Fout=1742.20 (A=007, B=136) +Fout=1742.40 (A=008, B=136) +Fout=1742.60 (A=009, B=136) +Fout=1742.80 (A=010, B=136) +Fout=1743.00 (A=011, B=136) +Fout=1743.20 (A=012, B=136) +Fout=1743.40 (A=013, B=136) +Fout=1743.60 (A=014, B=136) +Fout=1743.80 (A=015, B=136) +Fout=1744.00 (A=016, B=136) +Fout=1744.20 (A=017, B=136) +Fout=1744.40 (A=018, B=136) +Fout=1744.60 (A=019, B=136) +Fout=1744.80 (A=020, B=136) +Fout=1745.00 (A=021, B=136) +Fout=1745.20 (A=022, B=136) +Fout=1745.40 (A=023, B=136) +Fout=1745.60 (A=024, B=136) +Fout=1745.80 (A=025, B=136) +Fout=1746.00 (A=026, B=136) +Fout=1746.20 (A=027, B=136) +Fout=1746.40 (A=028, B=136) +Fout=1746.60 (A=029, B=136) +Fout=1746.80 (A=030, B=136) +Fout=1747.00 (A=031, B=136) +Fout=1747.20 (A=032, B=136) +Fout=1747.40 (A=033, B=136) +Fout=1747.60 (A=034, B=136) +Fout=1747.80 (A=035, B=136) +Fout=1748.00 (A=036, B=136) +Fout=1748.20 (A=037, B=136) +Fout=1748.40 (A=038, B=136) +Fout=1748.60 (A=039, B=136) +Fout=1748.80 (A=040, B=136) +Fout=1749.00 (A=041, B=136) +Fout=1749.20 (A=042, B=136) +Fout=1749.40 (A=043, B=136) +Fout=1749.60 (A=044, B=136) +Fout=1749.80 (A=045, B=136) +Fout=1750.00 (A=046, B=136) +Fout=1750.20 (A=047, B=136) +Fout=1750.40 (A=048, B=136) +Fout=1750.60 (A=049, B=136) +Fout=1750.80 (A=050, B=136) +Fout=1751.00 (A=051, B=136) +Fout=1751.20 (A=052, B=136) +Fout=1751.40 (A=053, B=136) +Fout=1751.60 (A=054, B=136) +Fout=1751.80 (A=055, B=136) +Fout=1752.00 (A=056, B=136) +Fout=1752.20 (A=057, B=136) +Fout=1752.40 (A=058, B=136) +Fout=1752.60 (A=059, B=136) +Fout=1752.80 (A=060, B=136) +Fout=1753.00 (A=061, B=136) +Fout=1753.20 (A=062, B=136) +Fout=1753.40 (A=063, B=136) +Fout=1753.60 (A=000, B=137) +Fout=1753.80 (A=001, B=137) +Fout=1754.00 (A=002, B=137) +Fout=1754.20 (A=003, B=137) +Fout=1754.40 (A=004, B=137) +Fout=1754.60 (A=005, B=137) +Fout=1754.80 (A=006, B=137) +Fout=1755.00 (A=007, B=137) +Fout=1755.20 (A=008, B=137) +Fout=1755.40 (A=009, B=137) +Fout=1755.60 (A=010, B=137) +Fout=1755.80 (A=011, B=137) +Fout=1756.00 (A=012, B=137) +Fout=1756.20 (A=013, B=137) +Fout=1756.40 (A=014, B=137) +Fout=1756.60 (A=015, B=137) +Fout=1756.80 (A=016, B=137) +Fout=1757.00 (A=017, B=137) +Fout=1757.20 (A=018, B=137) +Fout=1757.40 (A=019, B=137) +Fout=1757.60 (A=020, B=137) +Fout=1757.80 (A=021, B=137) +Fout=1758.00 (A=022, B=137) +Fout=1758.20 (A=023, B=137) +Fout=1758.40 (A=024, B=137) +Fout=1758.60 (A=025, B=137) +Fout=1758.80 (A=026, B=137) +Fout=1759.00 (A=027, B=137) +Fout=1759.20 (A=028, B=137) +Fout=1759.40 (A=029, B=137) +Fout=1759.60 (A=030, B=137) +Fout=1759.80 (A=031, B=137) +Fout=1760.00 (A=032, B=137) +Fout=1760.20 (A=033, B=137) +Fout=1760.40 (A=034, B=137) +Fout=1760.60 (A=035, B=137) +Fout=1760.80 (A=036, B=137) +Fout=1761.00 (A=037, B=137) +Fout=1761.20 (A=038, B=137) +Fout=1761.40 (A=039, B=137) +Fout=1761.60 (A=040, B=137) +Fout=1761.80 (A=041, B=137) +Fout=1762.00 (A=042, B=137) +Fout=1762.20 (A=043, B=137) +Fout=1762.40 (A=044, B=137) +Fout=1762.60 (A=045, B=137) +Fout=1762.80 (A=046, B=137) +Fout=1763.00 (A=047, B=137) +Fout=1763.20 (A=048, B=137) +Fout=1763.40 (A=049, B=137) +Fout=1763.60 (A=050, B=137) +Fout=1763.80 (A=051, B=137) +Fout=1764.00 (A=052, B=137) +Fout=1764.20 (A=053, B=137) +Fout=1764.40 (A=054, B=137) +Fout=1764.60 (A=055, B=137) +Fout=1764.80 (A=056, B=137) +Fout=1765.00 (A=057, B=137) +Fout=1765.20 (A=058, B=137) +Fout=1765.40 (A=059, B=137) +Fout=1765.60 (A=060, B=137) +Fout=1765.80 (A=061, B=137) +Fout=1766.00 (A=062, B=137) +Fout=1766.20 (A=063, B=137) +Fout=1766.40 (A=000, B=138) +Fout=1766.60 (A=001, B=138) +Fout=1766.80 (A=002, B=138) +Fout=1767.00 (A=003, B=138) +Fout=1767.20 (A=004, B=138) +Fout=1767.40 (A=005, B=138) +Fout=1767.60 (A=006, B=138) +Fout=1767.80 (A=007, B=138) +Fout=1768.00 (A=008, B=138) +Fout=1768.20 (A=009, B=138) +Fout=1768.40 (A=010, B=138) +Fout=1768.60 (A=011, B=138) +Fout=1768.80 (A=012, B=138) +Fout=1769.00 (A=013, B=138) +Fout=1769.20 (A=014, B=138) +Fout=1769.40 (A=015, B=138) +Fout=1769.60 (A=016, B=138) +Fout=1769.80 (A=017, B=138) +Fout=1770.00 (A=018, B=138) +Fout=1770.20 (A=019, B=138) +Fout=1770.40 (A=020, B=138) +Fout=1770.60 (A=021, B=138) +Fout=1770.80 (A=022, B=138) +Fout=1771.00 (A=023, B=138) +Fout=1771.20 (A=024, B=138) +Fout=1771.40 (A=025, B=138) +Fout=1771.60 (A=026, B=138) +Fout=1771.80 (A=027, B=138) +Fout=1772.00 (A=028, B=138) +Fout=1772.20 (A=029, B=138) +Fout=1772.40 (A=030, B=138) +Fout=1772.60 (A=031, B=138) +Fout=1772.80 (A=032, B=138) +Fout=1773.00 (A=033, B=138) +Fout=1773.20 (A=034, B=138) +Fout=1773.40 (A=035, B=138) +Fout=1773.60 (A=036, B=138) +Fout=1773.80 (A=037, B=138) +Fout=1774.00 (A=038, B=138) +Fout=1774.20 (A=039, B=138) +Fout=1774.40 (A=040, B=138) +Fout=1774.60 (A=041, B=138) +Fout=1774.80 (A=042, B=138) +Fout=1775.00 (A=043, B=138) +Fout=1775.20 (A=044, B=138) +Fout=1775.40 (A=045, B=138) +Fout=1775.60 (A=046, B=138) +Fout=1775.80 (A=047, B=138) +Fout=1776.00 (A=048, B=138) +Fout=1776.20 (A=049, B=138) +Fout=1776.40 (A=050, B=138) +Fout=1776.60 (A=051, B=138) +Fout=1776.80 (A=052, B=138) +Fout=1777.00 (A=053, B=138) +Fout=1777.20 (A=054, B=138) +Fout=1777.40 (A=055, B=138) +Fout=1777.60 (A=056, B=138) +Fout=1777.80 (A=057, B=138) +Fout=1778.00 (A=058, B=138) +Fout=1778.20 (A=059, B=138) +Fout=1778.40 (A=060, B=138) +Fout=1778.60 (A=061, B=138) +Fout=1778.80 (A=062, B=138) +Fout=1779.00 (A=063, B=138) +Fout=1779.20 (A=000, B=139) +Fout=1779.40 (A=001, B=139) +Fout=1779.60 (A=002, B=139) +Fout=1779.80 (A=003, B=139) +Fout=1780.00 (A=004, B=139) +Fout=1780.20 (A=005, B=139) +Fout=1780.40 (A=006, B=139) +Fout=1780.60 (A=007, B=139) +Fout=1780.80 (A=008, B=139) +Fout=1781.00 (A=009, B=139) +Fout=1781.20 (A=010, B=139) +Fout=1781.40 (A=011, B=139) +Fout=1781.60 (A=012, B=139) +Fout=1781.80 (A=013, B=139) +Fout=1782.00 (A=014, B=139) +Fout=1782.20 (A=015, B=139) +Fout=1782.40 (A=016, B=139) +Fout=1782.60 (A=017, B=139) +Fout=1782.80 (A=018, B=139) +Fout=1783.00 (A=019, B=139) +Fout=1783.20 (A=020, B=139) +Fout=1783.40 (A=021, B=139) +Fout=1783.60 (A=022, B=139) +Fout=1783.80 (A=023, B=139) +Fout=1784.00 (A=024, B=139) +Fout=1784.20 (A=025, B=139) +Fout=1784.40 (A=026, B=139) +Fout=1784.60 (A=027, B=139) +Fout=1784.80 (A=028, B=139) +Fout=1785.00 (A=029, B=139) +Fout=1785.20 (A=030, B=139) +Fout=1785.40 (A=031, B=139) +Fout=1785.60 (A=032, B=139) +Fout=1785.80 (A=033, B=139) +Fout=1786.00 (A=034, B=139) +Fout=1786.20 (A=035, B=139) +Fout=1786.40 (A=036, B=139) +Fout=1786.60 (A=037, B=139) +Fout=1786.80 (A=038, B=139) +Fout=1787.00 (A=039, B=139) +Fout=1787.20 (A=040, B=139) +Fout=1787.40 (A=041, B=139) +Fout=1787.60 (A=042, B=139) +Fout=1787.80 (A=043, B=139) +Fout=1788.00 (A=044, B=139) +Fout=1788.20 (A=045, B=139) +Fout=1788.40 (A=046, B=139) +Fout=1788.60 (A=047, B=139) +Fout=1788.80 (A=048, B=139) +Fout=1789.00 (A=049, B=139) +Fout=1789.20 (A=050, B=139) +Fout=1789.40 (A=051, B=139) +Fout=1789.60 (A=052, B=139) +Fout=1789.80 (A=053, B=139) +Fout=1790.00 (A=054, B=139) +Fout=1790.20 (A=055, B=139) +Fout=1790.40 (A=056, B=139) +Fout=1790.60 (A=057, B=139) +Fout=1790.80 (A=058, B=139) +Fout=1791.00 (A=059, B=139) +Fout=1791.20 (A=060, B=139) +Fout=1791.40 (A=061, B=139) +Fout=1791.60 (A=062, B=139) +Fout=1791.80 (A=063, B=139) +Fout=1792.00 (A=000, B=140) +Fout=1792.20 (A=001, B=140) +Fout=1792.40 (A=002, B=140) +Fout=1792.60 (A=003, B=140) +Fout=1792.80 (A=004, B=140) +Fout=1793.00 (A=005, B=140) +Fout=1793.20 (A=006, B=140) +Fout=1793.40 (A=007, B=140) +Fout=1793.60 (A=008, B=140) +Fout=1793.80 (A=009, B=140) +Fout=1794.00 (A=010, B=140) +Fout=1794.20 (A=011, B=140) +Fout=1794.40 (A=012, B=140) +Fout=1794.60 (A=013, B=140) +Fout=1794.80 (A=014, B=140) +Fout=1795.00 (A=015, B=140) +Fout=1795.20 (A=016, B=140) +Fout=1795.40 (A=017, B=140) +Fout=1795.60 (A=018, B=140) +Fout=1795.80 (A=019, B=140) +Fout=1796.00 (A=020, B=140) +Fout=1796.20 (A=021, B=140) +Fout=1796.40 (A=022, B=140) +Fout=1796.60 (A=023, B=140) +Fout=1796.80 (A=024, B=140) +Fout=1797.00 (A=025, B=140) +Fout=1797.20 (A=026, B=140) +Fout=1797.40 (A=027, B=140) +Fout=1797.60 (A=028, B=140) +Fout=1797.80 (A=029, B=140) +Fout=1798.00 (A=030, B=140) +Fout=1798.20 (A=031, B=140) +Fout=1798.40 (A=032, B=140) +Fout=1798.60 (A=033, B=140) +Fout=1798.80 (A=034, B=140) +Fout=1799.00 (A=035, B=140) +Fout=1799.20 (A=036, B=140) +Fout=1799.40 (A=037, B=140) +Fout=1799.60 (A=038, B=140) +Fout=1799.80 (A=039, B=140) +Fout=1800.00 (A=040, B=140) +Fout=1800.20 (A=041, B=140) +Fout=1800.40 (A=042, B=140) +Fout=1800.60 (A=043, B=140) +Fout=1800.80 (A=044, B=140) +Fout=1801.00 (A=045, B=140) +Fout=1801.20 (A=046, B=140) +Fout=1801.40 (A=047, B=140) +Fout=1801.60 (A=048, B=140) +Fout=1801.80 (A=049, B=140) +Fout=1802.00 (A=050, B=140) +Fout=1802.20 (A=051, B=140) +Fout=1802.40 (A=052, B=140) +Fout=1802.60 (A=053, B=140) +Fout=1802.80 (A=054, B=140) +Fout=1803.00 (A=055, B=140) +Fout=1803.20 (A=056, B=140) +Fout=1803.40 (A=057, B=140) +Fout=1803.60 (A=058, B=140) +Fout=1803.80 (A=059, B=140) +Fout=1804.00 (A=060, B=140) +Fout=1804.20 (A=061, B=140) +Fout=1804.40 (A=062, B=140) +Fout=1804.60 (A=063, B=140) +Fout=1804.80 (A=000, B=141) +Fout=1805.00 (A=001, B=141) +Fout=1805.20 (A=002, B=141) +Fout=1805.40 (A=003, B=141) +Fout=1805.60 (A=004, B=141) +Fout=1805.80 (A=005, B=141) +Fout=1806.00 (A=006, B=141) +Fout=1806.20 (A=007, B=141) +Fout=1806.40 (A=008, B=141) +Fout=1806.60 (A=009, B=141) +Fout=1806.80 (A=010, B=141) +Fout=1807.00 (A=011, B=141) +Fout=1807.20 (A=012, B=141) +Fout=1807.40 (A=013, B=141) +Fout=1807.60 (A=014, B=141) +Fout=1807.80 (A=015, B=141) +Fout=1808.00 (A=016, B=141) +Fout=1808.20 (A=017, B=141) +Fout=1808.40 (A=018, B=141) +Fout=1808.60 (A=019, B=141) +Fout=1808.80 (A=020, B=141) +Fout=1809.00 (A=021, B=141) +Fout=1809.20 (A=022, B=141) +Fout=1809.40 (A=023, B=141) +Fout=1809.60 (A=024, B=141) +Fout=1809.80 (A=025, B=141) +Fout=1810.00 (A=026, B=141) +Fout=1810.20 (A=027, B=141) +Fout=1810.40 (A=028, B=141) +Fout=1810.60 (A=029, B=141) +Fout=1810.80 (A=030, B=141) +Fout=1811.00 (A=031, B=141) +Fout=1811.20 (A=032, B=141) +Fout=1811.40 (A=033, B=141) +Fout=1811.60 (A=034, B=141) +Fout=1811.80 (A=035, B=141) +Fout=1812.00 (A=036, B=141) +Fout=1812.20 (A=037, B=141) +Fout=1812.40 (A=038, B=141) +Fout=1812.60 (A=039, B=141) +Fout=1812.80 (A=040, B=141) +Fout=1813.00 (A=041, B=141) +Fout=1813.20 (A=042, B=141) +Fout=1813.40 (A=043, B=141) +Fout=1813.60 (A=044, B=141) +Fout=1813.80 (A=045, B=141) +Fout=1814.00 (A=046, B=141) +Fout=1814.20 (A=047, B=141) +Fout=1814.40 (A=048, B=141) +Fout=1814.60 (A=049, B=141) +Fout=1814.80 (A=050, B=141) +Fout=1815.00 (A=051, B=141) +Fout=1815.20 (A=052, B=141) +Fout=1815.40 (A=053, B=141) +Fout=1815.60 (A=054, B=141) +Fout=1815.80 (A=055, B=141) +Fout=1816.00 (A=056, B=141) +Fout=1816.20 (A=057, B=141) +Fout=1816.40 (A=058, B=141) +Fout=1816.60 (A=059, B=141) +Fout=1816.80 (A=060, B=141) +Fout=1817.00 (A=061, B=141) +Fout=1817.20 (A=062, B=141) +Fout=1817.40 (A=063, B=141) +Fout=1817.60 (A=000, B=142) +Fout=1817.80 (A=001, B=142) +Fout=1818.00 (A=002, B=142) +Fout=1818.20 (A=003, B=142) +Fout=1818.40 (A=004, B=142) +Fout=1818.60 (A=005, B=142) +Fout=1818.80 (A=006, B=142) +Fout=1819.00 (A=007, B=142) +Fout=1819.20 (A=008, B=142) +Fout=1819.40 (A=009, B=142) +Fout=1819.60 (A=010, B=142) +Fout=1819.80 (A=011, B=142) +Fout=1820.00 (A=012, B=142) +Fout=1820.20 (A=013, B=142) +Fout=1820.40 (A=014, B=142) +Fout=1820.60 (A=015, B=142) +Fout=1820.80 (A=016, B=142) +Fout=1821.00 (A=017, B=142) +Fout=1821.20 (A=018, B=142) +Fout=1821.40 (A=019, B=142) +Fout=1821.60 (A=020, B=142) +Fout=1821.80 (A=021, B=142) +Fout=1822.00 (A=022, B=142) +Fout=1822.20 (A=023, B=142) +Fout=1822.40 (A=024, B=142) +Fout=1822.60 (A=025, B=142) +Fout=1822.80 (A=026, B=142) +Fout=1823.00 (A=027, B=142) +Fout=1823.20 (A=028, B=142) +Fout=1823.40 (A=029, B=142) +Fout=1823.60 (A=030, B=142) +Fout=1823.80 (A=031, B=142) +Fout=1824.00 (A=032, B=142) +Fout=1824.20 (A=033, B=142) +Fout=1824.40 (A=034, B=142) +Fout=1824.60 (A=035, B=142) +Fout=1824.80 (A=036, B=142) +Fout=1825.00 (A=037, B=142) +Fout=1825.20 (A=038, B=142) +Fout=1825.40 (A=039, B=142) +Fout=1825.60 (A=040, B=142) +Fout=1825.80 (A=041, B=142) +Fout=1826.00 (A=042, B=142) +Fout=1826.20 (A=043, B=142) +Fout=1826.40 (A=044, B=142) +Fout=1826.60 (A=045, B=142) +Fout=1826.80 (A=046, B=142) +Fout=1827.00 (A=047, B=142) +Fout=1827.20 (A=048, B=142) +Fout=1827.40 (A=049, B=142) +Fout=1827.60 (A=050, B=142) +Fout=1827.80 (A=051, B=142) +Fout=1828.00 (A=052, B=142) +Fout=1828.20 (A=053, B=142) +Fout=1828.40 (A=054, B=142) +Fout=1828.60 (A=055, B=142) +Fout=1828.80 (A=056, B=142) +Fout=1829.00 (A=057, B=142) +Fout=1829.20 (A=058, B=142) +Fout=1829.40 (A=059, B=142) +Fout=1829.60 (A=060, B=142) +Fout=1829.80 (A=061, B=142) +Fout=1830.00 (A=062, B=142) +Fout=1830.20 (A=063, B=142) +Fout=1830.40 (A=000, B=143) +Fout=1830.60 (A=001, B=143) +Fout=1830.80 (A=002, B=143) +Fout=1831.00 (A=003, B=143) +Fout=1831.20 (A=004, B=143) +Fout=1831.40 (A=005, B=143) +Fout=1831.60 (A=006, B=143) +Fout=1831.80 (A=007, B=143) +Fout=1832.00 (A=008, B=143) +Fout=1832.20 (A=009, B=143) +Fout=1832.40 (A=010, B=143) +Fout=1832.60 (A=011, B=143) +Fout=1832.80 (A=012, B=143) +Fout=1833.00 (A=013, B=143) +Fout=1833.20 (A=014, B=143) +Fout=1833.40 (A=015, B=143) +Fout=1833.60 (A=016, B=143) +Fout=1833.80 (A=017, B=143) +Fout=1834.00 (A=018, B=143) +Fout=1834.20 (A=019, B=143) +Fout=1834.40 (A=020, B=143) +Fout=1834.60 (A=021, B=143) +Fout=1834.80 (A=022, B=143) +Fout=1835.00 (A=023, B=143) +Fout=1835.20 (A=024, B=143) +Fout=1835.40 (A=025, B=143) +Fout=1835.60 (A=026, B=143) +Fout=1835.80 (A=027, B=143) +Fout=1836.00 (A=028, B=143) +Fout=1836.20 (A=029, B=143) +Fout=1836.40 (A=030, B=143) +Fout=1836.60 (A=031, B=143) +Fout=1836.80 (A=032, B=143) +Fout=1837.00 (A=033, B=143) +Fout=1837.20 (A=034, B=143) +Fout=1837.40 (A=035, B=143) +Fout=1837.60 (A=036, B=143) +Fout=1837.80 (A=037, B=143) +Fout=1838.00 (A=038, B=143) +Fout=1838.20 (A=039, B=143) +Fout=1838.40 (A=040, B=143) +Fout=1838.60 (A=041, B=143) +Fout=1838.80 (A=042, B=143) +Fout=1839.00 (A=043, B=143) +Fout=1839.20 (A=044, B=143) +Fout=1839.40 (A=045, B=143) +Fout=1839.60 (A=046, B=143) +Fout=1839.80 (A=047, B=143) +Fout=1840.00 (A=048, B=143) +Fout=1840.20 (A=049, B=143) +Fout=1840.40 (A=050, B=143) +Fout=1840.60 (A=051, B=143) +Fout=1840.80 (A=052, B=143) +Fout=1841.00 (A=053, B=143) +Fout=1841.20 (A=054, B=143) +Fout=1841.40 (A=055, B=143) +Fout=1841.60 (A=056, B=143) +Fout=1841.80 (A=057, B=143) +Fout=1842.00 (A=058, B=143) +Fout=1842.20 (A=059, B=143) +Fout=1842.40 (A=060, B=143) +Fout=1842.60 (A=061, B=143) +Fout=1842.80 (A=062, B=143) +Fout=1843.00 (A=063, B=143) +Fout=1843.20 (A=000, B=144) +Fout=1843.40 (A=001, B=144) +Fout=1843.60 (A=002, B=144) +Fout=1843.80 (A=003, B=144) +Fout=1844.00 (A=004, B=144) +Fout=1844.20 (A=005, B=144) +Fout=1844.40 (A=006, B=144) +Fout=1844.60 (A=007, B=144) +Fout=1844.80 (A=008, B=144) +Fout=1845.00 (A=009, B=144) +Fout=1845.20 (A=010, B=144) +Fout=1845.40 (A=011, B=144) +Fout=1845.60 (A=012, B=144) +Fout=1845.80 (A=013, B=144) +Fout=1846.00 (A=014, B=144) +Fout=1846.20 (A=015, B=144) +Fout=1846.40 (A=016, B=144) +Fout=1846.60 (A=017, B=144) +Fout=1846.80 (A=018, B=144) +Fout=1847.00 (A=019, B=144) +Fout=1847.20 (A=020, B=144) +Fout=1847.40 (A=021, B=144) +Fout=1847.60 (A=022, B=144) +Fout=1847.80 (A=023, B=144) +Fout=1848.00 (A=024, B=144) +Fout=1848.20 (A=025, B=144) +Fout=1848.40 (A=026, B=144) +Fout=1848.60 (A=027, B=144) +Fout=1848.80 (A=028, B=144) +Fout=1849.00 (A=029, B=144) +Fout=1849.20 (A=030, B=144) +Fout=1849.40 (A=031, B=144) +Fout=1849.60 (A=032, B=144) +Fout=1849.80 (A=033, B=144) +Fout=1850.00 (A=034, B=144) +Fout=1850.20 (A=035, B=144) +Fout=1850.40 (A=036, B=144) +Fout=1850.60 (A=037, B=144) +Fout=1850.80 (A=038, B=144) +Fout=1851.00 (A=039, B=144) +Fout=1851.20 (A=040, B=144) +Fout=1851.40 (A=041, B=144) +Fout=1851.60 (A=042, B=144) +Fout=1851.80 (A=043, B=144) +Fout=1852.00 (A=044, B=144) +Fout=1852.20 (A=045, B=144) +Fout=1852.40 (A=046, B=144) +Fout=1852.60 (A=047, B=144) +Fout=1852.80 (A=048, B=144) +Fout=1853.00 (A=049, B=144) +Fout=1853.20 (A=050, B=144) +Fout=1853.40 (A=051, B=144) +Fout=1853.60 (A=052, B=144) +Fout=1853.80 (A=053, B=144) +Fout=1854.00 (A=054, B=144) +Fout=1854.20 (A=055, B=144) +Fout=1854.40 (A=056, B=144) +Fout=1854.60 (A=057, B=144) +Fout=1854.80 (A=058, B=144) +Fout=1855.00 (A=059, B=144) +Fout=1855.20 (A=060, B=144) +Fout=1855.40 (A=061, B=144) +Fout=1855.60 (A=062, B=144) +Fout=1855.80 (A=063, B=144) +Fout=1856.00 (A=000, B=145) +Fout=1856.20 (A=001, B=145) +Fout=1856.40 (A=002, B=145) +Fout=1856.60 (A=003, B=145) +Fout=1856.80 (A=004, B=145) +Fout=1857.00 (A=005, B=145) +Fout=1857.20 (A=006, B=145) +Fout=1857.40 (A=007, B=145) +Fout=1857.60 (A=008, B=145) +Fout=1857.80 (A=009, B=145) +Fout=1858.00 (A=010, B=145) +Fout=1858.20 (A=011, B=145) +Fout=1858.40 (A=012, B=145) +Fout=1858.60 (A=013, B=145) +Fout=1858.80 (A=014, B=145) +Fout=1859.00 (A=015, B=145) +Fout=1859.20 (A=016, B=145) +Fout=1859.40 (A=017, B=145) +Fout=1859.60 (A=018, B=145) +Fout=1859.80 (A=019, B=145) +Fout=1860.00 (A=020, B=145) +Fout=1860.20 (A=021, B=145) +Fout=1860.40 (A=022, B=145) +Fout=1860.60 (A=023, B=145) +Fout=1860.80 (A=024, B=145) +Fout=1861.00 (A=025, B=145) +Fout=1861.20 (A=026, B=145) +Fout=1861.40 (A=027, B=145) +Fout=1861.60 (A=028, B=145) +Fout=1861.80 (A=029, B=145) +Fout=1862.00 (A=030, B=145) +Fout=1862.20 (A=031, B=145) +Fout=1862.40 (A=032, B=145) +Fout=1862.60 (A=033, B=145) +Fout=1862.80 (A=034, B=145) +Fout=1863.00 (A=035, B=145) +Fout=1863.20 (A=036, B=145) +Fout=1863.40 (A=037, B=145) +Fout=1863.60 (A=038, B=145) +Fout=1863.80 (A=039, B=145) +Fout=1864.00 (A=040, B=145) +Fout=1864.20 (A=041, B=145) +Fout=1864.40 (A=042, B=145) +Fout=1864.60 (A=043, B=145) +Fout=1864.80 (A=044, B=145) +Fout=1865.00 (A=045, B=145) +Fout=1865.20 (A=046, B=145) +Fout=1865.40 (A=047, B=145) +Fout=1865.60 (A=048, B=145) +Fout=1865.80 (A=049, B=145) +Fout=1866.00 (A=050, B=145) +Fout=1866.20 (A=051, B=145) +Fout=1866.40 (A=052, B=145) +Fout=1866.60 (A=053, B=145) +Fout=1866.80 (A=054, B=145) +Fout=1867.00 (A=055, B=145) +Fout=1867.20 (A=056, B=145) +Fout=1867.40 (A=057, B=145) +Fout=1867.60 (A=058, B=145) +Fout=1867.80 (A=059, B=145) +Fout=1868.00 (A=060, B=145) +Fout=1868.20 (A=061, B=145) +Fout=1868.40 (A=062, B=145) +Fout=1868.60 (A=063, B=145) +Fout=1868.80 (A=000, B=146) +Fout=1869.00 (A=001, B=146) +Fout=1869.20 (A=002, B=146) +Fout=1869.40 (A=003, B=146) +Fout=1869.60 (A=004, B=146) +Fout=1869.80 (A=005, B=146) +Fout=1870.00 (A=006, B=146) +Fout=1870.20 (A=007, B=146) +Fout=1870.40 (A=008, B=146) +Fout=1870.60 (A=009, B=146) +Fout=1870.80 (A=010, B=146) +Fout=1871.00 (A=011, B=146) +Fout=1871.20 (A=012, B=146) +Fout=1871.40 (A=013, B=146) +Fout=1871.60 (A=014, B=146) +Fout=1871.80 (A=015, B=146) +Fout=1872.00 (A=016, B=146) +Fout=1872.20 (A=017, B=146) +Fout=1872.40 (A=018, B=146) +Fout=1872.60 (A=019, B=146) +Fout=1872.80 (A=020, B=146) +Fout=1873.00 (A=021, B=146) +Fout=1873.20 (A=022, B=146) +Fout=1873.40 (A=023, B=146) +Fout=1873.60 (A=024, B=146) +Fout=1873.80 (A=025, B=146) +Fout=1874.00 (A=026, B=146) +Fout=1874.20 (A=027, B=146) +Fout=1874.40 (A=028, B=146) +Fout=1874.60 (A=029, B=146) +Fout=1874.80 (A=030, B=146) +Fout=1875.00 (A=031, B=146) +Fout=1875.20 (A=032, B=146) +Fout=1875.40 (A=033, B=146) +Fout=1875.60 (A=034, B=146) +Fout=1875.80 (A=035, B=146) +Fout=1876.00 (A=036, B=146) +Fout=1876.20 (A=037, B=146) +Fout=1876.40 (A=038, B=146) +Fout=1876.60 (A=039, B=146) +Fout=1876.80 (A=040, B=146) +Fout=1877.00 (A=041, B=146) +Fout=1877.20 (A=042, B=146) +Fout=1877.40 (A=043, B=146) +Fout=1877.60 (A=044, B=146) +Fout=1877.80 (A=045, B=146) +Fout=1878.00 (A=046, B=146) +Fout=1878.20 (A=047, B=146) +Fout=1878.40 (A=048, B=146) +Fout=1878.60 (A=049, B=146) +Fout=1878.80 (A=050, B=146) +Fout=1879.00 (A=051, B=146) +Fout=1879.20 (A=052, B=146) +Fout=1879.40 (A=053, B=146) +Fout=1879.60 (A=054, B=146) +Fout=1879.80 (A=055, B=146) +Fout=1880.00 (A=056, B=146) +Fout=1880.20 (A=057, B=146) +Fout=1880.40 (A=058, B=146) +Fout=1880.60 (A=059, B=146) +Fout=1880.80 (A=060, B=146) +Fout=1881.00 (A=061, B=146) +Fout=1881.20 (A=062, B=146) +Fout=1881.40 (A=063, B=146) +Fout=1881.60 (A=000, B=147) +Fout=1881.80 (A=001, B=147) +Fout=1882.00 (A=002, B=147) +Fout=1882.20 (A=003, B=147) +Fout=1882.40 (A=004, B=147) +Fout=1882.60 (A=005, B=147) +Fout=1882.80 (A=006, B=147) +Fout=1883.00 (A=007, B=147) +Fout=1883.20 (A=008, B=147) +Fout=1883.40 (A=009, B=147) +Fout=1883.60 (A=010, B=147) +Fout=1883.80 (A=011, B=147) +Fout=1884.00 (A=012, B=147) +Fout=1884.20 (A=013, B=147) +Fout=1884.40 (A=014, B=147) +Fout=1884.60 (A=015, B=147) +Fout=1884.80 (A=016, B=147) +Fout=1885.00 (A=017, B=147) +Fout=1885.20 (A=018, B=147) +Fout=1885.40 (A=019, B=147) +Fout=1885.60 (A=020, B=147) +Fout=1885.80 (A=021, B=147) +Fout=1886.00 (A=022, B=147) +Fout=1886.20 (A=023, B=147) +Fout=1886.40 (A=024, B=147) +Fout=1886.60 (A=025, B=147) +Fout=1886.80 (A=026, B=147) +Fout=1887.00 (A=027, B=147) +Fout=1887.20 (A=028, B=147) +Fout=1887.40 (A=029, B=147) +Fout=1887.60 (A=030, B=147) +Fout=1887.80 (A=031, B=147) +Fout=1888.00 (A=032, B=147) +Fout=1888.20 (A=033, B=147) +Fout=1888.40 (A=034, B=147) +Fout=1888.60 (A=035, B=147) +Fout=1888.80 (A=036, B=147) +Fout=1889.00 (A=037, B=147) +Fout=1889.20 (A=038, B=147) +Fout=1889.40 (A=039, B=147) +Fout=1889.60 (A=040, B=147) +Fout=1889.80 (A=041, B=147) +Fout=1890.00 (A=042, B=147) +Fout=1890.20 (A=043, B=147) +Fout=1890.40 (A=044, B=147) +Fout=1890.60 (A=045, B=147) +Fout=1890.80 (A=046, B=147) +Fout=1891.00 (A=047, B=147) +Fout=1891.20 (A=048, B=147) +Fout=1891.40 (A=049, B=147) +Fout=1891.60 (A=050, B=147) +Fout=1891.80 (A=051, B=147) +Fout=1892.00 (A=052, B=147) +Fout=1892.20 (A=053, B=147) +Fout=1892.40 (A=054, B=147) +Fout=1892.60 (A=055, B=147) +Fout=1892.80 (A=056, B=147) +Fout=1893.00 (A=057, B=147) +Fout=1893.20 (A=058, B=147) +Fout=1893.40 (A=059, B=147) +Fout=1893.60 (A=060, B=147) +Fout=1893.80 (A=061, B=147) +Fout=1894.00 (A=062, B=147) +Fout=1894.20 (A=063, B=147) +Fout=1894.40 (A=000, B=148) +Fout=1894.60 (A=001, B=148) +Fout=1894.80 (A=002, B=148) +Fout=1895.00 (A=003, B=148) +Fout=1895.20 (A=004, B=148) +Fout=1895.40 (A=005, B=148) +Fout=1895.60 (A=006, B=148) +Fout=1895.80 (A=007, B=148) +Fout=1896.00 (A=008, B=148) +Fout=1896.20 (A=009, B=148) +Fout=1896.40 (A=010, B=148) +Fout=1896.60 (A=011, B=148) +Fout=1896.80 (A=012, B=148) +Fout=1897.00 (A=013, B=148) +Fout=1897.20 (A=014, B=148) +Fout=1897.40 (A=015, B=148) +Fout=1897.60 (A=016, B=148) +Fout=1897.80 (A=017, B=148) +Fout=1898.00 (A=018, B=148) +Fout=1898.20 (A=019, B=148) +Fout=1898.40 (A=020, B=148) +Fout=1898.60 (A=021, B=148) +Fout=1898.80 (A=022, B=148) +Fout=1899.00 (A=023, B=148) +Fout=1899.20 (A=024, B=148) +Fout=1899.40 (A=025, B=148) +Fout=1899.60 (A=026, B=148) +Fout=1899.80 (A=027, B=148) +Fout=1900.00 (A=028, B=148) +Fout=1900.20 (A=029, B=148) +Fout=1900.40 (A=030, B=148) +Fout=1900.60 (A=031, B=148) +Fout=1900.80 (A=032, B=148) +Fout=1901.00 (A=033, B=148) +Fout=1901.20 (A=034, B=148) +Fout=1901.40 (A=035, B=148) +Fout=1901.60 (A=036, B=148) +Fout=1901.80 (A=037, B=148) +Fout=1902.00 (A=038, B=148) +Fout=1902.20 (A=039, B=148) +Fout=1902.40 (A=040, B=148) +Fout=1902.60 (A=041, B=148) +Fout=1902.80 (A=042, B=148) +Fout=1903.00 (A=043, B=148) +Fout=1903.20 (A=044, B=148) +Fout=1903.40 (A=045, B=148) +Fout=1903.60 (A=046, B=148) +Fout=1903.80 (A=047, B=148) +Fout=1904.00 (A=048, B=148) +Fout=1904.20 (A=049, B=148) +Fout=1904.40 (A=050, B=148) +Fout=1904.60 (A=051, B=148) +Fout=1904.80 (A=052, B=148) +Fout=1905.00 (A=053, B=148) +Fout=1905.20 (A=054, B=148) +Fout=1905.40 (A=055, B=148) +Fout=1905.60 (A=056, B=148) +Fout=1905.80 (A=057, B=148) +Fout=1906.00 (A=058, B=148) +Fout=1906.20 (A=059, B=148) +Fout=1906.40 (A=060, B=148) +Fout=1906.60 (A=061, B=148) +Fout=1906.80 (A=062, B=148) +Fout=1907.00 (A=063, B=148) +Fout=1907.20 (A=000, B=149) +Fout=1907.40 (A=001, B=149) +Fout=1907.60 (A=002, B=149) +Fout=1907.80 (A=003, B=149) +Fout=1908.00 (A=004, B=149) +Fout=1908.20 (A=005, B=149) +Fout=1908.40 (A=006, B=149) +Fout=1908.60 (A=007, B=149) +Fout=1908.80 (A=008, B=149) +Fout=1909.00 (A=009, B=149) +Fout=1909.20 (A=010, B=149) +Fout=1909.40 (A=011, B=149) +Fout=1909.60 (A=012, B=149) +Fout=1909.80 (A=013, B=149) +Fout=1910.00 (A=014, B=149) +Fout=1910.20 (A=015, B=149) +Fout=1910.40 (A=016, B=149) +Fout=1910.60 (A=017, B=149) +Fout=1910.80 (A=018, B=149) +Fout=1911.00 (A=019, B=149) +Fout=1911.20 (A=020, B=149) +Fout=1911.40 (A=021, B=149) +Fout=1911.60 (A=022, B=149) +Fout=1911.80 (A=023, B=149) +Fout=1912.00 (A=024, B=149) +Fout=1912.20 (A=025, B=149) +Fout=1912.40 (A=026, B=149) +Fout=1912.60 (A=027, B=149) +Fout=1912.80 (A=028, B=149) +Fout=1913.00 (A=029, B=149) +Fout=1913.20 (A=030, B=149) +Fout=1913.40 (A=031, B=149) +Fout=1913.60 (A=032, B=149) +Fout=1913.80 (A=033, B=149) +Fout=1914.00 (A=034, B=149) +Fout=1914.20 (A=035, B=149) +Fout=1914.40 (A=036, B=149) +Fout=1914.60 (A=037, B=149) +Fout=1914.80 (A=038, B=149) +Fout=1915.00 (A=039, B=149) +Fout=1915.20 (A=040, B=149) +Fout=1915.40 (A=041, B=149) +Fout=1915.60 (A=042, B=149) +Fout=1915.80 (A=043, B=149) +Fout=1916.00 (A=044, B=149) +Fout=1916.20 (A=045, B=149) +Fout=1916.40 (A=046, B=149) +Fout=1916.60 (A=047, B=149) +Fout=1916.80 (A=048, B=149) +Fout=1917.00 (A=049, B=149) +Fout=1917.20 (A=050, B=149) +Fout=1917.40 (A=051, B=149) +Fout=1917.60 (A=052, B=149) +Fout=1917.80 (A=053, B=149) +Fout=1918.00 (A=054, B=149) +Fout=1918.20 (A=055, B=149) +Fout=1918.40 (A=056, B=149) +Fout=1918.60 (A=057, B=149) +Fout=1918.80 (A=058, B=149) +Fout=1919.00 (A=059, B=149) +Fout=1919.20 (A=060, B=149) +Fout=1919.40 (A=061, B=149) +Fout=1919.60 (A=062, B=149) +Fout=1919.80 (A=063, B=149) diff --git a/src/host/rita_pll/rita_pll_notes.txt b/src/host/rita_pll/rita_pll_notes.txt new file mode 100644 index 00000000..8557d3ae --- /dev/null +++ b/src/host/rita_pll/rita_pll_notes.txt @@ -0,0 +1,8 @@ + +Regular Operation as per DS GSM SPEC + +GSM900 Tx: 870.4 ... 921.4 MHz 880.0 ... 914.8 +GSM900 Rx: 864.4 ... 966.2 MHz 925.0 ... 959.8 + +GSM1800 Tx: 1702.4 ... 1919.8 MHz 1710.2 ... 1784.8 +GSM1800 Rx: 1804.8 ... 1996.4 MHz 1805.2 ... 1879.8 diff --git a/src/shared/libosmocore/.gitignore b/src/shared/libosmocore/.gitignore new file mode 100644 index 00000000..06904fa0 --- /dev/null +++ b/src/shared/libosmocore/.gitignore @@ -0,0 +1,30 @@ +Makefile +Makefile.in +.deps +.libs +*.o +*.lo +*.la +*.pc +aclocal.m4 +m4/*.m4 +autom4te.cache +config.h* +config.sub +config.log +config.status +config.guess +configure +depcomp +missing +ltmain.sh +install-sh +stamp-h1 +libtool + +.tarball-version +.version + +tests/sms/sms_test +tests/timer/timer_test + diff --git a/src/shared/libosmocore/COPYING b/src/shared/libosmocore/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/shared/libosmocore/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/src/shared/libosmocore/Makefile.am b/src/shared/libosmocore/Makefile.am new file mode 100644 index 00000000..81da6294 --- /dev/null +++ b/src/shared/libosmocore/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 +ACLOCAL_AMFLAGS = -I m4 + +INCLUDES = $(all_includes) -I$(top_srcdir)/include +SUBDIRS = include src tests + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libosmocore.pc libosmovty.pc + +BUILT_SOURCES = $(top_srcdir)/.version +$(top_srcdir)/.version: + echo $(VERSION) > $@-t && mv $@-t $@ +dist-hook: + echo $(VERSION) > $(distdir)/.tarball-version diff --git a/src/shared/libosmocore/configure.in b/src/shared/libosmocore/configure.in new file mode 100644 index 00000000..e3e178c0 --- /dev/null +++ b/src/shared/libosmocore/configure.in @@ -0,0 +1,72 @@ +AC_INIT([libosmocore], + m4_esyscmd([./git-version-gen .tarball-version]), + [openbsc-devel@lists.openbsc.org]) + +AM_INIT_AUTOMAKE([dist-bzip2]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +dnl checks for programs +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +LT_INIT +AC_PROG_LIBTOOL + +AC_CONFIG_MACRO_DIR([m4]) + +dnl checks for header files +AC_HEADER_STDC +AC_CHECK_HEADERS(execinfo.h sys/select.h) + +# 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(config.h) + +AC_ARG_ENABLE(talloc, + [ --disable-talloc Disable building talloc memory allocator ], + [enable_talloc=0], [enable_talloc=1]) +AM_CONDITIONAL(ENABLE_TALLOC, test "x$enable_talloc" = "x1") + +AC_ARG_ENABLE(plugin, + [ --disable-plugin Disable support for dlopen plugins ], + [enable_plugin=0], [enable_plugin=1]) +AM_CONDITIONAL(ENABLE_PLUGIN, test "x$enable_plugin" = "x1") + +AC_ARG_ENABLE(tests, + [ --disable-tests Disable building test programs ], + [enable_tests=0], [enable_tests=1]) +AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "x1") + +AC_ARG_ENABLE(vty, + [ --disable-vty Disable building VTY telnet interface ], + [enable_vty=0], [enable_vty=1]) +AM_CONDITIONAL(ENABLE_VTY, test "x$enable_vty" = "x1") + + +AC_OUTPUT( + libosmocore.pc + libosmovty.pc + include/osmocom/Makefile + include/osmocom/vty/Makefile + include/osmocom/crypt/Makefile + include/osmocore/Makefile + include/osmocore/protocol/Makefile + include/Makefile + src/Makefile + src/vty/Makefile + tests/Makefile + tests/timer/Makefile + tests/sms/Makefile + Makefile) diff --git a/src/shared/libosmocore/git-version-gen b/src/shared/libosmocore/git-version-gen new file mode 100755 index 00000000..42cf3d2b --- /dev/null +++ b/src/shared/libosmocore/git-version-gen @@ -0,0 +1,151 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-01-28.01 + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; +esac + +tarball_version_file=$1 +nl=' +' + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif + v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && case $v in + [0-9]*) ;; + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git status > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d '\012' + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/shared/libosmocore/include/Makefile.am b/src/shared/libosmocore/include/Makefile.am new file mode 100644 index 00000000..185c6968 --- /dev/null +++ b/src/shared/libosmocore/include/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = osmocom osmocore diff --git a/src/shared/libosmocore/include/osmocom/Makefile.am b/src/shared/libosmocore/include/osmocom/Makefile.am new file mode 100644 index 00000000..fd9074cd --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/Makefile.am @@ -0,0 +1,5 @@ +if ENABLE_VTY +SUBDIRS = vty crypt +else +SUBDIRS = crypt +endif diff --git a/src/shared/libosmocore/include/osmocom/crypt/Makefile.am b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am new file mode 100644 index 00000000..7ce69fdd --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/crypt/Makefile.am @@ -0,0 +1,3 @@ +osmocrypt_HEADERS = gprs_cipher.h + +osmocryptdir = $(includedir)/osmocom/crypt diff --git a/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h new file mode 100644 index 00000000..3e514ec7 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/crypt/gprs_cipher.h @@ -0,0 +1,54 @@ +#ifndef _GPRS_CIPHER_H +#define _GPRS_CIPHER_H + +#include <osmocore/linuxlist.h> + +#define GSM0464_CIPH_MAX_BLOCK 1523 + +enum gprs_ciph_algo { + GPRS_ALGO_GEA0, + GPRS_ALGO_GEA1, + GPRS_ALGO_GEA2, + GPRS_ALGO_GEA3, + _GPRS_ALGO_NUM +}; + +enum gprs_cipher_direction { + GPRS_CIPH_MS2SGSN, + GPRS_CIPH_SGSN2MS, +}; + +/* An implementation of a GPRS cipher */ +struct gprs_cipher_impl { + struct llist_head list; + enum gprs_ciph_algo algo; + const char *name; + unsigned int priority; + + /* As specified in 04.64 Annex A. Uses Kc, IV and direction + * to generate the 1523 bytes cipher stream that need to be + * XORed wit the plaintext for encrypt / ciphertext for decrypt */ + int (*run)(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv, + enum gprs_cipher_direction direction); +}; + +/* register a cipher with the core (from a plugin) */ +int gprs_cipher_register(struct gprs_cipher_impl *ciph); + +/* load all available GPRS cipher plugins */ +int gprs_cipher_load(const char *path); + +/* function to be called by core code */ +int gprs_cipher_run(uint8_t *out, uint16_t len, enum gprs_ciph_algo algo, + uint64_t kc, uint32_t iv, enum gprs_cipher_direction dir); + +/* Do we have an implementation for this cipher? */ +int gprs_cipher_supported(enum gprs_ciph_algo algo); + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_ui(uint32_t iov_ui, uint8_t sapi, uint32_t lfn, uint32_t oc); + +/* GSM TS 04.64 / Section A.2.1 : Generation of 'input' */ +uint32_t gprs_cipher_gen_input_i(uint32_t iov_i, uint32_t lfn, uint32_t oc); + +#endif /* _GPRS_CIPHER_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/Makefile.am b/src/shared/libosmocore/include/osmocom/vty/Makefile.am new file mode 100644 index 00000000..d2f0616d --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/Makefile.am @@ -0,0 +1,4 @@ +osmovty_HEADERS = buffer.h command.h vector.h vty.h \ + telnet_interface.h logging.h + +osmovtydir = $(includedir)/osmocom/vty diff --git a/src/shared/libosmocore/include/osmocom/vty/buffer.h b/src/shared/libosmocore/include/osmocom/vty/buffer.h new file mode 100644 index 00000000..c9467a91 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/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(void *ctx, 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/src/shared/libosmocore/include/osmocom/vty/command.h b/src/shared/libosmocore/include/osmocom/vty/command.h new file mode 100644 index 00000000..69e9e772 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/command.h @@ -0,0 +1,349 @@ +/* + * 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; + + const struct vty_app_info *app_info; +}; + +/* There are some command levels which called from command node. */ +enum node_type { + 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. */ + + VTY_NODE, /* Vty node. */ + + _LAST_OSMOVTY_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) \ + static struct cmd_element cmdname = \ + { \ + .string = cmdstr, \ + .func = funcname, \ + .doc = helpstr, \ + .attr = attrs, \ + .daemon = dnum, \ + }; + +/* global (non static) cmd_element */ +#define gDEFUN_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) + +/* global (non static) cmd_element */ +#define gDEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_CMD_FUNC_DECL(funcname) \ + gDEFUN_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) + +/* global (non static) cmd_element */ +#define gALIAS(funcname, cmdname, cmdstr, helpstr) \ + gDEFUN_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 install_element_ve(struct cmd_element *cmd); +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_exit_cmd; +extern struct cmd_element config_help_cmd; +extern struct cmd_element config_list_cmd; +char *host_config_file(); +void host_config_set(const char *); + +/* This is called from main when a daemon is invoked with -v or --version. */ +void print_version(int print_copyright); + +extern void *tall_vty_cmd_ctx; + +#endif /* _ZEBRA_COMMAND_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/logging.h b/src/shared/libosmocore/include/osmocom/vty/logging.h new file mode 100644 index 00000000..f8ffbc3e --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/logging.h @@ -0,0 +1,7 @@ +#ifndef _VTY_LOGGING_H +#define _VTY_LOGGING_H + +#define LOGGING_STR "Configure log message to this terminal\n" +#define FILTER_STR "Filter log messages\n" + +#endif /* _VTY_LOGGING_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h new file mode 100644 index 00000000..444e6497 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/telnet_interface.h @@ -0,0 +1,40 @@ +/* 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 <osmocore/logging.h> +#include <osmocore/select.h> + +#include <osmocom/vty/vty.h> + +struct telnet_connection { + struct llist_head entry; + void *priv; + struct bsc_fd fd; + struct vty *vty; + struct log_target *dbg; +}; + + +int telnet_init(void *tall_ctx, void *priv, int port); + +#endif diff --git a/src/shared/libosmocore/include/osmocom/vty/vector.h b/src/shared/libosmocore/include/osmocom/vty/vector.h new file mode 100644 index 00000000..22a184d6 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/vector.h @@ -0,0 +1,64 @@ +/* + * 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); + +extern void *tall_vty_vec_ctx; + +#endif /* _ZEBRA_VECTOR_H */ diff --git a/src/shared/libosmocore/include/osmocom/vty/vty.h b/src/shared/libosmocore/include/osmocom/vty/vty.h new file mode 100644 index 00000000..e7399ba1 --- /dev/null +++ b/src/shared/libosmocore/include/osmocom/vty/vty.h @@ -0,0 +1,159 @@ +#ifndef _VTY_H +#define _VTY_H + +#include <stdio.h> +#include <stdarg.h> + +/* GCC have printf type attribute check. */ +#ifdef __GNUC__ +#define VTY_PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b))) +#else +#define VTY_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_CLOSED, + 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; +} + +struct vty_app_info { + const char *name; + const char *version; + const char *copyright; + void *tall_ctx; + enum node_type (*go_parent_cb)(struct vty *vty); +}; + +/* Prototypes. */ +void vty_init(struct vty_app_info *app_info); +int vty_read_config_file(const char *file_name, void *priv); +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 *, ...) VTY_PRINTF_ATTRIBUTE(2, 3); +int vty_out_newline(struct vty *); +int vty_read(struct vty *vty); +//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 *); + +void *tall_vty_ctx; +#endif diff --git a/src/shared/libosmocore/include/osmocore/Makefile.am b/src/shared/libosmocore/include/osmocore/Makefile.am new file mode 100644 index 00000000..84859a4b --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/Makefile.am @@ -0,0 +1,13 @@ +osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \ + tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \ + gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \ + gsm48_ie.h logging.h gsm0808.h rate_ctr.h gsmtap_util.h \ + plugin.h + +if ENABLE_TALLOC +osmocore_HEADERS += talloc.h +endif + +osmocoredir = $(includedir)/osmocore + +SUBDIRS = protocol diff --git a/src/shared/libosmocore/include/osmocore/bitvec.h b/src/shared/libosmocore/include/osmocore/bitvec.h new file mode 100644 index 00000000..42977fb2 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/bitvec.h @@ -0,0 +1,75 @@ +#ifndef _BITVEC_H +#define _BITVEC_H + +/* bit vector utility routines */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +/* In GSM mac blocks, every bit can be 0 or 1, or L or H. L/H are + * defined relative to the 0x2b padding pattern */ +enum bit_value { + ZERO = 0, + ONE = 1, + L = 2, + H = 3, +}; + +struct bitvec { + unsigned int cur_bit; /* curser to the next unused bit */ + unsigned int data_len; /* length of data array in bytes */ + uint8_t *data; /* pointer to data array */ +}; + +/* check if the bit is 0 or 1 for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos(const struct bitvec *bv, unsigned int bitnr); + +/* check if the bit is L or H for a given position inside a bitvec */ +enum bit_value bitvec_get_bit_pos_high(const struct bitvec *bv, + unsigned int bitnr); + +/* get the Nth set bit inside the bit vector */ +unsigned int bitvec_get_nth_set_bit(const struct bitvec *bv, unsigned int n); + +/* Set a bit at given position */ +int bitvec_set_bit_pos(struct bitvec *bv, unsigned int bitnum, + enum bit_value bit); + +/* Set the next bit in the vector */ +int bitvec_set_bit(struct bitvec *bv, enum bit_value bit); + +/* get the next bit (low/high) inside a bitvec */ +int bitvec_get_bit_high(struct bitvec *bv); + +/* Set multiple bits at the current position */ +int bitvec_set_bits(struct bitvec *bv, enum bit_value *bits, int count); + +/* Add an unsigned integer (of length count bits) to current position */ +int bitvec_set_uint(struct bitvec *bv, unsigned int in, int count); + +/* get multiple bits (based on numeric value) from current pos */ +int bitvec_get_uint(struct bitvec *bv, int num_bits); + + +/* Pad the bit vector up to a certain bit position */ +int bitvec_spare_padding(struct bitvec *bv, unsigned int up_to_bit); + +#endif /* _BITVEC_H */ diff --git a/src/shared/libosmocore/include/osmocore/comp128.h b/src/shared/libosmocore/include/osmocore/comp128.h new file mode 100644 index 00000000..c37808f0 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/comp128.h @@ -0,0 +1,22 @@ +/* + * COMP128 header + * + * See comp128.c for details + */ + +#ifndef __COMP128_H__ +#define __COMP128_H__ + +#include <stdint.h> + +/* + * Performs the COMP128 algorithm (used as A3/A8) + * ki : uint8_t [16] + * srand : uint8_t [16] + * sres : uint8_t [4] + * kc : uint8_t [8] + */ +void comp128(uint8_t *ki, uint8_t *srand, uint8_t *sres, uint8_t *kc); + +#endif /* __COMP128_H__ */ + diff --git a/src/shared/libosmocore/include/osmocore/gsm0808.h b/src/shared/libosmocore/include/osmocore/gsm0808.h new file mode 100644 index 00000000..9166e54f --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/gsm0808.h @@ -0,0 +1,43 @@ +/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2009,2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef OSMOCORE_GSM0808_H +#define OSMOCORE_GSM0808_H + +#include "tlv.h" + +struct msgb; + +struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, int ci); +struct msgb *gsm0808_create_reset(void); +struct msgb *gsm0808_create_clear_complete(void); +struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id); +struct msgb *gsm0808_create_cipher_reject(uint8_t cause); +struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark, uint8_t length); +struct msgb *gsm0808_create_sapi_reject(uint8_t link_id); +struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause, + uint8_t chosen_channel, uint8_t encr_alg_id, + uint8_t speech_mode); +struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause); + +void gsm0808_prepend_dtap_header(struct msgb *msg, uint8_t link_id); + +const struct tlv_definition *gsm0808_att_tlvdef(); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/gsm48.h b/src/shared/libosmocore/include/osmocore/gsm48.h new file mode 100644 index 00000000..ffe0399b --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/gsm48.h @@ -0,0 +1,36 @@ +#ifndef _OSMOCORE_GSM48_H +#define _OSMOCORE_GSM48_H + +#include <osmocore/tlv.h> +#include <osmocore/protocol/gsm_04_08.h> +#include <osmocore/gsm48_ie.h> + +/* A parsed GPRS routing area */ +struct gprs_ra_id { + uint16_t mnc; + uint16_t mcc; + uint16_t lac; + uint8_t rac; +}; + +extern const struct tlv_definition gsm48_att_tlvdef; +extern const struct tlv_definition gsm48_rr_att_tlvdef; +extern const struct tlv_definition gsm48_mm_att_tlvdef; +const char *gsm48_cc_state_name(uint8_t state); +const char *gsm48_cc_msg_name(uint8_t msgtype); +const char *rr_cause_name(uint8_t cause); + +void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc, + uint16_t mnc, uint16_t lac); +int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi); +int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi); + +/* Convert Mobile Identity (10.5.1.4) to string */ +int gsm48_mi_to_string(char *string, const int str_len, + const uint8_t *mi, const int mi_len); + +/* Parse Routeing Area Identifier */ +void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf); +int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/gsm48_ie.h b/src/shared/libosmocore/include/osmocore/gsm48_ie.h new file mode 100644 index 00000000..200619a7 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/gsm48_ie.h @@ -0,0 +1,107 @@ +#ifndef _OSMOCORE_GSM48_IE_H +#define _OSMOCORE_GSM48_IE_H + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocore/msgb.h> +#include <osmocore/tlv.h> +#include <osmocore/mncc.h> +#include <osmocore/protocol/gsm_04_08.h> + +/* decode a 'called/calling/connect party BCD number' as in 10.5.4.7 */ +int gsm48_decode_bcd_number(char *output, int output_len, + const uint8_t *bcd_lv, int h_len); + +/* convert a ASCII phone number to 'called/calling/connect party BCD number' */ +int gsm48_encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, + int h_len, const char *input); +/* decode 'bearer capability' */ +int gsm48_decode_bearer_cap(struct gsm_mncc_bearer_cap *bcap, + const uint8_t *lv); +/* encode 'bearer capability' */ +int gsm48_encode_bearer_cap(struct msgb *msg, int lv_only, + const struct gsm_mncc_bearer_cap *bcap); +/* decode 'call control cap' */ +int gsm48_decode_cccap(struct gsm_mncc_cccap *ccap, const uint8_t *lv); +/* encode 'call control cap' */ +int gsm48_encode_cccap(struct msgb *msg, + const struct gsm_mncc_cccap *ccap); +/* decode 'called party BCD number' */ +int gsm48_decode_called(struct gsm_mncc_number *called, + const uint8_t *lv); +/* encode 'called party BCD number' */ +int gsm48_encode_called(struct msgb *msg, + const struct gsm_mncc_number *called); +/* decode callerid of various IEs */ +int gsm48_decode_callerid(struct gsm_mncc_number *callerid, + const uint8_t *lv); +/* encode callerid of various IEs */ +int gsm48_encode_callerid(struct msgb *msg, int ie, int max_len, + const struct gsm_mncc_number *callerid); +/* decode 'cause' */ +int gsm48_decode_cause(struct gsm_mncc_cause *cause, + const uint8_t *lv); +/* encode 'cause' */ +int gsm48_encode_cause(struct msgb *msg, int lv_only, + const struct gsm_mncc_cause *cause); +/* decode 'calling number' */ +int gsm48_decode_calling(struct gsm_mncc_number *calling, + const uint8_t *lv); +/* encode 'calling number' */ +int gsm48_encode_calling(struct msgb *msg, + const struct gsm_mncc_number *calling); +/* decode 'connected number' */ +int gsm48_decode_connected(struct gsm_mncc_number *connected, + const uint8_t *lv); +/* encode 'connected number' */ +int gsm48_encode_connected(struct msgb *msg, + const struct gsm_mncc_number *connected); +/* decode 'redirecting number' */ +int gsm48_decode_redirecting(struct gsm_mncc_number *redirecting, + const uint8_t *lv); +/* encode 'redirecting number' */ +int gsm48_encode_redirecting(struct msgb *msg, + const struct gsm_mncc_number *redirecting); +/* decode 'facility' */ +int gsm48_decode_facility(struct gsm_mncc_facility *facility, + const uint8_t *lv); +/* encode 'facility' */ +int gsm48_encode_facility(struct msgb *msg, int lv_only, + const struct gsm_mncc_facility *facility); +/* decode 'notify' */ +int gsm48_decode_notify(int *notify, const uint8_t *v); +/* encode 'notify' */ +int gsm48_encode_notify(struct msgb *msg, int notify); +/* decode 'signal' */ +int gsm48_decode_signal(int *signal, const uint8_t *v); +/* encode 'signal' */ +int gsm48_encode_signal(struct msgb *msg, int signal); +/* decode 'keypad' */ +int gsm48_decode_keypad(int *keypad, const uint8_t *lv); +/* encode 'keypad' */ +int gsm48_encode_keypad(struct msgb *msg, int keypad); +/* decode 'progress' */ +int gsm48_decode_progress(struct gsm_mncc_progress *progress, + const uint8_t *lv); +/* encode 'progress' */ +int gsm48_encode_progress(struct msgb *msg, int lv_only, + const struct gsm_mncc_progress *p); +/* decode 'user-user' */ +int gsm48_decode_useruser(struct gsm_mncc_useruser *uu, + const uint8_t *lv); +/* encode 'useruser' */ +int gsm48_encode_useruser(struct msgb *msg, int lv_only, + const struct gsm_mncc_useruser *uu); +/* decode 'ss version' */ +int gsm48_decode_ssversion(struct gsm_mncc_ssversion *ssv, + const uint8_t *lv); +/* encode 'ss version' */ +int gsm48_encode_ssversion(struct msgb *msg, + const struct gsm_mncc_ssversion *ssv); +/* decode 'more data' does not require a function, because it has no value */ +/* encode 'more data' */ +int gsm48_encode_more(struct msgb *msg); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/gsm_utils.h b/src/shared/libosmocore/include/osmocore/gsm_utils.h new file mode 100644 index 00000000..7dc2388b --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/gsm_utils.h @@ -0,0 +1,117 @@ +/* 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-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef GSM_UTILS_H +#define GSM_UTILS_H + +#include <stdint.h> + +#define ADD_MODULO(sum, delta, modulo) do { \ + if ((sum += delta) >= modulo) \ + sum -= modulo; \ + } while (0) + +#define GSM_MAX_FN (26*51*2048) + +struct gsm_time { + uint32_t fn; /* FN count */ + uint16_t t1; /* FN div (26*51) */ + uint8_t t2; /* FN modulo 26 */ + uint8_t t3; /* FN modulo 51 */ + uint8_t tc; +}; + +enum gsm_band { + GSM_BAND_850 = 1, + GSM_BAND_900 = 2, + GSM_BAND_1800 = 4, + GSM_BAND_1900 = 8, + GSM_BAND_450 = 0x10, + GSM_BAND_480 = 0x20, + GSM_BAND_750 = 0x40, + GSM_BAND_810 = 0x80, +}; + +const char *gsm_band_name(enum gsm_band band); +enum gsm_band gsm_band_parse(const char *mhz); + +int gsm_7bit_decode(char *decoded, const uint8_t *user_data, uint8_t length); +int gsm_7bit_encode(uint8_t *result, const char *data); + +int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm); +int ms_pwr_dbm(enum gsm_band band, uint8_t lvl); + +/* According to TS 08.05 Chapter 8.1.4 */ +int rxlev2dbm(uint8_t rxlev); +uint8_t dbm2rxlev(int dbm); + +/* According to GSM 04.08 Chapter 10.5.1.6 */ +static inline int ms_cm2_a5n_support(uint8_t *cm2, int n) { + switch (n) { + case 0: return 1; + case 1: return (cm2[0] & (1<<3)) ? 0 : 1; + case 2: return (cm2[2] & (1<<0)) ? 1 : 0; + case 3: return (cm2[2] & (1<<1)) ? 1 : 0; + default: + return 0; + } +} + +/* According to GSM 04.08 Chapter 10.5.2.29 */ +static inline int rach_max_trans_val2raw(int val) { return (val >> 1) & 3; } +static inline int rach_max_trans_raw2val(int raw) { + const int tbl[4] = { 1, 2, 4, 7 }; + return tbl[raw & 3]; +} + +#define ARFCN_PCS 0x8000 +#define ARFCN_UPLINK 0x4000 + +enum gsm_band gsm_arfcn2band(uint16_t arfcn); + +/* Convert an ARFCN to the frequency in MHz * 10 */ +uint16_t gsm_arfcn2freq10(uint16_t arfcn, int uplink); + +/* Convert from frame number to GSM time */ +void gsm_fn2gsmtime(struct gsm_time *time, uint32_t fn); + +/* Convert from GSM time to frame number */ +uint32_t gsm_gsmtime2fn(struct gsm_time *time); + +/* GSM TS 03.03 Chapter 2.6 */ +enum gprs_tlli_type { + TLLI_LOCAL, + TLLI_FOREIGN, + TLLI_RANDOM, + TLLI_AUXILIARY, + TLLI_RESERVED, +}; + +/* TS 03.03 Chapter 2.6 */ +int gprs_tlli_type(uint32_t tlli); + +uint32_t gprs_tmsi2tlli(uint32_t p_tmsi, enum gprs_tlli_type type); + +void generate_backtrace(); +#endif diff --git a/src/shared/libosmocore/include/osmocore/gsmtap.h b/src/shared/libosmocore/include/osmocore/gsmtap.h new file mode 100644 index 00000000..dcd64bdf --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/gsmtap.h @@ -0,0 +1,72 @@ +#ifndef _GSMTAP_H +#define _GSMTAP_H + +/* gsmtap header, pseudo-header in front of the actua GSM payload */ + +/* GSMTAP is a generic header format for GSM protocol captures, + * it uses the IANA-assigned UDP port number 4729 and carries + * payload in various formats of GSM interfaces such as Um MAC + * blocks or Um bursts. + * + * Example programs generating GSMTAP data are airprobe + * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) + */ + +#include <stdint.h> + +#define GSMTAP_VERSION 0x02 + +#define GSMTAP_TYPE_UM 0x01 +#define GSMTAP_TYPE_ABIS 0x02 +#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ + +#define GSMTAP_BURST_UNKNOWN 0x00 +#define GSMTAP_BURST_FCCH 0x01 +#define GSMTAP_BURST_PARTIAL_SCH 0x02 +#define GSMTAP_BURST_SCH 0x03 +#define GSMTAP_BURST_CTS_SCH 0x04 +#define GSMTAP_BURST_COMPACT_SCH 0x05 +#define GSMTAP_BURST_NORMAL 0x06 +#define GSMTAP_BURST_DUMMY 0x07 +#define GSMTAP_BURST_ACCESS 0x08 +#define GSMTAP_BURST_NONE 0x09 + +#define GSMTAP_CHANNEL_UNKNOWN 0x00 +#define GSMTAP_CHANNEL_BCCH 0x01 +#define GSMTAP_CHANNEL_CCCH 0x02 +#define GSMTAP_CHANNEL_RACH 0x03 +#define GSMTAP_CHANNEL_AGCH 0x04 +#define GSMTAP_CHANNEL_PCH 0x05 +#define GSMTAP_CHANNEL_SDCCH 0x06 +#define GSMTAP_CHANNEL_SDCCH4 0x07 +#define GSMTAP_CHANNEL_SDCCH8 0x08 +#define GSMTAP_CHANNEL_TCH_F 0x09 +#define GSMTAP_CHANNEL_TCH_H 0x0a +#define GSMTAP_CHANNEL_ACCH 0x80 + +#define GSMTAP_ARFCN_F_PCS 0x8000 +#define GSMTAP_ARFCN_F_UPLINK 0x4000 +#define GSMTAP_ARFCN_MASK 0x3fff + +#define GSMTAP_UDP_PORT 4729 + +struct gsmtap_hdr { + uint8_t version; /* version, set to 0x01 currently */ + uint8_t hdr_len; /* length in number of 32bit words */ + uint8_t type; /* see GSMTAP_TYPE_* */ + uint8_t timeslot; /* timeslot (0..7 on Um) */ + + uint16_t arfcn; /* ARFCN (frequency) */ + int8_t signal_dbm; /* signal level in dBm */ + int8_t snr_db; /* signal/noise ratio in dB */ + + uint32_t frame_number; /* GSM Frame Number (FN) */ + + uint8_t sub_type; /* Type of burst/channel, see above */ + uint8_t antenna_nr; /* Antenna Number */ + uint8_t sub_slot; /* sub-slot within timeslot */ + uint8_t res; /* reserved for future use (RFU) */ + +} __attribute__((packed)); + +#endif /* _GSMTAP_H */ diff --git a/src/shared/libosmocore/include/osmocore/gsmtap_util.h b/src/shared/libosmocore/include/osmocore/gsmtap_util.h new file mode 100644 index 00000000..96449443 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/gsmtap_util.h @@ -0,0 +1,21 @@ +#ifndef _GSMTAP_UTIL_H +#define _GSMTAP_UTIL_H + +#include <stdint.h> + +/* convert RSL channel number to GSMTAP channel type */ +uint8_t chantype_rsl2gsmtap(uint8_t rsl_chantype, uint8_t rsl_link_id); + +/* receive a message from L1/L2 and put it in GSMTAP */ +struct msgb *gsmtap_makemsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, + uint8_t ss, uint32_t fn, int8_t signal_dbm, + uint8_t snr, const uint8_t *data, unsigned int len); + +/* receive a message from L1/L2 and put it in GSMTAP */ +int gsmtap_sendmsg(uint16_t arfcn, uint8_t ts, uint8_t chan_type, uint8_t ss, + uint32_t fn, int8_t signal_dbm, uint8_t snr, + const uint8_t *data, unsigned int len); + +int gsmtap_init(uint32_t dst_ip); + +#endif /* _GSMTAP_UTIL_H */ diff --git a/src/shared/libosmocore/include/osmocore/linuxlist.h b/src/shared/libosmocore/include/osmocore/linuxlist.h new file mode 100644 index 00000000..fb99c5ec --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/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/src/shared/libosmocore/include/osmocore/logging.h b/src/shared/libosmocore/include/osmocore/logging.h new file mode 100644 index 00000000..2e82959a --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/logging.h @@ -0,0 +1,135 @@ +#ifndef _OSMOCORE_LOGGING_H +#define _OSMOCORE_LOGGING_H + +#include <stdio.h> +#include <stdint.h> +#include <osmocore/linuxlist.h> + +#define LOG_MAX_CATEGORY 32 +#define LOG_MAX_CTX 8 +#define LOG_MAX_FILTERS 8 + +#define DEBUG + +#ifdef DEBUG +#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args) +#define DEBUGPC(ss, fmt, args...) logp(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(const unsigned char *buf, int len); +void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6))); + +/* new logging interface */ +#define LOGP(ss, level, fmt, args...) \ + logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args) +#define LOGPC(ss, level, fmt, args...) \ + logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args) + +/* different levels */ +#define LOGL_DEBUG 1 /* debugging information */ +#define LOGL_INFO 3 +#define LOGL_NOTICE 5 /* abnormal/unexpected condition */ +#define LOGL_ERROR 7 /* error condition, requires user action */ +#define LOGL_FATAL 8 /* fatal, program aborted */ + +#define LOG_FILTER_ALL 0x0001 + +struct log_category { + uint8_t loglevel; + uint8_t enabled; +}; + +struct log_info_cat { + const char *name; + const char *color; + const char *description; + uint8_t loglevel; + uint8_t enabled; +}; + +/* log context information, passed to filter */ +struct log_context { + void *ctx[LOG_MAX_CTX+1]; +}; + +struct log_target; + +typedef int log_filter(const struct log_context *ctx, + struct log_target *target); + +struct log_info { + /* filter callback function */ + log_filter *filter_fn; + + /* per-category information */ + const struct log_info_cat *cat; + unsigned int num_cat; +}; + +struct log_target { + struct llist_head entry; + + int filter_map; + void *filter_data[LOG_MAX_FILTERS+1]; + + struct log_category categories[LOG_MAX_CATEGORY+1]; + uint8_t loglevel; + int use_color:1; + int print_timestamp:1; + + union { + struct { + FILE *out; + } tgt_stdout; + + struct { + int priority; + } tgt_syslog; + + struct { + void *vty; + } tgt_vty; + }; + + void (*output) (struct log_target *target, const char *string); +}; + +/* use the above macros */ +void logp2(unsigned int subsys, unsigned int level, char *file, + int line, int cont, const char *format, ...) + __attribute__ ((format (printf, 6, 7))); +void log_init(const struct log_info *cat); + +/* context management */ +void log_reset_context(void); +int log_set_context(uint8_t ctx, void *value); + +/* filter on the targets */ +void log_set_all_filter(struct log_target *target, int); + +void log_set_use_color(struct log_target *target, int); +void log_set_print_timestamp(struct log_target *target, int); +void log_set_log_level(struct log_target *target, int log_level); +void log_parse_category_mask(struct log_target *target, const char* mask); +int log_parse_level(const char *lvl); +const char *log_level_str(unsigned int lvl); +int log_parse_category(const char *category); +void log_set_category_filter(struct log_target *target, int category, + int enable, int level); + +/* management of the targets */ +struct log_target *log_target_create(void); +struct log_target *log_target_create_stderr(void); +void log_add_target(struct log_target *target); +void log_del_target(struct log_target *target); + +/* Gernerate command argument strings for VTY use */ +const char *log_vty_category_string(struct log_info *info); +const char *log_vty_level_string(struct log_info *info); + +#endif /* _OSMOCORE_LOGGING_H */ diff --git a/src/shared/libosmocore/include/osmocore/mncc.h b/src/shared/libosmocore/include/osmocore/mncc.h new file mode 100644 index 00000000..a094bb9b --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/mncc.h @@ -0,0 +1,71 @@ +#ifndef _OSMOCORE_MNCC_H +#define _OSMOCORE_MNCC_H + +#define GSM_MAX_FACILITY 128 +#define GSM_MAX_SSVERSION 128 +#define GSM_MAX_USERUSER 128 + +/* Expanded fields from GSM TS 04.08, Table 10.5.102 */ +struct gsm_mncc_bearer_cap { + int transfer; /* Information Transfer Capability */ + int mode; /* Transfer Mode */ + int coding; /* Coding Standard */ + int radio; /* Radio Channel Requirement */ + int speech_ctm; /* CTM text telephony indication */ + int speech_ver[8]; /* Speech version indication */ +}; + +struct gsm_mncc_number { + int type; + int plan; + int present; + int screen; + char number[33]; +}; + +struct gsm_mncc_cause { + int location; + int coding; + int rec; + int rec_val; + int value; + int diag_len; + char diag[32]; +}; + +struct gsm_mncc_useruser { + int proto; + char info[GSM_MAX_USERUSER + 1]; /* + termination char */ +}; + +struct gsm_mncc_progress { + int coding; + int location; + int descr; +}; + +struct gsm_mncc_facility { + int len; + char info[GSM_MAX_FACILITY]; +}; + +struct gsm_mncc_ssversion { + int len; + char info[GSM_MAX_SSVERSION]; +}; + +struct gsm_mncc_cccap { + int dtmf; + int pcp; +}; + +enum { + GSM_MNCC_BCAP_SPEECH = 0, + GSM_MNCC_BCAP_UNR_DIG = 1, + GSM_MNCC_BCAP_AUDIO = 2, + GSM_MNCC_BCAP_FAX_G3 = 3, + GSM_MNCC_BCAP_OTHER_ITC = 5, + GSM_MNCC_BCAP_RESERVED = 7, +}; + +#endif diff --git a/src/shared/libosmocore/include/osmocore/msgb.h b/src/shared/libosmocore/include/osmocore/msgb.h new file mode 100644 index 00000000..2841dc56 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/msgb.h @@ -0,0 +1,165 @@ +#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 <stdint.h> +#include "linuxlist.h" + +struct msgb { + struct llist_head list; + + /* Part of which TRX logical channel we were received / transmitted */ + /* FIXME: move them into the control buffer */ + struct gsm_bts_trx *trx; + struct gsm_lchan *lchan; + + /* the Layer1 header (if any) */ + unsigned char *l1h; + /* the A-bis layer 2 header: OML, RSL(RLL), NS */ + unsigned char *l2h; + /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ + unsigned char *l3h; + /* the layer 4 header */ + unsigned char *l4h; + + /* the 'control buffer', large enough to contain 5 pointers */ + unsigned long cb[5]; + + uint16_t data_len; + uint16_t len; + + unsigned char *head; + unsigned char *tail; + unsigned char *data; + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(uint16_t size, const char *name); +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); +extern void msgb_reset(struct msgb *m); + +#define msgb_l1(m) ((void *)(m->l1h)) +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) +#define msgb_sms(m) ((void *)(m->l4h)) + +static inline unsigned int msgb_l1len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l1(msgb); +} + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_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 void msgb_put_u8(struct msgb *msgb, uint8_t word) +{ + uint8_t *space = msgb_put(msgb, 1); + space[0] = word & 0xFF; +} +static inline void msgb_put_u16(struct msgb *msgb, uint16_t word) +{ + uint8_t *space = msgb_put(msgb, 2); + space[0] = word >> 8 & 0xFF; + space[1] = word & 0xFF; +} +static inline void msgb_put_u32(struct msgb *msgb, uint32_t word) +{ + uint8_t *space = msgb_put(msgb, 4); + space[0] = word >> 24 & 0xFF; + space[1] = word >> 16 & 0xFF; + space[2] = word >> 8 & 0xFF; + space[3] = word & 0xFF; +} +static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->data; + msgb->data += len; + msgb->len -= len; + return tmp; +} +static inline uint8_t msgb_get_u8(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 1); + return space[0]; +} +static inline uint16_t msgb_get_u16(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 2); + return space[0] << 8 | space[1]; +} +static inline uint32_t msgb_get_u32(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 4); + return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3]; +} +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->head + 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, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/shared/libosmocore/include/osmocore/plugin.h b/src/shared/libosmocore/include/osmocore/plugin.h new file mode 100644 index 00000000..98f9b56d --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/plugin.h @@ -0,0 +1,6 @@ +#ifndef _OSMO_PLUGIN_H +#define _OSMO_PLUGIN_H + +int plugin_load_all(const char *directory); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/protocol/Makefile.am b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am new file mode 100644 index 00000000..557950ec --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/Makefile.am @@ -0,0 +1,3 @@ +osmocore_proto_HEADERS = gsm_04_08.h gsm_04_11.h gsm_04_80.h gsm_08_58.h gsm_12_21.h gsm_08_08.h + +osmocore_protodir = $(includedir)/osmocore/protocol diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h new file mode 100644 index 00000000..80a455dd --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_08.h @@ -0,0 +1,1243 @@ +#ifndef PROTO_GSM_04_08_H +#define PROTO_GSM_04_08_H + +#include <stdint.h> + +/* GSM TS 04.08 definitions */ +struct gsm_lchan; + +/* Chapter 10.5.1.5 */ +struct gsm48_classmark1 { + uint8_t pwr_lev:3, + a5_1:1, + es_ind:1, + rev_lev:2, + spare:1; +} __attribute__ ((packed)); + +/* Chapter 10.5.1.6 */ +struct gsm48_classmark2 { + uint8_t pwr_lev:3, + a5_1:1, + es_ind:1, + rev_lev:2, + spare:1; + uint8_t fc:1, + vgcs:1, + vbs:1, + sm_cap:1, + ss_scr:2, + ps_cap:1, + spare2:1; + uint8_t a5_2:1, + a5_3:1, + cmsp:1, + solsa:1, + spare3:1, + lcsva_cap:1, + spare4:1, + cm3:1; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.3 */ +struct gsm48_range_1024 { + uint8_t w1_hi:2, + f0:1, + form_id:5; + uint8_t w1_lo; + uint8_t w2_hi; + uint8_t w3_hi:7, + w2_lo:1; + uint8_t w4_hi:6, + w3_lo:2; + uint8_t w5_hi:6, + w4_lo:2; + uint8_t w6_hi:6, + w5_lo:2; + uint8_t w7_hi:6, + w6_lo:2; + uint8_t w8_hi:6, + w7_lo:2; + uint8_t w9:7, + w8_lo:1; + uint8_t w11_hi:1, + w10:7; + uint8_t w12_hi:2, + w11_lo:6; + uint8_t w13_hi:3, + w12_lo:5; + uint8_t w14_hi:4, + w13_lo:4; + uint8_t w15_hi:5, + w14_lo:3; + uint8_t w16:6, + w15_lo:2; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.4 */ +struct gsm48_range_512 { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t w1_hi:7, + orig_arfcn_lo:1; + uint8_t w2_hi:6, + w1_lo:2; + uint8_t w3_hi:6, + w2_lo:2; + uint8_t w4_hi:6, + w3_lo:2; + uint8_t w5:7, + w4_lo:1; + uint8_t w7_hi:1, + w6:7; + uint8_t w8_hi:2, + w7_lo:6; + uint8_t w9_hi:4, + w8_lo:4; + uint8_t w10:6, + w9_lo:2; + uint8_t w12_hi:2, + w11:6; + uint8_t w13_hi:4, + w12_lo:4; + uint8_t w14:6, + w13_lo:2; + uint8_t w16_hi:2, + w15:6; + uint8_t w17:5, + w16_lo:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.5 */ +struct gsm48_range_256 { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t w1_hi:7, + orig_arfcn_lo:1; + uint8_t w2:7, + w1_lo:1; + uint8_t w4_hi:1, + w3:7; + uint8_t w5_hi:3, + w4_lo:5; + uint8_t w6_hi:5, + w5_lo:3; + uint8_t w8_hi:1, + w7:6, + w6_lo:1; + uint8_t w9_hi:4, + w8_lo:4; + uint8_t w11_hi:2, + w10:5, + w9_lo:1; + uint8_t w12:5, + w11_lo:3; + uint8_t w14_hi:3, + w13:5; + uint8_t w16_hi:1, + w15:5, + w14_lo:2; + uint8_t w18_hi:1, + w17:4, + w16_lo:3; + uint8_t w20_hi:1, + w19:4, + w18_lo:3; + uint8_t spare:1, + w21:4, + w20_lo:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.6 */ +struct gsm48_range_128 { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t w1:7, + orig_arfcn_lo:1; + uint8_t w3_hi:2, + w2:6; + uint8_t w4_hi:4, + w3_lo:4; + uint8_t w6_hi:2, + w5:5, + w4_lo:1; + uint8_t w7:5, + w6_lo:3; + uint8_t w9:4, + w8:4; + uint8_t w11:4, + w10:4; + uint8_t w13:4, + w12:4; + uint8_t w15:4, + w14:4; + uint8_t w18_hi:2, + w17:3, + w16:3; + uint8_t w21_hi:1, + w20:3, + w19:3, + w18_lo:1; + uint8_t w23:3, + w22:3, + w21_lo:2; + uint8_t w26_hi:2, + w25:3, + w24:3; + uint8_t spare:1, + w28:3, + w27:3, + w26_lo:1; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.1b.7 */ +struct gsm48_var_bit { + uint8_t orig_arfcn_hi:1, + form_id:7; + uint8_t orig_arfcn_mid; + uint8_t rrfcn1_7:7, + orig_arfcn_lo:1; + uint8_t rrfcn8_111[13]; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.5 */ +struct gsm48_chan_desc { + uint8_t chan_nr; + union { + struct { + uint8_t maio_high:4, + h:1, + tsc:3; + uint8_t hsn:6, + maio_low:2; + } h1; + struct { + uint8_t arfcn_high:2, + spare:2, + h:1, + tsc:3; + uint8_t arfcn_low; + } h0; + }; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.20 */ +struct gsm48_meas_res { + uint8_t rxlev_full:6, + dtx_used:1, + ba_used:1; + uint8_t rxlev_sub:6, + meas_valid:1, + spare:1; + uint8_t no_nc_n_hi:1, + rxqual_sub:3, + rxqual_full:3, + spare2:1; + uint8_t rxlev_nc1:6, + no_nc_n_lo:2; + uint8_t bsic_nc1_hi:3, + bcch_f_nc1:5; + uint8_t rxlev_nc2_hi:5, + bsic_nc1_lo:3; + uint8_t bsic_nc2_hi:2, + bcch_f_nc2:5, + rxlev_nc2_lo:1; + uint8_t rxlev_nc3_hi:4, + bsic_nc2_lo:4; + uint8_t bsic_nc3_hi:1, + bcch_f_nc3:5, + rxlev_nc3_lo:2; + uint8_t rxlev_nc4_hi:3, + bsic_nc3_lo:5; + uint8_t bcch_f_nc4:5, + rxlev_nc4_lo:3; + uint8_t rxlev_nc5_hi:2, + bsic_nc4:6; + uint8_t bcch_f_nc5_hi:4, + rxlev_nc5_lo:4; + uint8_t rxlev_nc6_hi:1, + bsic_nc5:6, + bcch_f_nc5_lo:1; + uint8_t bcch_f_nc6_hi:3, + rxlev_nc6_lo:5; + uint8_t bsic_nc6:6, + bcch_f_nc6_lo:2; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.21aa */ +struct gsm48_multi_rate_conf { + uint8_t smod : 2, + spare: 1, + icmi : 1, + nscb : 1, + ver : 3; + uint8_t m4_75 : 1, + m5_15 : 1, + m5_90 : 1, + m6_70 : 1, + m7_40 : 1, + m7_95 : 1, + m10_2 : 1, + m12_2 : 1; +} __attribute__((packed)); + +/* Chapter 10.5.2.28(a) */ +struct gsm48_power_cmd { + uint8_t power_level:5, + spare:2, + atc:1; +} __attribute__((packed)); + +/* Chapter 10.5.2.29 */ +struct gsm48_rach_control { + uint8_t re :1, + cell_bar :1, + tx_integer :4, + max_trans :2; + uint8_t t2; + uint8_t t3; +} __attribute__ ((packed)); + + +/* Chapter 10.5.2.30 */ +struct gsm48_req_ref { + uint8_t ra; + uint8_t t3_high:3, + t1:5; + uint8_t t2:5, + t3_low:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.38 */ +struct gsm48_start_time { + uint8_t t3_high:3, + t1:5; + uint8_t t2:5, + t3_low:3; +} __attribute__ ((packed)); + +/* Chapter 10.5.2.39 */ +struct gsm48_sync_ind { + uint8_t si:2, + rot:1, + nci:1, + sync_ie:4; +} __attribute__((packed)); + +/* + * Chapter 9.1.5/9.1.6 + * + * For 9.1.6 the chan_desc has the meaning of 10.5.2.5a + */ +struct gsm48_chan_mode_modify { + struct gsm48_chan_desc chan_desc; + uint8_t mode; +} __attribute__ ((packed)); + +enum gsm48_chan_mode { + GSM48_CMODE_SIGN = 0x00, + GSM48_CMODE_SPEECH_V1 = 0x01, + GSM48_CMODE_SPEECH_EFR = 0x21, + GSM48_CMODE_SPEECH_AMR = 0x41, + GSM48_CMODE_DATA_14k5 = 0x0f, + GSM48_CMODE_DATA_12k0 = 0x03, + GSM48_CMODE_DATA_6k0 = 0x0b, + GSM48_CMODE_DATA_3k6 = 0x23, +}; + +/* Chapter 9.1.2 */ +struct gsm48_ass_cmd { + /* Semantic is from 10.5.2.5a */ + struct gsm48_chan_desc chan_desc; + uint8_t power_command; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 10.5.2.2 */ +struct gsm48_cell_desc { + uint8_t bcc:3, + ncc:3, + arfcn_hi:2; + uint8_t arfcn_lo; +} __attribute__((packed)); + +/* Chapter 9.1.15 */ +struct gsm48_ho_cmd { + struct gsm48_cell_desc cell_desc; + struct gsm48_chan_desc chan_desc; + uint8_t ho_ref; + uint8_t power_command; + uint8_t data[0]; +} __attribute__((packed)); + +/* Chapter 9.1.18 */ +struct gsm48_imm_ass { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t page_mode; + struct gsm48_chan_desc chan_desc; + struct gsm48_req_ref req_ref; + uint8_t timing_advance; + uint8_t mob_alloc_len; + uint8_t mob_alloc[0]; +} __attribute__ ((packed)); + +/* Chapter 9.1.25 */ +struct gsm48_pag_resp { + uint8_t spare:4, + key_seq:4; + uint32_t classmark2; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + +/* Chapter 10.5.1.3 */ +struct gsm48_loc_area_id { + uint8_t digits[3]; /* BCD! */ + uint16_t lac; +} __attribute__ ((packed)); + +/* Section 9.2.2 */ +struct gsm48_auth_req { + uint8_t key_seq:4, + spare:4; + uint8_t rand[16]; +} __attribute__ ((packed)); + +/* Section 9.2.3 */ +struct gsm48_auth_resp { + uint8_t sres[4]; +} __attribute__ ((packed)); + +/* Section 9.2.15 */ +struct gsm48_loc_upd_req { + uint8_t type:4, + key_seq:4; + struct gsm48_loc_area_id lai; + struct gsm48_classmark1 classmark1; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 10.1 */ +struct gsm48_hdr { + uint8_t proto_discr; + uint8_t msg_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.3x System information Type header */ +struct gsm48_system_information_type_header { + uint8_t l2_plen; + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; +} __attribute__ ((packed)); + +/* Section 10.5.2.4 Cell Selection Parameters */ +struct gsm48_cell_sel_par { + uint8_t ms_txpwr_max_ccch:5, /* GSM 05.08 MS-TXPWR-MAX-CCCH */ + cell_resel_hyst:3; /* GSM 05.08 CELL-RESELECT-HYSTERESIS */ + uint8_t rxlev_acc_min:6, /* GSM 05.08 RXLEV-ACCESS-MIN */ + neci:1, + acs:1; +} __attribute__ ((packed)); + +/* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */ +struct gsm48_control_channel_descr { + uint8_t ccch_conf :3, + bs_ag_blks_res :3, + att :1, + spare1 :1; + uint8_t bs_pa_mfrms : 3, + spare2 :5; + uint8_t t3212; +} __attribute__ ((packed)); + +struct gsm48_cell_options { + uint8_t radio_link_timeout:4, + dtx:2, + pwrc:1, + spare:1; +} __attribute__ ((packed)); + +/* Section 9.2.9 CM service request */ +struct gsm48_service_request { + uint8_t cm_service_type : 4, + cipher_key_seq : 4; + /* length + 3 bytes */ + uint32_t classmark; + uint8_t mi_len; + uint8_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; + uint8_t cell_channel_description[16]; + struct gsm48_rach_control rach_control; + uint8_t rest_octets[0]; /* NCH position on the CCCH */ +} __attribute__ ((packed)); + +/* Section 9.1.32 System information Type 2 */ +struct gsm48_system_information_type_2 { + struct gsm48_system_information_type_header header; + uint8_t bcch_frequency_list[16]; + uint8_t ncc_permitted; + struct gsm48_rach_control rach_control; +} __attribute__ ((packed)); + +/* Section 9.1.33 System information Type 2bis */ +struct gsm48_system_information_type_2bis { + struct gsm48_system_information_type_header header; + uint8_t bcch_frequency_list[16]; + struct gsm48_rach_control rach_control; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.34 System information Type 2ter */ +struct gsm48_system_information_type_2ter { + struct gsm48_system_information_type_header header; + uint8_t ext_bcch_frequency_list[16]; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.35 System information Type 3 */ +struct gsm48_system_information_type_3 { + struct gsm48_system_information_type_header header; + uint16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_control_channel_descr control_channel_desc; + struct gsm48_cell_options cell_options; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + uint8_t rest_octets[0]; +} __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; + struct gsm48_cell_sel_par cell_sel_par; + struct gsm48_rach_control rach_control; + /* optional CBCH conditional CBCH... followed by + mandantory SI 4 Reset Octets + */ + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Section 9.1.37 System information Type 5 */ +struct gsm48_system_information_type_5 { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.38 System information Type 5bis */ +struct gsm48_system_information_type_5bis { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.39 System information Type 5ter */ +struct gsm48_system_information_type_5ter { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint8_t bcch_frequency_list[16]; +} __attribute__ ((packed)); + +/* Section 9.1.40 System information Type 6 */ +struct gsm48_system_information_type_6 { + uint8_t rr_protocol_discriminator :4, + skip_indicator:4; + uint8_t system_information; + uint16_t cell_identity; + struct gsm48_loc_area_id lai; + struct gsm48_cell_options cell_options; + uint8_t ncc_permitted; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.1.43a System Information type 13 */ +struct gsm48_system_information_type_13 { + struct gsm48_system_information_type_header header; + uint8_t rest_octets[0]; +} __attribute__ ((packed)); + +/* Section 9.2.12 IMSI Detach Indication */ +struct gsm48_imsi_detach_ind { + struct gsm48_classmark1 classmark1; + uint8_t mi_len; + uint8_t mi[0]; +} __attribute__ ((packed)); + +/* Section 9.1.1 */ +struct gsm48_add_ass { + /* Semantic is from 10.5.2.5 */ + struct gsm48_chan_desc chan_desc; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.3 */ +struct gsm48_ass_cpl { + uint8_t rr_cause; +} __attribute__((packed)); + +/* Section 9.1.4 */ +struct gsm48_ass_fail { + uint8_t rr_cause; +} __attribute__((packed)); + +/* Section 9.1.7 */ +struct gsm48_chan_rel { + uint8_t rr_cause; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.9 */ +struct gsm48_cip_mode_cmd { + uint8_t sc:1, + alg_id:3, + spare:3, + cr:1; +} __attribute__((packed)); + +/* Section 9.1.11 */ +struct gsm48_cm_change { + uint8_t cm2_len; + struct gsm48_classmark2 cm2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.19 */ +struct gsm48_imm_ass_ext { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t page_mode; + struct gsm48_chan_desc chan_desc1; + struct gsm48_req_ref req_ref1; + uint8_t timing_advance1; + struct gsm48_chan_desc chan_desc2; + struct gsm48_req_ref req_ref2; + uint8_t timing_advance2; + uint8_t mob_alloc_len; + uint8_t mob_alloc[0]; +} __attribute__ ((packed)); + +/* Section 9.1.20 */ +struct gsm48_imm_ass_rej { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t page_mode; + struct gsm48_req_ref req_ref1; + uint8_t wait_ind1; + struct gsm48_req_ref req_ref2; + uint8_t wait_ind2; + struct gsm48_req_ref req_ref3; + uint8_t wait_ind3; + struct gsm48_req_ref req_ref4; + uint8_t wait_ind4; + uint8_t rest[0]; +} __attribute__ ((packed)); + +/* Section 9.1.22 */ +struct gsm48_paging1 { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t pag_mode:2, + spare:2, + cneed1:2, + cneed2:2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.23 */ +struct gsm48_paging2 { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t pag_mode:2, + spare:2, + cneed1:2, + cneed2:2; + uint32_t tmsi1; + uint32_t tmsi2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.24 */ +struct gsm48_paging3 { + uint8_t l2_plen; + uint8_t proto_discr; + uint8_t msg_type; + uint8_t pag_mode:2, + spare:2, + cneed1:2, + cneed2:2; + uint32_t tmsi1; + uint32_t tmsi2; + uint32_t tmsi3; + uint32_t tmsi4; + uint8_t cneed3:2, + cneed4:2, + spare2:4; + uint8_t rest[0]; +} __attribute__((packed)); + +/* Section 9.1.25 */ +struct gsm48_pag_rsp { + uint8_t key_seq:3, + spare:5; + uint8_t cm2_len; + struct gsm48_classmark2 cm2; + uint8_t data[0]; +} __attribute__((packed)); + +/* Section 9.1.29 */ +struct gsm48_rr_status { + uint8_t rr_cause; +} __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.3.3 CM service type */ +#define GSM48_CMSERV_MO_CALL_PACKET 1 +#define GSM48_CMSERV_EMERGENCY 2 +#define GSM48_CMSERV_SMS 4 +#define GSM48_CMSERV_SUP_SERV 8 +#define GSM48_CMSERV_VGCS 9 +#define GSM48_CMSERV_VBS 10 +#define GSM48_CMSERV_LOC_SERV 11 + +/* 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 /* 10.5.1.4 */ +#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_NOTIFY 0x27 /* 10.5.4.20 */ +#define GSM48_IE_KPD_FACILITY 0x2c /* 10.5.4.17 */ +#define GSM48_IE_SIGNAL 0x34 /* 10.5.4.23 */ +#define GSM48_IE_CONN_BCD 0x4c /* 10.5.4.13 */ +#define GSM48_IE_CONN_SUB 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 */ +#define GSM48_IE_REPEAT_CIR 0xd1 /* 10.5.4.22 */ +#define GSM48_IE_REPEAT_SEQ 0xd3 /* 10.5.4.22 */ + +/* Section 10.5.4.11 / Table 10.5.122 */ +#define GSM48_CAUSE_CS_GSM 0x60 + +/* Section 9.1.2 / Table 9.3 */ +/* RR elements */ +#define GSM48_IE_VGCS_TARGET 0x01 +//#define GSM48_IE_VGCS_T_MODE_I 0x01 +#define GSM48_IE_FRQSHORT_AFTER 0x02 +#define GSM48_IE_MUL_RATE_CFG 0x03 /* 10.5.2.21aa */ +#define GSM48_IE_FREQ_L_AFTER 0x05 +#define GSM48_IE_MSLOT_DESC 0x10 +#define GSM48_IE_CHANMODE_2 0x11 +#define GSM48_IE_FRQSHORT_BEFORE 0x12 +//#define GSM48_IE_FRQSHORT_BEFOR 0x12 +#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 +#define GSM48_IE_MA_AFTER 0x72 +#define GSM48_IE_START_TIME 0x7c +#define GSM48_IE_FREQ_L_BEFORE 0x19 +//#define GSM48_IE_FRQLIST_BEFORE 0x19 +#define GSM48_IE_CH_DESC_1_BEFORE 0x1c +//#define GSM48_IE_CHDES_1_BEFORE 0x1c +#define GSM48_IE_CH_DESC_2_BEFORE 0x1d +//#define GSM48_IE_CHDES_2_BEFORE 0x1d +#define GSM48_IE_F_CH_SEQ_BEFORE 0x1e +//#define GSM48_IE_FRQSEQ_BEFORE 0x1e +#define GSM48_IE_CLASSMARK3 0x20 +#define GSM48_IE_MA_BEFORE 0x21 +#define GSM48_IE_RR_PACKET_UL 0x22 +#define GSM48_IE_RR_PACKET_DL 0x23 +#define GSM48_IE_CELL_CH_DESC 0x62 +#define GSM48_IE_CHANMODE_1 0x63 +#define GSM48_IE_CHDES_2_AFTER 0x64 +#define GSM48_IE_MODE_SEC_CH 0x66 +#define GSM48_IE_F_CH_SEQ_AFTER 0x69 +#define GSM48_IE_MA_AFTER 0x72 +#define GSM48_IE_BA_RANGE 0x73 +#define GSM48_IE_GROUP_CHDES 0x74 +#define GSM48_IE_BA_LIST_PREF 0x75 +#define GSM48_IE_MOB_OVSERV_DIF 0x77 +#define GSM48_IE_REALTIME_DIFF 0x7b +#define GSM48_IE_START_TIME 0x7c +#define GSM48_IE_TIMING_ADVANCE 0x7d +#define GSM48_IE_GROUP_CIP_SEQ 0x80 +#define GSM48_IE_CIP_MODE_SET 0x90 +#define GSM48_IE_GPRS_RESUMPT 0xc0 +#define GSM48_IE_SYNC_IND 0xd0 +/* System Information 4 (types are equal IEs above) */ +#define GSM48_IE_CBCH_CHAN_DESC 0x64 +#define GSM48_IE_CBCH_MOB_AL 0x72 + +/* Additional MM elements */ +#define GSM48_IE_LOCATION_AREA 0x13 +#define GSM48_IE_PRIORITY_LEV 0x80 +#define GSM48_IE_FOLLOW_ON_PROC 0xa1 +#define GSM48_IE_CTS_PERMISSION 0xa2 + +/* 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, +}; + +/* Section 10.5.4.11 CC Cause / Table 10.5.123 */ +enum gsm48_cc_cause { + GSM48_CC_CAUSE_UNASSIGNED_NR = 1, + GSM48_CC_CAUSE_NO_ROUTE = 3, + GSM48_CC_CAUSE_CHAN_UNACCEPT = 6, + GSM48_CC_CAUSE_OP_DET_BARRING = 8, + GSM48_CC_CAUSE_NORM_CALL_CLEAR = 16, + GSM48_CC_CAUSE_USER_BUSY = 17, + GSM48_CC_CAUSE_USER_NOTRESPOND = 18, + GSM48_CC_CAUSE_USER_ALERTING_NA = 19, + GSM48_CC_CAUSE_CALL_REJECTED = 21, + GSM48_CC_CAUSE_NUMBER_CHANGED = 22, + GSM48_CC_CAUSE_PRE_EMPTION = 25, + GSM48_CC_CAUSE_NONSE_USER_CLR = 26, + GSM48_CC_CAUSE_DEST_OOO = 27, + GSM48_CC_CAUSE_INV_NR_FORMAT = 28, + GSM48_CC_CAUSE_FACILITY_REJ = 29, + GSM48_CC_CAUSE_RESP_STATUS_INQ = 30, + GSM48_CC_CAUSE_NORMAL_UNSPEC = 31, + GSM48_CC_CAUSE_NO_CIRCUIT_CHAN = 34, + GSM48_CC_CAUSE_NETWORK_OOO = 38, + GSM48_CC_CAUSE_TEMP_FAILURE = 41, + GSM48_CC_CAUSE_SWITCH_CONG = 42, + GSM48_CC_CAUSE_ACC_INF_DISCARD = 43, + GSM48_CC_CAUSE_REQ_CHAN_UNAVAIL = 44, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL = 47, + GSM48_CC_CAUSE_QOS_UNAVAIL = 49, + GSM48_CC_CAUSE_REQ_FAC_NOT_SUBSC= 50, + GSM48_CC_CAUSE_INC_BARRED_CUG = 55, + GSM48_CC_CAUSE_BEARER_CAP_UNAUTH= 57, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL= 58, + GSM48_CC_CAUSE_SERV_OPT_UNAVAIL = 63, + GSM48_CC_CAUSE_BEARERSERV_UNIMPL= 65, + GSM48_CC_CAUSE_ACM_GE_ACM_MAX = 68, + GSM48_CC_CAUSE_REQ_FAC_NOTIMPL = 69, + GSM48_CC_CAUSE_RESTR_BCAP_AVAIL = 70, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL = 79, + GSM48_CC_CAUSE_INVAL_TRANS_ID = 81, + GSM48_CC_CAUSE_USER_NOT_IN_CUG = 87, + GSM48_CC_CAUSE_INCOMPAT_DEST = 88, + GSM48_CC_CAUSE_INVAL_TRANS_NET = 91, + GSM48_CC_CAUSE_SEMANTIC_INCORR = 95, + GSM48_CC_CAUSE_INVAL_MAND_INF = 96, + GSM48_CC_CAUSE_MSGTYPE_NOTEXIST = 97, + GSM48_CC_CAUSE_MSGTYPE_INCOMPAT = 98, + GSM48_CC_CAUSE_IE_NOTEXIST = 99, + GSM48_CC_CAUSE_COND_IE_ERR = 100, + GSM48_CC_CAUSE_MSG_INCOMP_STATE = 101, + GSM48_CC_CAUSE_RECOVERY_TIMER = 102, + GSM48_CC_CAUSE_PROTO_ERR = 111, + GSM48_CC_CAUSE_INTERWORKING = 127, +}; + +/* 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, +}; + +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_NECI0, + CHREQ_T_PAG_R_ANY_NECI1, + CHREQ_T_PAG_R_TCH_F, + CHREQ_T_PAG_R_TCH_FH, + CHREQ_T_LMU, + CHREQ_T_RESERVED_SDCCH, + CHREQ_T_RESERVED_IGNORE, +}; + +/* Chapter 11.3 */ +#define GSM48_T301 180, 0 +#define GSM48_T303 30, 0 +#define GSM48_T305 30, 0 +#define GSM48_T306 30, 0 +#define GSM48_T308 10, 0 +#define GSM48_T310 180, 0 +#define GSM48_T313 30, 0 +#define GSM48_T323 30, 0 +#define GSM48_T331 30, 0 +#define GSM48_T333 30, 0 +#define GSM48_T334 25, 0 /* min 15 */ +#define GSM48_T338 30, 0 +#define GSM48_T303_MS 30, 0 +#define GSM48_T305_MS 30, 0 +#define GSM48_T308_MS 30, 0 +#define GSM48_T310_MS 30, 0 +#define GSM48_T313_MS 30, 0 +#define GSM48_T323_MS 30, 0 +#define GSM48_T332_MS 30, 0 +#define GSM48_T335_MS 30, 0 + +/* Chapter 5.1.2.2 */ +#define GSM_CSTATE_NULL 0 +#define GSM_CSTATE_INITIATED 1 +#define GSM_CSTATE_MM_CONNECTION_PEND 2 /* see 10.5.4.6 */ +#define GSM_CSTATE_MO_CALL_PROC 3 +#define GSM_CSTATE_CALL_DELIVERED 4 +#define GSM_CSTATE_CALL_PRESENT 6 +#define GSM_CSTATE_CALL_RECEIVED 7 +#define GSM_CSTATE_CONNECT_REQUEST 8 +#define GSM_CSTATE_MO_TERM_CALL_CONF 9 +#define GSM_CSTATE_ACTIVE 10 +#define GSM_CSTATE_DISCONNECT_REQ 12 +#define GSM_CSTATE_DISCONNECT_IND 12 +#define GSM_CSTATE_RELEASE_REQ 19 +#define GSM_CSTATE_MO_ORIG_MODIFY 26 +#define GSM_CSTATE_MO_TERM_MODIFY 27 +#define GSM_CSTATE_CONNECT_IND 28 + +#define SBIT(a) (1 << a) +#define ALL_STATES 0xffffffff + +/* Table 10.5.3/3GPP TS 04.08: Location Area Identification information element */ +#define GSM_LAC_RESERVED_DETACHED 0x0 +#define GSM_LAC_RESERVED_ALL_BTS 0xfffe + +/* GSM 04.08 Bearer Capability: Information Transfer Capability */ +enum gsm48_bcap_itcap { + GSM48_BCAP_ITCAP_SPEECH = 0, + GSM48_BCAP_ITCAP_UNR_DIG_INF = 1, + GSM48_BCAP_ITCAP_3k1_AUDIO = 2, + GSM48_BCAP_ITCAP_FAX_G3 = 3, + GSM48_BCAP_ITCAP_OTHER = 5, + GSM48_BCAP_ITCAP_RESERVED = 7, +}; + +/* GSM 04.08 Bearer Capability: Transfer Mode */ +enum gsm48_bcap_tmod { + GSM48_BCAP_TMOD_CIRCUIT = 0, + GSM48_BCAP_TMOD_PACKET = 1, +}; + +/* GSM 04.08 Bearer Capability: Coding Standard */ +enum gsm48_bcap_coding { + GSM48_BCAP_CODING_GSM_STD = 0, +}; + +/* GSM 04.08 Bearer Capability: Radio Channel Requirements */ +enum gsm48_bcap_rrq { + GSM48_BCAP_RRQ_FR_ONLY = 1, + GSM48_BCAP_RRQ_DUAL_HR = 2, + GSM48_BCAP_RRQ_DUAL_FR = 3, +}; + +#define GSM48_TMSI_LEN 5 +#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2) +#define GSM48_MI_SIZE 32 + +/* Chapter 10.4.4.15 */ +struct gsm48_ra_id { + uint8_t digits[3]; /* MCC + MNC BCD digits */ + uint16_t lac; /* Location Area Code */ + uint8_t rac; /* Routing Area Code */ +} __attribute__ ((packed)); + + + +#endif /* PROTO_GSM_04_08_H */ diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h new file mode 100644 index 00000000..c6a2b193 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_11.h @@ -0,0 +1,188 @@ +#ifndef PROTO_GSM_04_11_H +#define PROTO_GSM_04_11_H + +#include <stdint.h> + +/* GSM TS 04.11 definitions */ + +/* Chapter 5.2.3: SMC-CS states at the network side */ +enum gsm411_cp_state { + GSM411_CPS_IDLE = 0, + GSM411_CPS_MM_CONN_PENDING = 1, /* only MT ! */ + GSM411_CPS_WAIT_CP_ACK = 2, + GSM411_CPS_MM_ESTABLISHED = 3, +}; + +/* Chapter 6.2.2: SMR states at the network side */ +enum gsm411_rp_state { + GSM411_RPS_IDLE = 0, + GSM411_RPS_WAIT_FOR_RP_ACK = 1, + GSM411_RPS_WAIT_TO_TX_RP_ACK = 3, +}; + +/* 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. */ +}; + +/* Section 8.1.4.2 / Table 8.2 */ +enum gsm411_cp_cause { + GSM411_CP_CAUSE_NET_FAIL = 17, + GSM411_CP_CAUSE_CONGESTION = 22, + GSM411_CP_CAUSE_INV_TRANS_ID = 81, + GSM411_CP_CAUSE_SEMANT_INC_MSG = 95, + GSM411_CP_CAUSE_INV_MAND_INF = 96, + GSM411_CP_CAUSE_MSGTYPE_NOTEXIST= 97, + GSM411_CP_CAUSE_MSG_INCOMP_STATE= 98, + GSM411_CP_CAUSE_IE_NOTEXIST = 99, + GSM411_CP_CAUSE_PROTOCOL_ERR = 111, +}; + +/* 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 0x05 +#define GSM411_MT_RP_SMMA_MO 0x06 + +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.5.4 Table 8.4 */ +enum gsm411_rp_cause { + /* valid only for MO */ + GSM411_RP_CAUSE_MO_NUM_UNASSIGNED = 1, + GSM411_RP_CAUSE_MO_OP_DET_BARR = 8, + GSM411_RP_CAUSE_MO_CALL_BARRED = 10, + GSM411_RP_CAUSE_MO_SMS_REJECTED = 21, + GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER = 27, + GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR = 28, + GSM411_RP_CAUSE_MO_FACILITY_REJ = 29, + GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR = 30, + GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER = 38, + GSM411_RP_CAUSE_MO_TEMP_FAIL = 41, + GSM411_RP_CAUSE_MO_CONGESTION = 42, + GSM411_RP_CAUSE_MO_RES_UNAVAIL = 47, + GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR = 50, + GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL = 69, + GSM411_RP_CAUSE_MO_INTERWORKING = 127, + /* valid only for MT */ + GSM411_RP_CAUSE_MT_MEM_EXCEEDED = 22, + /* valid for both directions */ + GSM411_RP_CAUSE_INV_TRANS_REF = 81, + GSM411_RP_CAUSE_SEMANT_INC_MSG = 95, + GSM411_RP_CAUSE_INV_MAND_INF = 96, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST = 97, + GSM411_RP_CAUSE_MSG_INCOMP_STATE = 98, + GSM411_RP_CAUSE_IE_NOTEXIST = 99, + GSM411_RP_CAUSE_PROTOCOL_ERR = 111, +}; + +/* Chapter 10: Timers */ +#define GSM411_TMR_TR1M 40, 0 /* 35 < x < 45 seconds */ +#define GSM411_TMR_TRAM 30, 0 /* 25 < x < 35 seconds */ +#define GSM411_TMR_TR2M 15, 0 /* 12 < x < 20 seconds */ + +#define GSM411_TMR_TC1A 30, 0 + +/* Chapter 8.2.1 */ +struct gsm411_rp_hdr { + uint8_t len; + uint8_t msg_type; + uint8_t msg_ref; + uint8_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, +}; + +/* 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 */ + +#endif /* PROTO_GSM_04_11_H */ diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h new file mode 100644 index 00000000..fa5c9451 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_04_80.h @@ -0,0 +1,126 @@ +#ifndef PROTO_GSM_04_80_H +#define PROTO_GSM_04_80_H + +/* GSM TS 04.80 definitions (Supplementary Services Specification, Formats and Coding) */ + +/* Section 3.4 */ +#define GSM0480_MTYPE_RELEASE_COMPLETE 0x2A +#define GSM0480_MTYPE_FACILITY 0x3A +#define GSM0480_MTYPE_REGISTER 0x3B + +/* Section 3.5 */ +#define GSM0480_IE_FACILITY 0x1C +#define GSM0480_IE_SS_VERSION 0x7F + +/* Section 3.6.2 */ +#define GSM0480_CTYPE_INVOKE 0xA1 +#define GSM0480_CTYPE_RETURN_RESULT 0xA2 +#define GSM0480_CTYPE_RETURN_ERROR 0xA3 +#define GSM0480_CTYPE_REJECT 0xA4 + +/* Section 3.6.3 */ +#define GSM0480_COMPIDTAG_INVOKE_ID 0x02 +#define GSM0480_COMPIDTAG_LINKED_ID 0x80 + +/* Section 3.6.4 */ +#define GSM0480_OPERATION_CODE 0x02 + +/* Section 3.6.5 */ +#define GSM_0480_SEQUENCE_TAG 0x30 +#define GSM_0480_SET_TAG 0x31 + +/* Section 3.6.6 */ +#define GSM_0480_ERROR_CODE_TAG 0x02 + +/* Section 3.6.7 */ +/* Table 3.13 */ +#define GSM_0480_PROBLEM_CODE_TAG_GENERAL 0x80 +#define GSM_0480_PROBLEM_CODE_TAG_INVOKE 0x81 +#define GSM_0480_PROBLEM_CODE_TAG_RETURN_RESULT 0x82 +#define GSM_0480_PROBLEM_CODE_TAG_RETURN_ERROR 0x83 + +/* Table 3.14 */ +#define GSM_0480_GEN_PROB_CODE_UNRECOGNISED 0x00 +#define GSM_0480_GEN_PROB_CODE_MISTYPED 0x01 +#define GSM_0480_GEN_PROB_CODE_BAD_STRUCTURE 0x02 + +/* Table 3.15 */ +#define GSM_0480_INVOKE_PROB_CODE_DUPLICATE_INVOKE_ID 0x00 +#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_OPERATION 0x01 +#define GSM_0480_INVOKE_PROB_CODE_MISTYPED_PARAMETER 0x02 +#define GSM_0480_INVOKE_PROB_CODE_RESOURCE_LIMITATION 0x03 +#define GSM_0480_INVOKE_PROB_CODE_INITIATING_RELEASE 0x04 +#define GSM_0480_INVOKE_PROB_CODE_UNRECOGNISED_LINKED_ID 0x05 +#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_RESPONSE 0x06 +#define GSM_0480_INVOKE_PROB_CODE_UNEXPECTED_LINKED_OPERATION 0x07 + +/* Table 3.16 */ +#define GSM_0480_RESULT_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00 +#define GSM_0480_RESULT_PROB_CODE_RETURN_RESULT_UNEXPECTED 0x01 +#define GSM_0480_RESULT_PROB_CODE_MISTYPED_PARAMETER 0x02 + +/* Table 3.17 */ +#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_INVOKE_ID 0x00 +#define GSM_0480_ERROR_PROB_CODE_RETURN_ERROR_UNEXPECTED 0x01 +#define GSM_0480_ERROR_PROB_CODE_UNRECOGNISED_ERROR 0x02 +#define GSM_0480_ERROR_PROB_CODE_UNEXPECTED_ERROR 0x03 +#define GSM_0480_ERROR_PROB_CODE_MISTYPED_PARAMETER 0x04 + +/* Section 4.5 */ +#define GSM0480_OP_CODE_REGISTER_SS 0x0A +#define GSM0480_OP_CODE_ERASE_SS 0x0B +#define GSM0480_OP_CODE_ACTIVATE_SS 0x0C +#define GSM0480_OP_CODE_DEACTIVATE_SS 0x0D +#define GSM0480_OP_CODE_INTERROGATE_SS 0x0E +#define GSM0480_OP_CODE_NOTIFY_SS 0x10 +#define GSM0480_OP_CODE_REGISTER_PASSWORD 0x11 +#define GSM0480_OP_CODE_GET_PASSWORD 0x12 +#define GSM0480_OP_CODE_PROCESS_USS_DATA 0x13 +#define GSM0480_OP_CODE_FORWARD_CHECK_SS_IND 0x26 +#define GSM0480_OP_CODE_PROCESS_USS_REQ 0x3B +#define GSM0480_OP_CODE_USS_REQUEST 0x3C +#define GSM0480_OP_CODE_USS_NOTIFY 0x3D +#define GSM0480_OP_CODE_FORWARD_CUG_INFO 0x78 +#define GSM0480_OP_CODE_SPLIT_MPTY 0x79 +#define GSM0480_OP_CODE_RETRIEVE_MPTY 0x7A +#define GSM0480_OP_CODE_HOLD_MPTY 0x7B +#define GSM0480_OP_CODE_BUILD_MPTY 0x7C +#define GSM0480_OP_CODE_FORWARD_CHARGE_ADVICE 0x7D + +#define GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER 0x01 +#define GSM0480_ERR_CODE_ILLEGAL_SUBSCRIBER 0x09 +#define GSM0480_ERR_CODE_BEARER_SERVICE_NOT_PROVISIONED 0x0A +#define GSM0480_ERR_CODE_TELESERVICE_NOT_PROVISIONED 0x0B +#define GSM0480_ERR_CODE_ILLEGAL_EQUIPMENT 0x0C +#define GSM0480_ERR_CODE_CALL_BARRED 0x0D +#define GSM0480_ERR_CODE_ILLEGAL_SS_OPERATION 0x10 +#define GSM0480_ERR_CODE_SS_ERROR_STATUS 0x11 +#define GSM0480_ERR_CODE_SS_NOT_AVAILABLE 0x12 +#define GSM0480_ERR_CODE_SS_SUBSCRIPTION_VIOLATION 0x13 +#define GSM0480_ERR_CODE_SS_INCOMPATIBILITY 0x14 +#define GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED 0x15 +#define GSM0480_ERR_CODE_ABSENT_SUBSCRIBER 0x1B +#define GSM0480_ERR_CODE_SYSTEM_FAILURE 0x22 +#define GSM0480_ERR_CODE_DATA_MISSING 0x23 +#define GSM0480_ERR_CODE_UNEXPECTED_DATA_VALUE 0x24 +#define GSM0480_ERR_CODE_PW_REGISTRATION_FAILURE 0x25 +#define GSM0480_ERR_CODE_NEGATIVE_PW_CHECK 0x26 +#define GSM0480_ERR_CODE_NUM_PW_ATTEMPTS_VIOLATION 0x2B +#define GSM0480_ERR_CODE_UNKNOWN_ALPHABET 0x47 +#define GSM0480_ERR_CODE_USSD_BUSY 0x48 +#define GSM0480_ERR_CODE_MAX_MPTY_PARTICIPANTS 0x7E +#define GSM0480_ERR_CODE_RESOURCES_NOT_AVAILABLE 0x7F + +/* ASN.1 type-tags */ +#define ASN1_BOOLEAN_TAG 0x01 +#define ASN1_INTEGER_TAG 0x02 +#define ASN1_BIT_STRING_TAG 0x03 +#define ASN1_OCTET_STRING_TAG 0x04 +#define ASN1_NULL_TYPE_TAG 0x05 +#define ASN1_OBJECT_ID_TAG 0x06 +#define ASN1_UTF8_STRING_TAG 0x0C +#define ASN1_PRINTABLE_STRING_TAG 0x13 +#define ASN1_IA5_STRING_TAG 0x16 +#define ASN1_UNICODE_STRING_TAG 0x1E + +#endif /* PROTO_GSM_04_80_H */ diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h new file mode 100644 index 00000000..6b8f9359 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_08.h @@ -0,0 +1,303 @@ +/* From GSM08.08 */ + +#ifndef GSM_0808_H +#define GSM_0808_H + +#include <stdlib.h> + +/* + * this is from GSM 03.03 CGI but is copied in GSM 08.08 + * in § 3.2.2.27 for Cell Identifier List + */ +enum CELL_IDENT { + CELL_IDENT_WHOLE_GLOBAL = 0, + CELL_IDENT_LAC_AND_CI = 1, + CELL_IDENT_CI = 2, + CELL_IDENT_NO_CELL = 3, + CELL_IDENT_LAI_AND_LAC = 4, + CELL_IDENT_LAC = 5, + CELL_IDENT_BSS = 6, + CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8, + CELL_IDENT_UTRAN_RNC = 9, + CELL_IDENT_UTRAN_LAC_RNC = 10, +}; + + +/* GSM 08.06 § 6.3 */ +enum BSSAP_MSG_TYPE { + BSSAP_MSG_BSS_MANAGEMENT = 0x0, + BSSAP_MSG_DTAP = 0x1, +}; + +struct bssmap_header { + uint8_t type; + uint8_t length; +} __attribute__((packed)); + +struct dtap_header { + uint8_t type; + uint8_t link_id; + uint8_t length; +} __attribute__((packed)); + + +enum BSS_MAP_MSG_TYPE { + BSS_MAP_MSG_RESERVED_0 = 0, + + /* ASSIGNMENT MESSAGES */ + BSS_MAP_MSG_ASSIGMENT_RQST = 1, + BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2, + BSS_MAP_MSG_ASSIGMENT_FAILURE = 3, + + /* HANDOVER MESSAGES */ + BSS_MAP_MSG_HANDOVER_RQST = 16, + BSS_MAP_MSG_HANDOVER_REQUIRED = 17, + BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18, + BSS_MAP_MSG_HANDOVER_CMD = 19, + BSS_MAP_MSG_HANDOVER_COMPLETE = 20, + BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21, + BSS_MAP_MSG_HANDOVER_FAILURE = 22, + BSS_MAP_MSG_HANDOVER_PERFORMED = 23, + BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24, + BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25, + BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26, + BSS_MAP_MSG_HANDOVER_DETECT = 27, + + /* RELEASE MESSAGES */ + BSS_MAP_MSG_CLEAR_CMD = 32, + BSS_MAP_MSG_CLEAR_COMPLETE = 33, + BSS_MAP_MSG_CLEAR_RQST = 34, + BSS_MAP_MSG_RESERVED_1 = 35, + BSS_MAP_MSG_RESERVED_2 = 36, + BSS_MAP_MSG_SAPI_N_REJECT = 37, + BSS_MAP_MSG_CONFUSION = 38, + + /* OTHER CONNECTION RELATED MESSAGES */ + BSS_MAP_MSG_SUSPEND = 40, + BSS_MAP_MSG_RESUME = 41, + BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42, + BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43, + BSS_MAP_MSG_LSA_INFORMATION = 44, + BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45, + BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46, + BSS_MAP_MSG_COMMON_ID = 47, + + /* GENERAL MESSAGES */ + BSS_MAP_MSG_RESET = 48, + BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49, + BSS_MAP_MSG_OVERLOAD = 50, + BSS_MAP_MSG_RESERVED_3 = 51, + BSS_MAP_MSG_RESET_CIRCUIT = 52, + BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53, + BSS_MAP_MSG_MSC_INVOKE_TRACE = 54, + BSS_MAP_MSG_BSS_INVOKE_TRACE = 55, + BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58, + + /* TERRESTRIAL RESOURCE MESSAGES */ + BSS_MAP_MSG_BLOCK = 64, + BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65, + BSS_MAP_MSG_UNBLOCK = 66, + BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68, + BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70, + BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71, + BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72, + BSS_MAP_MSG_CHANGE_CIRCUIT = 78, + BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79, + + /* RADIO RESOURCE MESSAGES */ + BSS_MAP_MSG_RESOURCE_RQST = 80, + BSS_MAP_MSG_RESOURCE_INDICATION = 81, + BSS_MAP_MSG_PAGING = 82, + BSS_MAP_MSG_CIPHER_MODE_CMD = 83, + BSS_MAP_MSG_CLASSMARK_UPDATE = 84, + BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85, + BSS_MAP_MSG_QUEUING_INDICATION = 86, + BSS_MAP_MSG_COMPLETE_LAYER_3 = 87, + BSS_MAP_MSG_CLASSMARK_RQST = 88, + BSS_MAP_MSG_CIPHER_MODE_REJECT = 89, + BSS_MAP_MSG_LOAD_INDICATION = 90, + + /* VGCS/VBS */ + BSS_MAP_MSG_VGCS_VBS_SETUP = 4, + BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5, + BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28, + BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29, + BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30, + BSS_MAP_MSG_UPLINK_RQST = 31, + BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39, + BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73, + BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74, + BSS_MAP_MSG_UPLINK_REJECT_CMD = 75, + BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76, + BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77, +}; + +enum GSM0808_IE_CODING { + GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1, + GSM0808_IE_RESERVED_0 = 2, + GSM0808_IE_RESOURCE_AVAILABLE = 3, + GSM0808_IE_CAUSE = 4, + GSM0808_IE_CELL_IDENTIFIER = 5, + GSM0808_IE_PRIORITY = 6, + GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7, + GSM0808_IE_IMSI = 8, + GSM0808_IE_TMSI = 9, + GSM0808_IE_ENCRYPTION_INFORMATION = 10, + GSM0808_IE_CHANNEL_TYPE = 11, + GSM0808_IE_PERIODICITY = 12, + GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13, + GSM0808_IE_NUMBER_OF_MSS = 14, + GSM0808_IE_RESERVED_1 = 15, + GSM0808_IE_RESERVED_2 = 16, + GSM0808_IE_RESERVED_3 = 17, + GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18, + GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19, + GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20, + GSM0808_IE_RR_CAUSE = 21, + GSM0808_IE_RESERVED_4 = 22, + GSM0808_IE_LAYER_3_INFORMATION = 23, + GSM0808_IE_DLCI = 24, + GSM0808_IE_DOWNLINK_DTX_FLAG = 25, + GSM0808_IE_CELL_IDENTIFIER_LIST = 26, + GSM0808_IE_RESPONSE_RQST = 27, + GSM0808_IE_RESOURCE_INDICATION_METHOD = 28, + GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29, + GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30, + GSM0808_IE_DIAGNOSTIC = 31, + GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32, + GSM0808_IE_CHOSEN_CHANNEL = 33, + GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34, + GSM0808_IE_CIPHER_RESPONSE_MODE = 35, + GSM0808_IE_CHANNEL_NEEDED = 36, + GSM0808_IE_TRACE_TYPE = 37, + GSM0808_IE_TRIGGERID = 38, + GSM0808_IE_TRACE_REFERENCE = 39, + GSM0808_IE_TRANSACTIONID = 40, + GSM0808_IE_MOBILE_IDENTITY = 41, + GSM0808_IE_OMCID = 42, + GSM0808_IE_FORWARD_INDICATOR = 43, + GSM0808_IE_CHOSEN_ENCR_ALG = 44, + GSM0808_IE_CIRCUIT_POOL = 45, + GSM0808_IE_CIRCUIT_POOL_LIST = 46, + GSM0808_IE_TIME_INDICATION = 47, + GSM0808_IE_RESOURCE_SITUATION = 48, + GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49, + GSM0808_IE_QUEUEING_INDICATOR = 50, + GSM0808_IE_SPEECH_VERSION = 64, + GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51, + GSM0808_IE_TALKER_FLAG = 53, + GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54, + GSM0808_IE_GROUP_CALL_REFERENCE = 55, + GSM0808_IE_EMLPP_PRIORITY = 56, + GSM0808_IE_CONFIG_EVO_INDI = 57, + GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58, + GSM0808_IE_LSA_IDENTIFIER = 59, + GSM0808_IE_LSA_IDENTIFIER_LIST = 60, + GSM0808_IE_LSA_INFORMATION = 61, + GSM0808_IE_LCS_QOS = 62, + GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63, + GSM0808_IE_LCS_PRIORITY = 67, + GSM0808_IE_LOCATION_TYPE = 68, + GSM0808_IE_LOCATION_ESTIMATE = 69, + GSM0808_IE_POSITIONING_DATA = 70, + GSM0808_IE_LCS_CAUSE = 71, + GSM0808_IE_LCS_CLIENT_TYPE = 72, + GSM0808_IE_APDU = 73, + GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74, + GSM0808_IE_GPS_ASSISTANCE_DATA = 75, + GSM0808_IE_DECIPHERING_KEYS = 76, + GSM0808_IE_RETURN_ERROR_RQST = 77, + GSM0808_IE_RETURN_ERROR_CAUSE = 78, + GSM0808_IE_SEGMENTATION = 79, + GSM0808_IE_SERVICE_HANDOVER = 80, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81, + GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82, + GSM0808_IE_RESERVED_5 = 65, + GSM0808_IE_RESERVED_6 = 66, +}; + +enum gsm0808_cause { + GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1, + GSM0808_CAUSE_UPLINK_QUALITY = 2, + GSM0808_CAUSE_UPLINK_STRENGTH = 3, + GSM0808_CAUSE_DOWNLINK_QUALITY = 4, + GSM0808_CAUSE_DOWNLINK_STRENGTH = 5, + GSM0808_CAUSE_DISTANCE = 6, + GSM0808_CAUSE_O_AND_M_INTERVENTION = 7, + GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8, + GSM0808_CAUSE_CALL_CONTROL = 9, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10, + GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11, + GSM0808_CAUSE_BETTER_CELL = 12, + GSM0808_CAUSE_DIRECTED_RETRY = 13, + GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14, + GSM0808_CAUSE_TRAFFIC = 15, + GSM0808_CAUSE_EQUIPMENT_FAILURE = 32, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33, + GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34, + GSM0808_CAUSE_CCCH_OVERLOAD = 35, + GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36, + GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37, + GSM0808_CAUSE_MS_NOT_EQUIPPED = 38, + GSM0808_CAUSE_INVALID_CELL = 39, + GSM0808_CAUSE_TRAFFIC_LOAD = 40, + GSM0808_CAUSE_PREEMPTION = 41, + GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48, + GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49, + GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50, + GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51, + GSM0808_CAUSE_LSA_NOT_ALLOWED = 52, + GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64, + GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80, + GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81, + GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82, + GSM0808_CAUSE_INCORRECT_VALUE = 83, + GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84, + GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85, + GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96, +}; + +/* GSM 08.08 3.2.2.11 Channel Type */ +enum gsm0808_chan_indicator { + GSM0808_CHAN_SPEECH = 1, + GSM0808_CHAN_DATA = 2, + GSM0808_CHAN_SIGN = 3, +}; + +enum gsm0808_chan_rate_type_data { + GSM0808_DATA_FULL_BM = 0x8, + GSM0808_DATA_HALF_LM = 0x9, + GSM0808_DATA_FULL_RPREF = 0xa, + GSM0808_DATA_HALF_PREF = 0xb, + GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_DATA_MULTI_MASK = 0x20, + GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30, +}; + +enum gsm0808_chan_rate_type_speech { + GSM0808_SPEECH_FULL_BM = 0x8, + GSM0808_SPEECH_HALF_LM = 0x9, + GSM0808_SPEECH_FULL_PREF= 0xa, + GSM0808_SPEECH_HALF_PREF= 0xb, + GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a, + GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b, + GSM0808_SPEECH_PERM = 0xf, + GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f, +}; + +enum gsm0808_permitted_speech { + GSM0808_PERM_FR1 = 0x01, + GSM0808_PERM_FR2 = 0x11, + GSM0808_PERM_FR3 = 0x21, + GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4, + GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4, + GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4, +}; + +#endif diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h new file mode 100644 index 00000000..7dc35693 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_08_58.h @@ -0,0 +1,516 @@ +#ifndef PROTO_GSM_08_58_H +#define PROTO_GSM_08_58_H + +/* 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. + * + */ + +#include <stdint.h> + +struct abis_rsl_common_hdr { + uint8_t msg_discr; + uint8_t msg_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Chapter 8.3 */ +struct abis_rsl_rll_hdr { + struct abis_rsl_common_hdr c; + uint8_t ie_chan; + uint8_t chan_nr; + uint8_t ie_link_id; + uint8_t link_id; + uint8_t data[0]; +} __attribute__ ((packed)); + +/* Chapter 8.3 and 8.4 */ +struct abis_rsl_dchan_hdr { + struct abis_rsl_common_hdr c; + uint8_t ie_chan; + uint8_t chan_nr; + uint8_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 */ + RSL_MT_SUSP_REQ, /* non-standard elements */ + RSL_MT_SUSP_CONF, + RSL_MT_RES_REQ, + RSL_MT_RECON_REQ, /* 0x0f */ + + /* 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 */ + RSL_MT_LOCATION_INFO = 0x41, + + /* ip.access specific RSL message types */ + RSL_MT_IPAC_DIR_RETR_ENQ = 0x40, + RSL_MT_IPAC_PDCH_ACT = 0x48, + RSL_MT_IPAC_PDCH_ACT_ACK, + RSL_MT_IPAC_PDCH_ACT_NACK, + RSL_MT_IPAC_PDCH_DEACT = 0x4b, + RSL_MT_IPAC_PDCH_DEACT_ACK, + RSL_MT_IPAC_PDCH_DEACT_NACK, + RSL_MT_IPAC_CONNECT_MUX = 0x50, + RSL_MT_IPAC_CONNECT_MUX_ACK, + RSL_MT_IPAC_CONNECT_MUX_NACK, + RSL_MT_IPAC_BIND_MUX = 0x53, + RSL_MT_IPAC_BIND_MUX_ACK, + RSL_MT_IPAC_BIND_MUX_NACK, + RSL_MT_IPAC_DISC_MUX = 0x56, + RSL_MT_IPAC_DISC_MUX_ACK, + RSL_MT_IPAC_DISC_MUX_NACK, + RSL_MT_IPAC_CRCX = 0x70, /* Bind to local BTS RTP port */ + RSL_MT_IPAC_CRCX_ACK, + RSL_MT_IPAC_CRCX_NACK, + RSL_MT_IPAC_MDCX = 0x73, + RSL_MT_IPAC_MDCX_ACK, + RSL_MT_IPAC_MDCX_NACK, + RSL_MT_IPAC_DLCX_IND = 0x76, + RSL_MT_IPAC_DLCX = 0x77, + RSL_MT_IPAC_DLCX_ACK, + RSL_MT_IPAC_DLCX_NACK, +}; + +/* Siemens vendor-specific */ +enum abis_rsl_msgtype_siemens { + RSL_MT_SIEMENS_MRPCI = 0x41, + RSL_MT_SIEMENS_INTRAC_HO_COND_IND = 0x42, + RSL_MT_SIEMENS_INTERC_HO_COND_IND = 0x43, + RSL_MT_SIEMENS_FORCED_HO_REQ = 0x44, + RSL_MT_SIEMENS_PREF_AREA_REQ = 0x45, + RSL_MT_SIEMENS_PREF_AREA = 0x46, + RSL_MT_SIEMENS_START_TRACE = 0x47, + RSL_MT_SIEMENS_START_TRACE_ACK = 0x48, + RSL_MT_SIEMENS_STOP_TRACE = 0x49, + RSL_MT_SIEMENS_TRMR = 0x4a, + RSL_MT_SIEMENS_HO_FAIL_IND = 0x4b, + RSL_MT_SIEMENS_STOP_TRACE_ACK = 0x4c, + RSL_MT_SIEMENS_UPLF = 0x4d, + RSL_MT_SIEMENS_UPLB = 0x4e, + RSL_MT_SIEMENS_SET_SYS_INFO_10 = 0x4f, + RSL_MT_SIEMENS_MODIF_COND_IND = 0x50, +}; + +/* 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 = 0x30, + 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, + /* Siemens vendor-specific */ + RSL_IE_SIEMENS_MRPCI = 0x40, + RSL_IE_SIEMENS_PREF_AREA_TYPE = 0x43, + RSL_IE_SIEMENS_ININ_CELL_HO_PAR = 0x45, + RSL_IE_SIEMENS_TRACE_REF_NR = 0x46, + RSL_IE_SIEMENS_INT_TRACE_IDX = 0x47, + RSL_IE_SIEMENS_L2_HDR_INFO = 0x48, + RSL_IE_SIEMENS_HIGHEST_RATE = 0x4e, + RSL_IE_SIEMENS_SUGGESTED_RATE = 0x4f, + + /* ip.access */ + RSL_IE_IPAC_SRTP_CONFIG = 0xe0, + RSL_IE_IPAC_PROXY_UDP = 0xe1, + RSL_IE_IPAC_BSCMPL_TOUT = 0xe2, + RSL_IE_IPAC_REMOTE_IP = 0xf0, + RSL_IE_IPAC_REMOTE_PORT = 0xf1, + RSL_IE_IPAC_RTP_PAYLOAD = 0xf2, + RSL_IE_IPAC_LOCAL_PORT = 0xf3, + RSL_IE_IPAC_SPEECH_MODE = 0xf4, + RSL_IE_IPAC_LOCAL_IP = 0xf5, + RSL_IE_IPAC_CONN_STAT = 0xf6, + RSL_IE_IPAC_HO_C_PARMS = 0xf7, + RSL_IE_IPAC_CONN_ID = 0xf8, + RSL_IE_IPAC_RTP_CSD_FMT = 0xf9, + RSL_IE_IPAC_RTP_JIT_BUF = 0xfa, + RSL_IE_IPAC_RTP_COMPR = 0xfb, + RSL_IE_IPAC_RTP_PAYLOAD2= 0xfc, + RSL_IE_IPAC_RTP_MPLEX = 0xfd, + RSL_IE_IPAC_RTP_MPLEX_ID= 0xfe, +}; + +/* 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 { + uint8_t dtx_dtu; + uint8_t spd_ind; + uint8_t chan_rt; + uint8_t chan_rate; +} __attribute__ ((packed)); +#define RSL_CMOD_DTXu 0x01 /* uplink */ +#define RSL_CMOD_DTXd 0x02 /* downlink */ +enum rsl_cmod_spd { + RSL_CMOD_SPD_SPEECH = 0x01, + RSL_CMOD_SPD_DATA = 0x02, + 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 */ +/* Speech */ +#define RSL_CMOD_SP_GSM1 0x01 +#define RSL_CMOD_SP_GSM2 0x11 +#define RSL_CMOD_SP_GSM3 0x21 +/* Data */ +#define RSL_CMOD_SP_NT_14k5 0x58 +#define RSL_CMOD_SP_NT_12k0 0x50 +#define RSL_CMOD_SP_NT_6k0 0x51 + +/* Chapter 9.3.5 */ +struct rsl_ie_chan_ident { + /* GSM 04.08 10.5.2.5 */ + struct { + uint8_t iei; + uint8_t chan_nr; /* enc_chan_nr */ + uint8_t oct3; + uint8_t oct4; + } chan_desc; +#if 0 /* spec says we need this but Abissim doesn't use it */ + struct { + uint8_t tag; + uint8_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 + +/* normal event */ +#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_NORMAL_UNSPEC 0x0f +#define RSL_ERR_T_MSRFPCI_EXP 0x18 +/* resource unavailable */ +#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 +/* service or option not available */ +#define RSL_ERR_TRANSC_UNAVAIL 0x30 +#define RSL_ERR_SERV_OPT_UNAVAIL 0x3f +/* service or option not implemented */ +#define RSL_ERR_ENCR_UNIMPL 0x40 +#define RSL_ERR_SERV_OPT_UNIMPL 0x4f +/* invalid message */ +#define RSL_ERR_RCH_ALR_ACTV_ALLOC 0x50 +#define RSL_ERR_INVALID_MESSAGE 0x5f +/* protocol error */ +#define RSL_ERR_MSG_DISCR 0x60 +#define RSL_ERR_MSG_TYPE 0x61 +#define RSL_ERR_MSG_SEQ 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 +/* interworking */ +#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 + +/* RSL_IE_IPAC_RTP_PAYLOAD[2] */ +enum rsl_ipac_rtp_payload { + RSL_IPAC_RTP_GSM = 1, + RSL_IPAC_RTP_EFR, + RSL_IPAC_RTP_AMR, + RSL_IPAC_RTP_CSD, + RSL_IPAC_RTP_MUX, +}; + +/* RSL_IE_IPAC_SPEECH_MODE, lower four bits */ +enum rsl_ipac_speech_mode_s { + RSL_IPAC_SPEECH_GSM_FR = 0, /* GSM FR (Type 1, FS) */ + RSL_IPAC_SPEECH_GSM_EFR = 1, /* GSM EFR (Type 2, FS) */ + RSL_IPAC_SPEECH_GSM_AMR_FR = 2, /* GSM AMR/FR (Type 3, FS) */ + RSL_IPAC_SPEECH_GSM_HR = 3, /* GSM HR (Type 1, HS) */ + RSL_IPAC_SPEECH_GSM_AMR_HR = 5, /* GSM AMR/hr (Type 3, HS) */ + RSL_IPAC_SPEECH_AS_RTP = 0xf, /* As specified by RTP Payload IE */ +}; +/* RSL_IE_IPAC_SPEECH_MODE, upper four bits */ +enum rsl_ipac_speech_mode_m { + RSL_IPAC_SPEECH_M_RXTX = 0, /* Send and Receive */ + RSL_IPAC_SPEECH_M_RX = 1, /* Receive only */ + RSL_IPAC_SPEECH_M_TX = 2, /* Send only */ +}; + +/* RSL_IE_IPAC_RTP_CSD_FMT, lower four bits */ +enum rsl_ipac_rtp_csd_format_d { + RSL_IPAC_RTP_CSD_EXT_TRAU = 0, + RSL_IPAC_RTP_CSD_NON_TRAU = 1, + RSL_IPAC_RTP_CSD_TRAU_BTS = 2, + RSL_IPAC_RTP_CSD_IWF_FREE = 3, +}; +/* RSL_IE_IPAC_RTP_CSD_FMT, upper four bits */ +enum rsl_ipac_rtp_csd_format_ir { + RSL_IPAC_RTP_CSD_IR_8k = 0, + RSL_IPAC_RTP_CSD_IR_16k = 1, + RSL_IPAC_RTP_CSD_IR_32k = 2, + RSL_IPAC_RTP_CSD_IR_64k = 3, +}; + +/* Siemens vendor-specific RSL extensions */ +struct rsl_mrpci { + uint8_t power_class:3, + vgcs_capable:1, + vbs_capable:1, + gsm_phase:2; +} __attribute__ ((packed)); + +enum rsl_mrpci_pwrclass { + RSL_MRPCI_PWRC_1 = 0, + RSL_MRPCI_PWRC_2 = 1, + RSL_MRPCI_PWRC_3 = 2, + RSL_MRPCI_PWRC_4 = 3, + RSL_MRPCI_PWRC_5 = 4, +}; +enum rsl_mrpci_phase { + RSL_MRPCI_PHASE_1 = 0, + /* reserved */ + RSL_MRPCI_PHASE_2 = 2, + RSL_MRPCI_PHASE_2PLUS = 3, +}; + + +#endif /* PROTO_GSM_08_58_H */ diff --git a/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h new file mode 100644 index 00000000..9cae45da --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/protocol/gsm_12_21.h @@ -0,0 +1,713 @@ +#ifndef PROTO_GSM_12_21_H +#define PROTO_GSM_12_21_H + +/* 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. + * + */ + +#include <stdint.h> +#include <osmocore/tlv.h> + +/* generic header in front of every OML message according to TS 08.59 */ +struct abis_om_hdr { + uint8_t mdisc; + uint8_t placement; + uint8_t sequence; + uint8_t length; + uint8_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 { + uint8_t bts_nr; + uint8_t trx_nr; + uint8_t ts_nr; +} __attribute__ ((packed)); + +struct abis_om_fom_hdr { + uint8_t msg_type; + uint8_t obj_class; + struct abis_om_obj_inst obj_inst; + uint8_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_TEST_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, +}; + +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_RESTART = 0x87, + NM_MT_IPACC_RESTART_ACK, + NM_MT_IPACC_RESTART_NACK, + NM_MT_IPACC_RSL_CONNECT = 0xe0, + NM_MT_IPACC_RSL_CONNECT_ACK, + NM_MT_IPACC_RSL_CONNECT_NACK, + NM_MT_IPACC_RSL_DISCONNECT = 0xe3, + NM_MT_IPACC_RSL_DISCONNECT_ACK, + NM_MT_IPACC_RSL_DISCONNECT_NACK, + NM_MT_IPACC_CONN_TRAF = 0xe6, + NM_MT_IPACC_CONN_TRAF_ACK, + NM_MT_IPACC_CONN_TRAF_NACK, + NM_MT_IPACC_DEF_BOOT_SW = 0xec, + NM_MT_IPACC_DEF_BOOT_SW_ACK, + MN_MT_IPACC_DEF_BOOT_SW_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, + NM_MT_IPACC_SET_ATTR = 0xf5, + NM_MT_IPACC_SET_ATTR_ACK, + NM_MT_IPACC_SET_ATTR_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_IPAC_E1_TRUNK = 0x0e, + NM_OC_IPAC_E1_PORT = 0x0f, + NM_OC_IPAC_E1_CHAN = 0x10, + NM_OC_IPAC_CLK_MODULE = 0x22, + + 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_NSVC = 0xf2, + + 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_CELL_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_DST_IP = 0x80, + NM_ATT_IPACC_DST_IP_PORT = 0x81, + NM_ATT_IPACC_SSRC = 0x82, + NM_ATT_IPACC_RTP_PAYLD_TYPE = 0x83, + NM_ATT_IPACC_BASEB_ID = 0x84, + NM_ATT_IPACC_STREAM_ID = 0x85, + NM_ATT_IPACC_NV_FLAGS = 0x86, + NM_ATT_IPACC_FREQ_CTRL = 0x87, + NM_ATT_IPACC_PRIM_OML_CFG = 0x88, + NM_ATT_IPACC_SEC_OML_CFG = 0x89, + NM_ATT_IPACC_IP_IF_CFG = 0x8a, /* IP interface */ + NM_ATT_IPACC_IP_GW_CFG = 0x8b, /* IP gateway */ + NM_ATT_IPACC_IN_SERV_TIME = 0x8c, + NM_ATT_IPACC_TRX_BTS_ASS = 0x8d, + NM_ATT_IPACC_LOCATION = 0x8e, /* string describing location */ + NM_ATT_IPACC_PAGING_CFG = 0x8f, + NM_ATT_IPACC_FILE_DATA = 0x90, + NM_ATT_IPACC_UNIT_ID = 0x91, /* Site/BTS/TRX */ + NM_ATT_IPACC_PARENT_UNIT_ID = 0x92, + NM_ATT_IPACC_UNIT_NAME = 0x93, /* default: nbts-<mac-as-string> */ + NM_ATT_IPACC_SNMP_CFG = 0x94, + NM_ATT_IPACC_PRIM_OML_CFG_LIST = 0x95, + NM_ATT_IPACC_PRIM_OML_FB_TOUT = 0x96, + NM_ATT_IPACC_CUR_SW_CFG = 0x97, + NM_ATT_IPACC_TIMING_BUS = 0x98, + NM_ATT_IPACC_CGI = 0x99, + NM_ATT_IPACC_RAC = 0x9a, + NM_ATT_IPACC_OBJ_VERSION = 0x9b, + NM_ATT_IPACC_GPRS_PAGING_CFG = 0x9c, + NM_ATT_IPACC_NSEI = 0x9d, + NM_ATT_IPACC_BVCI = 0x9e, + NM_ATT_IPACC_NSVCI = 0x9f, + NM_ATT_IPACC_NS_CFG = 0xa0, + NM_ATT_IPACC_BSSGP_CFG = 0xa1, + NM_ATT_IPACC_NS_LINK_CFG = 0xa2, + NM_ATT_IPACC_RLC_CFG = 0xa3, + NM_ATT_IPACC_ALM_THRESH_LIST = 0xa4, + NM_ATT_IPACC_MONIT_VAL_LIST = 0xa5, + NM_ATT_IPACC_TIB_CONTROL = 0xa6, + NM_ATT_IPACC_SUPP_FEATURES = 0xa7, + NM_ATT_IPACC_CODING_SCHEMES = 0xa8, + NM_ATT_IPACC_RLC_CFG_2 = 0xa9, + NM_ATT_IPACC_HEARTB_TOUT = 0xaa, + NM_ATT_IPACC_UPTIME = 0xab, + NM_ATT_IPACC_RLC_CFG_3 = 0xac, + NM_ATT_IPACC_SSL_CFG = 0xad, + NM_ATT_IPACC_SEC_POSSIBLE = 0xae, + NM_ATT_IPACC_IML_SSL_STATE = 0xaf, + NM_ATT_IPACC_REVOC_DATE = 0xb0, + + + 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.7: Administrative State */ +enum abis_nm_avail_state { + NM_AVSTATE_IN_TEST = 1, + NM_AVSTATE_POWER_OFF = 2, + NM_AVSTATE_OFF_LINE = 3, + NM_AVSTATE_DEPENDENCY = 5, + NM_AVSTATE_DEGRADED = 6, + NM_AVSTATE_NOT_INSTALLED= 7, + NM_AVSTATE_OK = 0xff, +}; + +enum abis_nm_op_state { + NM_OPSTATE_DISABLED = 1, + NM_OPSTATE_ENABLED = 2, + NM_OPSTATE_NULL = 0xff, +}; + +/* Section 9.4.13: Channel Combination */ +enum abis_nm_chan_comb { + NM_CHANC_TCHFull = 0x00, /* TCH/F + TCH/H + SACCH/TF */ + NM_CHANC_TCHHalf = 0x01, /* TCH/H(0,1) + FACCH/H(0,1) + + SACCH/TH(0,1) */ + NM_CHANC_TCHHalf2 = 0x02, /* TCH/H(0) + FACCH/H(0) + SACCH/TH(0) + + TCH/H(1) */ + NM_CHANC_SDCCH = 0x03, /* SDCCH/8 + SACCH/8 */ + NM_CHANC_mainBCCH = 0x04, /* FCCH + SCH + BCCH + CCCH */ + NM_CHANC_BCCHComb = 0x05, /* FCCH + SCH + BCCH + CCCH + SDCCH/4 + + SACCH/C4 */ + NM_CHANC_BCCH = 0x06, /* BCCH + CCCH */ + NM_CHANC_BCCH_CBCH = 0x07, /* CHANC_BCCHComb + CBCH */ + NM_CHANC_SDCCH_CBCH = 0x08, /* CHANC_SDCCH8 + CBCH */ + /* ip.access */ + NM_CHANC_IPAC_bPDCH = 0x0b, /* PBCCH + PCCCH + PDTCH/F + PACCH/F + + PTCCH/F */ + NM_CHANC_IPAC_cPDCH = 0x0c, /* PBCCH + PDTCH/F + PACCH/F + PTCCH/F */ + NM_CHANC_IPAC_PDCH = 0x0d, /* PDTCH/F + PACCH/F + PTCCH/F */ + NM_CHANC_IPAC_TCHFull_PDCH = 0x80, + NM_CHANC_IPAC_TCHFull_TCHHalf = 0x81, +}; + +/* 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, + NM_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 { + uint8_t attrib; + uint8_t bts_port; + uint8_t timeslot; + uint8_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_line_cfg { + BS11_LINE_CFG_STAR = 0x00, + BS11_LINE_CFG_MULTIDROP = 0x01, + BS11_LINE_CFG_LOOP = 0x02, +}; + +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, +}; + +enum abis_nm_ipacc_test_no { + NM_IPACC_TESTNO_RLOOP_ANT = 0x01, + NM_IPACC_TESTNO_RLOOP_XCVR = 0x02, + NM_IPACC_TESTNO_FUNC_OBJ = 0x03, + NM_IPACC_TESTNO_CHAN_USAGE = 0x40, + NM_IPACC_TESTNO_BCCH_CHAN_USAGE = 0x41, + NM_IPACC_TESTNO_FREQ_SYNC = 0x42, + NM_IPACC_TESTNO_BCCH_INFO = 0x43, + NM_IPACC_TESTNO_TX_BEACON = 0x44, + NM_IPACC_TESTNO_SYSINFO_MONITOR = 0x45, + NM_IPACC_TESTNO_BCCCH_MONITOR = 0x46, +}; + +/* first byte after length inside NM_ATT_TEST_REPORT */ +enum abis_nm_ipacc_test_res { + NM_IPACC_TESTRES_SUCCESS = 0, + NM_IPACC_TESTRES_TIMEOUT = 1, + NM_IPACC_TESTRES_NO_CHANS = 2, + NM_IPACC_TESTRES_PARTIAL = 3, + NM_IPACC_TESTRES_STOPPED = 4, +}; + +/* internal IE inside NM_ATT_TEST_REPORT */ +enum abis_nm_ipacc_testres_ie { + NM_IPACC_TR_IE_FREQ_ERR_LIST = 3, + NM_IPACC_TR_IE_CHAN_USAGE = 4, + NM_IPACC_TR_IE_BCCH_INFO = 6, + NM_IPACC_TR_IE_RESULT_DETAILS = 8, + NM_IPACC_TR_IE_FREQ_ERR = 18, +}; + +enum ipac_eie { + NM_IPAC_EIE_ARFCN_WHITE = 0x01, + NM_IPAC_EIE_ARFCH_BLACK = 0x02, + NM_IPAC_EIE_FREQ_ERR_LIST = 0x03, + NM_IPAC_EIE_CHAN_USE_LIST = 0x04, + NM_IPAC_EIE_BCCH_INFO_TYPE = 0x05, + NM_IPAC_EIE_BCCH_INFO = 0x06, + NM_IPAC_EIE_CONFIG = 0x07, + NM_IPAC_EIE_RES_DETAILS = 0x08, + NM_IPAC_EIE_RXLEV_THRESH = 0x09, + NM_IPAC_EIE_FREQ_SYNC_OPTS = 0x0a, + NM_IPAC_EIE_MAC_ADDR = 0x0b, + NM_IPAC_EIE_HW_SW_COMPAT_NR = 0x0c, + NM_IPAC_EIE_MANUF_SER_NR = 0x0d, + NM_IPAC_EIE_OEM_ID = 0x0e, + NM_IPAC_EIE_DATE_TIME_MANUF = 0x0f, + NM_IPAC_EIE_DATE_TIME_CALIB = 0x10, + NM_IPAC_EIE_BEACON_INFO = 0x11, + NM_IPAC_EIE_FREQ_ERR = 0x12, + /* FIXME */ + NM_IPAC_EIE_FREQ_BANDS = 0x1c, + NM_IPAC_EIE_MAX_TA = 0x1d, + NM_IPAC_EIE_CIPH_ALGOS = 0x1e, + NM_IPAC_EIE_CHAN_TYPES = 0x1f, + NM_IPAC_EIE_CHAN_MODES = 0x20, + NM_IPAC_EIE_GPRS_CODING = 0x21, + NM_IPAC_EIE_RTP_FEATURES = 0x22, + NM_IPAC_EIE_RSL_FEATURES = 0x23, + NM_IPAC_EIE_BTS_HW_CLASS = 0x24, + NM_IPAC_EIE_BTS_ID = 0x25, +}; + +enum ipac_bcch_info_type { + IPAC_BINF_RXLEV = (1 << 8), + IPAC_BINF_RXQUAL = (1 << 9), + IPAC_BINF_FREQ_ERR_QUAL = (1 << 10), + IPAC_BINF_FRAME_OFFSET = (1 << 11), + IPAC_BINF_FRAME_NR_OFFSET = (1 << 12), + IPAC_BINF_BSIC = (1 << 13), + IPAC_BINF_CGI = (1 << 14), + IPAC_BINF_NEIGH_BA_SI2 = (1 << 15), + IPAC_BINF_NEIGH_BA_SI2bis = (1 << 0), + IPAC_BINF_NEIGH_BA_SI2ter = (1 << 1), + IPAC_BINF_CELL_ALLOC = (1 << 2), +}; + +#endif /* PROTO_GSM_12_21_H */ diff --git a/src/shared/libosmocore/include/osmocore/rate_ctr.h b/src/shared/libosmocore/include/osmocore/rate_ctr.h new file mode 100644 index 00000000..f887d9a7 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/rate_ctr.h @@ -0,0 +1,81 @@ +#ifndef _RATE_CTR_H +#define _RATE_CTR_H + +#include <stdint.h> + +#include <osmocore/linuxlist.h> + +#define RATE_CTR_INTV_NUM 4 + +enum rate_ctr_intv { + RATE_CTR_INTV_SEC, + RATE_CTR_INTV_MIN, + RATE_CTR_INTV_HOUR, + RATE_CTR_INTV_DAY, +}; + +/* for each of the intervals, we keep the following values */ +struct rate_ctr_per_intv { + uint64_t last; + uint64_t rate; +}; + +/* for each actual value, we keep the following data */ +struct rate_ctr { + uint64_t current; + struct rate_ctr_per_intv intv[RATE_CTR_INTV_NUM]; +}; + +struct rate_ctr_desc { + const char *name; + const char *description; +}; + +/* Describe a counter group class */ +struct rate_ctr_group_desc { + /* The prefix to the name of all counters in this group */ + const char *group_name_prefix; + /* The human-readable description of the group */ + const char *group_description; + /* The number of counters in this group */ + const unsigned int num_ctr; + /* Pointer to array of counter names */ + const struct rate_ctr_desc *ctr_desc; +}; + +/* One instance of a counter group class */ +struct rate_ctr_group { + /* Linked list of all counter groups in the system */ + struct llist_head list; + /* Pointer to the counter group class */ + const struct rate_ctr_group_desc *desc; + /* The index of this ctr_group within its class */ + unsigned int idx; + /* Actual counter structures below */ + struct rate_ctr ctr[0]; +}; + +/* Allocate a new group of counters according to description */ +struct rate_ctr_group *rate_ctr_group_alloc(void *ctx, + const struct rate_ctr_group_desc *desc, + unsigned int idx); + +/* Free the memory for the specified group of counters */ +void rate_ctr_group_free(struct rate_ctr_group *grp); + +/* Add a number to the counter */ +void rate_ctr_add(struct rate_ctr *ctr, int inc); + +/* Increment the counter by 1 */ +static inline void rate_ctr_inc(struct rate_ctr *ctr) +{ + rate_ctr_add(ctr, 1); +} + +/* Initialize the counter module */ +int rate_ctr_init(void *tall_ctx); + +struct vty; +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg); +#endif /* RATE_CTR_H */ diff --git a/src/shared/libosmocore/include/osmocore/rsl.h b/src/shared/libosmocore/include/osmocore/rsl.h new file mode 100644 index 00000000..99b90d68 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/rsl.h @@ -0,0 +1,32 @@ +#ifndef _OSMOCORE_RSL_H +#define _OSMOCORE_RSL_H + +#include <stdint.h> +#include <osmocore/utils.h> +#include <osmocore/protocol/gsm_08_58.h> + +void rsl_init_rll_hdr(struct abis_rsl_rll_hdr *dh, uint8_t msg_type); + +extern const struct tlv_definition rsl_att_tlvdef; +#define rsl_tlv_parse(dec, buf, len) \ + tlv_parse(dec, &rsl_att_tlvdef, buf, len, 0, 0) + +/* encode channel number as per Section 9.3.1 */ +uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot); +/* decode channel number as per Section 9.3.1 */ +int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot); + +const char *rsl_err_name(uint8_t err); +const char *rsl_rlm_cause_name(uint8_t err); + +/* Section 3.3.2.3 TS 05.02. I think this looks like a table */ +int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); + +/* Push a RSL RLL header with L3_INFO IE */ +void rsl_rll_push_l3(struct msgb *msg, uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); + +/* Allocate msgb and fill with simple RSL RLL header */ +struct msgb *rsl_rll_simple(uint8_t msg_type, uint8_t chan_nr, + uint8_t link_id, int transparent); +#endif /* _OSMOCORE_RSL_H */ diff --git a/src/shared/libosmocore/include/osmocore/rxlev_stat.h b/src/shared/libosmocore/include/osmocore/rxlev_stat.h new file mode 100644 index 00000000..415509dc --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/rxlev_stat.h @@ -0,0 +1,22 @@ +#ifndef _OSMOCORE_RXLEV_STATS_H +#define _OSMOCORE_RXLEV_STATS_H + +#define NUM_RXLEVS 32 +#define NUM_ARFCNS 1024 + +struct rxlev_stats { + /* the maximum number of ARFCN's is 1024, and there are 32 RxLevels, + * so in we keep one 1024bit-bitvec for each RxLev */ + uint8_t rxlev_buckets[NUM_RXLEVS][NUM_ARFCNS/8]; +}; + +void rxlev_stat_input(struct rxlev_stats *st, uint16_t arfcn, uint8_t rxlev); + +/* get the next ARFCN that has the specified Rxlev */ +int16_t rxlev_stat_get_next(const struct rxlev_stats *st, uint8_t rxlev, int16_t arfcn); + +void rxlev_stat_reset(struct rxlev_stats *st); + +void rxlev_stat_dump(const struct rxlev_stats *st); + +#endif /* _OSMOCORE_RXLEV_STATS_H */ diff --git a/src/shared/libosmocore/include/osmocore/select.h b/src/shared/libosmocore/include/osmocore/select.h new file mode 100644 index 00000000..2d8b3ec0 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/select.h @@ -0,0 +1,22 @@ +#ifndef _BSC_SELECT_H +#define _BSC_SELECT_H + +#include "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/src/shared/libosmocore/include/osmocore/signal.h b/src/shared/libosmocore/include/osmocore/signal.h new file mode 100644 index 00000000..02d83d2e --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/signal.h @@ -0,0 +1,15 @@ +#ifndef OSMOCORE_SIGNAL_H +#define OSMOCORE_SIGNAL_H + +typedef int signal_cbfn(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data); + + +/* 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 /* OSMOCORE_SIGNAL_H */ diff --git a/src/shared/libosmocore/include/osmocore/statistics.h b/src/shared/libosmocore/include/osmocore/statistics.h new file mode 100644 index 00000000..1d56054a --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/statistics.h @@ -0,0 +1,31 @@ +#ifndef _STATISTICS_H +#define _STATISTICS_H + +struct counter { + struct llist_head list; + const char *name; + const char *description; + unsigned long value; +}; + +static inline void counter_inc(struct counter *ctr) +{ + ctr->value++; +} + +static inline unsigned long counter_get(struct counter *ctr) +{ + return ctr->value; +} + +static inline void counter_reset(struct counter *ctr) +{ + ctr->value = 0; +} + +struct counter *counter_alloc(const char *name); +void counter_free(struct counter *ctr); + +int counters_for_each(int (*handle_counter)(struct counter *, void *), void *data); + +#endif /* _STATISTICS_H */ diff --git a/src/shared/libosmocore/include/osmocore/talloc.h b/src/shared/libosmocore/include/osmocore/talloc.h new file mode 100644 index 00000000..f7f7643b --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/talloc.h @@ -0,0 +1,192 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in 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. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +#define HAVE_VA_COPY + +/* this is only needed for compatibility with the old talloc */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)); __talloc_steal_ret; }) +#else +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) +#endif + +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) +#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) + +/* useful macros for creating type checked pointers */ +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) + +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) + +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) + +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) +#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx)) + +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) + +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) + +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) +#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__) + +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) +#endif + +#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0) + +/* The following definitions come from talloc.c */ +void *_talloc(const void *context, size_t size); +void *talloc_pool(const void *context, size_t size); +void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *)); +int talloc_increase_ref_count(const void *ptr); +size_t talloc_reference_count(const void *ptr); +void *_talloc_reference(const void *context, const void *ptr); +int talloc_unlink(const void *context, void *ptr); +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +void talloc_set_name_const(const void *ptr, const char *name); +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); +void *talloc_named_const(const void *context, size_t size, const char *name); +const char *talloc_get_name(const void *ptr); +void *talloc_check_name(const void *ptr, const char *name); +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location); +void *talloc_parent(const void *ptr); +const char *talloc_parent_name(const void *ptr); +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +int talloc_free(void *ptr); +void talloc_free_children(void *ptr); +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +void *_talloc_steal(const void *new_ctx, const void *ptr); +void *_talloc_move(const void *new_ctx, const void *pptr); +size_t talloc_total_size(const void *ptr); +size_t talloc_total_blocks(const void *ptr); +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); +void talloc_report_full(const void *ptr, FILE *f); +void talloc_report(const void *ptr, FILE *f); +void talloc_enable_null_tracking(void); +void talloc_disable_null_tracking(void); +void talloc_enable_leak_report(void); +void talloc_enable_leak_report_full(void); +void *_talloc_zero(const void *ctx, size_t size, const char *name); +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); +void *talloc_autofree_context(void); +size_t talloc_get_size(const void *ctx); +void *talloc_find_parent_byname(const void *ctx, const char *name); +void talloc_show_parents(const void *context, FILE *file); +int talloc_is_parent(const void *context, const void *ptr); + +char *talloc_strdup(const void *t, const char *p); +char *talloc_strdup_append(char *s, const char *a); +char *talloc_strdup_append_buffer(char *s, const char *a); + +char *talloc_strndup(const void *t, const char *p, size_t n); +char *talloc_strndup_append(char *s, const char *a, size_t n); +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/timer.h b/src/shared/libosmocore/include/osmocore/timer.h new file mode 100644 index 00000000..fee888bf --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/timer.h @@ -0,0 +1,72 @@ +/* + * (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; + unsigned int active : 1; + unsigned int handled : 1; + unsigned 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(); +int bsc_timer_check(void); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/tlv.h b/src/shared/libosmocore/include/osmocore/tlv.h new file mode 100644 index 00000000..4cfce872 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/tlv.h @@ -0,0 +1,245 @@ +#ifndef _TLV_H +#define _TLV_H + +#include <stdint.h> +#include <string.h> + +#include <osmocore/msgb.h> + +/* Terminology / wording + tag length value (in bits) + + V - - 8 + LV - 8 N * 8 + TLV 8 8 N * 8 + TL16V 8 16 N * 8 + TLV16 8 8 N * 16 + TvLV 8 8/16 N * 8 + +*/ + +#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) +#define L16TV_GROSS_LEN(x) (x+3) + +#define TVLV_MAX_ONEBYTE 0x7f + +static inline uint16_t TVLV_GROSS_LEN(uint16_t len) +{ + if (len <= TVLV_MAX_ONEBYTE) + return TLV_GROSS_LEN(len); + else + return TL16V_GROSS_LEN(len); +} + +/* TLV generation */ + +static inline uint8_t *lv_put(uint8_t *buf, uint8_t len, + const uint8_t *val) +{ + *buf++ = len; + memcpy(buf, val, len); + return buf + len; +} + +static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len, + const uint8_t *val) +{ + *buf++ = tag; + *buf++ = len; + memcpy(buf, val, len); + return buf + len; +} + +static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len, + const uint16_t *val) +{ + *buf++ = tag; + *buf++ = len; + memcpy(buf, val, len*2); + return buf + len*2; +} + +static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + *buf++ = tag; + *buf++ = len >> 8; + *buf++ = len & 0xff; + memcpy(buf, val, len); + return buf + len*2; +} + +static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *ret; + + if (len <= TVLV_MAX_ONEBYTE) { + ret = tlv_put(buf, tag, len, val); + buf[1] |= 0x80; + } else + ret = tl16v_put(buf, tag, len, val); + + return ret; +} + +static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val) +{ + uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len)); + return tlv16_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len)); + return tl16v_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len)); + return tvlv_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag, + const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len)); + + *buf++ = len >> 8; + *buf++ = len & 0xff; + *buf++ = tag; + memcpy(buf, val, len); + return buf + len; +} + +static inline uint8_t *v_put(uint8_t *buf, uint8_t val) +{ + *buf++ = val; + return buf; +} + +static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, + uint8_t val) +{ + *buf++ = tag; + *buf++ = val; + return buf; +} + +/* 'val' is still in host byte order! */ +static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, + uint16_t val) +{ + *buf++ = tag; + *buf++ = val >> 8; + *buf++ = val & 0xff; + return buf; +} + +static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len)); + return lv_put(buf, len, val); +} + +static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len)); + return tlv_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val) +{ + uint8_t *buf = msgb_put(msg, 2); + return tv_put(buf, tag, val); +} + +static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val) +{ + uint8_t *buf = msgb_put(msg, 1); + return v_put(buf, val); +} + +static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val) +{ + uint8_t *buf = msgb_put(msg, 3); + return tv16_put(buf, tag, val); +} + +static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val) +{ + uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len)); + return tlv_put(buf, tag, len, val); +} + +static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val) +{ + uint8_t *buf = msgb_push(msg, 2); + return tv_put(buf, tag, val); +} + +static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val) +{ + uint8_t *buf = msgb_push(msg, 3); + return tv16_put(buf, tag, val); +} + +static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len, + const uint8_t *val) +{ + uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len)); + return tvlv_put(buf, tag, len, val); +} + +/* TLV parsing */ + +struct tlv_p_entry { + uint16_t len; + const uint8_t *val; +}; + +enum tlv_type { + TLV_TYPE_NONE, + TLV_TYPE_FIXED, + TLV_TYPE_T, + TLV_TYPE_TV, + TLV_TYPE_TLV, + TLV_TYPE_TL16V, + TLV_TYPE_TvLV, + TLV_TYPE_SINGLE_TV +}; + +struct tlv_def { + enum tlv_type type; + uint8_t fixed_len; +}; + +struct tlv_definition { + struct tlv_def def[0xff]; +}; + +struct tlv_parsed { + struct tlv_p_entry lv[0xff]; +}; + +extern struct tlv_definition tvlv_att_def; + +int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val, + const struct tlv_definition *def, + const uint8_t *buf, int buf_len); +int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, + const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2); +/* take a master (src) tlvdev and fill up all empty slots in 'dst' */ +void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src); + +#define TLVP_PRESENT(x, y) ((x)->lv[y].val) +#define TLVP_LEN(x, y) (x)->lv[y].len +#define TLVP_VAL(x, y) (x)->lv[y].val + +#endif /* _TLV_H */ diff --git a/src/shared/libosmocore/include/osmocore/utils.h b/src/shared/libosmocore/include/osmocore/utils.h new file mode 100644 index 00000000..51c6f035 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/utils.h @@ -0,0 +1,20 @@ +#ifndef OSMOCORE_UTIL_H +#define OSMOCORE_UTIL_H + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#include <stdint.h> + +struct value_string { + unsigned int value; + const char *str; +}; + +const char *get_value_string(const struct value_string *vs, uint32_t val); +int get_string_value(const struct value_string *vs, const char *str); + +char bcd2char(uint8_t bcd); +/* only works for numbers in ascci */ +uint8_t char2bcd(char c); + +#endif diff --git a/src/shared/libosmocore/include/osmocore/write_queue.h b/src/shared/libosmocore/include/osmocore/write_queue.h new file mode 100644 index 00000000..ef244c32 --- /dev/null +++ b/src/shared/libosmocore/include/osmocore/write_queue.h @@ -0,0 +1,46 @@ +/* Generic write queue implementation */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#ifndef write_queue_h +#define write_queue_h + +#include "select.h" +#include "msgb.h" + +struct write_queue { + struct bsc_fd bfd; + unsigned int max_length; + unsigned int current_length; + + struct llist_head msg_queue; + + int (*read_cb)(struct bsc_fd *fd); + int (*write_cb)(struct bsc_fd *fd, struct msgb *msg); + int (*except_cb)(struct bsc_fd *fd); +}; + +void write_queue_init(struct write_queue *queue, int max_length); +void write_queue_clear(struct write_queue *queue); +int write_queue_enqueue(struct write_queue *queue, struct msgb *data); +int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what); + +#endif diff --git a/src/shared/libosmocore/libosmocore.pc.in b/src/shared/libosmocore/libosmocore.pc.in new file mode 100644 index 00000000..7c298693 --- /dev/null +++ b/src/shared/libosmocore/libosmocore.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom Core Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmocore +Cflags: -I${includedir}/ + diff --git a/src/shared/libosmocore/libosmovty.pc.in b/src/shared/libosmocore/libosmovty.pc.in new file mode 100644 index 00000000..2cc0b5f8 --- /dev/null +++ b/src/shared/libosmocore/libosmovty.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Osmocom VTY Interface Library +Description: C Utility Library +Version: @VERSION@ +Libs: -L${libdir} -losmovty +Cflags: -I${includedir}/ + diff --git a/src/shared/libosmocore/m4/DUMMY b/src/shared/libosmocore/m4/DUMMY new file mode 100644 index 00000000..fda557ad --- /dev/null +++ b/src/shared/libosmocore/m4/DUMMY @@ -0,0 +1 @@ +Dummply placeholder. diff --git a/src/Makefile.am b/src/shared/libosmocore/src/Makefile.am index a7d450b1..a7d450b1 100644 --- a/src/Makefile.am +++ b/src/shared/libosmocore/src/Makefile.am diff --git a/src/bitvec.c b/src/shared/libosmocore/src/bitvec.c index 04c465a8..04c465a8 100644 --- a/src/bitvec.c +++ b/src/shared/libosmocore/src/bitvec.c diff --git a/src/comp128.c b/src/shared/libosmocore/src/comp128.c index 5d5680c7..5d5680c7 100644 --- a/src/comp128.c +++ b/src/shared/libosmocore/src/comp128.c diff --git a/src/gprs_cipher_core.c b/src/shared/libosmocore/src/gprs_cipher_core.c index 6174bd72..6174bd72 100644 --- a/src/gprs_cipher_core.c +++ b/src/shared/libosmocore/src/gprs_cipher_core.c diff --git a/src/gsm0808.c b/src/shared/libosmocore/src/gsm0808.c index 1dc035b3..1dc035b3 100644 --- a/src/gsm0808.c +++ b/src/shared/libosmocore/src/gsm0808.c diff --git a/src/gsm48.c b/src/shared/libosmocore/src/gsm48.c index daec4f39..daec4f39 100644 --- a/src/gsm48.c +++ b/src/shared/libosmocore/src/gsm48.c diff --git a/src/gsm48_ie.c b/src/shared/libosmocore/src/gsm48_ie.c index 3c2a1f7b..3c2a1f7b 100644 --- a/src/gsm48_ie.c +++ b/src/shared/libosmocore/src/gsm48_ie.c diff --git a/src/gsm_utils.c b/src/shared/libosmocore/src/gsm_utils.c index dc97ceff..dc97ceff 100644 --- a/src/gsm_utils.c +++ b/src/shared/libosmocore/src/gsm_utils.c diff --git a/src/gsmtap_util.c b/src/shared/libosmocore/src/gsmtap_util.c index abee4dac..abee4dac 100644 --- a/src/gsmtap_util.c +++ b/src/shared/libosmocore/src/gsmtap_util.c diff --git a/src/logging.c b/src/shared/libosmocore/src/logging.c index 1dc30db3..1dc30db3 100644 --- a/src/logging.c +++ b/src/shared/libosmocore/src/logging.c diff --git a/src/msgb.c b/src/shared/libosmocore/src/msgb.c index a60e2ffa..a60e2ffa 100644 --- a/src/msgb.c +++ b/src/shared/libosmocore/src/msgb.c diff --git a/src/plugin.c b/src/shared/libosmocore/src/plugin.c index e953508a..e953508a 100644 --- a/src/plugin.c +++ b/src/shared/libosmocore/src/plugin.c diff --git a/src/rate_ctr.c b/src/shared/libosmocore/src/rate_ctr.c index f58b5c4a..f58b5c4a 100644 --- a/src/rate_ctr.c +++ b/src/shared/libosmocore/src/rate_ctr.c diff --git a/src/rsl.c b/src/shared/libosmocore/src/rsl.c index c002d33e..c002d33e 100644 --- a/src/rsl.c +++ b/src/shared/libosmocore/src/rsl.c diff --git a/src/rxlev_stat.c b/src/shared/libosmocore/src/rxlev_stat.c index 1bfd6795..1bfd6795 100644 --- a/src/rxlev_stat.c +++ b/src/shared/libosmocore/src/rxlev_stat.c diff --git a/src/select.c b/src/shared/libosmocore/src/select.c index 2f6afa7f..2f6afa7f 100644 --- a/src/select.c +++ b/src/shared/libosmocore/src/select.c diff --git a/src/signal.c b/src/shared/libosmocore/src/signal.c index c7ca86c4..c7ca86c4 100644 --- a/src/signal.c +++ b/src/shared/libosmocore/src/signal.c diff --git a/src/statistics.c b/src/shared/libosmocore/src/statistics.c index 34e6a408..34e6a408 100644 --- a/src/statistics.c +++ b/src/shared/libosmocore/src/statistics.c diff --git a/src/talloc.c b/src/shared/libosmocore/src/talloc.c index 98c2ee09..98c2ee09 100644 --- a/src/talloc.c +++ b/src/shared/libosmocore/src/talloc.c diff --git a/src/timer.c b/src/shared/libosmocore/src/timer.c index 37d7d166..37d7d166 100644 --- a/src/timer.c +++ b/src/shared/libosmocore/src/timer.c diff --git a/src/tlv_parser.c b/src/shared/libosmocore/src/tlv_parser.c index bbef7a9a..bbef7a9a 100644 --- a/src/tlv_parser.c +++ b/src/shared/libosmocore/src/tlv_parser.c diff --git a/src/utils.c b/src/shared/libosmocore/src/utils.c index 4dab0645..4dab0645 100644 --- a/src/utils.c +++ b/src/shared/libosmocore/src/utils.c diff --git a/src/vty/Makefile.am b/src/shared/libosmocore/src/vty/Makefile.am index f2859cff..f2859cff 100644 --- a/src/vty/Makefile.am +++ b/src/shared/libosmocore/src/vty/Makefile.am diff --git a/src/vty/buffer.c b/src/shared/libosmocore/src/vty/buffer.c index a5655b93..a5655b93 100644 --- a/src/vty/buffer.c +++ b/src/shared/libosmocore/src/vty/buffer.c diff --git a/src/vty/command.c b/src/shared/libosmocore/src/vty/command.c index 21afa5c0..21afa5c0 100644 --- a/src/vty/command.c +++ b/src/shared/libosmocore/src/vty/command.c diff --git a/src/vty/logging_vty.c b/src/shared/libosmocore/src/vty/logging_vty.c index 896d79a9..896d79a9 100644 --- a/src/vty/logging_vty.c +++ b/src/shared/libosmocore/src/vty/logging_vty.c diff --git a/src/vty/telnet_interface.c b/src/shared/libosmocore/src/vty/telnet_interface.c index 90690960..90690960 100644 --- a/src/vty/telnet_interface.c +++ b/src/shared/libosmocore/src/vty/telnet_interface.c diff --git a/src/vty/utils.c b/src/shared/libosmocore/src/vty/utils.c index e163526e..e163526e 100644 --- a/src/vty/utils.c +++ b/src/shared/libosmocore/src/vty/utils.c diff --git a/src/vty/vector.c b/src/shared/libosmocore/src/vty/vector.c index 0343163f..0343163f 100644 --- a/src/vty/vector.c +++ b/src/shared/libosmocore/src/vty/vector.c diff --git a/src/vty/vty.c b/src/shared/libosmocore/src/vty/vty.c index ff17abf6..ff17abf6 100644 --- a/src/vty/vty.c +++ b/src/shared/libosmocore/src/vty/vty.c diff --git a/src/write_queue.c b/src/shared/libosmocore/src/write_queue.c index 618a8c0b..618a8c0b 100644 --- a/src/write_queue.c +++ b/src/shared/libosmocore/src/write_queue.c diff --git a/src/shared/libosmocore/tests/Makefile.am b/src/shared/libosmocore/tests/Makefile.am new file mode 100644 index 00000000..0119a02c --- /dev/null +++ b/src/shared/libosmocore/tests/Makefile.am @@ -0,0 +1,3 @@ +if ENABLE_TESTS +SUBDIRS = timer sms +endif diff --git a/src/shared/libosmocore/tests/sms/Makefile.am b/src/shared/libosmocore/tests/sms/Makefile.am new file mode 100644 index 00000000..a8f1ff6a --- /dev/null +++ b/src/shared/libosmocore/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/libosmocore.la diff --git a/src/shared/libosmocore/tests/sms/sms_test.c b/src/shared/libosmocore/tests/sms/sms_test.c new file mode 100644 index 00000000..f5183d54 --- /dev/null +++ b/src/shared/libosmocore/tests/sms/sms_test.c @@ -0,0 +1,47 @@ +/* + * (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 <osmocore/msgb.h> +#include <osmocore/gsm_utils.h> + +int main(int argc, char** argv) +{ + printf("SMS testing\n"); + struct msgb *msg; + uint8_t *sms; + uint8_t i; + + /* test 7-bit coding/decoding */ + const char *input = "test text"; + uint8_t length; + uint8_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); + } +} diff --git a/src/shared/libosmocore/tests/timer/Makefile.am b/src/shared/libosmocore/tests/timer/Makefile.am new file mode 100644 index 00000000..d3decf55 --- /dev/null +++ b/src/shared/libosmocore/tests/timer/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include +noinst_PROGRAMS = timer_test + +timer_test_SOURCES = timer_test.c +timer_test_LDADD = $(top_builddir)/src/libosmocore.la + diff --git a/src/shared/libosmocore/tests/timer/timer_test.c b/src/shared/libosmocore/tests/timer/timer_test.c new file mode 100644 index 00000000..1b458d81 --- /dev/null +++ b/src/shared/libosmocore/tests/timer/timer_test.c @@ -0,0 +1,77 @@ +/* + * (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 <osmocore/timer.h> +#include <osmocore/select.h> + +#include "../../config.h" + +static void timer_fired(void *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(void *_data) +{ + unsigned long data = (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); + +#ifdef HAVE_SYS_SELECT_H + while (1) { + bsc_select_main(0); + } +#else + printf("Select not supported on this platform!\n"); +#endif +} diff --git a/src/shared/update-libosmocore.sh b/src/shared/update-libosmocore.sh new file mode 100755 index 00000000..e7940c3d --- /dev/null +++ b/src/shared/update-libosmocore.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +(cd ../.. && git-subtree pull --prefix=src/shared/libosmocore git://git.osmocom.org/libosmocore.git master) diff --git a/src/target/firmware/.gitignore b/src/target/firmware/.gitignore new file mode 100644 index 00000000..79e98dfc --- /dev/null +++ b/src/target/firmware/.gitignore @@ -0,0 +1,9 @@ +*.o +*.p +*.a +*.lst +*.bin +*.elf +*.map +*.size +*~ diff --git a/src/target/firmware/COPYING b/src/target/firmware/COPYING new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/target/firmware/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/src/target/firmware/Makefile b/src/target/firmware/Makefile new file mode 100644 index 00000000..ca0a13c1 --- /dev/null +++ b/src/target/firmware/Makefile @@ -0,0 +1,41 @@ + +# Global include path +INCLUDES=-Iinclude/ -I../../../include -I../../shared/libosmocore/include + +# Various objects that are currently linked into all applications +FLASH_OBJS=flash/cfi_flash.o +DISPLAY_OBJS=display/font_r8x8.o display/font_r8x8_horiz.o display/st7558.o display/ssd1783.o display/display.o +ABB_OBJS=abb/twl3025.o +RF_OBJS=rf/trf6151.o + +# Board- and environment-specific startup code and linker script +START=board/common/compal_ramload_start.S + +# List of all supported boards +BOARDS?=compal_e88 compal_e99 gta0x +compal_COMMON_OBJS=board/common/rffe_compal_dualband.o board/common/calypso_uart.o board/common/calypso_pwl.o +gta0x_COMMON_OBJS=board/common/rffe_gta0x_triband.o board/common/calypso_uart.o board/common/calypso_pwl.o +compal_e88_OBJS=$(compal_COMMON_OBJS) board/compal_e88/init.o +compal_e99_OBJS=$(compal_COMMON_OBJS) board/compal_e99/init.o +gta0x_OBJS=$(gta0x_COMMON_OBJS) board/gta0x/init.o + +# List of all supported execution environments +ENVIRONMENTS?=ramload osmoload +ramload_LDS=board/common/compal_ramload.lds +osmoload_LDS=board/common/compal_osmoload.lds + +# List of all applications (add yours here!) +APPLICATIONS=hello_world compal_dump compal_dsp_dump layer1 loader + +# Things that go in all applications +ANY_APP_OBJS+=$(START:.S=.o) $(ABB_OBJS) $(RF_OBJS) $(DISPLAY_OBJS) $(FLASH_OBJS) +ANY_APP_LIBS+=calypso/libcalypso.a layer1/liblayer1.a lib/libmini.a comm/libcomm.a ../../shared/libosmocore/build-target/src/.libs/libosmocore.a + +# Libraries are defined in subdirectories +-include calypso/Makefile +-include layer1/Makefile +-include comm/Makefile +-include lib/Makefile + +# Include rules +-include Makefile.inc diff --git a/src/target/firmware/Makefile.inc b/src/target/firmware/Makefile.inc new file mode 100644 index 00000000..7f91fb18 --- /dev/null +++ b/src/target/firmware/Makefile.inc @@ -0,0 +1,183 @@ + +#### TOOLCHAIN CONFIGURATION #### + +CROSS_COMPILE?=arm-elf- + +CC=gcc +LD=ld +AR=ar +SIZE=size +OBJCOPY=objcopy + +DEBUGF=dwarf-2 + +CFLAGS=-mcpu=arm7tdmi $(INCLUDES) +CFLAGS += -Wall -Wextra -Wcast-align -Wimplicit -Wunused +CFLAGS += -Wswitch -Wredundant-decls -Wreturn-type -Wshadow -Wnested-externs +CFLAGS += -Wbad-function-cast -Wsign-compare -Waggregate-return +CFLAGS += -Wa,-adhlns=$(subst $(suffix $<),.lst,$<) +CFLAGS += -Os -ffunction-sections +CFLAGS += -g$(DEBUGF) + +# Uncomment this line if you want to enable Tx (Transmit) Support. +#CFLAGS += -DCONFIG_TX_ENABLE + +# some older toolchains don't support this, ignore it for now +#ASFLAGS=-Wa,-adhlns=$(<:.S=.lst),--g$(DEBUGF) $(INCLUDES) -D__ASSEMBLY__ +ASFLAGS=-Wa,-adhlns=$(<:.S=.lst) $(INCLUDES) -D__ASSEMBLY__ + +LDFLAGS = -nostartfiles -nostdlib -nodefaultlibs --gc-sections #-Wl,-Map=$(TARGET).map,--cref + +#### GIT VERSION #### + +GIT_COMMIT:=$(shell git describe --always) +GIT_MODIFIED:=$(shell (git status | grep "modified:\|added:\|deleted:" -q) && echo "-modified") + +GIT_REVISION:=$(GIT_COMMIT)$(GIT_MODIFIED) + +CFLAGS += -DGIT_REVISION=\"$(GIT_REVISION)\" + +#### GLOBAL DATA #### + +ALL_OBJS= +ALL_DEPS= + +ALL_LSTS=$(ALL_OBJS:.o=.lst) + +#### APPLICATION DATA #### + +ALL_APPS= + +ALL_APP_TARGETS=$(ALL_APPS) $(ALL_APPS:.elf=.bin) $(ALL_APPS:.elf=.map) $(ALL_APPS:.elf=.size) + +#### LIBRARY DATA #### + +ALL_LIBS= + +ALL_LIB_TARGETS=$(ALL_LIBS) + + +#### DEFAULT RULE #### + +.PHONY: default +default: all + + +#### APPLICATION RULES #### + +ALL_OBJS+=$(ANY_APP_OBJS) +ALL_DEPS+=$(ANY_APP_OBJS:.o=.p) + +# template for application rules +define APPLICATION_BOARD_ENVIRONMENT_template + +# define set of objects for this binary +$(1)_$(2)_$(3)_OBJS := apps/$(1)/main.o $(ANY_APP_OBJS) $(ANY_APP_LIBS) $$($(2)_OBJS) + +# define manifest compilation +board/$(2)/$(1).$(3).manifest.o: board/manifest.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -DAPPLICATION=\"$(3)\" -DBOARD=\"$(2)\" -DENVIRONMENT=\"$(3)\" -c -o $$@ $$< + +# add manifest object to object list +$(1)_$(2)_$(3)_OBJS+=board/$(2)/$(1).$(3).manifest.o + +# define compilation, generating various extra files on the way +board/$(2)/$(1).$(3).elf board/$(2)/$(1).$(3).map board/$(2)/$(1).$(3).size: $$($(1)_$(2)_$(3)_OBJS) $$($(3)_LDS) + $(CROSS_COMPILE)$(LD) $(LDFLAGS) -T $$($(3)_LDS) -Bstatic \ + -Map board/$(2)/$(1).$(3).map -o board/$(2)/$(1).$(3).elf \ + --start-group $$($(1)_$(2)_$(3)_OBJS) --end-group + $(CROSS_COMPILE)$(SIZE) board/$(2)/$(1).$(3).elf | tee board/$(2)/$(1).$(3).size + +ALL_APPS+=board/$(2)/$(1).$(3).elf + +endef + +define BOARD_template + +ALL_OBJS+=$$($(1)_OBJS) + +endef + +define APPLICATION_template + +$(1)_SRCS_REL=$$(patsubst %,$$($(1)_DIR)/%,$$($(1)_SRCS)) +$(1)_OBJS:=$$($(1)_SRCS_REL:.c=.o) +$(1)_OBJS:=$$($(1)_OBJS:.S=.o) + +ALL_OBJS+=$$($(1)_OBJS) apps/$(1)/main.o +ALL_DEPS+=$$($(1)_OBJS:.o=.p) apps/$(1)/main.p + +endef + +# define rules for all defined applications +$(foreach app,$(APPLICATIONS),$(foreach brd,$(BOARDS),$(foreach env,$(ENVIRONMENTS),$(eval $(call APPLICATION_BOARD_ENVIRONMENT_template,$(app),$(brd),$(env)))))) +$(foreach brd,$(BOARDS),$(eval $(call BOARD_template,$(brd)))) +$(foreach app,$(APPLICATIONS),$(eval $(call APPLICATION_template,$(app)))) + + +#### LIBRARY RULES #### + +# template for library rules +define LIBRARY_template + +$(1)_SRCS_REL=$$(patsubst %,$$($(1)_DIR)/%,$$($(1)_SRCS)) +$(1)_OBJS:=$$($(1)_SRCS_REL:.c=.o) +$(1)_OBJS:=$$($(1)_OBJS:.S=.o) + +$$($(1)_DIR)/lib$(1).a: $$($(1)_OBJS) + $(CROSS_COMPILE)$(AR) cru $$($(1)_DIR)/lib$(1).a $$($(1)_OBJS) + +ALL_LIBS+=$$($(1)_DIR)/lib$(1).a + +ALL_OBJS+=$$($(1)_OBJS) +ALL_DEPS+=$$($(1)_OBJS:.o=.p) + +endef + +# define rules for all defined libraries +$(foreach lbr,$(LIBRARIES),$(eval $(call LIBRARY_template,$(lbr)))) + + +#### TOPLEVEL RULES #### + +.PHONY: all +all: $(ALL_DEPS) $(ALL_APP_TARGETS) $(ALL_LIB_TARGETS) + +.PHONY: depend +depend: $(ALL_DEPS) + + +#### COMPILATION RULES #### + +%.p: %.c + @$(CROSS_COMPILE)$(CC) $(CFLAGS) -M -o $(*).d $(<) + @sed 's|.*\.o:|$(@:.p=.o): |g' < $*.d > $@; rm -f $*.d; [ -s $@ ] || rm -f $@ + +%.p: %.S + @$(CROSS_COMPILE)$(CC) $(ASFLAGS) -M -o $(*).d $(<) + @sed 's|.*\.o:|$(@:.p=.o): |g' < $*.d > $@; rm -f $*.d; [ -s $@ ] || rm -f $@ + +%.o: %.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $< + +%.o: %.S + $(CROSS_COMPILE)$(CC) $(ASFLAGS) -c -o $@ $< + + +%.bin: %.elf + $(CROSS_COMPILE)objcopy --gap-fill=0xff -O binary $^ $@ + + +#### CLEANUP RULES #### + +.PHONY: clean +clean: + rm -f $(ALL_APP_TARGETS) $(ALL_LIB_TARGETS) $(ALL_OBJS) $(ALL_DEPS) $(ALL_LSTS) + +.PHONY: distclean +distclean: clean + + +#### DEPENDENCY LOAD #### + +-include $(ALL_DEPS) diff --git a/src/target/firmware/abb/twl3025.c b/src/target/firmware/abb/twl3025.c new file mode 100644 index 00000000..01817130 --- /dev/null +++ b/src/target/firmware/abb/twl3025.c @@ -0,0 +1,356 @@ +/* Driver for Analog Baseband Circuit (TWL3025) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <spi.h> +#include <calypso/irq.h> +#include <calypso/tsp.h> +#include <calypso/tpu.h> +#include <abb/twl3025.h> + +/* TWL3025 */ +#define REG_PAGE(n) (n >> 7) +#define REG_ADDR(n) (n & 0x3f) + +#define TWL3025_DEV_IDX 0 /* On the SPI bus */ +#define TWL3025_TSP_DEV_IDX 0 /* On the TSP bus */ + +/* values encountered on a GTA-02 for GSM900 (the same for GSM1800!?) */ +const uint16_t twl3025_default_ramp[16] = { + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 0, 11), + ABB_RAMP_VAL( 0, 31), + ABB_RAMP_VAL( 0, 31), + ABB_RAMP_VAL( 0, 31), + ABB_RAMP_VAL( 0, 24), + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 9, 0), + ABB_RAMP_VAL(18, 0), + ABB_RAMP_VAL(25, 0), + ABB_RAMP_VAL(31, 0), + ABB_RAMP_VAL(30, 0), + ABB_RAMP_VAL(15, 0), + ABB_RAMP_VAL( 0, 0), + ABB_RAMP_VAL( 0, 0), +}; + +struct twl3025 { + uint8_t page; +}; +static struct twl3025 twl3025_state; + +/* Switch the register page of the TWL3025 */ +static void twl3025_switch_page(uint8_t page) +{ + if (page == 0) + twl3025_reg_write(PAGEREG, 1 << 0); + else + twl3025_reg_write(PAGEREG, 1 << 1); + + twl3025_state.page = page; +} + +static void handle_charger(void) +{ + uint16_t status; + printd("handle_charger();"); + + status = twl3025_reg_read(VRPCSTS); +// printd("\nvrpcsts: 0x%02x", status); + + if (status & 0x40) { + printd(" inserted\n"); + } else { + printd(" removed\n"); + } + +// twl3025_dump_madc(); +} + +static void handle_adc_done(void) +{ + printd("handle_adc_done();"); +} + +static void twl3025_irq(enum irq_nr nr) +{ + uint16_t src; + printd("twl3025_irq: 0x%02x\n",nr); + switch (nr){ + case IRQ_EXTERNAL: // charger in/out, pwrbtn, adc done + src = twl3025_reg_read(ITSTATREG); +// printd("itstatreg 0x%02x\n", src); + if (src & 0x08) + handle_charger(); + if (src & 0x20) + handle_adc_done(); + break; + case IRQ_EXTERNAL_FIQ: // vcc <2.8V emergency power off + puts("\nBROWNOUT!1!"); + twl3025_power_off(); + break; + default: + return; + } +} + +void twl3025_init(void) +{ + spi_init(); + twl3025_switch_page(0); + twl3025_clk13m(1); + twl3025_reg_write(AFCCTLADD, 0x01); /* AFCCK(1:0) must not be zero! */ + twl3025_unit_enable(TWL3025_UNIT_AFC, 1); + + irq_register_handler(IRQ_EXTERNAL, &twl3025_irq); + irq_config(IRQ_EXTERNAL, 0, 0, 0); + irq_enable(IRQ_EXTERNAL); + + irq_register_handler(IRQ_EXTERNAL_FIQ, &twl3025_irq); + irq_config(IRQ_EXTERNAL_FIQ, 1, 0, 0); + irq_enable(IRQ_EXTERNAL_FIQ); +} + +void twl3025_reg_write(uint8_t reg, uint16_t data) +{ + uint16_t tx; + + printd("tw3025_reg_write(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), data); + + if (reg != PAGEREG && REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = ((data & 0x3ff) << 6) | (REG_ADDR(reg) << 1); + + spi_xfer(TWL3025_DEV_IDX, 16, &tx, NULL); +} + +void twl3025_tsp_write(uint8_t data) +{ + tsp_write(TWL3025_TSP_DEV_IDX, 7, data); +} + +uint16_t twl3025_reg_read(uint8_t reg) +{ + uint16_t tx, rx; + + if (REG_PAGE(reg) != twl3025_state.page) + twl3025_switch_page(REG_PAGE(reg)); + + tx = (REG_ADDR(reg) << 1) | 1; + + /* A read cycle contains two SPI transfers */ + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + delay_ms(1); + spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx); + + rx >>= 6; + + printd("tw3025_reg_read(%u,%u)=0x%04x\n", REG_PAGE(reg), + REG_ADDR(reg), rx); + + return rx; +} + +static void twl3025_wait_ibic_access(void) +{ + /* Wait 6 * 32kHz clock cycles for first IBIC access (187us + 10% = 210us) */ + delay_ms(1); +} + +void twl3025_power_off(void) +{ + twl3025_reg_write(VRPCDEV, 0x01); +} + +void twl3025_clk13m(int enable) +{ + if (enable) { + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + /* for whatever reason we need to do this twice */ + twl3025_reg_write(TOGBR2, TOGBR2_ACTS); + twl3025_wait_ibic_access(); + } else { + twl3025_reg_write(TOGBR2, TOGBR2_ACTR); + twl3025_wait_ibic_access(); + } +} + +#define TSP_DELAY 6 /* 13* Tclk6M5 = ~ 3 GSM Qbits + 3 TPU instructions */ +#define BDLON_TO_BDLCAL 6 +#define BDLCAL_DURATION 66 +#define BDLON_TO_BDLENA 7 +#define BULON_TO_BULENA 16 +#define BULON_TO_BULCAL 17 +#define BULCAL_DURATION 143 /* really that long? */ + +/* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL - TSP_DELAY */ +#define DOWNLINK_DELAY (3 * TSP_DELAY + BDLCAL_DURATION + BDLON_TO_BDLCAL) + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at) +{ + int16_t bdl_ena = at - TSP_DELAY - 6; + + if (on) { + if (bdl_ena < 0) + printf("BDLENA time negative (%d)\n", bdl_ena); + /* calibration should be done just before BDLENA */ + tpu_enq_at(bdl_ena - DOWNLINK_DELAY); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL - TSP_DELAY */ + twl3025_tsp_write(BDLON); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY - BDLON_TO_BDLCAL */ + tpu_enq_wait(BDLON_TO_BDLCAL - TSP_DELAY); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION - TSP_DELAY */ + twl3025_tsp_write(BDLON | BDLCAL); + /* bdl_ena - TSP_DELAY - BDLCAL_DURATION */ + tpu_enq_wait(BDLCAL_DURATION - TSP_DELAY); + /* bdl_ena - TSP_DELAY */ + twl3025_tsp_write(BDLON); + //tpu_enq_wait(BDLCAL_TO_BDLENA) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON | BDLENA); + } else { + tpu_enq_at(bdl_ena); + twl3025_tsp_write(BDLON); + //tpu_enq_wait(nBDLENA_TO_nBDLON) this is only 3.7us == 4 qbits, i.e. less than the TSP_DELAY + twl3025_tsp_write(0); + } +} + +/* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL - TSP_DELAY */ +#define UPLINK_DELAY (3 * TSP_DELAY + BULCAL_DURATION + BULON_TO_BULCAL + 35) + +void twl3025_uplink(int on, int16_t at) +{ + int16_t bul_ena = at - TSP_DELAY - 6; + + if (bul_ena < 0) + printf("BULENA time negative (%d)\n", bul_ena); + if (on) { + /* calibration should be done just before BULENA */ + tpu_enq_at(bul_ena - UPLINK_DELAY); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL - TSP_DELAY */ + twl3025_tsp_write(BULON); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY - BULON_TO_BULCAL */ + tpu_enq_wait(BULON_TO_BULCAL - TSP_DELAY); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION - TSP_DELAY */ + twl3025_tsp_write(BULON | BULCAL); + /* bdl_ena - 35 - TSP_DELAY - BULCAL_DURATION */ + tpu_enq_wait(BULCAL_DURATION - TSP_DELAY); + /* bdl_ena - 35 - TSP_DELAY */ + twl3025_tsp_write(BULON); + /* bdl_ena - 35 */ + tpu_enq_wait(35); /* minimum time required to bring the ramp up (really needed?) */ + tpu_enq_at(bul_ena); + twl3025_tsp_write(BULON | BULENA); + } else { + tpu_enq_at(bul_ena); + twl3025_tsp_write(BULON); + tpu_enq_wait(35); /* minimum time required to bring the ramp down (needed!) */ + twl3025_tsp_write(0); + } +} + +void twl3025_afc_set(int16_t val) +{ + printf("twl3025_afc_set(%d)\n", val); + + if (val > 4095) + val = 4095; + else if (val <= -4096) + val = -4096; + + /* FIXME: we currently write from the USP rather than BSP */ + twl3025_reg_write(AUXAFC2, val >> 10); + twl3025_reg_write(AUXAFC1, val & 0x3ff); +} + +int16_t twl3025_afc_get(void) +{ + int16_t val; + + val = (twl3025_reg_read(AUXAFC2) & 0x7); + val = val << 10; + val = val | (twl3025_reg_read(AUXAFC1) & 0x3ff); + + if (val > 4095) + val = -(8192 - val); + return val; +} + +void twl3025_unit_enable(enum twl3025_unit unit, int on) +{ + uint16_t togbr1 = 0; + + switch (unit) { + case TWL3025_UNIT_AFC: + if (on) + togbr1 = (1 << 7); + else + togbr1 = (1 << 6); + break; + case TWL3025_UNIT_MAD: + if (on) + togbr1 = (1 << 9); + else + togbr1 = (1 << 8); + break; + case TWL3025_UNIT_ADA: + if (on) + togbr1 = (1 << 5); + else + togbr1 = (1 << 4); + case TWL3025_UNIT_VDL: + if (on) + togbr1 = (1 << 3); + else + togbr1 = (1 << 2); + break; + case TWL3025_UNIT_VUL: + if (on) + togbr1 = (1 << 1); + else + togbr1 = (1 << 0); + break; + } + twl3025_reg_write(TOGBR1, togbr1); +} + +uint8_t twl3025_afcout_get(void) +{ + return twl3025_reg_read(AFCOUT) & 0xff; +} + +void twl3025_afcout_set(uint8_t val) +{ + twl3025_reg_write(AFCCTLADD, 0x05); + twl3025_reg_write(AFCOUT, val); +} diff --git a/src/target/firmware/apps/compal_dsp_dump/main.c b/src/target/firmware/apps/compal_dsp_dump/main.c new file mode 100644 index 00000000..282bce28 --- /dev/null +++ b/src/target/firmware/apps/compal_dsp_dump/main.c @@ -0,0 +1,74 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 Harald Welte <laforge@gnumonks.org> + * (C) 2010 Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> +#include <stdio.h> +#include <stdint.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <comm/timer.h> + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +int main(void) +{ + puts("\n\nCompal DSP data dumper\n"); + puts(hr); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Dump DSP content */ + dsp_dump(); + + while (1) { + update_timers(); + } +} + diff --git a/src/target/firmware/apps/compal_dump/main.c b/src/target/firmware/apps/compal_dump/main.c new file mode 100644 index 00000000..0eaf9b79 --- /dev/null +++ b/src/target/firmware/apps/compal_dump/main.c @@ -0,0 +1,93 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> +#include <stdio.h> +#include <stdint.h> +#include <cfi_flash.h> +#include <abb/twl3025.h> +#include <calypso/clock.h> +#include <calypso/timer.h> +#include <calypso/misc.h> +#include <comm/timer.h> + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) + +#define REG_DEV_ID_CODE 0xfffef000 +#define REG_DEV_VER_CODE 0xfffef002 +#define REG_DEV_ARMVER_CODE 0xfffffe00 +#define REG_cDSP_ID_CODE 0xfffffe02 +#define REG_DIE_ID_CODE 0xfffef010 + +/* Main Program */ +const char *hr = "======================================================================\n"; + +int main(void) +{ + puts("\n\nCompal device data dumper\n"); + puts(hr); + + /* Disable watchdog (for phones that have it enabled after boot) */ + wdog_enable(0); + + /* Initialize TWL3025 for power control */ + twl3025_init(); + + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Initialize flash, dumping the protection area. */ + cfi_flash_t f; + flash_init(&f, 0x00000000); + flash_dump_info(&f); + puts(hr); + + /* Dump flash contents */ + printf("Dump %lu kbytes of external flash\n", f.f_size/1024); + memdump_range((void *)0x00000000, f.f_size); + puts(hr); + + /* Power down */ + twl3025_power_off(); + + while (1) { + update_timers(); + } +} + diff --git a/src/target/firmware/apps/hello_world/main.c b/src/target/firmware/apps/hello_world/main.c new file mode 100644 index 00000000..effbc90a --- /dev/null +++ b/src/target/firmware/apps/hello_world/main.c @@ -0,0 +1,137 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <comm/sercomm.h> +#include <comm/timer.h> + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +void key_handler(enum key_codes code, enum key_states state); + +static void console_rx_cb(uint8_t dlci, struct msgb *msg) +{ + if (dlci != SC_DLCI_CONSOLE) { + printf("Message for unknown DLCI %u\n", dlci); + return; + } + + printf("Message on console DLCI: '%s'\n", msg->data); + display_puts((char *) msg->data); + msgb_free(msg); +} + +static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) +{ + int i; + puts("l1a_l23_rx_cb: "); + for (i = 0; i < msg->len; i++) + printf("%02x ", msg->data[i]); + puts("\n"); +} + +int main(void) +{ + puts("\n\nHello World from " __FILE__ " program code\n"); + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + /* Dump clock config before PLL set */ + calypso_clk_dump(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + /* Dump all memory */ + //dump_mem(); +#if 0 + /* Dump Bootloader */ + memdump_range((void *)0x00000000, 0x2000); + puts(hr); +#endif + + display_set_attr(DISP_ATTR_INVERT); + display_puts("Hello World"); + + sercomm_register_rx_cb(SC_DLCI_CONSOLE, console_rx_cb); + sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb); + + /* beyond this point we only react to interrupts */ + puts("entering interrupt loop\n"); + while (1) { + update_timers(); + } + + twl3025_power_off(); + + while (1) {} +} + +void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + default: + break; + } +} diff --git a/src/target/firmware/apps/l1test/main.c b/src/target/firmware/apps/l1test/main.c new file mode 100644 index 00000000..6e78f9c9 --- /dev/null +++ b/src/target/firmware/apps/l1test/main.c @@ -0,0 +1,288 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> + +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +#define SCAN + +#ifdef SCAN +/* if scanning is enabled, scan from 0 ... 124 */ +#define BASE_ARFCN 0 +#else +/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */ +#define BASE_ARFCN 871 +#endif + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +/* Best ARFCN MAP ************************************************************/ + +struct arfcn_map { + uint16_t arfcn; + int16_t dbm8; +}; + +static struct arfcn_map best_arfcn_map[10]; +static void best_arfcn_update(uint16_t arfcn, int16_t dbm8) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 < dbm8 || + best_arfcn_map[i].dbm8 == 0) { + best_arfcn_map[i].dbm8 = dbm8; + best_arfcn_map[i].arfcn = arfcn; + return; + } + } +} + +static void best_arfcn_dump(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(best_arfcn_map); i++) { + if (best_arfcn_map[i].dbm8 == 0) + continue; + printf("ARFCN %3d: %d dBm\n", + best_arfcn_map[i].arfcn, + best_arfcn_map[i].dbm8/8); + } +} + + +/* MAIN program **************************************************************/ + +enum l1test_state { + STATE_NONE, + STATE_PM, + STATE_FB, +}; + +static void l1test_state_change(enum l1test_state new_state) +{ + switch (new_state) { + case STATE_PM: + puts("Performing power measurement over GSM900\n"); + l1s_pm_test(1, BASE_ARFCN); + break; + case STATE_FB: + puts("Starting FCCH Recognition\n"); + l1s_fb_test(1, 0); + break; + case STATE_NONE: + /* disable frame interrupts */ + tpu_frame_irq_en(0, 0); + break; + } +} + +/* completion call-back for the L1 Sync Pwer Measurement */ +static void l1s_signal_cb(struct l1_signal *sig) +{ + uint16_t i, next_arfcn; + + switch (sig->signum) { + case L1_SIG_PM: + best_arfcn_update(sig->arfcn, sig->pm.dbm8[0]); + next_arfcn = sig->arfcn + 1; + + if (next_arfcn >= 124) { + puts("ARFCN Top 10 Rx Level\n"); + best_arfcn_dump(); + + trf6151_rx_window(0, best_arfcn_map[0].arfcn, 40, 0); + tpu_end_scenario(); + + /* PM phase completed, do FB det */ + l1test_state_change(STATE_FB); + + break; + } + + /* restart Power Measurement */ + l1s_pm_test(1, next_arfcn); + break; + case L1_SIG_NB: + puts("NB SNR "); + for (i = 0; i < 4; i++) { + uint16_t snr = sig->nb.meas[i].snr; + printf("%d.%03u ", l1s_snr_int(snr), l1s_snr_fract(snr)); + } + putchar('\n'); + printf("--> Frame %d %d 0x%04X ", sig->nb.fire, sig->nb.crc, sig->nb.num_biterr); + for (i = 0; i < ARRAY_SIZE(sig->nb.frame); i++) + printf("%02X ", sig->nb.frame[i]); + putchar('\n'); + break; + } +} + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + display_set_attr(DISP_ATTR_INVERT); + display_puts("l1test.bin"); + + layer1_init(); + l1s_set_handler(&l1s_signal_cb); + + //dsp_checksum_task(); +#ifdef SCAN + l1test_state_change(STATE_PM); +#else + l1test_state_change(STATE_FB); +#endif + tpu_frame_irq_en(1, 1); + + while (1) { + update_timers(); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/layer1/main.c b/src/target/firmware/apps/layer1/main.c new file mode 100644 index 00000000..cf45d7fd --- /dev/null +++ b/src/target/firmware/apps/layer1/main.c @@ -0,0 +1,173 @@ +/* main program of Free Software for Calypso Phone */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> + +#include <abb/twl3025.h> +#include <display.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} + +const char *hr = "======================================================================\n"; + +/* MAIN program **************************************************************/ + +static void key_handler(enum key_codes code, enum key_states state); + +int main(void) +{ + puts("\n\nHello World from " __FILE__ " program code\n"); + + puts(hr); + /* Dump device identification */ + dump_dev_id(); + puts(hr); + + keypad_set_handler(&key_handler); + + /* Dump clock config aftee PLL set */ + calypso_clk_dump(); + puts(hr); + + display_set_attr(DISP_ATTR_INVERT); + display_puts("layer1.bin"); + + layer1_init(); + + tpu_frame_irq_en(1, 1); + + while (1) { + l1a_compl_execute(); + update_timers(); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static int8_t vga_gain = 40; +static int high_gain = 0; +static int afcout = 0; + +static void update_vga_gain(void) +{ + printf("VGA Gain: %u %s\n", vga_gain, high_gain ? "HIGH" : "LOW"); + trf6151_set_gain(vga_gain, high_gain); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void tspact_toggle(uint8_t num) +{ + printf("TSPACT%u toggle\n", num); + tsp_act_toggle((1 << num)); + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_1: /* VGA gain decrement */ + vga_gain -= 2; + if (vga_gain < 14) + vga_gain = 14; + update_vga_gain(); + break; + case KEY_2: /* High/Low Rx gain */ + high_gain ^= 1; + update_vga_gain(); + break; + case KEY_3: /* VGA gain increment */ + vga_gain += 2; + if (vga_gain > 40) + vga_gain = 40; + update_vga_gain(); + break; + case KEY_4: + tspact_toggle(6); /* TRENA (RFFE) */ + break; + case KEY_5: + tspact_toggle(8); /* GSM_TXEN (RFFE) */ + break; + case KEY_6: + tspact_toggle(1); /* PAENA (RFFE) */ + break; + case KEY_7: /* decrement AFC OUT */ + afcout -= 100; + if (afcout < -4096) + afcout = -4096; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + case KEY_9: /* increase AFC OUT */ + afcout += 100; + if (afcout > 4095) + afcout = 4095; + twl3025_afc_set(afcout); + printf("AFC OUT: %u\n", twl3025_afcout_get()); + break; + default: + break; + } +} + + diff --git a/src/target/firmware/apps/loader/main.c b/src/target/firmware/apps/loader/main.c new file mode 100644 index 00000000..f4e1f545 --- /dev/null +++ b/src/target/firmware/apps/loader/main.c @@ -0,0 +1,225 @@ +/* boot loader for Calypso phones */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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 <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <keypad.h> +#include <board.h> +#include <console.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <comm/sercomm.h> + +#include <calypso/clock.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/irq.h> +#include <calypso/misc.h> +#include <calypso/uart.h> +#include <calypso/timer.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +#include "protocol.h" + +struct loader_mem_read { + uint8_t cmd; + uint8_t nbytes; + uint32_t address; + uint8_t data[0]; +} __attribute__((__packed__)); + +uint32_t htonl(uint32_t hostlong) { +#if BYTE_ORDER==LITTLE_ENDIAN + return (hostlong>>24) | ((hostlong&0xff0000)>>8) | + ((hostlong&0xff00)<<8) | (hostlong<<24); +#else + return hostlong; +#endif +} + +uint32_t ntohl(uint32_t hostlong) __attribute__((weak,alias("htonl"))); + +uint16_t htons(uint16_t hostshort) { +#if BYTE_ORDER==LITTLE_ENDIAN + return ((hostshort>>8)&0xff) | (hostshort<<8); +#else + return hostshort; +#endif +} + +uint16_t ntohs(uint16_t hostshort) __attribute__((weak,alias("htons"))); + +#define SCAN + +#ifdef SCAN +/* if scanning is enabled, scan from 0 ... 124 */ +#define BASE_ARFCN 0 +#else +/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */ +#define BASE_ARFCN 871 +#endif + +/* FIXME: We need proper calibrated delay loops at some point! */ +void delay_us(unsigned int us) +{ + volatile unsigned int i; + + for (i= 0; i < us*4; i++) { i; } +} + +void delay_ms(unsigned int ms) +{ + volatile unsigned int i; + + for (i= 0; i < ms*1300; i++) { i; } +} + +/* Main Program */ +const char *hr = "======================================================================\n"; + +static void key_handler(enum key_codes code, enum key_states state); +static void cmd_handler(uint8_t dlci, struct msgb *msg); + +int flag = 0; + +void poweroff(void) { + unsigned i; + for(i = 0; i < 10; i++) { + uart_poll(SERCOMM_UART_NR); + delay_ms(10); + } + twl3025_power_off(); +} + +int main(void) +{ + /* Always disable wdt (some platforms enable it on boot) */ + wdog_enable(0); + + /* Initialize TWL3025 for power control */ + twl3025_init(); + + /* Initialize UART without interrupts */ + uart_init(SERCOMM_UART_NR, 0); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* Initialize HDLC subsystem */ + sercomm_init(); + + /* Say hi */ + puts("\n\nOSMOCOM Calypso loader\n"); + puts(hr); + + /* Set up a key handler for powering off */ + keypad_set_handler(&key_handler); + + /* Set up loader communications */ + sercomm_register_rx_cb(SC_DLCI_LOADER, &cmd_handler); + + /* Wait for events */ + while (1) { + keypad_poll(); + uart_poll(SERCOMM_UART_NR); + } + + /* NOT REACHED */ + + twl3025_power_off(); +} + +static void cmd_handler(uint8_t dlci, struct msgb *msg) { + if(msg->data_len < 1) { + return; + } + + uint8_t command = 0; //= msgb_get_u8(msg); + + printf("command %u\n", command); + + msgb_free(msg); + + return; + + uint8_t nbytes; + uint32_t address; + + struct msgb *reply; + + switch(command) { + + case LOADER_PING: + + printf("ping\n"); + + //sercomm_sendmsg(dlci, msg); + //msg = NULL; + + break; + + case LOADER_MEM_READ: + + nbytes = msgb_get_u8(msg); + address = msgb_get_u32(msg); + + printf("mem read %u @ %p\n", nbytes, (void*)address); + + reply = sercomm_alloc_msgb(6 + nbytes); + + msgb_put_u8(reply, LOADER_MEM_READ); + msgb_put_u8(reply, nbytes); + msgb_put_u32(reply, address); + + memcpy(msgb_put(reply, nbytes), (void*)address, nbytes); + + sercomm_sendmsg(dlci, reply); + + break; + + } + + if(msg) { + msgb_free(msg); + } +} + +static void key_handler(enum key_codes code, enum key_states state) +{ + if (state != PRESSED) + return; + + switch (code) { + case KEY_POWER: + poweroff(); + break; + default: + break; + } +} diff --git a/src/target/firmware/apps/loader/protocol.h b/src/target/firmware/apps/loader/protocol.h new file mode 100644 index 00000000..afbd2390 --- /dev/null +++ b/src/target/firmware/apps/loader/protocol.h @@ -0,0 +1,6 @@ + +enum loader_command { + LOADER_PING, + LOADER_MEM_READ, + LOADER_MEM_WRITE, +}; diff --git a/src/target/firmware/board/common/calypso_pwl.S b/src/target/firmware/board/common/calypso_pwl.S new file mode 100644 index 00000000..90e29bff --- /dev/null +++ b/src/target/firmware/board/common/calypso_pwl.S @@ -0,0 +1,21 @@ + +/* Calypso PWL driver */ + +#define ASIC_CONF_REG 0xfffef008 +#define BA_PWL 0xfffe8000 + +.globl pwl_init +pwl_init: ldr r1, =ASIC_CONF_REG + ldr r2, [r1] + orr r2, r2, #0x10 @ set light output to PWL + str r2, [r1] + ldr r1, =BA_PWL + mov r0, #1 + strb r0, [r1, #1] @ enable clock of PWL unut + mov pc, lr + +.globl pwl_set_level +pwl_set_level: ldr r1, =BA_PWL + strb r0, [r1] + mov pc, lr + diff --git a/src/target/firmware/board/common/calypso_uart.S b/src/target/firmware/board/common/calypso_uart.S new file mode 100644 index 00000000..808cb051 --- /dev/null +++ b/src/target/firmware/board/common/calypso_uart.S @@ -0,0 +1,92 @@ +/* uart routines for early assembly code */ + +#define BA_UART_MODEM 0xFFFF5800 + +.macro senduart, rd, rx + strb \rd, [\rx, #0] +.endm + +.macro busyuart, rd, rx +1001: + @busy waiting until THR is empty + ldrb \rd, [\rx, #5] @ read LSR register + mov \rd, \rd, lsr #6 + tst \rd, #1 + beq 1001b +.endm + +.macro loadsp, rd + ldr \rd, =BA_UART_MODEM +.endm + +.section .text + + .align 2 + .type phexbuf, #object +phexbuf: .space 12 + .size phexubf, . - phexbuf + +.globl phex +phex: adr r3, phexbuf + mov r2, #0 + strb r2, [r3, r1] +1: subs r1, r1, #1 + movmi r0, r3 + bmi puts_asm + and r2, r0, #15 + mov r0, r0, lsr #4 + cmp r2, #10 + addge r2, r2, #7 + add r2, r2, #'0' + strb r2, [r3, r1] + b 1b + +.globl puts_asm +puts_asm: loadsp r3 +1: ldrb r2, [r0], #1 + teq r2, #0 + moveq pc, lr +2: senduart r2, r3 + busyuart r1, r3 + teq r2, #'\n' + moveq r2, #'\r' + beq 2b + teq r0, #0 + bne 1b + mov pc, lr + +.globl putchar_asm +putchar_asm: + mov r2, r0 + mov r0, #0 + loadsp r3 + b 2b + +.globl memdump_asm +memdump_asm: mov r12, r0 + mov r10, lr + mov r11, #0 +2: mov r0, r11, lsl #2 + add r0, r0, r12 + mov r1, #8 + bl phex + mov r0, #':' + bl putchar_asm +1: mov r0, #' ' + bl putchar_asm + ldr r0, [r12, r11, lsl #2] + mov r1, #8 + bl phex + and r0, r11, #7 + teq r0, #3 + moveq r0, #' ' + bleq putchar_asm + and r0, r11, #7 + add r11, r11, #1 + teq r0, #7 + bne 1b + mov r0, #'\n' + bl putchar_asm + cmp r11, #64 + blt 2b + mov pc, r10 diff --git a/src/target/firmware/board/common/compal_osmoload.lds b/src/target/firmware/board/common/compal_osmoload.lds new file mode 100644 index 00000000..2765ec22 --- /dev/null +++ b/src/target/firmware/board/common/compal_osmoload.lds @@ -0,0 +1,98 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* compal-loaded binary: text, initialized data */ + LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000 + /* compal-loaded binary: unitialized data, stacks, heap */ + DRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00010000 + /* remainder of internal ram: this is where we are linking to */ + IRAM (rw) : ORIGIN = 0x00820000, LENGTH = 0x00020000 +} +SECTIONS +{ + . = 0x820000; + + /* initialization code */ + . = ALIGN(4); + .text.start : { + PROVIDE(_start = .); + KEEP(*(.text.start)) + *(.text.start) + } > IRAM + + /* exception vectors from 0x80001c to 0x800034 */ + .text.exceptions 0x80001c : AT (LOADADDR(.text.start) + SIZEOF(.text.start)) { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + + /* constructors and destructors */ + . = ALIGN(4); + __CTOR_LIST__ = .; + LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) + KEEP(*(SORT(.ctors))) + LONG(0) /* end of list */ + __CTOR_END__ = .; + __DTOR_LIST__ = .; + LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) + KEEP(*(SORT(.dtors))) + LONG(0) /* end of list */ + __DTOR_END__ = .; + } > LRAM + PROVIDE(_exceptions = LOADADDR(.text.exceptions)); + + /* code */ + . = ALIGN(4); + .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) : + AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) { + /* regular code */ + *(.text*) + /* gcc voodoo */ + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + } > IRAM + PROVIDE(_text_start = LOADADDR(.text)); + PROVIDE(_text_end = LOADADDR(.text) + SIZEOF(.text)); + + /* read-only data */ + . = ALIGN(4); + .rodata : { + *(.rodata*) + } > IRAM + PROVIDE(_rodata_start = LOADADDR(.rodata)); + PROVIDE(_rodata_end = LOADADDR(.rodata) + SIZEOF(.rodata)); + + /* initialized data */ + . = ALIGN(4); + .data : { + *(.data) + } > IRAM + PROVIDE(_data_start = LOADADDR(.data)); + PROVIDE(_data_end = LOADADDR(.data) + SIZEOF(.data)); + + /* pic offset tables */ + . = ALIGN(4); + .got : { + *(.got) + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + } > IRAM + PROVIDE(_got_start = LOADADDR(.got)); + PROVIDE(_got_end = LOADADDR(.got) + SIZEOF(.got)); + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + } > IRAM + . = ALIGN(4); + __bss_end = .; + PROVIDE(_bss_start = __bss_start); + PROVIDE(_bss_end = __bss_end); + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/common/compal_ramload.lds b/src/target/firmware/board/common/compal_ramload.lds new file mode 100644 index 00000000..00f0d001 --- /dev/null +++ b/src/target/firmware/board/common/compal_ramload.lds @@ -0,0 +1,103 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +MEMORY +{ + /* compal-loaded binary: our text, initialized data */ + LRAM (rw) : ORIGIN = 0x00800000, LENGTH = 0x00010000 + /* compal-loaded binary: our unitialized data, stacks, heap */ + IRAM (rw) : ORIGIN = 0x00810000, LENGTH = 0x00010000 +} +SECTIONS +{ + . = 0x800000; + + /* romloader data section, contains passthru interrupt vectors */ + .compal.loader (NOLOAD) : { . = 0x100; } > LRAM + + /* image signature (prepended by osmocon according to phone type) */ + .compal.header (NOLOAD) : { . = 4; } > LRAM + + /* initialization code */ + . = ALIGN(4); + .text.start : { + PROVIDE(_start = .); + KEEP(*(.text.start)) + *(.text.start) + } > LRAM + + /* exception vectors from 0x80001c to 0x800034 */ + .text.exceptions 0x80001c : AT (LOADADDR(.text.start) + SIZEOF(.text.start)) { + KEEP(*(.text.exceptions)) + * (.text.exceptions) + . = ALIGN(4); + } > LRAM + PROVIDE(_exceptions = LOADADDR(.text.exceptions)); + + /* code */ + . = ALIGN(4); + .text (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) : + AT (LOADADDR(.text.exceptions) + SIZEOF(.text.exceptions)) { + /* regular code */ + *(.text*) + /* gcc voodoo */ + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + . = ALIGN(4); + + /* constructors and destructors */ + . = ALIGN(4); + __CTOR_LIST__ = .; + LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) + KEEP(*(SORT(.ctors))) + LONG(0) /* end of list */ + __CTOR_END__ = .; + __DTOR_LIST__ = .; + LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) + KEEP(*(SORT(.dtors))) + LONG(0) /* end of list */ + __DTOR_END__ = .; + } > LRAM + PROVIDE(_text_start = LOADADDR(.text)); + PROVIDE(_text_end = LOADADDR(.text) + SIZEOF(.text)); + + /* read-only data */ + . = ALIGN(4); + .rodata : { + *(.rodata*) + } > LRAM + PROVIDE(_rodata_start = LOADADDR(.rodata)); + PROVIDE(_rodata_end = LOADADDR(.rodata) + SIZEOF(.rodata)); + + /* initialized data */ + . = ALIGN(4); + .data : { + *(.data) + } > LRAM + PROVIDE(_data_start = LOADADDR(.data)); + PROVIDE(_data_end = LOADADDR(.data) + SIZEOF(.data)); + + /* pic offset tables */ + . = ALIGN(4); + .got : { + *(.got) + *(.got.plt) *(.igot.plt) *(.got) *(.igot) + } > LRAM + PROVIDE(_got_start = LOADADDR(.got)); + PROVIDE(_got_end = LOADADDR(.got) + SIZEOF(.got)); + + /* uninitialized data */ + .bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + } > IRAM + . = ALIGN(4); + __bss_end = .; + PROVIDE(_bss_start = __bss_start); + PROVIDE(_bss_end = __bss_end); + + /* end of image */ + . = ALIGN(4); + _end = .; + PROVIDE(end = .); +} diff --git a/src/target/firmware/board/common/compal_ramload_start.S b/src/target/firmware/board/common/compal_ramload_start.S new file mode 100644 index 00000000..c89c881c --- /dev/null +++ b/src/target/firmware/board/common/compal_ramload_start.S @@ -0,0 +1,163 @@ + + .EQU ARM_MODE_FIQ, 0x11 + .EQU ARM_MODE_IRQ, 0x12 + .EQU ARM_MODE_SVC, 0x13 + + .EQU I_BIT, 0x80 + .EQU F_BIT, 0x40 + +#define TOP_OF_RAM 0x083fff0 +#define FIQ_STACK_SIZE 1024 +#define IRQ_STACK_SIZE 1024 + +.section .text.start + +.globl _start +_start: + /* clear bss section */ + .global __bss_start + .global __bss_end + mov r0, #0 + ldr r1, =__bss_start + ldr r2, =__bss_end +2: cmp r1, r2 + strlo r0, [r1], #4 + blo 2b + + /* initialize stacks, starting at TOP_OF_RAM */ + ldr r0, =TOP_OF_RAM + + /* initialize FIQ stack */ + msr CPSR_c, #ARM_MODE_FIQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #FIQ_STACK_SIZE + + /* initialize IRQ stack */ + msr CPSR_c, #ARM_MODE_IRQ | I_BIT | F_BIT + mov r13, r0 + sub r0, r0, #IRQ_STACK_SIZE + + /* initialize supervisor stack */ + msr CPSR_c, #ARM_MODE_SVC | I_BIT | F_BIT + mov r13, r0 + + /* set backlight to moderate level */ + bl pwl_init + mov r0, #50 + bl pwl_set_level + + /* test uart output */ + @ldr r0, =string + @bl puts_asm + + /* dump some memory */ + @ldr r0, =0xfffef000 + @bl memdump + @ldr r0, =0xfffffe00 + @bl memdump + + /* call constructor functions */ + ldr r0, _ctor_list + ldr r1, _ctor_end + bl do_global_ctors + + /* jump to main */ + ldr pc, _jump_main + + /* endless loop at end of program */ +_end: b _end + b _start + +_jump_main: .word main +_ctor_list: .word __CTOR_LIST__ +_ctor_end: .word __CTOR_END__ + +/* handler for all kinds of aborts */ +handle_abort: + @ print the PC we would jump back to... + sub lr, lr, #4 @ we assume to be ARM32 + + mov r0, lr + mov r1, #8 + bl phex + + @ print abort message + mov r0, #'A' + bl putchar_asm + mov r0, #'B' + bl putchar_asm + mov r0, #'O' + bl putchar_asm + mov r0, #'R' + bl putchar_asm + mov r0, #'T' + bl putchar_asm + + @ disable IRQ and FIQ + msr CPSR_c, #I_BIT | F_BIT + +0: @ dead + b 0b + +/* entry point for IRQs */ +irq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl irq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +/* entry point for FIQs */ +fiq_entry: + /* Adjust and save LR_irq in IRQ stack */ + sub lr, lr, #4 + stmfd sp!, {lr} + + /* Save SPSR for nested interrupt */ + mrs r14, SPSR + stmfd sp!, {r14} + + /* Call the interrupt handler C function */ + stmfd sp!, {r0-r4, r12} + bl fiq + ldmfd sp!, {r0-r4, r12} + + /* Restore SPSR_irq from IRQ stack */ + ldmia sp!, {r14} + msr SPSR_cxsf, r14 + + /* Restore adjusted LR_irq from IRQ stack directly in the PC */ + ldmia sp!, {pc}^ + +/* Exception Vectors like they are needed for the exception vector + indirection of the internal boot ROM. The following section must be liked + to appear at 0x80'001c */ +.section .text.exceptions +_undef_instr: + b handle_abort +_sw_interr: + b _sw_interr +_prefetch_abort: + b handle_abort +_data_abort: + b handle_abort +_reserved: + b _reserved +_irq: + b irq_entry +_fiq: + b fiq_entry diff --git a/src/target/firmware/board/common/rffe_compal_dualband.c b/src/target/firmware/board/common/rffe_compal_dualband.c new file mode 100644 index 00000000..bfd3d98e --- /dev/null +++ b/src/target/firmware/board/common/rffe_compal_dualband.c @@ -0,0 +1,80 @@ +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <calypso/tsp.h> +#include <rf/trf6151.h> + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* describe how the RF frontend is wired on the Motorola E88 board (C117/C118/C121/C123) */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(1) /* Enable the Power Amplifier */ +#define TRENA TSPACT(6) /* Transmit Enable (Antenna Switch) */ +#define GSM_TXEN TSPACT(8) /* GSM (as opposed to DCS) Transmit */ + +#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact |= TRENA | GSM_TXEN; /* low-active */ + +#ifdef CONFIG_TX_ENABLE + /* Then we selectively set the bits on, if required */ + if (tx) { + tspact &= ~TRENA; + if (band == GSM_BAND_900) + tspact &= ~GSM_TXEN; + tspact |= PA_ENABLE; /* Dieter: TODO */ + } +#endif /* TRANSMIT_SUPPORT */ + + tsp_act_update(tspact); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~ (1 << 5); /* TSPACT6 I/O function, not nCS6 */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 5); /* TSPACT8 I/O function, not nMREQ */ + writew(reg, MCU_SW_TRACE); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN; + +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_set_gain(int16_t exp_inp, int16_t target_bb) +{ + trf6151_compute_gain(exp_inp, target_bb); +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + /* FIXME */ +} diff --git a/src/target/firmware/board/common/rffe_gta0x_triband.c b/src/target/firmware/board/common/rffe_gta0x_triband.c new file mode 100644 index 00000000..a21cc612 --- /dev/null +++ b/src/target/firmware/board/common/rffe_gta0x_triband.c @@ -0,0 +1,96 @@ +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <rffe.h> +#include <calypso/tsp.h> +#include <rf/trf6151.h> + +/* This is a value that has been measured on the C123 by Harald: 71dBm, + it is the difference between the input level at the antenna and what + the DSP reports, subtracted by the total gain of the TRF6151 */ +#define SYSTEM_INHERENT_GAIN 71 + +/* describe how the RF frontend is wired on the Openmoko GTA0x boards */ + +#define RITA_RESET TSPACT(0) /* Reset of the Rita TRF6151 */ +#define PA_ENABLE TSPACT(9) /* Enable the Power Amplifier */ +#define GSM_TXEN TSPACT(3) /* PA GSM switch, low-active */ + +/* All VCn controls are low-active */ +#define ASM_VC1 TSPACT(2) /* Antenna switch VC1 */ +#define ASM_VC2 TSPACT(1) /* Antenna switch VC2 */ +#define ASM_VC3 TSPACT(4) /* Antenna switch VC3 */ + +#define IOTA_STROBE TSPEN0 /* Strobe for the Iota TSP */ +#define RITA_STROBE TSPEN2 /* Strobe for the Rita TSP */ + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx) +{ + uint16_t tspact = tsp_act_state(); + + /* First we mask off all bits from the state cache */ + tspact &= ~PA_ENABLE; + tspact |= GSM_TXEN; /* low-active */ + tspact |= ASM_VC1 | ASM_VC2 | ASM_VC3; /* low-active */ + + switch (band) { + case GSM_BAND_850: + case GSM_BAND_900: + case GSM_BAND_1800: + break; + case GSM_BAND_1900: + tspact &= ~ASM_VC2; + break; + default: + /* TODO return/signal error here */ + break; + } + +#ifdef CONFIG_TX_ENABLE + /* Then we selectively set the bits on, if required */ + if (tx) { + // TODO: Implement tx + } +#endif /* TRANSMIT_SUPPORT */ + + tsp_act_update(tspact); +} + +#define MCU_SW_TRACE 0xfffef00e +#define ARM_CONF_REG 0xfffef006 + +void rffe_init(void) +{ + uint16_t reg; + + reg = readw(ARM_CONF_REG); + reg &= ~ (1 << 7); /* TSPACT4 I/O function, not nRDYMEM */ + writew(reg, ARM_CONF_REG); + + reg = readw(MCU_SW_TRACE); + reg &= ~(1 << 1); /* TSPACT9 I/O function, not MAS(1) */ + writew(reg, MCU_SW_TRACE); +} + +uint8_t rffe_get_gain(void) +{ + return trf6151_get_gain(); +} + +const uint8_t system_inherent_gain = SYSTEM_INHERENT_GAIN; + +#define to_dbm8(x) ((x)*8) +/* Given the expected input level of exp_inp dBm/8 and the target of target_bb + * dBm8, configure the RF Frontend with the respective gain */ +void rffe_set_gain(int16_t exp_inp, int16_t target_bb) +{ + trf6151_compute_gain(exp_inp, target_bb); +} + +void rffe_rx_win_ctrl(int16_t exp_inp, int16_t target_bb) +{ + /* FIXME */ +} diff --git a/src/target/firmware/board/compal_e88/init.c b/src/target/firmware/board/compal_e88/init.c new file mode 100644 index 00000000..94586754 --- /dev/null +++ b/src/target/firmware/board/compal_e88/init.c @@ -0,0 +1,132 @@ +/* Initialization for the Compal E88 (Motorola C115...C123) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <cfi_flash.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <calypso/uart.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <display.h> + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~(1 << 10); + /* Set function pins to I2C Mode */ + reg |= 0x1080; /* SCL / SDA */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~(1 << 3); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + writew(reg, ARMIO_LATCH_OUT); +} + +static void __ctor_board board_init(void) +{ + /* FIXME: this needs to go to board_e99/init.c once we have it */ + wdog_enable(0); + + static cfi_flash_t flash; + // XXX: move after mapping initialization and use final address + flash_init(&flash, 0x00000000); + + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR, 1); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR, 1); + uart_baudrate(CONS_UART_NR, UART_115200); + + hwtimer_init(); + + dma_init(); + rtc_init(); + + timer_init(); + + /* Initialize LCD driver (uses I2C) */ + display = &st7558_display; + display_init(); + + /* Initialize keypad driver */ + keypad_init(1); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/board/compal_e99/init.c b/src/target/firmware/board/compal_e99/init.c new file mode 100644 index 00000000..b2cdd7b1 --- /dev/null +++ b/src/target/firmware/board/compal_e99/init.c @@ -0,0 +1,130 @@ +/* Initialization for the Compal E99 (Motorola C155) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Steve Markgraf <steve@steve-m.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 <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <cfi_flash.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <calypso/uart.h> + +#include <comm/sercomm.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <display.h> + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~( (1 << 12) | (1 << 10) | (1 << 7) | (1 << 1)) ; + /* don't set function pins to I2C Mode, C155 uses UWire */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode and enable C155 backlight (IO1) */ + /* FIXME: Put the display backlight control to backlight.c */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~( (1 << 3) | (1 << 1)); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + reg |= (1 << 1); + writew(reg, ARMIO_LATCH_OUT); +} + +static void __ctor_board board_init(void) +{ + /* FIXME: this needs to go to board_e99/init.c once we have it */ + wdog_enable(0); + + static cfi_flash_t flash; + // XXX: move after mapping initialization and use final address + flash_init(&flash, 0x00000000); + + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR, 1); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR, 1); + uart_baudrate(CONS_UART_NR, UART_115200); + + hwtimer_init(); + + dma_init(); + rtc_init(); + + /* Initialize LCD driver (uses UWire) */ + display = &ssd1783_display; + display_init(); + + keypad_init(1); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/board/gta0x/init.c b/src/target/firmware/board/gta0x/init.c new file mode 100644 index 00000000..4dca2e1f --- /dev/null +++ b/src/target/firmware/board/gta0x/init.c @@ -0,0 +1,132 @@ +/* Initialization for the Openmoko Freerunner modem */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <ctors.h> +#include <memory.h> +#include <board.h> +#include <keypad.h> +#include <console.h> +#include <cfi_flash.h> + +#include <calypso/irq.h> +#include <calypso/clock.h> +#include <calypso/dma.h> +#include <calypso/rtc.h> +#include <calypso/timer.h> +#include <calypso/uart.h> + +#include <comm/sercomm.h> +#include <comm/timer.h> + +#include <abb/twl3025.h> +#include <rf/trf6151.h> +#include <display.h> + +#define ARMIO_LATCH_OUT 0xfffe4802 +#define ARMIO_CNTL_REG 0xfffe4804 +#define ASIC_CONF_REG 0xfffef008 + +static void board_io_init(void) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + /* LCD Set I/O(3) / SA0 to I/O(3) mode */ + reg &= ~(1 << 10); + /* Set function pins to I2C Mode */ + reg |= 0x1080; /* SCL / SDA */ + /* TWL3025: Set SPI+RIF RX clock to rising edge */ + reg |= (1 << 13) | (1 << 14); + writew(reg, ASIC_CONF_REG); + + /* LCD Set I/O(3) to output mode */ + reg = readw(ARMIO_CNTL_REG); + reg &= ~(1 << 3); + writew(reg, ARMIO_CNTL_REG); + + /* LCD Set I/O(3) output low */ + reg = readw(ARMIO_LATCH_OUT); + reg &= ~(1 << 3); + writew(reg, ARMIO_LATCH_OUT); +} + +static void __ctor_board board_init(void) +{ + /* FIXME: this needs to go to board_e99/init.c once we have it */ + wdog_enable(0); + + static cfi_flash_t flash; + // XXX: move after mapping initialization and use final address + flash_init(&flash, 0x00000000); + + calypso_mem_cfg(CALYPSO_nCS0, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS1, 3, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS2, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_nCS3, 5, CALYPSO_MEM_16bit, 1); + calypso_mem_cfg(CALYPSO_CS4, 0, CALYPSO_MEM_8bit, 1); + calypso_mem_cfg(CALYPSO_nCS6, 0, CALYPSO_MEM_32bit, 1); + calypso_mem_cfg(CALYPSO_nCS7, 0, CALYPSO_MEM_32bit, 0); + + /* Set VTCXO_DIV2 = 1, configure PLL for 104 MHz and give ARM half of that */ + calypso_clock_set(2, CALYPSO_PLL13_104_MHZ, ARM_MCLK_DIV_2); + + /* Configure the RHEA bridge with some sane default values */ + calypso_rhea_cfg(0, 0, 0xff, 0, 1, 0, 0); + + board_io_init(); + + /* Enable bootrom mapping to route exception vectors to RAM */ + calypso_bootrom(1); + calypso_exceptions_install(); + + irq_init(); + + /* initialize MODEM UART to be used for sercomm*/ + uart_init(SERCOMM_UART_NR, 1); + uart_baudrate(SERCOMM_UART_NR, UART_115200); + + /* initialize IRDA UART to be used for old-school console code. + * note: IRDA uart only accessible on C115 and C117 PCB */ + uart_init(CONS_UART_NR, 1); + uart_baudrate(CONS_UART_NR, UART_115200); + + hwtimer_init(); + + dma_init(); + rtc_init(); + + timer_init(); + + /* Initialize LCD driver (uses I2C) */ + display = &st7558_display; + display_init(); + + /* Initialize keypad driver */ + keypad_init(1); + + /* Initialize ABB driver (uses SPI) */ + twl3025_init(); +} diff --git a/src/target/firmware/board/manifest.c b/src/target/firmware/board/manifest.c new file mode 100644 index 00000000..025a7224 --- /dev/null +++ b/src/target/firmware/board/manifest.c @@ -0,0 +1,7 @@ + +#include "manifest.h" + +const char *manifest_application = APPLICATION; +const char *manifest_revision = GIT_REVISION; +const char *manifest_board = BOARD; +const char *manifest_environment = ENVIRONMENT; diff --git a/src/target/firmware/calypso/Makefile b/src/target/firmware/calypso/Makefile new file mode 100644 index 00000000..ef8a6a7a --- /dev/null +++ b/src/target/firmware/calypso/Makefile @@ -0,0 +1,4 @@ + +LIBRARIES+=calypso +calypso_DIR=calypso +calypso_SRCS=arm.c clock.c dma.c dsp.c du.c i2c.c irq.c rtc.c spi.c tpu.c tsp.c keypad.c misc.c timer.c backlight.c uart.c uwire.c diff --git a/src/target/firmware/calypso/arm.c b/src/target/firmware/calypso/arm.c new file mode 100644 index 00000000..8794ee35 --- /dev/null +++ b/src/target/firmware/calypso/arm.c @@ -0,0 +1,26 @@ + +/* enable IRQ+FIQ interrupts */ +void arm_enable_interrupts (void) +{ + unsigned long temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "bic %0, %0, #0xc0\n" + "msr cpsr_c, %0" + : "=r" (temp) + : + : "memory"); +} + +/* disable IRQ/FIQ interrupts + * returns true if interrupts had been enabled before we disabled them */ +int arm_disable_interrupts(void) +{ + unsigned long old,temp; + __asm__ __volatile__("mrs %0, cpsr\n" + "orr %1, %0, #0xc0\n" + "msr cpsr_c, %1" + : "=r" (old), "=r" (temp) + : + : "memory"); + return (old & 0x80) == 0; +} diff --git a/src/target/firmware/calypso/backlight.c b/src/target/firmware/calypso/backlight.c new file mode 100644 index 00000000..e2ff29cc --- /dev/null +++ b/src/target/firmware/calypso/backlight.c @@ -0,0 +1,67 @@ +/* Calypso DBB internal PWL (Pulse Width / Light) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <memory.h> + +#define BASE_ADDR_PWL 0xfffe8000 +#define PWL_REG(m) (BASE_ADDR_PWL + (m)) + +#define ASIC_CONF_REG 0xfffef008 +#define LIGHT_LEVEL_REG 0xfffe4810 + +enum pwl_reg { + PWL_LEVEL = 0, + PWL_CTRL = 2, +}; + +#define ASCONF_PWL_ENA (1 << 4) + +void bl_mode_pwl(int on) +{ + uint16_t reg; + + reg = readw(ASIC_CONF_REG); + + if (on) { + writeb(0x01, PWL_REG(PWL_CTRL)); + /* Switch pin from LT to PWL */ + reg |= ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + } else { + /* Switch pin from PWL to LT */ + reg |= ~ASCONF_PWL_ENA; + writew(reg, ASIC_CONF_REG); + writeb(0x00, PWL_REG(PWL_CTRL)); + } +} + +void bl_level(uint8_t level) +{ + if (readw(ASIC_CONF_REG) & ASCONF_PWL_ENA) { + writeb(level, PWL_REG(PWL_LEVEL)); + } else { + /* we need to scale the light level, as the + * ARMIO light controller only knows 0..63 */ + writeb(level>>2, LIGHT_LEVEL_REG); + } +} diff --git a/src/target/firmware/calypso/clock.c b/src/target/firmware/calypso/clock.c new file mode 100644 index 00000000..246b6e00 --- /dev/null +++ b/src/target/firmware/calypso/clock.c @@ -0,0 +1,200 @@ +/* Driver for Calypso clock management */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <calypso/clock.h> + +#define REG_DPLL 0xffff9800 +#define DPLL_LOCK (1 << 0) +#define DPLL_BREAKLN (1 << 1) +#define DPLL_BYPASS_DIV_SHIFT 2 /* 2 bits */ +#define DPLL_PLL_ENABLE (1 << 4) +#define DPLL_PLL_DIV_SHIFT 5 /* 2 bits */ +#define DPLL_PLL_MULT_SHIFT 7 /* 5 bits */ +#define DPLL_TEST (1 << 12) +#define DPLL_IOB (1 << 13) /* Initialize on break */ +#define DPLL_IAI (1 << 14) /* Initialize after Idle */ + +#define BASE_ADDR_CLKM 0xfffffd00 +#define CLKM_REG(m) (BASE_ADDR_CLKM+(m)) + +enum clkm_reg { + CNTL_ARM_CLK = 0, + CNTL_CLK = 2, + CNTL_RST = 4, + CNTL_ARM_DIV = 8, +}; + +/* CNTL_ARM_CLK */ +#define ARM_CLK_BIG_SLEEP (1 << 0) /* MCU Master Clock enabled? */ +#define ARM_CLK_CLKIN_SEL0 (1 << 1) /* MCU source clock (0 = DPLL output, 1 = VTCXO or CLKIN */ +#define ARM_CLK_CLKIN_SEL (1 << 2) /* 0 = VTCXO or 1 = CLKIN */ +#define ARM_CLK_MCLK_DIV5 (1 << 3) /* enable 1.5 or 2.5 division factor */ +#define ARM_CLK_MCLK_DIV_SHIFT 4 /* 3 bits */ +#define ARM_CLK_DEEP_POWER_SHIFT 8 +#define ARM_CLK_DEEP_SLEEP 12 + +/* CNTL_CLK */ +#define CLK_IRQ_CLK_DIS (1 << 0) /* IRQ clock control (0 always, 1 according ARM_MCLK_EN) */ +#define CLK_BRIDGE_CLK_DIS (1 << 1) +#define CLK_TIMER_CLK_DIS (1 << 2) +#define CLK_DPLL_DIS (1 << 3) /* 0: DPLL is not stopped during SLEEP */ +#define CLK_CLKOUT_EN (1 << 4) /* Enable CLKOUT output pins */ +#define CLK_EN_IDLE3_FLG (1 << 5) /* DSP idle flag control (1 = + * SAM/HOM register forced to HOM when DSP IDLE3) */ +#define CLK_VCLKOUT_DIV2 (1 << 6) /* 1: VCLKOUT-FR is divided by 2 */ +#define CLK_VTCXO_DIV2 (1 << 7) /* 1: VTCXO is dividied by 2 */ + +#define BASE_ADDR_MEMIF 0xfffffb00 +#define MEMIF_REG(x) (BASE_ADDR_MEMIF+(x)) + +enum memif_reg { + API_RHEA_CTL = 0x0e, + EXTRA_CONF = 0x10, +}; + +static void dump_reg16(uint32_t addr, char *name) +{ + printf("%s=0x%04x\n", name, readw(addr)); +} + +void calypso_clk_dump(void) +{ + dump_reg16(REG_DPLL, "REG_DPLL"); + dump_reg16(CLKM_REG(CNTL_ARM_CLK), "CNTL_ARM_CLK"); + dump_reg16(CLKM_REG(CNTL_CLK), "CNTL_CLK"); + dump_reg16(CLKM_REG(CNTL_RST), "CNTL_RST"); + dump_reg16(CLKM_REG(CNTL_ARM_DIV), "CNTL_ARM_DIV"); +} + +void calypso_pll_set(uint16_t inp) +{ + uint8_t mult = inp >> 8; + uint8_t div = inp & 0xff; + uint16_t reg = readw(REG_DPLL); + + reg &= ~0x0fe0; + reg |= (div & 0x3) << DPLL_PLL_DIV_SHIFT; + reg |= (mult & 0x1f) << DPLL_PLL_MULT_SHIFT; + reg |= DPLL_PLL_ENABLE; + + writew(reg, REG_DPLL); +} + +void calypso_reset_set(enum calypso_rst calypso_rst, int active) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (active) + reg |= calypso_rst; + else + reg &= ~calypso_rst; + + writeb(reg, CLKM_REG(CNTL_RST)); +} + +int calypso_reset_get(enum calypso_rst calypso_rst) +{ + uint8_t reg = readb(CLKM_REG(CNTL_RST)); + + if (reg & calypso_rst) + return 1; + else + return 0; +} + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div) +{ + uint16_t cntl_clock = readw(CLKM_REG(CNTL_CLK)); + uint16_t cntl_arm_clk = readw(CLKM_REG(CNTL_ARM_CLK)); + + /* First set the vtcxo_div2 */ + cntl_clock &= ~CLK_VCLKOUT_DIV2; + if (vtcxo_div2) + cntl_clock |= CLK_VTCXO_DIV2; + else + cntl_clock &= ~CLK_VTCXO_DIV2; + writew(cntl_clock, CLKM_REG(CNTL_CLK)); + + /* Then configure the MCLK divider */ + cntl_arm_clk &= ~ARM_CLK_CLKIN_SEL0; + if (mclk_div & 0x80) { + mclk_div &= ~0x80; + cntl_arm_clk |= ARM_CLK_MCLK_DIV5; + } else + cntl_arm_clk &= ~ARM_CLK_MCLK_DIV5; + cntl_arm_clk &= ~(0x7 << ARM_CLK_MCLK_DIV_SHIFT); + cntl_arm_clk |= (mclk_div << ARM_CLK_MCLK_DIV_SHIFT); + writew(cntl_arm_clk, CLKM_REG(CNTL_ARM_CLK)); + + /* Then finally set the PLL */ + calypso_pll_set(inp); +} + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we) +{ + writew((ws & 0x1f) | ((width & 3) << 5) | ((we & 1) << 7), + BASE_ADDR_MEMIF + bank); +} + +void calypso_bootrom(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + conf |= (3 << 8); + + if (enable) + conf &= ~(1 << 9); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +void calypso_debugunit(int enable) +{ + uint16_t conf = readw(MEMIF_REG(EXTRA_CONF)); + + if (enable) + conf &= ~(1 << 11); + else + conf |= (1 << 11); + + writew(conf, MEMIF_REG(EXTRA_CONF)); +} + +#define REG_RHEA_CNTL 0xfffff900 +#define REG_API_CNTL 0xfffff902 +#define REG_ARM_RHEA 0xfffff904 + +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1) +{ + writew(fac0 | (fac1 << 4) | (timeout << 8), REG_RHEA_CNTL); + writew(ws_h | (ws_l << 5), REG_API_CNTL); + writew(w_en0 | (w_en1 << 1), REG_ARM_RHEA); +} diff --git a/src/target/firmware/calypso/dma.c b/src/target/firmware/calypso/dma.c new file mode 100644 index 00000000..35c5be82 --- /dev/null +++ b/src/target/firmware/calypso/dma.c @@ -0,0 +1,44 @@ +/* Driver for Calypso DMA controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <memory.h> + +#define BASE_ADDR_DMA 0xfffffc00 + +enum dma_reg { + CONTROLLER_CONF = 0x00, + ALLOC_CONFIG = 0x02, +}; +#define DMA_REG(m) (BASE_ADDR_DMA + (m)) + +#define DMA_RAD(x) DMA_REG((x)*0x10 + 0x0) +#define DMA_RDPATH(x) DMA_REG((x)*0x10 + 0x2) +#define DMA_AAD(x) DMA_REG((x)*0x10 + 0x4) +#define DMA_ALGTH(x) DMA_REG((x)*0x10 + 0x6) +#define DMA_CTRL(x) DMA_REG((x)*0x10 + 0x8) +#define DMA_CUR_OFF_API(x) DMA_REG((x)*0x10 + 0xa) + +void dma_init(void) +{ + /* DMA 1 (RIF Tx), 2 (RIF Rx) allocated to DSP, all others to ARM */ + writew(0x000c, DMA_REG(ALLOC_CONFIG)); +} diff --git a/src/target/firmware/calypso/dsp.c b/src/target/firmware/calypso/dsp.c new file mode 100644 index 00000000..9cae1f6d --- /dev/null +++ b/src/target/firmware/calypso/dsp.c @@ -0,0 +1,465 @@ +#define DEBUG +/* Driver for the Calypso integrated DSP */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <calypso/clock.h> +#include <calypso/dsp.h> +#include <calypso/dsp_api.h> +#include <calypso/tpu.h> + +#include <abb/twl3025.h> + +#define REG_API_CONTROL 0xfffe0000 +#define APIC_R_SMODE_HOM (1 << 1) /* API is configured in HOM mode */ +#define APIC_R_HINT (1 << 3) /* Host processor interrupt (DSP->MCU) */ +#define APIC_W_DSPINT (1 << 2) /* ARM issues interrupt to DSP */ + +#define REG_API_WS 0xfffff902 /* Number of wait states for ARM access to API memory */ +#define REG_ARM_RHEA_CTL 0xfffff904 /* Write buffer bypassing */ +#define REG_EXT_RHEA_CTL 0xfffff906 /* Some timeout */ + +#define API_SIZE 0x2000U /* in words */ + +#define BASE_API_RAM 0xffd00000 /* Base address of API RAM form ARM point of view */ + +#define DSP_BASE_API 0x0800 /* Base address of API RAM for DSP */ +#define DSP_BASE_API_MIRROR 0xe000 /* Base address of API RAM for DSP (API boot mirrot */ +#define DSP_START 0x7000 /* DSP Start address */ + +/* Boot loader */ +#define BL_CMD_STATUS (BASE_API_RAM + 0x0ffe) /* Status / Command var */ +#define BL_ADDR_LO (BASE_API_RAM + 0x0ffc) /* Address (16 lsbs) */ +#define BL_ADDR_HI (BASE_API_RAM + 0x0ff8) /* Address (ext page bits) */ +#define BL_SIZE (BASE_API_RAM + 0x0ffa) /* Size */ + +#define BL_MAX_BLOCK_SIZE 0x7F0 /* Maximum size of copied block */ + + /* Possible values for the download status */ +#define BL_STATUS_NA 0 +#define BL_STATUS_IDLE 1 +#define BL_CMD_COPY_BLOCK 2 +#define BL_CMD_COPY_MODE 4 + +#define BL_MODE_PROG_WRITE 0 +#define BL_MODE_DATA_WRITE 1 +#define BL_MODE_PROG_READ 2 +#define BL_MODE_DATA_READ 3 +#define BL_MODE_PROM_READ 4 +#define BL_MODE_DROM_READ 5 + + +struct dsp_section { + uint32_t addr; /* addr for DSP */ + uint32_t size; /* size in words */ + const uint16_t *data; +}; + +#include "dsp_params.c" +#include "dsp_bootcode.c" +#include "dsp_dumpcode.c" + +struct dsp_api dsp_api = { + .ndb = (T_NDB_MCU_DSP *) BASE_API_NDB, + .db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0, + .db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0, + .param = (T_PARAM_MCU_DSP *) BASE_API_PARAM, + .r_page = 0, + .w_page = 0, +}; + + +void dsp_dump_version(void) +{ + printf("DSP Download Status: 0x%04x\n", readw(BL_CMD_STATUS)); + printf("DSP API Version: 0x%04x 0x%04x\n", + dsp_api.ndb->d_version_number1, dsp_api.ndb->d_version_number2); +} + +static void dsp_bl_wait_ready(void) +{ + while (readw(BL_CMD_STATUS) != BL_STATUS_IDLE); +} + +static int dsp_upload_sections_api(const struct dsp_section *sec, uint16_t dsp_base_api) +{ + for (; sec->data; sec++) { + unsigned int i; + volatile uint16_t *dptr; + + if (sec->addr & ~((1<<16)-1)) /* 64k max addr */ + return -1; + if (sec->addr < dsp_base_api) + return -1; + if ((sec->addr + sec->size) > (dsp_base_api + API_SIZE)) + return -1; + + dptr = (volatile uint16_t *)(BASE_API_RAM + ((sec->addr - dsp_base_api) * sizeof(uint16_t))); + for (i=0; i<sec->size; i++) + *dptr++ = sec->data[i]; + } + + /* FIXME need eioio or wb ? */ + + return 0; +} + +static void dsp_pre_boot(const struct dsp_section *bootcode) +{ + dputs("Assert DSP into Reset\n"); + calypso_reset_set(RESET_DSP, 1); + + if (bootcode) { + dputs("Loading initial DSP bootcode (API boot mode)\n"); + dsp_upload_sections_api(dsp_bootcode, DSP_BASE_API_MIRROR); + + writew(BL_STATUS_NA, BL_CMD_STATUS); + } else + delay_ms(10); + + dputs("Releasing DSP from Reset\n"); + calypso_reset_set(RESET_DSP, 0); + + /* Wait 10 us */ + delay_ms(100); + + dsp_bl_wait_ready(); +} + +static void dsp_set_params(int16_t *param_tab, int param_size) +{ + int i; + int16_t *param_ptr = (int16_t *) BASE_API_PARAM; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* FIXME: Implement Patch download, if any */ + + dputs("Setting some dsp_api.ndb values\n"); + dsp_api.ndb->d_background_enable = 0; + dsp_api.ndb->d_background_abort = 0; + dsp_api.ndb->d_background_state = 0; + dsp_api.ndb->d_debug_ptr = 0x0074; + dsp_api.ndb->d_debug_bk = 0x0001; + dsp_api.ndb->d_pll_config = 0x154; //C_PLL_CONFIG; + dsp_api.ndb->p_debug_buffer = 0x17ff; //C_DEBUG_BUFFER_ADD; + dsp_api.ndb->d_debug_buffer_size = 7; //C_DEBUG_BUFFER_SIZE; + dsp_api.ndb->d_debug_trace_type = 0; //C_DEBUG_TRACE_TYPE; + dsp_api.ndb->d_dsp_state = 3; //C_DSP_IDLE3; + dsp_api.ndb->d_audio_gain_ul = 0; + dsp_api.ndb->d_audio_gain_dl = 0; + dsp_api.ndb->d_es_level_api = 0x5213; + dsp_api.ndb->d_mu_api = 0x5000; + + dputs("Setting API NDB parameters\n"); + for (i = 0; i < param_size; i ++) + *param_ptr++ = param_tab[i]; + + dsp_dump_version(); + + dputs("Finishing download phase\n"); + writew(0, BL_SIZE); + writew(DSP_START, BL_ADDR_LO); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + dsp_dump_version(); +} + +void dsp_api_memset(uint16_t *ptr, int octets) +{ + uint16_t i; + for (i = 0; i < octets / sizeof(uint16_t); i++) + *ptr++ = 0; +} + +static void dsp_ndb_init(void) +{ + T_NDB_MCU_DSP *ndb = dsp_api.ndb; + uint8_t i; + + #define APCDEL_DOWN (2+0) // minimum value: 2 + #define APCDEL_UP (6+3+1) // minimum value: 6 + + /* load APC ramp: set to "no ramp" so that there will be no output if + * not properly initialised at some other place. */ + for (i = 0; i < 16; i++) + dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, ABB_RAMP_VAL(0, 0)); + + /* Iota registers values will be programmed at 1st DSP communication interrupt */ + + /* Enable f_tx delay of 400000 cyc DEBUG */ + ndb->d_debug1 = ABB_VAL_T(0, 0x000); + ndb->d_afcctladd= ABB_VAL_T(AFCCTLADD, 0x000); // Value at reset + ndb->d_vbuctrl = ABB_VAL_T(VBUCTRL, 0x0C9); // Uplink gain amp 0dB, Sidetone gain to mute + ndb->d_vbdctrl = ABB_VAL_T(VBDCTRL, 0x006); // Downlink gain amp 0dB, Volume control 0 dB + ndb->d_bbctrl = ABB_VAL_T(BBCTRL, 0x2C1); // value at reset + ndb->d_bulgcal = ABB_VAL_T(BULGCAL, 0x000); // value at reset + ndb->d_apcoff = ABB_VAL_T(APCOFF, 0x040); // value at reset + ndb->d_bulioff = ABB_VAL_T(BULIOFF, 0x0FF); // value at reset + ndb->d_bulqoff = ABB_VAL_T(BULQOFF, 0x0FF); // value at reset + ndb->d_dai_onoff= ABB_VAL_T(APCOFF, 0x000); // value at reset + ndb->d_auxdac = ABB_VAL_T(AUXDAC, 0x000); // value at reset + ndb->d_vbctrl1 = ABB_VAL_T(VBCTRL1, 0x00B); // VULSWITCH=0, VDLAUX=1, VDLEAR=1. + ndb->d_vbctrl2 = ABB_VAL_T(VBCTRL2, 0x000); // MICBIASEL=0, VDLHSO=0, MICAUX=0 + + /* APCDEL will be initialized on rach only */ + ndb->d_apcdel1 = ABB_VAL_T(APCDEL1, ((APCDEL_DOWN-2) << 5) | (APCDEL_UP-6)); + ndb->d_apcdel2 = ABB_VAL_T(APCDEL2, 0x000); + + ndb->d_fb_mode = 1; /* mode 1 FCCH burst detection */ + ndb->d_fb_det = 0; /* we have not yet detected a FB */ + ndb->a_cd[0] = (1<<B_FIRE1); /* CCCH/SACCH downlink */ + ndb->a_dd_0[0] = 0; + ndb->a_dd_0[2] = 0xffff; + ndb->a_dd_1[0] = 0; + ndb->a_dd_1[2] = 0xffff; + ndb->a_du_0[0] = 0; + ndb->a_du_0[2] = 0xffff; + ndb->a_du_1[0] = 0; + ndb->a_du_1[2] = 0xffff; + ndb->a_fd[0] = (1<<B_FIRE1); + ndb->a_fd[2] = 0xffff; + ndb->d_a5mode = 0; + ndb->d_tch_mode = 0x0800; + + #define GUARD_BITS 8 // 11 or 9 for TSM30, 7 for Freerunner + ndb->d_tch_mode |= (((GUARD_BITS - 4) & 0x000F) << 7); //Bit 7..10: guard bits + + ndb->a_sch26[0] = (1<<B_SCH_CRC); + + /* Interrupt RIF transmit if FIFO <= threshold with threshold == 0 */ + /* MCM = 1, XRST = 0, CLKX_AUTO=1, TXM=1, NCLK_EN=1, NCLK13_EN=1, + * THRESHOLD = 0, DIV_CLK = 0 (13MHz) */ + ndb->d_spcx_rif = 0x179; +} + +static void dsp_db_init(void) +{ + dsp_api_memset((uint16_t *)BASE_API_W_PAGE_0, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((uint16_t *)BASE_API_W_PAGE_1, sizeof(T_DB_MCU_TO_DSP)); + dsp_api_memset((uint16_t *)BASE_API_R_PAGE_0, sizeof(T_DB_DSP_TO_MCU)); + dsp_api_memset((uint16_t *)BASE_API_R_PAGE_1, sizeof(T_DB_DSP_TO_MCU)); +} + +void dsp_power_on(void) +{ + /* proabaly a good idea to initialize the whole API area to a know value */ + dsp_api_memset((uint16_t *)BASE_API_RAM, API_SIZE * 2); // size is in words + + dsp_set_params((int16_t *)&dsp_params, sizeof(dsp_params)/2); + dsp_ndb_init(); + dsp_db_init(); + dsp_api.frame_ctr = 0; + dsp_api.r_page = dsp_api.w_page = dsp_api.r_page_used = 0; +} + +/* test for frequency burst detection */ +#define REG_INT_STAT 0xffff1004 +static void wait_for_frame_irq(void) +{ + //puts("Waiting for Frame Interrupt"); + //while (readb(REG_INT_STAT) & 1) + while (readb((void *)0xffff1000) & (1<<4)) + ;// putchar('.'); + //puts("Done!\n"); +} + +void dsp_end_scenario(void) +{ + /* FIXME: we don't yet deal with the MISC_TASK */ + + /* End the DSP Scenario */ + dsp_api.ndb->d_dsp_page = B_GSM_TASK | dsp_api.w_page; + dsp_api.w_page ^= 1; + + /* Tell TPU to generate a FRAME interrupt to the DSP */ + tpu_dsp_frameirq_enable(); + tpu_frame_irq_en(1, 1); +} + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_d = task; + dsp_api.db_w->d_burst_d = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc) +{ + dsp_api.db_w->d_task_u = task; + dsp_api.db_w->d_burst_u = burst_id; + dsp_api.db_w->d_ctrl_system |= tsc & 0x7; +} + +/* no AMR, no ciphering yet, fn does not work this way */ +void dsp_load_tch_param(uint16_t fn, uint8_t chan_mode, uint8_t chan_type, + uint8_t subchannel, uint8_t tch_loop, uint8_t sync_tch) +{ + uint16_t d_ctrl_tch; + + /* d_ctrl_tch + ---------- + bit [0..3] -> b_chan_mode + bit [4..7] -> b_chan_type + bit [8] -> b_sync_tch_ul + bit [9] -> b_sync_tch_dl + bit [10] -> b_stop_tch_ul + bit [11] -> b_stop_tch_dl + bit [12..14] -> b_tch_loop + bit [15] -> b_subchannel */ + d_ctrl_tch = (chan_mode << B_CHAN_MODE) | + (chan_type << B_CHAN_TYPE) | + (subchannel << B_SUBCHANNEL) | + (sync_tch << B_SYNC_TCH_UL) | + (sync_tch << B_SYNC_TCH_DL) | + (tch_loop << B_TCH_LOOP); + + /* TODO (used for ciphering and TCH traffic) */ + + /* d_fn + ---- + bit [0..7] -> b_fn_report + bit [8..15] -> b_fn_sid */ + dsp_api.db_w->d_fn = fn; /* write both Fn_sid, Fn_report. */ + dsp_api.db_w->a_a5fn[0] = 0; /* cyphering FN part 1 (TODO) */ + dsp_api.db_w->a_a5fn[1] = 0; /* cyphering FN part 2 (TODO) */ + dsp_api.db_w->d_ctrl_tch = d_ctrl_tch; /* Channel config. */ +} + +#define SC_CHKSUM_VER (BASE_API_W_PAGE_0 + (2 * (0x08DB - 0x800))) +static void dsp_dump_csum(void) +{ + printf("dsp page : %u\n", dsp_api.ndb->d_dsp_page); + printf("dsp code version : 0x%04x\n", dsp_api.db_r->a_pm[0]); + printf("dsp checksum : 0x%04x\n", dsp_api.db_r->a_pm[1]); + printf("dsp patch version : 0x%04x\n", readw(SC_CHKSUM_VER)); +} + +void dsp_checksum_task(void) +{ + dsp_dump_csum(); + dsp_api.db_w->d_task_md = CHECKSUM_DSP_TASK; + dsp_api.ndb->d_fb_mode = 1; + + dsp_end_scenario(); + + wait_for_frame_irq(); + + dsp_dump_csum(); +} + +#define L1D_AUXAPC 0x0012 +#define L1D_APCRAM 0x0014 + +void dsp_load_apc_dac(uint16_t apc) +{ + dsp_api.db_w->d_power_ctl = (apc << 6) | L1D_AUXAPC; +} + + +static void _dsp_dump_range(uint32_t addr, uint32_t size, int mode) +{ + uint32_t bs; + + /* Mode selection */ + writew(mode, BASE_API_RAM); + writew(BL_CMD_COPY_MODE, BL_CMD_STATUS); + dsp_bl_wait_ready(); + + /* Block by block dump */ + while (size) { + volatile uint16_t *api = (volatile uint16_t *)BASE_API_RAM; + + bs = (size > BL_MAX_BLOCK_SIZE) ? BL_MAX_BLOCK_SIZE : size; + size -= bs; + + writew(addr >> 16, BL_ADDR_HI); + writew(addr & 0xffff, BL_ADDR_LO); + writew(bs, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + dsp_bl_wait_ready(); + + while (bs--) { + if ((addr&15)==0) + printf("%05x : ", addr); + printf("%04hx%c", *api++, ((addr&15)==15)?'\n':' '); + addr++; + } + }; + puts("\n"); +} + +void dsp_dump(void) +{ + static const struct { + const char *name; + uint32_t addr; + uint32_t size; + int mode; + } dr[] = { + { "Registers", 0x00000, 0x0060, BL_MODE_DATA_READ }, + { "DROM", 0x09000, 0x5000, BL_MODE_DROM_READ }, + { "PDROM", 0x0e000, 0x2000, BL_MODE_DROM_READ }, + { "PROM0", 0x07000, 0x7000, BL_MODE_PROM_READ }, + { "PROM1", 0x18000, 0x8000, BL_MODE_PROM_READ }, + { "PROM2", 0x28000, 0x8000, BL_MODE_PROM_READ }, + { "PROM3", 0x38000, 0x2000, BL_MODE_PROM_READ }, + { NULL, 0, 0, -1 } + }; + + int i; + + /* Start DSP up to bootloader */ + dsp_pre_boot(dsp_bootcode); + + /* Load and execute our dump code in the DSP */ + dsp_upload_sections_api(dsp_dumpcode, DSP_BASE_API); + + writew(0, BL_ADDR_HI); + writew(DSP_DUMPCODE_START, BL_ADDR_LO); + writew(0, BL_SIZE); + writew(BL_CMD_COPY_BLOCK, BL_CMD_STATUS); + + /* our dump code actually simulates the boot loaded + * but with added read commands */ + dsp_bl_wait_ready(); + + /* Test the 'version' command */ + writew(0xffff, BL_CMD_STATUS); + dsp_bl_wait_ready(); + printf("DSP bootloader version 0x%04x\n", readw(BASE_API_RAM)); + + /* Dump each range */ + for (i=0; dr[i].name; i++) { + printf("DSP dump: %s [%05x-%05x]\n", dr[i].name, + dr[i].addr, dr[i].addr+dr[i].size-1); + _dsp_dump_range(dr[i].addr, dr[i].size, dr[i].mode); + } +} + diff --git a/src/target/firmware/calypso/dsp_bootcode.c b/src/target/firmware/calypso/dsp_bootcode.c new file mode 100644 index 00000000..2db46568 --- /dev/null +++ b/src/target/firmware/calypso/dsp_bootcode.c @@ -0,0 +1,9 @@ +/* Calypso integrated DSP boot code */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +/* We don't really need any DSP boot code, it happily works with its own ROM */ +static const struct dsp_section *dsp_bootcode = NULL; + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_dumpcode.c b/src/target/firmware/calypso/dsp_dumpcode.c new file mode 100644 index 00000000..265a1c12 --- /dev/null +++ b/src/target/firmware/calypso/dsp_dumpcode.c @@ -0,0 +1,45 @@ +/* Generated from src/target_dsp/calypso/dsp_dump.bin */ + +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section dsp_dumpcode[] = { + { + .addr = 0x1000, + .size = 0x005b, + .data = _SA_DECL { + 0x69f8, 0x0029, 0x0002, 0xea1f, + 0x7718, 0x1100, 0x7714, 0x0000, + 0x7712, 0x0800, 0x767f, 0x0001, + 0x607f, 0xffff, 0xf820, 0x1014, + 0xf273, 0x1008, 0x7682, 0x0100, + 0x607f, 0x0004, 0xf820, 0x101c, + 0xf273, 0x1008, 0x7214, 0x0800, + 0x607f, 0x0002, 0xf820, 0x100c, + 0x127e, 0x8813, 0x3c7c, 0x137d, + 0x8911, 0xf84c, 0x1028, 0xf4e2, + 0x7715, 0x0014, 0x963d, 0xfa30, + 0x104b, 0x6d89, 0x963f, 0xfa30, + 0x103f, 0x963e, 0xf495, 0xf830, + 0x103a, 0x47f8, 0x0011, 0x7f92, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0x7e92, 0xf073, 0x1008, 0xf830, + 0x1046, 0x47f8, 0x0011, 0xe589, + 0xf073, 0x1008, 0x47f8, 0x0011, + 0xe598, 0xf073, 0x1008, 0x4911, + 0x891a, 0xf830, 0x1055, 0xf072, + 0x1052, 0xf074, 0x7213, 0xf073, + 0x1008, 0xf072, 0x1058, 0xf074, + 0xe4b8, 0xf073, 0x1008, + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#define DSP_DUMPCODE_START 0x1000 + +#undef _SA_DECL + diff --git a/src/target/firmware/calypso/dsp_params.c b/src/target/firmware/calypso/dsp_params.c new file mode 100644 index 00000000..ec44a0ed --- /dev/null +++ b/src/target/firmware/calypso/dsp_params.c @@ -0,0 +1,94 @@ +/* Values from an actual phone firmware that uses the 3306 DSP ROM code version */ +static T_PARAM_MCU_DSP dsp_params = { + .d_transfer_rate = 0x6666, + /* Latencies */ + .d_lat_mcu_bridge = 15, + .d_lat_mcu_hom2sam = 12, + .d_lat_mcu_bef_fast_access = 5, + .d_lat_dsp_after_sam = 4, + /* DSP Start Address */ + .d_gprs_install_address = 0x7002, /* needs to be set by patch or manually */ + .d_misc_config = 1, + .d_cn_sw_workaround = 0xE, + .d_hole2_param = { 0, 0, 0, 0 }, + /* Frequency Burst */ + .d_fb_margin_beg = 24, + .d_fb_margin_end = 22, + .d_nsubb_idle = 296, + .d_nsubb_dedic = 30, + .d_fb_thr_det_iacq = 0x3333, + .d_fb_thr_det_track = 0x28f6, + /* Demodulation */ + .d_dc_off_thres = 0x7fff, + .d_dummy_thres = 17408, + .d_dem_pond_gewl = 26624, + .d_dem_pond_red = 20152, + /* TCH Full Speech */ + .d_maccthresh1 = 7872, + .d_mldt = -4, + .d_maccthresh = 7172, + .d_gu = 5772, + .d_go = 7872, + .d_attmax = 53, + .d_sm = -892, + .d_b = 208, + /* V.42 bis */ + .d_v42b_switch_hyst = 16, + .d_v42b_switch_min = 64, + .d_v42b_switch_max = 250, + .d_v42b_reset_delay = 10, + /* TCH Half Speech */ + .d_ldT_hr = -5, + .d_maccthresh_hr = 6500, + .d_maccthresh1_hr = 6500, + .d_gu_hr = 2620, + .d_go_hr = 3700, + .d_b_hr = 182, + .d_sm_hr = -1608, + .d_attmax_hr = 53, + /* TCH Enhanced FR Speech */ + .c_mldt_efr = -4, + .c_maccthresh_efr = 8000, + .c_maccthresh1_efr = 8000, + .c_gu_efr = 4522, + .c_go_efr = 6500, + .c_b_efr = 174, + .c_sm_efr = -878, + .c_attmax_efr = 53, + /* CHED TCH Full Speech */ + .d_sd_min_thr_tchfs = 15, + .d_ma_min_thr_tchfs = 738, + .d_md_max_thr_tchfs = 1700, + .d_md1_max_thr_tchfs = 99, + /* CHED TCH Half Speech */ + .d_sd_min_thr_tchhs = 37, + .d_ma_min_thr_tchhs = 344, + .d_sd_av_thr_tchhs = 1845, + .d_md_max_thr_tchhs = 2175, + .d_md1_max_thr_tchhs = 138, + /* CHED TCH/F EFR Speech */ + .d_sd_min_thr_tchefs = 15, + .d_ma_min_thr_tchefs = 738, + .d_md_max_thr_tchefs = 0x4ce, + .d_md1_max_thr_tchefs = 0x63, + /* */ + .d_wed_fil_ini = 0x122a, + .d_wed_fil_tc = 0x7c00, + .d_x_min = 0xf, + .d_x_max = 0x17, + .d_slope = 0x87, + .d_y_min = 0x2bf, + .d_y_max = 0x99c, + .d_wed_diff_threshold = 0x196, + .d_mabfi_min_thr_tchhs = 0x14c8, + /* FACCH module */ + .d_facch_thr = 0, + /* IDS module */ + .d_max_ovsp_ul = 8, + .d_sync_thres = 0x3f50, + .d_idle_thres = 0x4000, + .d_m1_thres = 5, + .d_max_ovsp_dl = 8, + .d_gsm_bgd_mgt = 0, + /* we don't set the FIR coefficients !?! */ +}; diff --git a/src/target/firmware/calypso/du.c b/src/target/firmware/calypso/du.c new file mode 100644 index 00000000..58783b06 --- /dev/null +++ b/src/target/firmware/calypso/du.c @@ -0,0 +1,51 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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 <memory.h> +#include <stdint.h> +#include <stdio.h> + +#include <calypso/du.h> + +#define BASE_ADDR_DU 0x03c00000 +#define DU_REG(m) (BASE_ADDR_DU+(m)) + +void calypso_du_init() { + unsigned char c; + calypso_debugunit(1); + for(c = 0; c < 64; c++) { + writew(DU_REG(c), 0x00000000); + } +} + +void calypso_du_stop() { + calypso_debugunit(0); +} + +void calypso_du_dump() { + unsigned char c; + puts("Debug unit traceback:\n"); + for(c = 0; c < 64; c++) { + uint32_t w = readw(DU_REG(c)); + printf("t-%2x: 0x%8x\n", c, (unsigned int)w); + } +} diff --git a/src/target/firmware/calypso/i2c.c b/src/target/firmware/calypso/i2c.c new file mode 100644 index 00000000..a46fd72a --- /dev/null +++ b/src/target/firmware/calypso/i2c.c @@ -0,0 +1,123 @@ +/* Driver for I2C Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <i2c.h> + +#define BASE_ADDR_I2C 0xfffe2800 +#define I2C_REG(x) (BASE_ADDR_I2C+(x)) + +enum i2c_reg { + DEVICE_REG = 0, + ADDRESS_REG, + DATA_WR_REG, + DATA_RD_REG, + CMD_REG, + CONF_FIFO_REG, + CONF_CLK_REG, + CONF_CLK_FUNC_REF, + STATUS_FIFO_REG, + STATUS_ACTIVITY_REG, +}; + +#define I2C_CMD_SOFT_RESET (1 << 0) +#define I2C_CMD_EN_CLK (1 << 1) +#define I2C_CMD_START (1 << 2) +#define I2C_CMD_RW_READ (1 << 3) +#define I2C_CMD_COMP_READ (1 << 4) +#define I2C_CMD_IRQ_ENABLE (1 << 5) + +#define I2C_STATUS_ERROR_DATA (1 << 0) +#define I2C_STATUS_ERROR_DEV (1 << 1) +#define I2C_STATUS_IDLE (1 << 2) +#define I2C_STATUS_INTERRUPT (1 << 3) + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len) +{ + uint8_t cmd; + + /* Calypso I2C controller doesn't support fancy addressing */ + if (alen > 1) + return -1; + + /* FIXME: implement writes longer than fifo size */ + if (len > 16) + return -1; + + printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr) + + writeb(chip & 0x3f, I2C_REG(DEVICE_REG)); + writeb(addr & 0xff, I2C_REG(ADDRESS_REG)); + + /* we have to tell the controler how many bits we'll put into the fifo ?!? */ + writeb(len-1, I2C_REG(CONF_FIFO_REG)); + + /* fill the FIFO */ + while (len--) { + uint8_t byte = *buffer++; + writeb(byte, I2C_REG(DATA_WR_REG)); + printd("%02X ", byte); + } + dputchar('\n'); + + /* start the transfer */ + cmd = readb(I2C_REG(CMD_REG)); + cmd |= I2C_CMD_START; + writeb(cmd, I2C_REG(CMD_REG)); + + /* wait until transfer completes */ + while (1) { + uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG)); + printd("I2C Status: 0x%02x\n", rerg & 0xf); + if (reg & I2C_STATUS_IDLE) + break; + } + dputs("I2C transfer completed\n"); + + return 0; +} + +void i2c_init(int speed, int slaveadd) +{ + /* scl_out = clk_func_ref / 3, + clk_func_ref = master_clock_freq / (divisor_2 + 1) + master_clock_freq = ext_clock_freq / divisor_1 */ + /* clk_func_ref = scl_out * 3, + divisor_2 = (master_clock_freq / clk_func_ref) - 1 + divisor_1 = ext_clock_freq / master_clock_freq */ + /* for a target freq of 200kHz: + ext_clock_freq = 13MHz + clk_func_ref = 3 * 300kHZ = 600kHz + divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz + divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz + scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */ + writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG)); + + writeb(0x00, I2C_REG(CONF_CLK_REG)); + writeb(21, I2C_REG(CONF_CLK_FUNC_REF)); + + writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG)); +} diff --git a/src/target/firmware/calypso/irq.c b/src/target/firmware/calypso/irq.c new file mode 100644 index 00000000..a3d57fbe --- /dev/null +++ b/src/target/firmware/calypso/irq.c @@ -0,0 +1,266 @@ +/* Driver for Calypso IRQ controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <arm.h> +#include <calypso/irq.h> + +#define BASE_ADDR_IRQ 0xfffffa00 + +enum irq_reg { + IT_REG1 = 0x00, + IT_REG2 = 0x02, + MASK_IT_REG1 = 0x08, + MASK_IT_REG2 = 0x0a, + IRQ_NUM = 0x10, + FIQ_NUM = 0x12, + IRQ_CTRL = 0x14, +}; + +#define ILR_IRQ(x) (0x20 + (x*2)) +#define IRQ_REG(x) ((void *)BASE_ADDR_IRQ + (x)) + +#define NR_IRQS 32 + +static uint8_t default_irq_prio[] = { + [IRQ_WATCHDOG] = 0xff, + [IRQ_TIMER1] = 0xff, + [IRQ_TIMER2] = 0xff, + [IRQ_TSP_RX] = 0, + [IRQ_TPU_FRAME] = 3, + [IRQ_TPU_PAGE] = 0xff, + [IRQ_SIMCARD] = 0xff, + [IRQ_UART_MODEM] = 8, + [IRQ_KEYPAD_GPIO] = 4, + [IRQ_RTC_TIMER] = 9, + [IRQ_RTC_ALARM_I2C] = 10, + [IRQ_ULPD_GAUGING] = 2, + [IRQ_EXTERNAL] = 12, + [IRQ_SPI] = 0xff, + [IRQ_DMA] = 0xff, + [IRQ_API] = 0xff, + [IRQ_SIM_DETECT] = 0, + [IRQ_EXTERNAL_FIQ] = 7, + [IRQ_UART_IRDA] = 2, + [IRQ_ULPD_GSM_TIMER] = 1, + [IRQ_GEA] = 0xff, +}; + +static irq_handler *irq_handlers[NR_IRQS]; + +static void _irq_enable(enum irq_nr nr, int enable) +{ + uint16_t *reg = IRQ_REG(MASK_IT_REG1); + uint16_t val; + + if (nr > 15) { + reg = IRQ_REG(MASK_IT_REG2); + nr -= 16; + } + + val = readw(reg); + if (enable) + val &= ~(1 << nr); + else + val |= (1 << nr); + writew(val, reg); +} + +void irq_enable(enum irq_nr nr) +{ + _irq_enable(nr, 1); +} + +void irq_disable(enum irq_nr nr) +{ + _irq_enable(nr, 0); +} + +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio) +{ + uint16_t val; + + if (prio == -1) + prio = default_irq_prio[nr]; + + if (prio > 31) + prio = 31; + + val = prio << 2; + if (edge) + val |= 0x02; + if (fiq) + val |= 0x01; + + writew(val, IRQ_REG(ILR_IRQ(nr))); +} + +/* Entry point for interrupts */ +void irq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + +#if 1 + /* Hardware interrupt detection mode */ + num = readb(IRQ_REG(IRQ_NUM)) & 0x1f; + + printd("i%02x\n", num); + + handler = irq_handlers[num]; + + if (handler) + handler(num); +#else + /* Software interrupt detection mode */ + { + uint16_t it_reg, mask_reg; + uint32_t irqs; + + it_reg = readw(IRQ_REG(IT_REG1)); + mask_reg = readw(IRQ_REG(MASK_IT_REG1)); + irqs = it_reg & ~mask_reg; + + it_reg = readw(IRQ_REG(IT_REG2)); + mask_reg = readw(IRQ_REG(MASK_IT_REG2)); + irqs |= (it_reg & ~mask_reg) << 16; + + for (num = 0; num < 32; num++) { + if (irqs & (1 << num)) { + printd("i%d\n", num); + handler = irq_handlers[num]; + if (handler) + handler(num); + /* clear this interrupt */ + if (num < 16) + writew(~(1 << num), IRQ_REG(IT_REG1)); + else + writew(~(1 << (num-16)), IRQ_REG(IT_REG2)); + } + } + dputchar('\n'); + } +#endif + /* Start new IRQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x01; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +/* Entry point for FIQs */ +void fiq(void) +{ + uint8_t num, tmp; + irq_handler *handler; + + num = readb(IRQ_REG(FIQ_NUM)) & 0x1f; + if (num) { + printd("f%02x\n", num); + } + + handler = irq_handlers[num]; + + if (handler) + handler(num); + + /* Start new FIQ agreement */ + tmp = readb(IRQ_REG(IRQ_CTRL)); + tmp |= 0x02; + writeb(tmp, IRQ_REG(IRQ_CTRL)); +} + +void irq_register_handler(enum irq_nr nr, irq_handler *handler) +{ + if (nr > NR_IRQS) + return; + + irq_handlers[nr] = handler; +} + +#define BASE_ADDR_IBOOT_EXC 0x0080001C +extern uint32_t _exceptions; + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void) +{ + uint32_t *exceptions_dst = (uint32_t *) BASE_ADDR_IBOOT_EXC; + uint32_t *exceptions_src = &_exceptions; + int i; + + for (i = 0; i < 7; i++) + *exceptions_dst++ = *exceptions_src++; + +} + +static void set_default_priorities(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(default_irq_prio); i++) { + uint16_t val; + uint8_t prio = default_irq_prio[i]; + if (prio > 31) + prio = 31; + + val = readw(IRQ_REG(ILR_IRQ(i))); + val &= ~(0x1f << 2); + val |= prio << 2; + writew(val, IRQ_REG(ILR_IRQ(i))); + } +} + +static uint32_t irq_nest_mask; +/* mask off all interrupts that have a lower priority than irq_nr */ +static void mask_all_lower_prio_irqs(enum irq_nr irqnr) +{ + uint8_t our_prio = readb(IRQ_REG(ILR_IRQ(irqnr))) >> 2; + int i; + + for (i = 0; i < _NR_IRQ; i++) { + uint8_t prio; + + if (i == irqnr) + continue; + + prio = readb(IRQ_REG(ILR_IRQ(i))) >> 2; + if (prio >= our_prio) + irq_nest_mask |= (1 << i); + } +} + +void irq_init(void) +{ + /* set default priorities */ + set_default_priorities(); + /* mask all interrupts off */ + writew(0xffff, IRQ_REG(MASK_IT_REG1)); + writew(0xffff, IRQ_REG(MASK_IT_REG2)); + /* clear all pending interrupts */ + writew(0, IRQ_REG(IT_REG1)); + writew(0, IRQ_REG(IT_REG2)); + /* enable interrupts globally to the ARM core */ + arm_enable_interrupts(); +} diff --git a/src/target/firmware/calypso/keypad.c b/src/target/firmware/calypso/keypad.c new file mode 100644 index 00000000..8d9c9251 --- /dev/null +++ b/src/target/firmware/calypso/keypad.c @@ -0,0 +1,168 @@ +/* Driver for the keypad attached to the TI Calypso */ + +/* (C) 2010 by roh <roh@hyte.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 <stdint.h> +#include <stdio.h> + +#include <defines.h> +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <keypad.h> + +#include <calypso/irq.h> +#include <abb/twl3025.h> + + +#define KBR_LATCH_REG 0xfffe480a +#define KBC_REG 0xfffe480c +#define KBD_GPIO_INT 0xfffe4816 +#define KBD_GPIO_MASKIT 0xfffe4818 + +static key_handler_t key_handler = NULL; + +void emit_key(uint8_t key, uint8_t state) +{ + printf("key=%u %s\n", key, state == PRESSED ? "pressed" : "released"); + + if (state == RELEASED) + if (key == KEY_POWER) + twl3025_power_off(); + + if(key_handler) { + key_handler(key, state); + } +} + +volatile uint32_t lastbuttons; + +#define BTN_TO_KEY(name) \ + ((diff & BTN_##name) == BTN_##name) \ + { \ + key = KEY_##name; \ + diff = diff & ~BTN_##name; \ + } + +void dispatch_buttons(uint32_t buttons) +{ + uint8_t state; + + if (buttons == lastbuttons) + return; + + if (buttons > lastbuttons) + state = PRESSED; + else + state = RELEASED; + + uint32_t diff = buttons ^ lastbuttons; + uint8_t key=KEY_INV; + + while (diff != 0) + { + if BTN_TO_KEY(POWER) + else if BTN_TO_KEY(0) + else if BTN_TO_KEY(1) + else if BTN_TO_KEY(2) + else if BTN_TO_KEY(3) + else if BTN_TO_KEY(4) + else if BTN_TO_KEY(5) + else if BTN_TO_KEY(6) + else if BTN_TO_KEY(7) + else if BTN_TO_KEY(8) + else if BTN_TO_KEY(9) + else if BTN_TO_KEY(STAR) + else if BTN_TO_KEY(HASH) + else if BTN_TO_KEY(MENU) + else if BTN_TO_KEY(LEFT_SB) + else if BTN_TO_KEY(RIGHT_SB) + else if BTN_TO_KEY(UP) + else if BTN_TO_KEY(DOWN) + else if BTN_TO_KEY(LEFT) + else if BTN_TO_KEY(RIGHT) + else if BTN_TO_KEY(OK) + else + { + printf("\nunknown keycode: 0x%08x\n", diff); + break; + } + if ( key == KEY_POWER ) + diff = 0; + emit_key(key, state); + } + lastbuttons = buttons; +} + +static void keypad_irq(__unused enum irq_nr nr) +{ + keypad_poll(); +} + +void keypad_init(uint8_t interrupts) +{ + lastbuttons = 0; + writew(0, KBD_GPIO_MASKIT); + writew(0, KBC_REG); + + if(interrupts) { + irq_register_handler(IRQ_KEYPAD_GPIO, &keypad_irq); + irq_config(IRQ_KEYPAD_GPIO, 0, 0, 0); + irq_enable(IRQ_KEYPAD_GPIO); + } +} + +void keypad_set_handler(key_handler_t handler) +{ + key_handler = handler; +} + +void keypad_poll() +{ + uint16_t reg; + uint16_t col; + uint32_t buttons; + +// putchar('\n'); + buttons = 0x0; + //scan for BTN_POWER + writew(0xff, KBC_REG); + delay_ms(1); + reg = readw(KBR_LATCH_REG); +// printd("%02x ", (~reg & 0x1f)); + buttons = buttons | ((~reg & 0x1f) << 20 ); + + //scan for muxed keys if not powerbtn + if ((~reg & 0x1f) != 0x10) + for (col=0;col<4;col++) + { + writew(0x1f & ~(0x1 << col ), KBC_REG); + delay_ms(1); + reg = readw(KBR_LATCH_REG); + buttons = buttons | ((~reg & 0x1f) << (col * 5 )); +// printd("%02x ", (~reg & 0x1f)); + } + //enable keypad irq via master 'or' gate (needs col lines low in idle to work) + writew(0, KBC_REG); + dispatch_buttons(buttons); + +} + diff --git a/src/target/firmware/calypso/misc.c b/src/target/firmware/calypso/misc.c new file mode 100644 index 00000000..460cc5d5 --- /dev/null +++ b/src/target/firmware/calypso/misc.c @@ -0,0 +1,60 @@ + +#include <stdint.h> +#include <stdio.h> +#include <memory.h> + +/* dump a memory range */ +void memdump_range(unsigned int *ptr, unsigned int len) +{ + unsigned int *end = ptr + (len/4); + unsigned int *tmp; + + for (tmp = ptr; tmp < end; tmp += 8) { + int i; + printf("%08X: ", (unsigned int) tmp); + + for (i = 0; i < 8; i++) + printf("%08X %s", *(tmp+i), i == 3 ? " " : ""); + + putchar('\n'); + } +} + +#define KBIT 1024 +#define MBIT (1024*KBIT) +void dump_mem(void) +{ + puts("Dump 64kBits of internal ROM\n"); + memdump_range((void *)0x03800000, 64*KBIT/8); + + puts("Dump 8Mbits of external flash\n"); + memdump_range((void *)0x00000000, 8*MBIT/8); + + puts("Dump 2Mbits of internal RAM\n"); + memdump_range((void *)0x00800000, 2*MBIT/8); + + puts("Dump 2Mbits of external RAM\n"); + memdump_range((void *)0x01000000, 2*MBIT/8); +} + +#define REG_DEV_ID_CODE 0xfffef000 +#define REG_DEV_VER_CODE 0xfffef002 +#define REG_DEV_ARMVER_CODE 0xfffffe00 +#define REG_cDSP_ID_CODE 0xfffffe02 +#define REG_DIE_ID_CODE 0xfffef010 + +void dump_dev_id(void) +{ + int i; + + printf("Device ID code: 0x%04x\n", readw(REG_DEV_ID_CODE)); + printf("Device Version code: 0x%04x\n", readw(REG_DEV_VER_CODE)); + printf("ARM ID code: 0x%04x\n", readw(REG_DEV_ARMVER_CODE)); + printf("cDSP ID code: 0x%04x\n", readw(REG_cDSP_ID_CODE)); + puts("Die ID code: "); + for (i = 0; i < 64/8; i += 4) + printf("%08x", readl(REG_DIE_ID_CODE+i)); + putchar('\n'); +} + + diff --git a/src/target/firmware/calypso/rtc.c b/src/target/firmware/calypso/rtc.c new file mode 100644 index 00000000..ce750c29 --- /dev/null +++ b/src/target/firmware/calypso/rtc.c @@ -0,0 +1,83 @@ +/* Driver for Calypso RTC controller */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <display.h> +#include <calypso/irq.h> + +#define BASE_ADDR_RTC 0xfffe1800 +#define RTC_REG(x) ((void *)BASE_ADDR_RTC + (x)) + +enum rtc_reg { + SECOND_REG = 0x00, + MINUTES_REG = 0x01, + HOURS_REG = 0x02, + DAYS_REG = 0x03, + MONTHS_REG = 0x04, + YEARS_REG = 0x05, + WEEK_REG = 0x06, + /* reserved */ + ALARM_SECOND_REG = 0x08, + ALARM_MINUTES_REG = 0x09, + ALARM_HOURS_REG = 0x0a, + ALARM_DAYS_REG = 0x0b, + ALARM_MONTHS_REG = 0x0c, + ALARM_YEARS_REG = 0x0d, + /* reserved */ + /* reserved */ + CTRL_REG = 0x10, + STATUS_REG = 0x11, + INT_REG = 0x12, + COMP_LSB_REG = 0x13, + COMP_MSB_REG = 0x14, + RES_PROG_REG = 0x15, +}; + +static int tick_ctr; + +static void rtc_irq_tick(__unused enum irq_nr nr) +{ + if (tick_ctr & 1) + display_set_attr(DISP_ATTR_INVERT); + else + display_unset_attr(DISP_ATTR_INVERT); + tick_ctr++; +} + +void rtc_init(void) +{ + irq_register_handler(IRQ_RTC_TIMER, &rtc_irq_tick); + irq_config(IRQ_RTC_TIMER, 0, 1, 0); + irq_enable(IRQ_RTC_TIMER); + + /* clear power-up reset */ + writeb(0x80, RTC_REG(STATUS_REG)); + /* enable RTC running */ + writeb(0x01, RTC_REG(CTRL_REG)); + /* enable periodic interrupts every second */ + writeb(0x04, RTC_REG(INT_REG)); +} diff --git a/src/target/firmware/calypso/spi.c b/src/target/firmware/calypso/spi.c new file mode 100644 index 00000000..049ac080 --- /dev/null +++ b/src/target/firmware/calypso/spi.c @@ -0,0 +1,141 @@ +/* Driver for SPI Master Controller inside TI Calypso */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <spi.h> +#include <delay.h> + +#define BASE_ADDR_SPI 0xfffe3000 +#define SPI_REG(n) (BASE_ADDR_SPI+(n)) + +enum spi_regs { + REG_SET1 = 0x00, + REG_SET2 = 0x02, + REG_CTRL = 0x04, + REG_STATUS = 0x06, + REG_TX_LSB = 0x08, + REG_TX_MSB = 0x0a, + REG_RX_LSB = 0x0c, + REG_RX_MSB = 0x0e, +}; + +#define SPI_SET1_EN_CLK (1 << 0) +#define SPI_SET1_WR_IRQ_DIS (1 << 4) +#define SPI_SET1_RDWR_IRQ_DIS (1 << 5) + +#define SPI_CTRL_RDWR (1 << 0) +#define SPI_CTRL_WR (1 << 1) +#define SPI_CTRL_NB_SHIFT 2 +#define SPI_CTRL_AD_SHIFT 7 + +#define SPI_STATUS_RE (1 << 0) /* Read End */ +#define SPI_STATUS_WE (1 << 1) /* Write End */ + +void spi_init(void) +{ + writew(SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS | SPI_SET1_RDWR_IRQ_DIS, + SPI_REG(REG_SET1)); + + writew(0x0001, SPI_REG(REG_SET2)); +} + +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din) +{ + uint8_t bytes_per_xfer; + uint8_t reg_status, reg_ctrl = 0; + uint32_t tmp; + + if (bitlen == 0) + return 0; + + if (bitlen > 32) + return -1; + + if (dev_idx > 4) + return -1; + + bytes_per_xfer = bitlen / 8; + if (bitlen % 8) + bytes_per_xfer ++; + + reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT; + reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT; + + if (bitlen <= 8) { + tmp = *(uint8_t *)dout; + tmp <<= 24 + (8-bitlen); /* align to MSB */ + } else if (bitlen <= 16) { + tmp = *(uint16_t *)dout; + tmp <<= 16 + (16-bitlen); /* align to MSB */ + } else { + tmp = *(uint32_t *)dout; + tmp <<= (32-bitlen); /* align to MSB */ + } + printd("spi_xfer(dev_idx=%u, bitlen=%u, data_out=0x%08x): ", + dev_idx, bitlen, tmp); + + /* fill transmit registers */ + writew(tmp >> 16, SPI_REG(REG_TX_MSB)); + writew(tmp & 0xffff, SPI_REG(REG_TX_LSB)); + + /* initiate transfer */ + if (din) + reg_ctrl |= SPI_CTRL_RDWR; + else + reg_ctrl |= SPI_CTRL_WR; + writew(reg_ctrl, SPI_REG(REG_CTRL)); + printd("reg_ctrl=0x%04x ", reg_ctrl); + + /* wait until the transfer is complete */ + while (1) { + reg_status = readw(SPI_REG(REG_STATUS)); + printd("status=0x%04x ", reg_status); + if (din && (reg_status & SPI_STATUS_RE)) + break; + else if (reg_status & SPI_STATUS_WE) + break; + } + /* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */ + delay_ms(1); + + if (din) { + tmp = readw(SPI_REG(REG_RX_MSB)) << 16; + tmp |= readw(SPI_REG(REG_RX_LSB)); + printd("data_in=0x%08x ", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + else + *(uint32_t *)din = tmp; + } + dputchar('\n'); + + return 0; +} diff --git a/src/target/firmware/calypso/timer.c b/src/target/firmware/calypso/timer.c new file mode 100644 index 00000000..1dd55f26 --- /dev/null +++ b/src/target/firmware/calypso/timer.c @@ -0,0 +1,156 @@ +/* Calypso DBB internal Timer Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdio.h> +#include <memory.h> +#include <stdint.h> + +#include <defines.h> + +#include <calypso/timer.h> +#include <calypso/irq.h> + +#define BASE_ADDR_TIMER 0xfffe3800 +#define TIMER2_OFFSET 0x3000 + +#define TIMER_REG(n, m) (((n)-1) ? (BASE_ADDR_TIMER + TIMER2_OFFSET + (m)) : (BASE_ADDR_TIMER + (m))) + +enum timer_reg { + CNTL_TIMER = 0x00, + LOAD_TIMER = 0x02, + READ_TIMER = 0x04, +}; + +enum timer_ctl { + CNTL_START = (1 << 0), + CNTL_AUTO_RELOAD = (1 << 1), + CNTL_CLOCK_ENABLE = (1 << 5), +}; + +/* Regular Timers (1 and 2) */ + +void hwtimer_enable(int num, int on) +{ + uint8_t ctl; + + if (num < 1 || num > 2) { + printf("Unknown timer %u\n", num); + return; + } + + ctl = readb(TIMER_REG(num, CNTL_TIMER)); + if (on) + ctl |= CNTL_START|CNTL_CLOCK_ENABLE; + else + ctl &= ~CNTL_START; + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload) +{ + uint8_t ctl; + + ctl = (pre_scale & 0x7) << 2; + if (auto_reload) + ctl |= CNTL_AUTO_RELOAD; + + writeb(ctl, TIMER_REG(num, CNTL_TIMER)); +} + +void hwtimer_load(int num, uint16_t val) +{ + writew(val, TIMER_REG(num, LOAD_TIMER)); +} + +uint16_t hwtimer_read(int num) +{ + uint8_t ctl = readb(TIMER_REG(num, CNTL_TIMER)); + + /* somehow a read results in an abort */ + if ((ctl & (CNTL_START|CNTL_CLOCK_ENABLE)) != (CNTL_START|CNTL_CLOCK_ENABLE)) + return 0xFFFF; + return readw(TIMER_REG(num, READ_TIMER)); +} + +void hwtimer_init(void) +{ + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(1, CNTL_TIMER)); + writeb(CNTL_CLOCK_ENABLE, TIMER_REG(2, CNTL_TIMER)); +} + +/* Watchdog Timer */ + +#define BASE_ADDR_WDOG 0xfffff800 +#define WDOG_REG(m) (BASE_ADDR_WDOG + m) + +enum wdog_reg { + WD_CNTL_TIMER = CNTL_TIMER, + WD_LOAD_TIMER = LOAD_TIMER, + WD_READ_TIMER = 0x02, + WD_MODE = 0x04, +}; + +enum wdog_ctl { + WD_CTL_START = (1 << 7), + WD_CTL_AUTO_RELOAD = (1 << 8) +}; + +enum wdog_mode { + WD_MODE_DIS_ARM = 0xF5, + WD_MODE_DIS_CONFIRM = 0xA0, + WD_MODE_ENABLE = (1 << 15) +}; + +#define WD_CTL_PRESCALE(value) (((value)&0x07) << 9) + +static void wdog_irq(__unused enum irq_nr nr) +{ + puts("=> WATCHDOG\n"); +} + +void wdog_enable(int on) +{ + if (on) { + irq_config(IRQ_WATCHDOG, 0, 0, 0); + irq_register_handler(IRQ_WATCHDOG, &wdog_irq); + irq_enable(IRQ_WATCHDOG); + writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE)); + } else { + writew(WD_MODE_DIS_ARM, WDOG_REG(WD_MODE)); + writew(WD_MODE_DIS_CONFIRM, WDOG_REG(WD_MODE)); + } +} + +void wdog_reset(void) +{ +#if 0 + // XXX: this is supposed to reset immediately but does not seem to + writew(0xF5, WDOG_REG(WD_MODE)); + writew(0xFF, WDOG_REG(WD_MODE)); +#else + // enable watchdog + writew(WD_MODE_ENABLE, WDOG_REG(WD_MODE)); + // force expiration + writew(0x0000, WDOG_REG(WD_LOAD_TIMER)); + writew(0x0000, WDOG_REG(WD_LOAD_TIMER)); +#endif +} diff --git a/src/target/firmware/calypso/tpu.c b/src/target/firmware/calypso/tpu.c new file mode 100644 index 00000000..ed7aea74 --- /dev/null +++ b/src/target/firmware/calypso/tpu.c @@ -0,0 +1,346 @@ +/* Calypso DBB internal TPU (Time Processing Unit) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> + +/* Using TPU_DEBUG you will send special HLDC messages to the host PC + * containing the full TPU RAM content at the time you call tpu_enable() */ +//#define TPU_DEBUG + +#define BASE_ADDR_TPU 0xffff1000 +#define TPU_REG(x) (BASE_ADDR_TPU+(x)) + +#define BASE_ADDR_TPU_RAM 0xffff9000 +#define TPU_RAM_END 0xffff97ff + +enum tpu_reg_arm { + TPU_CTRL = 0x0, /* Control & Status Register */ + INT_CTRL = 0x2, /* Interrupt Control Register */ + INT_STAT = 0x4, /* Interrupt Status Register */ + TPU_OFFSET = 0xC, /* Offset operand value register */ + TPU_SYNCHRO = 0xE, /* synchro operand value register */ + IT_DSP_PG = 0x20, +}; + +enum tpu_ctrl_bits { + TPU_CTRL_RESET = (1 << 0), + TPU_CTRL_PAGE = (1 << 1), + TPU_CTRL_EN = (1 << 2), + /* unused */ + TPU_CTRL_DSP_EN = (1 << 4), + /* unused */ + TPU_CTRL_MCU_RAM_ACC = (1 << 6), + TPU_CTRL_TSP_RESET = (1 << 7), + TPU_CTRL_IDLE = (1 << 8), + TPU_CTRL_WAIT = (1 << 9), + TPU_CTRL_CK_ENABLE = (1 << 10), + TPU_CTRL_FULL_WRITE = (1 << 11), +}; + +enum tpu_int_ctrl_bits { + ICTRL_MCU_FRAME = (1 << 0), + ICTRL_MCU_PAGE = (1 << 1), + ICTRL_DSP_FRAME = (1 << 2), + ICTRL_DSP_FRAME_FORCE = (1 << 3), +}; + +static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM; + +#ifdef TPU_DEBUG +#include <comm/sercomm.h> +#include <layer1/sync.h> +static void tpu_ram_read_en(int enable) +{ + uint16_t reg; + + reg = readw(TPU_REG(TPU_CTRL)); + if (enable) + reg |= TPU_CTRL_MCU_RAM_ACC; + else + reg &= ~TPU_CTRL_MCU_RAM_ACC; + writew(reg, TPU_REG(TPU_CTRL)); +} + +static void tpu_debug(void) +{ + uint16_t *tpu_base = (uint16_t *)BASE_ADDR_TPU_RAM; + unsigned int tpu_size = tpu_ptr - tpu_base; + struct msgb *msg = sercomm_alloc_msgb(tpu_size*2); + uint16_t *data; + uint32_t *fn; + uint16_t reg; + int i; + + /* prepend tpu memory dump with frame number */ + fn = (uint32_t *) msgb_put(msg, sizeof(fn)); + *fn = l1s.current_time.fn; + + tpu_ram_read_en(1); + + data = (uint16_t *) msgb_put(msg, tpu_size*2); + for (i = 0; i < tpu_size; i ++) + data[i] = tpu_base[i]; + + tpu_ram_read_en(0); + + sercomm_sendmsg(SC_DLCI_DEBUG, msg); +} +#else +static void tpu_debug(void) { } +#endif + +#define BIT_SET 1 +#define BIT_CLEAR 0 + +/* wait for a certain control bit to be set */ +static int tpu_wait_ctrl_bit(uint16_t bit, int set) +{ + int timeout = 10*1000; + + while (1) { + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + if (set) { + if (reg & bit) + break; + } else { + if (!(reg & bit)) + break; + } + timeout--; + if (timeout <= 0) { + puts("Timeout while waiting for TPU ctrl bit!\n"); + return -1; + } + } + + return 0; +} + +/* assert or de-assert TPU reset */ +void tpu_reset(int active) +{ + uint16_t reg; + + printd("tpu_reset(%u)\n", active); + reg = readw(TPU_REG(TPU_CTRL)); + if (active) { + reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET); + } else { + reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET); + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR); + } +} + +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_enable(%u)\n", active); + + tpu_debug(); + + if (active) + reg |= TPU_CTRL_EN; + else + reg &= ~TPU_CTRL_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + /* After the new scenario is loaded, TPU switches the MCU-visible memory + * page, i.e. we can write without any danger */ + tpu_rewind(); +#if 0 + { + int i; + uint16_t oldreg = 0; + + for (i = 0; i < 100000; i++) { + reg = readw(TPU_REG(TPU_CTRL)); + if (i == 0 || oldreg != reg) { + printd("%d TPU state: 0x%04x\n", i, reg); + } + oldreg = reg; + } + } +#endif +} + +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + printd("tpu_clk_enable(%u)\n", active); + if (active) { + reg |= TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET); + } else { + reg &= ~TPU_CTRL_CK_ENABLE; + writew(reg, TPU_REG(TPU_CTRL)); + tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR); + } +} + +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + reg |= TPU_CTRL_DSP_EN; + writew(reg, TPU_REG(TPU_CTRL)); + + tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET); +} + +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void) +{ + uint16_t reg = readw(TPU_REG(TPU_CTRL)); + + if (reg & TPU_CTRL_DSP_EN) + return 1; + + return 0; +} + +void tpu_rewind(void) +{ + dputs("tpu_rewind()\n"); + tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM; +} + +void tpu_enqueue(uint16_t instr) +{ + printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr); + *tpu_ptr++ = instr; + if (tpu_ptr > (uint16_t *) TPU_RAM_END) + puts("TPU enqueue beyond end of TPU memory\n"); +} + +void tpu_init(void) +{ + uint16_t *ptr; + + /* Put TPU into Reset and enable clock */ + tpu_reset(1); + tpu_clk_enable(1); + + /* set all TPU RAM to zero */ + for (ptr = (uint16_t *) BASE_ADDR_TPU_RAM; ptr < (uint16_t *) TPU_RAM_END; ptr++) + *ptr = 0x0000; + + /* Get TPU out of reset */ + tpu_reset(0); + /* Disable all interrupts */ + writeb(0x7, TPU_REG(INT_CTRL)); + + tpu_rewind(); + tpu_enq_offset(0); + tpu_enq_sync(0); +} + +void tpu_test(void) +{ + int i; + + /* program a sequence of TSPACT events into the TPU */ + for (i = 0; i < 10; i++) { + puts("TSP ACT enable: "); + tsp_act_enable(0x0001); + tpu_enq_wait(10); + puts("TSP ACT disable: "); + tsp_act_disable(0x0001); + tpu_enq_wait(10); + } + tpu_enq_sleep(); + + /* tell the chip to execute the scenario */ + tpu_enable(1); +} + +void tpu_wait_idle(void) +{ + dputs("Waiting for TPU Idle "); + /* Wait until TPU is doing something */ + delay_us(3); + /* Wait until TPU is idle */ + while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE) + dputchar('.'); + dputs("Done!\n"); +} + +void tpu_frame_irq_en(int mcu, int dsp) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + if (mcu) + reg &= ~ICTRL_MCU_FRAME; + else + reg |= ICTRL_MCU_FRAME; + + if (dsp) + reg &= ~ICTRL_DSP_FRAME; + else + reg |= ICTRL_DSP_FRAME; + + writeb(reg, TPU_REG(INT_CTRL)); +} + +void tpu_force_dsp_frame_irq(void) +{ + uint8_t reg = readb(TPU_REG(INT_CTRL)); + reg |= ICTRL_DSP_FRAME_FORCE; + writeb(reg, TPU_REG(INT_CTRL)); +} + +uint16_t tpu_get_offset(void) +{ + return readw(TPU_REG(TPU_OFFSET)); +} + +uint16_t tpu_get_synchro(void) +{ + return readw(TPU_REG(TPU_SYNCHRO)); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(int16_t a, int16_t b) +{ + int32_t sum = (int32_t)a + (int32_t)b; + + sum %= 5000; + + /* wrap around zero */ + if (sum < 0) + sum += 5000; + + return sum; +} diff --git a/src/target/firmware/calypso/tsp.c b/src/target/firmware/calypso/tsp.c new file mode 100644 index 00000000..5d24f48e --- /dev/null +++ b/src/target/firmware/calypso/tsp.c @@ -0,0 +1,121 @@ +/* Calypso DBB internal TSP (Time Serial Port) Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> + +static uint16_t tspact_state; + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout) +{ + if (bitlen <= 8) { + tpu_enq_move(TPUI_TX_1, dout & 0xff); + } else if (bitlen <= 16) { + tpu_enq_move(TPUI_TX_1, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_2, dout & 0xff); + } else if (bitlen <= 24) { + tpu_enq_move(TPUI_TX_1, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_3, dout & 0xff); + } else { + tpu_enq_move(TPUI_TX_1, (dout >> 24) & 0xff); + tpu_enq_move(TPUI_TX_2, (dout >> 16) & 0xff); + tpu_enq_move(TPUI_TX_3, (dout >> 8) & 0xff); + tpu_enq_move(TPUI_TX_4, dout & 0xff); + } + tpu_enq_move(TPUI_TSP_CTRL1, (dev_idx << 5) | (bitlen - 1)); + tpu_enq_move(TPUI_TSP_CTRL2, TPUI_CTRL2_WR); +} + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge) +{ + uint8_t reg = TPUI_TSP_SET1 + (dev_idx / 2); + uint8_t val = 0; + uint8_t shift; + + if (dev_idx & 1) + shift = 4; + else + shift = 0; + + if (clk_rising) + val |= 1; + if (en_positive) + val |= 2; + if (en_edge) + val |= 4; + + tpu_enq_move(reg, (val << shift)); +} + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act) +{ + uint8_t low = new_act & 0xff; + uint8_t high = new_act >> 8; + + if (low != (tspact_state & 0xff)) + tpu_enq_move(TPUI_TSP_ACT_L, low); + if (high != (tspact_state >> 8)) + tpu_enq_move(TPUI_TSP_ACT_U, high); + + tspact_state = new_act; +} + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state | bitmask; + tsp_act_update(new_act); +} + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask) +{ + uint16_t new_act = tspact_state & ~bitmask; + tsp_act_update(new_act); +} + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void) +{ + return tspact_state; +} + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask) +{ + uint16_t new_act = tspact_state ^ bitmask; + tsp_act_update(new_act); +} + +void tsp_init(void) +{ + tsp_act_update(0); +} diff --git a/src/target/firmware/calypso/uart.c b/src/target/firmware/calypso/uart.c new file mode 100644 index 00000000..a46fff91 --- /dev/null +++ b/src/target/firmware/calypso/uart.c @@ -0,0 +1,440 @@ +/* Calypso DBB internal UART Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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 <debug.h> + +#include <memory.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> + +#include <defines.h> +#include <console.h> +#include <comm/sercomm.h> + +#include <calypso/irq.h> +#include <calypso/uart.h> + +#define BASE_ADDR_UART_MODEM 0xffff5000 +#define OFFSET_IRDA 0x800 + +#define UART_REG(n,m) (BASE_ADDR_UART_MODEM + ((n)*OFFSET_IRDA)+(m)) + +#define LCR7BIT 0x80 +#define LCRBFBIT 0x40 +#define MCR6BIT 0x20 +#define REG_OFFS(m) ((m) &= ~(LCR7BIT|LCRBFBIT|MCR6BIT)) +/* read access LCR[7] = 0 */ +enum uart_reg { + RHR = 0, + IER = 1, + IIR = 2, + LCR = 3, + MCR = 4, + LSR = 5, + MSR = 6, + SPR = 7, + MDR1 = 8, + DMR2 = 9, + SFLSR = 0x0a, + RESUME = 0x0b, + SFREGL = 0x0c, + SFREGH = 0x0d, + BLR = 0x0e, + ACREG = 0x0f, + SCR = 0x10, + SSR = 0x11, + EBLR = 0x12, +/* read access LCR[7] = 1 */ + DLL = RHR | LCR7BIT, + DLH = IER | LCR7BIT, + DIV1_6 = ACREG | LCR7BIT, +/* read/write access LCR[7:0] = 0xbf */ + EFR = IIR | LCRBFBIT, + XON1 = MCR | LCRBFBIT, + XON2 = LSR | LCRBFBIT, + XOFF1 = MSR | LCRBFBIT, + XOFF2 = SPR | LCRBFBIT, +/* read/write access if EFR[4] = 1 and MCR[6] = 1 */ + TCR = MSR | MCR6BIT, + TLR = SPR | MCR6BIT, +}; +/* write access LCR[7] = 0 */ +#define THR RHR +#define FCR IIR /* only if EFR[4] = 1 */ +#define TXFLL SFLSR +#define TXFLH RESUME +#define RXFLL SFREGL +#define RXFLH SFREGH + +enum fcr_bits { + FIFO_EN = (1 << 0), + RX_FIFO_CLEAR = (1 << 1), + TX_FIFO_CLEAR = (1 << 2), + DMA_MODE = (1 << 3), +}; +#define TX_FIFO_TRIG_SHIFT 4 +#define RX_FIFO_TRIG_SHIFT 6 + +enum iir_bits { + IIR_INT_PENDING = 0x01, + IIR_INT_TYPE = 0x3E, + IIR_INT_TYPE_RX_STATUS_ERROR = 0x06, + IIR_INT_TYPE_RX_TIMEOUT = 0x0C, + IIR_INT_TYPE_RHR = 0x04, + IIR_INT_TYPE_THR = 0x02, + IIR_INT_TYPE_MSR = 0x00, + IIR_INT_TYPE_XOFF = 0x10, + IIR_INT_TYPE_FLOW = 0x20, + IIR_FCR0_MIRROR = 0xC0, +}; + +#define UART_REG_UIR 0xffff6000 + +/* enable or disable the divisor latch for access to DLL, DLH */ +static void uart_set_lcr7bit(int uart, int on) +{ + uint8_t reg; + + reg = readb(UART_REG(uart, LCR)); + if (on) + reg |= (1 << 7); + else + reg &= ~(1 << 7); + writeb(reg, UART_REG(uart, LCR)); +} + +static uint8_t old_lcr; +static void uart_set_lcr_bf(int uart, int on) +{ + old_lcr = readb(UART_REG(uart, LCR)); + + if (on) + writeb(0xBF, UART_REG(uart, LCR)); + else + writeb(old_lcr, UART_REG(uart, LCR)); +} + +/* Enable or disable the TCR_TLR latch bit in MCR[6] */ +static void uart_set_mcr6bit(int uart, int on) +{ + uint8_t mcr; + /* we assume EFR[4] is always set to 1 */ + mcr = readb(UART_REG(uart, MCR)); + if (on) + mcr |= (1 << 6); + else + mcr &= ~(1 << 6); + writeb(mcr, UART_REG(uart, MCR)); +} + +static void uart_reg_write(int uart, enum uart_reg reg, uint8_t val) +{ + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + writeb(val, UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); +} + +/* read from a UART register, applying any required latch bits */ +static uint8_t uart_reg_read(int uart, enum uart_reg reg) +{ + uint8_t ret; + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 1); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 1); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 1); + + ret = readb(UART_REG(uart, REG_OFFS(reg))); + + if (reg & LCRBFBIT) + uart_set_lcr_bf(uart, 0); + else if (reg & LCR7BIT) + uart_set_lcr7bit(uart, 0); + else if (reg & MCR6BIT) + uart_set_mcr6bit(uart, 0); + + return ret; +} + +static void uart_irq_handler_cons(__unused enum irq_nr irqnr) +{ + const uint8_t uart = CONS_UART_NR; + uint8_t iir; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RHR: + break; + case IIR_INT_TYPE_THR: + if (cons_rb_flush() == 1) { + /* everything was flushed, disable THR IRQ */ + uint8_t ier = uart_reg_read(uart, IER); + ier &= ~(1 << 1); + uart_reg_write(uart, IER, ier); + } + break; + case IIR_INT_TYPE_MSR: + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + break; + case IIR_INT_TYPE_RX_TIMEOUT: + break; + case IIR_INT_TYPE_XOFF: + break; + } +} + +static void uart_irq_handler_sercomm(__unused enum irq_nr irqnr) +{ + const uint8_t uart = SERCOMM_UART_NR; + uint8_t iir, ch; + + //uart_putchar_nb(uart, 'U'); + + iir = uart_reg_read(uart, IIR); + if (iir & IIR_INT_PENDING) + return; + + switch (iir & IIR_INT_TYPE) { + case IIR_INT_TYPE_RX_TIMEOUT: + case IIR_INT_TYPE_RHR: + /* as long as we have rx data available */ + while (uart_getchar_nb(uart, &ch)) { + if (sercomm_drv_rx_char(ch) < 0) { + /* sercomm cannot receive more data right now */ + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 0); + } + } + break; + case IIR_INT_TYPE_THR: + /* as long as we have space in the FIFO */ + while (!uart_tx_busy(uart)) { + /* get a byte from sercomm */ + if (!sercomm_drv_pull(&ch)) { + /* no more bytes in sercomm, stop TX interrupts */ + uart_irq_enable(uart, UART_IRQ_TX_EMPTY, 0); + break; + } + /* write the byte into the TX FIFO */ + uart_putchar_nb(uart, ch); + } + break; + case IIR_INT_TYPE_MSR: + printf("UART IRQ MSR\n"); + break; + case IIR_INT_TYPE_RX_STATUS_ERROR: + printf("UART IRQ RX_SE\n"); + break; + case IIR_INT_TYPE_XOFF: + printf("UART IRQXOFF\n"); + break; + } +} + +static const uint8_t uart2irq[] = { + [0] = IRQ_UART_IRDA, + [1] = IRQ_UART_MODEM, +}; + +void uart_init(uint8_t uart, uint8_t interrupts) +{ + uint8_t irq = uart2irq[uart]; + + uart_reg_write(uart, IER, 0x00); + if (uart == CONS_UART_NR) { + cons_init(); + if(interrupts) { + irq_register_handler(irq, &uart_irq_handler_cons); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } + } else { + sercomm_init(); + if(interrupts) { + irq_register_handler(irq, &uart_irq_handler_sercomm); + irq_config(irq, 0, 0, 0xff); + irq_enable(irq); + } + uart_irq_enable(uart, UART_IRQ_RX_CHAR, 1); + } +#if 0 + if (uart == 1) { + /* assign UART to MCU and unmask interrupts*/ + writeb(UART_REG_UIR, 0x00); + } +#endif + + /* if we don't initialize these, we get strange corruptions in the + received data... :-( */ + uart_reg_write(uart, MDR1, 0x07); /* turn off UART */ + uart_reg_write(uart, XON1, 0x00); /* Xon1/Addr Register */ + uart_reg_write(uart, XON2, 0x00); /* Xon2/Addr Register */ + uart_reg_write(uart, XOFF1, 0x00); /* Xoff1 Register */ + uart_reg_write(uart, XOFF2, 0x00); /* Xoff2 Register */ + uart_reg_write(uart, EFR, 0x00); /* Enhanced Features Register */ + + /* select UART mode */ + uart_reg_write(uart, MDR1, 0); + /* no XON/XOFF flow control, ENHANCED_EN, no auto-RTS/CTS */ + uart_reg_write(uart, EFR, (1 << 4)); + /* enable Tx/Rx FIFO, Tx trigger at 56 spaces, Rx trigger at 60 chars */ + uart_reg_write(uart, FCR, FIFO_EN | RX_FIFO_CLEAR | TX_FIFO_CLEAR | + (3 << TX_FIFO_TRIG_SHIFT) | (3 << RX_FIFO_TRIG_SHIFT)); + + /* THR interrupt only when TX FIFO and TX shift register are empty */ + uart_reg_write(uart, SCR, (1 << 0));// | (1 << 3)); + + /* 8 bit, 1 stop bit, no parity, no break */ + uart_reg_write(uart, LCR, 0x03); + + uart_set_lcr7bit(uart, 0); +} + +void uart_poll(uint8_t uart) { + if(uart == CONS_UART_NR) { + uart_irq_handler_cons(0); + } else { + uart_irq_handler_sercomm(0); + } +} + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on) +{ + uint8_t ier = uart_reg_read(uart, IER); + uint8_t mask = 0; + + switch (irq) { + case UART_IRQ_TX_EMPTY: + mask = (1 << 1); + break; + case UART_IRQ_RX_CHAR: + mask = (1 << 0); + break; + } + + if (on) + ier |= mask; + else + ier &= ~mask; + + uart_reg_write(uart, IER, ier); +} + + +void uart_putchar_wait(uint8_t uart, int c) +{ + /* wait while TX FIFO indicates full */ + while (readb(UART_REG(uart, SSR)) & 0x01) { } + + /* put character in TX FIFO */ + writeb(c, UART_REG(uart, THR)); +} + +int uart_putchar_nb(uint8_t uart, int c) +{ + /* if TX FIFO indicates full, abort */ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 0; + + writeb(c, UART_REG(uart, THR)); + return 1; +} + +int uart_getchar_nb(uint8_t uart, uint8_t *ch) +{ + uint8_t lsr; + + lsr = readb(UART_REG(uart, LSR)); + + /* something strange happened */ + if (lsr & 0x02) + printf("LSR RX_OE\n"); + if (lsr & 0x04) + printf("LSR RX_PE\n"); + if (lsr & 0x08) + printf("LSR RX_FE\n"); + if (lsr & 0x10) + printf("LSR RX_BI\n"); + if (lsr & 0x80) + printf("LSR RX_FIFO_STS\n"); + + /* is the Rx FIFO empty? */ + if (!(lsr & 0x01)) + return 0; + + *ch = readb(UART_REG(uart, RHR)); + //printf("getchar_nb(%u) = %02x\n", uart, *ch); + return 1; +} + +int uart_tx_busy(uint8_t uart) +{ + if (readb(UART_REG(uart, SSR)) & 0x01) + return 1; + return 0; +} + +static const uint16_t divider[] = { + [UART_38400] = 21, /* 38,690 */ + [UART_57600] = 14, /* 58,035 */ + [UART_115200] = 7, /* 116,071 */ + [UART_230400] = 4, /* 203,125! (-3% would be 223,488) */ + [UART_460800] = 2, /* 406,250! (-3% would be 446,976) */ + [UART_921600] = 1, /* 812,500! (-3% would be 893,952) */ +}; + +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt) +{ + uint16_t div; + + if (bdrt > ARRAY_SIZE(divider)) + return -1; + + div = divider[bdrt]; + uart_set_lcr7bit(uart, 1); + writeb(div & 0xff, UART_REG(uart, DLL)); + writeb(div >> 8, UART_REG(uart, DLH)); + uart_set_lcr7bit(uart, 0); + + return 0; +} diff --git a/src/target/firmware/calypso/uwire.c b/src/target/firmware/calypso/uwire.c new file mode 100644 index 00000000..b79d9f38 --- /dev/null +++ b/src/target/firmware/calypso/uwire.c @@ -0,0 +1,136 @@ +/* Driver for uWire Master Controller inside TI Calypso */ + +/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +//#define DEBUG +#include <debug.h> + +#include <memory.h> +#include <uwire.h> +#include <delay.h> + +#define BASE_ADDR_UWIRE 0xfffe4000 +#define UWIRE_REG(n) (BASE_ADDR_UWIRE+(n)) + +enum uwire_regs { + REG_DATA = 0x00, + REG_CSR = 0x02, + REG_SR1 = 0x04, + REG_SR2 = 0x06, + REG_SR3 = 0x08, +}; + +#define UWIRE_CSR_BITS_RD(n) (((n) & 0x1f) << 0) +#define UWIRE_CSR_BITS_WR(n) (((n) & 0x1f) << 5) +#define UWIRE_CSR_IDX(n) (((n) & 3) << 10) +#define UWIRE_CSR_CS_CMD (1 << 12) +#define UWIRE_CSR_START (1 << 13) +#define UWIRE_CSR_CSRB (1 << 14) +#define UWIRE_CSR_RDRB (1 << 15) + +#define UWIRE_CSn_EDGE_RD (1 << 0) /* 1=falling 0=rising */ +#define UWIRE_CSn_EDGE_WR (1 << 1) /* 1=falling 0=rising */ +#define UWIRE_CSn_CS_LVL (1 << 2) +#define UWIRE_CSn_FRQ_DIV2 (0 << 3) +#define UWIRE_CSn_FRQ_DIV4 (1 << 3) +#define UWIRE_CSn_FRQ_DIV8 (2 << 3) +#define UWIRE_CSn_CKH + +#define UWIRE_CSn_SHIFT(n) (((n) & 1) ? 6 : 0) +#define UWIRE_CSn_REG(n) (((n) & 2) ? REG_SR2 : REG_SR1) + +#define UWIRE_SR3_CLK_EN (1 << 0) +#define UWIRE_SR3_CLK_DIV2 (0 << 1) +#define UWIRE_SR3_CLK_DIV4 (1 << 1) +#define UWIRE_SR3_CLK_DIV7 (2 << 1) +#define UWIRE_SR3_CLK_DIV10 (3 << 1) + +static inline void _uwire_wait(int mask, int val) +{ + while ((readw(UWIRE_REG(REG_CSR)) & mask) != val); +} + +void uwire_init(void) +{ + writew(UWIRE_SR3_CLK_EN | UWIRE_SR3_CLK_DIV2, UWIRE_REG(REG_SR3)); + /* FIXME only init CS0 for now */ + writew(((UWIRE_CSn_CS_LVL | UWIRE_CSn_FRQ_DIV2) << UWIRE_CSn_SHIFT(0)), + UWIRE_REG(UWIRE_CSn_REG(0))); + writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); +} + +int uwire_xfer(int cs, int bitlen, const void *dout, void *din) +{ + uint16_t tmp = 0; + + if (bitlen <= 0 || bitlen > 16) + return -1; + if (cs < 0 || cs > 4) + return -1; + + /* FIXME uwire_init always select CS0 for now */ + + printd("uwire_xfer(dev_idx=%u, bitlen=%u\n", cs, bitlen); + + /* select the chip */ + writew(UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); + + if (dout) { + if (bitlen <= 8) + tmp = *(uint8_t *)dout; + else if (bitlen <= 16) + tmp = *(uint16_t *)dout; + tmp <<= 16 - bitlen; /* align to MSB */ + writew(tmp, UWIRE_REG(REG_DATA)); + printd(", data_out=0x%04hx", tmp); + } + + tmp = (dout ? UWIRE_CSR_BITS_WR(bitlen) : 0) | + (din ? UWIRE_CSR_BITS_RD(bitlen) : 0) | + UWIRE_CSR_START; + writew(tmp, UWIRE_REG(REG_CSR)); + + _uwire_wait(UWIRE_CSR_CSRB, 0); + + if (din) { + _uwire_wait(UWIRE_CSR_RDRB, UWIRE_CSR_RDRB); + + tmp = readw(UWIRE_REG(REG_DATA)); + printd(", data_in=0x%08x", tmp); + + if (bitlen <= 8) + *(uint8_t *)din = tmp & 0xff; + else if (bitlen <= 16) + *(uint16_t *)din = tmp & 0xffff; + } + /* unselect the chip */ + writew(UWIRE_CSR_IDX(0) | 0, UWIRE_REG(REG_CSR)); + _uwire_wait(UWIRE_CSR_CSRB, 0); + + printd(")\n"); + + return 0; +} diff --git a/src/target/firmware/comm/Makefile b/src/target/firmware/comm/Makefile new file mode 100644 index 00000000..25fbb983 --- /dev/null +++ b/src/target/firmware/comm/Makefile @@ -0,0 +1,5 @@ + +LIBRARIES+=comm +comm_DIR=comm +comm_SRCS=msgb.c sercomm.c sercomm_cons.c timer.c + diff --git a/src/target/firmware/comm/msgb.c b/src/target/firmware/comm/msgb.c new file mode 100644 index 00000000..d412844c --- /dev/null +++ b/src/target/firmware/comm/msgb.c @@ -0,0 +1,74 @@ +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include <debug.h> +#include <delay.h> + +#include <osmocore/msgb.h> + +#include <calypso/backlight.h> + +#define NO_TALLOC + +void *tall_msgb_ctx; + +#ifdef NO_TALLOC +/* This is a poor mans static allocator for msgb objects */ +#define MSGB_DATA_SIZE 256+4 +#define MSGB_NUM 32 +struct supermsg { + uint8_t allocated; + struct msgb msg; + uint8_t buf[MSGB_DATA_SIZE]; +}; +static struct supermsg msgs[MSGB_NUM]; +void *_talloc_zero(void *ctx, unsigned int size, const char *name) +{ + unsigned int i; + if (size > sizeof(struct msgb) + MSGB_DATA_SIZE) + goto panic; + + while (1) { + for (i = 0; i < ARRAY_SIZE(msgs); i++) { + if (!msgs[i].allocated) { + msgs[i].allocated = 1; + memset(&msgs[i].msg, 0, sizeof(&msgs[i].msg)); + memset(&msgs[i].buf, 0, sizeof(&msgs[i].buf)); + return &msgs[i].msg; + } + } + cons_puts("unable to allocate msgb\n"); + bl_level(++i % 50); + delay_ms(50); + } +panic: + return NULL; +} +void talloc_free(void *msg) +{ + struct supermsg *smsg = container_of(msg, struct supermsg, msg); + smsg->allocated = 0; +} +#endif diff --git a/src/target/firmware/comm/sercomm.c b/src/target/firmware/comm/sercomm.c new file mode 100644 index 00000000..cace0465 --- /dev/null +++ b/src/target/firmware/comm/sercomm.c @@ -0,0 +1,277 @@ +/* Serial communications layer, based on HDLC */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <errno.h> + +#include <osmocore/msgb.h> + +#ifdef HOST_BUILD +#define SERCOMM_RX_MSG_SIZE 2048 +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#endif +#include <sercomm.h> +#define local_irq_save(x) +#define local_fiq_disable() +#define local_irq_restore(x) + +#else +#define SERCOMM_RX_MSG_SIZE 256 +#include <debug.h> +#include <osmocore/linuxlist.h> +#include <asm/system.h> + +#include <comm/sercomm.h> +#include <calypso/uart.h> +#endif + + +enum rx_state { + RX_ST_WAIT_START, + RX_ST_ADDR, + RX_ST_CTRL, + RX_ST_DATA, + RX_ST_ESCAPE, +}; + +static struct { + int initialized; + + /* transmit side */ + struct { + struct llist_head dlci_queues[_SC_DLCI_MAX]; + struct msgb *msg; + enum rx_state state; + uint8_t *next_char; + } tx; + + /* receive side */ + struct { + dlci_cb_t dlci_handler[_SC_DLCI_MAX]; + struct msgb *msg; + enum rx_state state; + uint8_t dlci; + uint8_t ctrl; + } rx; + +} sercomm; + +void sercomm_init(void) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) + INIT_LLIST_HEAD(&sercomm.tx.dlci_queues[i]); + + sercomm.rx.msg = NULL; + sercomm.initialized = 1; + + /* set up the echo dlci */ + sercomm_register_rx_cb(SC_DLCI_ECHO, &sercomm_sendmsg); +} + +int sercomm_initialized(void) +{ + return sercomm.initialized; +} + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg) +{ + unsigned long flags; + uint8_t *hdr; + + /* prepend address + control octet */ + hdr = msgb_push(msg, 2); + hdr[0] = dlci; + hdr[1] = HDLC_C_UI; + + /* This functiion can be called from any context: FIQ, IRQ + * and supervisor context. Proper locking is important! */ + local_irq_save(flags); + local_fiq_disable(); + msgb_enqueue(&sercomm.tx.dlci_queues[dlci], msg); + local_irq_restore(flags); + +#ifndef HOST_BUILD + /* tell UART that we have something to send */ + uart_irq_enable(SERCOMM_UART_NR, UART_IRQ_TX_EMPTY, 1); +#endif +} + +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci) +{ + struct llist_head *le; + unsigned int num = 0; + + llist_for_each(le, &sercomm.tx.dlci_queues[dlci]) { + num++; + } + + return num; +} + +/* fetch one octet of to-be-transmitted serial data */ +int sercomm_drv_pull(uint8_t *ch) +{ + /* we are always called from interrupt context in this function, + * which means that any data structures we use need to be for + * our exclusive access */ + if (!sercomm.tx.msg) { + unsigned int i; + /* dequeue a new message from the queues */ + for (i = 0; i < ARRAY_SIZE(sercomm.tx.dlci_queues); i++) { + sercomm.tx.msg = msgb_dequeue(&sercomm.tx.dlci_queues[i]); + if (sercomm.tx.msg) + break; + } + if (sercomm.tx.msg) { + /* start of a new message, send start flag octet */ + *ch = HDLC_FLAG; + sercomm.tx.next_char = sercomm.tx.msg->data; + return 1; + } else { + /* no more data avilable */ + return 0; + } + } + + if (sercomm.tx.state == RX_ST_ESCAPE) { + /* we've already transmitted the ESCAPE octet, + * we now need to trnsmit the escaped data */ + *ch = *sercomm.tx.next_char++; + sercomm.tx.state = RX_ST_DATA; + } else if (sercomm.tx.next_char >= sercomm.tx.msg->tail) { + /* last character has already been transmitted, + * send end-of-message octet */ + *ch = HDLC_FLAG; + /* we've reached the end of the message buffer */ + msgb_free(sercomm.tx.msg); + sercomm.tx.msg = NULL; + sercomm.tx.next_char = NULL; + /* escaping for the two control octets */ + } else if (*sercomm.tx.next_char == HDLC_FLAG || + *sercomm.tx.next_char == HDLC_ESCAPE || + *sercomm.tx.next_char == 0x00) { + /* send an escape octet */ + *ch = HDLC_ESCAPE; + /* invert bit 5 of the next octet to be sent */ + *sercomm.tx.next_char ^= (1 << 5); + sercomm.tx.state = RX_ST_ESCAPE; + } else { + /* standard case, simply send next octet */ + *ch = *sercomm.tx.next_char++; + } + return 1; +} + +/* register a handler for a given DLCI */ +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb) +{ + if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler)) + return -EINVAL; + + if (sercomm.rx.dlci_handler[dlci]) + return -EBUSY; + + sercomm.rx.dlci_handler[dlci] = cb; + return 0; +} + +/* dispatch an incomnig message once it is completely received */ +static void dispatch_rx_msg(uint8_t dlci, struct msgb *msg) +{ + if (dlci >= ARRAY_SIZE(sercomm.rx.dlci_handler) || + !sercomm.rx.dlci_handler[dlci]) { + msgb_free(msg); + return; + } + sercomm.rx.dlci_handler[dlci](dlci, msg); +} + +/* the driver has received one byte, pass it into sercomm layer */ +int sercomm_drv_rx_char(uint8_t ch) +{ + uint8_t *ptr; + + /* we are always called from interrupt context in this function, + * which means that any data structures we use need to be for + * our exclusive access */ + if (!sercomm.rx.msg) + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + + if (msgb_tailroom(sercomm.rx.msg) == 0) { + //cons_puts("sercomm_drv_rx_char() overflow!\n"); + msgb_free(sercomm.rx.msg); + sercomm.rx.msg = sercomm_alloc_msgb(SERCOMM_RX_MSG_SIZE); + sercomm.rx.state = RX_ST_WAIT_START; + return 0; + } + + switch (sercomm.rx.state) { + case RX_ST_WAIT_START: + if (ch != HDLC_FLAG) + break; + sercomm.rx.state = RX_ST_ADDR; + break; + case RX_ST_ADDR: + sercomm.rx.dlci = ch; + sercomm.rx.state = RX_ST_CTRL; + break; + case RX_ST_CTRL: + sercomm.rx.ctrl = ch; + sercomm.rx.state = RX_ST_DATA; + break; + case RX_ST_DATA: + if (ch == HDLC_ESCAPE) { + /* drop the escape octet, but change state */ + sercomm.rx.state = RX_ST_ESCAPE; + break; + } else if (ch == HDLC_FLAG) { + /* message is finished */ + dispatch_rx_msg(sercomm.rx.dlci, sercomm.rx.msg); + /* allocate new buffer */ + sercomm.rx.msg = NULL; + /* start all over again */ + sercomm.rx.state = RX_ST_WAIT_START; + + /* do not add the control char */ + break; + } + /* default case: store the octet */ + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + break; + case RX_ST_ESCAPE: + /* store bif-5-inverted octet in buffer */ + ch ^= (1 << 5); + ptr = msgb_put(sercomm.rx.msg, 1); + *ptr = ch; + /* transition back to nromal DATA state */ + sercomm.rx.state = RX_ST_DATA; + break; + } + + return 1; +} diff --git a/src/target/firmware/comm/sercomm_cons.c b/src/target/firmware/comm/sercomm_cons.c new file mode 100644 index 00000000..5d7842fb --- /dev/null +++ b/src/target/firmware/comm/sercomm_cons.c @@ -0,0 +1,140 @@ +/* Serial console layer, layered on top of sercomm HDLC */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> +#include <string.h> + +#include <asm/system.h> + +#include <calypso/uart.h> + +#include <console.h> +#include <osmocore/msgb.h> +#include <comm/sercomm.h> +#include <comm/sercomm_cons.h> + +static struct { + struct msgb *cur_msg; +} scons; + +static void raw_puts(const char *s) +{ + int i = strlen(s); + while (i--) + uart_putchar_wait(SERCOMM_UART_NR, *s++); +} + +#ifdef DEBUG +#define raw_putd(x) raw_puts(x) +#else +#define raw_putd(x) +#endif + +int sercomm_puts(const char *s) +{ + unsigned long flags; + const int len = strlen(s); + unsigned int bytes_left = len; + + if (!sercomm_initialized()) { + raw_putd("sercomm not initialized: "); + raw_puts(s); + return len - 1; + } + + /* This function is called from any context: Supervisor, IRQ, FIQ, ... + * as such, we need to ensure re-entrant calls are either supported or + * avoided. */ + local_irq_save(flags); + local_fiq_disable(); + + while (bytes_left > 0) { + unsigned int write_num, space_left, flush; + uint8_t *data; + + if (!scons.cur_msg) + scons.cur_msg = sercomm_alloc_msgb(SERCOMM_CONS_ALLOC); + + if (!scons.cur_msg) { + raw_putd("cannot allocate sercomm msgb: "); + raw_puts(s); + return -ENOMEM; + } + + /* space left in the current msgb */ + space_left = msgb_tailroom(scons.cur_msg); + + if (space_left <= bytes_left) { + write_num = space_left; + /* flush buffer when it is full */ + flush = 1; + } else { + write_num = bytes_left; + flush = 0; + } + + /* obtain pointer where to copy the data */ + data = msgb_put(scons.cur_msg, write_num); + + /* copy data while looking for \n line termination */ + { + unsigned int i; + for (i = 0; i < write_num; i++) { + /* flush buffer at end of line, but skip + * flushing if we have a backlog in order to + * increase efficiency of msgb filling */ + if (*s == '\n' && + sercomm_tx_queue_depth(SC_DLCI_CONSOLE) < 4) + flush = 1; + *data++ = *s++; + } + } + bytes_left -= write_num; + + if (flush) { + sercomm_sendmsg(SC_DLCI_CONSOLE, scons.cur_msg); + /* reset scons.cur_msg pointer to ensure we allocate + * a new one next round */ + scons.cur_msg = NULL; + } + } + + local_irq_restore(flags); + + return len - 1; +} + +int sercomm_putchar(int c) +{ + char s[2]; + int rc; + + s[0] = c & 0xff; + s[1] = '\0'; + + rc = sercomm_puts(s); + if (rc < 0) + return rc; + + return c; +} diff --git a/src/target/firmware/comm/timer.c b/src/target/firmware/comm/timer.c new file mode 100644 index 00000000..d69ef542 --- /dev/null +++ b/src/target/firmware/comm/timer.c @@ -0,0 +1,213 @@ +/* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <debug.h> +#include <osmocore/linuxlist.h> + +#include <comm/timer.h> + +#include <calypso/timer.h> +#include <calypso/irq.h> + +static LLIST_HEAD(timer_list); + +unsigned long volatile jiffies; + +#define TIMER_HZ 100 + +#define time_after(a,b) \ + (typecheck(unsigned long, a) && \ + typecheck(unsigned long, b) && \ + ((long)(b) - (long)(a) < 0)) +#define time_before(a,b) time_after(b,a) + +void 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 schedule_timer(struct timer_list *timer, int milliseconds) +{ + timer->expires = jiffies + ((milliseconds * TIMER_HZ) / 1000); + add_timer(timer); +} + +void del_timer(struct timer_list *timer) +{ + if (timer->in_list) { + timer->active = 0; + timer->in_list = 0; + llist_del(&timer->entry); + } +} + +int timer_pending(struct timer_list *timer) +{ + return timer->active; +} + +#if 0 +/* + * 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 *nearest_timer() +{ + struct timeval current_time; + + if (s_nearest_time.tv_sec == 0 && s_nearest_time.tv_usec == 0) + return NULL; + + if (gettimeofday(¤t_time, NULL) == -1) + return NULL; + + unsigned long long nearestTime = s_nearest_time.tv_sec * MICRO_SECONDS + s_nearest_time.tv_usec; + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + + if (nearestTime < currentTime) { + s_select_time.tv_sec = 0; + s_select_time.tv_usec = 0; + } else { + s_select_time.tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; + s_select_time.tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; + } + + return &s_select_time; +} + +/* + * Find the nearest time and update s_nearest_time + */ +void prepare_timers() +{ + struct timer_list *timer, *nearest_timer = NULL; + llist_for_each_entry(timer, &timer_list, entry) { + if (!nearest_timer || time_before(timer->expires, nearest_timer->expires)) { + nearest_timer = timer; + } + } + + if (nearest_timer) { + s_nearest_time = nearest_timer->timeout; + } else { + memset(&s_nearest_time, 0, sizeof(struct timeval)); + } +} +#endif + +/* + * fire all timers... and remove them + */ +int update_timers(void) +{ + struct timer_list *timer, *tmp; + int work = 0; + + /* + * 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_before(timer->expires, jiffies)) { + 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) { + del_timer(timer); + } + } + + return work; +} + +int timer_check(void) +{ + struct timer_list *timer; + int i = 0; + + llist_for_each_entry(timer, &timer_list, entry) { + i++; + } + return i; +} + +static void timer_irq(enum irq_nr irq) +{ + /* we only increment jiffies here. FIXME: does this need to be atomic? */ + jiffies++; +} + +void timer_init(void) +{ + /* configure TIMER2 for our purpose */ + hwtimer_enable(2, 1); + /* The timer runs at 13MHz / 32, i.e. 406.25kHz */ +#if (TIMER_HZ == 100) + hwtimer_load(2, 4062); + hwtimer_config(2, 0, 1); +#elif (TIMER_HZ == 10) + /* prescaler 4, 1015 ticks until expiry */ + hwtimer_load(2, 1015); + hwtimer_config(2, 4, 1); +#endif + hwtimer_enable(2, 1); + + /* register interrupt handler with default priority, EDGE triggered */ + irq_register_handler(IRQ_TIMER2, &timer_irq); + irq_config(IRQ_TIMER2, 0, 1, -1); + irq_enable(IRQ_TIMER2); +} diff --git a/src/target/firmware/display/display.c b/src/target/firmware/display/display.c new file mode 100644 index 00000000..1c8f1fb4 --- /dev/null +++ b/src/target/firmware/display/display.c @@ -0,0 +1,20 @@ + +#include <stdint.h> + +#include <display.h> + +struct display_driver *display; + +int display_puts(const char *str) +{ + char c; + + if (display->puts) + display->puts(str); + else { + while ((c = *str++)) + display_putchar(c); + } + + return 0; +} diff --git a/src/target/firmware/display/font_r8x8.c b/src/target/firmware/display/font_r8x8.c Binary files differnew file mode 100644 index 00000000..f6a8a820 --- /dev/null +++ b/src/target/firmware/display/font_r8x8.c diff --git a/src/target/firmware/display/font_r8x8_horiz.c b/src/target/firmware/display/font_r8x8_horiz.c new file mode 100644 index 00000000..046d09bf --- /dev/null +++ b/src/target/firmware/display/font_r8x8_horiz.c @@ -0,0 +1,261 @@ +/* 8x8 font, right aligned, horizontal scanning */ + +const unsigned char fontdata_r8x8_horiz[] ={ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e, + 0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e, + 0x6c,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00, + 0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00, + 0x1c,0x1c,0x1c,0x7f,0x7f,0x6b,0x08,0x1c, + 0x10,0x10,0x38,0x7c,0xfe,0x7c,0x10,0x38, + 0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00, + 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff, + 0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00, + 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff, + 0x0f,0x07,0x0f,0x7d,0xcc,0xcc,0xcc,0x78, + 0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18, + 0x3f,0x33,0x3f,0x30,0x30,0x70,0xf0,0xe0, + 0x7f,0x63,0x7f,0x63,0x63,0x67,0xe6,0xc0, + 0x18,0xdb,0x3c,0xe7,0xe7,0x3c,0xdb,0x18, + 0x80,0xe0,0xf8,0xfe,0xf8,0xe0,0x80,0x00, + 0x02,0x0e,0x3e,0xfe,0x3e,0x0e,0x02,0x00, + 0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18, + 0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00, + 0x7f,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x00, + 0x3e,0x63,0x38,0x6c,0x6c,0x38,0xcc,0x78, + 0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00, + 0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff, + 0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00, + 0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00, + 0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00, + 0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00, + 0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00, + 0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00, + 0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00, + 0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00, + 0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00, + 0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00, + 0x18,0x3e,0x60,0x3c,0x06,0x7c,0x18,0x00, + 0x00,0x63,0x66,0x0c,0x18,0x33,0x63,0x00, + 0x1c,0x36,0x1c,0x3b,0x6e,0x66,0x3b,0x00, + 0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00, + 0x0c,0x18,0x30,0x30,0x30,0x18,0x0c,0x00, + 0x30,0x18,0x0c,0x0c,0x0c,0x18,0x30,0x00, + 0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00, + 0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30, + 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00, + 0x03,0x06,0x0c,0x18,0x30,0x60,0x40,0x00, + 0x3e,0x63,0x67,0x6f,0x7b,0x73,0x3e,0x00, + 0x18,0x38,0x58,0x18,0x18,0x18,0x7e,0x00, + 0x3c,0x66,0x06,0x1c,0x30,0x66,0x7e,0x00, + 0x3c,0x66,0x06,0x1c,0x06,0x66,0x3c,0x00, + 0x0e,0x1e,0x36,0x66,0x7f,0x06,0x0f,0x00, + 0x7e,0x60,0x7c,0x06,0x06,0x66,0x3c,0x00, + 0x1c,0x30,0x60,0x7c,0x66,0x66,0x3c,0x00, + 0x7e,0x66,0x06,0x0c,0x18,0x18,0x18,0x00, + 0x3c,0x66,0x66,0x3c,0x66,0x66,0x3c,0x00, + 0x3c,0x66,0x66,0x3e,0x06,0x0c,0x38,0x00, + 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00, + 0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30, + 0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00, + 0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00, + 0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00, + 0x3c,0x66,0x06,0x0c,0x18,0x00,0x18,0x00, + 0x3e,0x63,0x6f,0x69,0x6f,0x60,0x3e,0x00, + 0x18,0x3c,0x66,0x66,0x7e,0x66,0x66,0x00, + 0x7e,0x33,0x33,0x3e,0x33,0x33,0x7e,0x00, + 0x1e,0x33,0x60,0x60,0x60,0x33,0x1e,0x00, + 0x7c,0x36,0x33,0x33,0x33,0x36,0x7c,0x00, + 0x7f,0x31,0x34,0x3c,0x34,0x31,0x7f,0x00, + 0x7f,0x31,0x34,0x3c,0x34,0x30,0x78,0x00, + 0x1e,0x33,0x60,0x60,0x67,0x33,0x1f,0x00, + 0x66,0x66,0x66,0x7e,0x66,0x66,0x66,0x00, + 0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00, + 0x0f,0x06,0x06,0x06,0x66,0x66,0x3c,0x00, + 0x73,0x33,0x36,0x3c,0x36,0x33,0x73,0x00, + 0x78,0x30,0x30,0x30,0x31,0x33,0x7f,0x00, + 0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00, + 0x63,0x73,0x7b,0x6f,0x67,0x63,0x63,0x00, + 0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00, + 0x7e,0x33,0x33,0x3e,0x30,0x30,0x78,0x00, + 0x3c,0x66,0x66,0x66,0x6e,0x3c,0x0e,0x00, + 0x7e,0x33,0x33,0x3e,0x36,0x33,0x73,0x00, + 0x3c,0x66,0x30,0x18,0x0c,0x66,0x3c,0x00, + 0x7e,0x5a,0x18,0x18,0x18,0x18,0x3c,0x00, + 0x66,0x66,0x66,0x66,0x66,0x66,0x7e,0x00, + 0x66,0x66,0x66,0x66,0x66,0x3c,0x18,0x00, + 0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00, + 0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00, + 0x66,0x66,0x66,0x3c,0x18,0x18,0x3c,0x00, + 0x7f,0x63,0x46,0x0c,0x19,0x33,0x7f,0x00, + 0x3c,0x30,0x30,0x30,0x30,0x30,0x3c,0x00, + 0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00, + 0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00, + 0x08,0x1c,0x36,0x63,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, + 0x18,0x18,0x0c,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3c,0x06,0x3e,0x66,0x3b,0x00, + 0x70,0x30,0x30,0x3e,0x33,0x33,0x6e,0x00, + 0x00,0x00,0x3c,0x66,0x60,0x66,0x3c,0x00, + 0x0e,0x06,0x06,0x3e,0x66,0x66,0x3b,0x00, + 0x00,0x00,0x3c,0x66,0x7e,0x60,0x3c,0x00, + 0x1c,0x36,0x30,0x78,0x30,0x30,0x78,0x00, + 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x7c, + 0x70,0x30,0x36,0x3b,0x33,0x33,0x73,0x00, + 0x18,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3c, + 0x70,0x30,0x33,0x36,0x3c,0x36,0x73,0x00, + 0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00, + 0x00,0x00,0x66,0x7f,0x7f,0x6b,0x63,0x00, + 0x00,0x00,0x7c,0x66,0x66,0x66,0x66,0x00, + 0x00,0x00,0x3c,0x66,0x66,0x66,0x3c,0x00, + 0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78, + 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f, + 0x00,0x00,0x6e,0x3b,0x33,0x30,0x78,0x00, + 0x00,0x00,0x3e,0x60,0x3c,0x06,0x7c,0x00, + 0x08,0x18,0x3e,0x18,0x18,0x1a,0x0c,0x00, + 0x00,0x00,0x66,0x66,0x66,0x66,0x3b,0x00, + 0x00,0x00,0x66,0x66,0x66,0x3c,0x18,0x00, + 0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00, + 0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00, + 0x00,0x00,0x66,0x66,0x66,0x3e,0x06,0x7c, + 0x00,0x00,0x7e,0x4c,0x18,0x32,0x7e,0x00, + 0x0e,0x18,0x18,0x70,0x18,0x18,0x0e,0x00, + 0x0c,0x0c,0x0c,0x00,0x0c,0x0c,0x0c,0x00, + 0x70,0x18,0x18,0x0e,0x18,0x18,0x70,0x00, + 0x3b,0x6e,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x08,0x1c,0x36,0x63,0x63,0x7f,0x00, + 0x3c,0x66,0x60,0x66,0x3c,0x0c,0x06,0x3c, + 0x00,0x66,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x1c,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, + 0x7e,0xc3,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x66,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x70,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x18,0x18,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x00,0x00,0x3c,0x60,0x60,0x3c,0x06,0x1c, + 0x7e,0xc3,0x3c,0x66,0x7e,0x60,0x3c,0x00, + 0xcc,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, + 0x70,0x00,0x3c,0x66,0x7e,0x60,0x3c,0x00, + 0x66,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x3e,0x63,0x1c,0x0c,0x0c,0x0c,0x1e,0x00, + 0x70,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x63,0x1c,0x36,0x63,0x7f,0x63,0x63,0x00, + 0x18,0x18,0x00,0x3c,0x66,0x7e,0x66,0x00, + 0x1c,0x00,0xfc,0x60,0x78,0x60,0xfc,0x00, + 0x00,0x00,0x7f,0x0c,0x7f,0xcc,0x7f,0x00, + 0x1f,0x36,0x66,0x7f,0x66,0x66,0x67,0x00, + 0x3c,0x66,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x00,0x66,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x00,0x70,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x3c,0x66,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x00,0x70,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x00,0xcc,0x00,0xcc,0xcc,0x7c,0x0c,0xf8, + 0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00, + 0x66,0x00,0x66,0x66,0x66,0x66,0x3c,0x00, + 0x0c,0x0c,0x3f,0x60,0x60,0x3f,0x0c,0x0c, + 0x1c,0x36,0x32,0x78,0x30,0x73,0x7e,0x00, + 0x66,0x66,0x3c,0x7e,0x18,0x7e,0x18,0x18, + 0xf8,0xcc,0xcc,0xfa,0xc6,0xcf,0xc6,0xc7, + 0x0e,0x1b,0x18,0x3c,0x18,0x18,0xd8,0x70, + 0x0e,0x00,0x3c,0x06,0x3e,0x66,0x3f,0x00, + 0x1c,0x00,0x38,0x18,0x18,0x18,0x3c,0x00, + 0x00,0x0e,0x00,0x3c,0x66,0x66,0x3c,0x00, + 0x00,0x0e,0x00,0x66,0x66,0x66,0x3f,0x00, + 0x00,0x7c,0x00,0x7c,0x66,0x66,0x66,0x00, + 0x7e,0x00,0x66,0x76,0x7e,0x6e,0x66,0x00, + 0x1e,0x36,0x36,0x1f,0x00,0x3f,0x00,0x00, + 0x1c,0x36,0x36,0x1c,0x00,0x3e,0x00,0x00, + 0x18,0x00,0x18,0x30,0x60,0x66,0x3c,0x00, + 0x00,0x00,0x00,0x7e,0x60,0x60,0x00,0x00, + 0x00,0x00,0x00,0xfc,0x0c,0x0c,0x00,0x00, + 0xc3,0xc6,0xcc,0xde,0x33,0x66,0xcc,0x0f, + 0xc3,0xc6,0xcc,0xdb,0x37,0x6f,0xcf,0x03, + 0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00, + 0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00, + 0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00, + 0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88, + 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, + 0xdb,0x77,0xdb,0xee,0xdb,0x77,0xdb,0xee, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18, + 0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18, + 0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36, + 0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36, + 0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18, + 0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36, + 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, + 0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36, + 0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00, + 0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00, + 0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00, + 0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18, + 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00, + 0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18, + 0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18, + 0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36, + 0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00, + 0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36, + 0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00, + 0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36, + 0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36, + 0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00, + 0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36, + 0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00, + 0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00, + 0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18, + 0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36, + 0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00, + 0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00, + 0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18, + 0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36, + 0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36, + 0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, + 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x3b,0x6e,0x64,0x6e,0x3b,0x00, + 0x00,0x3c,0x66,0x7c,0x66,0x7c,0x60,0x60, + 0x00,0x7e,0x66,0x60,0x60,0x60,0x60,0x00, + 0x00,0x7f,0x36,0x36,0x36,0x36,0x36,0x00, + 0x7e,0x66,0x30,0x18,0x30,0x66,0x7e,0x00, + 0x00,0x00,0x3f,0x6c,0x6c,0x6c,0x38,0x00, + 0x00,0x33,0x33,0x33,0x33,0x3e,0x30,0x60, + 0x00,0x3b,0x6e,0x0c,0x0c,0x0c,0x0c,0x00, + 0x7e,0x18,0x3c,0x66,0x66,0x3c,0x18,0x7e, + 0x1c,0x36,0x63,0x7f,0x63,0x36,0x1c,0x00, + 0x1c,0x36,0x63,0x63,0x36,0x36,0x77,0x00, + 0x0e,0x18,0x0c,0x3e,0x66,0x66,0x3c,0x00, + 0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00, + 0x06,0x0c,0x7e,0xdb,0xdb,0x7e,0x60,0xc0, + 0x1c,0x30,0x60,0x7c,0x60,0x30,0x1c,0x00, + 0x3c,0x66,0x66,0x66,0x66,0x66,0x66,0x00, + 0x00,0x7e,0x00,0x7e,0x00,0x7e,0x00,0x00, + 0x18,0x18,0x7e,0x18,0x18,0x00,0x7e,0x00, + 0x30,0x18,0x0c,0x18,0x30,0x00,0x7e,0x00, + 0x0c,0x18,0x30,0x18,0x0c,0x00,0x7e,0x00, + 0x0e,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70, + 0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00, + 0x00,0x3b,0x6e,0x00,0x3b,0x6e,0x00,0x00, + 0x1c,0x36,0x36,0x1c,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x0f,0x0c,0x0c,0x0c,0xec,0x6c,0x3c,0x1c, + 0x78,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00, + 0x70,0x18,0x30,0x60,0x78,0x00,0x00,0x00, + 0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + diff --git a/src/target/firmware/display/ssd1783.c b/src/target/firmware/display/ssd1783.c new file mode 100644 index 00000000..5696b48f --- /dev/null +++ b/src/target/firmware/display/ssd1783.c @@ -0,0 +1,257 @@ +/* Solomon SSD1783 LCD Driver (Epson S1D15G10D08B000 clone) */ + +/* (C) 2010 by Steve Markgraf <steve@steve-m.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +//#define DEBUG +#include <debug.h> +#include <delay.h> +#include <uwire.h> +#include <display.h> +#include <display/ssd1783.h> +#include <calypso/clock.h> + +#define LCD_COLUMNS 98 +#define LCD_ROWS 67 +#define LCD_TOP_FREE_ROWS 3 +#define LCD_LEFT_FREE_COLS 0 +#define PIXEL_BYTES 3 +#define SSD1783_UWIRE_BITLEN 9 +#define SSD1783_DEV_ID 0 +#define FONT_HEIGHT 8 +#define FONT_WIDTH 8 + +static const uint8_t rgb8_palette[] ={ + 0x00, //P01 Intermediate red tone 000 + 0x03, //P02 Intermediate red tone 001 + 0x05, //P03 Intermediate red tone 010 + 0x07, //P04 Intermediate red tone 011 + 0x09, //P05 Intermediate red tone 100 + 0x0b, //P06 Intermediate red tone 101 + 0x0d, //P07 Intermediate red tone 110 + 0x0f, //P08 Intermediate red tone 111 + 0x00, //P09 Intermediate green tone 000 + 0x03, //P10 Intermediate green tone 001 + 0x05, //P11 Intermediate green tone 010 + 0x07, //P12 Intermediate green tone 011 + 0x09, //P13 Intermediate green tone 100 + 0x0b, //P14 Intermediate green tone 101 + 0x0d, //P15 Intermediate green tone 110 + 0x0f, //P16 Intermediate green tone 111 + 0x00, //P17 Intermediate blue tone 00 + 0x05, //P18 Intermediate blue tone 01 + 0x0a, //P19 Intermediate blue tone 10 + 0x0f, //P20 Intermediate blue tone 11 +}; + +static void ssd1783_cmd_write(const uint8_t cmd) +{ + uint16_t cmd_out = cmd; + uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &cmd_out, NULL); +} + +static void ssd1783_data_write(const uint8_t data) +{ + uint16_t data_out = ((0x01 << 8) + data); + uwire_xfer(SSD1783_DEV_ID, SSD1783_UWIRE_BITLEN, &data_out, NULL); +} + +static void ssd1783_clrscr(void) +{ + uint16_t i; + + /* Select the whole display area for clearing */ + ssd1783_cmd_write(CMD_PASET); /* Page address set [2] */ + ssd1783_data_write(0x00); /* Start page: 0x00 */ + ssd1783_data_write(LCD_ROWS-1); /* End page */ + ssd1783_cmd_write(CMD_CASET); /* Column address set [2] */ + ssd1783_data_write(0x00); /* Start column: 0x00 */ + ssd1783_data_write((LCD_COLUMNS/2)-1); /* End column (2 pixels per column) */ + ssd1783_cmd_write(CMD_RAMWR); /* Write to memory */ + + /* Fill the display with white */ + for(i=0; i < (LCD_ROWS * (LCD_COLUMNS/2) * PIXEL_BYTES); i++){ + ssd1783_data_write(0xff); + } + ssd1783_cmd_write(CMD_NOP); /* Terminate RAMWR with NOP */ +} + +static void ssd1783_init(void) +{ + unsigned int i; + + calypso_reset_set(RESET_EXT, 0); + uwire_init(); + delay_ms(3); + + /* Begin SSD1783 initialization sequence */ + ssd1783_cmd_write(CMD_OSCON); /* Internal OSC on */ + ssd1783_cmd_write(CMD_SLPOUT); /* Sleep out (Leave sleep mode) */ + + ssd1783_cmd_write(CMD_COMSCN); /* Common scan direction [1] */ + ssd1783_data_write(0x01); /* Scan 1 -> 68, 132 <- 69 */ + ssd1783_cmd_write(CMD_DATCTL); /* Data Scan Direction [3] */ + ssd1783_data_write(0x00); /* Normal page address, normal rotation, + * scan direction in column direction */ + ssd1783_data_write(0x00); /* RGB arrangement: RGB-RGB */ + ssd1783_data_write(0x02); /* Gray-scale setup: 16 gray-scale Type A, 8-bit mode */ + + /* Initialize RGB8 palette for 8-Bit color mode */ + ssd1783_cmd_write(CMD_RGBSET8); /* 256-color position set [20] */ + for(i=0; i < sizeof(rgb8_palette); i++){ + ssd1783_data_write(rgb8_palette[i]); + } + + ssd1783_cmd_write(CMD_DISCTL); /* Display control [3] */ + ssd1783_data_write(0xff); /* no clock division, F1, F2 switching period = field */ + ssd1783_data_write(0x10); /* Drive duty, P24 = 1 */ + ssd1783_data_write(0x01); /* FR inverse set, P30=1 */ + ssd1783_cmd_write(CMD_SCSTART); /* Scroll start set [1] */ + ssd1783_data_write(0x00); /* Start block address 0x00 */ + + /* Turn on the power regulator which generates VLCD */ + ssd1783_cmd_write(CMD_PWRCTR); /* Power Control [1] */ + ssd1783_data_write(0x0b); /* Booster, follower and regulator circuit on */ + + /* FIXME: put this in a separate function (ssd1783_set_contrast) */ + ssd1783_cmd_write(CMD_VOLCTR); /* Electronic Volume Control [2] */ + ssd1783_data_write(0x29); /* Set contrast */ + ssd1783_data_write(0x05); /* Set contrast */ + + ssd1783_cmd_write(CMD_DISINV); /* Invert Display */ + ssd1783_cmd_write(CMD_TMPGRD); /* Temperature gradient set */ + ssd1783_data_write(0x00); /* default temperature gradient (-0.05% / °C) */ + ssd1783_cmd_write(CMD_BIASSET); /* Set biasing ratio [1] */ + ssd1783_data_write(0x03); /* 1/10 bias */ + ssd1783_cmd_write(CMD_FREQSET); /* Set frequency and n-line inversion [2] */ + ssd1783_data_write(0x08); /* frequency: 75Hz (POR) */ + ssd1783_data_write(0x06); /* n-line inversion: 6 lines */ + ssd1783_cmd_write(CMD_RESCMD); /* reserved command in datasheet? */ + ssd1783_cmd_write(CMD_PWMSEL); /* Select PWM/FRC, Full/8 color mode [3] */ + ssd1783_data_write(0x28); /* fixed */ + ssd1783_data_write(0x2c); /* 5 bits PWM + 1 bit FRC (POR) */ + ssd1783_data_write(0x05); /* Full color mode (0x45 would be 8 color powersaving) */ + + ssd1783_cmd_write(CMD_DISON); /* Display ON */ + ssd1783_clrscr(); /* Clear the display */ +} + +extern const unsigned char fontdata_r8x8_horiz[]; + +/* + * Pixel format for 8-bit mode, 12-bit color, 2 Pixel per 3 byte + * D7, D6, D5, D4, D3, D2, D1, D0: RRRRGGGG (8 bits) 1st write + * D7, D6, D5, D4, D3, D2, D1, D0: BBBBRRRR (8 bits) 2nd write + * D7, D6, D5, D4, D3, D2, D1, D0: GGGGBBBB (8 bits) 3rd write +*/ + +static void ssd1783_goto_xy(int xpos, int ypos) +{ + ssd1783_cmd_write(CMD_PASET); + ssd1783_data_write(xpos); + ssd1783_data_write(xpos + (FONT_HEIGHT-1)); + + ssd1783_cmd_write(CMD_CASET); + ssd1783_data_write(ypos); + ssd1783_data_write(ypos + ((FONT_WIDTH/2)-1)); + + ssd1783_cmd_write(CMD_NOP); +} + +static int ssd1783_putc_col(unsigned char c, int fColor, int bColor) +{ + int i, j; + uint8_t cols = FONT_WIDTH; + uint8_t rows = FONT_HEIGHT; + uint8_t row_slice; + uint8_t rowmask; + uint16_t pixel0; /* left pixel */ + uint16_t pixel1; /* right pixel */ + + ssd1783_cmd_write(CMD_RAMWR); + + for (i = 0; i < rows; i++) { + row_slice = fontdata_r8x8_horiz[(FONT_WIDTH * c)+i]; + printd("\nSSD1783 FontData=0x%02hx", row_slice); + rowmask = 0x80; + for (j = 0; j < cols; j += 2) { + if (!(row_slice & rowmask)) + pixel0 = bColor; + else + pixel0 = fColor; + rowmask = rowmask >> 1; + if (!(row_slice & rowmask)) + pixel1 = bColor; + else + pixel1 = fColor; + rowmask = rowmask >> 1; + /* Write the RGB-RGB pixel data */ + ssd1783_data_write((pixel0 >> 4) & 0xff); + ssd1783_data_write(((pixel0 & 0x00f) << 4) | ((pixel1 >> 8) & 0x00f)); + ssd1783_data_write(pixel1 & 0xff); + } + } + ssd1783_cmd_write(CMD_NOP); + + return c; +} + +static int ssd1783_puts_col(const char *str, int txtline, int fColor, int bColor) +{ + int i; + for (i = 0; *str != 0x00; i += (FONT_WIDTH/2)) { + ssd1783_goto_xy(((txtline*FONT_HEIGHT)+LCD_TOP_FREE_ROWS), + (i + LCD_LEFT_FREE_COLS)); + ssd1783_putc_col(*str++, fColor, bColor); + } + + return 0; +} + +/* interface to display driver core */ + +static void ssd1783_set_attr(unsigned long attr) +{ + /* FIXME */ +} + +static int ssd1783_putc(unsigned int c) +{ + return ssd1783_putc_col(c, BLACK, WHITE); +} + +static int ssd1783_puts(const char *str) +{ + return ssd1783_puts_col(str, 0, BLACK, WHITE); +} + +const struct display_driver ssd1783_display = { + .name = "ssd1783", + .init = &ssd1783_init, + .set_attr = &ssd1783_set_attr, + .unset_attr = &ssd1783_set_attr, + .clrscr = &ssd1783_clrscr, + .goto_xy = &ssd1783_goto_xy, + .putc = &ssd1783_putc, + .puts = &ssd1783_puts, +}; diff --git a/src/target/firmware/display/st7558.c b/src/target/firmware/display/st7558.c new file mode 100644 index 00000000..baed9eb0 --- /dev/null +++ b/src/target/firmware/display/st7558.c @@ -0,0 +1,123 @@ +/* Sitronix ST7558 LCD Driver */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <delay.h> +#include <memory.h> +#include <i2c.h> +#include <display.h> +#include <calypso/clock.h> + +#define MORE_CONTROL 0x80 +#define CONTROL_RS_RAM 0x40 +#define CONTROL_RS_CMD 0x00 +#define Y_ADDR(n) (0x40|((n)&0xf)) +#define X_ADDR(n) (0x80|((n)&0x3f)) + +static const uint8_t setup[] = { CONTROL_RS_CMD, 0x2e, 0x21, 0x12, 0xc0, 0x0b, 0x20, 0x11, 0x00, 0x40, 0x80 }; +static const uint8_t home[] = { CONTROL_RS_CMD, Y_ADDR(0), X_ADDR(0) }; + +/* video modes */ +static const uint8_t invert[] = { CONTROL_RS_CMD, 0x20, 0x0d }; +static const uint8_t normal[] = { CONTROL_RS_CMD, 0x20, 0x0c }; +static const uint8_t off[] = { CONTROL_RS_CMD, 0x20, 0x08 }; + +#define ST7558_SLAVE_ADDR 0x3c +static int st7558_write(const uint8_t *data, int len) +{ + int rc = i2c_write(ST7558_SLAVE_ADDR, data[0], 1, data+1, len-1); + /* FIXME: find out why this is needed! */ + delay_ms(10); + return rc; +} + +static const uint8_t zero16[] = { CONTROL_RS_RAM, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; +static void st7558_clrscr(void) +{ + int i; + + st7558_write(home, sizeof(home)); + + for (i = 0; i < 102*9; i += 16) + st7558_write(zero16, sizeof(zero16)); + + st7558_write(home, sizeof(home)); +} + +static void st7558_init(void) +{ + /* Release nRESET */ + calypso_reset_set(RESET_EXT, 0); + + i2c_init(0,0); + delay_ms(10); + + st7558_write(setup, sizeof(setup)); + st7558_clrscr(); +} + +static void st7558_set_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(invert, sizeof(invert)); +} + +static void st7558_unset_attr(unsigned long attr) +{ + if (attr & DISP_ATTR_INVERT) + st7558_write(normal, sizeof(normal)); +} + +/* FIXME: we need a mini-libc */ +static void *mcpy(uint8_t *dst, const uint8_t *src, int len) +{ + while (len--) + *dst++ = *src++; + + return dst; +} + +extern const unsigned char fontdata_r8x8[]; + +static void st7558_putc(unsigned char c) +{ + uint8_t putc_buf[16]; + uint8_t bytes_per_char = 8; + + putc_buf[0] = CONTROL_RS_RAM; + mcpy(putc_buf+1, fontdata_r8x8+(c*bytes_per_char), bytes_per_char); + st7558_write(putc_buf, 1+bytes_per_char); +} + +const struct display_driver st7558_display = { + .name = "st7558", + .init = &st7558_init, + .clrscr = &st7558_clrscr, + .set_attr = &st7558_set_attr, + .unset_attr = &st7558_unset_attr, + .putc = &st7558_putc, +}; diff --git a/src/target/firmware/flash/cfi_flash.c b/src/target/firmware/flash/cfi_flash.c new file mode 100644 index 00000000..a269142f --- /dev/null +++ b/src/target/firmware/flash/cfi_flash.c @@ -0,0 +1,436 @@ +/* NOR Flash Driver for Intel 28F160C3 NOR flash */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <debug.h> +#include <stdio.h> +#include <stdint.h> +#include <memory.h> +#include <cfi_flash.h> + +/* XXX: memdump_range() */ +#include <calypso/misc.h> + +enum flash_cmd { + FLASH_CMD_RESET = 0xff, + FLASH_CMD_READ_ID = 0x90, + FLASH_CMD_CFI = 0x98, + FLASH_CMD_READ_STATUS = 0x70, + FLASH_CMD_CLEAR_STATUS = 0x50, + FLASH_CMD_WRITE = 0x40, + FLASH_CMD_BLOCK_ERASE = 0x20, + FLASH_CMD_ERASE_CONFIRM = 0xD0, + FLASH_CMD_PROTECT = 0x60, +}; + +enum flash_prot_cmd { + FLASH_PROT_LOCK = 0x01, + FLASH_PROT_UNLOCK = 0xD0, + FLASH_PROT_LOCKDOWN = 0x2F +}; + +enum flash_offset { + FLASH_OFFSET_MANUFACTURER_ID = 0x00, + FLASH_OFFSET_DEVICE_ID = 0x01, + FLASH_OFFSET_INTEL_PROTECTION = 0x81, + FLASH_OFFSET_CFI_RESP = 0x10 +}; + +enum flash_block_offset { + FLASH_OFFSET_BLOCK_LOCKSTATE = 0x02 +}; + +enum flash_status { + FLASH_STATUS_READY = 0x80, + FLASH_STATUS_ERASE_SUSPENDED = 0x40, + FLASH_STATUS_ERASE_ERROR = 0x20, + FLASH_STATUS_PROGRAM_ERROR = 0x10, + FLASH_STATUS_VPP_LOW = 0x08, + FLASH_STATUS_PROGRAM_SUSPENDED = 0x04, + FLASH_STATUS_LOCKED_ERROR = 0x02, + FLASH_STATUS_RESERVED = 0x01 +}; + +static inline void flash_write_cmd(const void *base_addr, uint16_t cmd) +{ + writew(cmd, base_addr); +} + +static inline uint16_t flash_read16(const void *base_addr, uint32_t offset) +{ + return readw(base_addr + (offset << 1)); +} + +static char flash_protected(uint32_t block_offset) { + return block_offset < 64*1024; +} + +uint8_t flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + uint8_t lockstate; + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + lockstate = flash_read16(base_addr, block_offset + FLASH_OFFSET_BLOCK_LOCKSTATE); + flash_write_cmd(base_addr, FLASH_CMD_RESET); + return lockstate; +} + +void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Unlocking block at 0x%08x\n", block_offset); + + if(flash_protected(block_offset)) { + puts("error: block is soft-protected\n"); + return; + } + + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_UNLOCK); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Locking block at 0x%08x\n", block_offset); + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCK); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Locking down block at 0x%08x\n", block_offset); + flash_write_cmd(base_addr, FLASH_CMD_PROTECT); + flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCKDOWN); + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_block_erase(cfi_flash_t *flash, uint32_t block_offset) { + const void *base_addr = flash->f_base; + printf("Erasing block 0x%08x...", block_offset); + + if(flash_protected(block_offset)) { + puts("error: block is soft-protected\n"); + return; + } + + void *block_addr = ((uint8_t*)base_addr) + block_offset; + + flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS); + + flash_write_cmd(block_addr, FLASH_CMD_BLOCK_ERASE); + flash_write_cmd(block_addr, FLASH_CMD_ERASE_CONFIRM); + + flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while(!(status&FLASH_STATUS_READY)); + + if(status&FLASH_STATUS_ERASE_ERROR) { + puts("error: "); + if(status&FLASH_STATUS_VPP_LOW) { + puts("vpp insufficient\n"); + } + if(status&FLASH_STATUS_LOCKED_ERROR) { + puts("block is lock-protected\n"); + } + } else { + puts("done\n"); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) { + const void *base_addr = flash->f_base; + uint32_t i; + + printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src); + + if(dst%2) { + puts("error: unaligned destination\n"); + return; + } + + if(nbytes%2) { + puts("error: unaligned count\n"); + return; + } + + if(flash_protected(dst)) { + puts("error: block is soft-protected\n"); + return; + } + + flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS); + + puts("writing..."); + for(i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t*)(src + i); + uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i); + + uint16_t data = *src_addr; + + flash_write_cmd(dst_addr, FLASH_CMD_WRITE); + flash_write_cmd(dst_addr, data); + + flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS); + uint16_t status; + do { + status = flash_read16(base_addr, 0); + } while(!(status&FLASH_STATUS_READY)); + + if(status&FLASH_STATUS_PROGRAM_ERROR) { + puts("error: "); + if(status&FLASH_STATUS_VPP_LOW) { + puts("vpp insufficient"); + } + if(status&FLASH_STATUS_LOCKED_ERROR) { + puts("block is lock-protected"); + } + goto err_reset; + } + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); + + puts("verifying..."); + for(i = 0; i < nbytes; i += 2) { + uint16_t *src_addr = (uint16_t*)(src + i); + uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i); + if(*src_addr != *dst_addr) { + puts("error: verification failed"); + goto err; + } + } + + puts("done\n"); + + return; + + err_reset: + flash_write_cmd(base_addr, FLASH_CMD_RESET); + + err: + printf(" at offset 0x%x\n", i); +} + +typedef void (*flash_block_cb_t)(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size); + +void flash_iterate_blocks(cfi_flash_t *flash, struct cfi_query *qry, + uint32_t start_offset, uint32_t end_offset, + flash_block_cb_t callback) +{ + int region, block; + + uint32_t block_start = 0; + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + for(block = 0; block < actual_count; block++) { + uint32_t block_end = block_start + actual_size; + if(block_start >= start_offset && block_end-1 <= end_offset) { + callback(flash, block_start, actual_size); + } + block_start = block_end; + } + } +} + +static void get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) { + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + + *manufacturer_id = flash_read16(base_addr, FLASH_OFFSET_MANUFACTURER_ID); + *device_id = flash_read16(base_addr, FLASH_OFFSET_DEVICE_ID); + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void get_query(void *base_addr, struct cfi_query *query) { + unsigned int i; + + flash_write_cmd(base_addr, FLASH_CMD_CFI); + + for(i = 0; i < sizeof(struct cfi_query); i++) { + uint16_t byte = flash_read16(base_addr, FLASH_OFFSET_CFI_RESP+i); + *(((unsigned char*)query)+i) = byte; + } + + if(query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') { + puts("Error: CFI query signature not found\n"); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_query(void *base_addr, struct cfi_query *query) { + unsigned int i; + + flash_write_cmd(base_addr, FLASH_CMD_CFI); + + for(i = 0; i < sizeof(struct cfi_query); i++) { + uint8_t byte = *(((uint8_t*)query)+i); + printf("%04X: %02X\n", FLASH_OFFSET_CFI_RESP+i, byte); + } + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_layout(void *base_addr, const struct cfi_query *qry) { + int region; + + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + printf("Region of 0x%04x times 0x%6x bytes\n", actual_count, + actual_size); + } + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_locks(void *base_addr, const struct cfi_query *qry) { + int region, block; + + uint32_t block_addr = 0; + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + for(region = 0; region < qry->num_erase_regions; region++) { + uint16_t actual_count = qry->erase_regions[region].b_count + 1; + uint32_t actual_size = qry->erase_regions[region].b_size * 256; + for(block = 0; block < actual_count; block++) { + uint8_t lock = flash_read16(base_addr, block_addr+2); + printf("Block 0x%08x lock 0x%02x\n", block_addr*2, lock); + block_addr += actual_size / 2; + } + } + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_protection(void *base_addr) { + flash_write_cmd(base_addr, FLASH_CMD_READ_ID); + + uint16_t lock = flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION); + printf("Protection Lock: 0x%04x\n", lock); + + puts("Protection Data: "); + int i; + for(i = 0; i < 8; i++) { + printf("%04x", flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION + 1 + i)); + } + putchar('\n'); + + flash_write_cmd(base_addr, FLASH_CMD_RESET); +} + +static void dump_timing(void *base_addr, struct cfi_query *qry) { + uint32_t block_erase_typ = 1<<qry->block_erase_timeout_typ; + uint32_t block_erase_max = (1<<qry->block_erase_timeout_max) * block_erase_typ; + uint32_t word_program_typ = 1<<qry->word_write_timeout_typ; + uint32_t word_program_max = (1<<qry->word_write_timeout_max) * word_program_typ; + printf("Block Erase Typical: %u ms\n", block_erase_typ); + printf("Block Erase Maximum: %u ms\n", block_erase_max); + printf("Word Program Typical: %u us\n", word_program_typ); + printf("Word Program Maximum: %u us\n", word_program_max); +} + +static void dump_algorithms(void *base_addr, struct cfi_query *qry) { + printf("Primary Algorithm ID: %04x\n", qry->p_id); + printf("Primary Extended Query: %04x\n", qry->p_adr); + + printf("Alternate Algorithm ID: %04x\n", qry->a_id); + printf("Alternate Extended Query: %04x\n", qry->a_adr); +} + +void +lockdown_block_cb(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size) +{ + flash_block_lockdown(flash, block_offset); +} + +void +print_block_cb(cfi_flash_t *flash, + uint32_t block_offset, + uint32_t block_size) +{ + printf("%08x size %08x\n", block_offset, block_size); +} + +void flash_dump_info(cfi_flash_t *flash) { + void *base_addr = flash->f_base; + struct cfi_query *qry = &flash->f_query; + + printf("Flash Manufacturer ID: %04x\n", flash->f_manuf_id); + printf("Flash Device ID: %04x\n", flash->f_dev_id); + + printf("Flash Size: 0x%08x bytes\n", flash->f_size); + + dump_algorithms(base_addr, qry); + + dump_timing(base_addr, qry); + + dump_protection(base_addr); + + dump_layout(base_addr, qry); + + dump_locks(base_addr, qry); +} + +void flash_init(cfi_flash_t *flash, void *base_addr) { + printd("Initializing CFI flash at 0x%p\n", base_addr); + + flash->f_base = base_addr; + + get_id(base_addr, &flash->f_manuf_id, &flash->f_dev_id); + + get_query(base_addr, &flash->f_query); + + flash->f_size = 1<<flash->f_query.dev_size; +} + +void flash_test() { + /* block iterator test */ +#if 0 + flash_iterate_blocks(flash, qry, 0x0000, 0xFFFF, &lockdown_block_cb); +#endif + + /* programming test */ +#if 0 + static uint8_t magic[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF}; + + memdump_range(&magic, sizeof(magic)); + +#if 0 +#define ADDR 0x001E0000 + flash_block_unlock(flash, ADDR); + memdump_range(ADDR, 16); + flash_block_erase(flash, ADDR); + memdump_range(ADDR, 16); + flash_program(flash, ADDR, &magic, sizeof(magic)); + memdump_range(ADDR, 16); +#undef ADDR +#endif + +#endif +} diff --git a/src/target/firmware/include/abb/twl3025.h b/src/target/firmware/include/abb/twl3025.h new file mode 100644 index 00000000..2cd35a58 --- /dev/null +++ b/src/target/firmware/include/abb/twl3025.h @@ -0,0 +1,136 @@ +#ifndef _TWL3025_H +#define _TWL3025_H + +#define PAGE(n) (n << 7) +enum twl3025_reg { + VRPCCFG = PAGE(1) | 30, + VRPCDEV = PAGE(0) | 30, + VRPCMSK = PAGE(1) | 31, + VRPCMSKABB = PAGE(1) | 29, + VRPCSTS = PAGE(0) | 31, + /* Monitoring ADC Registers */ + MADCTRL = PAGE(0) | 13, + MADCSTAT = PAGE(0) | 24, + VBATREG = PAGE(0) | 15, + VCHGREG = PAGE(0) | 16, + ICHGREG = PAGE(0) | 17, + VBKPREG = PAGE(0) | 18, + ADIN1REG = PAGE(0) | 19, + ADIN2REG = PAGE(0) | 20, + ADIN3REG = PAGE(0) | 21, + ADIN4REG = PAGE(0) | 22, + /* Clock Generator Registers */ + TOGBR1 = PAGE(0) | 4, + TOGBR2 = PAGE(0) | 5, + PWDNRG = PAGE(1) | 9, + TAPCTRL = PAGE(1) | 19, + TAPREG = PAGE(1) | 20, + /* Automatic Frequency Control (AFC) Registers */ + AUXAFC1 = PAGE(0) | 7, + AUXAFC2 = PAGE(0) | 8, + AFCCTLADD = PAGE(1) | 21, + AFCOUT = PAGE(1) | 22, + /* Automatic Power Control (APC) Registers */ + APCDEL1 = PAGE(0) | 2, + APCDEL2 = PAGE(1) | 26, + AUXAPC = PAGE(0) | 9, + APCRAM = PAGE(0) | 10, + APCOFF = PAGE(0) | 11, + APCOUT = PAGE(1) | 12, + /* Auxiliary DAC Control Register */ + AUXDAC = PAGE(0) | 12, + /* SimCard Control Register */ + VRPCSIM = PAGE(1) | 23, + /* LED Driver Register */ + AUXLED = PAGE(1) | 24, + /* Battery Charger Interface (BCI) Registers */ + CHGREG = PAGE(0) | 25, + BCICTL1 = PAGE(0) | 28, + BCICTL2 = PAGE(0) | 29, + BCICONF = PAGE(1) | 13, + /* Interrupt and Bus Control (IBIC) Registers */ + ITMASK = PAGE(0) | 28, + ITSTATREG = PAGE(0) | 27, /* both pages! */ + PAGEREG = PAGE(0) | 1, /* both pages! */ + /* Baseband Codec (BBC) Registers */ + BULIOFF = PAGE(1) | 2, + BULQOFF = PAGE(1) | 3, + BULIDAC = PAGE(1) | 5, + BULQDAC = PAGE(1) | 4, + BULGCAL = PAGE(1) | 14, + BULDATA1 = PAGE(0) | 3, /* 16 words */ + BBCTRL = PAGE(1) | 6, + /* Voiceband Codec (VBC) Registers */ + VBCTRL1 = PAGE(1) | 8, + VBCTRL2 = PAGE(1) | 11, + VBPOP = PAGE(1) | 10, + VBUCTRL = PAGE(1) | 7, + VBDCTRL = PAGE(0) | 6, +}; +#define BULDATA2 BULDATA1 + +enum togbr2_bits { + TOGBR2_KEEPR = (1 << 0), /* Clear KEEPON bit */ + TOGBR2_KEEPS = (1 << 1), /* Set KEEPON bit */ + TOGBR2_ACTR = (1 << 2), /* Dectivate MCLK */ + TOGBR2_ACTS = (1 << 3), /* Activate MCLK */ + TOGBR2_IBUFPTR1 = (1 << 4), /* Initialize pointer of burst buffer 1 */ + TOGBR2_IBUFPTR2 = (1 << 5), /* Initialize pointer of burst buffer 2 */ + TOGBR2_IAPCPTR = (1 << 6), /* Initialize pointer of APC RAM */ +}; + +/* How a RAMP value is encoded */ +#define ABB_RAMP_VAL(up, down) ( ((down & 0x1F) << 5) | (up & 0x1F) ) + +enum twl3025_unit { + TWL3025_UNIT_AFC, + TWL3025_UNIT_MAD, + TWL3025_UNIT_ADA, + TWL3025_UNIT_VDL, + TWL3025_UNIT_VUL, +}; + +void twl3025_init(void); +void twl3025_reg_write(uint8_t reg, uint16_t data); +uint16_t twl3025_reg_read(uint8_t reg); + +void twl3025_power_off(void); + +void twl3025_clk13m(int enable); + +void twl3025_unit_enable(enum twl3025_unit unit, int on); + +enum twl3025_tsp_bits { + BULON = 0x80, + BULCAL = 0x40, + BULENA = 0x20, + BDLON = 0x10, + BDLCAL = 0x08, + BDLENA = 0x04, + STARTADC = 0x02, +}; + +extern const uint16_t twl3025_default_ramp[16]; + +/* Enqueue a TSP signal change via the TPU */ +void twl3025_tsp_write(uint8_t data); + +/* Enqueue a series of TSP commands in the TPU to (de)activate the downlink path */ +void twl3025_downlink(int on, int16_t at); + +/* Enqueue a series of TSP commands in the TPU to (de)activate the uplink path */ +void twl3025_uplink(int on, int16_t at); + +/* Update the AFC DAC value */ +void twl3025_afc_set(int16_t val); + +/* Get the AFC DAC value */ +int16_t twl3025_afc_get(void); + +/* Get the AFC DAC output value */ +uint8_t twl3025_afcout_get(void); + +/* Force a certain static AFC DAC output value */ +void twl3025_afcout_set(uint8_t val); + +#endif diff --git a/src/target/firmware/include/arm.h b/src/target/firmware/include/arm.h new file mode 100644 index 00000000..272c9c39 --- /dev/null +++ b/src/target/firmware/include/arm.h @@ -0,0 +1,7 @@ +#ifndef _ARM_H +#define _ARM_H + +void arm_enable_interrupts(void); +int arm_disable_interrupts(void); + +#endif diff --git a/src/target/firmware/include/arpa/inet.h b/src/target/firmware/include/arpa/inet.h new file mode 100644 index 00000000..9a4dd5c9 --- /dev/null +++ b/src/target/firmware/include/arpa/inet.h @@ -0,0 +1,2 @@ +/* we have this to make sure libosmocore uses our version of ntohl/htons */ +#include <byteorder.h> diff --git a/src/target/firmware/include/asm/assembler.h b/src/target/firmware/include/asm/assembler.h new file mode 100644 index 00000000..cd03e98d --- /dev/null +++ b/src/target/firmware/include/asm/assembler.h @@ -0,0 +1,113 @@ +/* + * linux/include/asm-arm/assembler.h + * + * Copyright (C) 1996-2000 Russell King + * + * 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 file contains arm architecture specific defines + * for the different processors. + * + * Do not include any C declarations in this file - it is included by + * assembler source. + */ +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#include <asm/ptrace.h> + +/* + * Endian independent macros for shifting bytes within registers. + */ +#ifndef __ARMEB__ +#define pull lsr +#define push lsl +#define get_byte_0 lsl #0 +#define get_byte_1 lsr #8 +#define get_byte_2 lsr #16 +#define get_byte_3 lsr #24 +#define put_byte_0 lsl #0 +#define put_byte_1 lsl #8 +#define put_byte_2 lsl #16 +#define put_byte_3 lsl #24 +#else +#define pull lsl +#define push lsr +#define get_byte_0 lsr #24 +#define get_byte_1 lsr #16 +#define get_byte_2 lsr #8 +#define get_byte_3 lsl #0 +#define put_byte_0 lsl #24 +#define put_byte_1 lsl #16 +#define put_byte_2 lsl #8 +#define put_byte_3 lsl #0 +#endif + +#define PLD(code...) + +#define MODE_USR USR_MODE +#define MODE_FIQ FIQ_MODE +#define MODE_IRQ IRQ_MODE +#define MODE_SVC SVC_MODE + +#define DEFAULT_FIQ MODE_FIQ + +/* + * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc}) + */ +#ifdef __STDC__ +#define LOADREGS(cond, base, reglist...)\ + ldm##cond base,reglist +#else +#define LOADREGS(cond, base, reglist...)\ + ldm/**/cond base,reglist +#endif + +/* + * Build a return instruction for this processor type. + */ +#define RETINSTR(instr, regs...)\ + instr regs + +/* + * Enable and disable interrupts + */ + .macro disable_irq + msr cpsr_c, #PSR_I_BIT | SVC_MODE + .endm + + .macro enable_irq + msr cpsr_c, #SVC_MODE + .endm + +/* + * Save the current IRQ state and disable IRQs. Note that this macro + * assumes FIQs are enabled, and that the processor is in SVC mode. + */ + .macro save_and_disable_irqs, oldcpsr + mrs \oldcpsr, cpsr + disable_irq + .endm + +/* + * Restore interrupt state previously stored in a register. We don't + * guarantee that this will preserve the flags. + */ + .macro restore_irqs, oldcpsr + msr cpsr_c, \oldcpsr + .endm + +/* + * These two are used to save LR/restore PC over a user-based access. + * The old 26-bit architecture requires that we do. On 32-bit + * architecture, we can safely ignore this requirement. + */ + .macro save_lr + .endm + + .macro restore_pc + mov pc, lr + .endm diff --git a/src/target/firmware/include/asm/atomic.h b/src/target/firmware/include/asm/atomic.h new file mode 100644 index 00000000..19e8ce6f --- /dev/null +++ b/src/target/firmware/include/asm/atomic.h @@ -0,0 +1,106 @@ +/* + * linux/include/asm-arm/atomic.h + * + * Copyright (C) 1996 Russell King. + * Copyright (C) 2002 Deep Blue Solutions Ltd. + * + * 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. + */ +#ifndef __ASM_ARM_ATOMIC_H +#define __ASM_ARM_ATOMIC_H + +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +#define atomic_read(v) ((v)->counter) + +#include <asm/system.h> +#include <asm/compiler.h> + +#define atomic_set(v,i) (((v)->counter) = (i)) + +static inline int atomic_add_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val += i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long flags; + int val; + + local_irq_save(flags); + val = v->counter; + v->counter = val -= i; + local_irq_restore(flags); + + return val; +} + +static inline int atomic_cmpxchg(atomic_t *v, int old, int new) +{ + int ret; + unsigned long flags; + + local_irq_save(flags); + ret = v->counter; + if (likely(ret == old)) + v->counter = new; + local_irq_restore(flags); + + return ret; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned long flags; + + local_irq_save(flags); + *addr &= ~mask; + local_irq_restore(flags); +} + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +static inline int atomic_add_unless(atomic_t *v, int a, int u) +{ + int c, old; + + c = atomic_read(v); + while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c) + c = old; + return c != u; +} +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +#define atomic_add(i, v) (void) atomic_add_return(i, v) +#define atomic_inc(v) (void) atomic_add_return(1, v) +#define atomic_sub(i, v) (void) atomic_sub_return(i, v) +#define atomic_dec(v) (void) atomic_sub_return(1, v) + +#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) +#define atomic_inc_return(v) (atomic_add_return(1, v)) +#define atomic_dec_return(v) (atomic_sub_return(1, v)) +#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) + +#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) + +/* Atomic operations are already serializing on ARM */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif diff --git a/src/target/firmware/include/asm/bitops.h b/src/target/firmware/include/asm/bitops.h new file mode 100644 index 00000000..337d800d --- /dev/null +++ b/src/target/firmware/include/asm/bitops.h @@ -0,0 +1,225 @@ +/* + * Copyright 1995, Russell King. + * Various bits and pieces copyrights include: + * Linus Torvalds (test_bit). + * Big endian support: Copyright 2001, Nicolas Pitre + * reworked by rmk. + * + * bit 0 is the LSB of an "unsigned long" quantity. + * + * Please note that the code in this file should never be included + * from user space. Many of these are not implemented in assembler + * since they would be too costly. Also, they require privileged + * instructions (which are not available from user mode) to ensure + * that they are atomic. + */ + +#ifndef __ASM_ARM_BITOPS_H +#define __ASM_ARM_BITOPS_H + +#include <asm/system.h> + +#define smp_mb__before_clear_bit() mb() +#define smp_mb__after_clear_bit() mb() + +/* + * These functions are the basis of our bit ops. + * + * First, the atomic bitops. These use native endian. + */ +static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p |= mask; + local_irq_restore(flags); +} + +static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p &= ~mask; + local_irq_restore(flags); +} + +static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + *p ^= mask; + local_irq_restore(flags); +} + +static inline int +____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res | mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res & ~mask; + local_irq_restore(flags); + + return res & mask; +} + +static inline int +____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p) +{ + unsigned long flags; + unsigned int res; + unsigned long mask = 1UL << (bit & 31); + + p += bit >> 5; + + local_irq_save(flags); + res = *p; + *p = res ^ mask; + local_irq_restore(flags); + + return res & mask; +} + +//#include <asm-generic/bitops/non-atomic.h> + +/* + * A note about Endian-ness. + * ------------------------- + * + * When the ARM is put into big endian mode via CR15, the processor + * merely swaps the order of bytes within words, thus: + * + * ------------ physical data bus bits ----------- + * D31 ... D24 D23 ... D16 D15 ... D8 D7 ... D0 + * little byte 3 byte 2 byte 1 byte 0 + * big byte 0 byte 1 byte 2 byte 3 + * + * This means that reading a 32-bit word at address 0 returns the same + * value irrespective of the endian mode bit. + * + * Peripheral devices should be connected with the data bus reversed in + * "Big Endian" mode. ARM Application Note 61 is applicable, and is + * available from http://www.arm.com/. + * + * The following assumes that the data bus connectivity for big endian + * mode has been followed. + * + * Note that bit 0 is defined to be 32-bit word bit 0, not byte 0 bit 0. + */ + +/* + * Little endian assembly bitops. nr = 0 -> byte 0 bit 0. + */ +extern void _set_bit_le(int nr, volatile unsigned long * p); +extern void _clear_bit_le(int nr, volatile unsigned long * p); +extern void _change_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_le(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_le(const void * p, unsigned size); +extern int _find_next_zero_bit_le(const void * p, int size, int offset); +extern int _find_first_bit_le(const unsigned long *p, unsigned size); +extern int _find_next_bit_le(const unsigned long *p, int size, int offset); + +/* + * Big endian assembly bitops. nr = 0 -> byte 3 bit 0. + */ +extern void _set_bit_be(int nr, volatile unsigned long * p); +extern void _clear_bit_be(int nr, volatile unsigned long * p); +extern void _change_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_set_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_clear_bit_be(int nr, volatile unsigned long * p); +extern int _test_and_change_bit_be(int nr, volatile unsigned long * p); +extern int _find_first_zero_bit_be(const void * p, unsigned size); +extern int _find_next_zero_bit_be(const void * p, int size, int offset); +extern int _find_first_bit_be(const unsigned long *p, unsigned size); +extern int _find_next_bit_be(const unsigned long *p, int size, int offset); + +/* + * The __* form of bitops are non-atomic and may be reordered. + */ +#define ATOMIC_BITOP_LE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_le(nr,p)) + +#define ATOMIC_BITOP_BE(name,nr,p) \ + (__builtin_constant_p(nr) ? \ + ____atomic_##name(nr, p) : \ + _##name##_be(nr,p)) + +#define NONATOMIC_BITOP(name,nr,p) \ + (____nonatomic_##name(nr, p)) + +/* + * These are the little endian, atomic definitions. + */ +#define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p) +#define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p) +#define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p) +#define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p) +#define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p) +#define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p) +#define find_first_zero_bit(p,sz) _find_first_zero_bit_le(p,sz) +#define find_next_zero_bit(p,sz,off) _find_next_zero_bit_le(p,sz,off) +#define find_first_bit(p,sz) _find_first_bit_le(p,sz) +#define find_next_bit(p,sz,off) _find_next_bit_le(p,sz,off) + +#define WORD_BITOFF_TO_LE(x) ((x)) + +#if 0 +#include <asm-generic/bitops/ffz.h> +#include <asm-generic/bitops/__ffs.h> +#include <asm-generic/bitops/fls.h> +#include <asm-generic/bitops/ffs.h> + +#include <asm-generic/bitops/fls64.h> + +#include <asm-generic/bitops/sched.h> +#include <asm-generic/bitops/hweight.h> +#endif + +#define BITS_PER_LONG 32 +#define BITOP_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + return 1UL & (addr[BITOP_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); +} + +#endif /* _ARM_BITOPS_H */ diff --git a/src/target/firmware/include/asm/div64.h b/src/target/firmware/include/asm/div64.h new file mode 100644 index 00000000..36826168 --- /dev/null +++ b/src/target/firmware/include/asm/div64.h @@ -0,0 +1,48 @@ +#ifndef __ASM_ARM_DIV64 +#define __ASM_ARM_DIV64 + +#include <asm/system.h> + +/* + * The semantics of do_div() are: + * + * uint32_t do_div(uint64_t *n, uint32_t base) + * { + * uint32_t remainder = *n % base; + * *n = *n / base; + * return remainder; + * } + * + * In other words, a 64-bit dividend with a 32-bit divisor producing + * a 64-bit result and a 32-bit remainder. To accomplish this optimally + * we call a special __do_div64 helper with completely non standard + * calling convention for arguments and results (beware). + */ + +#ifdef __ARMEB__ +#define __xh "r0" +#define __xl "r1" +#else +#define __xl "r0" +#define __xh "r1" +#endif + +#define do_div(n,base) \ +({ \ + register unsigned int __base asm("r4") = base; \ + register unsigned long long __n asm("r0") = n; \ + register unsigned long long __res asm("r2"); \ + register unsigned int __rem asm(__xh); \ + asm( __asmeq("%0", __xh) \ + __asmeq("%1", "r2") \ + __asmeq("%2", "r0") \ + __asmeq("%3", "r4") \ + "bl __do_div64" \ + : "=r" (__rem), "=r" (__res) \ + : "r" (__n), "r" (__base) \ + : "ip", "lr", "cc"); \ + n = __res; \ + __rem; \ +}) + +#endif diff --git a/src/target/firmware/include/asm/linkage.h b/src/target/firmware/include/asm/linkage.h new file mode 100644 index 00000000..ac1c900f --- /dev/null +++ b/src/target/firmware/include/asm/linkage.h @@ -0,0 +1,18 @@ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +/* asm-arm/linkage.h */ + +#define __ALIGN .align 0 +#define __ALIGN_STR ".align 0" + +/* linux/linkage.h */ + +#define ALIGN __ALIGN + +#define ENTRY(name) \ + .globl name; \ + ALIGN; \ + name: + +#endif diff --git a/src/target/firmware/include/asm/ptrace.h b/src/target/firmware/include/asm/ptrace.h new file mode 100644 index 00000000..f3a654e3 --- /dev/null +++ b/src/target/firmware/include/asm/ptrace.h @@ -0,0 +1,128 @@ +/* + * linux/include/asm-arm/ptrace.h + * + * Copyright (C) 1996-2003 Russell King + * + * 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. + */ +#ifndef __ASM_ARM_PTRACE_H +#define __ASM_ARM_PTRACE_H + +/* + * PSR bits + */ +#define USR26_MODE 0x00000000 +#define FIQ26_MODE 0x00000001 +#define IRQ26_MODE 0x00000002 +#define SVC26_MODE 0x00000003 +#define USR_MODE 0x00000010 +#define FIQ_MODE 0x00000011 +#define IRQ_MODE 0x00000012 +#define SVC_MODE 0x00000013 +#define ABT_MODE 0x00000017 +#define UND_MODE 0x0000001b +#define SYSTEM_MODE 0x0000001f +#define MODE32_BIT 0x00000010 +#define MODE_MASK 0x0000001f +#define PSR_T_BIT 0x00000020 +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_J_BIT 0x01000000 +#define PSR_Q_BIT 0x08000000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 +#define PCMASK 0 + +/* + * Groups of PSR bits + */ +#define PSR_f 0xff000000 /* Flags */ +#define PSR_s 0x00ff0000 /* Status */ +#define PSR_x 0x0000ff00 /* Extension */ +#define PSR_c 0x000000ff /* Control */ + +#ifndef __ASSEMBLY__ + +/* + * This struct defines the way the registers are stored on the + * stack during a system call. Note that sizeof(struct pt_regs) + * has to be a multiple of 8. + */ +struct pt_regs { + long uregs[18]; +}; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] +#define ARM_ORIG_r0 uregs[17] + +#define user_mode(regs) \ + (((regs)->ARM_cpsr & 0xf) == 0) + +#ifdef CONFIG_ARM_THUMB +#define thumb_mode(regs) \ + (((regs)->ARM_cpsr & PSR_T_BIT)) +#else +#define thumb_mode(regs) (0) +#endif + +#define processor_mode(regs) \ + ((regs)->ARM_cpsr & MODE_MASK) + +#define interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_I_BIT)) + +#define fast_interrupts_enabled(regs) \ + (!((regs)->ARM_cpsr & PSR_F_BIT)) + +#define condition_codes(regs) \ + ((regs)->ARM_cpsr & (PSR_V_BIT|PSR_C_BIT|PSR_Z_BIT|PSR_N_BIT)) + +/* Are the current registers suitable for user mode? + * (used to maintain security in signal handlers) + */ +static inline int valid_user_regs(struct pt_regs *regs) +{ + if (user_mode(regs) && + (regs->ARM_cpsr & (PSR_F_BIT|PSR_I_BIT)) == 0) + return 1; + + /* + * Force CPSR to something logical... + */ + regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | PSR_T_BIT | MODE32_BIT; + + return 0; +} + +#define pc_pointer(v) \ + ((v) & ~PCMASK) + +#define instruction_pointer(regs) \ + (pc_pointer((regs)->ARM_pc)) + +#define profile_pc(regs) instruction_pointer(regs) + +#endif /* __ASSEMBLY__ */ + +#endif + diff --git a/src/target/firmware/include/asm/swab.h b/src/target/firmware/include/asm/swab.h new file mode 100644 index 00000000..4640e271 --- /dev/null +++ b/src/target/firmware/include/asm/swab.h @@ -0,0 +1,45 @@ +/* + * arch/arm/include/asm/byteorder.h + * + * ARM Endian-ness. In little endian mode, the data bus is connected such + * that byte accesses appear as: + * 0 = d0...d7, 1 = d8...d15, 2 = d16...d23, 3 = d24...d31 + * and word accesses (data or instruction) appear as: + * d0...d31 + * + * When in big endian mode, byte accesses appear as: + * 0 = d24...d31, 1 = d16...d23, 2 = d8...d15, 3 = d0...d7 + * and word accesses (data or instruction) appear as: + * d0...d31 + */ +#ifndef __ASM_ARM_SWAB_H +#define __ASM_ARM_SWAB_H + +#include <stdint.h> +#include <defines.h> + +static inline uint32_t __arch_swab32(uint32_t x) +{ + uint32_t t; + +#ifndef __thumb__ + if (!__builtin_constant_p(x)) { + /* + * The compiler needs a bit of a hint here to always do the + * right thing and not screw it up to different degrees + * depending on the gcc version. + */ + asm ("eor\t%0, %1, %1, ror #16" : "=r" (t) : "r" (x)); + } else +#endif + t = x ^ ((x << 16) | (x >> 16)); /* eor r1,r0,r0,ror #16 */ + + x = (x << 24) | (x >> 8); /* mov r0,r0,ror #8 */ + t &= ~0x00FF0000; /* bic r1,r1,#0x00FF0000 */ + x ^= (t >> 8); /* eor r0,r0,r1,lsr #8 */ + + return x; +} +#define __arch_swab32 __arch_swab32 + +#endif diff --git a/src/target/firmware/include/asm/system.h b/src/target/firmware/include/asm/system.h new file mode 100644 index 00000000..3db0dc7a --- /dev/null +++ b/src/target/firmware/include/asm/system.h @@ -0,0 +1,123 @@ +#ifndef __ASM_ARM_SYSTEM_H +#define __ASM_ARM_SYSTEM_H + +/* Generic ARM7TDMI (ARMv4T) synchronisation primitives, mostly + * taken from Linux kernel source, licensed under GPL */ + +#define local_irq_save(x) \ + ({ \ + unsigned long temp; \ + (void) (&temp == &x); \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_save\n" \ +" orr %1, %0, #128\n" \ +" msr cpsr_c, %1" \ + : "=r" (x), "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* Save IRQ flags and disable FIQ + IRQ */ +#define local_firq_save(x) \ + ({ \ + unsigned long temp; \ + (void) (&temp == &x); \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_firq_save\n" \ +" orr %1, %0, #0xC0\n" \ +" msr cpsr_c, %1" \ + : "=r" (x), "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable IRQs + */ +#define local_irq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_enable\n" \ +" bic %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable IRQs + */ +#define local_irq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_irq_disable\n" \ +" orr %0, %0, #128\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Enable FIQs + */ +#define local_fiq_enable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ stf\n" \ +" bic %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Disable FIQs + */ +#define local_fiq_disable() \ + ({ \ + unsigned long temp; \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ clf\n" \ +" orr %0, %0, #64\n" \ +" msr cpsr_c, %0" \ + : "=r" (temp) \ + : \ + : "memory", "cc"); \ + }) + +/* + * Save the current interrupt enable state. + */ +#define local_save_flags(x) \ + ({ \ + __asm__ __volatile__( \ + "mrs %0, cpsr @ local_save_flags" \ + : "=r" (x) : : "memory", "cc"); \ + }) + +/* + * restore saved IRQ & FIQ state + */ +#define local_irq_restore(x) \ + __asm__ __volatile__( \ + "msr cpsr_c, %0 @ local_irq_restore\n" \ + : \ + : "r" (x) \ + : "memory", "cc") + +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + (int)(flags & PSR_I_BIT); \ +}) + +#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" + +#endif diff --git a/src/target/firmware/include/board.h b/src/target/firmware/include/board.h new file mode 100644 index 00000000..c2fb6018 --- /dev/null +++ b/src/target/firmware/include/board.h @@ -0,0 +1,6 @@ +#ifndef _BOARD_H +#define _BOARD_H + +extern const char *target_board; + +#endif /* _BOARD_H */ diff --git a/src/target/firmware/include/byteorder.h b/src/target/firmware/include/byteorder.h new file mode 100644 index 00000000..41edb93d --- /dev/null +++ b/src/target/firmware/include/byteorder.h @@ -0,0 +1,79 @@ +#ifndef _LINUX_BYTEORDER_LITTLE_ENDIAN_H +#define _LINUX_BYTEORDER_LITTLE_ENDIAN_H + +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif +#ifndef __LITTLE_ENDIAN_BITFIELD +#define __LITTLE_ENDIAN_BITFIELD +#endif + +#include <stdint.h> +#include <swab.h> + +#define __constant_htonl(x) ___constant_swab32(x) +#define __constant_ntohl(x) ___constant_swab32(x) +#define __constant_htons(x) ___constant_swab16(x) +#define __constant_ntohs(x) ___constant_swab16(x) +#define __constant_cpu_to_le64(x) (x) +#define __constant_le64_to_cpu(x) (x) +#define __constant_cpu_to_le32(x) (x) +#define __constant_le32_to_cpu(x) (x) +#define __constant_cpu_to_le16(x) (x) +#define __constant_le16_to_cpu(x) (x) +#define __constant_cpu_to_be64(x) ___constant_swab64(x) +#define __constant_be64_to_cpu(x) ___constant_swab64(x) +#define __constant_cpu_to_be32(x) ___constant_swab32(x) +#define __constant_be32_to_cpu(x) ___constant_swab32(x) +#define __constant_cpu_to_be16(x) ___constant_swab16(x) +#define __constant_be16_to_cpu(x) ___constant_swab16(x) +#define __cpu_to_le64(x) (x) +#define __le64_to_cpu(x) (x) +#define __cpu_to_le32(x) (x) +#define __le32_to_cpu(x) (x) +#define __cpu_to_le16(x) (x) +#define __le16_to_cpu(x) (x) +#define __cpu_to_be64(x) __swab64(x) +#define __be64_to_cpu(x) __swab64(x) +#define __cpu_to_be32(x) __swab32(x) +#define __be32_to_cpu(x) __swab32(x) +#define __cpu_to_be16(x) __swab16(x) +#define __be16_to_cpu(x) __swab16(x) + +/* from include/linux/byteorder/generic.h */ +#define cpu_to_le64 __cpu_to_le64 +#define le64_to_cpu __le64_to_cpu +#define cpu_to_le32 __cpu_to_le32 +#define le32_to_cpu __le32_to_cpu +#define cpu_to_le16 __cpu_to_le16 +#define le16_to_cpu __le16_to_cpu +#define cpu_to_be64 __cpu_to_be64 +#define be64_to_cpu __be64_to_cpu +#define cpu_to_be32 __cpu_to_be32 +#define be32_to_cpu __be32_to_cpu +#define cpu_to_be16 __cpu_to_be16 +#define be16_to_cpu __be16_to_cpu + +/* + * They have to be macros in order to do the constant folding + * correctly - if the argument passed into a inline function + * it is no longer constant according to gcc.. + */ + +#undef ntohl +#undef ntohs +#undef htonl +#undef htons + +#define ___htonl(x) __cpu_to_be32(x) +#define ___htons(x) __cpu_to_be16(x) +#define ___ntohl(x) __be32_to_cpu(x) +#define ___ntohs(x) __be16_to_cpu(x) + +#define htonl(x) ___htonl(x) +#define ntohl(x) ___ntohl(x) +#define htons(x) ___htons(x) +#define ntohs(x) ___ntohs(x) + + +#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */ diff --git a/src/target/firmware/include/calypso/backlight.h b/src/target/firmware/include/calypso/backlight.h new file mode 100644 index 00000000..3a6abd55 --- /dev/null +++ b/src/target/firmware/include/calypso/backlight.h @@ -0,0 +1,10 @@ +#ifndef _CAL_BACKLIGHT_H +#define _CAL_BACKLIGHT_H + +/* Switch backlight to PWL mode (or back) */ +void bl_mode_pwl(int on); + +/* Set the backlight level */ +void bl_level(uint8_t level); + +#endif /* CAL_BACKLIGHT_H */ diff --git a/src/target/firmware/include/calypso/clock.h b/src/target/firmware/include/calypso/clock.h new file mode 100644 index 00000000..abcfde1d --- /dev/null +++ b/src/target/firmware/include/calypso/clock.h @@ -0,0 +1,67 @@ +#ifndef _CALYPSO_CLK_H +#define _CALYPSO_CLK_H + +#include <stdint.h> + +#define CALYPSO_PLL26_52_MHZ ((2 << 8) | 0) +#define CALYPSO_PLL26_86_7_MHZ ((10 << 8) | 2) +#define CALYPSO_PLL26_87_MHZ ((3 << 8) | 0) +#define CALYPSO_PLL13_104_MHZ ((8 << 8) | 0) + +enum mclk_div { + _ARM_MCLK_DIV_1 = 0, + ARM_MCLK_DIV_1 = 1, + ARM_MCLK_DIV_2 = 2, + ARM_MCLK_DIV_3 = 3, + ARM_MCLK_DIV_4 = 4, + ARM_MCLK_DIV_5 = 5, + ARM_MCLK_DIV_6 = 6, + ARM_MCLK_DIV_7 = 7, + ARM_MCLK_DIV_1_5 = 0x80 | 1, + ARM_MCLK_DIV_2_5 = 0x80 | 2, +}; + +void calypso_clock_set(uint8_t vtcxo_div2, uint16_t inp, enum mclk_div mclk_div); +void calypso_pll_set(uint16_t inp); +void calypso_clk_dump(void); + +/* CNTL_RST */ +enum calypso_rst { + RESET_DSP = (1 << 1), + RESET_EXT = (1 << 2), + RESET_WDOG = (1 << 3), +}; + +void calypso_reset_set(enum calypso_rst calypso_rst, int active); +int calypso_reset_get(enum calypso_rst); + +enum calypso_bank { + CALYPSO_nCS0 = 0, + CALYPSO_nCS1 = 2, + CALYPSO_nCS2 = 4, + CALYPSO_nCS3 = 6, + CALYPSO_nCS7 = 8, + CALYPSO_CS4 = 0xa, + CALYPSO_nCS6 = 0xc, +}; + +enum calypso_mem_width { + CALYPSO_MEM_8bit = 0, + CALYPSO_MEM_16bit = 1, + CALYPSO_MEM_32bit = 2, +}; + +void calypso_mem_cfg(enum calypso_bank bank, uint8_t ws, + enum calypso_mem_width width, int we); + +/* Enable or disable the internal bootrom mapped to 0x0000'0000 */ +void calypso_bootrom(int enable); + +/* Enable or disable the debug unit */ +void calypso_debugunit(int enable); + +/* configure the RHEA bus bridge[s] */ +void calypso_rhea_cfg(uint8_t fac0, uint8_t fac1, uint8_t timeout, + uint8_t ws_h, uint8_t ws_l, uint8_t w_en0, uint8_t w_en1); + +#endif /* _CALYPSO_CLK_H */ diff --git a/src/target/firmware/include/calypso/dma.h b/src/target/firmware/include/calypso/dma.h new file mode 100644 index 00000000..00b9bde7 --- /dev/null +++ b/src/target/firmware/include/calypso/dma.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_DMA_H +#define _CALYPSO_DMA_H + +void dma_init(void); + +#endif /* _CALYPSO_DMA_H */ diff --git a/src/target/firmware/include/calypso/dsp.h b/src/target/firmware/include/calypso/dsp.h new file mode 100644 index 00000000..4839f827 --- /dev/null +++ b/src/target/firmware/include/calypso/dsp.h @@ -0,0 +1,35 @@ +#ifndef _CALYPSO_DSP_H +#define _CALYPSO_DSP_H + +#include <calypso/dsp_api.h> + +#define CAL_DSP_TGT_BB_LVL 80 + +struct dsp_api { + T_NDB_MCU_DSP *ndb; + T_DB_DSP_TO_MCU *db_r; + T_DB_MCU_TO_DSP *db_w; + T_PARAM_MCU_DSP *param; + int r_page; + int w_page; + int r_page_used; + int frame_ctr; +}; + +extern struct dsp_api dsp_api; + +void dsp_power_on(void); +void dsp_dump_version(void); +void dsp_dump(void); +void dsp_checksum_task(void); +void dsp_api_memset(uint16_t *ptr, int octets); +void dsp_load_afc_dac(uint16_t afc); +void dsp_load_apc_dac(uint16_t apc); +void dsp_load_tch_param(uint16_t fn, uint8_t chan_mode, uint8_t chan_type, + uint8_t subchannel, uint8_t tch_loop, uint8_t sync_tch); +void dsp_end_scenario(void); + +void dsp_load_rx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); +void dsp_load_tx_task(uint16_t task, uint8_t burst_id, uint8_t tsc); + +#endif diff --git a/src/target/firmware/include/calypso/dsp_api.h b/src/target/firmware/include/calypso/dsp_api.h new file mode 100644 index 00000000..f9751f37 --- /dev/null +++ b/src/target/firmware/include/calypso/dsp_api.h @@ -0,0 +1,1560 @@ +#ifndef _CAL_DSP_API_H +#define _CAL_DSP_API_H + +/* This is a header file with structures imported from the TSM30 source code (l1_defty.h) + * + * As this header file only is a list of definitions and data structures, it is + * not ocnsidered to be a copyrightable work itself. + * + * Nonetheless, it might be good to rewrite it (without ugly typedefs!) */ + +#if(L1_DYN_DSP_DWNLD == 1) + #include "l1_dyn_dwl_defty.h" +#endif + +/* Include a header file that defines everything this l1_defty.h needs */ +#include "l1_environment.h" + +#define BASE_API_NDB 0xFFD001A8L /* 268 words */ +#define BASE_API_PARAM 0xFFD00862L /* 57 words */ +#define BASE_API_R_PAGE_0 0xFFD00050L /* 20 words */ +#define BASE_API_R_PAGE_1 0xFFD00078L /* 20 words */ +#define BASE_API_W_PAGE_0 0xFFD00000L /* 20 words */ +#define BASE_API_W_PAGE_1 0xFFD00028L /* 20 words */ + + +/***********************************************************/ +/* */ +/* Data structure for global info components. */ +/* */ +/***********************************************************/ + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + API d_fn; // (8) FN, in Rep. period and FN%104, used for TRAFFIC/TCH only. + // bit [0..7] -> b_fn_report, FN in the normalized reporting period. + // bit [8..15] -> b_fn_sid, FN % 104, used for SID positionning. + API d_ctrl_tch; // (9) Tch channel description. + // bit [0..3] -> b_chan_mode, channel mode. + // bit [4..5] -> b_chan_type, channel type. + // bit [6] -> reset SACCH + // bit [7] -> vocoder ON + // bit [8] -> b_sync_tch_ul, synchro. TCH/UL. + // bit [9] -> b_sync_tch_dl, synchro. TCH/DL. + // bit [10] -> b_stop_tch_ul, stop TCH/UL. + // bit [11] -> b_stop_tch_dl, stop TCH/DL. + // bit [12.13] -> b_tch_loop, tch loops A/B/C. + API hole; // (10) unused hole. + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_ctrl_abb; // (11) Bit field indicating the analog baseband register to send. + // bit [0] -> b_ramp: the ramp information(a_ramp[]) is located in NDB + // bit [1.2] -> unused + // bit [3] -> b_apcdel: delays-register in NDB + // bit [4] -> b_afc: freq control register in DB + // bit [5..15] -> unused +#endif + API a_a5fn[2]; // (12..13) Encryption Frame number. + // word 0, bit [0..4] -> T2. + // word 0, bit [5..10] -> T3. + // word 1, bit [0..11] -> T1. + API d_power_ctl; // (14) Power level control. + API d_afc; // (15) AFC value (enabled by "b_afc" in "d_ctrl_TCM4400 or in d_ctrl_abb"). + API d_ctrl_system; // (16) Controle Register for RESET/RESUME. + // bit [0..2] -> b_tsq, training sequence. + // bit [3] -> b_bcch_freq_ind, BCCH frequency indication. + // bit [15] -> b_task_abort, DSP task abort command. +} +T_DB_MCU_TO_DSP; + +typedef struct +{ + API d_task_d; // (0) Downlink task command. + API d_burst_d; // (1) Downlink burst identifier. + API d_task_u; // (2) Uplink task command. + API d_burst_u; // (3) Uplink burst identifier. + API d_task_md; // (4) Downlink Monitoring (FB/SB) task command. +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API d_background; // (5) Background tasks +#else + API d_reserved; // (5) Reserved +#endif + API d_debug; // (6) Debug/Acknowledge/general purpose word. + API d_task_ra; // (7) RA task command. + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + API a_serv_demod[4]; // ( 8..11) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_pm[3]; // (12..14) Power measurement results, array of 3 words. + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#else + API a_pm[3]; // ( 8..10) Power measurement results, array of 3 words. + API a_serv_demod[4]; // (11..14) Serv. cell demod. result, array of 4 words (D_TOA,D_PM,D_ANGLE,D_SNR). + API a_sch[5]; // (15..19) Header + SB information, array of 5 words. +#endif +} +T_DB_DSP_TO_MCU; + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 5 words are reserved for any possible mapping modification + API d_hole1_ndb[2]; + #else + // 6 words are reserved for any possible mapping modification + API d_hole1_ndb[3]; + #endif + + #if (AMR == 1) + API p_debug_amr; + #else + API d_hole_debug_amr; + #endif + + #if (CHIPSET == 12) + #if (DSP == 35) || (DSP == 36) + API d_hole2_ndb[1]; + API d_mcsi_select; + #else + API d_hole2_ndb[2]; + #endif + #else + API d_hole2_ndb[2]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + +#if (ANLG_FAM == 3) + // SYREN specific registers + API d_vbpop; + API d_vau_delay_init; + API d_vaud_cfg; + API d_vauo_onoff; + API d_vaus_vol; + API d_vaud_pll; + API d_hole3_ndb[1]; +#elif ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + + API d_hole3_ndb[7]; + +#endif + + // word used for the init of USF threshold + API d_thr_usf_detect; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + // GTT API mapping for DSP code 34 (for test only) + #if (L1_GTT == 1) + API d_tty_status; + API d_tty_detect_thres; + API d_ctm_detect_shift; + API d_tty_fa_thres; + API d_tty_mod_norm; + API d_tty_reset_buffer_ul; + API d_tty_loop_ctrl; + API p_tty_loop_buffer; + #else + API a_tty_holes[8]; + #endif + + API a_sr_holes0[414]; + + #if (L1_NEW_AEC) + // new AEC + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #else + API a_new_aec_holes[12]; + #endif // L1_NEW_AEC + + // Speech recognition model + API a_sr_holes1[145]; + API d_cport_init; + API d_cport_ctrl; + API a_cport_cfr[2]; + API d_cport_tcl_tadt; + API d_cport_tdat; + API d_cport_tvs; + API d_cport_status; + API d_cport_reg_value; + + API a_cport_holes[1011]; + + API a_model[1041]; + + // EOTD buffer +#if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#else + API a_eotd_holes[22]; +#endif + // AMR ver 1.0 buffers + API a_amr_config[4]; + API a_ratscch_ul[6]; + API a_ratscch_dl[6]; + API d_amr_snr_est; // estimation of the SNR of the AMR speech block + #if (L1_VOICE_MEMO_AMR) + API d_amms_ul_voc; + #else + API a_voice_memo_amr_holes[1]; + #endif + API d_thr_onset_afs; // thresh detection ONSET AFS + API d_thr_sid_first_afs; // thresh detection SID_FIRST AFS + API d_thr_ratscch_afs; // thresh detection RATSCCH AFS + API d_thr_update_afs; // thresh detection SID_UPDATE AFS + API d_thr_onset_ahs; // thresh detection ONSET AHS + API d_thr_sid_ahs; // thresh detection SID frames AHS + API d_thr_ratscch_marker;// thresh detection RATSCCH MARKER + API d_thr_sp_dgr; // thresh detection SPEECH DEGRADED/NO_DATA + API d_thr_soft_bits; + #if (MELODY_E2) + API d_melody_e2_osc_stop; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API d_melody_e2_deltatime; + + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #else + API a_melody_e2_holes0[14]; + #endif + + API a_melody_e2_holes1[693]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #else + API d_holes[61]; + #if (AMR_THRESHOLDS_WORKAROUND) + API a_d_macc_thr_afs[8]; + API a_d_macc_thr_ahs[6]; + #endif + #endif + + } + T_NDB_MCU_DSP; +#elif (DSP == 33) // NDB GSM + typedef struct + { + // MISC Tasks + API d_dsp_page; + + // DSP status returned (DSP --> MCU). + API d_error_status; + + // RIF control (MCU -> DSP). + API d_spcx_rif; + + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + + API d_dsp_test; + + // Words dedicated to Software version (DSP code + Patch) + API d_version_number1; + API d_version_number2; + + API d_debug_ptr; + API d_debug_bk; + + API d_pll_config; + + // GSM/GPRS DSP Debug trace support + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + + #if (W_A_DSP_IDLE3 == 1) + // DSP report its state: 0 run, 1 Idle1, 2 Idle2, 3 Idle3. + API d_dsp_state; + // 10 words are reserved for any possible mapping modification + API d_hole1_ndb[5]; + #else + // 11 words are reserved for any possible mapping modification + API d_hole1_ndb[6]; + #endif + + // New words APCDEL1 and APCDEL2 for 2TX: TX/PRACH combinations + API d_apcdel1_bis; + API d_apcdel2_bis; + + + // New registers due to IOTA analog base band + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + + // Analog Based Band + API d_afcctladd; + + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3)) + API d_vbctrl1; + #endif + + API d_bbctrl; + + // Monitoring tasks control (MCU <- DSP) + // FB task + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // SB Task + API a_sch26[5]; // Header + SB information, array of 5 words. + + API d_audio_gain_ul; + API d_audio_gain_dl; + + // Controller of the melody E2 audio compressor + API d_audio_compressor_ctrl; + + // AUDIO module + API d_audio_init; + API d_audio_status; + + // Audio tasks + // TONES (MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + + API d_aec_ctrl; + + API d_es_level_api; + API d_mu_api; + + // Melody Ringer module + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + + // selection of the melody format + API d_melody_selection; + + // Holes due to the format melody E1 + API a_melo_holes[3]; + + // Speech Recognition module + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + + // Audio buffer + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // V42bis module + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + + // Background tasks + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + + // GEA module defined in l1p_deft.h (the following section is overlaid with GPRS NDB memory) + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + + API d_hole3_ndb[8]; + + // Encryption module + API d_a5mode; // Encryption Mode. + + API d_sched_mode_gprs_ovly; + + // 7 words are reserved for any possible mapping modification + API d_hole4_ndb[5]; + + // Ramp definition for Omega device + API a_ramp[16]; + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + + // Traffic downlink data frames......(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API a_kc[4]; // Encryption Key Code. + + // Integrated Data Services module + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + #if (L1_NEW_AEC) + // new AEC + API a_new_aec_holes[422]; + API d_cont_filter; + API d_granularity_att; + API d_coef_smooth; + API d_es_level_max; + API d_fact_vad; + API d_thrs_abs; + API d_fact_asd_fil; + API d_fact_asd_mut; + API d_far_end_pow_h; + API d_far_end_pow_l; + API d_far_end_noise_h; + API d_far_end_noise_l; + #endif + + // Speech recognition model + #if (L1_NEW_AEC) + API a_sr_holes[1165]; + #else + API a_sr_holes[1599]; + #endif // L1_NEW_AEC + API a_model[1041]; + + // EOTD buffer + #if (L1_EOTD==1) + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; + #else + API a_eotd_holes[22]; + #endif + + #if (MELODY_E2) + API a_melody_e2_holes0[27]; + API d_melody_e2_osc_used; + API d_melody_e2_osc_active; + API d_melody_e2_semaphore; + API a_melody_e2_osc[16][3]; + API d_melody_e2_globaltimefactor; + API a_melody_e2_instrument_ptr[8]; + API a_melody_e2_holes1[708]; + API a_dsp_trace[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_DSP_TRACE]; + API a_melody_e2_instrument_wave[SC_AUDIO_MELODY_E2_MAX_SIZE_OF_INSTRUMENT]; + #endif + } + T_NDB_MCU_DSP; + +#elif ((DSP == 32) || (DSP == 31)) + typedef struct + { + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + #if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[9]; + #else // DSP==32 + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif // DSP 32 + + #else // NO MELODY E1 + #if (DSP==31) + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #else // DSP==32 + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #endif //DSP == 32 + #endif // NO MELODY E1 + + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + + API d_bbctrl; + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API a_hole[24]; + + API d_sched_mode_gprs_ovly; + + API fir_holes1[384]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + +#if (L1_EOTD ==1) + API a_eotd_hole[369]; + + API d_eotd_first; + API d_eotd_max; + API d_eotd_nrj_high; + API d_eotd_nrj_low; + API a_eotd_crosscor[18]; +#endif + #endif + } + T_NDB_MCU_DSP; + + +#else // OTHER DSP CODE like 17 + +typedef struct +{ + // Monitoring tasks control..........(MCU <- DSP) + API d_fb_det; // FB detection result. (1 for FOUND). + API d_fb_mode; // Mode for FB detection algorithm. + API a_sync_demod[4]; // FB/SB demod. result, (D_TOA,D_PM,D_ANGLE,D_SNR). + + // CCCH/SACCH downlink information...(!!) + API a_cd[15]; // Header + CCCH/SACCH downlink information. + + // FACCH downlink information........(!!) + API a_fd[15]; // Header + FACCH downlink information. + + // Traffic downlink data frames......(!!) + #if (DATA14_4 == 0) + API a_dd_0[20]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[20]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_dd_0[22]; // Header + DATA traffic downlink information, sub. chan. 0. + API a_dd_1[22]; // Header + DATA traffic downlink information, sub. chan. 1. + #endif + + // CCCH/SACCH uplink information.....(!!) + API a_cu[15]; // Header + CCCH/SACCH uplink information. + + #if (SPEECH_RECO) + // FACCH downlink information........(!!) + API a_fu[3]; // Header + FACCH uplink information + // The size of this buffer is 15 word but some speech reco words + // are overlayer with this buffer. This is the reason why the size is 3 instead of 15. + API d_sr_status; // status of the DSP speech reco task + API d_sr_param; // paramters for the DSP speech reco task: OOV threshold. + API sr_hole1; // hole + API d_sr_bit_exact_test; // bit exact test + API d_sr_nb_words; // number of words used in the speech recognition task + API d_sr_db_level; // estimate voice level in dB + API d_sr_db_noise; // estimate noise in dB + API d_sr_mod_size; // size of the model + API sr_holes_1[4]; // hole + #else + // FACCH downlink information........(!!) + API a_fu[15]; // Header + FACCH uplink information + #endif + + // Traffic uplink data frames........(!!) + #if (DATA14_4 == 0) + API a_du_0[20]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[20]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + #if (DATA14_4 == 1) + API a_du_0[22]; // Header + DATA traffic uplink information, sub. chan. 0. + API a_du_1[22]; // Header + DATA traffic uplink information, sub. chan. 1. + #endif + + // Random access.....................(MCU -> DSP). + API d_rach; // RACH information. + + //...................................(MCU -> DSP). + API d_a5mode; // Encryption Mode. + API a_kc[4]; // Encryption Key Code. + API d_tch_mode; // TCH mode register. + // bit [0..1] -> b_dai_mode. + // bit [2] -> b_dtx. + + // OMEGA...........................(MCU -> DSP). + +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2)) + API a_ramp[16]; + #if (MELODY_E1) + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + #if (DSP == 17) + // selection of the melody format + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4]; + #else + API d_melody_selection; + API holes[9]; + #endif + #else // NO MELODY E1 + // selection of the melody format + #if (DSP == 17) + API holes[34]; // 34 unused holes. + API d_dco_type; // Tide + API p_start_IQ; + API d_level_off; + API d_dco_dbg; + API d_tide_resa; + API d_asynch_margin; // Perseus Asynch Audio Workaround + API hole[4] + #else + // selection of the melody format + API d_melody_selection; + API holes[43]; // 43 unused holes. + #endif + #endif + API d_debug3; + API d_debug2; + API d_debug1; // bit 0 at 1 enable dsp f_tx delay for Omega + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_aec_ctrl; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + #if (ANLG_FAM == 1) + API d_vbctrl; + #elif (ANLG_FAM == 2) + API d_vbctrl1; + #endif + API d_bbctrl; + + #else + #error DSPCODE not supported with given ANALOG + #endif //(ANALOG)1, 2 + //...................................(MCU -> DSP). + API a_sch26[5]; // Header + SB information, array of 5 words. + + // TONES.............................(MCU -> DSP) + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + + // PLL...............................(MCU -> DSP). + API d_pll_clkmod1; + API d_pll_clkmod2; + + // DSP status returned..........(DSP --> MCU). + API d_error_status; + + // RIF control.......................(MCU -> DSP). + API d_spcx_rif; + + API d_shiftdl; + API d_shiftul; + + #if (AEC == 1) + // AEC control.......................(MCU -> DSP). + #if (VOC == FR_EFR) + API p_aec_init; + API p_aec_prog; + API p_spenh_init; + API p_spenh_prog; + #endif + + #if (VOC == FR_HR_EFR) + API p_saec_prog; + API p_aec_prog; + API p_spenh_prog; + #endif + #endif + + API a_ovly[75]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + #if (SPEECH_RECO) + API a_data_buf_ul[3]; + API a_n_best_words[4]; // array of the 4 best words + API a_n_best_score[8]; // array of the 4 best scores (each score is 32 bits length) + API sr_holes_2[6]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + API a_model[1041]; // array of the speech reco model + #else + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + + API fir_holes1[409]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; + API d_audio_init; + API d_audio_status; + #endif +} +T_NDB_MCU_DSP; +#endif + +#if (DSP == 34) || (DSP == 35) || (DSP == 36) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + API_SIGNED d_hole2_param[4]; + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; +#elif (DSP == 33) +typedef struct +{ + API_SIGNED d_transfer_rate; + + // Common GSM/GPRS + // These words specified the latencies to applies on some peripherics + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + + // DSP Start address + API_SIGNED d_gprs_install_address; + + API_SIGNED d_misc_config; + + API_SIGNED d_cn_sw_workaround; + + #if DCO_ALGO + API_SIGNED d_cn_dco_param; + + API_SIGNED d_hole2_param[3]; + #else + API_SIGNED d_hole2_param[4]; + #endif + + //...................................Frequency Burst. + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + // V42Bis module + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + + //...................................CHED + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + + // FACCH module + API_SIGNED d_facch_thr; + + // IDS module + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + + // FIR coefficients + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +} +T_PARAM_MCU_DSP; + +#else + +typedef struct +{ + //...................................Frequency Burst. + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + //...................................Demodulation. + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + API_SIGNED hole[1]; + API_SIGNED d_transfer_rate; + //...................................TCH Full Speech. + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + + #if (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + #endif + + //...................................TCH Full Speech. + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + + #if (VOC == FR) || (VOC == FR_HR) || (VOC == FR_HR_EFR) + //...................................TCH Half Speech. + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + #endif + + #if (VOC == FR_EFR) || (VOC == FR_HR_EFR) + //...................................TCH Enhanced FR Speech. + API_SIGNED d_sd_min_thr_tchefs; //(24L *C_POND_RED) + API_SIGNED d_ma_min_thr_tchefs; //(1200L *C_POND_RED) + API_SIGNED d_md_max_thr_tchefs; //(2000L *C_POND_RED) + API_SIGNED d_md1_max_thr_tchefs; //(160L *C_POND_RED) + API_SIGNED d_hole1; + #endif + + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + API_SIGNED d_facch_thr; + API_SIGNED d_dsp_test; + + + #if (DATA14_4 == 0 ) || (VOC == FR_HR_EFR) + API_SIGNED d_patch_addr1; + API_SIGNED d_patch_data1; + API_SIGNED d_patch_addr2; + API_SIGNED d_patch_data2; + API_SIGNED d_patch_addr3; + API_SIGNED d_patch_data3; + API_SIGNED d_patch_addr4; + API_SIGNED d_patch_data4; + #endif + + //................................... + API_SIGNED d_version_number; // DSP patch version + API_SIGNED d_ti_version; // customer number. No more used since 1.5 + + API_SIGNED d_dsp_page; + + #if IDS + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + #endif + + +} +T_PARAM_MCU_DSP; +#endif + +#if (DSP_DEBUG_TRACE_ENABLE == 1) +typedef struct +{ + API d_debug_ptr_begin; + API d_debug_ptr_end; +} +T_DB2_DSP_TO_MCU; +#endif + +/* DSP error as per ndb->d_error_status */ +enum dsp_error { + DSP_ERR_RHEA = 0x0001, + DSP_ERR_IQ_SAMPLES = 0x0004, + DSP_ERR_DMA_PROG = 0x0008, + DSP_ERR_DMA_TASK = 0x0010, + DSP_ERR_DMA_PEND = 0x0020, + DSP_ERR_VM = 0x0080, + DSP_ERR_DMA_UL_TASK = 0x0100, + DSP_ERR_DMA_UL_PROG = 0x0200, + DSP_ERR_DMA_UL_PEND = 0x0400, + DSP_ERR_STACK_OV = 0x0800, +}; + +/* How an ABB register + value is expressed in the API RAM */ +#define ABB_VAL(reg, val) ( (((reg) & 0x1F) << 1) | (((val) & 0x3FF) << 6) ) + +/* How an ABB register + value | TRUE is expressed in the API RAM */ +#define ABB_VAL_T(reg, val) (ABB_VAL(reg, val) | 1) + +#endif /* _CAL_DSP_API_H */ diff --git a/src/target/firmware/include/calypso/du.h b/src/target/firmware/include/calypso/du.h new file mode 100644 index 00000000..f2eae091 --- /dev/null +++ b/src/target/firmware/include/calypso/du.h @@ -0,0 +1,32 @@ +/* Calypso DU (Debug Unit) Driver */ + +/* (C) 2010 by Ingo Albrecht <prom@berlin.ccc.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. + * + */ + +#ifndef _CALYPSO_DU_H +#define _CALYPSO_DU_H + +#include <calypso/clock.h> + +void calypso_du_init(); +void calypso_du_stop(); +void calypsu_du_dump(); + +#endif /* _CALYPSO_DU_H */ diff --git a/src/target/firmware/include/calypso/irq.h b/src/target/firmware/include/calypso/irq.h new file mode 100644 index 00000000..5ea59797 --- /dev/null +++ b/src/target/firmware/include/calypso/irq.h @@ -0,0 +1,49 @@ +#ifndef _CALYPSO_IRQ_H +#define _CALYPSO_IRQ_H + +enum irq_nr { + IRQ_WATCHDOG = 0, + IRQ_TIMER1 = 1, + IRQ_TIMER2 = 2, + IRQ_TSP_RX = 3, + IRQ_TPU_FRAME = 4, + IRQ_TPU_PAGE = 5, + IRQ_SIMCARD = 6, + IRQ_UART_MODEM = 7, + IRQ_KEYPAD_GPIO = 8, + IRQ_RTC_TIMER = 9, + IRQ_RTC_ALARM_I2C = 10, + IRQ_ULPD_GAUGING = 11, + IRQ_EXTERNAL = 12, + IRQ_SPI = 13, + IRQ_DMA = 14, + IRQ_API = 15, + IRQ_SIM_DETECT = 16, + IRQ_EXTERNAL_FIQ = 17, + IRQ_UART_IRDA = 18, + IRQ_ULPD_GSM_TIMER = 19, + IRQ_GEA = 20, + _NR_IRQ +}; + +typedef void irq_handler(enum irq_nr nr); + +/* initialize IRQ driver and enable interrupts */ +void irq_init(void); + +/* enable a certain interrupt */ +void irq_enable(enum irq_nr nr); + +/* disable a certain interrupt */ +void irq_disable(enum irq_nr nr); + +/* configure a certain interrupt */ +void irq_config(enum irq_nr nr, int fiq, int edge, int8_t prio); + +/* register an interrupt handler */ +void irq_register_handler(enum irq_nr nr, irq_handler *handler); + +/* Install the exception handlers to where the ROM loader jumps */ +void calypso_exceptions_install(void); + +#endif /* _CALYPSO_IRQ_H */ diff --git a/src/target/firmware/include/calypso/l1_environment.h b/src/target/firmware/include/calypso/l1_environment.h new file mode 100644 index 00000000..2d1f8d97 --- /dev/null +++ b/src/target/firmware/include/calypso/l1_environment.h @@ -0,0 +1,365 @@ +#include <stdint.h> + +typedef unsigned short API; +typedef signed short API_SIGNED; + +#define FAR + +#define CHIPSET 12 +#define DSP 36 +#define ANLG_FAM 2 /* Iota */ + +/* MFTAB */ +#define L1_MAX_FCT 5 /* Max number of fctions in a frame */ +#define MFTAB_SIZE 20 + +#define NBMAX_CARRIER 174+374 /* Number of carriers (GSM-Ext + DCS */ + +#define DPAGC_FIFO_LEN 4 + +#define SIZE_HIST 10 + +#if !L1_GPRS +# define NBR_DL_L1S_TASKS 32 +#else +# define NBR_DL_L1S_TASKS 45 +#endif + +#define NBR_L1A_PROCESSES 46 + +#define W_A_DSP_IDLE3 1 + + + +// Identifier for all DSP tasks. +// ...RX & TX tasks identifiers. +#define NO_DSP_TASK 0 // No task. +#define NP_DSP_TASK 21 // Normal Paging reading task. +#define EP_DSP_TASK 22 // Extended Paging reading task. +#define NBS_DSP_TASK 19 // Normal BCCH serving reading task. +#define EBS_DSP_TASK 20 // Extended BCCH serving reading task. +#define NBN_DSP_TASK 17 // Normal BCCH neighbour reading task. +#define EBN_DSP_TASK 18 // Extended BCCH neighbour reading task. +#define ALLC_DSP_TASK 24 // CCCH reading task while performing FULL BCCH/CCCH reading task. +#define CB_DSP_TASK 25 // CBCH reading task. +#define DDL_DSP_TASK 26 // SDCCH/D (data) reading task. +#define ADL_DSP_TASK 27 // SDCCH/A (SACCH) reading task. +#define DUL_DSP_TASK 12 // SDCCH/D (data) transmit task. +#define AUL_DSP_TASK 11 // SDCCH/A (SACCH) transmit task. +#define RACH_DSP_TASK 10 // RACH transmit task. +#define TCHT_DSP_TASK 13 // TCH Traffic data DSP task id (RX or TX) +#define TCHA_DSP_TASK 14 // TCH SACCH data DSP task id (RX or TX) +#define TCHD_DSP_TASK 28 // TCH Traffic data DSP task id (RX or TX) + +#define TCH_DTX_UL 15 // Replace UL task in DSP->MCU com. to say "burst not transmitted". + +#if (L1_GPRS) + // Identifier for DSP tasks Packet dedicated. + // ...RX & TX tasks identifiers. + //------------------------------------------------------------------------ + // WARNING ... Need to aligned following macro with MCU/DSP GPRS Interface + //------------------------------------------------------------------------ + #define PNP_DSP_TASK 30 + #define PEP_DSP_TASK 31 + #define PALLC_DSP_TASK 32 + #define PBS_DSP_TASK 33 + + #define PTCCH_DSP_TASK 33 + +#endif + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define FB_DSP_TASK 5 // Freq. Burst reading task in Idle mode. +#define SB_DSP_TASK 6 // Sync. Burst reading task in Idle mode. +#define TCH_FB_DSP_TASK 8 // Freq. Burst reading task in Dedicated mode. +#define TCH_SB_DSP_TASK 9 // Sync. Burst reading task in Dedicated mode. +#define IDLE1 1 + +// Debug tasks +#define CHECKSUM_DSP_TASK 33 +#define TST_NDB 35 // Checksum DSP->MCU +#define TST_DB 36 // DB communication check +#define INIT_VEGA 37 +#define DSP_LOOP_C 38 + +// Identifier for measurement, FB / SB search tasks. +// Values 1,2,3 reserved for "number of measurements". +#define TCH_LOOP_A 31 +#define TCH_LOOP_B 32 + +// bits in d_gsm_bgd_mgt - background task management +#define B_DSPBGD_RECO 1 // start of reco in dsp background +#define B_DSPBGD_UPD 2 // start of alignement update in dsp background +#define B_DSPBGD_STOP_RECO 256 // stop of reco in dsp background +#define B_DSPBGD_STOP_UPD 512 // stop of alignement update in dsp background + +// bit in d_pll_config +#define B_32KHZ_CALIB (1 << 14) // force DSP in Idle1 during 32 khz calibration +// **************************************************************** +// NDB AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +// bits in d_tch_mode +#define B_EOTD (1 << 0) // EOTD mode +#define B_PLAY_UL (1 << 3) // Play UL +#define B_DCO_ON (1 << 4) // DCO ON/OFF +#define B_AUDIO_ASYNC (1 << 1) // WCP reserved + +// **************************************************************** +// PARAMETER AREA (PARAM) MCU<->DSP COMMUNICATION DEFINITIONS +// **************************************************************** +#define C_POND_RED 1L +// below values are defined in the file l1_time.h +//#define D_NSUBB_IDLE 296L +//#define D_NSUBB_DEDIC 30L +#define D_FB_THR_DET_IACQ 0x3333L +#define D_FB_THR_DET_TRACK 0x28f6L +#define D_DC_OFF_THRES 0x7fffL +#define D_DUMMY_THRES 17408L +#define D_DEM_POND_GEWL 26624L +#define D_DEM_POND_RED 20152L +#define D_HOLE 0L +#define D_TRANSFER_RATE 0x6666L + +// Full Rate vocoder definitions. +#define D_MACCTHRESH1 7872L +#define D_MLDT -4L +#define D_MACCTHRESH 7872L +#define D_GU 5772L +#define D_GO 7872L +#define D_ATTMAX 53L +#define D_SM -892L +#define D_B 208L +#define D_SD_MIN_THR_TCHFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHFS 1700L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHFS 99L //(160L *C_POND_RED) + +#if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // Frequency burst definitions + #define D_FB_MARGIN_BEG 24 + #define D_FB_MARGIN_END 22 + + // V42bis definitions + #define D_V42B_SWITCH_HYST 16L + #define D_V42B_SWITCH_MIN 64L + #define D_V42B_SWITCH_MAX 250L + #define D_V42B_RESET_DELAY 10L + + // Latencies definitions + #if (DSP == 33) || (DSP == 34) || (DSP == 35) || (DSP == 36) + // C.f. BUG1404 + #define D_LAT_MCU_BRIDGE 0x000FL + #else + #define D_LAT_MCU_BRIDGE 0x0009L + #endif + + #define D_LAT_MCU_HOM2SAM 0x000CL + + #define D_LAT_MCU_BEF_FAST_ACCESS 0x0005L + #define D_LAT_DSP_AFTER_SAM 0x0004L + + // Background Task in GSM mode: Initialization. + #define D_GSM_BGD_MGT 0L + +#if (CHIPSET == 4) + #define D_MISC_CONFIG 0L +#elif (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12) + #define D_MISC_CONFIG 1L +#else + #define D_MISC_CONFIG 0L +#endif + +#endif + +// Hall Rate vocoder and ched definitions. + +#define D_SD_MIN_THR_TCHHS 37L +#define D_MA_MIN_THR_TCHHS 344L +#define D_MD_MAX_THR_TCHHS 2175L +#define D_MD1_MAX_THR_TCHHS 138L +#define D_SD_AV_THR_TCHHS 1845L +#define D_WED_FIL_TC 0x7c00L +#define D_WED_FIL_INI 4650L +#define D_X_MIN 15L +#define D_X_MAX 23L +#define D_Y_MIN 703L +#define D_Y_MAX 2460L +#define D_SLOPE 135L +#define D_WED_DIFF_THRESHOLD 406L +#define D_MABFI_MIN_THR_TCHHS 5320L +#define D_LDT_HR -5 +#define D_MACCTRESH_HR 6500 +#define D_MACCTRESH1_HR 6500 +#define D_GU_HR 2620 +#define D_GO_HR 3700 +#define D_B_HR 182 +#define D_SM_HR -1608 +#define D_ATTMAX_HR 53 + +// Enhanced Full Rate vocoder and ched definitions. + +#define C_MLDT_EFR -4 +#define C_MACCTHRESH_EFR 8000 +#define C_MACCTHRESH1_EFR 8000 +#define C_GU_EFR 4522 +#define C_GO_EFR 6500 +#define C_B_EFR 174 +#define C_SM_EFR -878 +#define C_ATTMAX_EFR 53 +#define D_SD_MIN_THR_TCHEFS 15L //(24L *C_POND_RED) +#define D_MA_MIN_THR_TCHEFS 738L //(1200L *C_POND_RED) +#define D_MD_MAX_THR_TCHEFS 1230L //(2000L *C_POND_RED) +#define D_MD1_MAX_THR_TCHEFS 99L //(160L *C_POND_RED) + + +// Integrated Data Services definitions. +#define D_MAX_OVSPD_UL 8 +// Detect frames containing 90% of 1s as synchro frames +#define D_SYNC_THRES 0x3f50 +// IDLE frames are only frames with 100 % of 1s +#define D_IDLE_THRES 0x4000 +#define D_M1_THRES 5 +#define D_MAX_OVSP_DL 8 + +// d_ra_act: bit field definition +#define B_F48BLK 5 + +// Mask for b_itc information (d_ra_conf) +#define CE_MASK 0x04 + +#define D_FACCH_THR 0 +#define D_DSP_TEST 0 +#define D_VERSION_NUMBER 0 +#define D_TI_VERSION 0 + + +/*------------------------------------------------------------------------------*/ +/* */ +/* DEFINITIONS FOR DSP <-> MCU COMMUNICATION. */ +/* ++++++++++++++++++++++++++++++++++++++++++ */ +/* */ +/*------------------------------------------------------------------------------*/ +// COMMUNICATION Interrupt definition +//------------------------------------ +#define ALL_16BIT 0xffffL +#define B_GSM_PAGE (1 << 0) +#define B_GSM_TASK (1 << 1) +#define B_MISC_PAGE (1 << 2) +#define B_MISC_TASK (1 << 3) + +#define B_GSM_PAGE_MASK (ALL_16BIT ^ B_GSM_PAGE) +#define B_GSM_TASK_MASK (ALL_16BIT ^ B_GSM_TASK) +#define B_MISC_PAGE_MASK (ALL_16BIT ^ B_MISC_PAGE) +#define B_MISC_TASK_MASK (ALL_16BIT ^ B_MISC_TASK) + +// Common definition +//---------------------------------- +// Index to *_DEMOD* arrays. +#define D_TOA 0 // Time Of Arrival. +#define D_PM 1 // Power Measurement. +#define D_ANGLE 2 // Angle (AFC correction) +#define D_SNR 3 // Signal / Noise Ratio. + +// Bit name/position definitions. +#define B_FIRE0 5 // Fire result bit 0. (00 -> NO ERROR) (01 -> ERROR CORRECTED) +#define B_FIRE1 6 // Fire result bit 1. (10 -> ERROR) (11 -> unused) +#define B_SCH_CRC 8 // CRC result for SB decoding. (1 for ERROR). +#define B_BLUD 15 // Uplink,Downlink data block Present. (1 for PRESENT). +#define B_AF 14 // Activity bit: 1 if data block is valid. +#define B_BFI 2 // Bad Frame Indicator +#define B_UFI 0 // UNRELIABLE FRAME Indicator +#define B_ECRC 9 // Enhanced full rate CRC bit +#define B_EMPTY_BLOCK 10 // for voice memo purpose, this bit is used to determine + +#if (DEBUG_DEDIC_TCH_BLOCK_STAT == 1) + #define FACCH_GOOD 10 + #define FACCH_BAD 11 +#endif + +#if (AMR == 1) + // Place of the RX type in the AMR block header + #define RX_TYPE_SHIFT 3 + #define RX_TYPE_MASK 0x0038 + + // Place of the vocoder type in the AMR block header + #define VOCODER_TYPE_SHIFT 0 + #define VOCODER_TYPE_MASK 0x0007 + + // List of the possible RX types in a_dd block + #define SPEECH_GOOD 0 + #define SPEECH_DEGRADED 1 + #define ONSET 2 + #define SPEECH_BAD 3 + #define SID_FIRST 4 + #define SID_UPDATE 5 + #define SID_BAD 6 + #define AMR_NO_DATA 7 + #define AMR_INHIBIT 8 + + // List of possible RX types in RATSCCH block + #define C_RATSCCH_GOOD 5 + + // List of the possible AMR channel rate + #define AMR_CHANNEL_4_75 0 + #define AMR_CHANNEL_5_15 1 + #define AMR_CHANNEL_5_9 2 + #define AMR_CHANNEL_6_7 3 + #define AMR_CHANNEL_7_4 4 + #define AMR_CHANNEL_7_95 5 + #define AMR_CHANNEL_10_2 6 + #define AMR_CHANNEL_12_2 7 + + // Types of RATSCCH blocks + #define C_RATSCCH_UNKNOWN 0 + #define C_RATSCCH_CMI_PHASE_REQ 1 + #define C_RATSCCH_AMR_CONFIG_REQ_MAIN 2 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT 3 + #define C_RATSCCH_AMR_CONFIG_REQ_ALT_IGNORE 4 // Alternative AMR_CONFIG_REQ with updates coming in the next THRES_REQ block + #define C_RATSCCH_THRES_REQ 5 + + // These flags define a bitmap that indicates which AMR parameters are being modified by a RATSCCH + #define C_AMR_CHANGE_CMIP 0 + #define C_AMR_CHANGE_ACS 1 + #define C_AMR_CHANGE_ICM 2 + #define C_AMR_CHANGE_THR1 3 + #define C_AMR_CHANGE_THR2 4 + #define C_AMR_CHANGE_THR3 5 + #define C_AMR_CHANGE_HYST1 6 + #define C_AMR_CHANGE_HYST2 7 + #define C_AMR_CHANGE_HYST3 8 + + // CMIP default value + #define C_AMR_CMIP_DEFAULT 1 // According to ETSI specification 05.09, cmip is always 1 by default (new channel, handover...) + +#endif +// "d_ctrl_tch" bits positions for TCH configuration. +#define B_CHAN_MODE 0 +#define B_CHAN_TYPE 4 +#define B_RESET_SACCH 6 +#define B_VOCODER_ON 7 +#define B_SYNC_TCH_UL 8 +#if (AMR == 1) + #define B_SYNC_AMR 9 +#else +#define B_SYNC_TCH_DL 9 +#endif +#define B_STOP_TCH_UL 10 +#define B_STOP_TCH_DL 11 +#define B_TCH_LOOP 12 +#define B_SUBCHANNEL 15 + +// "d_ctrl_abb" bits positions for conditionnal loading of abb registers. +#define B_RAMP 0 +#if ((ANLG_FAM == 1) || (ANLG_FAM == 2) || (ANLG_FAM == 3)) + #define B_BULRAMPDEL 3 // Note: this name is changed + #define B_BULRAMPDEL2 2 // Note: this name is changed + #define B_BULRAMPDEL_BIS 9 + #define B_BULRAMPDEL2_BIS 10 +#endif +#define B_AFC 4 + +// "d_ctrl_system" bits positions. +#define B_TSQ 0 +#define B_BCCH_FREQ_IND 3 +#define B_TASK_ABORT 15 // Abort RF tasks for DSP. diff --git a/src/target/firmware/include/calypso/misc.h b/src/target/firmware/include/calypso/misc.h new file mode 100644 index 00000000..4e480938 --- /dev/null +++ b/src/target/firmware/include/calypso/misc.h @@ -0,0 +1,8 @@ +#ifndef _CAL_MISC_H +#define _CAL_MISC_H + +void memdump_range(unsigned int *ptr, unsigned int len); +void dump_mem(void); +void dump_dev_id(void); + +#endif /* _CAL_MISC_H */ diff --git a/src/target/firmware/include/calypso/rtc.h b/src/target/firmware/include/calypso/rtc.h new file mode 100644 index 00000000..17528d00 --- /dev/null +++ b/src/target/firmware/include/calypso/rtc.h @@ -0,0 +1,6 @@ +#ifndef _CALYPSO_RTC_H +#define _CALYPSO_RTC_H + +void rtc_init(void); + +#endif /* _CALYPSO_RTC_H */ diff --git a/src/target/firmware/include/calypso/timer.h b/src/target/firmware/include/calypso/timer.h new file mode 100644 index 00000000..694e4ebc --- /dev/null +++ b/src/target/firmware/include/calypso/timer.h @@ -0,0 +1,25 @@ +#ifndef _CAL_TIMER_H +#define _CAL_TIMER_H + +/* Enable or Disable a timer */ +void hwtimer_enable(int num, int on); + +/* Configure pre-scaler and if timer is auto-reload */ +void hwtimer_config(int num, uint8_t pre_scale, int auto_reload); + +/* Load a timer with the given value */ +void hwtimer_load(int num, uint16_t val); + +/* Read the current timer value */ +uint16_t hwtimer_read(int num); + +/* Enable or disable the watchdog */ +void wdog_enable(int on); + +/* Reset cpu using watchdog */ +void wdog_reset(void); + +/* power up the timers */ +void hwtimer_init(void); + +#endif /* _CAL_TIMER_H */ diff --git a/src/target/firmware/include/calypso/tpu.h b/src/target/firmware/include/calypso/tpu.h new file mode 100644 index 00000000..2db95aa1 --- /dev/null +++ b/src/target/firmware/include/calypso/tpu.h @@ -0,0 +1,122 @@ +#ifndef _CALYPSO_TPU_H +#define _CALYPSO_TPU_H + +#define BITS_PER_TDMA 1250 +#define QBITS_PER_TDMA (BITS_PER_TDMA * 4) /* 5000 */ +#define TPU_RANGE QBITS_PER_TDMA +#define SWITCH_TIME (TPU_RANGE-10) + +/* Assert or de-assert TPU reset */ +void tpu_reset(int active); +/* Enable or Disable a new scenario loaded into the TPU */ +void tpu_enable(int active); +/* Enable or Disable the clock of teh TPU Module */ +void tpu_clk_enable(int active); +/* Enable Frame Interrupt generation on next frame. DSP will reset it */ +void tpu_dsp_frameirq_enable(void); +/* Is a Frame interrupt still pending for the DSP ? */ +int tpu_dsp_fameirq_pending(void); +/* Rewind the TPU, i.e. restart enqueueing instructions at the base addr */ +void tpu_rewind(void); +/* Enqueue a raw TPU instruction */ +void tpu_enqueue(uint16_t instr); +/* Initialize TPU and TPU driver */ +void tpu_init(void); +/* (Busy)Wait until TPU is idle */ +void tpu_wait_idle(void); +/* Enable FRAME interrupt generation */ +void tpu_frame_irq_en(int mcu, int dsp); +/* Force the generation of a DSP interrupt */ +void tpu_force_dsp_frame_irq(void); + +/* Get the current TPU SYNCHRO register */ +uint16_t tpu_get_synchro(void); +/* Get the current TPU OFFSET register */ +uint16_t tpu_get_offset(void); + +enum tpu_instr { + TPU_INSTR_AT = (1 << 13), + TPU_INSTR_OFFSET = (2 << 13), + TPU_INSTR_SYNCHRO = (3 << 13), /* Loading delta synchro value in TPU synchro register */ + TPU_INSTR_WAIT = (5 << 13), /* Wait a certain period (in GSM qbits) */ + TPU_INSTR_SLEEP = (0 << 13), /* Stop the sequencer by disabling TPU ENABLE bit in ctrl reg */ + /* data processing */ + TPU_INSTR_MOVE = (4 << 13), +}; + +/* Addresses internal to the TPU, only accessible via MOVE */ +enum tpu_reg_int { + TPUI_TSP_CTRL1 = 0x00, + TPUI_TSP_CTRL2 = 0x01, + TPUI_TX_1 = 0x04, + TPUI_TX_2 = 0x03, + TPUI_TX_3 = 0x02, + TPUI_TX_4 = 0x05, + TPUI_TSP_ACT_L = 0x06, + TPUI_TSP_ACT_U = 0x07, + TPUI_TSP_SET1 = 0x09, + TPUI_TSP_SET2 = 0x0a, + TPUI_TSP_SET3 = 0x0b, + TPUI_DSP_INT_PG = 0x10, + TPUI_GAUGING_EN = 0x11, +}; + +enum tpui_ctrl2_bits { + TPUI_CTRL2_RD = (1 << 0), + TPUI_CTRL2_WR = (1 << 1), +}; + +static inline uint16_t tpu_mod5000(int16_t time) +{ + if (time < 0) + return time + 5000; + if (time >= 5000) + return time - 5000; + return time; +} + +/* Enqueue a SLEEP operation (stop sequencer by disabling TPU ENABLE bit) */ +static inline void tpu_enq_sleep(void) +{ + tpu_enqueue(TPU_INSTR_SLEEP); +} + +/* Enqueue a MOVE operation */ +static inline void tpu_enq_move(uint8_t addr, uint8_t data) +{ + tpu_enqueue(TPU_INSTR_MOVE | (data << 5) | (addr & 0x1f)); +} + +/* Enqueue an AT operation */ +static inline void tpu_enq_at(int16_t time) +{ + tpu_enqueue(TPU_INSTR_AT | tpu_mod5000(time)); +} + +/* Enqueue a SYNC operation */ +static inline void tpu_enq_sync(int16_t time) +{ + tpu_enqueue(TPU_INSTR_SYNCHRO | time); +} + +/* Enqueue a WAIT operation */ +static inline void tpu_enq_wait(int16_t time) +{ + tpu_enqueue(TPU_INSTR_WAIT | time); +} + +/* Enqueue an OFFSET operation */ +static inline void tpu_enq_offset(int16_t time) +{ + tpu_enqueue(TPU_INSTR_OFFSET | time); +} + +static inline void tpu_enq_dsp_irq(void) +{ + tpu_enq_move(TPUI_DSP_INT_PG, 0x0001); +} + +/* add two numbers, modulo 5000, and ensure the result is positive */ +uint16_t add_mod5000(int16_t a, int16_t b); + +#endif /* _CALYPSO_TPU_H */ diff --git a/src/target/firmware/include/calypso/tsp.h b/src/target/firmware/include/calypso/tsp.h new file mode 100644 index 00000000..0252f36e --- /dev/null +++ b/src/target/firmware/include/calypso/tsp.h @@ -0,0 +1,30 @@ +#ifndef _CALYPSO_TSP_H +#define _CALYPSO_TSP_H + +#define TSPACT(x) (1 << x) + +/* initiate a TSP write through the TPU */ +void tsp_write(uint8_t dev_idx, uint8_t bitlen, uint32_t dout); + +/* Configure clock edge and chip enable polarity for a device */ +void tsp_setup(uint8_t dev_idx, int clk_rising, int en_positive, int en_edge); + +/* Obtain the current tspact state */ +uint16_t tsp_act_state(void); + +/* Update the TSPACT state, including enable and disable */ +void tsp_act_update(uint16_t new_act); + +/* Enable one or multiple TSPACT signals */ +void tsp_act_enable(uint16_t bitmask); + +/* Disable one or multiple TSPACT signals */ +void tsp_act_disable(uint16_t bitmask); + +/* Toggle one or multiple TSPACT signals */ +void tsp_act_toggle(uint16_t bitmask); + +/* Initialize TSP driver */ +void tsp_init(void); + +#endif /* _CALYPSO_TSP_H */ diff --git a/src/target/firmware/include/calypso/uart.h b/src/target/firmware/include/calypso/uart.h new file mode 100644 index 00000000..7eb925ed --- /dev/null +++ b/src/target/firmware/include/calypso/uart.h @@ -0,0 +1,32 @@ +#ifndef _CAL_UART_H +#define _CAL_UART_H + +#include <stdint.h> + +enum uart_baudrate { + UART_38400, + UART_57600, + UART_115200, + UART_230400, + UART_460800, + UART_614400, + UART_921600, +}; + +void uart_init(uint8_t uart, uint8_t interrupts); +void uart_putchar_wait(uint8_t uart, int c); +int uart_putchar_nb(uint8_t uart, int c); +int uart_getchar_nb(uint8_t uart, uint8_t *ch); +int uart_tx_busy(uint8_t uart); +int uart_baudrate(uint8_t uart, enum uart_baudrate bdrt); + +enum uart_irq { + UART_IRQ_TX_EMPTY, + UART_IRQ_RX_CHAR, +}; + +void uart_irq_enable(uint8_t uart, enum uart_irq irq, int on); + +void uart_poll(uint8_t uart); + +#endif /* _CAL_UART_H */ diff --git a/src/target/firmware/include/cfi_flash.h b/src/target/firmware/include/cfi_flash.h new file mode 100644 index 00000000..2ab8842a --- /dev/null +++ b/src/target/firmware/include/cfi_flash.h @@ -0,0 +1,68 @@ + +#ifndef _CFI_FLASH_H +#define _CFI_FLASH_H + +#include <stdint.h> + + +#define CFI_FLASH_MAX_ERASE_REGIONS 4 + +/* structure of erase region descriptor */ +struct cfi_region { + uint16_t b_count; + uint16_t b_size; +} __attribute__((packed)); + + +/* structure of cfi query response */ +struct cfi_query { + uint8_t qry[3]; + uint16_t p_id; + uint16_t p_adr; + uint16_t a_id; + uint16_t a_adr; + uint8_t vcc_min; + uint8_t vcc_max; + uint8_t vpp_min; + uint8_t vpp_max; + uint8_t word_write_timeout_typ; + uint8_t buf_write_timeout_typ; + uint8_t block_erase_timeout_typ; + uint8_t chip_erase_timeout_typ; + uint8_t word_write_timeout_max; + uint8_t buf_write_timeout_max; + uint8_t block_erase_timeout_max; + uint8_t chip_erase_timeout_max; + uint8_t dev_size; + uint16_t interface_desc; + uint16_t max_buf_write_size; + uint8_t num_erase_regions; + struct cfi_region erase_regions[CFI_FLASH_MAX_ERASE_REGIONS]; +} __attribute__((packed)); + +typedef struct { + void *f_base; + + uint32_t f_size; + + uint16_t f_manuf_id; + uint16_t f_dev_id; + + struct cfi_query f_query; +} cfi_flash_t; + +typedef uint8_t flash_lock; + +void flash_init(cfi_flash_t *flash, void *base_addr); + +void flash_dump_info(cfi_flash_t *flash); + +flash_lock flash_block_getlock(cfi_flash_t *base_addr, uint32_t block_offset); + +void flash_block_unlock(cfi_flash_t *base_addr, uint32_t block_offset); +void flash_block_lock(cfi_flash_t *base_addr, uint32_t block_offset); +void flash_block_lockdown(cfi_flash_t *base_addr, uint32_t block_offset); + +void flash_block_erase(cfi_flash_t *base_addr, uint32_t block_addr); + +#endif diff --git a/src/target/firmware/include/comm/msgb.h b/src/target/firmware/include/comm/msgb.h new file mode 100644 index 00000000..3113fa2c --- /dev/null +++ b/src/target/firmware/include/comm/msgb.h @@ -0,0 +1,161 @@ +#ifndef _MSGB_H +#define _MSGB_H + +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <osmocore/linuxlist.h> +#include <console.h> + +struct msgb { + struct llist_head list; + + /* the layer 1 header, if any */ + unsigned char *l1h; + /* the A-bis layer 2 header: OML, RSL(RLL), NS */ + unsigned char *l2h; + /* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */ + unsigned char *l3h; + + uint16_t data_len; + uint16_t len; + + unsigned char *head; /* start of buffer */ + unsigned char *tail; /* end of message */ + unsigned char *data; /* start of message */ + unsigned char _data[0]; +}; + +extern struct msgb *msgb_alloc(uint16_t size, const char *name); +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); +extern void msgb_reset(struct msgb *m); + +#define msgb_l1(m) ((void *)(m->l1h)) +#define msgb_l2(m) ((void *)(m->l2h)) +#define msgb_l3(m) ((void *)(m->l3h)) + +static inline unsigned int msgb_l1len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l1(msgb); +} + +static inline unsigned int msgb_l2len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l2(msgb); +} + +static inline unsigned int msgb_l3len(const struct msgb *msgb) +{ + return msgb->tail - (uint8_t *)msgb_l3(msgb); +} + +static inline unsigned int msgb_headlen(const struct msgb *msgb) +{ + return msgb->len - msgb->data_len; +} +static inline int msgb_tailroom(const struct msgb *msgb) +{ + return (msgb->head + msgb->data_len) - msgb->tail; +} +static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->tail; + + /* we intentionally call cons_puts() here to display an allocation + * failure on the _other_ serial port (i.e. the one that doesn't + * have the HDLC layer on it */ + if (msgb_tailroom(msgb) < len) + cons_puts("msgb_tailroom insufficient!\n"); + + msgb->tail += len; + msgb->len += len; + return tmp; +} +static inline void msgb_put_u8(struct msgb *msgb, uint8_t word) +{ + uint8_t *space = msgb_put(msgb, 1); + space[0] = word & 0xFF; +} +static inline void msgb_put_u16(struct msgb *msgb, uint16_t word) +{ + uint8_t *space = msgb_put(msgb, 2); + space[0] = word >> 8 & 0xFF; + space[1] = word & 0xFF; +} +static inline void msgb_put_u32(struct msgb *msgb, uint32_t word) +{ + uint8_t *space = msgb_put(msgb, 4); + space[0] = word >> 24 & 0xFF; + space[1] = word >> 16 & 0xFF; + space[2] = word >> 8 & 0xFF; + space[3] = word & 0xFF; +} +static inline unsigned char *msgb_get(struct msgb *msgb, unsigned int len) +{ + unsigned char *tmp = msgb->data; + msgb->data += len; + msgb->len -= len; + return tmp; +} +static inline uint8_t msgb_get_u8(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 1); + return space[0]; +} +static inline uint16_t msgb_get_u16(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 2); + return space[0] << 8 | space[1]; +} +static inline uint32_t msgb_get_u32(struct msgb *msgb) +{ + uint8_t *space = msgb_get(msgb, 4); + return space[0] << 24 | space[1] << 16 | space[2] << 8 | space[3]; +} +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; +} + +/* 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, + const char *name) +{ + struct msgb *msg = msgb_alloc(size, name); + if (msg) + msgb_reserve(msg, headroom); + return msg; +} + +#endif /* _MSGB_H */ diff --git a/src/target/firmware/include/comm/sercomm.h b/src/target/firmware/include/comm/sercomm.h new file mode 100644 index 00000000..8fbbff97 --- /dev/null +++ b/src/target/firmware/include/comm/sercomm.h @@ -0,0 +1,57 @@ +#ifndef _SERCOMM_H +#define _SERCOMM_H + +/* SERCOMM layer on UART1 (modem UART) */ + +#include <osmocore/msgb.h> + +#define SERCOMM_UART_NR 1 + +#define HDLC_FLAG 0x7E +#define HDLC_ESCAPE 0x7D + +#define HDLC_C_UI 0x03 +#define HDLC_C_P_BIT (1 << 4) +#define HDLC_C_F_BIT (1 << 4) + +/* a low sercomm_dlci means high priority. A high DLCI means low priority */ +enum sercomm_dlci { + SC_DLCI_HIGHEST = 0, + SC_DLCI_DEBUG = 4, + SC_DLCI_L1A_L23 = 5, + SC_DLCI_LOADER = 9, + SC_DLCI_CONSOLE = 10, + SC_DLCI_ECHO = 128, + _SC_DLCI_MAX +}; + +void sercomm_init(void); +int sercomm_initialized(void); + +/* User Interface: Tx */ + +/* user interface for transmitting messages for a given DLCI */ +void sercomm_sendmsg(uint8_t dlci, struct msgb *msg); +/* how deep is the Tx queue for a given DLCI */ +unsigned int sercomm_tx_queue_depth(uint8_t dlci); + +/* User Interface: Rx */ + +/* receiving messages for a given DLCI */ +typedef void (*dlci_cb_t)(uint8_t dlci, struct msgb *msg); +int sercomm_register_rx_cb(uint8_t dlci, dlci_cb_t cb); + +/* Driver Interface */ + +/* fetch one octet of to-be-transmitted serial data. returns 0 if no more data */ +int sercomm_drv_pull(uint8_t *ch); +/* the driver has received one byte, pass it into sercomm layer. + returns 1 in case of success, 0 in case of unrecognized char */ +int sercomm_drv_rx_char(uint8_t ch); + +static inline struct msgb *sercomm_alloc_msgb(unsigned int len) +{ + return msgb_alloc_headroom(len+4, 4, "sercomm_tx"); +} + +#endif /* _SERCOMM_H */ diff --git a/src/target/firmware/include/comm/sercomm_cons.h b/src/target/firmware/include/comm/sercomm_cons.h new file mode 100644 index 00000000..11f66545 --- /dev/null +++ b/src/target/firmware/include/comm/sercomm_cons.h @@ -0,0 +1,10 @@ +#ifndef _SERCOMM_CONS_H +#define _SERCOMM_CONS_H + +/* how large buffers do we allocate? */ +#define SERCOMM_CONS_ALLOC 256 + +int sercomm_puts(const char *s); +int sercomm_putchar(int c); + +#endif /* _SERCOMM_CONS_H */ diff --git a/src/target/firmware/include/comm/timer.h b/src/target/firmware/include/comm/timer.h new file mode 100644 index 00000000..814d2c60 --- /dev/null +++ b/src/target/firmware/include/comm/timer.h @@ -0,0 +1,76 @@ +/* + * (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 <osmocore/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; + unsigned long expires; + + unsigned int active : 1; + unsigned int handled : 1; + unsigned int in_list : 1; + + void (*cb)(void*); + void *data; +}; + +extern unsigned long volatile jiffies; + +/** + * timer management + */ +void add_timer(struct timer_list *timer); +void schedule_timer(struct timer_list *timer, int miliseconds); +void del_timer(struct timer_list *timer); +int timer_pending(struct timer_list *timer); + + +/** + * internal timer list management + */ +void prepare_timers(void); +int update_timers(void); +int timer_check(void); + +void timer_init(void); + +#endif diff --git a/src/target/firmware/include/console.h b/src/target/firmware/include/console.h new file mode 100644 index 00000000..7146e990 --- /dev/null +++ b/src/target/firmware/include/console.h @@ -0,0 +1,20 @@ +#ifndef _CONSOLE_H +#define _CONSOLE_H + +/* This is the direct (IRQ driven) UART console, bypassing the HDLC layer. + * You should not need to call those functions unless you've decided to + * not use the HLDC layer or have a device with two UARTs */ + +int cons_rb_append(const char *data, int len); +int cons_puts(const char *s); +int cons_putchar(char c); +int cons_rb_flush(void); +void cons_init(void); + +/* We want the console on UART 0 (IRDA UART) */ +#define CONS_UART_NR 0 + +/* Size of the static ring-buffer that we keep for console print messages */ +#define CONS_RB_SIZE 4096 + +#endif /* _CONSOLE_H */ diff --git a/src/target/firmware/include/ctors.h b/src/target/firmware/include/ctors.h new file mode 100644 index 00000000..ee4c7b3e --- /dev/null +++ b/src/target/firmware/include/ctors.h @@ -0,0 +1,16 @@ +#ifndef _CTORS_H +#define _CTORS_H + +#if 0 +/* only supported by gcc 3.4 or later */ +#define __ctor_data __attribute__ ((constructor) (100)) +#define __ctor_board __attribute__ ((constructor) (200)) +#else +#define __ctor_data __attribute__ ((constructor)) +#define __ctor_board __attribute__ ((constructor)) +#endif + +/* iterate over list of constructor functions and call each element */ +void do_global_ctors(const char *ctors_start, const char *ctors_end); + +#endif diff --git a/src/target/firmware/include/ctype.h b/src/target/firmware/include/ctype.h new file mode 100644 index 00000000..afa36392 --- /dev/null +++ b/src/target/firmware/include/ctype.h @@ -0,0 +1,54 @@ +#ifndef _LINUX_CTYPE_H +#define _LINUX_CTYPE_H + +/* + * NOTE! This ctype does not handle EOF like the standard C + * library is required to. + */ + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; + +#define __ismask(x) (_ctype[(int)(unsigned char)(x)]) + +#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) +#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) +#define iscntrl(c) ((__ismask(c)&(_C)) != 0) +#define isdigit(c) ((__ismask(c)&(_D)) != 0) +#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) +#define islower(c) ((__ismask(c)&(_L)) != 0) +#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) +#define ispunct(c) ((__ismask(c)&(_P)) != 0) +#define isspace(c) ((__ismask(c)&(_S)) != 0) +#define isupper(c) ((__ismask(c)&(_U)) != 0) +#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) + +#define isascii(c) (((unsigned char)(c))<=0x7f) +#define toascii(c) (((unsigned char)(c))&0x7f) + +static inline unsigned char __tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#define tolower(c) __tolower(c) +#define toupper(c) __toupper(c) + +#endif diff --git a/src/target/firmware/include/debug.h b/src/target/firmware/include/debug.h new file mode 100644 index 00000000..27c4185d --- /dev/null +++ b/src/target/firmware/include/debug.h @@ -0,0 +1,31 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/* + * Check at compile time that something is of a particular type. + * Always evaluates to 1 so you may use it easily in comparisons. + */ +#define typecheck(type,x) \ +({ type __dummy; \ + typeof(x) __dummy2; \ + (void)(&__dummy == &__dummy2); \ + 1; \ +}) + +#ifdef DEBUG +#define dputchar(x) putchar(x) +#define dputs(x) puts(x) +#define dphex(x,y) phex(x,y) +#define printd(x, args ...) printf(x, ## args) +#else +#define dputchar(x) +#define dputs(x) +#define dphex(x,y) +#define printd(x, args ...) +#endif + +#endif /* _DEBUG_H */ diff --git a/src/target/firmware/include/defines.h b/src/target/firmware/include/defines.h new file mode 100644 index 00000000..b3945163 --- /dev/null +++ b/src/target/firmware/include/defines.h @@ -0,0 +1,21 @@ + +#ifndef _DEFINES_H +#define _DEFINES_H + +#define __attribute_const__ __attribute__((__const__)) + +/* type properties */ +#define __packed __attribute__((packed)) +#define __aligned(alignment) __attribute__((aligned(alignment))) +#define __unused __attribute__((unused)) + +/* linkage */ +#define __section(name) __attribute__((section(name))) + +/* force placement in zero-waitstate memory */ +/* XXX: these are placeholders */ +#define __fast_text +#define __fast_data +#define __fast_bss + +#endif /* !_DEFINES_H */ diff --git a/src/target/firmware/include/delay.h b/src/target/firmware/include/delay.h new file mode 100644 index 00000000..0d6f3efd --- /dev/null +++ b/src/target/firmware/include/delay.h @@ -0,0 +1,7 @@ +#ifndef delay_h +#define delay_h + +void delay_ms(unsigned int ms); +void delay_us(unsigned int us); + +#endif diff --git a/src/target/firmware/include/display.h b/src/target/firmware/include/display.h new file mode 100644 index 00000000..b49ae7bd --- /dev/null +++ b/src/target/firmware/include/display.h @@ -0,0 +1,47 @@ +#ifndef _DISPLAY_DRIVER_H +#define _DISPLAY_DRIVER_H + +enum display_attr { + DISP_ATTR_INVERT = 0x0001, +}; + +struct display_driver { + char *name; + void (*init)(void); + void (*set_attr)(unsigned long attr); + void (*unset_attr)(unsigned long attr); + void (*clrscr)(void); + void (*goto_xy)(int xpos, int ypos); + void (*set_color)(int fgcolor, int bgcolor); + int (*putc)(unsigned char c); + int (*puts)(const char *str); +}; + +extern struct display_driver *display; + +static inline void display_init(void) +{ + display->init(); +} +static inline void display_set_attr(unsigned long attr) +{ + display->set_attr(attr); +} +static inline void display_unset_attr(unsigned long attr) +{ + display->unset_attr(attr); +} +static inline void display_clrscr(void) +{ + display->clrscr(); +} +static inline int display_putchar(unsigned char c) +{ + return display->putc(c); +} +int display_puts(const char *s); + +extern const struct display_driver st7558_display; +extern const struct display_driver ssd1783_display; + +#endif diff --git a/src/target/firmware/include/display/ssd1783.h b/src/target/firmware/include/display/ssd1783.h new file mode 100644 index 00000000..c72eebac --- /dev/null +++ b/src/target/firmware/include/display/ssd1783.h @@ -0,0 +1,56 @@ +#ifndef _SSD1783_H +#define _SSD1783_H + +/* Some basic colors */ +#define RED 0x0f00 +#define GREEN 0x00f0 +#define BLUE 0x000f +#define YELLOW 0x0ff0 +#define MAGENTA 0x0f0f +#define CYAN 0x00ff +#define BLACK 0x0000 +#define WHITE 0x0fff + +/* Epson S1D15G10D08B000 commandset */ +#define CMD_DISON 0xaf // Display on +#define CMD_DISOFF 0xae // Display off +#define CMD_DISNOR 0xa6 // Normal display +#define CMD_DISINV 0xa7 // Inverse display +#define CMD_COMSCN 0xbb // Common scan direction +#define CMD_DISCTL 0xca // Display control +#define CMD_SLPIN 0x95 // Sleep in +#define CMD_SLPOUT 0x94 // Sleep out +#define CMD_PASET 0x75 // Page address set +#define CMD_CASET 0x15 // Column address set +#define CMD_DATCTL 0xbc // Data scan direction, etc. +#define CMD_RGBSET8 0xce // 256-color position set +#define CMD_RAMWR 0x5c // Writing to memory +#define CMD_RAMRD 0x5d // Reading from memory +#define CMD_PTLIN 0xa8 // Partial display in +#define CMD_PTLOUT 0xa9 // Partial display out +#define CMD_RMWIN 0xe0 // Read and modify write +#define CMD_RMWOUT 0xee // End +#define CMD_ASCSE 0xaa // Area scroll set +#define CMD_SCSTART 0xab // Scroll start set +#define CMD_OSCON 0xd1 // Internal oscillation on +#define CMD_OSCOFF 0xd2 // Internal oscillation off +#define CMD_PWRCTR 0x20 // Power control +#define CMD_VOLCTR 0x81 // Electronic volume control +#define CMD_VOLUP 0xd6 // Increment electronic control by 1 +#define CMD_VOLDOWN 0xd7 // Decrement electronic control by 1 +#define CMD_TMPGRD 0x82 // Temperature gradient set +#define CMD_EPCTIN 0xcd // Control EEPROM +#define CMD_EPCOUT 0xcc // Cancel EEPROM control +#define CMD_EPMWR 0xfc // Write into EEPROM +#define CMD_EPMRD 0xfd // Read from EEPROM +#define CMD_EPSRRD1 0x7c // Read register 1 +#define CMD_EPSRRD2 0x7d // Read register 2 +#define CMD_NOP 0x25 // NOP instruction + +/* Extended SSD1783 commandset, partly (also has HW graphic functionalities) */ +#define CMD_BIASSET 0xfb // Set bias ratio +#define CMD_FREQSET 0xf2 // Set frequency and n-line inversion +#define CMD_RESCMD 0xa2 // reserved command +#define CMD_PWMSEL 0xf7 // Select PWM/FRC, Full/8 color mode + +#endif diff --git a/src/target/firmware/include/i2c.h b/src/target/firmware/include/i2c.h new file mode 100644 index 00000000..37097a85 --- /dev/null +++ b/src/target/firmware/include/i2c.h @@ -0,0 +1,7 @@ +#ifndef _I2C_H +#define _I2C_H + +int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len); +void i2c_init(int speed, int slaveadd); + +#endif /* I2C_H */ diff --git a/src/target/firmware/include/keypad.h b/src/target/firmware/include/keypad.h new file mode 100644 index 00000000..e2e6519f --- /dev/null +++ b/src/target/firmware/include/keypad.h @@ -0,0 +1,66 @@ +#ifndef _KEYPAD_H +#define _KEYPAD_H + +enum buttons { + BTN_0 = 0x00002000, + BTN_1 = 0x00008000, + BTN_2 = 0x00000400, + BTN_3 = 0x00000020, + BTN_4 = 0x00010000, + BTN_5 = 0x00000800, + BTN_6 = 0x00000040, + BTN_7 = 0x00020000, + BTN_8 = 0x00001000, + BTN_9 = 0x00000080, + BTN_STAR = 0x00040000, + BTN_HASH = 0x00000100, + BTN_MENU = 0x00004000, + BTN_LEFT_SB = 0x00080000, + BTN_RIGHT_SB = 0x00000200, + BTN_UP = 0x00000002, + BTN_DOWN = 0x00000004, + BTN_LEFT = 0x00000008, + BTN_RIGHT = 0x00000010, + BTN_OK = 0x00000001, + BTN_POWER = 0x01000000, +}; + +enum key_codes { + KEY_0 = 0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_STAR, //* + KEY_HASH, //# + KEY_MENU, //center of directional keys + KEY_LEFT_SB, //softbutton + KEY_RIGHT_SB, //softbutton + KEY_UP, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_OK, //green off-hook + KEY_POWER, //red on-hook + KEY_INV = 0xFF +}; + +enum key_states { + PRESSED, + RELEASED, +}; + +void keypad_init(uint8_t interrupts); + +void keypad_poll(); + +typedef void (*key_handler_t)(enum key_codes code, enum key_states state); + +void keypad_set_handler(key_handler_t handler); + +#endif /* KEYPAD_H */ diff --git a/src/target/firmware/include/layer1/afc.h b/src/target/firmware/include/layer1/afc.h new file mode 100644 index 00000000..8b43f8aa --- /dev/null +++ b/src/target/firmware/include/layer1/afc.h @@ -0,0 +1,18 @@ +#ifndef _L1_AFC_H +#define _L1_AFC_H + +#define AFC_SNR_THRESHOLD 2560 /* 2.5 dB in fx6.10 */ + +/* Input a frequency error sample into the AFC averaging */ +void afc_input(int32_t freq_error, uint16_t arfcn, int valid); + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn); + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void); + +/* Reset the AFC to its initial DAC value */ +void afc_reset(void); + +#endif diff --git a/src/target/firmware/include/layer1/agc.h b/src/target/firmware/include/layer1/agc.h new file mode 100644 index 00000000..2b7e46e9 --- /dev/null +++ b/src/target/firmware/include/layer1/agc.h @@ -0,0 +1,7 @@ +#ifndef _L1_AGC_H +#define _L1_AGC_H + +#define to_dbm8(x) ((x)*8) +int16_t agc_inp_dbm8_by_pm(int16_t pm); + +#endif /* _L1_AGC_H */ diff --git a/src/target/firmware/include/layer1/async.h b/src/target/firmware/include/layer1/async.h new file mode 100644 index 00000000..03e33ca5 --- /dev/null +++ b/src/target/firmware/include/layer1/async.h @@ -0,0 +1,38 @@ +#ifndef _L1_ASYNC_H +#define _L1_ASYNC_H + +#include <osmocore/msgb.h> + +#include <layer1/mframe_sched.h> + +/* When altering data structures used by L1 Sync part, we need to + * make sure to temporarily disable IRQ/FIQ to keep data consistent */ +static inline void l1a_lock_sync(void) +{ + arm_disable_interrupts(); +} + +static inline void l1a_unlock_sync(void) +{ + arm_enable_interrupts(); +} + +/* safely enable a message into the L1S TX queue */ +void l1a_txq_msgb_enq(struct llist_head *queue, struct msgb *msg); + +/* request a RACH request at the next multiframe T3 = fn51 */ +void l1a_rach_req(uint8_t fn51, uint8_t ra); + +/* Enable a repeating multiframe task */ +void l1a_mftask_enable(enum mframe_task task); + +/* Disable a repeating multiframe task */ +void l1a_mftask_disable(enum mframe_task task); + +/* Execute pending L1A completions */ +void l1a_compl_execute(void); + +/* Initialize asynchronous part of Layer1 */ +void l1a_init(void); + +#endif diff --git a/src/target/firmware/include/layer1/avg.h b/src/target/firmware/include/layer1/avg.h new file mode 100644 index 00000000..6c5de172 --- /dev/null +++ b/src/target/firmware/include/layer1/avg.h @@ -0,0 +1,23 @@ +#ifndef _L1_AVG_H +#define _L1_AVG_H + +struct running_avg { + /* configuration */ + uint16_t period; /* over how many samples to average */ + uint16_t min_valid; + + int32_t acc_val; + uint16_t num_samples; /* how often did we try to sample? */ + uint16_t num_samples_valid; /* how often did we receive valid samples? */ + + void (*outfn)(struct running_avg *, int32_t avg); + void *priv; +}; + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid); + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg); + +#endif /* _AVG_H */ diff --git a/src/target/firmware/include/layer1/l23_api.h b/src/target/firmware/include/layer1/l23_api.h new file mode 100644 index 00000000..1ba6d173 --- /dev/null +++ b/src/target/firmware/include/layer1/l23_api.h @@ -0,0 +1,15 @@ +#ifndef _L1_L23_API_H +#define _L1_L23_API_H + +#include <stdint.h> +#include <osmocore/msgb.h> +#include <l1a_l23_interface.h> + +void l1a_l23api_init(void); +void l1_queue_for_l2(struct msgb *msg); +struct msgb *l1ctl_msgb_alloc(uint8_t msg_type); +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, uint16_t arfcn); + +void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type); + +#endif /* _L1_L23_API_H */ diff --git a/src/target/firmware/include/layer1/mframe_sched.h b/src/target/firmware/include/layer1/mframe_sched.h new file mode 100644 index 00000000..5e71daca --- /dev/null +++ b/src/target/firmware/include/layer1/mframe_sched.h @@ -0,0 +1,58 @@ +#ifndef _L1_MFRAME_SCHED_H +#define _L1_MFRAME_SCHED_H + +#include <stdint.h> + +enum mframe_task { + MF_TASK_BCCH_NORM, + MF_TASK_BCCH_EXT, + MF_TASK_CCCH, + MF_TASK_CCCH_COMB, + + MF_TASK_SDCCH4_0, + MF_TASK_SDCCH4_1, + MF_TASK_SDCCH4_2, + MF_TASK_SDCCH4_3, + + MF_TASK_SDCCH8_0, + MF_TASK_SDCCH8_1, + MF_TASK_SDCCH8_2, + MF_TASK_SDCCH8_3, + MF_TASK_SDCCH8_4, + MF_TASK_SDCCH8_5, + MF_TASK_SDCCH8_6, + MF_TASK_SDCCH8_7, + + /* Test task: send Normal Burst in all timeslots */ + MF_TASK_UL_ALL_NB, +}; + +enum mf_sched_item_flag { + MF_F_SACCH = (1 << 0), +}; + +/* The scheduler itself */ +struct mframe_scheduler { + uint32_t tasks; + uint32_t tasks_tgt; + uint32_t safe_fn; +}; + +uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts); + +/* Enable a specific task */ +void mframe_enable(enum mframe_task task_id); + +/* Disable a specific task */ +void mframe_disable(enum mframe_task task_id); + +/* Replace the current active set by the new one */ +void mframe_set(uint32_t tasks); + +/* Schedule mframe_sched_items according to current MF TASK list */ +void mframe_schedule(void); + +/* reset the scheduler, disabling all tasks */ +void mframe_reset(void); + +#endif /* _MFRAME_SCHED_H */ diff --git a/src/target/firmware/include/layer1/rfch.h b/src/target/firmware/include/layer1/rfch.h new file mode 100644 index 00000000..344523c3 --- /dev/null +++ b/src/target/firmware/include/layer1/rfch.h @@ -0,0 +1,9 @@ +#ifndef _L1_RFCH_H +#define _L1_RFCH_H + +struct gsm_time; + +void rfch_get_params(struct gsm_time *t, + uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p); + +#endif /* _L1_RFCH_H */ diff --git a/src/target/firmware/include/layer1/sched_gsmtime.h b/src/target/firmware/include/layer1/sched_gsmtime.h new file mode 100644 index 00000000..630c6163 --- /dev/null +++ b/src/target/firmware/include/layer1/sched_gsmtime.h @@ -0,0 +1,24 @@ +#ifndef _L1_SCHED_GSMTIME_H +#define _L1_SCHED_GSMTIME_H + +#include <stdint.h> +#include <osmocore/linuxlist.h> + +struct sched_gsmtime_event { + struct llist_head list; + const struct tdma_sched_item *si; + uint32_t fn; + uint16_t p3; /* parameter for TDMA scheduler */ +}; + +/* initialize the GSMTIME scheduler */ +void sched_gsmtime_init(void); + +/* Scheduling of a single event at a givnen GSM time */ +int sched_gsmtime(const struct tdma_sched_item *si, uint32_t fn, uint16_t p3); + +/* execute all GSMTIME one-shot events pending for 'current_fn' */ +int sched_gsmtime_execute(uint32_t current_fn); + +void sched_gsmtime_reset(void); +#endif diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h new file mode 100644 index 00000000..760b44ce --- /dev/null +++ b/src/target/firmware/include/layer1/sync.h @@ -0,0 +1,171 @@ +#ifndef _L1_SYNC_H +#define _L1_SYNC_H + +#include <osmocore/linuxlist.h> +#include <osmocore/gsm_utils.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <l1a_l23_interface.h> + +/* structure representing L1 sync information about a cell */ +struct l1_cell_info { + /* on which ARFCN (+band) is the cell? */ + uint16_t arfcn; + /* what's the BSIC of the cell (from SCH burst decoding) */ + uint8_t bsic; + /* Combined or non-combined CCCH */ + uint8_t ccch_mode; /* enum ccch_mode */ + /* whats the delta of the cells current GSM frame number + * compared to our current local frame number */ + int32_t fn_offset; + /* how much does the TPU need adjustment (delta) to synchronize + * with the cells burst */ + uint32_t time_alignment; + /* FIXME: should we also store the AFC value? */ +}; + +enum l1s_chan { + L1S_CHAN_MAIN, + L1S_CHAN_SACCH, + _NUM_L1S_CHAN +}; + +enum l1_compl { + L1_COMPL_FB, + L1_COMPL_RACH, + L1_COMPL_TX_NB, +}; + +typedef void l1_compl_cb(enum l1_compl c); + +#define L1S_NUM_COMPL 32 +#define L1S_NUM_NEIGH_CELL 6 + +struct l1s_state { + struct gsm_time current_time; /* current GSM time */ + struct gsm_time next_time; /* GSM time at next TMDMA irq */ + + /* the cell on which we are camping right now */ + struct l1_cell_info serving_cell; + + /* neighbor cell sync info */ + struct l1_cell_info neigh_cell[L1S_NUM_NEIGH_CELL]; + + /* TDMA scheduler */ + struct tdma_scheduler tdma_sched; + + /* Multiframe scheduler */ + struct mframe_scheduler mframe_sched; + + /* The current TPU offset register */ + uint32_t tpu_offset; + + /* Transmit queues of pending packets for main DCCH and ACCH */ + struct llist_head tx_queue[_NUM_L1S_CHAN]; + + /* Which L1A completions are scheduled right now */ + uint32_t scheduled_compl; + /* callbacks for each of the completions */ + l1_compl_cb *completion[L1S_NUM_COMPL]; + + /* Structures below are for L1-task specific parameters, used + * to communicate between l1-sync and l1-async (l23_api) */ + struct { + uint8_t mode; /* FB_MODE 0/1 */ + } fb; + + struct { + /* power measurement l1 task */ + unsigned int mode; + union { + struct { + uint16_t arfcn_start; + uint16_t arfcn_next; + uint16_t arfcn_end; + } range; + }; + struct msgb *msg; + } pm; + + struct { + uint8_t ra; + } rach; + + struct { + enum { + GSM_DCHAN_NONE = 0, + GSM_DCHAN_SDCCH_4, + GSM_DCHAN_SDCCH_8, + GSM_DCHAN_TCH_H, + GSM_DCHAN_TCH_F, + GSM_DCHAN_UNKNOWN, + } type; + + uint8_t scn; + uint8_t tsc; + uint8_t tn; + uint8_t h; + + union { + struct { + uint16_t arfcn; + } h0; + struct { + uint8_t hsn; + uint8_t maio; + uint8_t n; + uint16_t ma[64]; + } h1; + }; + } dedicated; +}; + +extern struct l1s_state l1s; + +struct l1s_meas_hdr { + uint16_t snr; /* signal/noise ratio */ + int16_t toa_qbit; /* time of arrival (qbits) */ + int16_t pm_dbm8; /* power level in dbm/8 */ + int16_t freq_err; /* Frequency error in Hz */ +}; + +int16_t l1s_snr_int(uint16_t snr); +uint16_t l1s_snr_fract(uint16_t snr); + +void l1s_dsp_abort(void); + +void l1s_fb_test(uint8_t base_fn, uint8_t fb_mode); +void l1s_sb_test(uint8_t base_fn); +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn); +void l1s_nb_test(uint8_t base_fn); + +/* schedule a completion */ +void l1s_compl_sched(enum l1_compl c); + +void l1s_init(void); + +/* reset the layer1 as part of synchronizing to a new cell */ +void l1s_reset(void); + +/* init.c */ +void layer1_init(void); + +/* A debug macro to print every TDMA frame */ +#ifdef DEBUG_EVERY_TDMA +#define putchart(x) putchar(x) +#else +#define putchart(x) +#endif + +/* Convert an angle in fx1.15 notatinon into Hz */ +#define BITFREQ_DIV_2PI 43104 /* 270kHz / 2 * pi */ +#define BITFREQ_DIV_PI 86208 /* 270kHz / pi */ +#define ANG2FREQ_SCALING (2<<15) /* 2^15 scaling factor for fx1.15 */ +#define ANGLE_TO_FREQ(angle) ((int16_t)angle * BITFREQ_DIV_PI / ANG2FREQ_SCALING) + +void l1s_reset_hw(void); +void synchronize_tdma(struct l1_cell_info *cinfo); +void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn); +void l1s_time_dump(const struct gsm_time *time); + +#endif /* _L1_SYNC_H */ diff --git a/src/target/firmware/include/layer1/tdma_sched.h b/src/target/firmware/include/layer1/tdma_sched.h new file mode 100644 index 00000000..9486c4ab --- /dev/null +++ b/src/target/firmware/include/layer1/tdma_sched.h @@ -0,0 +1,62 @@ +#ifndef _L1_TDMA_SCHED_H +#define _L1_TDMA_SCHED_H + +#include <stdint.h> + +/* TDMA scheduler */ + +/* The idea of this scheduler is that we have a circular buffer of buckets, + * where each bucket corresponds to one future TDMA frame [interrupt]. Each + * bucket contains of a list of callbacks which are executed when the bucket + * index reaches that particular bucket. */ + +#define TDMASCHED_NUM_FRAMES 25 +#define TDMASCHED_NUM_CB 5 + +typedef int tdma_sched_cb(uint8_t p1, uint8_t p2, uint16_t p3); + +/* A single item in a TDMA scheduler bucket */ +struct tdma_sched_item { + tdma_sched_cb *cb; + uint8_t p1; + uint8_t p2; + uint16_t p3; +}; + +/* A bucket inside the TDMA scheduler */ +struct tdma_sched_bucket { + struct tdma_sched_item item[TDMASCHED_NUM_CB]; + uint8_t num_items; +}; + +/* The scheduler itself, consisting of buckets and a current index */ +struct tdma_scheduler { + struct tdma_sched_bucket bucket[TDMASCHED_NUM_FRAMES]; + uint8_t cur_bucket; +}; + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint8_t p1, uint8_t p2, uint16_t p3); + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint16_t p3); + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void); + +/* Advance TDMA scheduler to the next bucket */ +void tdma_sched_advance(void); + +/* reset the scheduler; erase all scheduled items */ +void tdma_sched_reset(void); + +/* debug function: print number of entries of all TDMA buckets */ +void tdma_sched_dump(void); + + +extern int tdma_end_set(uint8_t p1, uint8_t p2, uint16_t p3); +#define SCHED_ITEM(x, y, z) { .cb = x, .p1 = y, .p2 = z } +#define SCHED_END_FRAME() { .cb = NULL, .p1 = 0, .p2 = 0 } +#define SCHED_END_SET() { .cb = &tdma_end_set, .p1 = 0, .p2 = 0 } + +#endif /* _L1_TDMA_SCHED_H */ diff --git a/src/target/firmware/include/layer1/tpu_window.h b/src/target/firmware/include/layer1/tpu_window.h new file mode 100644 index 00000000..09856c7e --- /dev/null +++ b/src/target/firmware/include/layer1/tpu_window.h @@ -0,0 +1,23 @@ +#ifndef _L1_TPU_CTRL_H +#define _L1_TPU_CTRL_H + +enum l1_rxwin_type { + L1_RXWIN_PW, /* power measurement */ + L1_RXWIN_FB, /* FCCH burst detection */ + L1_RXWIN_SB, /* SCH burst detection */ + L1_RXWIN_NB, /* Normal burst decoding */ + _NUM_L1_RXWIN +}; + +enum l1_txwin_type { + L1_TXWIN_NB, /* Normal burst sending */ + L1_TXWIN_AB, /* RACH burst sending */ + _NUM_L1_TXWIN +}; + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn); +void l1s_tx_win_ctrl(uint16_t arfcn, enum l1_txwin_type wtype, uint8_t pwr, uint8_t tn); + +void tpu_end_scenario(void); + +#endif /* _L1_TPU_CTRL_H */ diff --git a/src/target/firmware/include/manifest.h b/src/target/firmware/include/manifest.h new file mode 100644 index 00000000..6c1b2026 --- /dev/null +++ b/src/target/firmware/include/manifest.h @@ -0,0 +1,10 @@ + +#ifndef _MANIFEST_H +#define _MANIFEST_H + +extern const char *manifest_application; +extern const char *manifest_revision; +extern const char *manifest_board; +extern const char *manifest_environment; + +#endif /* !_MANIFEST_H */ diff --git a/src/target/firmware/include/memory.h b/src/target/firmware/include/memory.h new file mode 100644 index 00000000..b0a0490c --- /dev/null +++ b/src/target/firmware/include/memory.h @@ -0,0 +1,28 @@ +#ifndef _MEMORY_H +#define _MEMORY_H + +#define __arch_getb(a) (*(volatile unsigned char *)(a)) +#define __arch_getw(a) (*(volatile unsigned short *)(a)) +#define __arch_getl(a) (*(volatile unsigned int *)(a)) + +#define __arch_putb(v,a) (*(volatile unsigned char *)(a) = (v)) +#define __arch_putw(v,a) (*(volatile unsigned short *)(a) = (v)) +#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v)) + +#define __raw_writeb(v,a) __arch_putb(v,a) +#define __raw_writew(v,a) __arch_putw(v,a) +#define __raw_writel(v,a) __arch_putl(v,a) + +#define __raw_readb(a) __arch_getb(a) +#define __raw_readw(a) __arch_getw(a) +#define __raw_readl(a) __arch_getl(a) + +#define writeb(v,a) __arch_putb(v,a) +#define writew(v,a) __arch_putw(v,a) +#define writel(v,a) __arch_putl(v,a) + +#define readb(a) __arch_getb(a) +#define readw(a) __arch_getw(a) +#define readl(a) __arch_getl(a) + +#endif /* _MEMORY_H */ diff --git a/src/target/firmware/include/rf/trf6151.h b/src/target/firmware/include/rf/trf6151.h new file mode 100644 index 00000000..c7951c0c --- /dev/null +++ b/src/target/firmware/include/rf/trf6151.h @@ -0,0 +1,50 @@ +#ifndef _TRF6151_H +#define _TRF6151_H + +#include <osmocore/gsm_utils.h> + +/* minimum gain (FE = LOW, VGA at lowest setting */ +#define TRF6151_GAIN_MIN 14 +/* minimum gain (FE = HIGH, VGA at highest setting */ +#define TRF6151_GAIN_MAX 60 +/* Frontend gain if FE = HIGH */ +#define TRF6151_GAIN_FE 20 + +/* initialize (reset + power up) */ +void trf6151_init(void); + +/* switch power off or on */ +void trf6151_power(int on); + +/* set the VGA and RF gain */ +int trf6151_set_gain(uint8_t dbm, int high); + +/* obtain the current total gain of the TRF6151 */ +uint8_t trf6151_get_gain(void); + +/* Request the PLL to be tuned to the given frequency */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink); + +enum trf6151_mode { + TRF6151_IDLE, + TRF6151_RX, + TRF6151_TX, +}; + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode); + +void trf6151_test(uint16_t arfcn); +void trf6151_tx_test(uint16_t arfcn); + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn); + +/* prepare a Tx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_tx_window(int16_t start_qbits, uint16_t arfcn); + +/* Given the expected input level of exp_inp dBm and the target of target_bb + * dBm, configure the RF Frontend with the respective gain */ +void trf6151_compute_gain(int16_t exp_inp, int16_t target_bb); + +#endif /* TRF6151_H */ diff --git a/src/target/firmware/include/rffe.h b/src/target/firmware/include/rffe.h new file mode 100644 index 00000000..950e597e --- /dev/null +++ b/src/target/firmware/include/rffe.h @@ -0,0 +1,19 @@ +#ifndef _RFFE_H +#define _RFFE_H + +#include <osmocore/gsm_utils.h> + +extern const uint8_t system_inherent_gain; + +/* initialize RF Frontend */ +void rffe_init(void); + +/* switch RF Frontend Mode */ +void rffe_mode(enum gsm_band band, int tx); + +/* get current gain of RF frontend (anything between antenna and baseband in dBm */ +uint8_t rffe_get_gain(void); + +void rffe_set_gain(int16_t exp_inp, int16_t target_bb); + +#endif diff --git a/src/target/firmware/include/spi.h b/src/target/firmware/include/spi.h new file mode 100644 index 00000000..0925a9a3 --- /dev/null +++ b/src/target/firmware/include/spi.h @@ -0,0 +1,7 @@ +#ifndef _SPI_H +#define _SPI_H + +void spi_init(void); +int spi_xfer(uint8_t dev_idx, uint8_t bitlen, const void *dout, void *din); + +#endif /* _SPI_H */ diff --git a/src/target/firmware/include/stdint.h b/src/target/firmware/include/stdint.h new file mode 100644 index 00000000..96907992 --- /dev/null +++ b/src/target/firmware/include/stdint.h @@ -0,0 +1,29 @@ +#ifndef OSMO_STDINT_H +#define OSMO_STDINT_H + +/* some older toolchains (like gnuarm-3.x) don't provide a C99 + compliant stdint.h yet, so we define our own here */ + +/* to make matters worse newer gcc with glibc headers have + a incompatible definition of these types. We will use the + gcc'ism of #include_next to include the compiler's libc + header file and then check if it has defined int8_t and + if not we will use our own typedefs */ + +#include_next <stdint.h> + +#ifndef __int8_t_defined +typedef signed char int8_t; +typedef unsigned char uint8_t; + +typedef signed short int16_t; +typedef unsigned short uint16_t; + +typedef signed int int32_t; +typedef unsigned int uint32_t; + +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif + +#endif diff --git a/src/target/firmware/include/stdio.h b/src/target/firmware/include/stdio.h new file mode 100644 index 00000000..15ed6688 --- /dev/null +++ b/src/target/firmware/include/stdio.h @@ -0,0 +1,52 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +#include <sys/types.h> + +int printf(const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); + +#include <stdarg.h> + +int vprintf(const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int puts(const char *s); + +#if 0 +/* start.S based uart console */ +#include <calypso/uart.h> +#define putchar(c) uart_putchar_wait(1, c) +int puts(const char *s); +#endif + +#if 0 +/* regular UART console */ +#include <console.h> +#define putchar(c) cons_putchar(c) +#define _puts(s) cons_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +#if 1 +/* sercomm based console */ +#include <comm/sercomm_cons.h> +#define putchar(c) sercomm_putchar(c) +#define _puts(s) sercomm_puts(s) +#define ARCH_HAS_CONSOLE +#endif + +struct __file { +}; + +typedef struct __file FILE; + +/* non-standard */ +extern void phex(unsigned int c, unsigned int len); + +#endif /* _STDIO_H */ diff --git a/src/target/firmware/include/string.h b/src/target/firmware/include/string.h new file mode 100644 index 00000000..f060659a --- /dev/null +++ b/src/target/firmware/include/string.h @@ -0,0 +1,12 @@ +#ifndef _STRING_H +#define _STRING_H + +#include <sys/types.h> + +size_t strnlen(const char *s, size_t count); +size_t strlen(const char *s); + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); + +#endif diff --git a/src/target/firmware/include/swab.h b/src/target/firmware/include/swab.h new file mode 100644 index 00000000..61be900d --- /dev/null +++ b/src/target/firmware/include/swab.h @@ -0,0 +1,297 @@ +#ifndef _LINUX_SWAB_H +#define _LINUX_SWAB_H + +#include <stdint.h> +#include <defines.h> +#include <asm/swab.h> + +/* + * casts are necessary for constants, because we never know how for sure + * how U/UL/ULL map to uint16_t, uint32_t, uint64_t. At least not in a portable way. + */ +#define ___constant_swab16(x) ((uint16_t)( \ + (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(x) & (uint16_t)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))) + +#define ___constant_swab64(x) ((uint64_t)( \ + (((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56))) + +#define ___constant_swahw32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x0000ffffUL) << 16) | \ + (((uint32_t)(x) & (uint32_t)0xffff0000UL) >> 16))) + +#define ___constant_swahb32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x00ff00ffUL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0xff00ff00UL) >> 8))) + +/* + * Implement the following as inlines, but define the interface using + * macros to allow constant folding when possible: + * ___swab16, ___swab32, ___swab64, ___swahw32, ___swahb32 + */ + +static inline __attribute_const__ uint16_t __fswab16(uint16_t val) +{ +#ifdef __arch_swab16 + return __arch_swab16(val); +#else + return ___constant_swab16(val); +#endif +} + +static inline __attribute_const__ uint32_t __fswab32(uint32_t val) +{ +#ifdef __arch_swab32 + return __arch_swab32(val); +#else + return ___constant_swab32(val); +#endif +} + +static inline __attribute_const__ uint64_t __fswab64(uint64_t val) +{ +#ifdef __arch_swab64 + return __arch_swab64(val); +#elif defined(__SWAB_64_THRU_32__) + uint32_t h = val >> 32; + uint32_t l = val & ((1ULL << 32) - 1); + return (((uint64_t)__fswab32(l)) << 32) | ((uint64_t)(__fswab32(h))); +#else + return ___constant_swab64(val); +#endif +} + +static inline __attribute_const__ uint32_t __fswahw32(uint32_t val) +{ +#ifdef __arch_swahw32 + return __arch_swahw32(val); +#else + return ___constant_swahw32(val); +#endif +} + +static inline __attribute_const__ uint32_t __fswahb32(uint32_t val) +{ +#ifdef __arch_swahb32 + return __arch_swahb32(val); +#else + return ___constant_swahb32(val); +#endif +} + +/** + * __swab16 - return a byteswapped 16-bit value + * @x: value to byteswap + */ +#define __swab16(x) \ + (__builtin_constant_p((uint16_t)(x)) ? \ + ___constant_swab16(x) : \ + __fswab16(x)) + +/** + * __swab32 - return a byteswapped 32-bit value + * @x: value to byteswap + */ +#define __swab32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swab32(x) : \ + __fswab32(x)) + +/** + * __swab64 - return a byteswapped 64-bit value + * @x: value to byteswap + */ +#define __swab64(x) \ + (__builtin_constant_p((uint64_t)(x)) ? \ + ___constant_swab64(x) : \ + __fswab64(x)) + +/** + * __swahw32 - return a word-swapped 32-bit value + * @x: value to wordswap + * + * __swahw32(0x12340000) is 0x00001234 + */ +#define __swahw32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swahw32(x) : \ + __fswahw32(x)) + +/** + * __swahb32 - return a high and low byte-swapped 32-bit value + * @x: value to byteswap + * + * __swahb32(0x12345678) is 0x34127856 + */ +#define __swahb32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swahb32(x) : \ + __fswahb32(x)) + +/** + * __swab16p - return a byteswapped 16-bit value from a pointer + * @p: pointer to a naturally-aligned 16-bit value + */ +static inline uint16_t __swab16p(const uint16_t *p) +{ +#ifdef __arch_swab16p + return __arch_swab16p(p); +#else + return __swab16(*p); +#endif +} + +/** + * __swab32p - return a byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + */ +static inline uint32_t __swab32p(const uint32_t *p) +{ +#ifdef __arch_swab32p + return __arch_swab32p(p); +#else + return __swab32(*p); +#endif +} + +/** + * __swab64p - return a byteswapped 64-bit value from a pointer + * @p: pointer to a naturally-aligned 64-bit value + */ +static inline uint64_t __swab64p(const uint64_t *p) +{ +#ifdef __arch_swab64p + return __arch_swab64p(p); +#else + return __swab64(*p); +#endif +} + +/** + * __swahw32p - return a wordswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping. + */ +static inline uint32_t __swahw32p(const uint32_t *p) +{ +#ifdef __arch_swahw32p + return __arch_swahw32p(p); +#else + return __swahw32(*p); +#endif +} + +/** + * __swahb32p - return a high and low byteswapped 32-bit value from a pointer + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high/low byteswapping. + */ +static inline uint32_t __swahb32p(const uint32_t *p) +{ +#ifdef __arch_swahb32p + return __arch_swahb32p(p); +#else + return __swahb32(*p); +#endif +} + +/** + * __swab16s - byteswap a 16-bit value in-place + * @p: pointer to a naturally-aligned 16-bit value + */ +static inline void __swab16s(uint16_t *p) +{ +#ifdef __arch_swab16s + __arch_swab16s(p); +#else + *p = __swab16p(p); +#endif +} +/** + * __swab32s - byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + */ +static inline void __swab32s(uint32_t *p) +{ +#ifdef __arch_swab32s + __arch_swab32s(p); +#else + *p = __swab32p(p); +#endif +} + +/** + * __swab64s - byteswap a 64-bit value in-place + * @p: pointer to a naturally-aligned 64-bit value + */ +static inline void __swab64s(uint64_t *p) +{ +#ifdef __arch_swab64s + __arch_swab64s(p); +#else + *p = __swab64p(p); +#endif +} + +/** + * __swahw32s - wordswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahw32() for details of wordswapping + */ +static inline void __swahw32s(uint32_t *p) +{ +#ifdef __arch_swahw32s + __arch_swahw32s(p); +#else + *p = __swahw32p(p); +#endif +} + +/** + * __swahb32s - high and low byteswap a 32-bit value in-place + * @p: pointer to a naturally-aligned 32-bit value + * + * See __swahb32() for details of high and low byte swapping + */ +static inline void __swahb32s(uint32_t *p) +{ +#ifdef __arch_swahb32s + __arch_swahb32s(p); +#else + *p = __swahb32p(p); +#endif +} + +# define swab16 __swab16 +# define swab32 __swab32 +# define swab64 __swab64 +# define swahw32 __swahw32 +# define swahb32 __swahb32 +# define swab16p __swab16p +# define swab32p __swab32p +# define swab64p __swab64p +# define swahw32p __swahw32p +# define swahb32p __swahb32p +# define swab16s __swab16s +# define swab32s __swab32s +# define swab64s __swab64s +# define swahw32s __swahw32s +# define swahb32s __swahb32s + +#endif /* _LINUX_SWAB_H */ diff --git a/src/target/firmware/include/uwire.h b/src/target/firmware/include/uwire.h new file mode 100644 index 00000000..6d345534 --- /dev/null +++ b/src/target/firmware/include/uwire.h @@ -0,0 +1,7 @@ +#ifndef _UWIRE_H +#define _UWIRE_H + +void uwire_init(void); +int uwire_xfer(int cs, int bitlen, const void *dout, void *din); + +#endif /* _UWIRE_H */ diff --git a/src/target/firmware/layer1/Makefile b/src/target/firmware/layer1/Makefile new file mode 100644 index 00000000..dd3fcf1f --- /dev/null +++ b/src/target/firmware/layer1/Makefile @@ -0,0 +1,8 @@ + +LIBRARIES+=layer1 +layer1_DIR=layer1 +layer1_SRCS=avg.c agc.c afc.c sync.c tdma_sched.c tpu_window.c init.c l23_api.c \ + mframe_sched.c sched_gsmtime.c async.c rfch.c + +layer1_SRCS += prim_pm.c prim_rach.c prim_tx_nb.c prim_rx_nb.c prim_fbsb.c + diff --git a/src/target/firmware/layer1/afc.c b/src/target/firmware/layer1/afc.c new file mode 100644 index 00000000..3890972d --- /dev/null +++ b/src/target/firmware/layer1/afc.c @@ -0,0 +1,130 @@ +/* AFC (Automatic Frequency Correction) Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <osmocore/gsm_utils.h> + +#include <layer1/afc.h> +#include <layer1/avg.h> +#include <calypso/dsp.h> + +#define AFC_INITIAL_DAC_VALUE -700 + +/* Over how many TDMA frames do we want to average? (this may change in dedicated mode) */ +#define AFC_PERIOD 40 +/* How many of our measurements have to be valid? */ +#define AFC_MIN_MUN_VALID 8 + +/* The actual AFC code */ + +struct afc_state { + struct running_avg ravg; /* running average */ + int16_t dac_value; /* current DAC output value */ + uint16_t arfcn; +}; + +static void afc_ravg_output(struct running_avg *ravg, int32_t avg); + +static struct afc_state afc_state = { + .ravg = { + .outfn = &afc_ravg_output, + .period = AFC_PERIOD, + .min_valid = AFC_MIN_MUN_VALID, + }, + .dac_value = AFC_INITIAL_DAC_VALUE, +}; + +/* The AFC DAC in the ABB has to be configured as follows: + * DAC = 1MHz / 947MHz * FreqErr(Hz) / AFCslop(ppm/LSB) + * where: + * 947 MHz is the center of EGSM + * AFCslope is coded F1.15, thus a normalization factor of 2^15 aplpies + */ + +#define AFC_NORM_FACTOR_GSM ((1<<15) / 947) +#define AFC_NORM_FACTOR_DCS ((1<<15) / 1894) + +/* we assume 8.769ppb per LSB, equals 0.008769 * 32768 == 287 */ +//#define AFC_SLOPE 320 +#define AFC_SLOPE 287 + +/* The DSP can measure the frequency error in the following ranges: + * FB_MODE0: +/- 20 kHz + * FB_MODE1: +/- 4 kHz + * Sync Burst: +/- 1 kHz + * Normal Burst: +/- 400 Hz + */ + +/* Update the AFC with a frequency error, bypassing averaging */ +void afc_correct(int16_t freq_error, uint16_t arfcn) +{ + int32_t afc_norm_factor; + int16_t delta; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_BAND_900: + case GSM_BAND_850: + afc_norm_factor = AFC_NORM_FACTOR_GSM; + break; + default: + afc_norm_factor = AFC_NORM_FACTOR_DCS; + } + + delta = (int16_t) ((afc_norm_factor * (int32_t)freq_error) / AFC_SLOPE); + printd("afc_correct(error=%dHz, arfcn=%u): delta=%d, afc_dac(old=%d,new=%d)\n", + freq_error, arfcn, delta, afc_state.dac_value, afc_state.dac_value+delta); + afc_state.dac_value += delta; + + /* The AFC DAC has only 13 bits */ + if (afc_state.dac_value > 4095) + afc_state.dac_value = 4095; + else if (afc_state.dac_value < -4096) + afc_state.dac_value = -4096; +} + +void afc_reset(void) +{ + afc_state.dac_value = AFC_INITIAL_DAC_VALUE; +} + +void afc_input(int32_t freq_error, uint16_t arfcn, int valid) +{ + afc_state.arfcn = arfcn; + runavg_input(&afc_state.ravg, freq_error, valid); + runavg_check_output(&afc_state.ravg); +} + +/* callback function for runavg */ +static void afc_ravg_output(struct running_avg *ravg, int32_t avg) +{ + afc_correct(avg, afc_state.arfcn); +} + +/* Update DSP with new AFC DAC value to be used for next TDMA frame */ +void afc_load_dsp(void) +{ + dsp_api.db_w->d_afc = afc_state.dac_value; + dsp_api.db_w->d_ctrl_abb |= (1 << B_AFC); +} diff --git a/src/target/firmware/layer1/agc.c b/src/target/firmware/layer1/agc.c new file mode 100644 index 00000000..780e260f --- /dev/null +++ b/src/target/firmware/layer1/agc.c @@ -0,0 +1,62 @@ +/* AFC (Automatic Gain Control) Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <osmocore/gsm_utils.h> +#include <debug.h> +#include <rffe.h> + +#include <layer1/agc.h> +#include <calypso/dsp.h> + +/* compute the input level present at the antenna based on a baseband + * power measurement of the DSP at baseband */ +int16_t agc_inp_dbm8_by_pm(int16_t pm) +{ + /* pm is in 1/8 dBm at baseband */ + int16_t total_gain_dbm8; + + /* compute total current gain */ + total_gain_dbm8 = (system_inherent_gain + rffe_get_gain()) * 8; + + /* subtract gain from power measurement at baseband level */ + return pm - total_gain_dbm8; +} + +uint8_t agc_il_by_dbm8(int16_t dbm8) +{ + uint16_t il; + + /* convert from 1/8 dBm to l1c format: [220..0] in -1/2dBm unit */ + if (dbm8 >= 0) + il = 0; + else + il = -dbm8; + + /* saturate */ + if (il > 4 * 255) + il = 4 * 255; + + return (uint8_t)(il >> 2); +} diff --git a/src/target/firmware/layer1/async.c b/src/target/firmware/layer1/async.c new file mode 100644 index 00000000..41f443ac --- /dev/null +++ b/src/target/firmware/layer1/async.c @@ -0,0 +1,95 @@ +/* Asynchronous part of GSM Layer 1 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <debug.h> +#include <arm.h> +#include <asm/system.h> + +#include <osmocore/msgb.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/mframe_sched.h> +#include <layer1/sched_gsmtime.h> +#include <layer1/l23_api.h> + +extern const struct tdma_sched_item rach_sched_set_ul[]; + +/* safely enable a message into the L1S TX queue */ +void l1a_txq_msgb_enq(struct llist_head *queue, struct msgb *msg) +{ + l1a_lock_sync(); + msgb_enqueue(queue, msg); + l1a_unlock_sync(); +} + +/* Enable a repeating multiframe task */ +void l1a_mftask_enable(enum mframe_task task) +{ + /* we don't need locking here as L1S only reads mframe.tasks */ + mframe_enable(task); +} + +/* Disable a repeating multiframe task */ +void l1a_mftask_disable(enum mframe_task task) +{ + /* we don't need locking here as L1S only reads mframe.tasks */ + mframe_disable(task); +} + +/* Set the mask for repeating multiframe tasks */ +void l1a_mftask_set(uint32_t tasks) +{ + /* we don't need locking here as L1S only reads mframe.tasks */ + mframe_set(tasks); +} + +/* Initialize asynchronous part of Layer1 */ +void l1a_init(void) +{ + l1a_l23api_init(); +} + +/* Execute pending L1A completions */ +void l1a_compl_execute(void) +{ + unsigned long flags; + unsigned int scheduled; + unsigned int i; + + /* get and reset the currently scheduled tasks */ + local_firq_save(flags); + scheduled = l1s.scheduled_compl; + l1s.scheduled_compl = 0; + local_irq_restore(flags); + + /* Iterate over list of scheduled completions, call their + * respective completion handler */ + for (i = 0; i < 32; i++) { + if (!(scheduled & (1 << i))) + continue; + /* call completion function */ + l1s.completion[i](i); + } +} diff --git a/src/target/firmware/layer1/avg.c b/src/target/firmware/layer1/avg.c new file mode 100644 index 00000000..a4bf565b --- /dev/null +++ b/src/target/firmware/layer1/avg.c @@ -0,0 +1,57 @@ +/* Averaging Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <layer1/avg.h> + +/* input a new sample into the averaging process */ +void runavg_input(struct running_avg *ravg, int32_t val, int valid) +{ + ravg->num_samples++; + if (valid) { + ravg->acc_val += val; + ravg->num_samples_valid++; + } +} + +/* check if sufficient samples have been obtained, and call outfn() */ +int runavg_check_output(struct running_avg *ravg) +{ + if (ravg->num_samples < ravg->period) + return 0; + + if (ravg->num_samples_valid >= ravg->min_valid) { + int32_t avg = ravg->acc_val / ravg->num_samples_valid; + + ravg->outfn(ravg, avg); + + ravg->num_samples = ravg->num_samples_valid = 0; + ravg->acc_val = 0; + + return 1; + } + + return 0; +} + + diff --git a/src/target/firmware/layer1/init.c b/src/target/firmware/layer1/init.c new file mode 100644 index 00000000..7af327eb --- /dev/null +++ b/src/target/firmware/layer1/init.c @@ -0,0 +1,74 @@ +/* OsmocomBB Layer1 initialization */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include <stdint.h> +#include <stdio.h> + +#include <rffe.h> +#include <rf/trf6151.h> +#include <abb/twl3025.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/irq.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/l23_api.h> + +void layer1_init(void) +{ +#ifndef CONFIG_TX_ENABLE + printf("\n\nTHIS FIRMWARE WAS COMPILED WITHOUT TX SUPPORT!!!\n\n"); +#endif + + /* initialize asynchronous part of L1 */ + l1a_init(); + /* initialize TDMA Frame IRQ driven synchronous L1 */ + l1s_init(); + /* power up the DSP */ + dsp_power_on(); + + /* Initialize TPU, TSP and TRF drivers */ + tpu_init(); + tsp_init(); + trf6151_init(); + + rffe_init(); + +#if 0 /* only if RX TPU window is disabled! */ + /* Put TWL3025 in downlink mode (includes calibration) */ + twl3025_downlink(1, 1000); +#endif + + /* issue the TRF and TWL initialization sequence */ + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); + + /* Disable RTC interrupt as it causes lost TDMA frames */ + irq_disable(IRQ_RTC_TIMER); + + /* inform l2 and upwards that we are ready for orders */ + l1ctl_tx_reset(L1CTL_RESET_IND, L1CTL_RES_T_BOOT); +} diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c new file mode 100644 index 00000000..c1dbbb81 --- /dev/null +++ b/src/target/firmware/layer1/l23_api.c @@ -0,0 +1,395 @@ +/* Synchronous part of GSM Layer 1: API to Layer2+ */ + +/* (C) 2010 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. + * + */ + +#define DEBUG + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> +#include <byteorder.h> + +#include <osmocore/msgb.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> + +#include <rf/trf6151.h> + +#include <l1a_l23_interface.h> + +/* the size we will allocate struct msgb* for HDLC */ +#define L3_MSG_HEAD 4 +#define L3_MSG_SIZE (sizeof(struct l1ctl_info_dl)+sizeof(struct l1ctl_data_ind) + L3_MSG_HEAD) + +void l1_queue_for_l2(struct msgb *msg) +{ + /* forward via serial for now */ + sercomm_sendmsg(SC_DLCI_L1A_L23, msg); +} + +static enum mframe_task chan_nr2mf_task(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + uint8_t lch_idx; + + if (cbits == 0x01) { + lch_idx = 0; + /* FIXME: TCH/F */ + } else if ((cbits & 0x1e) == 0x02) { + lch_idx = cbits & 0x1; + /* FIXME: TCH/H */ + } else if ((cbits & 0x1c) == 0x04) { + lch_idx = cbits & 0x3; + return MF_TASK_SDCCH4_0 + lch_idx; + } else if ((cbits & 0x18) == 0x08) { + lch_idx = cbits & 0x7; + return MF_TASK_SDCCH8_0 + lch_idx; +#if 0 + } else if (cbits == 0x10) { + /* FIXME: when to do extended BCCH? */ + return MF_TASK_BCCH_NORM; + } else if (cbits == 0x11 || cbits == 0x12) { + /* FIXME: how to decide CCCH norm/extd? */ + return MF_TASK_BCCH_CCCH; +#endif + } + return 0; +} + +static int chan_nr2dchan_type(uint8_t chan_nr) +{ + uint8_t cbits = chan_nr >> 3; + + if (cbits == 0x01) { + return GSM_DCHAN_TCH_F; + } else if ((cbits & 0x1e) == 0x02) { + return GSM_DCHAN_TCH_H; + } else if ((cbits & 0x1c) == 0x04) { + return GSM_DCHAN_SDCCH_4; + } else if ((cbits & 0x18) == 0x08) { + return GSM_DCHAN_SDCCH_8; + } + return GSM_DCHAN_UNKNOWN; +} + +struct msgb *l1ctl_msgb_alloc(uint8_t msg_type) +{ + struct msgb *msg; + struct l1ctl_hdr *l1h; + + msg = msgb_alloc_headroom(L3_MSG_SIZE, L3_MSG_HEAD, "l1ctl"); + if (!msg) { + while (1) { + puts("OOPS. Out of buffers...\n"); + } + + return NULL; + } + l1h = (struct l1ctl_hdr *) msgb_put(msg, sizeof(*l1h)); + l1h->msg_type = msg_type; + l1h->flags = 0; + + msg->l1h = (uint8_t *)l1h; + + return msg; +} + +struct msgb *l1_create_l2_msg(int msg_type, uint32_t fn, uint16_t snr, + uint16_t arfcn) +{ + struct l1ctl_info_dl *dl; + struct msgb *msg = l1ctl_msgb_alloc(msg_type); + + dl = (struct l1ctl_info_dl *) msgb_put(msg, sizeof(*dl)); + dl->frame_nr = htonl(fn); + dl->snr = snr; + dl->band_arfcn = htons(arfcn); + + return msg; +} + +/* receive a L1CTL_FBSB_REQ from L23 */ +static void l1ctl_rx_fbsb_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_fbsb_req *sync_req = (struct l1ctl_fbsb_req *) l1h->data; + + if (sizeof(*sync_req) > msg->len) { + printf("Short sync msg. %u\n", msg->len); + return; + } + + printd("L1CTL_FBSB_REQ (arfcn=%u, flags=0x%x)\n", + ntohs(sync_req->band_arfcn), sync_req->flags); + + /* reset scheduler and hardware */ + l1s_reset(); + + /* pre-set the CCCH mode */ + l1s.serving_cell.ccch_mode = sync_req->ccch_mode; + + printd("Starting FCCH Recognition\n"); + l1s_fbsb_req(1, sync_req); +} + +/* receive a L1CTL_DM_EST_REQ from L23 */ +static void l1ctl_rx_dm_est_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload; + + printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n", + ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc); + + /* Current limitations */ + if ((ul->chan_nr & 0x7) > 4) { + /* FIXME: Timeslot */ + puts("We don't support TS > 4 yet\n"); + return; + } + + if ((chan_nr2mf_task(ul->chan_nr) >= MF_TASK_SDCCH8_4) && + (chan_nr2mf_task(ul->chan_nr) <= MF_TASK_SDCCH8_7)) { + /* FIXME: TX while RX prevents SDCCH8 [4..7] */ + puts("We don't support SDCCH8 [4..7] yet\n"); + return; + } + + /* configure dedicated channel state */ + l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); + l1s.dedicated.tsc = est_req->tsc; + l1s.dedicated.tn = ul->chan_nr & 0x7; + l1s.dedicated.h = est_req->h; + + if (est_req->h) { + int i; + l1s.dedicated.h1.hsn = est_req->h1.hsn; + l1s.dedicated.h1.maio = est_req->h1.maio; + l1s.dedicated.h1.n = est_req->h1.n; + for (i=0; i<est_req->h1.n; i++) + l1s.dedicated.h1.ma[i] = ntohs(est_req->h1.ma[i]); + } else { + l1s.dedicated.h0.arfcn = ntohs(est_req->h0.band_arfcn); + } + + /* figure out which MF tasks to enable */ + l1a_mftask_set(1 << chan_nr2mf_task(ul->chan_nr)); +} + +/* receive a L1CTL_DM_REL_REQ from L23 */ +static void l1ctl_rx_dm_rel_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + + l1a_mftask_set(0); +} + +/* receive a L1CTL_RACH_REQ from L23 */ +static void l1ctl_rx_rach_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload; + + printd("L1CTL_RACH_REQ (ra=0x%02x, fn51=%d)\n", rach_req->ra, rach_req->fn51); + + l1a_rach_req(rach_req->fn51, rach_req->ra); +} + +/* receive a L1CTL_DATA_REQ from L23 */ +static void l1ctl_rx_data_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; + struct l1ctl_data_ind *data_ind = (struct l1ctl_data_ind *) ul->payload; + struct llist_head *tx_queue; + + printd("L1CTL_DATA_REQ (link_id=0x%02x)\n", ul->link_id); + + msg->l3h = data_ind->data; + tx_queue = (ul->link_id & 0x40) ? + &l1s.tx_queue[L1S_CHAN_SACCH] : + &l1s.tx_queue[L1S_CHAN_MAIN]; + + printd("ul=%p, ul->payload=%p, data_ind=%p, data_ind->data=%p l3h=%p\n", + ul, ul->payload, data_ind, data_ind->data, msg->l3h); + + l1a_txq_msgb_enq(tx_queue, msg); +} + +/* receive a L1CTL_PM_REQ from L23 */ +static void l1ctl_rx_pm_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data; + + switch (pm_req->type) { + case 1: + l1s.pm.mode = 1; + l1s.pm.range.arfcn_start = + ntohs(pm_req->range.band_arfcn_from); + l1s.pm.range.arfcn_next = + ntohs(pm_req->range.band_arfcn_from); + l1s.pm.range.arfcn_end = + ntohs(pm_req->range.band_arfcn_to); + printf("L1CTL_PM_REQ start=%u end=%u\n", + l1s.pm.range.arfcn_start, l1s.pm.range.arfcn_end); + break; + } + + l1s_pm_test(1, l1s.pm.range.arfcn_next); +} + +/* Transmit a L1CTL_RESET_IND or L1CTL_RESET_CONF */ +void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type) +{ + struct msgb *msg = l1ctl_msgb_alloc(msg_type); + struct l1ctl_reset *reset_resp; + reset_resp = (struct l1ctl_reset *) + msgb_put(msg, sizeof(*reset_resp)); + reset_resp->type = reset_type; + + l1_queue_for_l2(msg); +} + +/* receive a L1CTL_RESET_REQ from L23 */ +static void l1ctl_rx_reset_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_reset *reset_req = + (struct l1ctl_reset *) l1h->data; + + switch (reset_req->type) { + case L1CTL_RES_T_FULL: + printf("L1CTL_RESET_REQ: FULL!\n"); + l1s_reset(); + l1s_reset_hw(); + l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type); + break; + default: + printf("unknown L1CTL_RESET_REQ type\n"); + break; + } +} + +/* Transmit a L1CTL_CCCH_MODE_CONF */ +static void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode) +{ + struct msgb *msg = l1ctl_msgb_alloc(L1CTL_CCCH_MODE_CONF); + struct l1ctl_ccch_mode_conf *mode_conf; + mode_conf = (struct l1ctl_ccch_mode_conf *) + msgb_put(msg, sizeof(*mode_conf)); + mode_conf->ccch_mode = ccch_mode; + + l1_queue_for_l2(msg); +} + +/* receive a L1CTL_CCCH_MODE_REQ from L23 */ +static void l1ctl_rx_ccch_mode_req(struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + struct l1ctl_ccch_mode_req *ccch_mode_req = + (struct l1ctl_ccch_mode_req *) l1h->data; + uint8_t ccch_mode = ccch_mode_req->ccch_mode; + + /* pre-set the CCCH mode */ + l1s.serving_cell.ccch_mode = ccch_mode; + + /* Update task */ + mframe_disable(MF_TASK_CCCH_COMB); + mframe_disable(MF_TASK_CCCH); + + if (ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1ctl_tx_ccch_mode_conf(ccch_mode); +} + +/* callback from SERCOMM when L2 sends a message to L1 */ +static void l1a_l23_rx_cb(uint8_t dlci, struct msgb *msg) +{ + struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; + +#if 0 + { + int i; + printf("l1a_l23_rx_cb (%u): ", msg->len); + for (i = 0; i < msg->len; i++) + printf("%02x ", msg->data[i]); + puts("\n"); + } +#endif + + msg->l1h = msg->data; + + if (sizeof(*l1h) > msg->len) { + printf("l1a_l23_cb: Short message. %u\n", msg->len); + goto exit_msgbfree; + } + + switch (l1h->msg_type) { + case L1CTL_FBSB_REQ: + l1ctl_rx_fbsb_req(msg); + break; + case L1CTL_DM_EST_REQ: + l1ctl_rx_dm_est_req(msg); + break; + case L1CTL_DM_REL_REQ: + l1ctl_rx_dm_rel_req(msg); + break; + case L1CTL_RACH_REQ: + l1ctl_rx_rach_req(msg); + break; + case L1CTL_DATA_REQ: + l1ctl_rx_data_req(msg); + /* we have to keep the msgb, not free it! */ + goto exit_nofree; + case L1CTL_PM_REQ: + l1ctl_rx_pm_req(msg); + break; + case L1CTL_RESET_REQ: + l1ctl_rx_reset_req(msg); + break; + case L1CTL_CCCH_MODE_REQ: + l1ctl_rx_ccch_mode_req(msg); + break; + } + +exit_msgbfree: + msgb_free(msg); +exit_nofree: + return; +} + +void l1a_l23api_init(void) +{ + sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx_cb); +} diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c new file mode 100644 index 00000000..e51d7582 --- /dev/null +++ b/src/target/firmware/layer1/mframe_sched.c @@ -0,0 +1,370 @@ +/* GSM Multiframe Scheduler Implementation (on top of TDMA sched) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <debug.h> + +#include <osmocore/gsm_utils.h> + +#include <layer1/sync.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> + +/* A multiframe operation which can be scheduled for a multiframe */ +struct mframe_sched_item { + /* The TDMA scheduler item that shall be scheduled */ + const struct tdma_sched_item *sched_set; + /* Which modulo shall be used on the frame number */ + uint16_t modulo; + /* At which number inside the modulo shall we be scheduled */ + uint16_t frame_nr; + /* bit-mask of flags */ + uint16_t flags; +}; + +/* FIXME: properly clean this up */ +extern const struct tdma_sched_item nb_sched_set[]; +extern const struct tdma_sched_item nb_sched_set_ul[]; +#define NB_QUAD_DL nb_sched_set +#define NB_QUAD_FH_DL NB_QUAD_DL +#define NB_QUAD_UL nb_sched_set_ul +#define NB_QUAD_FH_UL NB_QUAD_UL + +/* BCCH Normal */ +static const struct mframe_sched_item mf_bcch_norm[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 2 }, + { .sched_set = NULL } +}; + +/* BCCH Extended */ +static const struct mframe_sched_item mf_bcch_ext[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 }, + { .sched_set = NULL } +}; + +/* Full CCCH in a pure BCCH + CCCH C0T0 */ +static const struct mframe_sched_item mf_ccch[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 42 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 46 }, + { .sched_set = NULL } +}; + +/* Full CCCH in a combined CCCH on C0T0 */ +static const struct mframe_sched_item mf_ccch_comb[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 6 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 12 }, + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 16 }, + { .sched_set = NULL } +}; + +/* SDCCH/4 in a combined CCCH on C0T0, cannot be FH */ +static const struct mframe_sched_item mf_sdcch4_0[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 22 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 22+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 42, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 42+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch4_1[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 26 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 26+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 46, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 46+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch4_2[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 32 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 32+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+42, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+42+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch4_3[] = { + { .sched_set = NB_QUAD_DL, .modulo = 51, .frame_nr = 36 }, + { .sched_set = NB_QUAD_UL, .modulo = 51, .frame_nr = 36+15 }, + { .sched_set = NB_QUAD_DL, .modulo = 2*51, .frame_nr = 51+46, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_UL, .modulo = 2*51, .frame_nr = 51+46+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +/* SDCCH/8, can be frequency hopping (FH) */ +static const struct mframe_sched_item mf_sdcch8_0[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 0 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 0+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 32, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 32+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_1[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 4 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 4+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 36, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 36+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_2[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 8 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 8+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 40, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 40+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_3[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 12 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 12+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 44, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 44+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_4[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 16 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 16+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+32, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+32+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_5[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 20 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 20+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+36, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+36+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_6[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 24 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 24+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+40, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+40+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; +static const struct mframe_sched_item mf_sdcch8_7[] = { + { .sched_set = NB_QUAD_FH_DL, .modulo = 51, .frame_nr = 28 }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 51, .frame_nr = 28+15 }, + { .sched_set = NB_QUAD_FH_DL, .modulo = 2*51, .frame_nr = 51+44, + .flags = MF_F_SACCH }, + { .sched_set = NB_QUAD_FH_UL, .modulo = 2*51, .frame_nr = 51+44+15, + .flags = MF_F_SACCH }, + { .sched_set = NULL } +}; + +static const struct mframe_sched_item mf_tx_all_nb[] = { + { .sched_set = NB_QUAD_FH_UL, .modulo = 4, .frame_nr = 0 }, + { .sched_set = NULL } +}; + +static const struct mframe_sched_item *sched_set_for_task[32] = { + [MF_TASK_BCCH_NORM] = mf_bcch_norm, + [MF_TASK_BCCH_EXT] = mf_bcch_ext, + [MF_TASK_CCCH] = mf_ccch, + [MF_TASK_CCCH_COMB] = mf_ccch_comb, + + [MF_TASK_SDCCH4_0] = mf_sdcch4_0, + [MF_TASK_SDCCH4_1] = mf_sdcch4_1, + [MF_TASK_SDCCH4_2] = mf_sdcch4_2, + [MF_TASK_SDCCH4_3] = mf_sdcch4_3, + + [MF_TASK_SDCCH8_0] = mf_sdcch8_0, + [MF_TASK_SDCCH8_1] = mf_sdcch8_1, + [MF_TASK_SDCCH8_2] = mf_sdcch8_2, + [MF_TASK_SDCCH8_3] = mf_sdcch8_3, + [MF_TASK_SDCCH8_4] = mf_sdcch8_4, + [MF_TASK_SDCCH8_5] = mf_sdcch8_5, + [MF_TASK_SDCCH8_6] = mf_sdcch8_6, + [MF_TASK_SDCCH8_7] = mf_sdcch8_7, + + [MF_TASK_UL_ALL_NB] = mf_tx_all_nb, +}; + +/* encodes a channel number according to 08.58 Chapter 9.3.1 */ +uint8_t mframe_task2chan_nr(enum mframe_task mft, uint8_t ts) +{ + uint8_t cbits; + + switch (mft) { + case MF_TASK_BCCH_NORM: + case MF_TASK_BCCH_EXT: + cbits = 0x10; + break; + case MF_TASK_CCCH: + case MF_TASK_CCCH_COMB: + cbits = 0x12; + break; + case MF_TASK_SDCCH4_0: + cbits = 0x04 + 0; + break; + case MF_TASK_SDCCH4_1: + cbits = 0x04 + 1; + break; + case MF_TASK_SDCCH4_2: + cbits = 0x04 + 2; + break; + case MF_TASK_SDCCH4_3: + cbits = 0x04 + 3; + break; + case MF_TASK_SDCCH8_0: + cbits = 0x08 + 0; + break; + case MF_TASK_SDCCH8_1: + cbits = 0x08 + 1; + break; + case MF_TASK_SDCCH8_2: + cbits = 0x08 + 2; + break; + case MF_TASK_SDCCH8_3: + cbits = 0x08 + 3; + break; + case MF_TASK_SDCCH8_4: + cbits = 0x08 + 4; + break; + case MF_TASK_SDCCH8_5: + cbits = 0x08 + 5; + break; + case MF_TASK_SDCCH8_6: + cbits = 0x08 + 6; + break; + case MF_TASK_SDCCH8_7: + cbits = 0x08 + 7; + break; + case MF_TASK_UL_ALL_NB: + /* ERROR: cannot express as channel number */ + cbits = 0; + break; + } + + return (cbits << 3) | (ts & 0x7); +} + +/* how many TDMA frame ticks should we schedule events ahead? */ +#define SCHEDULE_AHEAD 2 + +/* how long do we need to tell the DSP in advance what we want to do? */ +#define SCHEDULE_LATENCY 1 + +/* (test and) schedule one particular sched_item_set by means of the TDMA scheduler */ +static void mframe_schedule_set(enum mframe_task task_id) +{ + const struct mframe_sched_item *set = sched_set_for_task[task_id]; + const struct mframe_sched_item *si; + + for (si = set; si->sched_set != NULL; si++) { + unsigned int trigger = si->frame_nr % si->modulo; + unsigned int current = (l1s.current_time.fn + SCHEDULE_AHEAD) % si->modulo; + if (current == trigger) { + uint32_t fn; + int rv; + + /* Schedule the set */ + /* FIXME: what to do with SACCH Flag etc? */ + rv = tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY, + si->sched_set, task_id | (si->flags<<8)); + + /* Compute the next safe time to queue a DSP command */ + fn = l1s.current_time.fn; + ADD_MODULO(fn, rv - 2, GSM_MAX_FN); /* -2 = worst case last dsp command */ + if ((fn > l1s.mframe_sched.safe_fn) || + (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) + l1s.mframe_sched.safe_fn = fn; + } + } +} + +/* Enable a specific task */ +void mframe_enable(enum mframe_task task_id) +{ + l1s.mframe_sched.tasks_tgt |= (1 << task_id); +} + +/* Disable a specific task */ +void mframe_disable(enum mframe_task task_id) +{ + l1s.mframe_sched.tasks_tgt &= ~(1 << task_id); +} + +/* Replace the current active set by the new one */ +void mframe_set(uint32_t tasks) +{ + l1s.mframe_sched.tasks_tgt = tasks; +} + +/* Schedule mframe_sched_items according to current MF TASK list */ +void mframe_schedule(void) +{ + unsigned int i; + int fn_diff; + + /* Try to enable/disable task to meet target bitmap */ + fn_diff = l1s.mframe_sched.safe_fn - l1s.current_time.fn; + if ((fn_diff <= 0) || (fn_diff >= (GSM_MAX_FN>>1)) || + (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) + /* If nothing is in the way, enable new tasks */ + l1s.mframe_sched.tasks = l1s.mframe_sched.tasks_tgt; + else + /* Else, Disable only */ + l1s.mframe_sched.tasks &= l1s.mframe_sched.tasks_tgt; + + /* Schedule any active pending set */ + for (i = 0; i < 32; i++) { + if (l1s.mframe_sched.tasks & (1 << i)) + mframe_schedule_set(i); + } +} + +/* reset the scheduler, disabling all tasks */ +void mframe_reset(void) +{ + l1s.mframe_sched.tasks = 0; + l1s.mframe_sched.tasks_tgt = 0; + l1s.mframe_sched.safe_fn = -1UL; /* Force safe */ +} + diff --git a/src/target/firmware/layer1/prim_fbsb.c b/src/target/firmware/layer1/prim_fbsb.c new file mode 100644 index 00000000..4360f7c4 --- /dev/null +++ b/src/target/firmware/layer1/prim_fbsb.c @@ -0,0 +1,572 @@ +/* Layer 1 - FCCH and SCH burst handling */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1a_l23_interface.h> + +#define FB0_RETRY_COUNT 3 +#define AFC_RETRY_COUNT 30 + +extern uint16_t rf_arfcn; // TODO + +struct mon_state { + uint32_t fnr_report; /* frame number when DSP reported it */ + int attempt; /* which attempt was this ? */ + + int16_t toa; + uint16_t pm; + uint16_t angle; + uint16_t snr; + + /* computed values */ + int16_t freq_diff; + + /* Sync Burst (SB) */ + uint8_t bsic; + struct gsm_time time; +}; + +struct l1a_fb_state { + struct mon_state mon; + struct l1ctl_fbsb_req req; + int16_t initial_freq_err; + uint8_t fb_retries; + uint8_t afc_retries; +}; + +static struct l1a_fb_state fbs; +static struct mon_state *last_fb = &fbs.mon; + +static void dump_mon_state(struct mon_state *fb) +{ +#if 0 + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz, " + "SNR=%04x(%d.%u) OFFSET=%u SYNCHRO=%u\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle), + fb->snr, l1s_snr_int(fb->snr), l1s_snr_fract(fb->snr), + tpu_get_offset(), tpu_get_synchro()); +#else + printf("(%u:%u): TOA=%5u, Power=%4ddBm, Angle=%5dHz\n", + fb->fnr_report, fb->attempt, fb->toa, + agc_inp_dbm8_by_pm(fb->pm)/8, ANGLE_TO_FREQ(fb->angle)); +#endif +} + +static int l1ctl_fbsb_resp(uint8_t res) +{ + struct msgb *msg; + struct l1ctl_fbsb_conf *resp; + + msg = l1_create_l2_msg(L1CTL_FBSB_CONF, fbs.mon.time.fn, + l1s_snr_int(fbs.mon.snr), + fbs.req.band_arfcn); + if (!msg) + return -ENOMEM; + + resp = (struct l1ctl_fbsb_conf *) msgb_put(msg, sizeof(*resp)); + resp->initial_freq_err = htons(fbs.initial_freq_err); + resp->result = res; + resp->bsic = fbs.mon.bsic; + + /* no need to set BSIC, as it is never used here */ + l1_queue_for_l2(msg); + + return 0; +} + +/* SCH Burst Detection ********************************************************/ + +/* determine the GSM time and BSIC from a Sync Burst */ +static uint8_t l1s_decode_sb(struct gsm_time *time, uint32_t sb) +{ + uint8_t bsic = (sb >> 2) & 0x3f; + uint8_t t3p; + + memset(time, 0, sizeof(*time)); + + /* TS 05.02 Chapter 3.3.2.2.1 SCH Frame Numbers */ + time->t1 = ((sb >> 23) & 1) | ((sb >> 7) & 0x1fe) | ((sb << 9) & 0x600); + time->t2 = (sb >> 18) & 0x1f; + t3p = ((sb >> 24) & 1) | ((sb >> 15) & 6); + time->t3 = t3p*10 + 1; + + /* TS 05.02 Chapter 4.3.3 TDMA frame number */ + time->fn = gsm_gsmtime2fn(time); + + time->tc = (time->fn / 51) % 8; + + return bsic; +} + +static void read_sb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.db_r->a_serv_demod[D_TOA]; + st->pm = dsp_api.db_r->a_serv_demod[D_PM]>>3; + st->angle = dsp_api.db_r->a_serv_demod[D_ANGLE]; + st->snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + st->freq_diff = ANGLE_TO_FREQ(st->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + if (st->snr > AFC_SNR_THRESHOLD) + afc_input(st->freq_diff, rf_arfcn, 1); + else + afc_input(st->freq_diff, rf_arfcn, 0); + + dsp_api.r_page_used = 1; +} + +/* Note: When we get the SB response, it is 2 TDMA frames after the SB + * actually happened, as it is a "C W W R" task */ +#define SB2_LATENCY 2 + +static int l1s_sbdet_resp(__unused uint8_t p1, uint8_t attempt, + __unused uint16_t p3) +{ + uint32_t sb; + int qbits, fn_offset; + struct l1_cell_info *cinfo = &l1s.serving_cell; + int fnr_delta, bits_delta; + struct l1ctl_sync_new_ccch_resp *l1; + struct msgb *msg; + + putchart('s'); + + if (dsp_api.db_r->a_sch[0] & (1<<B_SCH_CRC)) { + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + /* after 2nd attempt, we failed */ + if (attempt == 2) { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + /* after 1st attempt, we simply wait for 2nd */ + return 0; + } + + printf("SB%d ", attempt); + read_sb_result(last_fb, attempt); + + sb = dsp_api.db_r->a_sch[3] | dsp_api.db_r->a_sch[4] << 16; + fbs.mon.bsic = l1s_decode_sb(&fbs.mon.time, sb); + printf("=> SB 0x%08x: BSIC=%u ", sb, fbs.mon.bsic); + l1s_time_dump(&fbs.mon.time); + + l1s.serving_cell.bsic = fbs.mon.bsic; + + /* calculate synchronisation value (TODO: only complete for qbits) */ + last_fb->toa -= 23; + qbits = last_fb->toa * 4; + fn_offset = l1s.current_time.fn; // TODO + + if (qbits > QBITS_PER_TDMA) { + qbits -= QBITS_PER_TDMA; + fn_offset -= 1; + } else if (qbits < 0) { + qbits += QBITS_PER_TDMA; + fn_offset += 1; + } + + fnr_delta = last_fb->fnr_report - attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports SB in bit that is %d bits in the " + "future?!?\n", last_fb->toa - bits_delta); + else + printf(" qbits=%u\n", qbits); + + synchronize_tdma(&l1s.serving_cell); + + /* if we have recived a SYNC burst, update our local GSM time */ + gsm_fn2gsmtime(&l1s.current_time, fbs.mon.time.fn + SB2_LATENCY); + /* compute next time from new current time */ + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* If we call tdma_sched_reset(), which is only needed if there + * are further l1s_sbdet_resp() scheduled, we will bring + * dsp_api.db_r and dsp_api.db_w out of sync because we changed + * dsp_api.db_w for l1s_sbdet_cmd() and canceled + * l1s_sbdet_resp() which would change dsp_api.db_r. The DSP + * however expects dsp_api.db_w and dsp_api.db_r to be in sync + * (either "0 - 0" or "1 - 1"). So we have to bring dsp_api.db_w + * and dsp_api.db_r into sync again, otherwise NB reading will + * complain. We probably don't need the Abort command and could + * just bring dsp_api.db_w and dsp_api.db_r into sync. */ + if (attempt != 2) { + tdma_sched_reset(); + l1s_dsp_abort(); + } + + l1s_reset_hw(); + /* enable the MF Task for BCCH reading */ + mframe_enable(MF_TASK_BCCH_NORM); + if (l1s.serving_cell.ccch_mode == CCCH_MODE_COMBINED) + mframe_enable(MF_TASK_CCCH_COMB); + else if (l1s.serving_cell.ccch_mode == CCCH_MODE_NON_COMBINED) + mframe_enable(MF_TASK_CCCH); + + l1s_compl_sched(L1_COMPL_FB); + + return 0; +} + +static int l1s_sbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('S'); + + fbs.mon.bsic = 0; + fbs.mon.time.fn = 0; + + dsp_api.db_w->d_task_md = SB_DSP_TASK; + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + dsp_end_scenario(); + + /* Program TPU */ + l1s_rx_win_ctrl(rf_arfcn, L1_RXWIN_SB, 0); + tpu_end_scenario(); + + return 0; +} + +/* This is how it is done by the TSM30 */ +static const struct tdma_sched_item sb_sched_set[] = { + SCHED_ITEM(l1s_sbdet_cmd, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_cmd, 0, 2), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_sbdet_resp, 0, 2), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +void l1s_sb_test(uint8_t base_fn) +{ + tdma_schedule_set(base_fn, sb_sched_set, 0); +} +/* FCCH Burst *****************************************************************/ + +static int read_fb_result(struct mon_state *st, int attempt) +{ + st->toa = dsp_api.ndb->a_sync_demod[D_TOA]; + st->pm = dsp_api.ndb->a_sync_demod[D_PM]>>3; + st->angle = dsp_api.ndb->a_sync_demod[D_ANGLE]; + st->snr = dsp_api.ndb->a_sync_demod[D_SNR]; + + //last_fb->angle = clip_int16(last_fb->angle, AFC_MAX_ANGLE); + st->freq_diff = ANGLE_TO_FREQ(last_fb->angle); + st->fnr_report = l1s.current_time.fn; + st->attempt = attempt; + + dump_mon_state(st); + + dsp_api.ndb->d_fb_det = 0; + dsp_api.ndb->a_sync_demod[D_TOA] = 0; /* TSM30 does it (really needed ?) */ + + /* Update AFC with current frequency offset */ + afc_correct(st->freq_diff, rf_arfcn); + + //tpu_dsp_frameirq_enable(); + return 1; +} + +static void fbinfo2cellinfo(struct l1_cell_info *cinfo, + const struct mon_state *mon) +{ + int ntdma, qbits, fn_offset, fnr_delta, bits_delta; + + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + fnr_delta = last_fb->fnr_report - last_fb->attempt; + bits_delta = fnr_delta * BITS_PER_TDMA; + + cinfo->fn_offset = fnr_delta; + cinfo->time_alignment = qbits; + cinfo->arfcn = rf_arfcn; + + if (last_fb->toa > bits_delta) + printf("=> DSP reports FB in bit that is %d bits in " + "the future?!?\n", last_fb->toa - bits_delta); + else { + int fb_fnr = (last_fb->fnr_report - last_fb->attempt) + + last_fb->toa/BITS_PER_TDMA; + printf("=>FB @ FNR %u fn_offset=%d qbits=%u\n", + fb_fnr, fn_offset, qbits); + } +} + +/* scheduler callback to issue a FB detection task to the DSP */ +static int l1s_fbdet_cmd(__unused uint8_t p1, __unused uint8_t p2, + uint16_t fb_mode) +{ + if (fb_mode == 0) { + putchart('F'); + } else { + putchart('V'); + } + + l1s.fb.mode = fb_mode; + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(-85, CAL_DSP_TGT_BB_LVL); + + /* Program DSP */ + dsp_api.db_w->d_task_md = FB_DSP_TASK; /* maybe with I/Q swap? */ + dsp_api.ndb->d_fb_mode = fb_mode; + dsp_end_scenario(); + + /* Program TPU */ + l1s_rx_win_ctrl(fbs.req.band_arfcn, L1_RXWIN_FB, 0); + tpu_end_scenario(); + + return 0; +} + +#if 0 +#define FB0_SNR_THRESH 2000 +#define FB1_SNR_THRESH 3000 +#else +#define FB0_SNR_THRESH 0 +#define FB1_SNR_THRESH 0 +#endif + +static const struct tdma_sched_item fb_sched_set[]; + +/* scheduler callback to check for a FB detection response */ +static int l1s_fbdet_resp(__unused uint8_t p1, uint8_t attempt, + uint16_t fb_mode) +{ + putchart('f'); + + if (!dsp_api.ndb->d_fb_det) { + /* we did not detect a FB */ + + /* attempt < 12, do nothing */ + if (attempt < 12) + return 0; + + /* attempt >= 12, we simply don't find one */ + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + if (fbs.fb_retries < FB0_RETRY_COUNT) { + /* retry once more */ + tdma_schedule_set(1, fb_sched_set, 0); + fbs.fb_retries++; + } else { + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; + } + + /* We found a frequency burst, reset everything */ + l1s_reset_hw(); + + printf("FB%u ", dsp_api.ndb->d_fb_mode); + read_fb_result(last_fb, attempt); + + /* if this is the first success, save freq err */ + if (!fbs.initial_freq_err) + fbs.initial_freq_err = last_fb->freq_diff; + + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + + /* Immediately schedule further TDMA tasklets, if requested. Doing + * this directly from L1S means we can do this quickly without any + * additional delays */ + if (fb_mode == 0) { + if (fbs.req.flags & L1CTL_FBSB_F_FB1) { + /* If we don't reset here, we get DSP DMA errors */ + tdma_sched_reset(); + /* FIXME: don't only use the last but an average */ + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh1 && + last_fb->snr > FB0_SNR_THRESH) { + /* continue with FB1 task in DSP */ + tdma_schedule_set(1, fb_sched_set, 1); + } else { + if (fbs.afc_retries < AFC_RETRY_COUNT) { + tdma_schedule_set(1, fb_sched_set, 0); + fbs.afc_retries++; + } else { + /* Abort */ + last_fb->attempt = 13; + l1s_compl_sched(L1_COMPL_FB); + } + } + } else + l1s_compl_sched(L1_COMPL_FB); + } else if (fb_mode == 1) { + if (fbs.req.flags & L1CTL_FBSB_F_SB) { + + int ntdma, qbits; + /* FIXME: where did this magic 23 come from? */ + last_fb->toa -= 23; + + if (last_fb->toa < 0) { + qbits = (last_fb->toa + BITS_PER_TDMA) * 4; + ntdma = -1; + } else { + ntdma = (last_fb->toa) / BITS_PER_TDMA; + qbits = (last_fb->toa - ntdma * BITS_PER_TDMA) * 4; + } + + + int fn_offset = l1s.current_time.fn - last_fb->attempt + ntdma; + int delay = fn_offset + 11 - l1s.current_time.fn - 1; + printf(" fn_offset=%d (fn=%u + attempt=%u + ntdma = %d)\m", + fn_offset, l1s.current_time.fn, last_fb->attempt, ntdma); + printf(" delay=%d (fn_offset=%d + 11 - fn=%u - 1\n", delay, + fn_offset, l1s.current_time.fn); + printf(" scheduling next FB/SB detection task with delay %u\n", delay); + if (abs(last_fb->freq_diff) < fbs.req.freq_err_thresh2 && + last_fb->snr > FB1_SNR_THRESH) { + /* synchronize before reading SB */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + synchronize_tdma(&l1s.serving_cell); + tdma_schedule_set(delay, sb_sched_set, 0); + } else + tdma_schedule_set(delay, fb_sched_set, 1); + } else + l1s_compl_sched(L1_COMPL_FB); + } + + return 0; +} + +/* FB detection */ +static const struct tdma_sched_item fb_sched_set[] = { + SCHED_ITEM(l1s_fbdet_cmd, 0, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 4), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 5), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 6), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 7), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 8), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 9), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 10), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 11), SCHED_END_FRAME(), + SCHED_ITEM(l1s_fbdet_resp, 0, 12), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_fb_compl(__unused enum l1_compl c) +{ + struct l1_cell_info *cinfo = &l1s.serving_cell; + + if (last_fb->attempt >= 13) { + /* FB detection failed, signal this via L1CTL */ + return l1ctl_fbsb_resp(255); + } + + /* FIME: use l1s.neigh_cell[fbs.cinfo_idx] */ + fbinfo2cellinfo(&l1s.serving_cell, last_fb); + + /* send FBSB_CONF success message via L1CTL */ + l1ctl_fbsb_resp(0); +} + +void l1s_fbsb_req(uint8_t base_fn, struct l1ctl_fbsb_req *req) +{ + /* copy + endian convert request data */ + fbs.req.band_arfcn = ntohs(req->band_arfcn); + fbs.req.timeout = ntohs(req->timeout); + fbs.req.freq_err_thresh1 = ntohs(req->freq_err_thresh1); + fbs.req.freq_err_thresh2 = ntohs(req->freq_err_thresh2); + fbs.req.num_freqerr_avg = req->num_freqerr_avg; + fbs.req.flags = req->flags; + fbs.req.sync_info_idx = req->sync_info_idx; + + /* clear initial frequency error */ + fbs.initial_freq_err = 0; + fbs.fb_retries = 0; + + /* Make sure we start at a 'center' AFCDAC output value */ + afc_reset(); + + if (fbs.req.flags & L1CTL_FBSB_F_FB0) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_FB1) + tdma_schedule_set(base_fn, fb_sched_set, 0); + else if (fbs.req.flags & L1CTL_FBSB_F_SB) + tdma_schedule_set(base_fn, sb_sched_set, 0); + +} + +static __attribute__ ((constructor)) void l1s_prim_fbsb_init(void) +{ + l1s.completion[L1_COMPL_FB] = &l1a_fb_compl; +} diff --git a/src/target/firmware/layer1/prim_pm.c b/src/target/firmware/layer1/prim_pm.c new file mode 100644 index 00000000..80650f60 --- /dev/null +++ b/src/target/firmware/layer1/prim_pm.c @@ -0,0 +1,148 @@ +/* Layer 1 Power Measurement */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/agc.h> +#include <layer1/tdma_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1a_l23_interface.h> + +static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm) +{ + uint8_t i; + + for (i = 0; i < nbmeas; i++) + pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3); + dsp_api.r_page_used = 1; +} + +/* scheduler callback to issue a power measurement task to the DSP */ +static int l1s_pm_cmd(uint8_t num_meas, + __unused uint8_t p2, uint16_t arfcn) +{ + putchart('P'); + + dsp_api.db_w->d_task_md = num_meas; /* number of measurements */ + dsp_api.ndb->d_fb_mode = 0; /* wideband search */ + dsp_end_scenario(); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(-85, CAL_DSP_TGT_BB_LVL); + + /* Program TPU */ + /* FIXME: RXWIN_PW needs to set up multiple times in case + * num_meas > 1 */ + l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW, 0); + //l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB); + tpu_end_scenario(); + + return 0; +} + +/* scheduler callback to read power measurement resposnse from the DSP */ +static int l1s_pm_resp(uint8_t num_meas, __unused uint8_t p2, + uint16_t arfcn) +{ + struct l1ctl_pm_conf *pmr; + uint16_t pm_level[2]; + + putchart('p'); + + l1ddsp_meas_read(num_meas, pm_level); + + printf("PM MEAS: ARFCN=%u, %-4d dBm at baseband, %-4d dBm at RF\n", + arfcn, pm_level[0]/8, agc_inp_dbm8_by_pm(pm_level[0])/8); + + printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n", + agc_inp_dbm8_by_pm(pm_level[0])/8, + agc_inp_dbm8_by_pm(pm_level[1])/8, arfcn); + + if (!l1s.pm.msg) + l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF); + + if (msgb_tailroom(l1s.pm.msg) < sizeof(*pmr)) { + /* flush current msgb */ + l1_queue_for_l2(l1s.pm.msg); + /* allocate a new msgb and initialize header */ + l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF); + } + + pmr = msgb_put(l1s.pm.msg, sizeof(*pmr)); + pmr->band_arfcn = htons(arfcn); + /* FIXME: do this as RxLev rather than DBM8 ? */ + pmr->pm[0] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[0])/8); + if (num_meas > 1) + pmr->pm[1] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[1])/8); + else + pmr->pm[1] = 0; + + if (l1s.pm.mode == 1) { + if (l1s.pm.range.arfcn_next <= l1s.pm.range.arfcn_end) { + /* schedule PM for next ARFCN in range */ + l1s_pm_test(1, l1s.pm.range.arfcn_next); + l1s.pm.range.arfcn_next++; + } else { + /* we have finished, flush the msgb to L2 */ + struct l1ctl_hdr *l1h = l1s.pm.msg->l1h; + l1h->flags |= L1CTL_F_DONE; + l1_queue_for_l2(l1s.pm.msg); + l1s.pm.msg = NULL; + } + } + + return 0; +} + +static const struct tdma_sched_item pm_sched_set[] = { + SCHED_ITEM(l1s_pm_cmd, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_pm_resp, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Schedule a power measurement test */ +void l1s_pm_test(uint8_t base_fn, uint16_t arfcn) +{ + printd("l1s_pm_test(%u, %u)\n", base_fn, arfcn); + tdma_schedule_set(base_fn, pm_sched_set, arfcn); +} diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c new file mode 100644 index 00000000..f91af3dd --- /dev/null +++ b/src/target/firmware/layer1/prim_rach.c @@ -0,0 +1,136 @@ +/* Layer 1 Random Access Channel Burst */ + +/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/async.h> +#include <layer1/tdma_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1a_l23_interface.h> + +struct { + uint32_t fn; + uint16_t band_arfcn; +} last_rach; + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_rach_cmd(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) +{ + int i; + uint16_t *info_ptr; + uint8_t data[2]; + + putchart('T'); + + l1s_tx_apc_helper(); + + data[0] = l1s.serving_cell.bsic << 2; + data[1] = l1s.rach.ra; + + info_ptr = &dsp_api.ndb->d_rach; + info_ptr[0] = ((uint16_t)(data[0])) | ((uint16_t)(data[1])<<8); + + dsp_api.db_w->d_task_ra = RACH_DSP_TASK; + dsp_end_scenario(); + + l1s_tx_win_ctrl(l1s.serving_cell.arfcn, L1_TXWIN_AB, 0, 0); + tpu_end_scenario(); + + return 0; +} + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_rach_resp(__unused uint8_t p1, __unused uint8_t burst_id, + __unused uint16_t p3) +{ + putchart('t'); + + dsp_api.r_page_used = 1; + + /* schedule a confirmation back indicating the GSM time at which + * the RACH burst was transmitted to the BTS */ + last_rach.fn = l1s.current_time.fn - 1; + last_rach.band_arfcn = l1s.serving_cell.arfcn; + l1s_compl_sched(L1_COMPL_RACH); + + return 0; +} + +/* sched sets for uplink */ +const struct tdma_sched_item rach_sched_set_ul[] = { + SCHED_ITEM(l1s_tx_rach_cmd, 1, 0), SCHED_END_FRAME(), + SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_rach_resp, 1, 0), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +/* Asynchronous completion handler for FB detection */ +static void l1a_rach_compl(__unused enum l1_compl c) +{ + struct msgb *msg; + + msg = l1_create_l2_msg(L1CTL_RACH_CONF, last_rach.fn, 0, + last_rach.band_arfcn); + l1_queue_for_l2(msg); +} + +/* request a RACH request at the next multiframe T3 = fn51 */ +void l1a_rach_req(uint8_t fn51, uint8_t ra) +{ + uint32_t fn_sched; + + l1a_lock_sync(); + l1s.rach.ra = ra; + /* TODO: can we wrap here? I don't think so */ + fn_sched = l1s.current_time.fn - l1s.current_time.t3; + fn_sched += fn51; + sched_gsmtime(rach_sched_set_ul, fn_sched, 0); + l1a_unlock_sync(); + + memset(&last_rach, 0, sizeof(last_rach)); +} + +static __attribute__ ((constructor)) void prim_rach_init(void) +{ + l1s.completion[L1_COMPL_RACH] = &l1a_rach_compl; +} diff --git a/src/target/firmware/layer1/prim_rx_nb.c b/src/target/firmware/layer1/prim_rx_nb.c new file mode 100644 index 00000000..036f4e6d --- /dev/null +++ b/src/target/firmware/layer1/prim_rx_nb.c @@ -0,0 +1,199 @@ +/* Layer 1 - Receiving Normal Bursts */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> +#include <layer1/rfch.h> + +#include <l1a_l23_interface.h> + +struct l1s_rxnb_state { + struct l1s_meas_hdr meas[4]; + + struct msgb *msg; + struct l1ctl_info_dl *dl; + struct l1ctl_data_ind *di; +}; + +static struct l1s_rxnb_state rxnb; + +static int l1s_nb_resp(__unused uint8_t p1, uint8_t burst_id, uint16_t p3) +{ + struct gsm_time rx_time; + uint8_t mf_task_id = p3 & 0xff; + uint8_t mf_task_flags = p3 >> 8; + uint16_t rf_arfcn; + uint8_t tsc, tn; + + putchart('n'); + + gsm_fn2gsmtime(&rx_time, l1s.current_time.fn - 4); + rfch_get_params(&rx_time, &rf_arfcn, &tsc, &tn); + + /* just for debugging, d_task_d should not be 0 */ + if (dsp_api.db_r->d_task_d == 0) { + puts("EMPTY\n"); + return 0; + } + + /* DSP burst ID needs to corespond with what we expect */ + if (dsp_api.db_r->d_burst_d != burst_id) { + printf("BURST ID %u!=%u\n", dsp_api.db_r->d_burst_d, burst_id); + return 0; + } + + rxnb.meas[burst_id].toa_qbit = dsp_api.db_r->a_serv_demod[D_TOA]; + rxnb.meas[burst_id].pm_dbm8 = + agc_inp_dbm8_by_pm(dsp_api.db_r->a_serv_demod[D_PM] >> 3); + rxnb.meas[burst_id].freq_err = + ANGLE_TO_FREQ(dsp_api.db_r->a_serv_demod[D_ANGLE]); + rxnb.meas[burst_id].snr = dsp_api.db_r->a_serv_demod[D_SNR]; + + /* feed computed frequency error into AFC loop */ + if (rxnb.meas[burst_id].snr > AFC_SNR_THRESHOLD) + afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 1); + else + afc_input(rxnb.meas[burst_id].freq_err, rf_arfcn, 0); + + /* Tell the RF frontend to set the gain appropriately */ + rffe_set_gain(rxnb.meas[burst_id].pm_dbm8/8, CAL_DSP_TGT_BB_LVL); + + /* 4th burst, get frame data */ + if (dsp_api.db_r->d_burst_d == 3) { + uint8_t i, j; + uint16_t num_biterr; + uint32_t avg_snr = 0; + int32_t avg_dbm8 = 0; + + /* Set Channel Number depending on MFrame Task ID */ + rxnb.dl->chan_nr = mframe_task2chan_nr(mf_task_id, tn); + + /* Set SACCH indication in Link IDentifier */ + if (mf_task_flags & MF_F_SACCH) + rxnb.dl->link_id = 0x40; + else + rxnb.dl->link_id = 0x00; + + rxnb.dl->band_arfcn = htons(rf_arfcn); + + rxnb.dl->frame_nr = htonl(l1s.current_time.fn-4); + + /* compute average snr and rx level */ + for (i = 0; i < 4; ++i) { + avg_snr += rxnb.meas[i].snr; + avg_dbm8 += rxnb.meas[i].pm_dbm8; + } + rxnb.dl->snr = avg_snr / 4; + rxnb.dl->rx_level = (avg_dbm8 / (8*4)) + 110; + + num_biterr = dsp_api.ndb->a_cd[2] & 0xffff; + if (num_biterr > 0xff) + rxnb.dl->num_biterr = 0xff; + else + rxnb.dl->num_biterr = num_biterr; + + rxnb.dl->fire_crc = ((dsp_api.ndb->a_cd[0] & 0xffff) & ((1 << B_FIRE1) | (1 << B_FIRE0))) >> B_FIRE0; + + /* copy actual data, skipping the information block [0,1,2] */ + for (j = 0,i = 3; i < 15; i++) { + rxnb.di->data[j++] = dsp_api.ndb->a_cd[i] & 0xFF; + rxnb.di->data[j++] = (dsp_api.ndb->a_cd[i] >> 8) & 0xFF; + } + + l1_queue_for_l2(rxnb.msg); + rxnb.msg = NULL; rxnb.dl = NULL; rxnb.di = NULL; + + /* clear downlink task */ + dsp_api.db_w->d_task_d = 0; + } + + /* mark READ page as being used */ + dsp_api.r_page_used = 1; + + return 0; +} + +static int l1s_nb_cmd(__unused uint8_t p1, uint8_t burst_id, + __unused uint16_t p3) +{ + uint16_t arfcn; + uint8_t tsc, tn; + + putchart('N'); + + if (burst_id == 1) { + /* allocate message only at 2nd burst in case of + * consecutive/overlapping normal burst RX tasks */ + /* FIXME: we actually want all allocation out of L1S! */ + if (rxnb.msg) + printf("nb_cmd(0) and rxnb.msg != NULL\n"); + /* allocate msgb as needed. FIXME: from L1A ?? */ + rxnb.msg = l1ctl_msgb_alloc(L1CTL_DATA_IND); + if (!rxnb.msg) + printf("nb_cmd(0): unable to allocate msgb\n"); + rxnb.dl = (struct l1ctl_info_dl *) msgb_put(rxnb.msg, sizeof(*rxnb.dl)); + rxnb.di = (struct l1ctl_data_ind *) msgb_put(rxnb.msg, sizeof(*rxnb.di)); + } + + rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); + + dsp_load_rx_task(ALLC_DSP_TASK, burst_id, tsc); + dsp_end_scenario(); + + l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB, tn); + tpu_end_scenario(); + + return 0; +} + +const struct tdma_sched_item nb_sched_set[] = { + SCHED_ITEM(l1s_nb_cmd, 0, 0), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_cmd, 0, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 0), SCHED_ITEM(l1s_nb_cmd, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 1), SCHED_ITEM(l1s_nb_cmd, 0, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_nb_resp, 0, 3), SCHED_END_FRAME(), + SCHED_END_SET() +}; diff --git a/src/target/firmware/layer1/prim_tx_nb.c b/src/target/firmware/layer1/prim_tx_nb.c new file mode 100644 index 00000000..7e4bd960 --- /dev/null +++ b/src/target/firmware/layer1/prim_tx_nb.c @@ -0,0 +1,209 @@ +/* Layer 1 - Transmit Normal Burst */ + +/* (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <layer1/sync.h> +#include <layer1/agc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> +#include <layer1/rfch.h> + +#include <l1a_l23_interface.h> + +/* Channel type definitions for DEDICATED mode */ +#define INVALID_CHANNEL 0 +#define TCH_F 1 +#define TCH_H 2 +#define SDCCH_4 3 +#define SDCCH_8 4 + +/* Channel mode definitions for DEDICATED mode */ +#define SIG_ONLY_MODE 0 // signalling only +#define TCH_FS_MODE 1 // speech full rate +#define TCH_HS_MODE 2 // speech half rate +#define TCH_96_MODE 3 // data 9,6 kb/s +#define TCH_48F_MODE 4 // data 4,8 kb/s full rate +#define TCH_48H_MODE 5 // data 4,8 kb/s half rate +#define TCH_24F_MODE 6 // data 2,4 kb/s full rate +#define TCH_24H_MODE 7 // data 2,4 kb/s half rate +#define TCH_EFR_MODE 8 // enhanced full rate +#define TCH_144_MODE 9 // data 14,4 kb/s half rate + +static uint32_t last_txnb_fn; + +static const uint8_t ubUui[23] = { 0x01, 0x03, 0x01, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b }; + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_resp(__unused uint8_t p1, __unused uint8_t burst_id, + __unused uint16_t p3) +{ + putchart('t'); + + dsp_api.r_page_used = 1; + + if (burst_id == 3) { + last_txnb_fn = l1s.current_time.fn - 4; + l1s_compl_sched(L1_COMPL_TX_NB); + } + + return 0; +} + +/* p1: type of operation (0: one NB, 1: one RACH burst, 2: four NB */ +static int l1s_tx_cmd(uint8_t p1, uint8_t burst_id, uint16_t p3) +{ + uint16_t arfcn; + uint8_t tsc, tn; + uint8_t mf_task_id = p3 & 0xff; + uint8_t mf_task_flags = p3 >> 8; + + putchart('T'); + + l1s_tx_apc_helper(); + + if (p1 == 0) /* DUL_DSP_TASK, one normal burst */ + dsp_load_tch_param(0, SIG_ONLY_MODE, INVALID_CHANNEL, 0, 0, 0); + else if (p1 == 2) /* DUL_DSP_TASK, four normal bursts */ + dsp_load_tch_param(0, SIG_ONLY_MODE, SDCCH_4, 0, 0, 0); + + /* before sending first of the four bursts, copy data to API ram */ + if (burst_id == 0) { + uint16_t *info_ptr = dsp_api.ndb->a_cu; + struct llist_head *tx_queue; + struct msgb *msg; + const uint8_t *data; + int i; + uint8_t j; + + /* distinguish between DCCH and ACCH */ + if (mf_task_flags & MF_F_SACCH) { + puts("SACCH queue "); + tx_queue = &l1s.tx_queue[L1S_CHAN_SACCH]; + } else { + puts("SDCCH queue "); + tx_queue = &l1s.tx_queue[L1S_CHAN_MAIN]; + } + msg = msgb_dequeue(tx_queue); + + /* If the TX queue is empty, send idle pattern */ + if (!msg) { + puts("TX idle pattern\n"); + data = ubUui; + } else { + puts("TX uplink msg\n"); + data = msg->l3h; + } + + /* Fill data block Header */ + info_ptr[0] = (1 << B_BLUD); // 1st word: Set B_BLU bit. + info_ptr[1] = 0; // 2nd word: cleared. + info_ptr[2] = 0; // 3rd word: cleared. + + /* Copy first 22 bytes in the first 11 words after header. */ + for (i=0, j=(3+0); j<(3+11); j++) { + info_ptr[j] = ((uint16_t)(data[i])) | ((uint16_t)(data[i+1]) << 8); + printf("%02x %02x ", data[i], data[i+1]); + i += 2; + } + /* Copy last UWORD8 (23rd) in the 12th word after header. */ + info_ptr[14] = data[22]; + printf("%02x\n", data[22]); + + if (msg) + msgb_free(msg); + } + + rfch_get_params(&l1s.next_time, &arfcn, &tsc, &tn); + + dsp_load_tx_task(DUL_DSP_TASK, burst_id, tsc); + dsp_end_scenario(); + + l1s_tx_win_ctrl(arfcn, L1_TXWIN_NB, 0, tn); + tpu_end_scenario(); + + return 0; +} + +/* Asynchronous completion handler for NB transmit */ +static void l1a_tx_nb_compl(__unused enum l1_compl c) +{ + struct msgb *msg; + + msg = l1_create_l2_msg(L1CTL_DATA_CONF, last_txnb_fn, 0, 0); + l1_queue_for_l2(msg); +} + +void l1s_tx_test(uint8_t base_fn, uint8_t type) +{ + printf("Starting TX %d\n", type); + + if (type == 0) {// one normal burst + tdma_schedule(base_fn, &l1s_tx_cmd, 0, 0, 0); + tdma_schedule(base_fn + 2, &l1s_tx_resp, 0, 0, 0); + } else if (type == 2) { // four normal burst + tdma_schedule(base_fn, &l1s_tx_cmd, 2, 0, 0); + tdma_schedule(base_fn + 1, &l1s_tx_cmd, 2, 1, 0); + tdma_schedule(base_fn + 2, &l1s_tx_resp, 2, 0, 0); + tdma_schedule(base_fn + 2, &l1s_tx_cmd, 2, 2, 0); + tdma_schedule(base_fn + 3, &l1s_tx_resp, 2, 1, 0); + tdma_schedule(base_fn + 3, &l1s_tx_cmd, 2, 3, 0); + tdma_schedule(base_fn + 4, &l1s_tx_resp, 2, 2, 0); + tdma_schedule(base_fn + 5, &l1s_tx_resp, 2, 3, 0); + } +} + +/* sched sets for uplink */ +const struct tdma_sched_item nb_sched_set_ul[] = { + SCHED_ITEM(l1s_tx_cmd, 2, 0), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_cmd, 2, 1), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, 2, 0), SCHED_ITEM(l1s_tx_cmd, 2, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, 2, 1), SCHED_ITEM(l1s_tx_cmd, 2, 3), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, 2, 2), SCHED_END_FRAME(), + SCHED_ITEM(l1s_tx_resp, 2, 3), SCHED_END_FRAME(), + SCHED_END_SET() +}; + +static __attribute__ ((constructor)) void prim_tx_nb_init(void) +{ + l1s.completion[L1_COMPL_TX_NB] = &l1a_tx_nb_compl; +} diff --git a/src/target/firmware/layer1/rfch.c b/src/target/firmware/layer1/rfch.c new file mode 100644 index 00000000..50331bf5 --- /dev/null +++ b/src/target/firmware/layer1/rfch.c @@ -0,0 +1,142 @@ +/* RF Channel utilities */ + +/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> + +#include <osmocore/gsm_utils.h> + +#include <layer1/sync.h> + + +/* + * Hopping sequence generation + * + * The algorithm is explained in GSM 05.02 Section 6.2.3 + * + * if HSN = 0 (cyclic hopping) then: + * MAI, integer (0 .. N-1) : + * MAI = (FN + MAIO) modulo N + * + * else: + * M, integer (0 .. 152) : + * M = T2 + RNTABLE((HSN xor T1R) + T3) + * + * S, integer (0 .. N-1) : + * M' = M modulo (2 ^ NBIN) + * T' = T3 modulo (2 ^ NBIN) + * + * if M' < N then: + * S = M' + * else: + * S = (M'+T') modulo N + * + * MAI, integer (0 .. N-1) : + * MAI = (S + MAIO) modulo N + */ + +static uint8_t rn_table[114] = { + 48, 98, 63, 1, 36, 95, 78, 102, 94, 73, + 0, 64, 25, 81, 76, 59, 124, 23, 104, 100, + 101, 47, 118, 85, 18, 56, 96, 86, 54, 2, + 80, 34, 127, 13, 6, 89, 57, 103, 12, 74, + 55, 111, 75, 38, 109, 71, 112, 29, 11, 88, + 87, 19, 3, 68, 110, 26, 33, 31, 8, 45, + 82, 58, 40, 107, 32, 5, 106, 92, 62, 67, + 77, 108, 122, 37, 60, 66, 121, 42, 51, 126, + 117, 114, 4, 90, 43, 52, 53, 113, 120, 72, + 16, 49, 7, 79, 119, 61, 22, 84, 9, 97, + 91, 15, 21, 24, 46, 39, 93, 105, 65, 70, + 125, 99, 17, 123, +}; + + +static int pow_nbin_mask(int n) +{ + int x; + x = (n ) | + (n >> 1) | + (n >> 2) | + (n >> 3) | + (n >> 4) | + (n >> 5) | + (n >> 6); + return x; +} + +static int16_t rfch_hop_seq_gen(struct gsm_time *t, + uint8_t hsn, uint8_t maio, + uint8_t n, uint16_t *arfcn_tbl) +{ + int mai; + + if (!hsn) { + /* cyclic hopping */ + mai = (t->fn + maio) % n; + } else { + /* pseudo random hopping */ + int m, mp, tp, s, pnm; + + pnm = pow_nbin_mask(n); + + m = t->t2 + rn_table[(hsn ^ (t->t1 & 63)) + t->t3]; + mp = m & pnm; + + if (mp < n) + s = mp; + else { + tp = t->t3 & pnm; + s = (mp + tp) % n; + } + + mai = (s + maio) % n; + } + + return arfcn_tbl ? arfcn_tbl[mai] : mai; +} + + +/* RF Channel parameters */ +void rfch_get_params(struct gsm_time *t, + uint16_t *arfcn_p, uint8_t *tsc_p, uint8_t *tn_p) +{ + if (l1s.dedicated.type == GSM_DCHAN_NONE) { + /* Serving cell only */ + *arfcn_p = l1s.serving_cell.arfcn; + *tsc_p = l1s.serving_cell.bsic & 0x7; + *tn_p = 0; + } else { + /* Dedicated channel */ + if (l1s.dedicated.h) { + *arfcn_p = rfch_hop_seq_gen(t, + l1s.dedicated.h1.hsn, + l1s.dedicated.h1.maio, + l1s.dedicated.h1.n, + l1s.dedicated.h1.ma); + } else { + *arfcn_p = l1s.dedicated.h0.arfcn; + } + + *tsc_p = l1s.dedicated.tsc; + *tn_p = l1s.dedicated.tn; + } +} + diff --git a/src/target/firmware/layer1/sched_gsmtime.c b/src/target/firmware/layer1/sched_gsmtime.c new file mode 100644 index 00000000..6a549e2d --- /dev/null +++ b/src/target/firmware/layer1/sched_gsmtime.c @@ -0,0 +1,119 @@ +/* GSM-Time One-shot Event Scheduler Implementation (on top of TDMA sched) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <errno.h> + +#include <debug.h> +#include <osmocore/linuxlist.h> + +#include <layer1/tdma_sched.h> +#include <layer1/sched_gsmtime.h> + +static struct sched_gsmtime_event sched_gsmtime_events[16]; +static LLIST_HEAD(active_evts); +static LLIST_HEAD(inactive_evts); + +/* Scheduling of a tdma_sched_item list one-shot at a givnen GSM time */ +int sched_gsmtime(const struct tdma_sched_item *si, uint32_t fn, uint16_t p3) +{ + struct llist_head *lh; + struct sched_gsmtime_event *evt, *cur; + + printd("sched_gsmtime(si=%p, fn=%u)\n", si, fn); + + /* obtain a free/inactive event structure */ + if (llist_empty(&inactive_evts)) + return -EBUSY; + lh = inactive_evts.next; + llist_del(lh); + evt = llist_entry(lh, struct sched_gsmtime_event, list); + + evt->fn = fn; + evt->si = si; + evt->p3 = p3; + + /* do a sorted insert into the list, i.e. insert the new + * event _before_ the first entry that has a higher fn */ + llist_for_each_entry(cur, &active_evts, list) { + if (cur->fn > evt->fn) { + llist_add_tail(lh, &cur->list); + return 0; + } + } + + /* if we reach here, active_evts is empty _OR_ new event + * is after all the other events: append at end of list */ + llist_add_tail(lh, &active_evts); + + return 0; +} + +/* how many TDMA frame ticks should we schedule events ahead? */ +#define SCHEDULE_AHEAD 2 + +/* how long do we need to tell the DSP in advance what we want to do? */ +#define SCHEDULE_LATENCY 1 + +/* execute all GSMTIME one-shot events pending for 'fn' */ +int sched_gsmtime_execute(uint32_t fn) +{ + struct sched_gsmtime_event *evt, *evt2; + int num = 0; + + llist_for_each_entry_safe(evt, evt2, &active_evts, list) { + if (evt->fn == fn + SCHEDULE_AHEAD) { + printd("sched_gsmtime_execute(time=%u): fn=%u si=%p\n", fn, evt->fn, evt->si); + tdma_schedule_set(SCHEDULE_AHEAD-SCHEDULE_LATENCY, + evt->si, evt->p3); + llist_del(&evt->list); + /* put event back in list of inactive (free) events */ + llist_add(&evt->list, &inactive_evts); + num++; + } if (evt->fn > fn + SCHEDULE_AHEAD) { + /* break the loop as our list is ordered */ + break; + } + } + return num; +} + +void sched_gsmtime_init(void) +{ + unsigned int i; + + printd("sched_gsmtime_init()\n"); + + for (i = 0; i < ARRAY_SIZE(sched_gsmtime_events); i++) + llist_add(&sched_gsmtime_events[i].list, &inactive_evts); +} + +void sched_gsmtime_reset(void) +{ + struct sched_gsmtime_event *evt, *evt2; + + llist_for_each_entry_safe(evt, evt2, &active_evts, list) { + llist_del(&evt->list); + /* put event back in list of inactive (free) events */ + llist_add(&evt->list, &inactive_evts); + } +} diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c new file mode 100644 index 00000000..0c85c57b --- /dev/null +++ b/src/target/firmware/layer1/sync.c @@ -0,0 +1,367 @@ +/* Synchronous part of GSM Layer 1 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de> + * (C) 2010 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 <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <defines.h> +#include <debug.h> +#include <memory.h> +#include <byteorder.h> +#include <asm/system.h> + +#include <osmocore/gsm_utils.h> +#include <osmocore/msgb.h> +#include <calypso/dsp_api.h> +#include <calypso/irq.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <calypso/dsp.h> +#include <calypso/timer.h> +#include <comm/sercomm.h> + +#include <abb/twl3025.h> + +//#define DEBUG_EVERY_TDMA + +#include <layer1/sync.h> +#include <layer1/afc.h> +#include <layer1/agc.h> +#include <layer1/tdma_sched.h> +#include <layer1/mframe_sched.h> +#include <layer1/sched_gsmtime.h> +#include <layer1/tpu_window.h> +#include <layer1/l23_api.h> + +#include <l1a_l23_interface.h> + +struct l1s_state l1s; + +void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn) +{ + ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN); + + if (delta_fn == 1) { + ADD_MODULO(time->t2, 1, 26); + ADD_MODULO(time->t3, 1, 51); + + /* if the new frame number is a multiple of 51 */ + if (time->t3 == 0) { + ADD_MODULO(time->tc, 1, 8); + + /* if new FN is multiple of 51 and 26 */ + if (time->t2 == 0) + ADD_MODULO(time->t1, 1, 2048); + } + } else + gsm_fn2gsmtime(time, time->fn); +} + +void l1s_time_dump(const struct gsm_time *time) +{ + printf("fn=%lu(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3); +} + +/* clip a signed 16bit value at a certain limit */ +int16_t clip_int16(int16_t angle, int16_t clip_at) +{ + if (angle > clip_at) + angle = clip_at; + else if (angle < -clip_at) + angle = -clip_at; + + return angle; +} + +int16_t l1s_snr_int(uint16_t snr) +{ + return snr >> 10; +} + +uint16_t l1s_snr_fract(uint16_t snr) +{ + uint32_t fract = snr & 0x3ff; + fract = fract * 1000 / (2 << 10); + + return fract & 0xffff; +} + +#define AFC_MAX_ANGLE 328 /* 0.01 radian in fx1.15 */ + +/* synchronize the L1S to a new timebase (typically a new cell */ +void synchronize_tdma(struct l1_cell_info *cinfo) +{ + int32_t fn_offset; + uint32_t tpu_shift = cinfo->time_alignment; + + /* NB detection only works if the TOA of the SB + * is within 0...8. We have to add 75 to get an SB TOA of 4. */ + tpu_shift += 75; + + tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA; + + fn_offset = cinfo->fn_offset - 1; + + /* if we're already very close to the end of the TPU frame, the + * next interrupt will basically occur now and we need to + * compensate */ + if (tpu_shift < SWITCH_TIME) + fn_offset++; + +#if 0 /* probably wrong as we already added "offset" and "shift" above */ + /* increment the TPU quarter-bit offset */ + l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE; +#else + l1s.tpu_offset = tpu_shift; +#endif + + puts("Synchronize_TDMA\n"); + /* request the TPU to adjust the SYNCHRO and OFFSET registers */ + tpu_enq_at(SWITCH_TIME); + tpu_enq_sync(l1s.tpu_offset); +#if 0 + /* FIXME: properly end the TPU window at the emd of l1_sync() */ + tpu_end_scenario(); +#endif + + /* Change the current time to reflect the new value */ + l1s_time_inc(&l1s.current_time, fn_offset); + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + + /* The serving cell now no longer has a frame or bit offset */ + cinfo->fn_offset = 0; + cinfo->time_alignment = 0; +} + +void l1s_reset_hw(void) +{ + dsp_api.w_page = 0; + dsp_api.r_page = 0; + dsp_api.r_page_used = 0; + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + dsp_api.ndb->d_dsp_page = 0; + + /* we have to really reset the TPU, otherwise FB detection + * somtimes returns wrong TOA values. */ + tpu_reset(1); + tpu_reset(0); + tpu_rewind(); + tpu_enq_wait(5); /* really needed ? */ + tpu_enq_sync(l1s.tpu_offset); + tpu_end_scenario(); +} + +/* Timer for detecting lost IRQ */ +#define TIMER_TICKS_PER_TDMA 1875 + +static int last_timestamp; + +static inline void check_lost_frame(void) +{ + int diff, timestamp = hwtimer_read(1); + + if (last_timestamp < timestamp) + last_timestamp += (4*TIMER_TICKS_PER_TDMA); + + diff = last_timestamp - timestamp; + if (diff != 1875) + printf("LOST!\n"); + + last_timestamp = timestamp; +} + +/* schedule a completion */ +void l1s_compl_sched(enum l1_compl c) +{ + unsigned long flags; + + local_firq_save(flags); + l1s.scheduled_compl |= (1 << c); + local_irq_restore(flags); +} + +/* main routine for synchronous part of layer 1, called by frame interrupt + * generated by TPU once every TDMA frame */ +static void l1_sync(void) +{ + putchart('+'); + + check_lost_frame(); + + /* Increment Time */ + l1s.current_time = l1s.next_time; + l1s_time_inc(&l1s.next_time, 1); + //l1s_time_dump(&l1s.current_time); putchar(' '); + + dsp_api.frame_ctr++; + dsp_api.r_page_used = 0; + + /* Update pointers */ + if (dsp_api.w_page == 0) + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + else + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1; + + if (dsp_api.r_page == 0) + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + else + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1; + + /* Reset MCU->DSP page */ + dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w)); + + /* Update AFC */ + afc_load_dsp(); + + if (dsp_api.ndb->d_error_status) { + printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status); + dsp_api.ndb->d_error_status = 0; + } + + /* execute the sched_items that have been scheduled for this + * TDMA frame */ + tdma_sched_execute(); + + if (dsp_api.r_page_used) { + /* clear and switch the read page */ + dsp_api_memset((uint16_t *) dsp_api.db_r, + sizeof(*dsp_api.db_r)); + + /* TSM30 does it (really needed ?): + * Set crc result as "SB not found". */ + dsp_api.db_r->a_sch[0] = (1<<B_SCH_CRC); /* B_SCH_CRC =1, BLUD =0 */ + + dsp_api.r_page ^= 1; + } + + //dsp_end_scenario(); + + /* schedule new / upcoming TDMA items */ + mframe_schedule(); + /* schedule new / upcoming one-shot events */ + sched_gsmtime_execute(l1s.current_time.fn); + + tdma_sched_advance(); +} + +/* ABORT command ********************************************************/ + +static int l1s_abort_cmd(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + putchart('A'); + + /* similar to l1s_reset_hw() without touching the TPU */ + + dsp_api.w_page = 0; + dsp_api.r_page = 0; + dsp_api.r_page_used = 0; + dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0; + dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0; + + /* Reset task commands. */ + dsp_api.db_w->d_task_d = NO_DSP_TASK; /* Init. RX task to NO TASK */ + dsp_api.db_w->d_task_u = NO_DSP_TASK; /* Init. TX task to NO TASK */ + dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */ + dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */ + dsp_api.ndb->d_dsp_page = 0; + + /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */ + dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT); + return 0; +} + +void l1s_dsp_abort(void) +{ + /* abort right now */ + tdma_schedule(0, &l1s_abort_cmd, 0, 0, 0); +} + +void l1s_tx_apc_helper(void) +{ + int i; + + /* Load the ApcOffset into the DSP */ + #define MY_OFFSET 4 + dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | MY_OFFSET) | 1; /* 2x slope for the GTA-02 ramp */ + + /* Load the TX Power into the DSP */ + /* + If the power is too low (below 0 dBm) the ramp is not OK, + especially for GSM-1800. However an MS does not send below + 0dBm anyway. + */ + dsp_api.db_w->d_power_ctl = ABB_VAL(AUXAPC, 0xC0); /* 2 dBm pulse with offset 4 (GSM-1800) */ + + /* Update the ramp according to the PCL */ + for (i = 0; i < 16; i++) + dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, twl3025_default_ramp[i]); + + /* The Ramp Table is sent to ABB only once after RF init routine called */ + dsp_api.db_w->d_ctrl_abb |= (1 << B_RAMP) | (1 << B_BULRAMPDEL); +} + +/* Interrupt handler */ +static void frame_irq(__unused enum irq_nr nr) +{ + l1_sync(); +} + +/* reset the layer1 as part of synchronizing to a new cell */ +void l1s_reset(void) +{ + l1s.fb.mode = 0; + + /* reset scheduler and hardware */ + sched_gsmtime_reset(); + mframe_reset(); + tdma_sched_reset(); + l1s_dsp_abort(); +} + +void l1s_init(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(l1s.tx_queue); i++) + INIT_LLIST_HEAD(&l1s.tx_queue[i]); + + sched_gsmtime_init(); + + /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */ + irq_register_handler(IRQ_TPU_FRAME, &frame_irq); + irq_config(IRQ_TPU_FRAME, 1, 1, 0); + irq_enable(IRQ_TPU_FRAME); + + /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */ + hwtimer_enable(1, 1); + hwtimer_load(1, (1875*4)-1); + hwtimer_config(1, 0, 1); + hwtimer_enable(1, 1); +} + diff --git a/src/target/firmware/layer1/tdma_sched.c b/src/target/firmware/layer1/tdma_sched.c new file mode 100644 index 00000000..82892103 --- /dev/null +++ b/src/target/firmware/layer1/tdma_sched.c @@ -0,0 +1,185 @@ +/* TDMA Scheduler Implementation */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <defines.h> +#include <debug.h> +#include <osmocore/gsm_utils.h> + +#include <layer1/tdma_sched.h> +#include <layer1/sync.h> + +#include <calypso/dsp.h> + +/* dummy function to mark end of set */ +int tdma_end_set(__unused uint8_t p1, __unused uint8_t p2, + __unused uint16_t p3) +{ + return 0; +} + +static uint8_t wrap_bucket(uint8_t offset) +{ + uint16_t bucket; + + bucket = (l1s.tdma_sched.cur_bucket + offset) + % ARRAY_SIZE(l1s.tdma_sched.bucket); + + return bucket; +} + +/* Schedule an item at 'frame_offset' TDMA frames in the future */ +int tdma_schedule(uint8_t frame_offset, tdma_sched_cb *cb, uint8_t p1, uint8_t p2, uint16_t p3) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + struct tdma_sched_item *sched_item; + + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + + sched_item = &bucket->item[bucket->num_items++]; + + sched_item->cb = cb; + sched_item->p1 = p1; + sched_item->p2 = p2; + sched_item->p3 = p3; + + return 0; +} + +/* Schedule a set of items starting from 'frame_offset' TDMA frames in the future */ +int tdma_schedule_set(uint8_t frame_offset, const struct tdma_sched_item *item_set, uint16_t p3) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t bucket_nr = wrap_bucket(frame_offset); + int i, j; + + for (i = 0, j = 0; 1; i++) { + const struct tdma_sched_item *sched_item = &item_set[i]; + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + + if (sched_item->cb == &tdma_end_set) { + /* end of scheduler set, return */ + break; + } + + if (sched_item->cb == NULL) { + /* advance to next bucket (== TDMA frame) */ + bucket_nr = wrap_bucket(++frame_offset); + j++; + continue; + } + /* check for bucket overflow */ + if (bucket->num_items >= ARRAY_SIZE(bucket->item)) { + puts("tdma_schedule bucket overflow\n"); + return -1; + } + /* copy the item from the set into the current bucket item position */ + memcpy(&bucket->item[bucket->num_items], sched_item, sizeof(*sched_item)); + bucket->item[bucket->num_items].p3 = p3; + bucket->num_items++; + } + + return j; +} + +/* Advance TDMA scheduler to the next bucket */ +void tdma_sched_advance(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + uint8_t next_bucket; + + /* advance to the next bucket */ + next_bucket = wrap_bucket(1); + sched->cur_bucket = next_bucket; +} + +/* Execute pre-scheduled events for current frame */ +int tdma_sched_execute(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + struct tdma_sched_bucket *bucket; + int i, num_events = 0; + + /* determine current bucket */ + bucket = &sched->bucket[sched->cur_bucket]; + + /* iterate over items in this bucket and call callback function */ + for (i = 0; i < bucket->num_items; i++) { + struct tdma_sched_item *item = &bucket->item[i]; + int rc; + + num_events++; + + rc = item->cb(item->p1, item->p2, item->p3); + if (rc < 0) { + printf("Error %d during processing of item %u of bucket %u\n", + rc, i, sched->cur_bucket); + return rc; + } + /* if the cb() we just called has scheduled more items for the + * current TDMA, bucket->num_items will have increased and we + * will simply continue to execute them as intended */ + } + + /* clear/reset the bucket */ + bucket->num_items = 0; + + /* return number of items that we called */ + return num_events; +} + +void tdma_sched_reset(void) +{ + struct tdma_scheduler *sched = &l1s.tdma_sched; + unsigned int bucket_nr; + + for (bucket_nr = 0; bucket_nr < ARRAY_SIZE(sched->bucket); bucket_nr++) { + struct tdma_sched_bucket *bucket = &sched->bucket[bucket_nr]; + /* current bucket will be reset by iteration code above! */ + if (bucket_nr != sched->cur_bucket) + bucket->num_items = 0; + } + + /* Don't reset cur_bucket, as it would upset the bucket iteration code + * in tdma_sched_execute() */ +} + +void tdma_sched_dump(void) +{ + unsigned int i; + + printf("\n(%2u)", l1s.tdma_sched.cur_bucket); + for (i = 0; i < ARRAY_SIZE(l1s.tdma_sched.bucket); i++) { + int bucket_nr = wrap_bucket(i); + struct tdma_sched_bucket *bucket = &l1s.tdma_sched.bucket[bucket_nr]; + printf("%u:", bucket->num_items); + } + putchar('\n'); +} diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c new file mode 100644 index 00000000..ef195ca3 --- /dev/null +++ b/src/target/firmware/layer1/tpu_window.c @@ -0,0 +1,133 @@ +/* TPU window control routines for Layer 1 */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <debug.h> +#include <stdio.h> + +#include <rffe.h> +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <abb/twl3025.h> +#include <rf/trf6151.h> + +#include <layer1/sync.h> +#include <layer1/tpu_window.h> + +/* all units in GSM quarter-bits (923.1ns) */ +#define L1_TDMA_LENGTH_Q 5000 +#define L1_BURST_LENGTH_Q 625 /* L1_TDMA_LENGTH_Q/8 */ + +#define L1_NB_MARGIN_Q (3 * 4) +#define L1_SB_MARGIN_Q (23 * 4) +#define L1_TAIL_DURATION_Q (3 * 4) + +/* Sample length as required by the Calypso DSP */ +#define L1_NB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_NB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_SB_DURATION_Q (L1_BURST_LENGTH_Q + 2 * L1_SB_MARGIN_Q - L1_TAIL_DURATION_Q) +#define L1_FB_DURATION_Q (11 * L1_TDMA_LENGTH_Q + 2057) /* more than 11 full slots */ +#define L1_FB26_DURATION_Q (L1_TDMA_LENGTH_Q + 798) +#define L1_PW_DURATION_Q 289 + +#define DSP_SETUP_TIME 66 + +static const uint16_t rx_burst_duration[_NUM_L1_RXWIN] = { + [L1_RXWIN_PW] = L1_PW_DURATION_Q, + [L1_RXWIN_FB] = L1_FB_DURATION_Q, + [L1_RXWIN_SB] = L1_SB_DURATION_Q, + [L1_RXWIN_NB] = L1_NB_DURATION_Q, +}; + +#define L1_TX_NB_DURATION_Q 626 +#define L1_TX_AB_DURATION_Q 386 + +static const uint16_t tx_burst_duration[_NUM_L1_TXWIN] = { + [L1_TXWIN_NB] = L1_TX_NB_DURATION_Q, + [L1_TXWIN_AB] = L1_TX_AB_DURATION_Q, +}; + +void l1s_rx_win_ctrl(uint16_t arfcn, enum l1_rxwin_type wtype, uint8_t tn) +{ + int16_t start = DSP_SETUP_TIME; + int16_t stop = start + rx_burst_duration[wtype] - 1; + + /* FIXME: AGC */ + /* FIXME: RF PLL */ + + /* Alignement */ + tpu_enq_offset( (5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000 ); + tpu_enq_at(5000 - 1000 - (L1_BURST_LENGTH_Q * tn)); + + /* window open for TRF6151 */ + /* FIXME: why do we need the magic value 100 ? */ + rffe_mode(gsm_arfcn2band(arfcn), 0); + trf6151_rx_window(start - 100, arfcn); + + /* Window open for ABB */ + twl3025_downlink(1, start); + + /* Delay 11 full TDMA frames */ + if (wtype == L1_RXWIN_FB) { + uint8_t i; + for (i = 0; i < 11; i++) + tpu_enq_at(0); + + stop -= 11 * L1_TDMA_LENGTH_Q; + } + + /* Window close for ABB */ + twl3025_downlink(0, stop); + + /* window close for TRF6151 */ + trf6151_set_mode(TRF6151_IDLE); +} + +void l1s_tx_win_ctrl(uint16_t arfcn, enum l1_txwin_type wtype, uint8_t pwr, uint8_t tn) +{ + /* uplink is three TS after downlink ( "+ 32" gives a TA of 1) */ + uint16_t offset = (L1_BURST_LENGTH_Q * 3) + 28; + + /* Alignement */ + tpu_enq_offset( (5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000 ); + tpu_enq_at(5000 - 10 - (L1_BURST_LENGTH_Q * tn)); + +#ifdef CONFIG_TX_ENABLE + /* window open for TRF6151 and RFFE */ + rffe_mode(gsm_arfcn2band(arfcn), 1); + trf6151_tx_window(offset, arfcn); +#endif + + /* Window open for ABB */ + twl3025_uplink(1, offset); + + /* Window close for ABB */ + twl3025_uplink(0, tx_burst_duration[wtype] + offset + 2); // TODO: "+ 2" + + /* window close for TRF6151 and RFFE */ + trf6151_set_mode(TRF6151_IDLE); +} + +void tpu_end_scenario(void) +{ + tpu_enq_sleep(); + tpu_enable(1); +} diff --git a/src/target/firmware/lib/Makefile b/src/target/firmware/lib/Makefile new file mode 100644 index 00000000..987857c9 --- /dev/null +++ b/src/target/firmware/lib/Makefile @@ -0,0 +1,7 @@ + +LIBRARIES+=mini +mini_DIR=lib +mini_SRCS=vsprintf.c string.c ctype.c printf.c console.c ctors.c \ + changebit.S clearbit.S div64.S lib1funcs.S memcpy.S memset.S setbit.S testchangebit.S testclearbit.S testsetbit.S + + diff --git a/src/target/firmware/lib/bitops.h b/src/target/firmware/lib/bitops.h new file mode 100644 index 00000000..428c9a63 --- /dev/null +++ b/src/target/firmware/lib/bitops.h @@ -0,0 +1,33 @@ + .macro bitop, instr + and r2, r0, #7 + mov r3, #1 + mov r3, r3, lsl r2 + save_and_disable_irqs ip + ldrb r2, [r1, r0, lsr #3] + \instr r2, r2, r3 + strb r2, [r1, r0, lsr #3] + restore_irqs ip + mov pc, lr + .endm + +/** + * testop - implement a test_and_xxx_bit operation. + * @instr: operational instruction + * @store: store instruction + * + * Note: we can trivially conditionalise the store instruction + * to avoid dirting the data cache. + */ + .macro testop, instr, store + add r1, r1, r0, lsr #3 + and r3, r0, #7 + mov r0, #1 + save_and_disable_irqs ip + ldrb r2, [r1] + tst r2, r0, lsl r3 + \instr r2, r2, r0, lsl r3 + \store r2, [r1] + restore_irqs ip + moveq r0, #0 + mov pc, lr + .endm diff --git a/src/target/firmware/lib/changebit.S b/src/target/firmware/lib/changebit.S new file mode 100644 index 00000000..7c709fb3 --- /dev/null +++ b/src/target/firmware/lib/changebit.S @@ -0,0 +1,21 @@ +/* + * linux/arch/arm/lib/changebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * 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. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +/* Purpose : Function to change a bit + * Prototype: int change_bit(int bit, void *addr) + */ +ENTRY(_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_change_bit_le) + bitop eor diff --git a/src/target/firmware/lib/clearbit.S b/src/target/firmware/lib/clearbit.S new file mode 100644 index 00000000..cb48f7ac --- /dev/null +++ b/src/target/firmware/lib/clearbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/clearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * 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. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +/* + * Purpose : Function to clear a bit + * Prototype: int clear_bit(int bit, void *addr) + */ +ENTRY(_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_clear_bit_le) + bitop bic diff --git a/src/target/firmware/lib/console.c b/src/target/firmware/lib/console.c new file mode 100644 index 00000000..2ec028a0 --- /dev/null +++ b/src/target/firmware/lib/console.c @@ -0,0 +1,190 @@ +/* Ringbuffer based serial console layer, imported from OpenPCD */ + +/* (C) 2006-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <string.h> +#include <console.h> +#include <calypso/uart.h> + +#include <asm/system.h> + +struct cons { + char buf[CONS_RB_SIZE]; + char *next_inbyte; + char *next_outbyte; + int initialized; +}; +static struct cons cons; + +void cons_init(void) +{ + memset(cons.buf, 0, sizeof(cons.buf)); + cons.next_inbyte = &cons.buf[0]; + cons.next_outbyte = &cons.buf[0]; + cons.initialized = 1; +} + +/* determine how many bytes are left in the ringbuffer without overwriting + bytes that haven't been written to the console yet */ +static int __cons_rb_space(void) +{ + if (cons.next_inbyte == cons.next_outbyte) + return sizeof(cons.buf)-1; + else if (cons.next_outbyte > cons.next_inbyte) + return (cons.next_outbyte - cons.next_inbyte) -1; + else + return sizeof(cons.buf) - 1 - (cons.next_inbyte - cons.next_outbyte); +} + +/* pull one char out of debug ring buffer */ +static int cons_rb_pull(char *ret) +{ + unsigned long flags; + + local_irq_save(flags); + + if (cons.next_outbyte == cons.next_inbyte) { + local_irq_restore(flags); + return -1; + } + + *ret = *cons.next_outbyte; + + cons.next_outbyte++; + if (cons.next_outbyte >= &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte = &cons.buf[0]; + } +#if 0 + else if (cons.next_outbyte > &cons.buf[0]+sizeof(cons.buf)) { + cons.next_outbyte -= sizeof(cons.buf); + } +#endif + + local_irq_restore(flags); + + return 0; +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static void __rb_flush_wait(void) +{ + char ch; + while (cons_rb_pull(&ch) >= 0) + uart_putchar_wait(CONS_UART_NR, ch); +} + +/* returns if everything was flushed (1) or if there's more to flush (0) */ +static int __rb_flush(void) +{ + while (!uart_tx_busy(CONS_UART_NR)) { + char ch; + if (cons_rb_pull(&ch) < 0) { + /* no more data to write, disable interest in Tx FIFO interrupts */ + return 1; + } + uart_putchar_nb(CONS_UART_NR, ch); + } + + /* if we reach here, UART Tx FIFO is busy again */ + return 0; +} + +/* flush pending data from debug ring buffer to serial port */ +int cons_rb_flush(void) +{ + return __rb_flush(); +} + +/* Append bytes to ring buffer, not more than we have left! */ +static void __cons_rb_append(const char *data, int len) +{ + if (cons.next_inbyte + len >= &cons.buf[0]+sizeof(cons.buf)) { + int before_tail = (&cons.buf[0]+sizeof(cons.buf)) - cons.next_inbyte; + /* copy the first part before we wrap */ + memcpy(cons.next_inbyte, data, before_tail); + data += before_tail; + len -= before_tail; + /* reset the buffer */ + cons.next_inbyte = &cons.buf[0]; + } + memcpy(cons.next_inbyte, data, len); + cons.next_inbyte += len; +} + +/* append bytes to the ringbuffer, do one wrap */ +int cons_rb_append(const char *data, int len) +{ + unsigned long flags; + int bytes_left; + const char *data_cur; + + /* we will never be able to write more than the console buffer */ + if (len > (int) sizeof(cons.buf)) + len = sizeof(cons.buf); + + local_irq_save(flags); + + bytes_left = __cons_rb_space(); + data_cur = data; + + if (len > bytes_left) { + /* append what we can */ + __cons_rb_append(data_cur, bytes_left); + /* busy-wait for all characters to be transmitted */ + __rb_flush_wait(); + /* fill it with the remaining bytes */ + len -= bytes_left; + data_cur += bytes_left; + } + __cons_rb_append(data_cur, len); + + /* we want to get Tx FIFO interrupts */ + uart_irq_enable(CONS_UART_NR, UART_IRQ_TX_EMPTY, 1); + + local_irq_restore(flags); + + return len; +} + +int cons_puts(const char *s) +{ + if (cons.initialized) { + return cons_rb_append(s, strlen(s)); + } else { + /* if the console is not active yet, we need to fall back */ + int i = strlen(s); + while (i--) + uart_putchar_wait(CONS_UART_NR, *s++); + return i; + } +} + +int cons_putchar(char c) +{ + if (cons.initialized) + return cons_rb_append(&c, 1); + else { + /* if the console is not active yet, we need to fall back */ + uart_putchar_wait(CONS_UART_NR, c); + return 0; + } +} diff --git a/src/target/firmware/lib/copy_template.S b/src/target/firmware/lib/copy_template.S new file mode 100644 index 00000000..cab355c0 --- /dev/null +++ b/src/target/firmware/lib/copy_template.S @@ -0,0 +1,255 @@ +/* + * linux/arch/arm/lib/copy_template.s + * + * Code template for optimized memory copy functions + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * 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 can be used to enable code to cacheline align the source pointer. + * Experiments on tested architectures (StrongARM and XScale) didn't show + * this a worthwhile thing to do. That might be different in the future. + */ +//#define CALGN(code...) code +#define CALGN(code...) + +/* + * Theory of operation + * ------------------- + * + * This file provides the core code for a forward memory copy used in + * the implementation of memcopy(), copy_to_user() and copy_from_user(). + * + * The including file must define the following accessor macros + * according to the need of the given function: + * + * ldr1w ptr reg abort + * + * This loads one word from 'ptr', stores it in 'reg' and increments + * 'ptr' to the next word. The 'abort' argument is used for fixup tables. + * + * ldr4w ptr reg1 reg2 reg3 reg4 abort + * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * + * This loads four or eight words starting from 'ptr', stores them + * in provided registers and increments 'ptr' past those words. + * The'abort' argument is used for fixup tables. + * + * ldr1b ptr reg cond abort + * + * Similar to ldr1w, but it loads a byte and increments 'ptr' one byte. + * It also must apply the condition code if provided, otherwise the + * "al" condition is assumed by default. + * + * str1w ptr reg abort + * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * str1b ptr reg cond abort + * + * Same as their ldr* counterparts, but data is stored to 'ptr' location + * rather than being loaded. + * + * enter reg1 reg2 + * + * Preserve the provided registers on the stack plus any additional + * data as needed by the implementation including this code. Called + * upon code entry. + * + * exit reg1 reg2 + * + * Restore registers with the values previously saved with the + * 'preserv' macro. Called upon code termination. + */ + + + enter r4, lr + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 4f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +3: PLD( pld [r1, #124] ) +4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + subs r2, r2, #32 + str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f + bge 3b + PLD( cmn r2, #96 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 + addne pc, pc, ip @ C is always clear here + b 7f +6: nop + ldr1w r1, r3, abort=20f + ldr1w r1, r4, abort=20f + ldr1w r1, r5, abort=20f + ldr1w r1, r6, abort=20f + ldr1w r1, r7, abort=20f + ldr1w r1, r8, abort=20f + ldr1w r1, lr, abort=20f + + add pc, pc, ip + nop + nop + str1w r0, r3, abort=20f + str1w r0, r4, abort=20f + str1w r0, r5, abort=20f + str1w r0, r6, abort=20f + str1w r0, r7, abort=20f + str1w r0, r8, abort=20f + str1w r0, lr, abort=20f + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldr1b r1, r3, ne, abort=21f + ldr1b r1, r4, cs, abort=21f + ldr1b r1, ip, cs, abort=21f + str1b r0, r3, ne, abort=21f + str1b r0, r4, cs, abort=21f + str1b r0, ip, cs, abort=21f + + exit r4, pc + +9: rsb ip, ip, #4 + cmp ip, #2 + ldr1b r1, r3, gt, abort=21f + ldr1b r1, r4, ge, abort=21f + ldr1b r1, lr, abort=21f + str1b r0, r3, gt, abort=21f + str1b r0, r4, ge, abort=21f + subs r2, r2, ip + str1b r0, lr, abort=21f + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr1w r1, lr, abort=21f + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 13f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +12: PLD( pld [r1, #124] ) +13: ldr4w r1, r4, r5, r6, r7, abort=19f + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldr4w r1, r8, r9, ip, lr, abort=19f + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + bge 12b + PLD( cmn r2, #96 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr1w r1, lr, abort=21f + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str1w r0, r3, abort=21f + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 + + +/* + * Abort preamble and completion macros. + * If a fixup handler is required then those macros must surround it. + * It is assumed that the fixup code will handle the private part of + * the exit macro. + */ + + .macro copy_abort_preamble +19: ldmfd sp!, {r5 - r9} + b 21f +20: ldmfd sp!, {r5 - r8} +21: + .endm + + .macro copy_abort_end + ldmfd sp!, {r4, pc} + .endm + diff --git a/src/target/firmware/lib/ctors.c b/src/target/firmware/lib/ctors.c new file mode 100644 index 00000000..982169df --- /dev/null +++ b/src/target/firmware/lib/ctors.c @@ -0,0 +1,15 @@ + +/* iterate over list of constructor functions and call each element */ +void do_global_ctors(const char *_ctors_start, const char *ctors_end) +{ + typedef void (*func_ptr)(void); + func_ptr *func, *ctors_start = (func_ptr *) _ctors_start; + + /* skip the first entry, as it contains the number of + * constructors which we don't use */ + ctors_start++; + + for (func = ctors_start; + *func && (func != (func_ptr *) ctors_end); func++) + (*func)(); +} diff --git a/src/target/firmware/lib/ctype.c b/src/target/firmware/lib/ctype.c new file mode 100644 index 00000000..f3732140 --- /dev/null +++ b/src/target/firmware/lib/ctype.c @@ -0,0 +1,34 @@ +/* + * linux/lib/ctype.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <ctype.h> + +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + diff --git a/src/target/firmware/lib/div64.S b/src/target/firmware/lib/div64.S new file mode 100644 index 00000000..7eeef50c --- /dev/null +++ b/src/target/firmware/lib/div64.S @@ -0,0 +1,200 @@ +/* + * linux/arch/arm/lib/div64.S + * + * Optimized computation of 64-bit dividend / 32-bit divisor + * + * Author: Nicolas Pitre + * Created: Oct 5, 2003 + * Copyright: Monta Vista Software, Inc. + * + * 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. + */ + +#include <asm/linkage.h> + +#ifdef __ARMEB__ +#define xh r0 +#define xl r1 +#define yh r2 +#define yl r3 +#else +#define xl r0 +#define xh r1 +#define yl r2 +#define yh r3 +#endif + +/* + * __do_div64: perform a division with 64-bit dividend and 32-bit divisor. + * + * Note: Calling convention is totally non standard for optimal code. + * This is meant to be used by do_div() from include/asm/div64.h only. + * + * Input parameters: + * xh-xl = dividend (clobbered) + * r4 = divisor (preserved) + * + * Output values: + * yh-yl = result + * xh = remainder + * + * Clobbered regs: xl, ip + */ + +ENTRY(__do_div64) + + @ Test for easy paths first. + subs ip, r4, #1 + bls 9f @ divisor is 0 or 1 + tst ip, r4 + beq 8f @ divisor is power of 2 + + @ See if we need to handle upper 32-bit result. + cmp xh, r4 + mov yh, #0 + blo 3f + + @ Align divisor with upper part of dividend. + @ The aligned divisor is stored in yl preserving the original. + @ The bit position is stored in ip. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz yl, r4 + clz ip, xh + sub yl, yl, ip + mov ip, #1 + mov ip, ip, lsl yl + mov yl, r4, lsl yl + +#else + + mov yl, r4 + mov ip, #1 +1: cmp yl, #0x80000000 + cmpcc yl, xh + movcc yl, yl, lsl #1 + movcc ip, ip, lsl #1 + bcc 1b + +#endif + + @ The division loop for needed upper bit positions. + @ Break out early if dividend reaches 0. +2: cmp xh, yl + orrcs yh, yh, ip + subcss xh, xh, yl + movnes ip, ip, lsr #1 + mov yl, yl, lsr #1 + bne 2b + + @ See if we need to handle lower 32-bit result. +3: cmp xh, #0 + mov yl, #0 + cmpeq xl, r4 + movlo xh, xl + movlo pc, lr + + @ The division loop for lower bit positions. + @ Here we shift remainer bits leftwards rather than moving the + @ divisor for comparisons, considering the carry-out bit as well. + mov ip, #0x80000000 +4: movs xl, xl, lsl #1 + adcs xh, xh, xh + beq 6f + cmpcc xh, r4 +5: orrcs yl, yl, ip + subcs xh, xh, r4 + movs ip, ip, lsr #1 + bne 4b + mov pc, lr + + @ The top part of remainder became zero. If carry is set + @ (the 33th bit) this is a false positive so resume the loop. + @ Otherwise, if lower part is also null then we are done. +6: bcs 5b + cmp xl, #0 + moveq pc, lr + + @ We still have remainer bits in the low part. Bring them up. + +#if __LINUX_ARM_ARCH__ >= 5 + + clz xh, xl @ we know xh is zero here so... + add xh, xh, #1 + mov xl, xl, lsl xh + mov ip, ip, lsr xh + +#else + +7: movs xl, xl, lsl #1 + mov ip, ip, lsr #1 + bcc 7b + +#endif + + @ Current remainder is now 1. It is worthless to compare with + @ divisor at this point since divisor can not be smaller than 3 here. + @ If possible, branch for another shift in the division loop. + @ If no bit position left then we are done. + movs ip, ip, lsr #1 + mov xh, #1 + bne 4b + mov pc, lr + +8: @ Division by a power of 2: determine what that divisor order is + @ then simply shift values around + +#if __LINUX_ARM_ARCH__ >= 5 + + clz ip, r4 + rsb ip, ip, #31 + +#else + + mov yl, r4 + cmp r4, #(1 << 16) + mov ip, #0 + movhs yl, yl, lsr #16 + movhs ip, #16 + + cmp yl, #(1 << 8) + movhs yl, yl, lsr #8 + addhs ip, ip, #8 + + cmp yl, #(1 << 4) + movhs yl, yl, lsr #4 + addhs ip, ip, #4 + + cmp yl, #(1 << 2) + addhi ip, ip, #3 + addls ip, ip, yl, lsr #1 + +#endif + + mov yh, xh, lsr ip + mov yl, xl, lsr ip + rsb ip, ip, #32 + orr yl, yl, xh, lsl ip + mov xh, xl, lsl ip + mov xh, xh, lsr ip + mov pc, lr + + @ eq -> division by 1: obvious enough... +9: moveq yl, xl + moveq yh, xh + moveq xh, #0 + moveq pc, lr + + @ Division by 0: + str lr, [sp, #-8]! + bl __div0 + + @ as wrong as it could be... + mov yl, #0 + mov yh, #0 + mov xh, #0 + ldr pc, [sp], #8 + diff --git a/src/target/firmware/lib/lib1funcs.S b/src/target/firmware/lib/lib1funcs.S new file mode 100644 index 00000000..b02a85eb --- /dev/null +++ b/src/target/firmware/lib/lib1funcs.S @@ -0,0 +1,334 @@ +/* + * linux/arch/arm/lib/lib1funcs.S: Optimized ARM division routines + * + * Author: Nicolas Pitre <nico@cam.org> + * - contributed to gcc-3.4 on Sep 30, 2003 + * - adapted for the Linux kernel on Oct 2, 2003 + */ + +/* Copyright 1995, 1996, 1998, 1999, 2000, 2003 Free Software Foundation, Inc. + +This file 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. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in 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; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#include <asm/linkage.h> +#include <asm/assembler.h> + + +.macro ARM_DIV_BODY dividend, divisor, result, curbit + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \curbit, \divisor + clz \result, \dividend + sub \result, \curbit, \result + mov \curbit, #1 + mov \divisor, \divisor, lsl \result + mov \curbit, \curbit, lsl \result + mov \result, #0 + +#else + + @ Initially shift the divisor left 3 bits if possible, + @ set curbit accordingly. This allows for curbit to be located + @ at the left end of each 4 bit nibbles in the division loop + @ to save one loop in most cases. + tst \divisor, #0xe0000000 + moveq \divisor, \divisor, lsl #3 + moveq \curbit, #8 + movne \curbit, #1 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + movlo \curbit, \curbit, lsl #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + movlo \curbit, \curbit, lsl #1 + blo 1b + + mov \result, #0 + +#endif + + @ Division loop +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + orrhs \result, \result, \curbit + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + orrhs \result, \result, \curbit, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + orrhs \result, \result, \curbit, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + orrhs \result, \result, \curbit, lsr #3 + cmp \dividend, #0 @ Early termination? + movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? + movne \divisor, \divisor, lsr #4 + bne 1b + +.endm + + +.macro ARM_DIV2_ORDER divisor, order + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + rsb \order, \order, #31 + +#else + + cmp \divisor, #(1 << 16) + movhs \divisor, \divisor, lsr #16 + movhs \order, #16 + movlo \order, #0 + + cmp \divisor, #(1 << 8) + movhs \divisor, \divisor, lsr #8 + addhs \order, \order, #8 + + cmp \divisor, #(1 << 4) + movhs \divisor, \divisor, lsr #4 + addhs \order, \order, #4 + + cmp \divisor, #(1 << 2) + addhi \order, \order, #3 + addls \order, \order, \divisor, lsr #1 + +#endif + +.endm + + +.macro ARM_MOD_BODY dividend, divisor, order, spare + +#if __LINUX_ARM_ARCH__ >= 5 + + clz \order, \divisor + clz \spare, \dividend + sub \order, \order, \spare + mov \divisor, \divisor, lsl \order + +#else + + mov \order, #0 + + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. +1: cmp \divisor, #0x10000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #4 + addlo \order, \order, #4 + blo 1b + + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. +1: cmp \divisor, #0x80000000 + cmplo \divisor, \dividend + movlo \divisor, \divisor, lsl #1 + addlo \order, \order, #1 + blo 1b + +#endif + + @ Perform all needed substractions to keep only the reminder. + @ Do comparisons in batch of 4 first. + subs \order, \order, #3 @ yes, 3 is intended here + blt 2f + +1: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + cmp \dividend, \divisor, lsr #1 + subhs \dividend, \dividend, \divisor, lsr #1 + cmp \dividend, \divisor, lsr #2 + subhs \dividend, \dividend, \divisor, lsr #2 + cmp \dividend, \divisor, lsr #3 + subhs \dividend, \dividend, \divisor, lsr #3 + cmp \dividend, #1 + mov \divisor, \divisor, lsr #4 + subges \order, \order, #4 + bge 1b + + tst \order, #3 + teqne \dividend, #0 + beq 5f + + @ Either 1, 2 or 3 comparison/substractions are left. +2: cmn \order, #2 + blt 4f + beq 3f + cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +3: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor + mov \divisor, \divisor, lsr #1 +4: cmp \dividend, \divisor + subhs \dividend, \dividend, \divisor +5: +.endm + + +ENTRY(__udivsi3) +ENTRY(__aeabi_uidiv) + + subs r2, r1, #1 + moveq pc, lr + bcc Ldiv0 + cmp r0, r1 + bls 11f + tst r1, r2 + beq 12f + + ARM_DIV_BODY r0, r1, r2, r3 + + mov r0, r2 + mov pc, lr + +11: moveq r0, #1 + movne r0, #0 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + mov r0, r0, lsr r2 + mov pc, lr + + +ENTRY(__umodsi3) + + subs r2, r1, #1 @ compare divisor with 1 + bcc Ldiv0 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + movls pc, lr + + ARM_MOD_BODY r0, r1, r2, r3 + + mov pc, lr + + +ENTRY(__divsi3) +ENTRY(__aeabi_idiv) + + cmp r1, #0 + eor ip, r0, r1 @ save the sign of the result. + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + subs r2, r1, #1 @ division by 1 or -1 ? + beq 10f + movs r3, r0 + rsbmi r3, r0, #0 @ positive dividend value + cmp r3, r1 + bls 11f + tst r1, r2 @ divisor is power of 2 ? + beq 12f + + ARM_DIV_BODY r3, r1, r0, r2 + + cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +10: teq ip, r0 @ same sign ? + rsbmi r0, r0, #0 + mov pc, lr + +11: movlo r0, #0 + moveq r0, ip, asr #31 + orreq r0, r0, #1 + mov pc, lr + +12: ARM_DIV2_ORDER r1, r2 + + cmp ip, #0 + mov r0, r3, lsr r2 + rsbmi r0, r0, #0 + mov pc, lr + + +ENTRY(__modsi3) + + cmp r1, #0 + beq Ldiv0 + rsbmi r1, r1, #0 @ loops below use unsigned. + movs ip, r0 @ preserve sign of dividend + rsbmi r0, r0, #0 @ if negative make positive + subs r2, r1, #1 @ compare divisor with 1 + cmpne r0, r1 @ compare dividend with divisor + moveq r0, #0 + tsthi r1, r2 @ see if divisor is power of 2 + andeq r0, r0, r2 + bls 10f + + ARM_MOD_BODY r0, r1, r2, r3 + +10: cmp ip, #0 + rsbmi r0, r0, #0 + mov pc, lr + +ENTRY(__aeabi_uidivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_uidiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +ENTRY(__aeabi_idivmod) + + stmfd sp!, {r0, r1, ip, lr} + bl __aeabi_idiv + ldmfd sp!, {r1, r2, ip, lr} + mul r3, r0, r2 + sub r1, r1, r3 + mov pc, lr + +Ldiv0: + + str lr, [sp, #-8]! + bl __div0 + mov r0, #0 @ About as wrong as it could be. + ldr pc, [sp], #8 + +ENTRY(__div0) + mov pc, lr diff --git a/src/target/firmware/lib/memcpy.S b/src/target/firmware/lib/memcpy.S new file mode 100644 index 00000000..2bbd5692 --- /dev/null +++ b/src/target/firmware/lib/memcpy.S @@ -0,0 +1,59 @@ +/* + * linux/arch/arm/lib/memcpy.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * 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. + */ + +#include <asm/linkage.h> +#include <asm/assembler.h> + + .macro ldr1w ptr reg abort + ldr \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond\()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + str \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond\()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + stmdb sp!, {r0, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ + +ENTRY(memcpy) + +#include "copy_template.S" + diff --git a/src/target/firmware/lib/memset.S b/src/target/firmware/lib/memset.S new file mode 100644 index 00000000..04e254a8 --- /dev/null +++ b/src/target/firmware/lib/memset.S @@ -0,0 +1,80 @@ +/* + * linux/arch/arm/lib/memset.S + * + * Copyright (C) 1995-2000 Russell King + * + * 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. + * + * ASM optimised string functions + */ +#include <asm/linkage.h> +#include <asm/assembler.h> + + .text + .align 5 + .word 0 + +1: subs r2, r2, #4 @ 1 do we have enough + blt 5f @ 1 bytes to align with? + cmp r3, #2 @ 1 + strltb r1, [r0], #1 @ 1 + strleb r1, [r0], #1 @ 1 + strb r1, [r0], #1 @ 1 + add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3)) +/* + * The pointer is now aligned and the length is adjusted. Try doing the + * memzero again. + */ + +ENTRY(memset) + ands r3, r0, #3 @ 1 unaligned? + bne 1b @ 1 +/* + * we know that the pointer in r0 is aligned to a word boundary. + */ + orr r1, r1, r1, lsl #8 + orr r1, r1, r1, lsl #16 + mov r3, r1 + cmp r2, #16 + blt 4f +/* + * We need an extra register for this loop - save the return address and + * use the LR + */ + str lr, [sp, #-4]! + mov ip, r1 + mov lr, r1 + +2: subs r2, r2, #64 + stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time. + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + stmgeia r0!, {r1, r3, ip, lr} + bgt 2b + LOADREGS(eqfd, sp!, {pc}) @ Now <64 bytes to go. +/* + * No need to correct the count; we're only testing bits from now on + */ + tst r2, #32 + stmneia r0!, {r1, r3, ip, lr} + stmneia r0!, {r1, r3, ip, lr} + tst r2, #16 + stmneia r0!, {r1, r3, ip, lr} + ldr lr, [sp], #4 + +4: tst r2, #8 + stmneia r0!, {r1, r3} + tst r2, #4 + strne r1, [r0], #4 +/* + * When we get here, we've got less than 4 bytes to zero. We + * may have an unaligned pointer as well. + */ +5: tst r2, #2 + strneb r1, [r0], #1 + strneb r1, [r0], #1 + tst r2, #1 + strneb r1, [r0], #1 + RETINSTR(mov,pc,lr) diff --git a/src/target/firmware/lib/printf.c b/src/target/firmware/lib/printf.c new file mode 100644 index 00000000..a4fc6876 --- /dev/null +++ b/src/target/firmware/lib/printf.c @@ -0,0 +1,19 @@ + +#include <stdio.h> +#include <stdarg.h> + +static char printf_buffer[1024]; + +int printf(const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + r = vsnprintf(printf_buffer, sizeof(printf_buffer), fmt, args); + va_end(args); + + puts(printf_buffer); + + return r; +} diff --git a/src/target/firmware/lib/setbit.S b/src/target/firmware/lib/setbit.S new file mode 100644 index 00000000..9009bc1e --- /dev/null +++ b/src/target/firmware/lib/setbit.S @@ -0,0 +1,22 @@ +/* + * linux/arch/arm/lib/setbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * 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. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +/* + * Purpose : Function to set a bit + * Prototype: int set_bit(int bit, void *addr) + */ +ENTRY(_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_set_bit_le) + bitop orr diff --git a/src/target/firmware/lib/string.c b/src/target/firmware/lib/string.c new file mode 100644 index 00000000..97036528 --- /dev/null +++ b/src/target/firmware/lib/string.c @@ -0,0 +1,50 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in <asm-xx/string.h> + * + * These are buggy as well.. + * + * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de> + * - Added strsep() which will replace strtok() soon (because strsep() is + * reentrant and should be faster). Use only strsep() in new code, please. + * + * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>, + * Matthew Hawkins <matt@mh.dropbear.id.au> + * - Kissed strtok() goodbye + */ + +#include <sys/types.h> +#include <string.h> +#include <ctype.h> + + +#ifndef __HAVE_ARCH_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} diff --git a/src/target/firmware/lib/testchangebit.S b/src/target/firmware/lib/testchangebit.S new file mode 100644 index 00000000..37c303e3 --- /dev/null +++ b/src/target/firmware/lib/testchangebit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testchangebit.S + * + * Copyright (C) 1995-1996 Russell King + * + * 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. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +ENTRY(_test_and_change_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_change_bit_le) + testop eor, strb diff --git a/src/target/firmware/lib/testclearbit.S b/src/target/firmware/lib/testclearbit.S new file mode 100644 index 00000000..985c3996 --- /dev/null +++ b/src/target/firmware/lib/testclearbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testclearbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * 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. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +ENTRY(_test_and_clear_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_clear_bit_le) + testop bicne, strneb diff --git a/src/target/firmware/lib/testsetbit.S b/src/target/firmware/lib/testsetbit.S new file mode 100644 index 00000000..4a8a164b --- /dev/null +++ b/src/target/firmware/lib/testsetbit.S @@ -0,0 +1,18 @@ +/* + * linux/arch/arm/lib/testsetbit.S + * + * Copyright (C) 1995-1996 Russell King + * + * 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. + */ +#include <asm/linkage.h> +#include <asm/assembler.h> +#include "bitops.h" + .text + +ENTRY(_test_and_set_bit_be) + eor r0, r0, #0x18 @ big endian byte ordering +ENTRY(_test_and_set_bit_le) + testop orreq, streqb diff --git a/src/target/firmware/lib/vsprintf.c b/src/target/firmware/lib/vsprintf.c new file mode 100644 index 00000000..80e8c1ad --- /dev/null +++ b/src/target/firmware/lib/vsprintf.c @@ -0,0 +1,847 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +/* + * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com> + * - changed to provide snprintf and vsnprintf functions + * So Feb 1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de> + * - scnprintf and vscnprintf + */ + +#include <stdio.h> +#include <limits.h> +#include <stdarg.h> +#include <sys/types.h> +#include <string.h> +#include <ctype.h> + +#include <asm/div64.h> + +/** + * strtoul - convert a string to an unsigned long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * strtol - convert a string to a signed long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -strtoul(cp+1,endp,base); + return strtoul(cp,endp,base); +} + + +/** + * strtoull - convert a string to an unsigned long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +unsigned long long strtoull(const char *cp,char **endp,unsigned int base) +{ + unsigned long long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } else if (base == 16) { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + + +/** + * strtoll - convert a string to a signed long long + * @cp: The start of the string + * @endp: A pointer to the end of the parsed string will be placed here + * @base: The number base to use + */ +long long strtoll(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -strtoull(cp+1,endp,base); + return strtoull(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits; + static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if ((signed long long) num < 0) { + sign = '-'; + num = - (signed long long) num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) { + while(size-->0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + } + if (sign) { + if (buf <= end) + *buf = sign; + ++buf; + } + if (type & SPECIAL) { + if (base==8) { + if (buf <= end) + *buf = '0'; + ++buf; + } else if (base==16) { + if (buf <= end) + *buf = '0'; + ++buf; + if (buf <= end) + *buf = digits[33]; + ++buf; + } + } + if (!(type & LEFT)) { + while (size-- > 0) { + if (buf <= end) + *buf = c; + ++buf; + } + } + while (i < precision--) { + if (buf <= end) + *buf = '0'; + ++buf; + } + while (i-- > 0) { + if (buf <= end) + *buf = tmp[i]; + ++buf; + } + while (size-- > 0) { + if (buf <= end) + *buf = ' '; + ++buf; + } + return buf; +} + +/** + * vsnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which would + * be generated for the given input, excluding the trailing + * '\0', as per ISO C99. If you want to have the exact + * number of characters written into @buf as return value + * (not including the trailing '\0'), use vscnprintf. If the + * return is greater than or equal to @size, the resulting + * string is truncated. + * + * Call this function if you are already dealing with a va_list. + * You probably want snprintf instead. + */ +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char *str, *end, c; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* Reject out-of-range values early */ + if ((int) size < 0) { + return 0; + } + + str = buf; + end = buf + size - 1; + + if (end < buf - 1) { + end = ((void *) -1); + size = end - buf + 1; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str <= end) + *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) { + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str <= end) + *str = c; + ++str; + while (--field_width > 0) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + } + for (i = 0; i < len; ++i) { + if (str <= end) + *str = *s; + ++str; ++s; + } + while (len < field_width--) { + if (str <= end) + *str = ' '; + ++str; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, end, + (unsigned long) va_arg(args, void *), + 16, field_width, precision, flags); + continue; + + + case 'n': + /* FIXME: + * What does C99 say about the overflow case here? */ + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z' || qualifier == 'z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str <= end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str <= end) + *str = '%'; + ++str; + if (*fmt) { + if (str <= end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z' || qualifier == 'z') { + num = va_arg(args, size_t); + } else if (qualifier == 't') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, end, num, base, + field_width, precision, flags); + } + if (str <= end) + *str = '\0'; + else if (size > 0) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + + +/** + * vscnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The return value is the number of characters which have been written into + * the @buf not including the trailing '\0'. If @size is <= 0 the function + * returns 0. + * + * Call this function if you are already dealing with a va_list. + * You probably want scnprintf instead. + */ +int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ + unsigned int i; + + i=vsnprintf(buf,size,fmt,args); + return (i >= size) ? (size - 1) : i; +} + + +/** + * snprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters which would be + * generated for the given input, excluding the trailing null, + * as per ISO C99. If the return is greater than or equal to + * @size, the resulting string is truncated. + */ +int snprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf,size,fmt,args); + va_end(args); + return i; +} + + +/** + * scnprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @size: The size of the buffer, including the trailing null space + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The return value is the number of characters written into @buf not including + * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is + * greater than or equal to @size, the resulting string is truncated. + */ + +int scnprintf(char * buf, size_t size, const char *fmt, ...) +{ + va_list args; + unsigned int i; + + va_start(args, fmt); + i = vsnprintf(buf, size, fmt, args); + va_end(args); + return (i >= size) ? (size - 1) : i; +} + +/** + * vsprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @args: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use vsnprintf or vscnprintf in order to avoid + * buffer overflows. + * + * Call this function if you are already dealing with a va_list. + * You probably want sprintf instead. + */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ + return vsnprintf(buf, INT_MAX, fmt, args); +} + + +/** + * sprintf - Format a string and place it in a buffer + * @buf: The buffer to place the result into + * @fmt: The format string to use + * @...: Arguments for the format string + * + * The function returns the number of characters written + * into @buf. Use snprintf or scnprintf in order to avoid + * buffer overflows. + */ +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsnprintf(buf, INT_MAX, fmt, args); + va_end(args); + return i; +} + + +/** + * vsscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: format of buffer + * @args: arguments + */ +int vsscanf(const char * buf, const char * fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + int qualifier; + int base; + int field_width; + int is_sign = 0; + + while(*fmt && *str) { + /* skip any white space in format */ + /* white space in format matchs any amount of + * white space, including none, in the input. + */ + if (isspace(*fmt)) { + while (isspace(*fmt)) + ++fmt; + while (isspace(*str)) + ++str; + } + + /* anything that is not a conversion must match exactly */ + if (*fmt != '%' && *fmt) { + if (*fmt++ != *str++) + break; + continue; + } + + if (!*fmt) + break; + ++fmt; + + /* skip this conversion. + * advance both strings to next white space + */ + if (*fmt == '*') { + while (!isspace(*fmt) && *fmt) + fmt++; + while (!isspace(*str) && *str) + str++; + continue; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + + /* get conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z') { + qualifier = *fmt++; + if (qualifier == *fmt) { + if (qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if (qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + base = 10; + is_sign = 0; + + if (!*fmt || !*str) + break; + + switch(*fmt++) { + case 'c': + { + char *s = (char *) va_arg(args,char*); + if (field_width == -1) + field_width = 1; + do { + *s++ = *str++; + } while (--field_width > 0 && *str); + num++; + } + continue; + case 's': + { + char *s = (char *) va_arg(args, char *); + if(field_width == -1) + field_width = INT_MAX; + /* first, skip leading white space in buffer */ + while (isspace(*str)) + str++; + + /* now copy until next white space */ + while (*str && !isspace(*str) && field_width--) { + *s++ = *str++; + } + *s = '\0'; + num++; + } + continue; + case 'n': + /* return number of characters read so far */ + { + int *i = (int *)va_arg(args,int*); + *i = str - buf; + } + continue; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + case 'i': + base = 0; + case 'd': + is_sign = 1; + case 'u': + break; + case '%': + /* looking for '%' in str */ + if (*str++ != '%') + return num; + continue; + default: + /* invalid format; stop here */ + return num; + } + + /* have some sort of integer conversion. + * first, skip white space in buffer. + */ + while (isspace(*str)) + str++; + + digit = *str; + if (is_sign && digit == '-') + digit = *(str + 1); + + if (!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) + break; + + switch(qualifier) { + case 'H': /* that's 'hh' in format */ + if (is_sign) { + signed char *s = (signed char *) va_arg(args,signed char *); + *s = (signed char) strtol(str,&next,base); + } else { + unsigned char *s = (unsigned char *) va_arg(args, unsigned char *); + *s = (unsigned char) strtoul(str, &next, base); + } + break; + case 'h': + if (is_sign) { + short *s = (short *) va_arg(args,short *); + *s = (short) strtol(str,&next,base); + } else { + unsigned short *s = (unsigned short *) va_arg(args, unsigned short *); + *s = (unsigned short) strtoul(str, &next, base); + } + break; + case 'l': + if (is_sign) { + long *l = (long *) va_arg(args,long *); + *l = strtol(str,&next,base); + } else { + unsigned long *l = (unsigned long*) va_arg(args,unsigned long*); + *l = strtoul(str,&next,base); + } + break; + case 'L': + if (is_sign) { + long long *l = (long long*) va_arg(args,long long *); + *l = strtoll(str,&next,base); + } else { + unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*); + *l = strtoull(str,&next,base); + } + break; + case 'Z': + case 'z': + { + size_t *s = (size_t*) va_arg(args,size_t*); + *s = (size_t) strtoul(str,&next,base); + } + break; + default: + if (is_sign) { + int *i = (int *) va_arg(args, int*); + *i = (int) strtol(str,&next,base); + } else { + unsigned int *i = (unsigned int*) va_arg(args, unsigned int*); + *i = (unsigned int) strtoul(str,&next,base); + } + break; + } + num++; + + if (!next) + break; + str = next; + } + return num; +} + + +/** + * sscanf - Unformat a buffer into a list of arguments + * @buf: input buffer + * @fmt: formatting of buffer + * @...: resulting arguments + */ +int sscanf(const char * buf, const char * fmt, ...) +{ + va_list args; + int i; + + va_start(args,fmt); + i = vsscanf(buf,fmt,args); + va_end(args); + return i; +} + +/* generic puts() implementation independent of who provides putchar() */ +int puts(const char *s) +{ +#ifdef ARCH_HAS_CONSOLE + return _puts(s); +#else + while (1) { + char c = *s++; + if (c == 0) + return; + putchar(c); + } + return 0; +#endif +} diff --git a/src/target/firmware/rf/trf6151.c b/src/target/firmware/rf/trf6151.c new file mode 100644 index 00000000..4d0541d7 --- /dev/null +++ b/src/target/firmware/rf/trf6151.c @@ -0,0 +1,502 @@ +/* Driver for RF Transceiver Circuit (TRF6151) */ + +/* (C) 2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <stdint.h> +#include <stdio.h> + +#include <debug.h> +#include <memory.h> +#include <keypad.h> +#include <osmocore/gsm_utils.h> + +#include <calypso/tpu.h> +#include <calypso/tsp.h> +#include <layer1/agc.h> +#include <rffe.h> + +#include <rf/trf6151.h> + +enum trf6151_reg { + REG_RX = 0, /* RF general settings */ + REG_PLL = 1, /* PLL settings */ + REG_PWR = 2, /* Power on/off funcitonal blocks */ + REG_CFG = 3, /* Transceiver and PA controller settings */ + REG_TEST1 = 4, + REG_TEST2 = 5, + REG_TEST3 = 6, + REG_TEST4 = 7, + _MAX_REG +}; + +/* REG_RX */ +#define RX_READ_EN (1 << 7) +#define RX_CAL_MODE (1 << 8) +#define RX_RF_GAIN_HIGH (3 << 9) +#define RX_VGA_GAIN_SHIFT 11 + +/* REG_PWR */ +#define PWR_BANDGAP_SHIFT 3 +#define PWR_BANDGAP_OFF (0 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON_SPEEDUP (2 << PWR_BANDGAP_SHIFT) +#define PWR_BANDGAP_ON (3 << PWR_BANDGAP_SHIFT) +#define PWR_REGUL_ON (1 << 5) +#define PWR_SYNTHE_OFF (0) +#define PWR_SYNTHE_RX_ON (1 << 9) +#define PWR_SYNTHE_TX_ON (1 << 10) +#define PWR_RX_MODE (1 << 11) +#define PWR_TX_MODE (1 << 13) +#define PWR_PACTRL_APC (1 << 14) +#define PWR_PACTRL_APCEN (1 << 15) + +/* REG_CFG */ +#define CFG_TX_LOOP_MANU (1 << 3) +#define CFG_PACTLR_IDIOD_30uA (0 << 4) +#define CFG_PACTLR_IDIOD_300uA (1 << 4) +#define CFG_PACTLR_RES_OPEN (0 << 10) +#define CFG_PACTLR_RES_150k (1 << 10) +#define CFG_PACTLR_RES_300k (2 << 10) +#define CFG_PACTLR_CAP_0pF (0 << 12) +#define CFG_PACTLR_CAP_12p5F (1 << 12) +#define CFG_PACTLR_CAP_25pF (3 << 12) +#define CFG_PACTLR_CAP_50pF (2 << 12) +#define CFG_TEMP_SENSOR (1 << 14) +#define CFG_ILOGIC_INIT_DIS (1 << 15) + +/* FIXME: This must be defined in the RFFE configuration */ +#define TRF6151_TSP_UID 2 +#define TRF6151_PACTRL_CFG (CFG_PACTLR_RES_OPEN|CFG_PACTLR_CAP_0pF|CFG_PACTLR_IDIOD_30uA) + +#define PLL_VAL(a, b) ((a << 3) | (((b)-64) << 9)) + +/* All values in qbits unless otherwise speciifed */ +#define TRF6151_LDO_DELAY_TS 6 /* six TDMA frames (at least 25ms) */ +#define TRF6151_RX_PLL_DELAY 184 /* 170 us */ +#define TRF6151_TX_PLL_DELAY 260 /* 240 us */ + +uint16_t rf_arfcn = 871; /* TODO: this needs to be private */ +static uint16_t rf_band; + +static uint8_t trf6151_vga_dbm = 40; +static int trf6151_gain_high = 1; + +static uint16_t trf6151_reg_cache[_MAX_REG] = { + [REG_RX] = 0x9E00, + [REG_PLL] = 0x0000, + [REG_PWR] = 0x0000, + [REG_CFG] = 0x2980, +}; + +/* Write to a TRF6151 register (4 TPU instructions) */ +static void trf6151_reg_write(uint16_t reg, uint16_t val) +{ + printd("trf6151_reg_write(reg=%u, val=0x%04x)\n", reg, val); + /* each TSP write takes 4 TPU instructions */ + tsp_write(TRF6151_TSP_UID, 16, (reg | val)); + trf6151_reg_cache[reg] = val; +} + +int trf6151_set_gain(uint8_t dbm, int high) +{ + uint16_t reg = trf6151_reg_cache[REG_RX] & 0x07ff; + printf("trf6151_set_gain(%u, %d)\n", dbm, high); + + if (dbm < 14 || dbm > 40) + return -1; + + /* clear the gain bits first */ + reg &= ~((0x1F) << RX_VGA_GAIN_SHIFT); + /* OR-in the new gain value */ + reg |= (6 + (dbm-14)/2) << RX_VGA_GAIN_SHIFT; + + if (high) + reg |= RX_RF_GAIN_HIGH; + else + reg &= ~RX_RF_GAIN_HIGH; + + trf6151_reg_write(REG_RX, reg); + + return 0; +} + +#define SCALE_100KHZ 100 + +/* Compute TRF6151 PLL valuese for all 4 RX bands */ +static uint16_t trf6151_pll_rx(uint32_t freq_khz) +{ + uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */ + uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */ + uint32_t l; + uint32_t a, b; /* The PLL multipliers we want to compute */ + + /* L = 4 for low band, 2 for high band */ + if (freq_khz < 1000000) + l = 4; + else + l = 2; + + /* To compute B, we assume A is zero */ + b = (freq_100khz * 65 * l) / (64 * 26 * 10); + + if ((l == 4 && (b < 135 || b > 150)) || + (l == 2 && (b < 141 || b > 155))) + printf("Frequency %u kHz is out of spec\n", freq_khz); + + /* Compute PLL frequency assuming A == 0 */ + fb_100khz = (b * 64 * 26 * 10) / (65 * l); + + /* Compute how many 100kHz units A needs to add */ + a = freq_100khz - fb_100khz; + + if (l == 2) + a = a / 2; + + /* since all frequencies are expanded a factor of 10, we don't need to multiply A */ + printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b); + + /* return value in trf6151 register layout form */ + return PLL_VAL(a, b); +} + +/* Compute TRF6151 PLL TX values for GSM900 and GSM1800 only! */ +static uint16_t trf6151_pll_tx(uint32_t freq_khz) +{ + uint32_t freq_100khz = freq_khz / SCALE_100KHZ; /* Scale from *1000 (k) to *100000 (0.1M) */ + uint32_t fb_100khz; /* frequency of B alone, without A (units of 100kHz) */ + uint32_t l, r, m; + uint32_t a, b; /* The PLL multipliers we want to compute */ + + /* L = 4 for low band, 2 for high band */ + if (freq_khz < 1000000) { + r = 35; + l = 4; + m = 52; + } else { + r = 70; + l = 2; + m = 26; + } + + /* To compute B, we assume A is zero */ + b = (freq_100khz * r * l * m) / (64 * 26 * 10 * (m + l)); + + if ((l == 4 && (b < 68 || b > 71)) || + (l == 2 && (b < 133 || b > 149))) + printf("Frequency %u kHz is out of spec\n", freq_khz); + + /* Compute PLL frequency assuming A == 0 */ + fb_100khz = (b * 64 * 26 * 10 * (m + l)) / (r * l * m); + + /* Compute how many 100kHz units A needs to add */ + a = freq_100khz - fb_100khz; + + a = a / 2; + + /* since all frequencies are expanded a factor of 10, we don't need to multiply A */ + printd("Freq %u kHz => A = %u, B = %u\n", freq_khz, a, b); + + /* return value in trf6151 register layout form */ + return PLL_VAL(a, b); +} + +enum trf6151_pwr_unit { + TRF1651_PACTLR_APC, + TRF6151_PACTRL_APCEN, + TRF6151_TRANSMITTER, + TRF6151_REGULATORS, +}; + +enum trf6151_gsm_band { + GSM900 = 1, + GSM1800 = 2, + GSM850_LOW = 4, + GSM850_HIGH = 5, + GSM1900 = 6, +}; + +static inline void trf6151_reset(void) +{ + /* pull the nRESET line low */ + tsp_act_disable((1 << 0)); + tpu_enq_wait(50); + /* release nRESET */ + tsp_act_enable((1 << 0)); +} + +void trf6151_init(void) +{ + /* Configure TSPEN0, which is connected to TWL3025, + * FIXME: why is this here and not in the TWL3025 driver? */ + tsp_setup(0, 1, 0, 0); + /* Configure TSPEN2, which is connected ot TRF6151 STROBE */ + tsp_setup(TRF6151_TSP_UID, 0, 1, 1); + + trf6151_reset(); + + /* configure TRF6151 for operation */ + trf6151_power(1); + trf6151_reg_write(REG_CFG, TRF6151_PACTRL_CFG | CFG_ILOGIC_INIT_DIS); + + /* FIXME: Uplink / Downlink Calibration */ +} + +void trf6151_power(int on) +{ + if (on) { + trf6151_reg_write(REG_PWR, PWR_REGUL_ON | PWR_BANDGAP_ON); + /* wait until regulators are stable (25ms == 27100 qbits) */ + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(5000); + tpu_enq_wait(2100); + } else + trf6151_reg_write(REG_PWR, PWR_BANDGAP_ON); +} + +/* Set the operational mode of the TRF6151 chip */ +void trf6151_set_mode(enum trf6151_mode mode) +{ + uint16_t pwr = (PWR_REGUL_ON | PWR_BANDGAP_ON | (rf_band<<6)); + + switch (mode) { + case TRF6151_IDLE: + /* should we switch of the RF gain for power saving? */ + break; + case TRF6151_RX: + pwr |= (PWR_SYNTHE_RX_ON | PWR_RX_MODE); + break; + case TRF6151_TX: +#if 0 + pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE); +#else // Dieter: we should turn power control on (for TPU: check timing and order !) + pwr |= (PWR_SYNTHE_TX_ON | PWR_TX_MODE | PWR_PACTRL_APC | PWR_PACTRL_APCEN); // Dieter: TODO +#endif + break; + } + trf6151_reg_write(REG_PWR, pwr); +} + +static void trf6151_band_select(enum trf6151_gsm_band band) +{ + uint16_t pwr = trf6151_reg_cache[REG_PWR]; + + pwr &= ~(3 << 6); + pwr |= (band << 6); + + trf6151_reg_write(REG_PWR, pwr); +} + +/* Set ARFCN. Takes 2 reg_write, i.e. 8 TPU instructions */ +void trf6151_set_arfcn(uint16_t arfcn, int uplink) +{ + uint32_t freq_khz; + + switch (gsm_arfcn2band(arfcn)) { + case GSM_BAND_850: + rf_band = GSM850_LOW; /* FIXME: what about HIGH */ + break; + case GSM_BAND_900: + rf_band = GSM900; + break; + case GSM_BAND_1800: + rf_band = GSM1800; + break; + case GSM_BAND_1900: + rf_band = GSM1900; + break; + case GSM_BAND_450: + case GSM_BAND_480: + case GSM_BAND_750: + case GSM_BAND_810: + printf("Unsupported rf_band.\n"); + break; + } + + trf6151_band_select(rf_band); + + freq_khz = gsm_arfcn2freq10(arfcn, uplink) * 100; + printd("ARFCN %u -> %u kHz\n", arfcn, freq_khz); + + if (uplink == 0) + trf6151_reg_write(REG_PLL, trf6151_pll_rx(freq_khz)); + else { + if (rf_band != GSM900 && rf_band != GSM1800) { + printf("TX only supports GSM900/1800\n"); + return; + } + trf6151_reg_write(REG_PLL, trf6151_pll_tx(freq_khz)); + } + + rf_arfcn = arfcn; // TODO: arfcn is referenced at other places +} + +void trf6151_calib_dc_offs(void) +{ + uint16_t rx = trf6151_reg_cache[REG_RX]; + + /* Set RX CAL Mode bit, it will re-set automatically */ + trf6151_reg_write(REG_RX, rx | RX_CAL_MODE); + /* DC offset calibration can take up to 50us, i.e. 54.16 * 923ns*/ + tpu_enq_wait(55); +} + +/* Frontend gain can be switched high or low (dB) */ +#define TRF6151_FE_GAIN_LOW 7 +#define TRF6151_FE_GAIN_HIGH 27 + +/* VGA at baseband can be adjusted in this range (dB) */ +#define TRF6151_VGA_GAIN_MIN 14 +#define TRF6151_VGA_GAIN_MAX 40 + +uint8_t trf6151_get_gain(void) +{ + uint16_t vga, reg_rx = trf6151_reg_cache[REG_RX]; + uint8_t gain = 0; + + switch ((reg_rx >> 9) & 3) { + case 0: + gain += TRF6151_FE_GAIN_LOW; + break; + case 3: + gain += TRF6151_FE_GAIN_HIGH; + break; + } + + vga = (reg_rx >> RX_VGA_GAIN_SHIFT) & 0x1f; + if (vga < 6) + vga = 6; + + gain += TRF6151_VGA_GAIN_MIN + (vga - 6) * 2; + + return gain; +} + +void trf6151_test(uint16_t arfcn) +{ + /* Select ARFCN 871 downlink */ + trf6151_set_arfcn(arfcn, 0); + + trf6151_set_mode(TRF6151_RX); + //trf6151_reg_write(REG_PWR, (PWR_SYNTHE_RX_ON | PWR_RX_MODE | PWR_REGUL_ON | (rf_band<<6) | PWR_BANDGAP_ON)); + /* Wait for PLL stabilization (170us max) */ + tpu_enq_wait(TRF6151_RX_PLL_DELAY); + + /* Use DC offset calibration after RX mode has been switched on + * (might not be needed) */ + trf6151_calib_dc_offs(); + + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +void trf6151_tx_test(uint16_t arfcn) +{ + /* Select ARFCN uplink */ + trf6151_set_arfcn(arfcn, 1); + + trf6151_set_mode(TRF6151_TX); + tpu_enq_wait(TRF6151_RX_PLL_DELAY); + + tpu_enq_sleep(); + tpu_enable(1); + tpu_wait_idle(); +} + +#define TRF6151_REGWR_QBITS 8 /* 4 GSM qbits + 4 TPU instructions */ +#define TRF6151_RX_TPU_INSTR 4 /* set_gain(1), set_arfcn(2), set_mode(1) */ + +/* delay caused by this driver programming the TPU for RX mode */ +#define TRF6151_RX_TPU_DELAY (TRF6151_RX_TPU_INSTR * TRF6151_REGWR_QBITS) + +/* prepare a Rx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_rx_window(int16_t start_qbits, uint16_t arfcn) +{ + int16_t start_pll_qbits; + + /* power up at the right time _before_ the 'start_qbits' point in time */ + start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_RX_PLL_DELAY + TRF6151_RX_TPU_DELAY)); + tpu_enq_at(start_pll_qbits); + + /* Set the AGC and PLL registers */ + trf6151_set_arfcn(arfcn, 0); + trf6151_set_gain(trf6151_vga_dbm, trf6151_gain_high); + trf6151_set_mode(TRF6151_RX); + + /* FIXME: power down at the right time again */ +} + +/* prepare a Tx window with the TRF6151 finished at time 'start' (in qbits) */ +void trf6151_tx_window(int16_t start_qbits, uint16_t arfcn) +{ +#ifdef CONFIG_TX_ENABLE + int16_t start_pll_qbits; + + /* power up at the right time _before_ the 'start_qbits' point in time */ + start_pll_qbits = add_mod5000(start_qbits, -(TRF6151_TX_PLL_DELAY + TRF6151_RX_TPU_DELAY)); + tpu_enq_at(start_pll_qbits); + + trf6151_set_arfcn(arfcn, 1); + trf6151_set_mode(TRF6151_TX); + + /* FIXME: power down at the right time again */ +#endif +} + +/* Given the expected input level of exp_inp dBm and the target of target_bb + * dBm, configure the RF Frontend with the respective gain */ +void trf6151_compute_gain(int16_t exp_inp, int16_t target_bb) +{ + /* TRF6151 VGA gain between 14 to 40 dB, plus 20db high/low */ + int16_t exp_bb_dbm8, delta_dbm8; + int16_t exp_inp_dbm8 = to_dbm8(exp_inp); + int16_t target_bb_dbm8 = to_dbm8(target_bb); + int16_t vga_gain = TRF6151_GAIN_MIN; + int high = 0; + + /* calculate the dBm8 that we expect at the baseband */ + exp_bb_dbm8 = exp_inp_dbm8 + to_dbm8(system_inherent_gain); + + /* calculate the error that we expect. */ + delta_dbm8 = target_bb_dbm8 - exp_bb_dbm8; + + /* If this is negative or less than TRF6151_GAIN_MIN, we are pretty + * much lost as we cannot reduce the system inherent gain. If it is + * positive, it corresponds to the gain that we need to configure */ + if (delta_dbm8 < to_dbm8(TRF6151_GAIN_MIN)) { + printd("AGC Input level overflow\n"); + high = 0; + vga_gain = TRF6151_GAIN_MIN; + } else if (delta_dbm8 > to_dbm8(TRF6151_GAIN_FE + TRF6151_GAIN_MIN)) { + high = 1; + delta_dbm8 -= to_dbm8(TRF6151_GAIN_FE); + } + vga_gain = delta_dbm8/8; + if (vga_gain > TRF6151_GAIN_MAX) + vga_gain = TRF6151_GAIN_MAX; + + /* update the static global variables which are used when programming + * the window */ + trf6151_vga_dbm = vga_gain; + trf6151_gain_high = high; +} diff --git a/src/target_dsp/.gitignore b/src/target_dsp/.gitignore new file mode 100644 index 00000000..5cf144ea --- /dev/null +++ b/src/target_dsp/.gitignore @@ -0,0 +1,4 @@ +*.o +*.a +*.coff +*.bin diff --git a/src/target_dsp/calypso/Makefile b/src/target_dsp/calypso/Makefile new file mode 100644 index 00000000..ff21e694 --- /dev/null +++ b/src/target_dsp/calypso/Makefile @@ -0,0 +1,7 @@ +dsp_dump.bin: bl_stage3.S dsp_dump.lds + c54x-coff-as bl_stage3.S -o bl_stage3.o + c54x-coff-ld --script dsp_dump.lds bl_stage3.o -o dsp_dump.coff + c54x-coff-objcopy -j .text -O binary dsp_dump.coff dsp_dump.bin + +clean: + rm -f *.o *.bin *.coff diff --git a/src/target_dsp/calypso/bin2cfile.py b/src/target_dsp/calypso/bin2cfile.py new file mode 100755 index 00000000..51401b8f --- /dev/null +++ b/src/target_dsp/calypso/bin2cfile.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +import struct +import sys + +def group_by_n(s, n, do_join=True): + return ( ''.join(x) for x in zip(*[s[i::n] for i in range(n)]) ) + + +def main(pn, filename): + # Get all bytes + f = open(filename, 'r') + d = f.read() + f.close() + + # Get the data + ops = ''.join([ + '0x%04x,%c' % ( + struct.unpack('=H', x)[0], + '\n' if (i&3==3) else ' ' + ) + for i, x + in enumerate(group_by_n(d, 2)) + ])[:-1] + + # Header / footer + print """ +#define _SA_DECL (const uint16_t *)&(const uint16_t []) + +static const struct dsp_section dsp_xxx[] = { + { + .addr = 0x, + .size = 0x%04x, + .data = _SA_DECL { +%s + }, + }, + { /* Guard */ + .addr = 0, + .size = 0, + .data = NULL, + }, +}; + +#undef _SA_DECL +""" % (len(d)/2, ops) + + +if __name__ == "__main__": + main(*sys.argv) diff --git a/src/target_dsp/calypso/bl_stage3.S b/src/target_dsp/calypso/bl_stage3.S new file mode 100644 index 00000000..402c3c59 --- /dev/null +++ b/src/target_dsp/calypso/bl_stage3.S @@ -0,0 +1,142 @@ + +BCSR .equ 0x29 + +CMD_IDLE .equ 1 ; Do nothing / DSP ready for commands +CMD_COPY_BLOCK .equ 2 ; (if size == 0, then exec) +CMD_COPY_MODE .equ 4 ; Select copy mode + ; (0=code write, 1=data write, + ; 2=code read, 3=data read, + ; 4=prom read, 5=drom read) +CMD_VERSION .equ 0xffff ; API_RAM[0] = bootloader version + +VERSION .equ 0x0100 ; 1.00 + + + .section .apiram + + .org 0x07fc +bl_addr_hi .ds 1 +bl_size .ds 1 +bl_addr_lo .ds 1 +bl_status .ds 1 + + + .text + .mmregs +_start: + orm #2, *(BCSR) ; ? + + ld #0x1f, DP + stm #0x1100, SP + stm #0, AR4 +_done: + stm #_api_ram, AR2 + st #CMD_IDLE, @bl_status +_loop: + ; Version + cmpm @bl_status, #CMD_VERSION + bc 1f, ntc + + bd _done + st #VERSION, *AR2 +1: + + ; Select copy mode + cmpm @bl_status, #CMD_COPY_MODE + bc 1f, ntc + + bd _done + mvdm @_api_ram, AR4 +1: + + ; Copy + cmpm @bl_status, #CMD_COPY_BLOCK + bc _loop, ntc + + ; Capture values for copy operations + ; A = full address + ; AR1 size-1 + ; AR2 api_ram (set previously) + ; AR3 data/code address + ; AR4 mode + + ldu @bl_addr_lo, A + stlm A, AR3 + add @bl_addr_hi, 16, A + + ldu @bl_size, B + stlm B, AR1 + ; mar *AR1- ; We do this in a delay slot later on ... + + ; Start + bc 1f, bneq ; B still contains size + bacc A + +1: + ; Select + stm #AR4, AR5 ; AR5 = &AR4 + bit *AR5, 13 ; Test mode(2) + bcd _read_rom, tc + mar *AR1- + bit *AR5, 15 ; Test mode(0) lsb + bcd _copy_data, tc + bit *AR5, 14 ; Test mode(1) + nop + + ; Copy to/from Program space +_copy_prog: + bc _read_prog, tc + + ; Copy from API -> prog space (mode 0) +_write_prog: + rpt *(AR1) + writa *AR2+ + b _done + + ; Copy from prog space -> API (mode 2) +_read_prog: + rpt *(AR1) + reada *AR2+ + b _done + + ; Copy to/from Data space +_copy_data: + bc _read_data, tc + + ; Copy from API -> data space (mode 1) +_write_data: + rpt *(AR1) + mvdd *AR2+, *AR3+ + b _done + + ; Copy from data space -> API (mode 3) +_read_data: + rpt *(AR1) + mvdd *AR3+, *AR2+ + b _done + + ; Read from {D,P}ROM bypassing protection +_read_rom: + ldm AR1, B ; Can't put those two ops in the delay slot of + stlm B, BRC ; 'bc' because of unprotected pipeline conflicts + bc _read_rom_data, tc + +_read_rom_prog: + rptb 1f - 1 + call prom_read_xplt +1: + b _done + +_read_rom_data: + rptb 1f - 1 + call drom_read_xplt +1: + b _done + + +drom_read_xplt .equ 0xe4b8 +prom_read_xplt .equ 0x7213 + + + .end + diff --git a/src/target_dsp/calypso/dsp_dump.lds b/src/target_dsp/calypso/dsp_dump.lds new file mode 100644 index 00000000..56633026 --- /dev/null +++ b/src/target_dsp/calypso/dsp_dump.lds @@ -0,0 +1,22 @@ +OUTPUT_FORMAT("coff1-c54x") +OUTPUT_ARCH("") +MEMORY +{ + apiram (RWXI) : ORIGIN = 0x0800, LENGTH = 0x2000 +} +SECTIONS +{ + . = 0x0800; + + .apiram : + { + PROVIDE(_api_ram = .); + *(.apiram) + } > apiram + + .text : + { + *(.text) + } > apiram +} + diff --git a/src/target_dsp/calypso/dump2coff.py b/src/target_dsp/calypso/dump2coff.py new file mode 100755 index 00000000..67a49e8e --- /dev/null +++ b/src/target_dsp/calypso/dump2coff.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python + +from collections import namedtuple +import re +import sys +import struct + +DATA = 0 +DATA = 1 + + +class Section(object): + + DATA = 0 + CODE = 1 + + STYP_NOLOAD = 0x0002 + STYP_TEXT = 0x0020 + STYP_DATA = 0x0040 + STYP_BSS = 0x0080 + + def __init__(self, name, type, start, size, data=None): + self.name = name + self.type = type + self.start = start + self.size = size + self.data = data + + @property + def flags(self): + if self.type == Section.DATA: + return Section.STYP_DATA if self.data else Section.STYP_BSS + else: + return Section.STYP_TEXT if self.data else Section.STYP_NOLOAD + + +class CalypsoCOFF(object): + + F_RELFLG = 0x0001 # Relocation information stripped from the file + F_EXEC = 0x0002 # File is executable (i.e., no unresolved external references) + F_LNNO = 0x0004 # Line numbers stripped from the file + F_LSYMS = 0x0010 # Local symbols stripped from the file + F_LITTLE = 0x0100 # Little endian + + def __init__(self, data_seg_base=0x80000000): + self.sections = {} + self.data_seg_base = data_seg_base + self.ver_magic = 0x00c1 + self.tgt_magic = 0x0098 + self.flags = \ + CalypsoCOFF.F_RELFLG | \ + CalypsoCOFF.F_EXEC | \ + CalypsoCOFF.F_LNNO | \ + CalypsoCOFF.F_LSYMS | \ + CalypsoCOFF.F_LITTLE + + def _data_pack(self, d): + return ''.join(struct.pack('<H', x) for x in d) + + def save(self, filename): + # Formats + HDR_FILE = '<HHlllHHH' + HDR_SECTIONS = '<8sLLllllHHHcc' + + # Optional header + oh = '' + + # File header + fh = struct.pack(HDR_FILE, + self.ver_magic, # unsigned short f_ver_magic; /* version magic number */ + len(self.sections), # unsigned short f_nscns; /* number of section */ + 0, # long f_timdat; /* time and date stamp */ + 0, # long f_symptr; /* file ptr to symbol table */ + 0, # long f_nsyms; /* number entries in the sym table */ + len(oh), # unsigned short f_opthdr; /* size of optional header */ + self.flags, # unsigned short f_flags; /* flags */ + self.tgt_magic, # unsigned short f_tgt_magic; /* target magic number */ + ) + + # File header size + #sections * sizeof(section header) + dptr = struct.calcsize(HDR_FILE) + len(oh) + len(self.sections) * struct.calcsize(HDR_SECTIONS) + + # Section headers + sh = [] + sd = [] + + sk = lambda x: self.data_seg_base + x.start if x.type==Section.DATA else x.start + + for s in sorted(self.sections.values(), key=sk): + # Values + if s.type == Section.DATA: + mp = 0x80 + sa = s.start + else: + mp = 0 + sa = s.start + sptr = dptr if s.data else 0 + + # Header + sh.append(struct.pack(HDR_SECTIONS, + s.name, # char[8] s_name; /* 8-character null padded section name */ + sa, # long int s_paddr; /* Physical address of section */ + sa, # long int s_vaddr; /* Virtual address of section */ + s.size, # long int s_size; /* Section size in bytes */ + sptr, # long int s_scnptr; /* File pointer to raw data */ + 0, # long int s_relptr; /* File pointer to relocation entries */ + 0, # long int s_lnnoptr;/* File pointer to line number entries */ + 0, # unsigned short s_nreloc; /* Number of relocation entrie */ + 0, # unsigned short s_nlnno; /* Number of line number entries */ + s.flags,# unsigned short s_flags; /* Flags (see ``Section header flags'') */ + '\x00', # / + chr(mp),# char s_mempage;/* Memory page number */ + )) + + # Data + if s.data: + sd.append(self._data_pack(s.data)) + dptr += s.size * 2 + + # Write the thing + f = open(filename, 'wb') + + f.write(fh) + f.write(oh) + f.write(''.join(sh)) + f.write(''.join(sd)) + + f.close() + + def add_section(self, name, type, addr, size, data=None): + self.sections[name] = Section(name, type, addr, size, data=data) + + +# ---------------------------------------------------------------------------- +# Dump loading +# ---------------------------------------------------------------------------- + +RE_DUMP_HDR = re.compile( + r"^DSP dump: (\w*) \[([0-9a-fA-F]{5})-([0-9a-fA-F]{5})\]$" +) + + +def _file_strip_gen(f): + while True: + l = f.readline() + if not l: + return + yield l.strip() + + +def dump_load_section(fg, sa, ea): + data = [] + ca = sa + for l in fg: + if not l: + break + + ra = int(l[0:5], 16) + if ra != ca: + raise ValueError('Invalid dump address %05x != %05x', ra, ca) + + v = l[8:].split() + if len(v) != 16: + raise ValueError('Invalid dump format') + + v = [int(x,16) for x in v] + data.extend(v) + + ca += 0x10 + + if ca != ea: + raise ValueError('Missing dump data %05x != %05x', ra, ea) + + return data + + +def dump_load(filename): + # Open file + f = open(filename, 'r') + fg = _file_strip_gen(f) + + # Scan line by line for a dump header line + sections = [] + + for l in fg: + m = RE_DUMP_HDR.match(l) + if not m: + continue + + name = m.group(1) + sa = int(m.group(2), 16) + ea = int(m.group(3), 16) + 1 + + sections.append(( + name, sa, ea, + dump_load_section(fg, sa, ea), + )) + + # Done + f.close() + + return sections + + +# ---------------------------------------------------------------------------- +# Main +# ---------------------------------------------------------------------------- + +def main(pname, dump_filename, out_filename): + + # Section to place in the COFF + sections = [ + # name type start size + ('.regs', Section.DATA, 0x00000, 0x0060), + ('.scratch', Section.DATA, 0x00060, 0x0020), + ('.drom', Section.DATA, 0x09000, 0x5000), + ('.pdrom', Section.CODE, 0x0e000, 0x2000), + ('.prom0', Section.CODE, 0x07000, 0x7000), + ('.prom1', Section.CODE, 0x18000, 0x8000), + ('.prom2', Section.CODE, 0x28000, 0x8000), + ('.prom3', Section.CODE, 0x38000, 0x2000), + ('.daram0', Section.DATA, 0x00080, 0x0780), + ('.api', Section.DATA, 0x00800, 0x2000), + ('.daram1', Section.DATA, 0x02800, 0x4800), + ] + + # COFF name -> dump name + dump_mapping = { + # '.regs' : 'Registers', + '.drom' : 'DROM', + '.pdrom' : 'PDROM', + '.prom0' : 'PROM0', + '.prom1' : 'PROM1', + '.prom2' : 'PROM2', + '.prom3' : 'PROM3', + } + + # Load the dump + dump_sections = dict([(s[0], s) for s in dump_load(dump_filename)]) + + # Create the COFF + coff = CalypsoCOFF() + + # Add each section (with data if we have some) + for name, type, start, size in sections: + # Dumped data ? + d_data = None + if (name in dump_mapping) and (dump_mapping[name] in dump_sections): + d_name, d_sa, d_ea, d_data = dump_sections[dump_mapping[name]] + + # Add sections + coff.add_section(name, type, start, size, d_data) + + # Save result + coff.save(out_filename) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(*sys.argv)) diff --git a/src/target_dsp/calypso/ida/README.txt b/src/target_dsp/calypso/ida/README.txt new file mode 100644 index 00000000..a7939083 --- /dev/null +++ b/src/target_dsp/calypso/ida/README.txt @@ -0,0 +1,73 @@ +Here's a few steps to get started quickly and get something readable: + + - Compile a patched for the IDA TMS320C54 module + + I made several enhancement to it to support the calypso better (the tms320c54 + module is part of the SDK and can be modded and recompiled) : + + - Add support for memory mappings so that the same memory zone can + 'appear' at several place in the address space (to handle data & code + overlay) + - Fix the section handling when loading a file: + . to set XPC properly, + . to not override section name + . to support more than 2 sections + - Fix a bug in cross reference detection when dealing with section + having selectors != 0 + - Add stub support for the type system. This allows loading of a .h + header file with the NDB structure definition + - Add definition for the IO ports so that they are symbolically + displayed + + I can't publically distribute the IDA processor module modification + because even just the patch contains some hex-rays code, so I'll handle + this on a case by case basis. (just ask me privately and we'll work it out) + + - Dump the DSP ROM + + Using the compal_dsp_dump.bin, you must create a text dump of the DSP ROM, + just piping the console output to a text file. + + - Generate COFF image + + The dump2coff.py script can convert the text dump into a usable COFF file + containing all the correct sections and addresses. + + - Load this COFF image into IDA + + In the load dialog make sure : + - Uncheck the 'Fill segment gaps (COFF)' checkbox + - Select 'TMS320C54' in 'Change processor' + - In 'Analysis Options/Processor specific analysis options' : + - 'Choose device name': CALYPSO + - 'Data segment address': 0x80000000 + - 'Add mapping' (do it several time) + - From 0x00000060 -> 0x80000060 size 0x6FA0 + - From 0x00010060 -> 0x80000060 size 0x6FA0 + - From 0x00020060 -> 0x80000060 size 0x6FA0 + - From 0x00030060 -> 0x80000060 size 0x6FA0 + - From 0x8000E000 -> 0x0000E000 size 0x2000 + + - Set 'stub' compiler options to allow the type system to load .h files + + In 'Options/Compiler': + - Compiler: 'GNU C++' + - Calling convention: 'Cdecl' + - Memory model: 'Code Near, Data Near' + - Pointer size: 'Near 16bit, Far 32bit' + - Include directory: '/usr/include' (or a directory with your includes + ... needs to exist) + + - Load the NDB types + + - Load the ndb.h file + - In the local types view, import all structure / enum into the database + - Then declare the following symbol and set them as struct type + appropriately. + + 0x80000800 api_w_page_0 db_mcu_to_dsp + 0x80000814 api_w_page_1 db_mcu_to_dsp + 0x80000828 api_r_page_0 db_dsp_to_mcu + 0x8000083c api_r_page_1 db_dsp_to_mcu + 0x800008d4 ndb ndb_mcu_dsp + diff --git a/src/target_dsp/calypso/ida/ndb.h b/src/target_dsp/calypso/ida/ndb.h new file mode 100644 index 00000000..ad9c1056 --- /dev/null +++ b/src/target_dsp/calypso/ida/ndb.h @@ -0,0 +1,294 @@ +typedef unsigned char API; +typedef signed char API_SIGNED; + +struct db_mcu_to_dsp +{ + API d_task_d; + API d_burst_d; + API d_task_u; + API d_burst_u; + API d_task_md; + API d_background; + API d_debug; + API d_task_ra; + API d_fn; + API d_ctrl_tch; + API hole; + API d_ctrl_abb; + API a_a5fn[2]; + API d_power_ctl; + API d_afc; + API d_ctrl_system; +}; + +struct db_dsp_to_mcu +{ + API d_task_d; + API d_burst_d; + API d_task_u; + API d_burst_u; + API d_task_md; + API d_background; + API d_debug; + API d_task_ra; + API a_serv_demod[4]; + API a_pm[3]; + API a_sch[5]; +}; + +struct param_mcu_dsp +{ + API_SIGNED d_transfer_rate; + API_SIGNED d_lat_mcu_bridge; + API_SIGNED d_lat_mcu_hom2sam; + API_SIGNED d_lat_mcu_bef_fast_access; + API_SIGNED d_lat_dsp_after_sam; + API_SIGNED d_gprs_install_address; + API_SIGNED d_misc_config; + API_SIGNED d_cn_sw_workaround; + API_SIGNED d_hole2_param[4]; + API_SIGNED d_fb_margin_beg; + API_SIGNED d_fb_margin_end; + API_SIGNED d_nsubb_idle; + API_SIGNED d_nsubb_dedic; + API_SIGNED d_fb_thr_det_iacq; + API_SIGNED d_fb_thr_det_track; + API_SIGNED d_dc_off_thres; + API_SIGNED d_dummy_thres; + API_SIGNED d_dem_pond_gewl; + API_SIGNED d_dem_pond_red; + API_SIGNED d_maccthresh1; + API_SIGNED d_mldt; + API_SIGNED d_maccthresh; + API_SIGNED d_gu; + API_SIGNED d_go; + API_SIGNED d_attmax; + API_SIGNED d_sm; + API_SIGNED d_b; + API_SIGNED d_v42b_switch_hyst; + API_SIGNED d_v42b_switch_min; + API_SIGNED d_v42b_switch_max; + API_SIGNED d_v42b_reset_delay; + API_SIGNED d_ldT_hr; + API_SIGNED d_maccthresh_hr; + API_SIGNED d_maccthresh1_hr; + API_SIGNED d_gu_hr; + API_SIGNED d_go_hr; + API_SIGNED d_b_hr; + API_SIGNED d_sm_hr; + API_SIGNED d_attmax_hr; + API_SIGNED c_mldt_efr; + API_SIGNED c_maccthresh_efr; + API_SIGNED c_maccthresh1_efr; + API_SIGNED c_gu_efr; + API_SIGNED c_go_efr; + API_SIGNED c_b_efr; + API_SIGNED c_sm_efr; + API_SIGNED c_attmax_efr; + API_SIGNED d_sd_min_thr_tchfs; + API_SIGNED d_ma_min_thr_tchfs; + API_SIGNED d_md_max_thr_tchfs; + API_SIGNED d_md1_max_thr_tchfs; + API_SIGNED d_sd_min_thr_tchhs; + API_SIGNED d_ma_min_thr_tchhs; + API_SIGNED d_sd_av_thr_tchhs; + API_SIGNED d_md_max_thr_tchhs; + API_SIGNED d_md1_max_thr_tchhs; + API_SIGNED d_sd_min_thr_tchefs; + API_SIGNED d_ma_min_thr_tchefs; + API_SIGNED d_md_max_thr_tchefs; + API_SIGNED d_md1_max_thr_tchefs; + API_SIGNED d_wed_fil_ini; + API_SIGNED d_wed_fil_tc; + API_SIGNED d_x_min; + API_SIGNED d_x_max; + API_SIGNED d_slope; + API_SIGNED d_y_min; + API_SIGNED d_y_max; + API_SIGNED d_wed_diff_threshold; + API_SIGNED d_mabfi_min_thr_tchhs; + API_SIGNED d_facch_thr; + API_SIGNED d_max_ovsp_ul; + API_SIGNED d_sync_thres; + API_SIGNED d_idle_thres; + API_SIGNED d_m1_thres; + API_SIGNED d_max_ovsp_dl; + API_SIGNED d_gsm_bgd_mgt; + API a_fir_holes[4]; + API a_fir31_uplink[31]; + API a_fir31_downlink[31]; +}; + +struct ndb_mcu_dsp +{ + API d_dsp_page; + API d_error_status; + API d_spcx_rif; + API d_tch_mode; + API d_debug1; + API d_dsp_test; + API d_version_number1; + API d_version_number2; + API d_debug_ptr; + API d_debug_bk; + API d_pll_config; + API p_debug_buffer; + API d_debug_buffer_size; + API d_debug_trace_type; + API d_dsp_state; + API d_hole1_ndb[2]; + API d_hole_debug_amr; + API d_hole2_ndb[1]; + API d_mcsi_select; + API d_apcdel1_bis; + API d_apcdel2_bis; + API d_apcdel2; + API d_vbctrl2; + API d_bulgcal; + API d_afcctladd; + API d_vbuctrl; + API d_vbdctrl; + API d_apcdel1; + API d_apcoff; + API d_bulioff; + API d_bulqoff; + API d_dai_onoff; + API d_auxdac; + API d_vbctrl1; + API d_bbctrl; + API d_fb_det; + API d_fb_mode; + API a_sync_demod[4]; + API a_sch26[5]; + API d_audio_gain_ul; + API d_audio_gain_dl; + API d_audio_compressor_ctrl; + API d_audio_init; + API d_audio_status; + API d_toneskb_init; + API d_toneskb_status; + API d_k_x1_t0; + API d_k_x1_t1; + API d_k_x1_t2; + API d_pe_rep; + API d_pe_off; + API d_se_off; + API d_bu_off; + API d_t0_on; + API d_t0_off; + API d_t1_on; + API d_t1_off; + API d_t2_on; + API d_t2_off; + API d_k_x1_kt0; + API d_k_x1_kt1; + API d_dur_kb; + API d_shiftdl; + API d_shiftul; + API d_aec_ctrl; + API d_es_level_api; + API d_mu_api; + API d_melo_osc_used; + API d_melo_osc_active; + API a_melo_note0[4]; + API a_melo_note1[4]; + API a_melo_note2[4]; + API a_melo_note3[4]; + API a_melo_note4[4]; + API a_melo_note5[4]; + API a_melo_note6[4]; + API a_melo_note7[4]; + API d_melody_selection; + API a_melo_holes[3]; + API d_sr_status; + API d_sr_param; + API d_sr_bit_exact_test; + API d_sr_nb_words; + API d_sr_db_level; + API d_sr_db_noise; + API d_sr_mod_size; + API a_n_best_words[4]; + API a_n_best_score[8]; + API a_dd_1[22]; + API a_du_1[22]; + API d_v42b_nego0; + API d_v42b_nego1; + API d_v42b_control; + API d_v42b_ratio_ind; + API d_mcu_control; + API d_mcu_control_sema; + API d_background_enable; + API d_background_abort; + API d_background_state; + API d_max_background; + API a_background_tasks[16]; + API a_back_task_io[16]; + API d_gea_mode_ovly; + API a_gea_kc_ovly[4]; + API d_hole3_ndb[7]; + API d_thr_usf_detect; + API d_a5mode; + API d_sched_mode_gprs_ovly; + API d_hole4_ndb[5]; + API a_ramp[16]; + API a_cd[15]; + API a_fd[15]; + API a_dd_0[22]; + API a_cu[15]; + API a_fu[15]; + API a_du_0[22]; + API d_rach; + API a_kc[4]; + API d_ra_conf; + API d_ra_act; + API d_ra_test; + API d_ra_statu; + API d_ra_statd; + API d_fax; + API a_data_buf_ul[21]; + API a_data_buf_dl[37]; + API a_tty_holes[8]; + API a_sr_holes0[414]; + API a_new_aec_holes[12]; + // API a_sr_holes1[145]; + struct param_mcu_dsp params; + API d_cport_init; + API d_cport_ctrl; + API a_cport_cfr[2]; + API d_cport_tcl_tadt; + API d_cport_tdat; + API d_cport_tvs; + API d_cport_status; + API d_cport_reg_value; + API a_cport_holes[1011]; + API a_model[1041]; + API a_eotd_holes[22]; + API a_amr_config[4]; + API a_ratscch_ul[6]; + API a_ratscch_dl[6]; + API d_amr_snr_est; + API a_voice_memo_amr_holes[1]; + API d_thr_onset_afs; + API d_thr_sid_first_afs; + API d_thr_ratscch_afs; + API d_thr_update_afs; + API d_thr_onset_ahs; + API d_thr_sid_ahs; + API d_thr_ratscch_marker; + API d_thr_sp_dgr; + API d_thr_soft_bits; + API d_holes[61]; +}; + +enum dsp_error { + DSP_ERR_RHEA = 0x0001, + DSP_ERR_IQ_SAMPLES = 0x0004, + DSP_ERR_DMA_PROG = 0x0008, + DSP_ERR_DMA_TASK = 0x0010, + DSP_ERR_DMA_PEND = 0x0020, + DSP_ERR_VM = 0x0080, + DSP_ERR_DMA_UL_TASK = 0x0100, + DSP_ERR_DMA_UL_PROG = 0x0200, + DSP_ERR_DMA_UL_PEND = 0x0400, + DSP_ERR_STACK_OV = 0x0800, +}; diff --git a/src/target_dsp/calypso/ida/tms320c54.cfg b/src/target_dsp/calypso/ida/tms320c54.cfg new file mode 100644 index 00000000..7962bee2 --- /dev/null +++ b/src/target_dsp/calypso/ida/tms320c54.cfg @@ -0,0 +1,136 @@ +; Append this to the tms320c54.cfg shipped with IDA
+
+.CALYPSO
+
+; entry _reset 0xff80 Reset vector
+
+; RIF
+RIF_DXR 0x0000
+RIF_DRR 0x0001
+RIF_SPCX 0x0002
+RIF_SPCR 0x0003
+
+; CYPHER
+CYPHER_CNTL 0x2800
+CYPHER_CNTL.START 0
+CYPHER_CNTL.RESETSW 1
+CYPHER_CNTL.MODE0 2
+CYPHER_CNTL.MODE1 3
+CYPHER_CNTL.CLK_EN 4
+CYPHER_CNTL.CYPHER_ONLY 5
+
+CYPHER_STATUS_IRQ 0x2801
+CYPHER_STATUS_IRQ.LT_FIN 0
+
+CYPHER_STATUS_WORK 0x2802
+CYPHER_STATUS_WORK.WORKING 0
+
+CYPHER_KC_1 0x2803
+CYPHER_KC_2 0x2804
+CYPHER_KC_3 0x2805
+CYPHER_KC_4 0x2806
+CYPHER_COUNT_1 0x2807
+CYPHER_COUNT_2 0x2808
+CYPHER_DECI_1 0x2809
+CYPHER_DECI_2 0x280A
+CYPHER_DECI_3 0x280B
+CYPHER_DECI_4 0x280C
+CYPHER_DECI_5 0x280D
+CYPHER_DECI_6 0x280E
+CYPHER_DECI_7 0x280F
+CYPHER_DECI_8 0x2810
+CYPHER_ENCI_1 0x2811
+CYPHER_ENCI_2 0x2812
+CYPHER_ENCI_3 0x2813
+CYPHER_ENCI_4 0x2814
+CYPHER_ENCI_5 0x2815
+CYPHER_ENCI_6 0x2816
+CYPHER_ENCI_7 0x2817
+CYPHER_ENCI_8 0x2818
+
+; MCSI
+MCSI_CONTROL 0x0800
+MCSI_MAIN-PARAMETERS 0x0801
+MCSI_INTERRUPTS 0x0802
+MCSI_CHANNEL-USED 0x0803
+MCSI_OVER-CLK 0x0804
+MCSI_CLK-FREQ 0x0805
+MCSI_STATUS 0x0806
+MCSI_TX0 0x0820
+MCSI_TX1 0x0821
+MCSI_TX2 0x0822
+MCSI_TX3 0x0823
+MCSI_TX4 0x0824
+MCSI_TX5 0x0825
+MCSI_TX6 0x0826
+MCSI_TX7 0x0827
+MCSI_TX8 0x0828
+MCSI_TX9 0x0829
+MCSI_TX10 0x082A
+MCSI_TX11 0x082B
+MCSI_TX12 0x082C
+MCSI_TX13 0x082D
+MCSI_TX14 0x082E
+MCSI_TX15 0x082F
+MCSI_RX0 0x0830
+MCSI_RX1 0x0831
+MCSI_RX2 0x0832
+MCSI_RX3 0x0833
+MCSI_RX4 0x0834
+MCSI_RX5 0x0835
+MCSI_RX6 0x0836
+MCSI_RX7 0x0837
+MCSI_RX8 0x0838
+MCSI_RX9 0x0839
+MCSI_RX10 0x083A
+MCSI_RX11 0x083B
+MCSI_RX12 0x083C
+MCSI_RX13 0x083D
+MCSI_RX14 0x083E
+MCSI_RX15 0x083F
+
+; RHEA
+RHEA_TRANSFER_RATE 0xF800
+
+RHEA_BRIDGE-CTRL 0xF801
+RHEA_BRIDGE-CTRL.TIMEOUT_ENABLE 8
+RHEA_BRIDGE-CTRL.NSUPV 9
+
+; API
+API_CONF 0xF900
+API_CONF.RESERVED0 0
+API_CONF.API_HOM 1
+API_CONF.BRIDGE_CLK_EN 2
+
+; Interrupts
+INT_CNTRL 0xFA00
+INT_CLEAR 0xFA01
+
+; DMA
+DMA_CONTROLLER_CONF 0xFC00
+DMA_ALLOC_CONFIG 0xFC02
+DMA1_RAD 0xFC10
+DMA1_RDPTH 0xFC12
+DMA1_AAD 0xFC14
+DMA1_ALGTH 0xFC16
+DMA1_CTRL 0xFC18
+DMA1_CUR_OFFSET_API 0xFC1A
+DMA2_RAD 0xFC20
+DMA2_RDPTH 0xFC22
+DMA2_AAD 0xFC24
+DMA2_ALGTH 0xFC26
+DMA2_CTRL 0xFC28
+DMA2_CUR_OFFSET_API 0xFC2A
+DMA3_RAD 0xFC30
+DMA3_RDPTH 0xFC32
+DMA3_AAD 0xFC34
+DMA3_ALGTH 0xFC36
+DMA3_CTRL 0xFC38
+DMA3_CUR_OFFSET_API 0xFC3A
+DMA4_RAD 0xFC40
+DMA4_RDPTH 0xFC42
+DMA4_AAD 0xFC44
+DMA4_ALGTH 0xFC46
+DMA4_CTRL 0xFC48
+DMA4_CUR_OFFSET_API 0xFC4A
+
diff --git a/src/wireshark/gsmtap.patch b/src/wireshark/gsmtap.patch new file mode 100644 index 00000000..019c8b49 --- /dev/null +++ b/src/wireshark/gsmtap.patch @@ -0,0 +1,445 @@ +Index: epan/dissectors/packet-gsmtap.c +=================================================================== +--- /dev/null ++++ epan/dissectors/packet-gsmtap.c +@@ -0,0 +1,345 @@ ++/* packet-gsmtap.c ++ * Routines for GSMTAP captures ++ * ++ * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> ++ * ++ * $Id$ ++ * ++ * Wireshark - Network traffic analyzer ++ * By Gerald Combs <gerald@wireshark.org> ++ * Copyright 1998 Gerald Combs ++ * ++ * 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., 59 Temple Place - Suite 330, Boston, MA ++ * 02111-1307, USA. ++ * ++ */ ++ ++/* GSMTAP is a generic header format for GSM protocol captures, ++ * it uses the IANA-assigned UDP port number 4729 and carries ++ * payload in various formats of GSM interfaces such as Um MAC ++ * blocks or Um bursts. ++ * ++ * Example programs generating GSMTAP data are airprobe ++ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) ++ */ ++ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include <glib.h> ++#include <epan/packet.h> ++#include <epan/prefs.h> ++ ++#include "packet-gsmtap.h" ++ ++static int proto_gsmtap = -1; ++ ++static int hf_gsmtap_version = -1; ++static int hf_gsmtap_hdrlen = -1; ++static int hf_gsmtap_type = -1; ++static int hf_gsmtap_timeslot = -1; ++static int hf_gsmtap_subslot = -1; ++static int hf_gsmtap_arfcn = -1; ++static int hf_gsmtap_uplink = -1; ++static int hf_gsmtap_noise_dbm = -1; ++static int hf_gsmtap_signal_dbm = -1; ++static int hf_gsmtap_frame_nr = -1; ++static int hf_gsmtap_burst_type = -1; ++static int hf_gsmtap_channel_type = -1; ++static int hf_gsmtap_antenna = -1; ++ ++static int hf_sacch_l1h_power_lev = -1; ++static int hf_sacch_l1h_fpc = -1; ++static int hf_sacch_l1h_ta = -1; ++ ++static gint ett_gsmtap = -1; ++ ++enum { ++ GSMTAP_SUB_DATA = 0, ++ GSMTAP_SUB_UM, ++ GSMTAP_SUB_UM_LAPDM, ++ GSMTAP_SUB_ABIS, ++ ++ GSMTAP_SUB_MAX ++}; ++ ++static dissector_handle_t sub_handles[GSMTAP_SUB_MAX]; ++ ++static const value_string gsmtap_bursts[] = { ++ { GSMTAP_BURST_UNKNOWN, "UNKNOWN" }, ++ { GSMTAP_BURST_FCCH, "FCCH" }, ++ { GSMTAP_BURST_PARTIAL_SCH, "PARTIAL SCH" }, ++ { GSMTAP_BURST_SCH, "SCH" }, ++ { GSMTAP_BURST_CTS_SCH, "CTS SCH" }, ++ { GSMTAP_BURST_COMPACT_SCH, "COMPACT SCH" }, ++ { GSMTAP_BURST_NORMAL, "NORMAL" }, ++ { GSMTAP_BURST_DUMMY, "DUMMY" }, ++ { GSMTAP_BURST_ACCESS, "RACH" }, ++ { 0, NULL }, ++}; ++ ++static const value_string gsmtap_channels[] = { ++ { GSMTAP_CHANNEL_UNKNOWN, "UNKNOWN" }, ++ { GSMTAP_CHANNEL_BCCH, "BCCH" }, ++ { GSMTAP_CHANNEL_CCCH, "CCCH" }, ++ { GSMTAP_CHANNEL_RACH, "RACH" }, ++ { GSMTAP_CHANNEL_AGCH, "AGCH" }, ++ { GSMTAP_CHANNEL_PCH, "PCH" }, ++ { GSMTAP_CHANNEL_SDCCH, "SDCCH" }, ++ { GSMTAP_CHANNEL_SDCCH4, "SDCCH/4" }, ++ { GSMTAP_CHANNEL_SDCCH8, "SDCCH/8" }, ++ { GSMTAP_CHANNEL_TCH_F, "FACCH/F" }, ++ { GSMTAP_CHANNEL_TCH_H, "FACCH/H" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_SDCCH, "LSACCH" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_SDCCH4, "SACCH/4" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_SDCCH8, "SACCH/8" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_TCH_F, "SACCH/F" }, ++ { GSMTAP_CHANNEL_ACCH| ++ GSMTAP_CHANNEL_TCH_F, "SACCH/H" }, ++ { 0, NULL }, ++}; ++ ++static const value_string gsmtap_types[] = { ++ { GSMTAP_TYPE_UM, "GSM Um (MS<->BTS)" }, ++ { GSMTAP_TYPE_ABIS, "GSM Abis (BTS<->BSC)" }, ++ { GSMTAP_TYPE_UM_BURST, "GSM Um burst (MS<->BTS)" }, ++ { 0, NULL }, ++}; ++ ++/* dissect a SACCH L1 header which is included in the first 2 bytes ++ * of every SACCH frame (according to TS 04.04) */ ++static void ++dissect_sacch_l1h(tvbuff_t *tvb, proto_tree *tree) ++{ ++ proto_item *ti; ++ proto_tree *l1h_tree = NULL; ++ ++ if (!tree) ++ return; ++ ++ ti = proto_tree_add_protocol_format(tree, proto_gsmtap, tvb, 0, 2, ++ "SACCH L1 Header, Power Level: %u, Timing Advance: %u", ++ tvb_get_guint8(tvb, 0) & 0x1f, ++ tvb_get_guint8(tvb, 1)); ++ l1h_tree = proto_item_add_subtree(ti, ett_gsmtap); ++ /* Power Level */ ++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_power_lev, tvb, 0, 1, FALSE); ++ /* Fast Power Control */ ++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_fpc, tvb, 0, 1, FALSE); ++ /* Acutal Timing Advance */ ++ proto_tree_add_item(l1h_tree, hf_sacch_l1h_ta, tvb, 1, 1, FALSE); ++} ++ ++/* dissect a GSMTAP header and hand payload off to respective dissector */ ++static void ++dissect_gsmtap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) ++{ ++ int sub_handle, len, offset = 0; ++ proto_item *ti; ++ proto_tree *gsmtap_tree = NULL; ++ tvbuff_t *payload_tvb, *l1h_tvb = NULL; ++ guint8 hdr_len, type, sub_type; ++ guint16 arfcn; ++ ++ len = tvb_length(tvb); ++ ++ hdr_len = tvb_get_guint8(tvb, offset + 1) <<2; ++ type = tvb_get_guint8(tvb, offset + 2); ++ sub_type = tvb_get_guint8(tvb, offset + 12); ++ arfcn = tvb_get_ntohs(tvb, offset + 4); ++ ++ /* In case of a SACCH, there is a two-byte L1 header in front ++ * of the packet (see TS 04.04) */ ++ if (type == GSMTAP_TYPE_UM && ++ sub_type & GSMTAP_CHANNEL_ACCH) { ++ l1h_tvb = tvb_new_subset(tvb, hdr_len, 2, 2); ++ payload_tvb = tvb_new_subset(tvb, hdr_len+2, len-(hdr_len+2), ++ len-(hdr_len+2)); ++ } else { ++ payload_tvb = tvb_new_subset(tvb, hdr_len, len-hdr_len, ++ len-hdr_len); ++ } ++ ++ /* We don't want any UDP related info left in the INFO field, as the ++ * gsm_a_dtap dissector will not clear but only append */ ++ col_clear(pinfo->cinfo, COL_INFO); ++ ++ col_set_str(pinfo->cinfo, COL_PROTOCOL, "GSMTAP"); ++ ++ if (arfcn & GSMTAP_ARFCN_F_UPLINK) { ++ col_append_str(pinfo->cinfo, COL_RES_NET_SRC, "MS"); ++ col_append_str(pinfo->cinfo, COL_RES_NET_DST, "BTS"); ++ /* p2p_dir is used by the LAPDm dissector */ ++ pinfo->p2p_dir = P2P_DIR_SENT; ++ } else { ++ col_set_str(pinfo->cinfo, COL_RES_NET_SRC, "BTS"); ++ switch (sub_type & ~GSMTAP_CHANNEL_ACCH) { ++ case GSMTAP_CHANNEL_BCCH: ++ case GSMTAP_CHANNEL_CCCH: ++ case GSMTAP_CHANNEL_PCH: ++ case GSMTAP_CHANNEL_AGCH: ++ col_set_str(pinfo->cinfo, COL_RES_NET_DST, "Broadcast"); ++ break; ++ default: ++ col_set_str(pinfo->cinfo, COL_RES_NET_DST, "MS"); ++ break; ++ } ++ /* p2p_dir is used by the LAPDm dissector */ ++ pinfo->p2p_dir = P2P_DIR_RECV; ++ } ++ ++ if (tree) { ++ ti = proto_tree_add_protocol_format(tree, proto_gsmtap, tvb, 0, hdr_len, ++ "GSM TAP Header, ARFCN: %u (%s), TS: %u, Channel: %s (%u)", ++ arfcn & GSMTAP_ARFCN_MASK, ++ arfcn & GSMTAP_ARFCN_F_UPLINK ? "Uplink" : "Downlink", ++ tvb_get_guint8(tvb, offset+3), ++ match_strval(tvb_get_guint8(tvb, offset+12), gsmtap_channels), ++ tvb_get_guint8(tvb, offset+14)); ++ gsmtap_tree = proto_item_add_subtree(ti, ett_gsmtap); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_version, ++ tvb, offset, 1, FALSE); ++ proto_tree_add_uint_format(gsmtap_tree, hf_gsmtap_hdrlen, ++ tvb, offset+1, 1, hdr_len, ++ "Header length: %u bytes", hdr_len); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_type, ++ tvb, offset+2, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_timeslot, ++ tvb, offset+3, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_arfcn, ++ tvb, offset+4, 2, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_uplink, ++ tvb, offset+4, 2, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_noise_dbm, ++ tvb, offset+6, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_signal_dbm, ++ tvb, offset+7, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_frame_nr, ++ tvb, offset+8, 4, FALSE); ++ if (type == GSMTAP_TYPE_UM_BURST) ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_burst_type, ++ tvb, offset+12, 1, FALSE); ++ else if (type == GSMTAP_TYPE_UM) ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_channel_type, ++ tvb, offset+12, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_antenna, ++ tvb, offset+13, 1, FALSE); ++ proto_tree_add_item(gsmtap_tree, hf_gsmtap_subslot, ++ tvb, offset+14, 1, FALSE); ++ } ++ ++ switch (type) { ++ case GSMTAP_TYPE_UM: ++ if (l1h_tvb) ++ dissect_sacch_l1h(l1h_tvb, tree); ++ switch (sub_type & ~GSMTAP_CHANNEL_ACCH) { ++ case GSMTAP_CHANNEL_BCCH: ++ case GSMTAP_CHANNEL_CCCH: ++ case GSMTAP_CHANNEL_PCH: ++ case GSMTAP_CHANNEL_AGCH: ++ /* FIXME: we might want to skip idle frames */ ++ sub_handle = GSMTAP_SUB_UM; ++ break; ++ case GSMTAP_CHANNEL_SDCCH: ++ case GSMTAP_CHANNEL_SDCCH4: ++ case GSMTAP_CHANNEL_SDCCH8: ++ case GSMTAP_CHANNEL_TCH_F: ++ case GSMTAP_CHANNEL_TCH_H: ++ sub_handle = GSMTAP_SUB_UM_LAPDM; ++ break; ++ case GSMTAP_CHANNEL_RACH: ++ default: ++ sub_handle = GSMTAP_SUB_DATA; ++ break; ++ } ++ break; ++ case GSMTAP_TYPE_UM_BURST: ++ default: ++ sub_handle = GSMTAP_SUB_DATA; ++ break; ++ } ++ call_dissector(sub_handles[sub_handle], payload_tvb, pinfo, tree); ++} ++ ++static const true_false_string sacch_l1h_fpc_mode_vals = { ++ "In use", ++ "Not in use" ++}; ++ ++void ++proto_register_gsmtap(void) ++{ ++ static hf_register_info hf[] = { ++ { &hf_gsmtap_version, { "Version", "gsmtap.version", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_hdrlen, { "Header Length", "gsmtap.hdr_len", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_type, { "Payload Type", "gsmtap.type", ++ FT_UINT8, BASE_DEC, VALS(gsmtap_types), 0, NULL, HFILL } }, ++ { &hf_gsmtap_timeslot, { "Time Slot", "gsmtap.ts", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_arfcn, { "ARFCN", "gsmtap.arfcn", ++ FT_UINT16, BASE_DEC, NULL, GSMTAP_ARFCN_MASK, NULL, HFILL } }, ++ { &hf_gsmtap_uplink, { "Uplink", "gsmtap.uplink", ++ FT_UINT16, BASE_DEC, NULL, GSMTAP_ARFCN_F_UPLINK, NULL, HFILL } }, ++ { &hf_gsmtap_noise_dbm, { "Signal/Noise Ratio (dB)", "gsmtap.snr_db", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_signal_dbm, { "Signal Level (dBm)", "gsmtap.signal_dbm", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_frame_nr, { "GSM Frame Number", "gsmtap.frame_nr", ++ FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_burst_type, { "Burst Type", "gsmtap.burst_type", ++ FT_UINT8, BASE_DEC, VALS(gsmtap_bursts), 0, NULL, HFILL }}, ++ { &hf_gsmtap_channel_type, { "Channel Type", "gsmtap.chan_type", ++ FT_UINT8, BASE_DEC, VALS(gsmtap_channels), 0, NULL, HFILL }}, ++ { &hf_gsmtap_antenna, { "Antenna Number", "gsmtap.antenna", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ { &hf_gsmtap_subslot, { "Sub-Slot", "gsmtap.sub_slot", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ ++ { &hf_sacch_l1h_power_lev, { "MS power level", "gsmtap.sacch_l1.power_lev", ++ FT_UINT8, BASE_DEC, NULL, 0x1f, NULL, HFILL } }, ++ { &hf_sacch_l1h_fpc, { "FPC", "gsmtap.sacch_l1.fpc", ++ FT_BOOLEAN, 8, TFS(&sacch_l1h_fpc_mode_vals), 0x04, ++ NULL, HFILL } }, ++ { &hf_sacch_l1h_ta, { "Actual Timing Advance", "gsmtap.sacch_l1.ta", ++ FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } }, ++ }; ++ static gint *ett[] = { ++ &ett_gsmtap ++ }; ++ ++ proto_gsmtap = proto_register_protocol("GSM Radiotap", "GSMTAP", "gsmtap"); ++ proto_register_field_array(proto_gsmtap, hf, array_length(hf)); ++ proto_register_subtree_array(ett, array_length(ett)); ++} ++ ++void ++proto_reg_handoff_gsmtap(void) ++{ ++ dissector_handle_t gsmtap_handle; ++ ++ sub_handles[GSMTAP_SUB_DATA] = find_dissector("data"); ++ sub_handles[GSMTAP_SUB_UM] = find_dissector("gsm_a_ccch"); ++ sub_handles[GSMTAP_SUB_UM_LAPDM] = find_dissector("lapdm"); ++ sub_handles[GSMTAP_SUB_ABIS] = find_dissector("gsm_a_dtap"); ++ gsmtap_handle = create_dissector_handle(dissect_gsmtap, proto_gsmtap); ++ dissector_add("udp.port", GSMTAP_UDP_PORT, gsmtap_handle); ++} +Index: epan/dissectors/packet-gsmtap.h +=================================================================== +--- /dev/null ++++ epan/dissectors/packet-gsmtap.h +@@ -0,0 +1,70 @@ ++#ifndef _GSMTAP_H ++#define _GSMTAP_H ++ ++/* gsmtap header, pseudo-header in front of the actua/ GSM payload */ ++ ++/* GSMTAP is a generic header format for GSM protocol captures, ++ * it uses the IANA-assigned UDP port number 4729 and carries ++ * payload in various formats of GSM interfaces such as Um MAC ++ * blocks or Um bursts. ++ * ++ * Example programs generating GSMTAP data are airprobe ++ * (http://airprobe.org/) or OsmocomBB (http://bb.osmocom.org/) ++ */ ++ ++#define GSMTAP_TYPE_UM 0x01 ++#define GSMTAP_TYPE_ABIS 0x02 ++#define GSMTAP_TYPE_UM_BURST 0x03 /* raw burst bits */ ++ ++#define GSMTAP_BURST_UNKNOWN 0x00 ++#define GSMTAP_BURST_FCCH 0x01 ++#define GSMTAP_BURST_PARTIAL_SCH 0x02 ++#define GSMTAP_BURST_SCH 0x03 ++#define GSMTAP_BURST_CTS_SCH 0x04 ++#define GSMTAP_BURST_COMPACT_SCH 0x05 ++#define GSMTAP_BURST_NORMAL 0x06 ++#define GSMTAP_BURST_DUMMY 0x07 ++#define GSMTAP_BURST_ACCESS 0x08 ++#define GSMTAP_BURST_NONE 0x09 ++ ++#define GSMTAP_CHANNEL_UNKNOWN 0x00 ++#define GSMTAP_CHANNEL_BCCH 0x01 ++#define GSMTAP_CHANNEL_CCCH 0x02 ++#define GSMTAP_CHANNEL_RACH 0x03 ++#define GSMTAP_CHANNEL_AGCH 0x04 ++#define GSMTAP_CHANNEL_PCH 0x05 ++#define GSMTAP_CHANNEL_SDCCH 0x06 ++#define GSMTAP_CHANNEL_SDCCH4 0x07 ++#define GSMTAP_CHANNEL_SDCCH8 0x08 ++#define GSMTAP_CHANNEL_TCH_F 0x09 ++#define GSMTAP_CHANNEL_TCH_H 0x0a ++#define GSMTAP_CHANNEL_ACCH 0x80 ++ ++#define GSMTAP_ARFCN_F_PCS 0x8000 ++#define GSMTAP_ARFCN_F_UPLINK 0x4000 ++#define GSMTAP_ARFCN_MASK 0x3fff ++ ++#define GSMTAP_UDP_PORT 4729 ++ ++/* This is the header as it is used by gsmtap-generating software. ++ * It is not used by the wireshark dissector and provided for reference only. ++struct gsmtap_hdr { ++ guint8 version; // version, set to 0x01 currently ++ guint8 hdr_len; // length in number of 32bit words ++ guint8 type; // see GSMTAP_TYPE_* ++ guint8 timeslot; // timeslot (0..7 on Um) ++ ++ guint16 arfcn; // ARFCN (frequency) ++ gint8 signal_dbm; // signal level in dBm ++ gint8 snr_db; // signal/noise ratio in dB ++ ++ guint32 frame_number; // GSM Frame Number (FN) ++ ++ guint8 sub_type; // Type of burst/channel, see above ++ guint8 antenna_nr; // Antenna Number ++ guint8 sub_slot; // sub-slot within timeslot ++ guint8 res; // reserved for future use (RFU) ++} ++ */ ++ ++#endif /* _GSMTAP_H */ +Index: epan/dissectors/Makefile.common +=================================================================== +--- epan/dissectors/Makefile.common.orig ++++ epan/dissectors/Makefile.common +@@ -484,6 +484,7 @@ + packet-gsm_sms.c \ + packet-gsm_sms_ud.c \ + packet-gsm_um.c \ ++ packet-gsmtap.c \ + packet-gssapi.c \ + packet-gtp.c \ + packet-gtpv2.c \ +@@ -1072,6 +1073,7 @@ + packet-gsm_a_common.h \ + packet-gsm_map.h \ + packet-gsm_sms.h \ ++ packet-gsmtap.h \ + packet-gssapi.h \ + packet-gtp.h \ + packet-h223.h \ |