Found in "gnokii-working" directory, some November-patches version marked_working
authorshort <>
Wed, 3 Apr 2002 00:08:34 +0000 (00:08 +0000)
committershort <>
Wed, 3 Apr 2002 00:08:34 +0000 (00:08 +0000)
146 files changed:
.exrc [new file with mode: 0644]
COPYING
Docs/CREDITS
Docs/FAQ
Docs/Makefile
Docs/README-AT [new file with mode: 0644]
Docs/README-CIMD [new file with mode: 0644]
Docs/gettext-howto
Docs/man/gnokii.1
Docs/man/todologo.1
Docs/man/xgnokii.1x
Docs/protocol/nk6110.txt
Docs/protocol/nk7110.txt
Docs/sample/cimd-connect [new file with mode: 0755]
Docs/sample/gnokiirc
INSTALL
Makefile
Makefile.global.in
VERSION
common/Makefile
common/at-hw.c [new file with mode: 0644]
common/cfgreader.c
common/cimd.c [new file with mode: 0644]
common/data/at-emulator.c
common/data/datapump.c
common/data/rlp-common.c
common/data/rlp-crc24.c
common/data/virtmodem.c
common/devices/tekram.c
common/devices/unixirda.c
common/devices/unixserial.c
common/fbus-6110.c
common/gsm-api.c
common/gsm-bitmaps.c
common/gsm-common.c
common/gsm-encoding.c
common/gsm-error.c
common/gsm-networks.c
common/gsm-ringtones.c
common/gsm-sms.c
common/gsm-statemachine.c
common/links/Makefile
common/links/atbus.c
common/links/atbus.c-REMOVE [new file with mode: 0644]
common/links/fbus-phonet.c
common/links/fbus.c
common/mbus-2110.c
common/mbus-6160.c
common/mbus-640.c
common/misc.c
common/phones/Makefile
common/phones/atgen.c
common/phones/atgen.c-REMOVE [new file with mode: 0644]
common/phones/dc2711.c
common/phones/dc2711.c-REMOVE [new file with mode: 0644]
common/phones/nk2110.c
common/phones/nk3110.c
common/phones/nk6100.c
common/phones/nk7110.c
common/phones/nokia.c
configure.in
do [new file with mode: 0755]
getopt/Makefile
gnokii/Makefile
gnokii/gnokii.c
gnokiid/Makefile
gnokiid/gnokiid.c
include/cfgreader.h
include/config.h.in.in
include/data/at-emulator.h
include/data/datapump.h
include/data/rlp-common.h
include/data/rlp-crc24.h
include/data/virtmodem.h
include/devices/tekram.h
include/devices/unixirda.h
include/devices/unixserial.h
include/fbus-3810.h [new file with mode: 0644]
include/fbus-6110.h
include/gnokii.h
include/gsm-api.h
include/gsm-bitmaps.h
include/gsm-common.h
include/gsm-networks.h
include/gsm-ringtones.h
include/gsm-sms.h
include/links/atbus.h
include/links/atbus.h-REMOVE [new file with mode: 0644]
include/links/fbus.h
include/mbus-6160.h
include/mbus-640.h
include/misc.h
include/phones/nk6100.h
include/phones/nk7110.h
include/phones/nokia.h
packaging/Debian/README.debian
packaging/Debian/control
packaging/Debian/copyright
packaging/Debian/docs
packaging/Debian/menu
packaging/Debian/postinst
packaging/Debian/preinst
packaging/Debian/rules
packaging/RedHat/gnokii.spec.in
packaging/Slackware/SlackBuild.in
po/Makefile.in.in
po/POTFILES.in
po/et.po
smsd/ChangeLog
smsd/Makefile
smsd/db.c
smsd/lowlevel.c
smsd/lowlevel.h
smsd/sms-sendsms [new file with mode: 0755]
smsd/sms.tables.sql
smsd/smsd.c
smsd/smsd.h
utils/Makefile
utils/mgnokiidev.c
xgnokii/Makefile
xgnokii/VERSION
xgnokii/xgnokii.c
xgnokii/xgnokii.h
xgnokii/xgnokii_calendar.c
xgnokii/xgnokii_calendar.h
xgnokii/xgnokii_cfg.c
xgnokii/xgnokii_cfg.h
xgnokii/xgnokii_common.c
xgnokii/xgnokii_common.h
xgnokii/xgnokii_contacts.c
xgnokii/xgnokii_contacts.h
xgnokii/xgnokii_data.c
xgnokii/xgnokii_data.h
xgnokii/xgnokii_dtmf.c
xgnokii/xgnokii_dtmf.h
xgnokii/xgnokii_logos.c
xgnokii/xgnokii_lowlevel.c
xgnokii/xgnokii_lowlevel.h
xgnokii/xgnokii_netmon.c
xgnokii/xgnokii_netmon.h
xgnokii/xgnokii_sms.c
xgnokii/xgnokii_sms.h
xgnokii/xgnokii_speed.c
xgnokii/xgnokii_speed.h
xgnokii/xgnokii_xkeyb.c
xgnokii/xgnokii_xkeyb.h

diff --git a/.exrc b/.exrc
new file mode 100644 (file)
index 0000000..f3c07bf
--- /dev/null
+++ b/.exrc
@@ -0,0 +1,2 @@
+set tabstop=8
+set shiftwidth=8
diff --git a/COPYING b/COPYING
index 60549be..99a8ab6 100644 (file)
--- a/COPYING
+++ b/COPYING
-                   GNU GENERAL PUBLIC LICENSE
-                      Version 2, June 1991
+GNOKII - A Linux/Unix toolset and driver for the Nokia 3x10/8110/51x0/61x0
+         Copyright (C) 1999 Hugh Blemings & Pavel Janík ml.
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+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.
 
-                           Preamble
+This program is distributed in the hope that 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.
 
-  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 Library General Public License instead.)  You can apply it to
-your programs, too.
+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.
 
-  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.
+You can contact authors by mail at
 
-  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.
+hugh@blemings.org                     Pavel.Janik@suse.cz
+Hugh Blemings                         Mgr. Pavel Janík, SuSE CR, s.r.o.
+PO Box 234                            Pod Pekárnami 338/12
+Belconnen ACT 2616                    190 00 Praha 9
+Australia                             Czech Republic
 
-  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 would be delighted to receive a postcard from you if you use gnokii but
+this is not a requirement of these licence terms! :)
 
-  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.
-\f
-                   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.)
-\f
-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.
-\f
-  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.
-\f
-  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
-\f
-           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) 19yy  <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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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) 19yy 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 Library General
-Public License instead of this License.
+Any trademarks in this or other gnokii documents/files are the property of
+their respective owners.
index 7193775..820c4af 100644 (file)
@@ -56,7 +56,7 @@ Thank you to, in no particular order...
 *  Goran Dokic, Samuli Sorvakko and anonymous for gettext files for
    Dutch, Finnish and German translations.
    
-*  Marcel Holtmann for RLP FCS code and misc stuff.
+*  Marcel Holtmann for RLP FCS code, the irda code and other misc stuff.
 
 *  Chris Kemp for logo stuff and a big part of the RLP code.
   
@@ -73,38 +73,54 @@ Thank you to, in no particular order...
    
 *  Alessandro Zummo for reset and getopts code.
 
+*  Manfred Jonsson for his manufacturer and windows patch, and AT command
+   support.
+
 *  Mark Looi for Cell Broadcast code - based on protocol decoded by
-   Colin Paton
+   Colin Paton.
 
-*  Sebastian Zagrodzki and Jacek Fiok for Polish .po file
+*  Sebastian Zagrodzki and Jacek Fiok for Polish .po file.
 
-*  Lucy for making pizza and putting up with Hugh disappearing to
-   work on this.  Rachael for new parent induced insomnia...
+*  Marcin Wi±cek for all his work and tons of patches.
 
-*  Last not least: Mirka for allowing Pavel to work on gnokii
-   and for _great_ gnokii operator logos...
+*  Gabriele Zappi for testing, general help and bug fixes.
 
-*  Hendrik Spohr for getting 7110 functions and DLR-3 to work (one small step
-   for Hendrik, big step for (my)gnokii :-))
-   
-*  Balazs Nagy for frames list and long hacker's work...
+*  Erik Rossen for help with documentation and debian packaging support.
+
+*  Marian Jancar for patches on 7110 series.
+
+*  Tamar Bondar for updates on SMS handling on 3810 series.
 
-*  Ladislav Michl & Manfred Jonsson for support for various AT phones...
+*  Marek Rogoziñski for help in tracing some nasty bugs.
 
-*  Manfred Jonsson for idea of programming, which looks for me good
+*  Pavel Machek for developing 2110 and Duncall support and many clever
+   hints.
 
-*  Michael Hund for many improvements and progress in 7110/6210 source...
+*  Bo¹tjan Müller for Slovenian translation and other things.
 
-*  Ralf Thelen for very required by many, many people patch and getting
-   infrared sockets to work -> small changes, few days of hard searching :-).
-   Also sms stuff is OK now
+*  Hans Motshärg for Estonian translation.
 
-*  Gabriele Zappi for many 6210 improvements and big calendar source
+*  Marcin P³awiñski for preview logos.
 
-*  Andrea Scopece for 61xx source for netmonitor and other improvements
+*  Martin Lucina for any kind of help he is providing ;-)
 
-*  ....and other people, who reported me bugs or want(ed) to use mygnokii
-   and help me in making it the best !
+*  Gabriele Stella for Italian translation.
+
+*  Andrea Scopece for bug reports and other help.
+*  Panagiotis Astithas, Daniel O'Connor, Sheldon Hearn for help with FreeBSD.
+
+*  Artur Kubiak for help with Solaris.
+
+*  Markus Plail for work on 7110 support.
+
+*  Simon Huggins for help with 6210 and IrDA.
+
+*  Lucy for making pizza and putting up with Hugh disappearing to
+   work on this.  Rachael for new parent induced insomnia...
+
+*  Last not least: Mirka for allowing Pavel to work on gnokii
+   and for _great_ gnokii operator logos...
 
 We tried to mentioned everyone who contributed to gnokii project but we
 might to forgot someone. If anyone does feel that we missed him and his
index f140e5d..f8d32c5 100644 (file)
--- a/Docs/FAQ
+++ b/Docs/FAQ
@@ -103,12 +103,3 @@ Q. Does gnokii work under Sun's Solaris ?
 A. Generally yes, but in fact it depends on hardware -- your serial port
 must support at least 115200 baud rate. Sun Enterprise 250 machine is known
 to work with gnokii.
-
--------------------------------------------------------------------------------
-Q. I get "Sorry, phone has not yet been converted to new style.
-   Phone.Functions == NULL!" message. What's wrong?
-
-A. Don't worry. Nothing's wrong with your phone. Gnokii internal structure is
-   changing to be more flexible. It is quite complex process so it may take
-   some time to get everything to work. You may help us to do it -- you'll get
-   more functionality sooner.
index 8dd1724..d5666ac 100644 (file)
@@ -1,63 +1,47 @@
 #
-# Makefile for the xGNOKII tool suite.
+# Makefile for the GNOKII tool suite's documentation directory
+#
+# Copyright (C) 1999 Hugh Blemings & Pavel Janík ml.
+#               2000 Karel Zak
 #
 
-TOPDIR=..
-include $(TOPDIR)/Makefile.global
-
-HELP1_DIR = en_US
-HELP11_DIR = pl_PL
-HELP2_DIR = examples
-HELP3_DIR = default
+TOPDIR=../
+include ${TOPDIR}/Makefile.global
 
-GNOKII1_MAN1 = "man/gnokii.1"
-GNOKII1_MAN2 = "man/todologo.1"
-GNOKII8_MAN1 = "man/gnokiid.8"
-GNOKII8_MAN2 = "man/mgnokiidev.8"
-XGNOKII_MAN = "man/xgnokii.1x"
+#
+# Documentation for gnokii (xgnokii install documentation files itself).
+#
 
-all: 
+INSTALL_DOCS =         README \
+               README-3810 \
+               README-6110 \
+               CREDITS \
+               DataCalls-QuickStart \
+               README-WIN32 \
+               gettext-howto \
+               gnokii.nol \
+               gnokii-ir-howto \
+               packaging-howto \
+               sample.gnokiirc
+
+GNOKII1_MAN = "man/gnokii.1 man/todologo.1"
+GNOKII8_MAN = "man/gnokiid.8 man/mgnokiidev.8"
+XGNOKII_MAN = man/xgnokii.1x
+
+all:
        @echo
 
-makelib:
-       @echo
-       
 install:
        $(INSTALL) -d $(docdir)
-
-       ( cd $(HELP1_DIR); \
-         $(FIND) . -type d \! -path "*CVS*" \
-              -exec $(INSTALL) -d $(docdir)/en_US/{} \; ; \
-         $(FIND) . -type f \! -path "*CVS*" \
-              -exec $(INSTALL) -m 0444 {} $(docdir)/en_US/{} \; \
-       )
-       ( cd $(HELP11_DIR); \
-         $(FIND) . -type d \! -path "*CVS*" \
-              -exec $(INSTALL) -d $(docdir)/pl_PL/{} \; ; \
-         $(FIND) . -type f \! -path "*CVS*" \
-              -exec $(INSTALL) -m 0444 {} $(docdir)/pl_PL/{} \; \
-       )
-
-       ( cd $(HELP2_DIR); \
-         $(FIND) . -type d \! -path "*CVS*" \
-              -exec $(INSTALL) -d $(docdir)/examples/{} \; ; \
-         $(FIND) . -type f \! -path "*CVS*" \
-              -exec $(INSTALL) -m 0444 {} $(docdir)/examples/{} \; \
-       )
-       ( cd $(HELP3_DIR); \
-         $(FIND) . -type d \! -path "*CVS*" \
-              -exec $(INSTALL) -d $(docdir)/default/{} \; ; \
-         $(FIND) . -type f \! -path "*CVS*" \
-              -exec $(INSTALL) -m 0444 {} $(docdir)/default/{} \; \
-       )
-       $(INSTALL) -m 0444 CREDITS $(docdir)
-       $(INSTALL) -m 0444 ../COPYING $(docdir)
-       $(INSTALL) $(GNOKII1_MAN1) $(man1dir)
-       $(INSTALL) $(GNOKII1_MAN2) $(man1dir)
-       $(INSTALL) $(GNOKII8_MAN1) $(man8dir)
-       $(INSTALL) $(GNOKII8_MAN2) $(man8dir)
-       if [ "x$(HAVE_XGNOKII)" = xyes ]; then \
-               ($(INSTALL) $(XGNOKII_MAN) $(xmandir)) \
+       @for xxx in $(INSTALL_DOCS); do \
+           if [ -e $$xxx ]; then \
+               $(INSTALL) -m 0444 $$xxx $(docdir)/$$xxx; \
+           fi; \
+       done
+       $(INSTALL) $(GNOKII1_MAN) $(man1dir)
+       $(INSTALL) $(GNOKII8_MAN) $(man8dir)
+       if [ "x$HAVE_XGNOKII" = xyes ]; then \
+               $(INSTALL) $(XGNOKII_MAN) $(xmandir) \
        fi
        @echo "done"
 
diff --git a/Docs/README-AT b/Docs/README-AT
new file mode 100644 (file)
index 0000000..4f906bd
--- /dev/null
@@ -0,0 +1,58 @@
+-- README-AT                                                    --
+-- Notes, bugs and a todo list for AT command compatible modems --
+
+* Notes *
+
+General PDU documentation can be found on:
+       http://www.siemens-mobile.de/pages/wm/downloads/m20_sms.pdf
+General AT commands documentation can be found on:
+       http://www.siemens-mobile.de/pages/wm/downloads/tc35_at_e.pdf
+
+Although this documentation is for Siemens devices, your modem will probably
+support some subset (or superset?) of it. Official standard document could be
+probably also found in www.etsi.org library.
+
+
+Successfuly tested:
+
+Siemens M20
+       (provided by the courtesy of Lubor Otta)
+       ATI1 --> SIEMENS  M20  Revision: 5.4, 13.12.00 13:15
+
+       gnokiirc argument "serial_write_usleep" MUST be >=1 otherwise
+       the device communication is very unreliable.
+
+Nokia Communicator 9000i
+       ATI2 --> SW6.36 : HW0320
+
+       Only +CMGF==1 mode capable so no logo/ring send/receive.
+       No +CNMI new SMS reporting so their detection is slow (+CMGL).
+       Unable to receive SMSes to SIM so no Gnokii SMS receive possible.
+       No +COPS/+CREG so no network code detection, set it manually.
+
+Nokia Communicator 9110
+       (provided by the courtesy of Karel Zatloukal)
+       ATI2 --> SW 4.05: HW 3600
+
+       Only +CMGF==1 mode capable so no logo/ring receive but it can send!
+       No PhoneBook commands supported so no contacts access available.
+       Unable to receive SMSes to SIM so no Gnokii SMS receive possible.
+       No +COPS/+CREG so no network code detection, set it manually.
+
+Nokia Communicator 9210
+       ATI2 --> SW03.54
+
+       No PhoneBook commands supported so no contacts access available.
+       Unable to receive SMSes to SIM so no Gnokii SMS receive possible.
+       No +COPS/+CREG so no network code detection, set it manually.
+
+Nokia CardPhone
+       (provided by the courtesy of Tim Boudreau)
+       ATI2 --> SW4.23
+
+       No known disadvantages.
+
+
+* Known Bugs *
+
+* To-do *
diff --git a/Docs/README-CIMD b/Docs/README-CIMD
new file mode 100644 (file)
index 0000000..65b640c
--- /dev/null
@@ -0,0 +1,20 @@
+-- README-CIMD                                                 --
+-- Notes, bugs and a todo list for SMS center protocol support --
+
+* Notes *
+
+Currently it has been tested only on "EOTEL BMG" BIP (=Business messaging
+gateway Interface Protocol) if using BMG (=Bussiness Messaging Gateway) <->
+<-> SMSC (SMS Center) protocol CIMD. This service is provided
+by Eurotel CZ (230 02) operator, service description (not technical and
+just in Czech language!):
+       http://www.eurotel.cz/eurotel/Site/sluzby/slsms_webbmg.htm
+
+BIP is probably predecessor to the very similiar to Nokia CIMD2 protocol,
+its description for Artus SMS center can be found on:
+       http://forum.nokia.com/download/cimdspec.pdf
+
+
+* Known Bugs *
+
+* To-do *
index 1c78ab1..c24e67d 100644 (file)
@@ -100,25 +100,3 @@ SnowWhite:/tmp/gnokii$
 The second line is in czech language :-) There are some problems - for
 example with "Mar" which is substitute by the C preprocessor.
 
-6. How to use locale in the gnokii code
-
-It's simple. Instead uf using:
-"some string"
-use:
-_("some string")
-
-In some cases _() cannot be used but the text still needs to be localized.
-In such cases use:
-N_("some string")
-For more detailes refer to gettext info pages.
-
-When sending us the patches and writing code for gnokii follow the rules:
- - do NOT translate DEBUG output (usually done by dprintf()),
- - translate all other output
- - do not output to stdout, unless you are in the application level code
- (gnokii/*.c, gnokiid/*.c, xgnokii/*.c, smsd/*.c)
-
--- 
-$Id$
-Pavel Janik
-Pawel Kot
index 214d696..1c9e4a2 100644 (file)
@@ -61,16 +61,16 @@ resets the phone.
 
 .SS CALENDAR
 .TP 
-.BR "\-\-getcalendarnote \fIindex\fP [\-v]"
-get the note with number \fIindex\fR from calendar.
+.BR "\-\-getcalendarnote \fIstart\fP [\fIsend\fP] [\-v]"
+get the note with numbers from \fIstart\fR to \fIend\fR from calendar.
 .PP 
 [\-v] \- output in vCalendar 1.0 format
 .TP 
 .BR "\-\-writecalendarnote"
 write the note to calendar.
 .TP 
-.BR "\-\-deletecalendarnote \fIindex\fP"
-delete the note with number [\fIindex\fR] from calendar.
+.BR "\-\-deletecalendarnote \fIstart\fP [\fIend\fP]"
+delete the note with numbers from \fIstart\fR to \fIend\fR from calendar.
 
 .SS SMS
 .TP 
@@ -193,4 +193,4 @@ See also Docs/CREDITS from Gnokii sources.
 This program is distributed under the GNU Public License.
 
 .SH "SEE ALSO"
-gnokiid, xgnokii, xlogos
+gnokiid, xgnokii
index c66955b..db9b969 100644 (file)
@@ -33,7 +33,7 @@ from \fIgnokii.nol\fR that is distributed with
 mobile phone. Use 
 .B gnokii
 or
-.B xlogos
+.B xgnokii
 to do the uploading.
 
 .SH EXAMPLES
@@ -52,6 +52,5 @@ Tomi Ollila <Tomi.Ollila@iki.fi>
 This program is distributed under the GNU Public License.
 
 .SH SEE ALSO
-.BR gnokii(1),
-xlogos
+.BR gnokii(1), xgnokii(1x)
 
index 5fad541..16f1037 100644 (file)
@@ -36,4 +36,4 @@ See also Docs/CREDITS from the Gnokii sources.
 This program is distributed under the GNU Public License.
 
 .SH "SEE ALSO"
-gnokii, gnokiid, xlogos
+gnokii, gnokiid
index baac106..a4f35ed 100644 (file)
@@ -298,9 +298,9 @@ Correct format is FBUS version 2/Direct IRDA/MBUS version 2
                               0x05: 5 second
                               0x0a: 10 second
                               0x0f: 15 second
-                              0x14: 20 second
-                              0x19: 25 second
-                              0x1e: 30 second
+                              0x1e: 20 second
+                              0x14: 25 second
+                              0x19: 30 second
                             where divtype:
                               0x02: all diverts for all call types ?
                                     Found only, when deactivate all diverts for all call types (with call type 0x00)
index c95d9b9..525bf61 100644 (file)
@@ -215,11 +215,11 @@ Correct format is FBUS version 2/Infrared/MBUS version 2
                               where location: 0x21 (always ?)
     r Set Picture Image     { 0x0051, location, number[2 bytes], 0x07 }
                               where location: 0x21 (always ?)
-
-    s List Picture Images   { 0x0096, FolderID, 0x0f, 0x07 }
-                              where: FolderID - see 0x14/0x017B
+    s List Picture Images   { 0x0096, location, 0x0f, 0x07 }
+                              where location:
+                                LM tries with 0x09, 0x11, 0x19, 0x21, 0x29, 0x31, 0x39, 0x41, 0x49
+                                Returned value with 0x21
     r List Picture Images   { 0x0097, number of pictures[2 bytes], number1[2 bytes], number2[2 bytes], ..., }
-
     s Get SMS from folder   { 0x0107, folderID, 0x00, location, 0x01, 0x65, 0x01}
                             where: folderID - see 0x14/0x017B
     r Get SMS from folder   { 0x0108, status, folderID, 0x00, location, type, sender number,...}
diff --git a/Docs/sample/cimd-connect b/Docs/sample/cimd-connect
new file mode 100755 (executable)
index 0000000..320c4e9
--- /dev/null
@@ -0,0 +1,6 @@
+#! /usr/sbin/chat -vEf
+# ^^^ all options MUST be given as ONE word only (all are passed as ARGV[1])
+TIMEOUT 3 "" "ATZ" "OK-ATZ-OK" "" ABORT "BUSY" ABORT "NO CARRIER" ABORT "NO DIAL TONE" ABORT "ERROR" ABORT "VOICE" ""
+ATL2 OK
+ATD$TELEPHONE
+TIMEOUT 90 CONNECT \c ^M \c
index 845aca8..bbecf77 100644 (file)
@@ -21,10 +21,7 @@ model = 6110
 # to understand what this changes if you're curious.
 initlength = default
 
-# The type of the connection, for IR set this to infrared or irda.
-# If you have 6210/6250/7110 phone and dau9p cable (the one you can
-# use with 6100 series and cannot use hardware modem from the phone)
-# you may want to use 'dau9p' value to get faster initialization.
+# The type of the connection, for IR set this to infrared
 connection = serial
 
 # Set bindir to point to the location of the various gnokii binaries.
@@ -32,3 +29,45 @@ connection = serial
 # permissions 4750, owned by root, group gnokii.  Ensure you
 # are in the gnokii group and that the group exists...
 bindir = /usr/local/sbin/
+
+# Baudrate to use on serial port connections.
+# Currently used only by models AT and BIP/CIMD. Defaults to 19200.
+serial_baudrate = 19200
+
+# Force waiting after each send character the specified usec time.
+# Value -1 forces the fastest 'block' writing,
+# value 0 writes each character separately without any explicite waiting,
+# other positive values specify the appropriate 1/1000000 sec delaying.
+# Siemens M20 requires at least "1"! FIXME: Autodetect
+#serial_write_usleep = 10000
+
+# Force serial port handshaking mode, useful primarily for "AT" model.
+# Gnokii "AT" model uses software handshake by default.
+# Possible values: hardware (RTS/CTS - 7 wires) or software (XON/XOFF - 3 wires)
+#handshake = software
+
+# If defined (not commented out by '#') it will quit Gnokii anytime
+# when DCD line will drop.
+require_dcd = 1
+
+# Run the specified script(s) right after opening and initializing the device
+# and before any communucation (right before closing for disconnect_script).
+# You may find handy to use it to connect your modem to SMS Center
+# when using BIP or CIMD protocols
+connect_script = echo x
+disconnect_script = echo y
+
+# Any entries in the following two sections will be set as environment
+# variables when running the scripts.
+# Handy for use for $VAR substitutions in your chat(8) script.
+[connect_script]
+TELEPHONE = 01234567
+[disconnect_script]
+
+
+[CIMD]
+
+# This section is used only if "model" is set to BIP or CIMD.
+# Set here your authorization pair given you by the operator.
+name = USERNAME
+password = PASSWORD
diff --git a/INSTALL b/INSTALL
index 7d5654f..3551ce2 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -16,7 +16,7 @@
                        Install path prefix. Default /usr/local/
 
                --without-x
-                       Compile without GTK (X11) programs
+                       Compile without GTK (Xwin) programs
                
                --disable-nls
                        Set if you don't have/want GNU gettext support
        Good default (example):
 
                ./configure --prefix=/usr \
-                           --enable-security
+                           --enable-gettext
 
 2/
        compilation:
        
-               make (or make gnokii-debug to create staticlly linked binary
-                       -- mainly for debugging)
+               make 
                groupadd gnokii  - it is really needed
                (make dep - if you want/need) 
-               make install or make install-suid or make install-strip or
-                       make install-ss (suid + strip)
+               make install
                make install-docs
 
        clean source:
index 1feaf9c..f43d66d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,27 +17,20 @@ include ${TOPDIR}/Makefile.global
 
 BIN_DIRS = gnokii
 
+BIN_DIRS += smsd
+
 ifndef WIN32
-BIN_DIRS += gnokiid
-BIN_DIRS += mgnetd
-BIN_DIRS += mgnetd/mg_demo_client
+#BIN_DIRS += gnokiid utils
 endif
 
-DIRS =  common \
-        Docs \
+DIRS =  common/phones \
+       common/links \
+       common/devices \
+        common \
        $(BIN_DIRS)
+#      common/data \
 
-#
-# For now gnokiid and utils only make sense on Unix like systems.
-# Some other stuff that makes only sense on Win32 platform.
-#
-
-ifndef WIN32
-DIRS +=        utils
-endif
-
-GTK_DIRS =  xgnokii \
-            xlogos
+GTK_DIRS =  xgnokii
 
 PO_DIR   =     po
 DOCS_DIR =     Docs
@@ -56,25 +49,6 @@ all: $(DIRS)
        fi
        @echo "done"
 
-makelib:
-       @for dir in $(DIRS); do \
-           if [ -e $$dir/Makefile ]; then \
-               $(MAKE) -C $$dir makelib; \
-           fi; \
-       done
-       @if [ "x$(USE_NLS)" = xyes ]; then \
-               $(MAKE) -C $(PO_DIR) makelib; \
-       fi
-
-       @if [ "$(GTK_LIBS)" ]; then \
-               for dir in $(GTK_DIRS); do \
-                   if [ -e $$dir/Makefile ]; then \
-                       $(MAKE) -C $$dir makelib; \
-                   fi; \
-               done \
-       fi
-       @echo "done"
-
 dummy:
 
 $(DIRS): dummy
@@ -115,9 +89,10 @@ distclean:  clean
                include/config.h \
                include/config.h.in \
                packaging/RedHat/gnokii.spec \
-               packaging/Slackware/SlackBuild \
                po/Makefile.in \
                debian
+       $(RM) `$(FIND) . -name "*~"`
+       @echo "done"
 
 dep:
        @for dir in $(DIRS); do \
@@ -135,7 +110,7 @@ dep:
        fi
        @echo "done"
 
-install:
+install: all
        @for dir in $(DIRS); do \
            if [ -e $$dir/Makefile ]; then \
                $(MAKE) -C $$dir install; \
index 23fa672..b06e08c 100644 (file)
@@ -23,11 +23,11 @@ exec_prefix    = @exec_prefix@
 bindir         = @bindir@
 sbindir        = @sbindir@
 xbindir        = ${prefix}/X11R6/bin/
-libdir         = @libdir@
+libdir         = @libdir@/${PACKAGE}
 xgnokii_libdir = @XGNOKIIDIR@/@XPACKAGE@
 man1dir        = @mandir@/man1/
 man8dir        = @mandir@/man8/
-xmandir        = @mandir@/man1/
+xmandir        = ${prefix}/X11R6/man/man1/
 docdir         = ${prefix}/doc/${PACKAGE}
 locdir         = @datadir@/locale
 
@@ -73,5 +73,5 @@ endif
 
 
 CFLAGS += -I$(GNOKII_INCLUDE)
-LDFLAGS = $(LIBS) -Wl,--rpath -Wl,$(libdir)
+LDFLAGS = $(LIBS)
 
diff --git a/VERSION b/VERSION
index 2a8e926..4a22caa 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.3.3_pre8-gold_2002_03_10
+0.4.0pre1_ats
index 2dad729..c63a8a7 100644 (file)
@@ -1,7 +1,12 @@
 
 #
+# $Id$
+#
 # Makefile for the GNOKII tool suite.
 #
+# Copyright (C) 1999 Hugh Blemings & Pavel Janík ml.
+#               2000 Karel Zak
+#
 
 #
 # For this common directory is used "subsystem.o" .o files concept.
 TOPDIR=..
 include $(TOPDIR)/Makefile.global
 
-CFLAGS += $(PTHREAD_CFLAGS)
-
-DIRS =         protocol \
-               oldmodules \
-               newmodules
-
-OBJS = devices/device.o \
-       data/rlp-common.o \
-       data/rlp-crc24.o \
-       files/midifile.o \
+OBJS = cfgreader.o \
+       device.o \
        gsm-ringtones.o \
-       gsm-coding.o \
-       gsm-datetime.o \
-       gsm-wap.o \
+       at-hw.o \
+       cimd.o \
        gsm-api.o \
-       gsm-phonebook.o \
-       gsm-calendar.o \
        gsm-networks.o \
-       gsm-bitmaps.o \
-       gsm-sms.o \
-       files/cfgreader.o \
+       vcal.o \
        misc.o \
-       protocol/fbus.o \
-       protocol/fbus3110.o \
-       protocol/fbusirda.o \
-       protocol/mbus.o \
-       protocol/at.o \
-       newmodules/sniff/sniff.o \
-       newmodules/newat.o \
-       newmodules/n6110.o \
-       newmodules/n3110.o \
-       newmodules/n7110.o
-
-DATA_OBJS = data/virtmodem.o \
-       data/at-emulator.o \
-       data/datapump.o
-
-ifdef WIN32
-       OBJS += $(TOPDIR)/win32/winserial.o
-else
-       OBJS += devices/unixserial.o \
-               devices/unixirda.o \
-               devices/tekram.o
-endif
+       gsm-bitmaps.o \
+       gsm-common.o \
+       gsm-encoding.o \
+       gsm-statemachine.o
+#        gsm-sms.o \
 
-ifdef XPM_CFLAGS
-    CFLAGS += $(XPM_CFLAGS)
-endif
+all: libgnokii.a gsm-filetypes.o
+
+phones/PHONES.o:
+       $(MAKE) -C phones PHONES.o
 
-all: COMMON.o DATA.o gsm-filetypes.o
+links/LINKS.o:
+       $(MAKE) -C links LINKS.o
 
-COMMON.o: $(OBJS)
-       $(LD) $(LDREL) $(LDOUT) COMMON.o $(OBJS)
+devices/DEVICES.o:
+       $(MAKE) -C devices DEVICES.o
 
-DATA.o: $(DATA_OBJS)
-       $(LD) $(LDREL) $(LDOUT) DATA.o $(DATA_OBJS)
+libgnokii.a: $(OBJS) phones/PHONES.o links/LINKS.o devices/DEVICES.o
+       ar rcs $@ $^
+#      $(CC) -shared -o libgnokii.so $(OBJS) phones/PHONES.o links/LINKS.o devices/DEVICES.o
 
-gsm-filetypes.o: files/gsm-filetypes.c 
-       $(CC) $(CFLAGS) -c files/gsm-filetypes.c
+gsm-filetypes.o: gsm-filetypes.c
+       $(CC) $(CFLAGS) $(CPPFLAGS) $(XPM_CFLAGS) -c gsm-filetypes.c
 
-makelib: $(OBJS) DATA.o gsm-filetypes.o
-       $(CC) -shared -o libmygnokii.so $(OBJS) DATA.o gsm-filetypes.o
+vcal.c: vcal.lx
+       $(LEX) -ovcal.c vcal.lx
 
 clean:
-       $(RM) $(OBJS) $(DATA_OBJS) *~ depend libmygnokii.so COMMON.o gsm-filetypes.o DATA.o
-       
+       $(RM) $(OBJS) *~ depend COMMON.o gsm-filetypes.o vcal.c phones/PHONES.o links/LINKS.o devices/DEVICES.o *.a
+
 install:
        $(INSTALL) -d $(libdir)
-       $(INSTALL) libmygnokii.so $(libdir)
+       $(INSTALL) libgnokii.so $(libdir)
        @echo
 
 depend dep:
@@ -92,3 +70,4 @@ endif
 
 
 .PHONY: all install clean dep depend
+
diff --git a/common/at-hw.c b/common/at-hw.c
new file mode 100644 (file)
index 0000000..74b9f91
--- /dev/null
@@ -0,0 +1,2659 @@
+/*
+
+  $Id$
+  
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Copyright (C) 2001 Jan Kratochvil,
+  based on code by Hugh Blemings & Pavel Janík ml.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+  This file provides an API for accessing functions on AT (Hayes) command
+  compatible hardware modems such as Siemens M20.
+  See README-AT for more details on supported mobile phones and GSM modems.
+
+  The various routines are called ATHW (whatever) as a concatenation of AT
+  and HardWare communication type.
+
+  $Log$
+  Revision 1.1.1.1  2002/04/03 00:08:02  short
+  Found in "gnokii-working" directory, some November-patches version
+
+
+*/
+
+#define ATHW_DEBUG 1
+/* #define DISABLE_CMGF0 1 */          /* Force AT+CMGF=1 on phones capable +CMGF=0 */
+
+/* System header files */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#ifdef WIN32
+
+#include <windows.h>
+#include "win32/winserial.h"
+
+#undef IN
+#undef OUT
+
+#define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
+#define sleep(x) Sleep((x) * 1000)
+#define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
+extern HANDLE hPhone;
+
+#else
+
+#define WRITEPHONE(a, b, c) device_write(b, c)
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <errno.h>
+#include "device.h"
+#include "devices/unixserial.h"
+
+#endif
+
+/* Various header file */
+
+#include "config.h"
+#include "misc.h"
+#include "gsm-common.h"
+
+/* Global variables used by code in gsm-api.c to expose the functions
+   supported by this model of phone. */
+
+
+#if __unices__
+/* fd opened in device.c */
+extern int device_portfd;
+#endif
+
+/* Our private defines */
+
+#define ATHW_CME_NOT_FOUND            (22)     /* +CME ERROR: 22 */
+#define ATHW_CMS_INVALID_MEMORY_INDEX (321)    /* +CMS ERROR: 321 */
+
+/* When now catchbuffer was provided and must have some space to decode
+ * OK/ERROR/... result codes.
+ */
+#define ATHW_CATCHBUFFER_LENGTH 0x400
+
+/* Default message reference, filled-in by GSM modem
+ */
+#define ATHW_PDU_MR_DEFAULT (0x00)
+
+/* Maximum PDU size in bytes
+ */
+#define GNOKII_MAX_PDU_LENGTH (64+GSM_MAX_DESTINATION_LENGTH/2+(GSM_MAX_SMS_LENGTH*7)/8)
+
+/* Local variables */
+
+
+#ifndef WIN32
+static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
+#endif
+static bool RequestTerminate;
+static GSM_MemoryType ATHW_CurrentMemoryType=GMT_XX;
+static int ATHW_CurrentCMGF=-1;
+static bool ATHW_CMGS_CMGF1_8bit_HaveBinHex;   /* AT+CMGS in +CMGF==1 and 8bit mode accepts hexstring */
+static bool ATHW_HaveCNMI=false;
+static bool ATHW_HaveSiemensMGR=false; /* "AT^SMGR" supported? */
+static bool ATHW_HaveSiemensMGL=false; /* "AT^SMGL" supported? */
+static int ATHW_CNMI_count=-1; /* value (-1) means it is not yet known */
+
+/* We don't know whether we should place SMSC in the front of +CMGS PDU
+ * so we will try both methods.
+ * When we will at least once successfuly send a message, we will *_force-it
+ * as the probability of wrong settings and successful message send is REALLY low. :-)
+ * (Maybe not so low when we will try to send prefix and no prefix is expected,
+ * this is also the reason why we will first try to NOT to send the prefix as we
+ * would have to successfuly hit some valid SMS center by the initial part of PDU.)
+ * Note: Applicable only if +CMGF==0
+ */
+static bool ATHW_CurrentSMSCPrefix=false;
+static bool ATHW_CurrentSMSCPrefix_force=false;
+/* Always try 4 retries - w/o prefix, w/prefix, w/o prefix again, w/prefix again, fail
+ * This number should be probable even to give some 'stability' when all SMSes are failing
+ */
+#define ATHW_CURRENTSMSCPREFIX_RETRIES (4)
+
+/* Please see the comment above ATHW_SaveSMS_StatSupported_solve().
+ * Number of retry sessions before giving up sending SMS <stat> during SaveSMS.
+ */
+#define ATHW_SAVESMS_STATSUPPORTED_RETRIES (2)
+
+
+static char ATHW_CatchBuffer[ATHW_CATCHBUFFER_LENGTH];
+static char *ATHW_CatchBufferPtr=ATHW_CatchBuffer;     /* current destination writing ptr */
+static char *ATHW_CatchBufferMarker;                   /* marks begin of currently catched stream */
+static GSM_Error *ATHW_CatchBufferErrorP;
+
+static long ATHW_RX_Patrol_CME_ERROR_code;
+static long ATHW_RX_Patrol_CMS_ERROR_code;
+
+typedef char *(*ATHW_RX_PatrolFunc)(char *after);
+typedef void (*ATHW_RX_PatrolReset)(void);
+
+struct ATHW_RX_Patrol {
+       const char *buoy;
+       ATHW_RX_PatrolFunc func;
+       ATHW_RX_PatrolReset reset;
+       };
+
+static const struct ATHW_RX_Patrol *ATHW_RX_Patrol_Current;
+
+static void ATHW_CatchBufferReset(void);
+static void ATHW_CatchBufferMarkStart(GSM_Error *errorcodep);
+
+#ifndef WIN32
+
+static pthread_t Thread;
+# if __unices__
+static pthread_t selThread;
+# endif
+
+#endif
+
+/* Local variables used by get/set phonebook entry code. Buffer is used as a
+   source or destination for phonebook data and other functions... Error is
+   set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
+   handler routines as appropriate. */
+
+static GSM_MemoryStatus   *CurrentMemoryStatus;
+static GSM_Error          CurrentMemoryStatusError;
+
+static GSM_PhonebookEntry *CurrentPhonebookEntry;
+static GSM_Error          CurrentPhonebookError;
+
+static GSM_SMSMessage     *CurrentSMSMessage;
+static char               *CurrentSMSMessagePDU;       /* hex string representation for +CMGF=0 patrol */
+static size_t             CurrentSMSMessagePDU_size;   /* used only if (+CMGF==0), size in bytes */
+static GSM_Error          CurrentSMSMessageError;
+
+static GSM_SMSStatus      *CurrentSMSStatus;
+static GSM_Error          CurrentSMSStatusError;
+
+static GSM_MessageCenter  *CurrentMessageCenter;
+static GSM_Error          CurrentMessageCenterError;
+
+static float              *CurrentRFLevel;             /* AT+CSQ */
+static GSM_Error          GetRFLevelError;
+
+static float              *CurrentBatteryLevel;                /* AT+CBC */
+static GSM_Error          GetBatteryLevelError;
+
+static GSM_PowerSource    *CurrentPowersource;         /* AT+CBC */
+static GSM_Error          GetPowersourceError;
+
+static GSM_Error          DialVoiceError;
+
+static GSM_Error          CancelCallError;
+
+static GSM_Error          CurrentPhoneInfoError;
+
+static unsigned char      Manufacturer[GSM_MAX_MANUFACTURER_LENGTH];
+static unsigned char      Model[GSM_MAX_MODEL_LENGTH];
+static unsigned char      Revision[GSM_MAX_REVISION_LENGTH];
+static unsigned char      IMEI[GSM_MAX_IMEI_LENGTH];
+
+static char               CurrentIncomingCall[20] = " ";
+
+static GSM_NetworkInfo    *CurrentNetworkInfo;
+static GSM_Error          CurrentNetworkInfoError;
+
+
+/* Pointer to a callback function used to return changes to a calls status */
+/* This saves unreliable polling */
+static void (*CallPassup)(char c);
+
+
+/* "catchbufer" can be provided as NULL:
+ *  - sizeof() will be bogus but it will be ingored anyway
+ */
+
+static void ATHW_TX_SendCommand(GSM_Error *errorcodep,const struct ATHW_RX_Patrol *patrol,const char *fmt,...) G_GNUC_PRINTF(3,4);
+static void ATHW_TX_SendCommand(GSM_Error *errorcodep,const struct ATHW_RX_Patrol *patrol,const char *fmt,...)
+{
+char *command;
+size_t command_len;
+va_list ap;
+int writephone_got;
+
+       if (errorcodep)
+               *errorcodep=GE_BUSY;
+       ATHW_RX_Patrol_Current=patrol;
+
+       va_start(ap,fmt);
+       command_len=gvasprintf(&command,fmt,ap);
+       va_end(ap);
+       if (-1==command_len) {
+               if (errorcodep)
+                       *errorcodep=GE_INTERNALERROR;
+               return;
+               }
+
+       ATHW_CatchBufferMarkStart(errorcodep);
+
+       writephone_got=WRITEPHONE(PortFD, command, command_len);
+
+#ifdef ATHW_DEBUG
+       write(1,"CMD:",4);
+       write(1,command,command_len);
+       write(1,"\n",1);
+#endif
+
+       free(command);
+       if (command_len!=writephone_got) {
+               if (errorcodep)
+                       *errorcodep=GE_INTERNALERROR;
+               return;
+               }
+
+       /* success but we don't wait for the result code */
+}
+
+/* This function is used to get storage status from the phone. It currently
+   supports two different memory areas - internal and SIM. */
+
+static GSM_Error 
+wait_on(volatile GSM_Error *what, int timeout)
+{
+GSM_Error r=GE_TIMEOUT;        /* shut up GCC when (timeout==0) */
+
+       while (timeout && ((r=*what)==GE_BUSY)) {
+               if (!--timeout) {
+                       r=GE_TIMEOUT;
+                       break;
+                       }
+               usleep(100000);
+       }
+       /* any specific patrollers are no longer valid */
+       ATHW_RX_Patrol_Current=NULL;
+
+#ifdef ATHW_DEBUG
+       printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
+#endif
+
+       return(r);
+}
+
+#define WAIT_ON(what, timeout) \
+       do { \
+               GSM_Error res = wait_on(what, timeout); \
+               if (res != GE_NONE) \
+                       return res; \
+        } while (0)
+
+/* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
+ */
+#define ATHW_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
+       do { \
+GSM_Error _ATHW_TX_SENDCOMMAND_WAIT_ON_err,*_ATHW_TX_SENDCOMMAND_WAIT_ON_errp; \
+ \
+               if (!(_ATHW_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
+                       _ATHW_TX_SENDCOMMAND_WAIT_ON_errp=&_ATHW_TX_SENDCOMMAND_WAIT_ON_err; \
+               ATHW_TX_SendCommand(_ATHW_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
+               WAIT_ON(_ATHW_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
+       } while (0)
+
+
+#define ATHW_ERR_WRAPPER(expr) \
+       do { \
+               GSM_Error err=(expr); \
+               if (err!=GE_NONE) \
+                       return(err); \
+       } while (0)
+
+/* Currently we use a heuristic for extraction as at least Nokia 9000i doesn't
+ * escape quote characters even if they are inside enquoted strings!
+ * When we find -- ", -- we assume it is the separator although it may be false!
+ * Hmm, Siemens M20 has the same broken behaviour.
+ */
+static void ATHW_ExtractString(char *dest,size_t destlen,char *src,int element_no)
+{
+char *srcend,*start,*end;
+
+       *dest='\0';
+       if (!(srcend=strchr(src,'\n')))
+               return;         /* INTERNAL error! */
+       do {
+               while (*src==' ') src++;
+               if (*src!='"') {
+                       start=src;
+                       while (src<srcend && *src!=',') src++;
+                       end=src;
+                       }
+               else {          /* *src=='"' */
+                       start=++src;
+                       while (src<srcend && !(src[0]=='"' && (src[1]==',' || src+1==srcend))) src++;
+                       end=src;
+                       if (*src=='"')
+                               src++;
+                       }
+               while (*src==' ') src++;
+               if (*src==',')
+                       src++;
+               /* here we have <start..end) as the current element */
+               } while (element_no--);
+
+       if (destlen-1<end-start)
+               end=start+destlen-1;
+
+       memcpy(dest,start,end-start);
+       dest[end-start]='\0';
+}
+
+#define ATHW_EXTRACTSTRING(dest,src,element_no) (ATHW_ExtractString((dest),sizeof((dest)),(src),(element_no)))
+
+static long ATHW_ExtractNumber(char *src,int element_no)
+{
+char buf[32],*end=NULL;
+long r;
+
+       ATHW_EXTRACTSTRING(buf,src,element_no);
+       r=strtol(buf,&end,10);
+       if (!*buf || (end && *end))
+               return(LONG_MIN);
+       return(r);
+}
+
+/* s/^\s+//; s/\s+$//; s/\s+/ /g; */
+
+static void ATHW_BufferTrimCopy(char *d_buf,size_t d_len,const char *s)
+{
+char *d_end=d_buf+d_len,*d;
+
+       for (d=d_buf;*s && d<d_end -1/*Terminating '\0'*/ ;s++) {
+               if (isspace(*s)) {
+                       if (d==d_buf || d[-1]==' ')
+                               continue;
+                       *d++=' ';
+                       continue;
+                       }
+               *d++=*s;
+               }
+       if (d>d_buf && d[-1]==' ')
+               d--;
+       *d='\0';
+}
+
+#define ATHW_BUFFERTRIMCOPY_OFFSET(d,s,offset) (ATHW_BufferTrimCopy((d)+(offset),sizeof((d))-(offset),(s)))
+#define ATHW_BUFFERTRIMCOPY(d,s) (ATHW_BUFFERTRIMCOPY_OFFSET((d),(s),0))
+
+static GSM_Error ATHW_GetPhoneInfo(void)
+{
+/* +CGMI=Request Manufacturer Identification
+ * +CGMM=Request Model Identification
+ * +CGMR=Request Revision Identification
+ * +CGSN=Request Product Serial Number Identification (IMEI)
+ */
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMI\r");
+       ATHW_BUFFERTRIMCOPY(Manufacturer,ATHW_CatchBufferMarker);
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMM\r");
+       ATHW_BUFFERTRIMCOPY_OFFSET(Model,ATHW_CatchBufferMarker,3);
+       memcpy(Model,"AT-",3);
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMR\r");
+       ATHW_BUFFERTRIMCOPY(Revision,ATHW_CatchBufferMarker);
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGSN\r");
+       ATHW_BUFFERTRIMCOPY(IMEI,ATHW_CatchBufferMarker);
+
+       return(GE_NONE);
+}
+
+static unsigned char ATHW_SMStoFO(GSM_SMSMessage *SMS)
+{
+       return(0
+                       |((SMS->Type==GST_MO)<<0)
+                       |(2<<3) /*<vp>=integer*/
+                       |0 /* bit 5 is Status Report Request, not supported */
+                       |((!!SMS->UDHPresent)<<6) /*UDH set*/
+                       |((!!SMS->ReplyViaSameSMSC)<<7) /*ReplyPath*/
+                       );
+}
+
+static unsigned char ATHW_SMStoDCS(GSM_SMSMessage *SMS)
+{
+int class=(SMS->Class==-1 ? 1 : SMS->Class);   /* we default to class 1 (Mobile Equipment target) */
+
+       if (!SMS->EightBit && class==1)
+               return(0x00);
+       return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
+}
+
+/* Nokia 9000i: We need to temporarily turn on ECHO otherwise we wouldn't got the "\n> " prompt
+ * (At least Siemens M20 doesn't have this broken behaviour.)
+ */
+static GSM_Error ATHW_SMS_CMGF01_pre(void)
+{
+       if (ATHW_CurrentCMGF==1) {
+               /* We just cannot send UDH header in pure CMGF 1 mode */
+               if (CurrentSMSMessage->UDHPresent && (!CurrentSMSMessage->EightBit || !ATHW_CMGS_CMGF1_8bit_HaveBinHex))
+                       return(GE_INTERNALERROR);
+
+               /* AT+CSMP=<fo>,<vp>,<pid>,<dcs> */
+               ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+                       "AT+CSMP=%d,%d,%d,%d\r",
+                       /*  <fo>: */ ATHW_SMStoFO(CurrentSMSMessage),
+                       /*  <vp>: */ SMS_Validity_to_VP(CurrentSMSMessage->Validity),
+
+                       /* FIXME: Should we query current "MessageCenter.No" to get "Format" field? */
+                       /* <pid>: */ (unsigned char)CurrentSMSMessage->MessageCenter.Format,
+
+                       /* <dcs>: */ ATHW_SMStoDCS(CurrentSMSMessage)
+                       );
+               }
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "ATE1\r");
+
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_SMS_CMGF01_post(void)
+{
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "ATE0\r");
+
+       return(GE_NONE);
+}
+
+/* For now we just kick out all quotes in the source "string" as no escaping
+ * method was found on Nokia 9000i - what the other ones?
+ */
+static const char *ATHW_Enquote(const char *string)
+{
+
+#define ATHW_ENQUOTE_SLOTS (4)
+
+struct ATHW_Enquote_slot {
+       char *buf;
+       size_t len;
+       };
+
+static struct ATHW_Enquote_slot slots[ATHW_ENQUOTE_SLOTS];
+static struct ATHW_Enquote_slot *slot=slots;
+size_t stringl=strlen(string);
+char *d;
+const char *r,*s;
+
+       if (slot->len<1+stringl+1+1) {  /* "string"\0 */
+char *newbuf;
+size_t newlen=(!slot->len ? 0x100 : slot->len*2);
+
+               if (!(newbuf=realloc(slot->buf,newlen)))
+                       return(NULL);
+               slot->buf=newbuf;
+               slot->len=newlen;
+               }
+
+       d=slot->buf;
+       *d++='"';
+       for (s=string;*s;s++) {
+               if (*s=='"')
+                       continue;
+               *d++=*s;
+               }
+       *d++='"';
+       *d++='\0';
+
+       r=slot->buf;
+       if (++slot>=slots+ARRAY_LEN(slots))
+               slot=slots;
+       return(r);
+}
+
+static char *ATHW_PhoneSetup_CMGF1_Detect_Patrol_9(char *after)
+{
+       /* We got at least echo of the initial "9" digit.
+        * Nokia 9000i/9110i/CardPhone will echo even "Q" followed by ERROR,
+        * but Nokia 9210 will give ERROR immediately (w/o echo of "Q").
+        * When the feature is not supported we will get GE_TIMEOUT and 
+        * ATHW_CMGS_CMGF1_8bit_HaveBinHex will be reset to "false" by the error check
+        * in ATHW_PhoneSetup_CMGF1_Detect_core().
+        */
+       ATHW_CMGS_CMGF1_8bit_HaveBinHex=true;
+
+       WRITEPHONE(PortFD,"Q",1);
+
+       /* We don't need to patrol for "Q", its detection would be cross-phone
+        * incompatible.
+        */
+
+       return(after);  /* eat line - it will be just one character... */
+}
+
+static const struct ATHW_RX_Patrol ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct=
+       { "9",ATHW_PhoneSetup_CMGF1_Detect_Patrol_9 };
+
+static char *ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE(char *after)
+{
+       WRITEPHONE(PortFD,"9",1);
+       ATHW_RX_Patrol_Current=&ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct;
+       return(after);  /* eat line - it will be just one character... */
+}
+
+static const struct ATHW_RX_Patrol ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct=
+       { "\n> ",ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE };
+
+/* ATHW_CMGS_CMGF1_8bit_HaveBinHex feature is AFAIK present on Nokia 9000i/9110
+ * and also on Nokia CardPhone (but CardPhone supports +CMGF==0 so it is useless there).
+ * On Nokia 9000i we will detect the feature as present although it cannot be used
+ * for logo/ring sending as it will later fail to set UDH bit in FO by AT+CSMP.
+ */
+static GSM_Error ATHW_PhoneSetup_CMGF1_Detect_core(void)
+{
+GSM_SMSMessage sms;
+
+       ATHW_CMGS_CMGF1_8bit_HaveBinHex=false;  /* default */
+
+       /* We need to reset the catchbuffer as we would otherwise
+        * catch some completely bogus previous output.
+        * We are called only during phone setup so here is no risk in loosing
+        * some unsolicited result codes.
+        */
+       ATHW_CatchBufferReset();
+
+       CurrentSMSMessage=&sms;
+       /* for ATHW_SMStoFO(): */
+       CurrentSMSMessage->UDHPresent=false;
+       CurrentSMSMessage->Type=GST_MO;
+       CurrentSMSMessage->UDHPresent=false;            /* warning: it would fail on Nokia 9000i or Siemens M20! */
+       CurrentSMSMessage->ReplyViaSameSMSC=false;
+       /* for SMS_Validity_to_VP(): */
+       CurrentSMSMessage->Validity=GSMV_72_Hours;      /* default */
+       /* for ATHW_SMS_CMGF01_pre(): */
+       CurrentSMSMessage->MessageCenter.Format=GSMF_Text;
+       /* for ATHW_SMStoDCS(): */
+       CurrentSMSMessage->EightBit=true;               /* IMPORTANT!: The ONLY meaning of the whole procedure! */
+       CurrentSMSMessage->Class=1;
+       /* Do proper AT+CSMP= and also ATE1 */
+       ATHW_SMS_CMGF01_pre();
+
+       ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct/*patrol*/,
+               "AT+CMGS=%s\r",ATHW_Enquote("123456"/*just some test number*/));
+
+       /* Timeout 5 is too small for Nokia 9000i, give it a rest...
+        */
+       if (GE_TIMEOUT==wait_on(&CurrentSMSMessageError,20/*timeout*/))
+               ATHW_CMGS_CMGF1_8bit_HaveBinHex=false;
+
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_PhoneSetup_CMGS_Reset(void)
+{
+GSM_Error err;
+int retry;
+
+       /* We try to escape AT+CMGS mode, at least Siemens M20 then needs to get some rest
+        */
+       WRITEPHONE(PortFD,"\x1B\r",2);
+       usleep(500000);
+
+       ATHW_CatchBufferReset();        /* output can be very bogus due to escaping */
+
+       for (retry=0;retry<3;retry++) {
+               ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+                       "AT\r");
+               if (wait_on(&err,10/*timeout*/)==GE_NONE)
+                       return(err);    /* success */
+               }
+       /* Hmm, we've probably stucked the phone :-(
+        */
+       return(err);    /* failure */
+}
+
+static void ATHW_PhoneSetup_CMGF1_Detect(void)
+{
+       ATHW_PhoneSetup_CMGF1_Detect_core();    /* errors of the detection are ignored */
+       ATHW_PhoneSetup_CMGS_Reset();
+       ATHW_SMS_CMGF01_post();         /* turn off echo */
+#ifdef ATHW_DEBUG
+       printf("ATHW_PhoneSetup_CMGF1_Detect: ATHW_CMGS_CMGF1_8bit_HaveBinHex=%d\n",(int)ATHW_CMGS_CMGF1_8bit_HaveBinHex);
+#endif
+}
+
+static GSM_Error ATHW_PhoneSetup_CMGFSet(int CMGF)
+{
+       ATHW_CurrentCMGF=-1;
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CMGF=%d\r",CMGF);
+       ATHW_CurrentCMGF=CMGF;
+
+       if (CMGF==1)
+               ATHW_PhoneSetup_CMGF1_Detect();
+
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_PhoneSetup(void)
+{
+GSM_Error err;
+
+       ATHW_CurrentMemoryType=GMT_XX;          /* invalidate */
+
+       ATHW_PhoneSetup_CMGS_Reset();
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,
+               "AT&F\r");
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "ATE0\r");
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CSDH=1\r");
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CMEE=1\r");
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CRC=1\r");          /* request "+CRING: VOICE" reporting */
+
+       /* Try to detect Siemens M20 with M1 backward compatibility mode */
+       ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               "AT+CSMS=128\r");
+       if (GE_NONE==wait_on(&err,10/*timeout*/)) {
+               ATHW_CurrentSMSCPrefix=true;            /* it will be true ASAP +CMGS=0 is done */
+               ATHW_CurrentSMSCPrefix_force=true;
+               }
+       ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CSMS=0\r");                         /* turn on ATHW_CurrentSMSCPrefix, if possible */
+
+#ifndef DISABLE_CMGF0
+       if (GE_NONE!=ATHW_PhoneSetup_CMGFSet(0))
+#endif
+               ATHW_PhoneSetup_CMGFSet(1);
+
+       ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               "AT^SMGR=?\r");         /* it should just return "\nOK\n" */
+       ATHW_HaveSiemensMGR=(GE_NONE==wait_on(&err,10/*timeout*/));
+       ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               "AT^SMGL=?\r");         /* it should just return "\nOK\n" */
+       ATHW_HaveSiemensMGL=(GE_NONE==wait_on(&err,10/*timeout*/));
+
+       ATHW_HaveCNMI=true;
+       ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               "AT+CNMI=2,1\r");               /* <mode>=2 (buffer when data active), <mt>=1 (+CMTI codes) */
+       if (GE_NONE!=wait_on(&err,10/*timeout*/)) {
+               ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+                       "AT+CNMI=1,1\r");               /* <mode>=1 (discard when data active), <mt>=1 (+CMTI codes) */
+               if (GE_NONE!=wait_on(&err,10/*timeout*/))
+                       ATHW_HaveCNMI=false;
+               }
+       ATHW_CNMI_count=-1;     /* unknown yet */
+
+       ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               "AT+COPS=3,2\r");               /* <mode>=3 (just set <format>), <format>=2 (numeric) */
+       wait_on(&err,10/*timeout*/);    /* error ignored, GetNetworkInfo will find the failure itself */
+
+       /* Enable LAC+CellID returns, it will also disable unsolicited changes reporting
+        * but we ignore it successfuly.
+        */
+       ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               "AT+CREG=2\r");
+       wait_on(&err,10/*timeout*/);    /* error ignored, GetNetworkInfo will find the failure itself */
+
+       ATHW_GetPhoneInfo();
+
+       return(GE_NONE);
+}
+
+
+static void ATHW_RX_Char(char rx_byte);
+static void ATHW_SigHandler(int status);
+static bool ATHW_OpenSerial(void);
+
+GSM_Phone phone_at_hw; /* forward declaration */
+
+/* Initialise variables and state machine. */
+
+static GSM_Error ATHW_Init(GSM_Data *data, GSM_Statemachine *state)
+{
+       RequestTerminate = false;
+       CallPassup = NULL;
+
+       /* Create and start main thread. */
+
+#ifdef WIN32
+{
+int rtn;
+       rtn = ! OpenConnection(State->Link.PortDevice,ATHW_RX_Char,ATHW_KeepAliveProc);
+       if (rtn != 0) {
+               return(GE_INTERNALERROR);
+}
+#else
+       SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
+       if (!ATHW_OpenSerial())
+               return(GE_INTERNALERROR);
+#endif
+
+        ATHW_ERR_WRAPPER(ATHW_PhoneSetup());
+
+       return (GE_NONE);
+}
+
+#if __unices__
+/* thread for handling incoming data */
+void ATHW_SelectLoop()
+{
+       int err;
+       fd_set readfds;
+       struct timeval timeout;
+
+       FD_ZERO(&readfds);
+       FD_SET(device_portfd, &readfds);
+       /* set timeout to 15 seconds */
+       timeout.tv_sec=15;
+       timeout.tv_usec=0;
+       while (!RequestTerminate) {
+               err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
+               /* call singal handler to process incoming data */
+               if ( err > 0 ) ATHW_SigHandler(0);
+               else if (err == -1) perror("Error in SelectLoop");
+       }
+}
+#endif
+
+static GSM_Error ATHW_AnswerCall(GSM_Data *data, GSM_Statemachine *state)
+{
+       /* data->CallNo not present up to Nokia 9110i */
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"ATA\r");
+       return(GE_NONE);
+}
+
+/* Applications should call ATHW_Terminate to shut down the ATHW thread and
+   close the serial port. */
+
+static GSM_Error ATHW_Terminate(GSM_Data *data, GSM_Statemachine *state)
+{
+       /* Request termination of thread */
+       RequestTerminate = true;
+
+#ifndef WIN32
+       /* Now wait for thread to terminate. */
+       pthread_join(Thread, NULL);
+
+       /* Close serial port. */
+       device_close();
+
+#else
+       CloseConnection();
+#endif
+
+       return(GE_NONE);
+}
+
+/* +CSQ: Signal Quality
+ *     CurrentRFLevel = 1st arg <rssi>
+ */
+static char *ATHW_RX_Patrol_CSQ(char *after)
+{
+long l;
+
+       l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
+       if (l>=0 && l<=31)
+               *CurrentRFLevel=l;
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSQ_struct=
+       { "\n+CSQ:", ATHW_RX_Patrol_CSQ };
+
+static GSM_Error ATHW_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentRFLevel=data->RFLevel;
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&GetRFLevelError/*errorcodep*/,30/*timeout*/,&ATHW_RX_Patrol_CSQ_struct,"AT+CSQ\r");
+
+       if (*data->RFUnits!=GRF_CSQ) {
+               *data->RFUnits = GRF_Percentage;                /* required by xgnokii_lowlevel.c */
+               *data->RFLevel = (*data->RFLevel)*100/31;       /* +CSQ */
+               }
+
+       return (GE_NONE);
+}
+
+/* +CBC: Battery Charge
+ *     CurrentPowersource  = 1st arg <bcs>
+ *     CurrentBatteryLevel = 2nd arg <bcl>
+ */
+static char *ATHW_RX_Patrol_CBC(char *after)
+{
+long l;
+
+       if (CurrentPowersource) {
+               l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
+               if (l==0 || l==1)
+                       *CurrentPowersource=(l==1 ? GPS_ACDC : GPS_BATTERY);
+               }
+
+       if (CurrentBatteryLevel) {
+               l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
+               if (l>=0 && l<=100)
+                       *CurrentBatteryLevel=l;
+               }
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CBC_struct=
+       { "\n+CBC:", ATHW_RX_Patrol_CBC };
+
+static GSM_Error ATHW_GetBatteryLevel(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentBatteryLevel=data->BatteryLevel;
+       *data->BatteryUnits = GBU_Percentage;
+
+       ATHW_TX_SendCommand(&GetBatteryLevelError/*errorcodep*/,&ATHW_RX_Patrol_CBC_struct,"AT+CBC\r");
+       if (GE_NONE!=wait_on(&GetBatteryLevelError,30/*timeout*/)) {
+               /* We are probably on AC powered device such as Nokia CardPhone
+                */
+               *CurrentBatteryLevel=100;
+               }
+
+
+       return (GE_NONE);
+}
+
+static GSM_Error ATHW_GetPowersource(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentPowersource=data->PowerSource;
+
+       ATHW_TX_SendCommand(&GetPowersourceError/*errorcodep*/,&ATHW_RX_Patrol_CBC_struct,"AT+CBC\r");
+       if (GE_NONE!=wait_on(&GetPowersourceError,30/*timeout*/)) {
+               /* We are probably on AC powered device such as Nokia CardPhone
+                */
+               *CurrentPowersource=GPS_ACDC;
+               }
+
+       return (GE_NONE);
+}
+
+static GSM_Error ATHW_DialVoice(GSM_Data *data, GSM_Statemachine *state)
+{
+       /* "ATDnumber;" is notation supported at least on Nokia up to 9110i
+        * We need to wait for establishing a connection as other commands would otherwise break it!
+        */
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&DialVoiceError/*errorcodep*/,600/*timeout*/,NULL/*patrol*/,"ATD%s;\r",data->VoiceNumber);
+
+       return(GE_NONE);
+}
+
+/* Dial a data call - type specifies request to use: 
+     type 0 should normally be used
+     type 1 should be used when calling a digital line - corresponds to ats35=0
+     Maybe one day we'll know what they mean!
+         FIXME: ATHW currently ignores it - what value should be used for S35?
+*/
+
+static GSM_Error ATHW_DialData(GSM_Data *data, GSM_Statemachine *state)
+{
+       CallPassup=data->DataCallPassUp;
+
+       switch (*data->DataType) {
+       case 0: /* FALLTHRU */
+       case 1: /* FALLTHRU */
+       default:
+               break;
+               break;
+       case -1:   /* Just used to set the call passup */
+               return GE_NONE;
+               break;
+       }
+
+       ATHW_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,"ATD%s\r",data->DataNumber);
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_GetIncomingCallNr(GSM_Data *data, GSM_Statemachine *state)
+{
+       if (*CurrentIncomingCall != ' ') {
+               strcpy(data->IncomingCallNr, CurrentIncomingCall);
+               return GE_NONE;
+       } else return GE_BUSY;
+}
+
+static GSM_Error ATHW_CancelCall(GSM_Data *data, GSM_Statemachine *state)
+{
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CancelCallError/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,"ATH\r");
+
+       return(GE_NONE);
+}
+
+/* messagecenter->No" is NOT set as it is input argument */
+static void ATHW_MessageCenterClear(GSM_MessageCenter *messagecenter)
+{
+       messagecenter->Name[0]='\0';            /* not present up to Nokia 9110i */
+       messagecenter->Recipient[0]='\0';       /* not present up to Nokia 9110i */
+       messagecenter->Number[0]='\0';          /* default */
+       messagecenter->Format=GSMF_Text;        /* default */
+       messagecenter->Validity=GSMV_72_Hours;  /* default */
+}
+
+/* +CSCA:
+ *     MessageCenter->Number = 1st arg <sca>
+ */
+static char *ATHW_RX_Patrol_CSCA(char *after)
+{
+       ATHW_EXTRACTSTRING(CurrentMessageCenter->Number,after/*src*/,0/*element_no*/);
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSCA_struct=
+       { "\n+CSCA:", ATHW_RX_Patrol_CSCA };
+
+/* +CSMP:
+ *     CurrentMessageCenter->Format   = 3rd arg <pid>
+ *     CurrentMessageCenter->Validity = 2nd arg <vp>
+ */
+static char *ATHW_RX_Patrol_CSMP(char *after)
+{
+long l;
+
+       l=ATHW_ExtractNumber(after/*src*/,2/*element_no*/);
+       if (l>=0 && l<0x100)
+               CurrentMessageCenter->Format=(GSM_SMSMessageFormat)l;
+
+       l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
+       if (l>=0 && l<0x100)
+               CurrentMessageCenter->Validity=(GSM_SMSMessageValidity)l;
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSMP_struct=
+       { "\n+CSMP:", ATHW_RX_Patrol_CSMP };
+  
+/* This function sends to the mobile phone a request for the SMS Center */
+
+static GSM_Error ATHW_GetSMSCenter(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentMessageCenter = data->MessageCenter;
+
+       if (CurrentMessageCenter->No!=1)        /* "CurrentMessageCenter->No" not present up to Nokia 9110i */
+               return(GE_INTERNALERROR);       /* FIXME: some better code? */
+       ATHW_MessageCenterClear(CurrentMessageCenter);
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMessageCenterError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CSCA_struct,"AT+CSCA?\r");
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMessageCenterError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CSMP_struct,"AT+CSMP?\r");
+
+       printf("message center OK: %s\n",CurrentMessageCenter->Number);
+       return(GE_NONE);
+}
+
+/* This function set the SMS Center profile on the phone. */
+
+static GSM_Error ATHW_SetSMSCenter(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentMessageCenter = data->MessageCenter;
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMessageCenterError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CSCA=%s\r",ATHW_Enquote(CurrentMessageCenter->Number));
+       return(GE_NONE);
+}
+
+static int ATHW_RX_Patrol_SiemensMGL_count;
+
+static char *ATHW_RX_Patrol_SiemensMGL(char *after)
+{
+char *s;
+
+       /* There may be left string from AT+CMGL=? which would confuse us!
+        */
+       for (s=after;isspace(*s);s++);
+       if (*s=='(')
+               return(after);  /* eat line */
+               
+       /* Lines with our wanted "REC UNREAD" should be already filtered by the request
+        */
+       ATHW_RX_Patrol_SiemensMGL_count++;
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_SiemensMGL_struct=
+       { "\n^SMGL:", ATHW_RX_Patrol_SiemensMGL };
+
+static void ATHW_Update_CNMI_count(void)
+{
+const char *state;
+
+       if (!ATHW_HaveSiemensMGL)
+               return;
+
+       switch (ATHW_CurrentCMGF) {
+       case 0: state="0"; break;
+       case 1: state=ATHW_Enquote("REC UNREAD"); break;
+       default:
+               return; /* not supported */
+       }
+
+       ATHW_RX_Patrol_SiemensMGL_count=0;
+       ATHW_TX_SendCommand(&CurrentSMSStatusError/*errorcodep*/,&ATHW_RX_Patrol_SiemensMGL_struct/*patrol*/,
+               "AT^SMGL=%s\r",state);
+       if (GE_NONE==wait_on(&CurrentSMSStatusError,600/*timeout*/))
+               ATHW_CNMI_count=ATHW_RX_Patrol_SiemensMGL_count;        /* otherwise not updated */
+}
+
+/* +CPMS:
+ *     CurrentSMSStatus->Used   = 2nd arg <used1>
+ *     CurrentSMSStatus->Slots  = 3nd arg <total1>
+ */
+static char *ATHW_RX_Patrol_CPMS(char *after)
+{
+long l;
+
+       l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
+       if (l>=0 && l<INT_MAX)
+               CurrentSMSStatus->Used=l;
+
+       l=ATHW_ExtractNumber(after/*src*/,2/*element_no*/);
+       if (l>=0 && l<INT_MAX)
+               CurrentSMSStatus->Slots=l;
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPMS_struct=
+       { "\n+CPMS:", ATHW_RX_Patrol_CPMS };
+
+/* Note: "Status->UnRead" cannot be detected at all up to Nokia 9110i
+ * as its GEOS always reads the incoming message immediately.
+ * On some other devices it could be probably solved by: AT+CMGL="REC UNREAD"
+ */
+static GSM_Error ATHW_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentSMSStatus = data->SMSStatus;
+       if (ATHW_CNMI_count<0)
+               ATHW_Update_CNMI_count();
+       CurrentSMSStatus->UnRead=(ATHW_CNMI_count<0 ? 0 : ATHW_CNMI_count);     /* not present up to Nokia 9110i */
+       CurrentSMSStatus->Used =10;             /* default */
+       CurrentSMSStatus->Slots=10;             /* default */
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CPMS_struct,
+               "AT+CPMS?\r");
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_GetImei(GSM_Data *data, GSM_Statemachine *state)
+{
+       if (*IMEI) {
+               strcpy(data->Imei,IMEI);
+               return (GE_NONE);
+       } else return (GE_TRYAGAIN);
+}
+
+static GSM_Error ATHW_GetRevision(GSM_Data *data, GSM_Statemachine *state)
+{
+       if (*Revision) {
+               strcpy(data->Revision,Revision);
+               return (GE_NONE);
+       } else return (GE_TRYAGAIN);
+}
+
+static GSM_Error ATHW_GetModel(GSM_Data *data, GSM_Statemachine *state)
+{
+       if (*Model) {
+               strcpy(data->Model,Model);
+               return (GE_NONE);
+       } else return (GE_TRYAGAIN);
+}
+
+static GSM_Error ATHW_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
+{
+       if (*Model) {
+               strcpy(data->Manufacturer,Manufacturer);
+               return (GE_NONE);
+       } else return (GE_TRYAGAIN);
+}
+
+/* This function translates GMT_MemoryType to the string for AT+CPBS
+ * Nokia 9000i: ("SM")
+ * Siemens M20: ("SM","FD","LD","RC","ON","ME","MC","MT")
+ */
+
+#define GETMEMORYTYPE_ENTRY(code) \
+               case GMT_##code: return( #code );
+
+static const char *ATHW_GetMemoryType(GSM_MemoryType memory_type)
+{
+       switch (memory_type) {
+       GETMEMORYTYPE_ENTRY(ME)
+       GETMEMORYTYPE_ENTRY(SM)
+       GETMEMORYTYPE_ENTRY(FD)
+       GETMEMORYTYPE_ENTRY(ON)
+       GETMEMORYTYPE_ENTRY(EN)
+       GETMEMORYTYPE_ENTRY(DC)
+       GETMEMORYTYPE_ENTRY(RC)
+       GETMEMORYTYPE_ENTRY(MC)
+       GETMEMORYTYPE_ENTRY(LD)
+       GETMEMORYTYPE_ENTRY(MT)
+       GETMEMORYTYPE_ENTRY(TA)
+       GETMEMORYTYPE_ENTRY(CB)
+               default:     return(NULL);
+        }
+}
+
+#define ATHW_GETMEMORYTYPE(dst,memory_type) \
+       do { \
+               (dst)=ATHW_GetMemoryType((memory_type)); \
+               if (!(dst)) \
+                       return(GE_INVALIDMEMORYTYPE); \
+       } while (0)
+
+static GSM_Error ATHW_SelectPhonebookMemory(GSM_MemoryType memory_type)
+{
+const char *atmemtype;
+
+       if (memory_type==ATHW_CurrentMemoryType)
+               return(GE_NONE);
+       ATHW_CurrentMemoryType=GMT_XX;
+
+       ATHW_GETMEMORYTYPE(atmemtype,memory_type);
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhonebookError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CPBS=%s\r",ATHW_Enquote(atmemtype));
+
+       ATHW_CurrentMemoryType=memory_type;
+
+       return(GE_NONE);
+}
+
+/* +CPBR: (GetMemoryStatus)
+ *     CurrentMemoryStatus->Free=1st arg (1-<index>)
+ */
+static char *ATHW_RX_Patrol_CPBR_GetMemoryStatus(char *after)
+{
+char buf[32];
+int num_1,num_2;
+
+       ATHW_EXTRACTSTRING(buf,after/*src*/,0/*element_no*/);
+       if (2==sscanf(buf,"(%d-%d)",&num_1,&num_2)) {
+               CurrentMemoryStatus->Used=0;            /* not present up to Nokia 9110i, we ignore it now */
+               CurrentMemoryStatus->Free=num_2;
+               }
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPBR_GetMemoryStatus_struct=
+       { "\n+CPBR:", ATHW_RX_Patrol_CPBR_GetMemoryStatus };
+
+static GSM_Error ATHW_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentMemoryStatus=data->MemoryStatus;
+
+       ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentMemoryStatus->MemoryType));
+
+       CurrentMemoryStatus->Used=0;            /* default */
+       CurrentMemoryStatus->Free=0;            /* default */
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMemoryStatusError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CPBR_GetMemoryStatus_struct,
+               "AT+CPBR=?\r");
+
+       return(GE_NONE);
+}
+
+static void ATHW_DateTimeSetCurrent(GSM_DateTime *datetime)
+{
+time_t current=time(NULL);
+struct tm *tm=localtime(&current);
+
+       datetime->AlarmEnabled=false;
+
+       datetime->Year  =tm->tm_year+1900;
+       datetime->Month =tm->tm_mon+1;
+       datetime->Day   =tm->tm_mday;
+       datetime->Hour  =tm->tm_hour;
+       datetime->Minute=tm->tm_min;
+       datetime->Second=tm->tm_sec;
+
+       datetime->Timezone=timezone;
+}
+
+/* +CPBR: (ReadPhonebook)
+ *     CurrentPhonebookEntry->Name  =4th arg <text>
+ *     CurrentPhonebookEntry->Number=2nd arg <number>
+ */
+static char *ATHW_RX_Patrol_CPBR_ReadPhonebook(char *after)
+{
+       CurrentPhonebookEntry->Empty=false;
+       ATHW_EXTRACTSTRING(CurrentPhonebookEntry->Name  ,after/*src*/,3/*element_no*/);
+       ATHW_EXTRACTSTRING(CurrentPhonebookEntry->Number,after/*src*/,1/*element_no*/);
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPBR_ReadPhonebook_struct=
+       { "\n+CPBR:", ATHW_RX_Patrol_CPBR_ReadPhonebook };
+
+/* Routine to get specifed phone book location.  Designed to be called by
+ * application.  Will block until location is retrieved or a timeout/error
+ * occurs.
+ */
+static GSM_Error ATHW_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentPhonebookEntry=data->PhonebookEntry;
+
+       ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentPhonebookEntry->MemoryType));
+
+       /* We may get just OK response (Siemens M20) which means empty location
+        */
+       CurrentPhonebookEntry->Empty=true;
+       CurrentPhonebookEntry->Group=0;                         /* not present up to Nokia 9110i */
+       ATHW_DateTimeSetCurrent(&CurrentPhonebookEntry->Date);  /* not present up to Nokia 9110i */
+       CurrentPhonebookEntry->SubEntriesCount=0;               /* not present up to Nokia 9110i */
+       CurrentPhonebookEntry->Name[0]='\0';                    /* default */
+       CurrentPhonebookEntry->Number[0]='\0';                  /* default */
+
+       ATHW_TX_SendCommand(&CurrentPhonebookError/*errorcodep*/,&ATHW_RX_Patrol_CPBR_ReadPhonebook_struct,
+               "AT+CPBR=%d\r",CurrentPhonebookEntry->Location);
+       if (GE_NONE!=wait_on(&CurrentPhonebookError,10/*timeout*/)) {
+               /* Nokia 9000i returns ATHW_CME_NOT_FOUND error code for empty locations
+                */
+               if (ATHW_CME_NOT_FOUND!=ATHW_RX_Patrol_CME_ERROR_code)
+                       return(CurrentPhonebookError);
+               return(GE_NONE);
+               }
+
+       return(GE_NONE);
+}
+
+
+/* Routine to write phonebook location in phone. Designed to be called by
+   application code. Will block until location is written or timeout
+   occurs. */
+
+static GSM_Error ATHW_WritePhonebook(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentPhonebookEntry=data->PhonebookEntry;
+
+       ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentPhonebookEntry->MemoryType));
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhonebookError/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,
+               (CurrentPhonebookEntry->Empty ? "AT+CPBW=%d\r" : "AT+CPBW=%d,%s,,%s\r"),
+               CurrentPhonebookEntry->Location,
+               ATHW_Enquote(CurrentPhonebookEntry->Number),
+               ATHW_Enquote(CurrentPhonebookEntry->Name)
+               );
+
+       return(GE_NONE);
+}
+
+static const char *ATHW_SMStoStat(GSM_SMSMessage *SMS)
+{
+       switch (CurrentSMSMessage->Type) {
+       case GST_MT:
+               switch (CurrentSMSMessage->Status) {
+               case GSS_NOTSENTREAD:
+                       return("REC UNREAD");
+               case GSS_SENTREAD:
+                       return("REC READ");
+               }
+               return(NULL);
+       case GST_MO:
+               switch (CurrentSMSMessage->Status) {
+               case GSS_NOTSENTREAD:
+                       return("STO UNSENT");
+               case GSS_SENTREAD:
+                       return("STO SENT");
+               }
+               return(NULL);
+       default:
+               return(NULL);
+       }
+       /* NOTREACHED */
+}
+
+/* RETURN: Success of recognizing "SMSstat"
+ */
+static bool ATHW_StattoSMS(GSM_SMSMessage *SMS,const char *SMSstat)
+{
+       /**/ if (!strcmp(SMSstat,"0") || !strcmp(SMSstat,"REC UNREAD")) {
+               SMS->Type=GST_MT;
+               SMS->Status=GSS_NOTSENTREAD;
+               }
+       else if (!strcmp(SMSstat,"1") || !strcmp(SMSstat,"REC READ")) {
+               SMS->Type=GST_MT;
+               SMS->Status=GSS_SENTREAD;
+               }
+       else if (!strcmp(SMSstat,"2") || !strcmp(SMSstat,"STO UNSENT")) {
+               SMS->Type=GST_MO;
+               SMS->Status=GSS_NOTSENTREAD;
+               }
+       else if (!strcmp(SMSstat,"3") || !strcmp(SMSstat,"STO SENT")) {
+               SMS->Type=GST_MO;
+               SMS->Status=GSS_SENTREAD;
+               }
+       else return(false);     /* not recognized! */
+       return(true);
+}
+
+/* scts=="01/10/25,02:28:18+08", scts WILL BE DESTROYED!
+ */
+static bool ATHW_SCTStoSMS_CMGF1(GSM_SMSMessage *SMS,char *scts)
+{
+GSM_DateTime *DateTime=&SMS->Time;
+bool timezone_minus;
+const char *fmt="%d/%d/%d,%d:%d:%d+%d";        /* trailing '\0' IS used! */
+int i;
+
+       if (strlen(scts)!=20)
+               return(false);
+       if ((timezone_minus=(scts[17]=='-')))
+               scts[17]='+';
+       i=0;
+       do {
+               if (!isdigit(scts[i++]))
+                       return(false);
+               if (!isdigit(scts[i++]))
+                       return(false);
+               if (scts[i]!=fmt[i])
+                       return(false);
+               } while (fmt[i++]);
+       /* string is completely valid now */
+
+       sscanf(scts,fmt,
+                       &DateTime->Year,
+                       &DateTime->Month,
+                       &DateTime->Day,
+                       &DateTime->Hour,
+                       &DateTime->Minute,
+                       &DateTime->Second,
+                       &DateTime->Timezone);
+       if (DateTime->Year>=85)
+               DateTime->Year-=100;
+       DateTime->Year+=2000;
+       if (timezone_minus)
+               DateTime->Timezone=-DateTime->Timezone;
+       return(true);
+}
+
+static void ATHW_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
+{
+       switch ((dcs&0xF0)>>4) {
+               case 0x0:
+                       switch (dcs&0x0F) {
+                               case 0x0:
+                                       CurrentSMSMessage->EightBit=false;
+                                       break;
+                               }
+                       break;
+               case 0xF:
+                       CurrentSMSMessage->EightBit=!!(dcs&0x04);       /* bit 2 */
+                       CurrentSMSMessage->Class=(dcs&0x03);            /* bits 0 & 1 */
+                       break;
+               }
+}
+
+static bool ATHW_SCTStoSMS_CMGF0(GSM_SMSMessage *SMS,unsigned char *scts)
+{
+GSM_DateTime *DateTime=&SMS->Time;
+int *fields[]={                /* FIXME: offsetof() would be nice here but currently not supported by Gnokii */
+               &DateTime->Year,
+               &DateTime->Month,
+               &DateTime->Day,
+               &DateTime->Hour,
+               &DateTime->Minute,
+               &DateTime->Second,
+               &DateTime->Timezone,
+               };
+int i;
+unsigned char digit0,digit1;
+
+       for (i=0;i<ARRAY_LEN(fields);i++,scts++) {
+               if (0
+                       || (digit0= (*scts)    &0x0F) > 9
+                       || (digit1=((*scts)>>4)&0x0F) > 9
+                   )
+                       return(false);
+               (*fields[i])=10*digit0 + digit1;
+               }
+       /* scts is completely valid now */
+
+       if (DateTime->Year>=85)
+               DateTime->Year-=100;
+       DateTime->Year+=2000;
+       return(true);
+}
+
+/* Value MUST match ((fo>>3)&0x03) !
+ */
+enum ATHW_FO_Validity {
+       ATHW_FOV_None    =0,
+       ATHW_FOV_Reserved=1,
+       ATHW_FOV_Relative=2,
+       ATHW_FOV_Absolute=3,
+       };
+static const size_t ATHW_FO_Validity_sizes[]={
+       0,      /* ATHW_FOV_None */
+       0,      /* ATHW_FOV_Reserved - not valid */
+       1,      /* ATHW_FOV_Relative */
+       7,      /* ATHW_FOV_Absolute */
+       };
+
+/* RETURN: Success of recognizing "SMSstat"
+ */
+static void ATHW_FOtoSMS(GSM_SMSMessage *SMS,enum ATHW_FO_Validity *validityp,unsigned char fo)
+{
+       switch (fo&0x03) {
+               case 0: SMS->Type=GST_MT; break;
+               case 1: SMS->Type=GST_MO; break;
+               default: /* FIXME: value 2 and 3? */
+               }
+
+       /* bit 2 - reject duplices - not supported by Gnokii
+        */
+
+       if (validityp)
+               *validityp=(enum ATHW_FO_Validity)((fo>>3)&0x03);
+
+       /* bit 5 - status report request - not supported by Gnokii
+        */
+
+       SMS->UDHPresent=!!(fo&(1<<6));
+
+       SMS->ReplyViaSameSMSC=!!(fo&(1<<7));
+}
+
+static GSM_Error ATHW_RX_Patrol_CMGR_CMGF0_core(char *after,char *messagetext,char *end)
+{
+unsigned char pdu[GNOKII_MAX_PDU_LENGTH],*pduend,*s;
+char *oada;
+size_t oada_size,udl;
+enum ATHW_FO_Validity FO_validity;
+unsigned udbits;       /* 7 or 8 bits in UserData */
+
+       if (end-messagetext>2*sizeof(pdu))
+               return(GE_INTERNALERROR);       /* input PDU hex too long! */
+       if (!(pduend=SMS_BlockFromHex(pdu,messagetext,end-messagetext/*len*/)))
+               return(GE_INTERNALERROR);
+       s=pdu;
+       if (ATHW_CurrentSMSCPrefix) {
+               if (s >= pduend
+                       || *s < 1/*type*/
+                       || *s > 1/*type*/
+                               + ( sizeof(CurrentSMSMessage->MessageCenter.Number)
+                               -1/*'\0' termination*/
+                               +1 )/2/*bytes of rounded-up nibbles*/
+                       || s +1/*type*/ +((*s)+1)/2/*bytes of rounded-up nibbles*/ > pduend     /* whole SMSC.Number must fit */
+                       )
+                       return(GE_INTERNALERROR);
+               if (!(s=SemiOctetUnpack(CurrentSMSMessage->MessageCenter.Number,sizeof(CurrentSMSMessage->MessageCenter.Number),
+                               s+1,2*((*s) -1/*type*/ ))))
+                       return(GE_INTERNALERROR);               /* invalid SMSCenter */
+               }
+       if (s +1/*<fo>*/ +1/*received number length*/ > pduend)
+               return(GE_INTERNALERROR);
+       ATHW_FOtoSMS(CurrentSMSMessage,&FO_validity,*s++);
+
+       switch (CurrentSMSMessage->Type) {
+
+       case GST_MT:
+               oada     =CurrentSMSMessage->Sender;
+               oada_size=sizeof(CurrentSMSMessage->Sender);
+               break;
+
+       case GST_MO:
+               oada     =CurrentSMSMessage->Destination;
+               oada_size=sizeof(CurrentSMSMessage->Destination);
+               
+               /* message reference is really not interesting to be read */
+               if (s +1/*<mr>*/ > pduend)
+                       return(GE_INTERNALERROR);
+               s++;
+
+               break;
+
+       /* FIXME: GST_DR - Delivery Report - not supported!
+        */
+       default:
+               /* Unable to skip OA/DA, parsing would fail
+                */
+               return(GE_INTERNALERROR);
+       }
+
+       if (oada) {
+               if (s + 1/*type*/ +((*s)+1)/2/*bytes of rounded-up nibbles*/ > pduend)
+                       return(GE_INTERNALERROR);
+               if (!(s=SemiOctetUnpack(oada,oada_size,s+1,(*s)/*nibbles*/)))
+                       return(GE_INTERNALERROR);               /* invalid OA/DA */
+               }
+
+       if (s +1/*<pid>*/ +1/*<dcs>*/ > pduend)
+               return(GE_INTERNALERROR);
+
+       /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
+       CurrentSMSMessage->MessageCenter.Format=*s++;   /* <pid> */
+
+       ATHW_DCStoSMS(CurrentSMSMessage,*s++);          /* <dcs> */
+
+       switch (CurrentSMSMessage->Type) {
+
+       case GST_MT:            /* SCTS field */
+               if (s +7/*scts*/ > pduend)
+                       return(GE_INTERNALERROR);
+               if (!ATHW_SCTStoSMS_CMGF0(CurrentSMSMessage,s))
+                       return(GE_INTERNALERROR);
+               s+=7;
+               break;
+
+       case GST_MO:            /* VP field */
+               if (s +ATHW_FO_Validity_sizes[(unsigned)FO_validity] > pduend)
+                       return(GE_INTERNALERROR);
+               switch (FO_validity) {
+               case ATHW_FOV_None:
+                       break;
+               case ATHW_FOV_Reserved:
+                       return(GE_INTERNALERROR);       /* not supported */
+               case ATHW_FOV_Relative:
+                       CurrentSMSMessage->Validity=SMS_VP_to_Validity((GSM_SMSMessageValidity)*s);
+                       break;
+               case ATHW_FOV_Absolute:
+                       /* leave default, not supported by Gnokii */
+                       break;
+               }
+               s+=ATHW_FO_Validity_sizes[(unsigned)FO_validity];
+               break;
+
+       default:
+               /* To be written when other types get supported in the above switch
+                */
+       }
+
+       if (s +1/*udl*/ > pduend)
+               return(GE_INTERNALERROR);
+
+       /* We are pretty strict as UDL must exactly match the end of SMS
+        * as the is the primary check for the good guess of SMSCenter presentness
+        * during guessing by ATHW_RX_Patrol_CMGR_CMGF0_retrier().
+        */
+       udl=(*s++);
+       udbits=(CurrentSMSMessage->EightBit ? 8 : 7);
+       if (s +( udl*udbits +7/*round-up*/ )/8 != pduend)
+               return(GE_INTERNALERROR);
+
+       if (CurrentSMSMessage->UDHPresent) {
+               if (0
+                               || s +1/*sizeof(UDH[0])*/ > pduend
+                               || s +1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/ > pduend
+                               ||    1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/ > sizeof(CurrentSMSMessage->UDH)
+                               )
+                       return(GE_INTERNALERROR);
+               memcpy(CurrentSMSMessage->UDH,s, 1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/ );
+               s+=1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/;
+               udl-=(( 1/*sizeof(UDH[0])*/ +CurrentSMSMessage->UDH[0] )*8 +0/*round-down*/ )/udbits;
+               }
+
+       if (udl > (sizeof(CurrentSMSMessage->MessageText) -1/*terminating '\0'*/))
+               return(GE_INTERNALERROR);
+       CurrentSMSMessage->MessageTextLength=udl;
+       CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength]='\0';
+
+       if (udbits==8) {
+               memcpy(CurrentSMSMessage->MessageText,s,udl);
+               }
+       else {  /* udhbits==7 */
+               UnpackEightBitsToSeven((7-(!CurrentSMSMessage->UDHPresent ? 0 : 1+CurrentSMSMessage->UDH[0]))%7,        /* offset */
+                       pduend-s,       /*  in_length */
+                       udl,            /* out_length */
+                       s,              /* input */
+                       CurrentSMSMessage->MessageText  /* output */
+                       );
+               CurrentSMSMessage->MessageText[udl]='\0';
+               }
+
+       return(GE_NONE);
+}
+
+/* This is just a variant of ATHW_SMS_CMGF0(), I was too lazy to generalize it
+ */
+static GSM_Error ATHW_RX_Patrol_CMGR_CMGF0_retrier(char *after,char *messagetext,char *end)
+{
+GSM_Error err,err_first=GE_INTERNALERROR/*shut up GCC*/;
+int retry;
+
+       for (retry=0;retry<2;retry++) {
+               /* There is no need to give up trying decoding (=> retried>0 )
+                * by both ways as it does't cost us anything (not as in ATHW_SMS_CMGF0()).
+                */
+               if (GE_NONE==(err=ATHW_RX_Patrol_CMGR_CMGF0_core(after,messagetext,end),1/*retried*/)) {
+                       ATHW_CurrentSMSCPrefix_force=true;
+                       return(err);
+                       }
+               if (ATHW_CurrentSMSCPrefix_force)
+                       return(err);
+               if (!retry)
+                       err_first=err;
+               ATHW_CurrentSMSCPrefix=!ATHW_CurrentSMSCPrefix;
+               }
+       /* Return rather the first error code as it is more probable that
+        * it was generated with valid ATHW_CurrentSMSCPrefix setting
+        */
+       return(err_first);
+}
+
+/* +CMGR message reading will be finalized by "\nOK\n" which will terminate wait_on().
+ * But invalid message data followed by "\nOK\n" are still invalid so we have to indicate it.
+ */
+static void ATHW_RX_Patrol_CMGR_CMGF0(char *after,char *messagetext,char *end)
+{
+GSM_Error err;
+
+       err=ATHW_RX_Patrol_CMGR_CMGF0_retrier(after,messagetext,end);
+       if (err!=GE_NONE)
+               CurrentSMSMessageError=err;
+}
+
+/* +CMGR: (+CMGF==1)
+ *     CurrentSMSMessage->Status      = 1st arg <stat>
+ *     CurrentSMSMessage->Sender      = 2nd arg <oa>
+ *     CurrentSMSMessage->Destination = 2nd arg <da>
+ *     ...etc., see the code below
+ *
+ *     Type==GST_MT:
+ *             +CMGR: <stat>,<oa>,<alpha>,<scts>,<tooa>,<fo>,<pid>,<dcs>     ,<sca>,<tosca>,<length>
+ *                    0      1    2       3      4      5    6     7          8     9       10
+ *     Type==GSM_MO:
+ *             +CMGR: <stat>,<da>,<alpha>       ,<toda>,<fo>,<pid>,<dcs>,<vp>,<sca>,<tosca>,<length>
+ *                    0      1    2              3      4    5     6     7    8     9       10
+ */
+static void ATHW_RX_Patrol_CMGR_CMGF1(char *after,char *messagetext,char *end)
+{
+char buf[32];
+int fieldno_fo=-1,fieldno_dcs=-1;
+long l;
+
+       switch (CurrentSMSMessage->Type) {
+
+       case GST_MT:
+               ATHW_EXTRACTSTRING(CurrentSMSMessage->Sender,after/*src*/,1/*element_no*/);
+
+               /* <scts> Service centre time stamp string format "01/10/25,02:28:18+08"
+                * Note: not present up to Nokia 9110i
+                */
+               ATHW_EXTRACTSTRING(buf                      ,after/*src*/,3/*element_no*/);
+               ATHW_SCTStoSMS_CMGF1(CurrentSMSMessage,buf/*scts*/);    /* error ignored, leave the default value */
+
+               fieldno_fo=5;
+               fieldno_dcs=7;
+               break;
+
+       case GST_MO:
+               ATHW_EXTRACTSTRING(CurrentSMSMessage->Destination,after/*src*/,1/*element_no*/);
+               fieldno_fo=4;
+               fieldno_dcs=6;
+
+               l=ATHW_ExtractNumber(after/*src*/,7/*element_no*/);
+               if (l>=0 && l<0x100)
+                       CurrentSMSMessage->Validity=SMS_VP_to_Validity((GSM_SMSMessageValidity)l);
+               break;
+
+       default:
+               /* Not supported */
+       }
+
+       if (fieldno_fo>=0) {
+               l=ATHW_ExtractNumber(after/*src*/,fieldno_fo/*element_no*/);
+               if (l>=0 && l<0x100)
+                       ATHW_FOtoSMS(CurrentSMSMessage,
+                                       NULL,   /* validityp - solved by GSM device in +CMGF==1 */
+                                       l);
+               }
+
+       if (fieldno_dcs>=0) {
+               l=ATHW_ExtractNumber(after/*src*/,fieldno_dcs/*element_no*/);
+               if (l>=0 && l<0x100)
+                       ATHW_DCStoSMS(CurrentSMSMessage,l);
+               }
+
+       ATHW_EXTRACTSTRING(CurrentSMSMessage->MessageCenter.Number,after/*src*/,8/*element_no*/);
+
+       CurrentSMSMessage->MessageTextLength=GNOKII_MIN(end-messagetext,sizeof(CurrentSMSMessage->MessageText)-1);
+       memcpy(CurrentSMSMessage->MessageText,messagetext,CurrentSMSMessage->MessageTextLength);
+       CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength]='\0';
+}
+
+/* +CMGR:
+ *     CurrentSMSMessage->Type        = 1st arg <stat>
+ */
+static char *ATHW_RX_Patrol_CMGR(char *after)
+{
+char buf[32],*end,*messagetext;
+
+       end=strchr(after,'\n');
+       if (!end++)
+               return(after);          /* assert, INTERNAL! */
+       if (!*end)
+               return(NULL);           /* need data */
+       if (*end=='\n')                 /* 2nd optional '\n' */
+               end++;                  /* '\n' should be really double as it was "\r\n" from the device */
+       messagetext=end;
+       if (!(end=strchr(end,'\n')))
+               return(NULL);           /* need data */
+       /* Now we have read "+CMGR:*\n[\n]*\n" */
+
+       /* 1st arg <stat> is numeric(+CMGF==0)/alpha(+CMGF==1), ATHW_StattoSMS parses it all
+        */
+       ATHW_EXTRACTSTRING(buf,after/*src*/,0/*element_no*/);
+       ATHW_StattoSMS(CurrentSMSMessage,buf);                  /* error ignored: what to do if unknown? */;
+
+       switch (ATHW_CurrentCMGF) {
+               case 0: ATHW_RX_Patrol_CMGR_CMGF0(after,messagetext,end); break;
+               case 1: ATHW_RX_Patrol_CMGR_CMGF1(after,messagetext,end); break;
+               }
+
+       return(++end);  /* eat the whole SMS block, INCLUDING the trailing '\n' */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGR_struct=
+       { "\n+CMGR:", ATHW_RX_Patrol_CMGR };
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_SiemensMGR_struct=
+       { "\n^SMGR:", ATHW_RX_Patrol_CMGR };
+
+/* Currently we always set the first two memory types as the syntax with empty
+ * preceding elements is not supported at least by Nokia 9110i & Siemens M20.
+ * Ideal case would be to first query the state by AT+CPMS? and then the settings
+ * would be written back.
+ * We never need to set the third argument, for example on Nokia 9110i it even isn't
+ * supported for "SM" memory type!
+ */
+static GSM_Error ATHW_SMS_SelectMemoryType(GSM_SMSMessage *SMS)
+{
+const char *atmemtype;
+const char *atmemtype_quoted;
+
+       ATHW_GETMEMORYTYPE(atmemtype,SMS->MemoryType);
+       atmemtype_quoted=ATHW_Enquote(atmemtype);
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               "AT+CPMS=%s,%s\r",atmemtype_quoted,atmemtype_quoted);
+
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_GetSMS(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentSMSMessage = data->SMSMessage;
+
+       ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage)); /* select <mem1> - reading/deleting */
+
+       ATHW_DateTimeSetCurrent(&CurrentSMSMessage->Time);              /* not present up to Nokia 9110i */
+       ATHW_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime);          /* not present up to Nokia 9110i */
+       CurrentSMSMessage->MessageTextLength=0;                         /* default */
+       CurrentSMSMessage->Validity=72/*hours*/*60;                     /* default */
+       CurrentSMSMessage->UDHPresent=false;                            /* default */
+       CurrentSMSMessage->MessageText[0]='\0';                         /* default */
+
+       ATHW_MessageCenterClear(&CurrentSMSMessage->MessageCenter);     /* default */
+       CurrentSMSMessage->MessageCenter.No=0;                          /* default - input for GetSMSCenter */
+
+       CurrentSMSMessage->Sender[0]='\0';                              /* default */
+       CurrentSMSMessage->Destination[0]='\0';                         /* default */
+       CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location;   /* default */
+       /* CurrentSMSMessage->MemoryType is input argument */
+       CurrentSMSMessage->Type=GST_UN;                                 /* default, detection of EMPTY SMSes! */
+       CurrentSMSMessage->Status=GSS_SENTREAD;                         /* default */
+       CurrentSMSMessage->Class=1;                                     /* default */
+       CurrentSMSMessage->EightBit=false;                              /* default */
+       CurrentSMSMessage->Compression=false;                           /* default */
+       /* CurrentSMSMessage->Location is input argument */
+       CurrentSMSMessage->ReplyViaSameSMSC=false;                      /* default */
+
+       /* We may now read "unread" SMS which will change its status to "read".
+        * We don't know its current state so we don't know whether the number
+        * of "unread" SMSes will change.
+        */
+       if (!ATHW_HaveSiemensMGR)
+               ATHW_CNMI_count=-1;
+
+       ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,
+               (ATHW_HaveSiemensMGR ? &ATHW_RX_Patrol_SiemensMGR_struct : &ATHW_RX_Patrol_CMGR_struct)/*patrol*/,
+               (ATHW_HaveSiemensMGR ? "AT^SMGR=%d\r" : "AT+CMGR=%d\r"),
+               CurrentSMSMessage->Location);
+       if (GE_NONE!=wait_on(&CurrentSMSMessageError,60/*timeout*/)) {
+               if (ATHW_CMS_INVALID_MEMORY_INDEX!=ATHW_RX_Patrol_CMS_ERROR_code)
+                       return(CurrentSMSMessageError);
+               return(GE_EMPTYSMSLOCATION);
+               }
+       if (CurrentSMSMessage->Type==GST_UN)    /* Empty slot with "\nOK\n" response detection */
+               return(GE_EMPTYSMSLOCATION);
+
+       return(GE_NONE);
+}
+
+static GSM_Error ATHW_DeleteSMS(GSM_Data *data, GSM_Statemachine *state)
+{
+        CurrentSMSMessage = data->SMSMessage;
+
+       ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage));         /* select <mem1> - reading/deleting */
+
+       ATHW_CNMI_count=-1;
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,30/*timeout*/,NULL/*patrol*/,
+               "AT+CMGD=%d\r",CurrentSMSMessage->Location);
+
+       return(GE_NONE);
+}
+
+static const struct ATHW_RX_Patrol *ATHW_RX_Patrol_GT_SPACE_patrol_after;
+
+/* RETURNS: "true" if successful
+ */
+static bool WRITEPHONE_hex(unsigned char *buf,size_t buflen)
+{
+char *hex;
+size_t hexsize;                /* should be ==2*buflen */
+size_t got;
+
+       if (!(hex=malloc(2*buflen)))
+               return(false);
+       hexsize=(SMS_BlockToHex(hex,buf,buflen)-hex);
+       got=WRITEPHONE(PortFD,hex,hexsize);
+       free(hex);
+
+       if (hexsize!=got)
+               return(false);
+       return(true);
+}
+
+static char *ATHW_RX_Patrol_GT_SPACE(char *after)
+{
+       switch (ATHW_CurrentCMGF) {
+
+       case 0:
+               if (!WRITEPHONE_hex(CurrentSMSMessagePDU,CurrentSMSMessagePDU_size))
+                       goto fail;
+               break;
+
+       case 1:
+               if (CurrentSMSMessage->UDHPresent) {    /* ATHW_CMGS_CMGF1_8bit_HaveBinHex==true assumed */
+                       if (!WRITEPHONE_hex(CurrentSMSMessage->UDH,1+CurrentSMSMessage->UDH[0]))
+                               goto fail;
+                       }
+               if (CurrentSMSMessage->EightBit && ATHW_CMGS_CMGF1_8bit_HaveBinHex) {
+                       if (!WRITEPHONE_hex(   CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength))
+                               goto fail;
+                       }
+               else {
+                       if (CurrentSMSMessage->MessageTextLength!=WRITEPHONE(PortFD,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength))
+                               goto fail;
+                       }
+               break;
+       }
+
+       if (1  !=WRITEPHONE(PortFD,"\x1A"/*CTRL-Z*/,1)) {
+fail:
+               CurrentSMSMessageError=GE_INTERNALERROR;
+               /* FALLTHRU to exit path */
+               }
+
+       /* We may be actually doing +CMGS instead of +CMGW but we do not yet
+        * need to patrol any output from +CMGS so don't bother its distinguishing now.
+        */
+       ATHW_RX_Patrol_Current=ATHW_RX_Patrol_GT_SPACE_patrol_after;
+
+       /* We just cannot return "after" as it would mean "eat line".
+        * Just leaving there an additional '\n' is pretty harmless.
+        */
+       after[-1]='\n';
+       return(after-1);
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_GT_SPACE_struct=
+       { "\n> ",ATHW_RX_Patrol_GT_SPACE };
+
+static char *ATHW_SMS_TypeToSenderOrDestination(GSM_SMSMessage *SMS)
+{
+       switch (CurrentSMSMessage->Type) {
+               case GST_MO:
+                       return(SMS->Destination);
+               case GST_MT:
+                       return(SMS->Sender);
+               default:
+                       return(NULL);
+               }
+       /* NOTREACHED */
+}
+
+/* The second argument is the size of the data in octets,
+ * excluding User Data Header - important only for 8bit data
+ * Ooops, we cannot share the PDU generating code with Gnokii FBUS code
+ * as FBUS doesn't use PDU format in its SMS-Send packet!
+ */
+static GSM_Error ATHW_SendSMS_CMGF0_core(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt) G_GNUC_PRINTF(3,0);
+static GSM_Error ATHW_SendSMS_CMGF0_core(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt)
+{
+unsigned char pdu[GNOKII_MAX_PDU_LENGTH];
+unsigned char *d,*pdustart,*bodystart,*pdudatalengthp;
+int i;
+char *SMSsenderdestination;
+
+       d=pdu;
+
+       if (ATHW_CurrentSMSCPrefix) {
+
+               /* We should get SMSC number */
+               if (CurrentSMSMessage->MessageCenter.No) {
+GSM_Data data_messagecenter;
+
+                       data_messagecenter.MessageCenter=&CurrentSMSMessage->MessageCenter;
+                       ATHW_ERR_WRAPPER(ATHW_GetSMSCenter(&data_messagecenter,state));
+                       CurrentSMSMessage->MessageCenter.No = 0;
+                       }
+               if (!*CurrentSMSMessage->MessageCenter.Number)
+                       *d++=0x00;      /* total SMSC length */
+               else {
+                       i=SemiOctetPack(CurrentSMSMessage->MessageCenter.Number,d+1);
+                       *d=               1/*type*/ + (i+1)/2/*rounded-up bytes of nibbles*/ ;
+                       d+= 1/*length*/ + 1/*type*/ + (i+1)/2/*rounded-up bytes of nibbles*/ ;
+                       }
+               }
+       /* We MUST NOT count SMSCenter length into +CMGS=%d count argument
+        */
+       pdustart=d;
+
+       *d++=ATHW_SMStoFO(CurrentSMSMessage);   /* <fo>: First Octet of SMS-SUBMIT */
+       *d++=ATHW_PDU_MR_DEFAULT;               /* Message Reference */
+       SMSsenderdestination=ATHW_SMS_TypeToSenderOrDestination(CurrentSMSMessage);
+       i=SemiOctetPack((!SMSsenderdestination ? "" : CurrentSMSMessage->Destination),d+1);
+       *d= i/*nibbles*/ ;
+       d+= 1/*length*/ + 1/*type*/ + (i+1)/2/*rounded-up bytes of nibbles*/ ;
+       *d++=(unsigned char)CurrentSMSMessage->MessageCenter.Format;
+       *d++=ATHW_SMStoDCS(CurrentSMSMessage);
+       *d++=SMS_Validity_to_VP(CurrentSMSMessage->Validity);
+       pdudatalengthp=d++;
+
+       bodystart=d;
+       if (CurrentSMSMessage->UDHPresent) {
+size_t UDHlen=1+CurrentSMSMessage->UDH[0];
+
+               memcpy(d,CurrentSMSMessage->UDH,UDHlen);
+               d+=UDHlen;
+               }
+       if (CurrentSMSMessage->EightBit) {
+               memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
+               d+=CurrentSMSMessage->MessageTextLength;
+       } else {
+size_t byteslen=PackSevenBitsToEight(
+                       /* check it out yourself, really the number of used bits for UDH header on the start
+                        * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
+                        */
+                       (7-(d-bodystart))%7,
+                       CurrentSMSMessage->MessageText,d);
+               d+=byteslen;
+       }
+       *pdudatalengthp=((d-bodystart)*8)/(CurrentSMSMessage->EightBit ? 8 : 7);
+
+       CurrentSMSMessagePDU=pdu;
+       CurrentSMSMessagePDU_size=d-pdu;
+
+       ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,150/*timeout*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
+               commandfmt,d-pdustart);
+       ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_post());
+
+       return(GE_NONE);
+}
+
+/* Houston, we have a problem.
+ * Siemens M20 supports +CMGW <stat> specification but on my model it just
+ * reports ERROR (and <stat> is not respected).
+ * Fortunately it will write "+CMGW: <index>\n" before and the message gets written
+ * so we try to ignore ERROR reports during initial probing of <stat> support.
+ */
+static bool ATHW_SaveSMS_StatSupported=true;
+static bool ATHW_SaveSMS_StatSupported_force=false;
+static int  ATHW_SaveSMS_StatSupported_retries=ATHW_SAVESMS_STATSUPPORTED_RETRIES;
+
+static GSM_Error ATHW_SaveSMS_StatSupported_solve(GSM_Error err,int retried)
+{
+       if (ATHW_SaveSMS_StatSupported_force)
+               return(err);
+       if (err==GE_NONE || CurrentSMSMessage->MessageNumber) {
+               ATHW_SaveSMS_StatSupported_force=true;
+               return(GE_NONE);
+               }
+       if (!ATHW_SaveSMS_StatSupported_retries) {
+               ATHW_SaveSMS_StatSupported=false;
+               ATHW_SaveSMS_StatSupported_force=true;
+               return(err);
+               }
+       /* Count only the sending sessions, not each attempt.
+        * Otherwise SMS with not-acceptable for write would
+        * cost us ATHW_CURRENTSMSCPREFIX_RETRIES number of retries
+        * in ATHW_SaveSMS_StatSupported_retries counter!
+        */
+       if (!retried)
+               ATHW_SaveSMS_StatSupported_retries--;
+       return(err);
+}
+
+/* This is just a wrapper for ATHW_CurrentSMSCPrefix retrying, please see the comment
+ * at the definition of this variable.
+ */
+/* This is just a variant of ATHW_RX_Patrol_CMGR_CMGF0(), I was too lazy to generalize it
+ */
+static GSM_Error ATHW_SMS_CMGF0(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt)
+{
+GSM_Error err,err_first=GE_INTERNALERROR/*shut up GCC*/;
+int retry;
+
+       for (retry=0;retry<ATHW_CURRENTSMSCPREFIX_RETRIES;retry++) {
+               if (GE_NONE==(err=ATHW_SaveSMS_StatSupported_solve(ATHW_SendSMS_CMGF0_core(data,state,commandfmt),retry))) {
+                       ATHW_CurrentSMSCPrefix_force=true;
+                       return(err);
+                       }
+               if (ATHW_CurrentSMSCPrefix_force)
+                       return(err);
+               if (!retry)
+                       err_first=err;
+               ATHW_CurrentSMSCPrefix=!ATHW_CurrentSMSCPrefix;
+               }
+       /* Return rather the first error code as it is more probable that
+        * it was generated with valid ATHW_CurrentSMSCPrefix setting
+        */
+       return(err_first);
+}
+
+static GSM_Error ATHW_SendSMS_CMGF0(GSM_Data *data, GSM_Statemachine *state)
+{
+GSM_Error err=ATHW_SMS_CMGF0(data,state,"AT+CMGS=%d\r");
+
+       if (err==GE_NONE)
+               return(GE_SMSSENDOK);
+       return(err);
+}
+
+/* The second argument is the size of the data in octets,
+   excluding User Data Header - important only for 8bit data */
+static GSM_Error ATHW_SendSMS_CMGF1(GSM_Data *data, GSM_Statemachine *state)
+{
+       ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,150/*timeout*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
+               "AT+CMGS=%s\r",ATHW_Enquote(CurrentSMSMessage->Destination));
+       ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_post());
+
+       return(GE_SMSSENDOK);
+}
+
+static char *ATHW_RX_Patrol_CMGS_CMGW(char *after)
+{
+long l;
+
+       l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
+       if (l>=0 && l<=INT_MAX/* SMS->Location is int */)
+               CurrentSMSMessage->MessageNumber=l;
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGS_struct=
+       { "\n+CMGS:", ATHW_RX_Patrol_CMGS_CMGW };
+
+static GSM_Error ATHW_SendSMS(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentSMSMessage = data->SMSMessage;
+       ATHW_RX_Patrol_GT_SPACE_patrol_after=&ATHW_RX_Patrol_CMGS_struct;
+       CurrentSMSMessage->MessageNumber=0;     /* default */
+
+       switch (ATHW_CurrentCMGF) {
+               case 0:  return(ATHW_SendSMS_CMGF0(data,state));
+               case 1:  return(ATHW_SendSMS_CMGF1(data,state));
+               }
+       return(GE_INTERNALERROR);
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGW_struct=
+       { "\n+CMGW:", ATHW_RX_Patrol_CMGS_CMGW };
+
+static GSM_Error ATHW_SaveSMS_CMGF0(GSM_Data *data, GSM_Statemachine *state)
+{
+const char *SMSstat;
+char *commandfmt=NULL; /* just paranoia */
+GSM_Error err;
+
+       SMSstat=(!ATHW_SaveSMS_StatSupported ? NULL : ATHW_SMStoStat(CurrentSMSMessage));
+       gasprintf(&commandfmt,"AT+CMGW=%%d,%s\r",(!SMSstat ? "" : ATHW_Enquote(SMSstat)));
+       if (!commandfmt)
+               return(GE_INTERNALERROR);
+
+       err=ATHW_SMS_CMGF0(data,state,commandfmt);
+       free(commandfmt);
+
+       return(err);
+}
+
+static GSM_Error ATHW_SaveSMS_CMGF1(GSM_Data *data, GSM_Statemachine *state)
+{
+const char *SMSstat;
+char *SMSsenderdestination;
+GSM_Error err;
+
+       SMSstat=(!ATHW_SaveSMS_StatSupported ? NULL : ATHW_SMStoStat(CurrentSMSMessage));
+       SMSsenderdestination=ATHW_SMS_TypeToSenderOrDestination(CurrentSMSMessage);
+
+       ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
+       ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
+               "AT+CMGW=%s,,%s\r",
+               (!SMSsenderdestination ? "" : ATHW_Enquote(SMSsenderdestination)),
+               (!SMSstat ? "" : ATHW_Enquote(SMSstat)));
+       err=wait_on(&CurrentSMSMessageError,150/*timeout*/);
+       ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_post());
+
+       return(ATHW_SaveSMS_StatSupported_solve(err,0/*retried-no retry session here*/));
+}
+
+static GSM_Error ATHW_SaveSMS(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentSMSMessage = data->SMSMessage;
+       ATHW_RX_Patrol_GT_SPACE_patrol_after=&ATHW_RX_Patrol_CMGW_struct;
+       CurrentSMSMessage->MessageNumber=0;             /* default */
+
+       ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage));         /* select <mem1> - writing */
+
+       switch (ATHW_CurrentCMGF) {
+               case 0:  return(ATHW_SaveSMS_CMGF0(data,state));
+               case 1:  return(ATHW_SaveSMS_CMGF1(data,state));
+               }
+       return(GE_INTERNALERROR);
+}
+
+static GSM_Error ATHW_Reset(GSM_Data *data, GSM_Statemachine *state)
+{
+       ATHW_ERR_WRAPPER(ATHW_PhoneSetup());
+
+       return(GE_NONE);
+}
+
+/* +COPS:
+ *     CurrentNetworkInfo->NetworkCode = 3rd arg <oper>
+ */
+static char *ATHW_RX_Patrol_COPS(char *after)
+{
+long l;
+char *s;
+
+       l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
+       /* check whether numeric operator mode is on */
+       if (l!=2)
+               return(after);  /* eat line */
+
+       ATHW_EXTRACTSTRING(CurrentNetworkInfo->NetworkCode,after/*src*/,2/*element_no*/);
+       if (strlen(CurrentNetworkInfo->NetworkCode)!=5) {
+fail:
+               CurrentNetworkInfo->NetworkCode[0]='\0';
+               return(after);  /* eat line */
+               }
+       for (s=CurrentNetworkInfo->NetworkCode;*s;s++)
+               if (!isdigit(*s))
+                       goto fail;
+       /* network code valid */
+
+       memmove(CurrentNetworkInfo->NetworkCode+3+1,CurrentNetworkInfo->NetworkCode+3,2 +1/*terminating '\0'*/);
+       CurrentNetworkInfo->NetworkCode[3]=' ';
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_COPS_struct=
+       { "\n+COPS:", ATHW_RX_Patrol_COPS };
+
+static void ATHW_RX_Patrol_CREG_hex4valid(char *hex4)
+{
+char *s;
+
+       if (strlen(hex4)!=4) {
+fail:
+               hex4[0]='\0';
+               return;
+               }
+       for (s=hex4;*s;s++)
+               if (!isxdigit(*s))
+                       goto fail;
+}
+
+/* +CREG:
+ *     CurrentNetworkInfo->CellID = 4rd arg <lac>
+ *     CurrentNetworkInfo->LAC    = 3nd arg <cid>
+ */
+static char *ATHW_RX_Patrol_CREG(char *after)
+{
+long l;
+
+       l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
+       /* check whether we don't report stale information
+        */
+       if (l==1/*registered-home network*/ || l==5/*registered-roaming*/)
+               return(after);  /* eat line */
+
+       ATHW_EXTRACTSTRING(           CurrentNetworkInfo->CellID,after/*src*/,3/*element_no*/);
+       ATHW_RX_Patrol_CREG_hex4valid(CurrentNetworkInfo->CellID);
+       ATHW_EXTRACTSTRING(           CurrentNetworkInfo->LAC   ,after/*src*/,2/*element_no*/);
+       ATHW_RX_Patrol_CREG_hex4valid(CurrentNetworkInfo->LAC   );
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CREG_struct=
+       { "\n+CREG:", ATHW_RX_Patrol_CREG };
+
+static GSM_Error ATHW_GetNetworkInfo(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentNetworkInfo=data->NetworkInfo;
+
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentNetworkInfoError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_COPS_struct/*patrol*/,
+               "AT+COPS?\r");
+       ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentNetworkInfoError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CREG_struct/*patrol*/,
+               "AT+CREG?\r");
+
+       /* When no information was gather we will rather report failure
+        */
+       if (1
+               && !*CurrentNetworkInfo->NetworkCode
+               && !*CurrentNetworkInfo->CellID
+               && !*CurrentNetworkInfo->LAC
+               )
+               return(GE_INTERNALERROR);
+       
+
+       return(GE_NONE);
+}
+
+
+#ifndef WIN32
+
+/* Called by initialisation code to open comm port in asynchronous mode. */
+
+static bool ATHW_OpenSerial(void)
+{
+       int result;
+  
+#if __unices__
+       int rtn;
+#else
+       struct sigaction sig_io;
+
+       /* Set up and install handler before enabling async IO on port. */
+
+       sig_io.sa_handler = ATHW_SigHandler;
+       sig_io.sa_flags = 0;
+       sigaction (SIGIO, &sig_io, NULL);
+#endif
+
+       /* Open device. */
+
+       result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, GCT_Serial);
+
+       if (!result) {
+               perror(_("Couldn't open AT device"));
+               return false;
+       }
+
+#if __unices__
+       /* create a thread to handle incoming data from mobile phone */
+       rtn = pthread_create(&selThread, NULL, (void*)ATHW_SelectLoop, (void*)NULL);
+       if (rtn != 0) return false;
+#endif
+
+       /* device_changespeed() not needed as device_open() now automatically
+        * sets the user-specified (or default) speed.
+        */
+       return (true);
+}
+
+static void ATHW_SigHandler(int status)
+{
+       unsigned char buffer[255];
+       int count, res;
+       res = device_read(buffer, 255);
+       for (count = 0; count < res ; count ++)
+               ATHW_RX_Char(buffer[count]);
+}
+#endif /* WIN32 */
+
+static char *ATHW_RX_Patrol_OK(char *after)
+{
+       /* Some patrol may have already indicated some error!
+        */
+       if (ATHW_CatchBufferErrorP && *ATHW_CatchBufferErrorP==GE_BUSY)
+               *ATHW_CatchBufferErrorP=GE_NONE;
+
+       return(after);  /* eat line */
+}
+
+static char *ATHW_RX_Patrol_ERROR(char *after)
+{
+       if (ATHW_CatchBufferErrorP)
+               *ATHW_CatchBufferErrorP=GE_INTERNALERROR;
+
+       return(after);  /* eat line */
+}
+
+/* We can't kick the user with anything else */
+#define ATHW_RX_Patrol_NO_CARRIER  ATHW_RX_Patrol_ERROR
+#define ATHW_RX_Patrol_DELAYED     ATHW_RX_Patrol_ERROR
+#define ATHW_RX_Patrol_NO_DIALTONE ATHW_RX_Patrol_ERROR
+#define ATHW_RX_Patrol_BUSY        ATHW_RX_Patrol_ERROR
+
+static char *ATHW_RX_Patrol_CMX_ERROR(char *after,long *errorp)
+{
+char *end=NULL;
+long l;
+
+       while (*after==' ') after++;
+
+       l=strtol(after,&end,10);
+       if (*after && l>=0 && l<LONG_MAX && end && *end=='\n')
+               *errorp=l;
+
+       if (ATHW_CatchBufferErrorP)
+               *ATHW_CatchBufferErrorP=GE_INTERNALERROR;
+
+       return(after);  /* eat line */
+}
+
+static char *ATHW_RX_Patrol_CME_ERROR(char *after)
+{
+       return(ATHW_RX_Patrol_CMX_ERROR(after,&ATHW_RX_Patrol_CME_ERROR_code));
+}
+
+static void ATHW_RX_Patrol_CME_ERROR_reset(void)
+{
+       ATHW_RX_Patrol_CME_ERROR_code=-1;
+}
+
+static char *ATHW_RX_Patrol_CMS_ERROR(char *after)
+{
+       return(ATHW_RX_Patrol_CMX_ERROR(after,&ATHW_RX_Patrol_CMS_ERROR_code));
+}
+
+static void ATHW_RX_Patrol_CMS_ERROR_reset(void)
+{
+       ATHW_RX_Patrol_CMS_ERROR_code=-1;
+}
+
+static char *ATHW_RX_Patrol_CRING_VOICE(char *after)
+{
+       if (CallPassup)
+               (*CallPassup)('V');
+
+       return(after);  /* eat line */
+}
+
+static char *ATHW_RX_Patrol_CMTI(char *after)
+{
+#ifdef ATHW_DEBUG
+       printf("ATHW_RX_Patrol_CMTI: ATHW_CNMI_count==%d\n",ATHW_CNMI_count);
+#endif
+       
+       if (ATHW_CNMI_count>=0) {
+               ATHW_CNMI_count++;
+#ifdef ATHW_DEBUG
+               printf("ATHW_CNMI_count increased to %d\n",ATHW_CNMI_count);
+#endif
+               }
+
+       return(after);  /* eat line */
+}
+
+static const struct ATHW_RX_Patrol ATHW_RX_Patrols[]={
+       { "\nOK\n"           ,ATHW_RX_Patrol_OK },
+       { "\nERROR\n"        ,ATHW_RX_Patrol_ERROR },
+       { "\nNO CARRIER\n"   ,ATHW_RX_Patrol_NO_CARRIER },
+       { "\nDELAYED\n"      ,ATHW_RX_Patrol_DELAYED },
+       { "\nNO DIALTONE\n"  ,ATHW_RX_Patrol_NO_DIALTONE },
+       { "\nBUSY\n"         ,ATHW_RX_Patrol_BUSY },
+       { "\n+CME ERROR:"    ,ATHW_RX_Patrol_CME_ERROR, ATHW_RX_Patrol_CME_ERROR_reset },
+       { "\n+CMS ERROR:"    ,ATHW_RX_Patrol_CMS_ERROR, ATHW_RX_Patrol_CMS_ERROR_reset },
+       { "\n+CRING: VOICE\n",ATHW_RX_Patrol_CRING_VOICE },
+       { "\n+CMTI:"         ,ATHW_RX_Patrol_CMTI },
+       };
+
+static void ATHW_CatchBufferReset(void)
+{
+       ATHW_CatchBufferPtr=ATHW_CatchBuffer;
+       *ATHW_CatchBufferPtr='\0';
+       ATHW_CatchBufferMarker=NULL;
+}
+
+static void ATHW_CatchBufferMarkStart(GSM_Error *errorcodep)
+{
+static GSM_Error err_trashcan;
+const struct ATHW_RX_Patrol *patrol;
+
+       if (!errorcodep)
+               errorcodep=&err_trashcan;
+       ATHW_CatchBufferErrorP=errorcodep;
+
+       ATHW_CatchBufferMarker=ATHW_CatchBufferPtr;
+
+       for (patrol=ATHW_RX_Patrols;patrol<ATHW_RX_Patrols+ARRAY_LEN(ATHW_RX_Patrols);patrol++)
+               if (patrol->reset)
+                       (*patrol->reset)();
+}
+
+static const struct ATHW_RX_Patrol *ATHW_RX_Char_EvalPatrol_best;
+static char                        *ATHW_RX_Char_EvalPatrol_best_found;
+
+static void ATHW_RX_Char_EvalPatrol(const struct ATHW_RX_Patrol *patrol)
+{
+char *found,*foundend;
+
+       if (!patrol)
+               return;
+
+       if (!(found=strstr(ATHW_CatchBuffer,patrol->buoy)))
+               return;
+
+       /* When the buoy doesn't end with '\n' we need to find some '\n' after it.
+        * Otherwise the whole line hasn't been read yet and we to yet wait.
+        * It means that we doesn't support Patrol which would catch an incomplete line
+        *  - the only exception are the explicite checks for functions like "ATHW_RX_Patrol_GT_SPACE_struct" :-(
+        */
+       foundend=found+strlen(patrol->buoy);
+       if (foundend<=found)            /* assert */
+               return;
+       if (1   /* FIXME: This list is an ugly solution... */
+               && patrol!=&ATHW_RX_Patrol_GT_SPACE_struct
+               && patrol!=&ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct
+               && patrol!=&ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct
+
+                       && foundend>found && foundend[-1]!='\n' && !strchr(foundend,'\n'))
+               return;         /* no whole line has been read yet */
+
+       if (!ATHW_RX_Char_EvalPatrol_best || ATHW_RX_Char_EvalPatrol_best_found>found) {
+               ATHW_RX_Char_EvalPatrol_best=patrol;
+               ATHW_RX_Char_EvalPatrol_best_found=found;
+               }
+}
+
+
+/* RX_State machine for receive handling.  Called once for each character
+   received from the phone/phone. */
+
+static void ATHW_RX_Char(char rx_byte)
+{
+size_t offset;
+#ifdef ATHW_DEBUG
+#if 0
+       dprintf(_("Received character '%c' (0x%02X)\n"),
+               (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
+#endif
+       putchar(rx_byte);
+#endif
+
+/* We try to keep back 1/2 of buffer data, ineffectively shifting it up when the buffer
+ * gets filled up.
+ */
+       if (ATHW_CatchBufferPtr>=ATHW_CatchBuffer+sizeof(ATHW_CatchBuffer) -1/*Terminating '\0'*/ ) {
+char *movefrom;
+
+#ifdef ATHW_DEBUG
+               dprintf(_("Shifting buffer:\n%s\n__END__\n"),ATHW_CatchBuffer);
+#endif
+               movefrom=ATHW_CatchBuffer+(sizeof(ATHW_CatchBuffer)/2);
+               if (1           && ATHW_CatchBufferMarker
+                               && ATHW_CatchBufferMarker>ATHW_CatchBuffer
+                               && ATHW_CatchBufferMarker<movefrom)
+                       movefrom=ATHW_CatchBufferMarker;
+               offset=ATHW_CatchBuffer+sizeof(ATHW_CatchBuffer)-movefrom;
+               memmove(ATHW_CatchBuffer,movefrom,offset);
+               ATHW_CatchBufferPtr-=offset;
+               if (ATHW_CatchBufferMarker) {
+                       if ((ATHW_CatchBufferMarker-=offset)<ATHW_CatchBuffer)
+                               ATHW_CatchBufferMarker=ATHW_CatchBuffer;
+                       }
+               }
+
+       /* NEVER store '\0' as it would knock-out completely our patrolling system!
+        */
+       if (rx_byte=='\r' || rx_byte=='\0')
+               rx_byte='\n';
+
+       *ATHW_CatchBufferPtr++=rx_byte;
+       *ATHW_CatchBufferPtr='\0';
+
+       for (;;) {
+const struct ATHW_RX_Patrol *patrol;
+char *found,*end,*foundend;
+
+               ATHW_RX_Char_EvalPatrol_best=NULL;
+               for (patrol=ATHW_RX_Patrols;patrol<ATHW_RX_Patrols+ARRAY_LEN(ATHW_RX_Patrols);patrol++) {
+                       ATHW_RX_Char_EvalPatrol(patrol);
+                       }
+               ATHW_RX_Char_EvalPatrol(ATHW_RX_Patrol_Current);
+               if (!ATHW_RX_Char_EvalPatrol_best)
+                       return;
+
+#if 0
+#ifdef ATHW_DEBUG
+               printf("Invoking patrol for: %s\n",ATHW_RX_Char_EvalPatrol_best->buoy);
+#endif
+#endif
+               found=ATHW_RX_Char_EvalPatrol_best_found;
+               foundend=found+strlen(ATHW_RX_Char_EvalPatrol_best->buoy);
+               end=(*ATHW_RX_Char_EvalPatrol_best->func)(foundend);
+
+               if (!end)               /* patrol returned NULL - it is on the track but it needs more data! */
+                       return;
+               if (end==foundend) {    /* they did simple 'return(after);' */
+                       if (foundend[-1]!='\n') {
+                               if (!(end=strchr(foundend,'\n')))
+                                       end=foundend+strlen(foundend);
+                               }
+                       }
+               /* We place '\n' delimited at the *end */
+               memmove(found+1,end,ATHW_CatchBufferPtr+1-end);
+               offset=end-(found+1);
+               ATHW_CatchBufferPtr-=offset;
+               ATHW_CatchBufferPtr[-1]='\n';
+
+               /* Move Marker right behind the squeezed data if we were inside
+                */
+               if (ATHW_CatchBufferMarker>=found) {
+                       if (ATHW_CatchBufferMarker<=end)
+                               ATHW_CatchBufferMarker=ATHW_CatchBufferPtr+1;
+                       else
+                               ATHW_CatchBufferMarker-=offset;
+                       }
+               }
+}
+
+/* Here we initialise model specific functions. */
+
+#define ATHW_FUNCTIONS_ENTRY(name) \
+       case GOP_##name:        return(ATHW_##name(data,state));
+
+static GSM_Error ATHW_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
+{
+static GSM_Statemachine *catcher=NULL;
+
+       if (!catcher || catcher==state)
+               catcher=state;
+       else
+               *((char *)NULL)=1;
+
+       switch (op) {
+               ATHW_FUNCTIONS_ENTRY(Init)
+               ATHW_FUNCTIONS_ENTRY(Terminate)
+               ATHW_FUNCTIONS_ENTRY(GetMemoryStatus)
+               ATHW_FUNCTIONS_ENTRY(ReadPhonebook)
+               ATHW_FUNCTIONS_ENTRY(WritePhonebook)
+               ATHW_FUNCTIONS_ENTRY(GetSMSStatus)
+               ATHW_FUNCTIONS_ENTRY(GetSMSCenter)
+               ATHW_FUNCTIONS_ENTRY(SetSMSCenter)
+               ATHW_FUNCTIONS_ENTRY(GetSMS)
+               ATHW_FUNCTIONS_ENTRY(DeleteSMS)
+               ATHW_FUNCTIONS_ENTRY(SendSMS)
+               ATHW_FUNCTIONS_ENTRY(SaveSMS)
+               ATHW_FUNCTIONS_ENTRY(GetRFLevel)
+               ATHW_FUNCTIONS_ENTRY(GetBatteryLevel)
+               ATHW_FUNCTIONS_ENTRY(GetPowersource)
+               ATHW_FUNCTIONS_ENTRY(GetImei)
+               ATHW_FUNCTIONS_ENTRY(GetRevision)
+               ATHW_FUNCTIONS_ENTRY(GetModel)
+               ATHW_FUNCTIONS_ENTRY(GetManufacturer)
+               ATHW_FUNCTIONS_ENTRY(DialVoice)
+               ATHW_FUNCTIONS_ENTRY(DialData)
+               ATHW_FUNCTIONS_ENTRY(GetIncomingCallNr)
+               ATHW_FUNCTIONS_ENTRY(Reset)
+               ATHW_FUNCTIONS_ENTRY(CancelCall)
+               ATHW_FUNCTIONS_ENTRY(AnswerCall)
+               ATHW_FUNCTIONS_ENTRY(GetNetworkInfo)
+
+               case GOP_Identify: {
+GSM_Error err,r=GE_NONE;
+                       if (GE_NONE!=(err=ATHW_GetImei        (data,state)))
+                               r=err;
+                       if (GE_NONE!=(err=ATHW_GetRevision    (data,state)))
+                               r=err;
+                       if (GE_NONE!=(err=ATHW_GetModel       (data,state)))
+                               r=err;
+                       if (GE_NONE!=(err=ATHW_GetManufacturer(data,state)))
+                               r=err;
+                       return(r);
+                       }
+
+               default:
+                       return(GE_NOTIMPLEMENTED);
+       }
+}
+
+GSM_Phone phone_at_hw = {
+       UNIMPLEMENTED,  /* IncomingFunctions - we don't use default StateMachine */
+       UNIMPLEMENTED,  /* DefaultFunction   - we don't use default StateMachine */
+        /* Mobile phone information */
+       {
+               "AT",                   /* Supported models */
+               31,                     /* Max RF Level (AT+CSQ) */
+               0,                      /* Min RF Level (AT+CSQ) */
+               GRF_CSQ,                /* RF level units */
+               100,                    /* Max Battery Level (AT+CBC) */
+               0,                      /* Min Battery Level (AT+CBC) */
+               GBU_Percentage,         /* Battery level units */
+               GDT_None,               /* Have date/time support */
+               GDT_None,               /* Alarm supports time only */
+               0,                      /* No alarm available */
+               48, 84,                 /* Startup logo size */
+               14, 72,                 /* Op logo size */
+               14, 72,                 /* Caller logo size */
+       },
+       ATHW_Functions,
+};
index 1b18760..16365a2 100644 (file)
   Modified from code by Tim Potter.
 
   $Log$
-  Revision 1.1.1.1  2001/11/25 21:58:58  short
-  :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
-
-  Revision 1.16  2001/11/14 10:46:12  pkot
-  Small cleanup with __unices__
+  Revision 1.1.1.2  2002/04/03 00:07:51  short
+  Found in "gnokii-working" directory, some November-patches version
 
   Revision 1.15  2001/06/10 11:24:57  machek
   Kill "slash star" inside comment.
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#if __unices__
+#  include <strings.h>
+#endif
 #include <ctype.h>
 #include <errno.h>
 
 #include "cfgreader.h"
 
+struct CFG_Header *CFG_Info;
+
 /* Read configuration information from a ".INI" style file */
 struct CFG_Header *CFG_ReadFile(char *filename)
 {
@@ -208,7 +210,7 @@ int CFG_WriteFile(struct CFG_Header *cfg, char *filename)
  * with key or NULL if no such key exists. 
  */
 
-char *CFG_Get(struct CFG_Header *cfg, char *section, char *key)
+char *CFG_Get(struct CFG_Header *cfg, const char *section, const char *key)
 {
         struct CFG_Header *h;
         struct CFG_Entry *e;
@@ -233,6 +235,29 @@ char *CFG_Get(struct CFG_Header *cfg, char *section, char *key)
         return NULL;
 }
 
+/* 
+ * Return all the entries of the fiven section.
+ */
+
+void CFG_GetForeach(struct CFG_Header *cfg, const char *section, CFG_GetForeach_func func)
+{
+        struct CFG_Header *h;
+        struct CFG_Entry *e;
+
+        if ((cfg == NULL) || (section == NULL) || (func == NULL)) {
+                return;
+        }
+
+        /* Search for section name */
+        for (h = cfg; h != NULL; h = h->next) {
+                if (strcmp(section, h->section) == 0) {
+                        /* Search for key within section */
+                        for (e = h->entries; e != NULL; e = e->next)
+                               (*func)(section,e->key,e->value);
+                }
+        }
+}
+
 /*  Set the value of a key in a config file.  Return the new value if
     the section/key can be found, else return NULL.  */
 
@@ -269,12 +294,17 @@ char *CFG_Set(struct CFG_Header *cfg, char *section, char *key,
 int readconfig(char **model, char **port, char **initlength,
                char **connection, char **bindir)
 {
-        struct CFG_Header *cfg_info;
         char *homedir;
         char rcfile[200];
         char *DefaultConnection = "serial";
         char *DefaultBindir     = "/usr/local/sbin/";
 
+       /* I know that it doesn't belong here but currently there is now generic
+        * application init function anywhere.
+        */
+       setvbuf(stdout, NULL, _IONBF, 0);
+       setvbuf(stderr, NULL, _IONBF, 0);
+
 #ifdef WIN32
         homedir = getenv("HOMEDRIVE");
         strncpy(rcfile, homedir ? homedir : "", 200);
@@ -288,34 +318,34 @@ int readconfig(char **model, char **port, char **initlength,
 #endif
 
         /* Try opening .gnokirc from users home directory first */
-        if ((cfg_info = CFG_ReadFile(rcfile)) == NULL) {
+        if ((CFG_Info = CFG_ReadFile(rcfile)) == NULL) {
                 /* It failed so try for /etc/gnokiirc */
-                if ((cfg_info = CFG_ReadFile("/etc/gnokiirc")) == NULL) {
+                if ((CFG_Info = CFG_ReadFile("/etc/gnokiirc")) == NULL) {
                         /* That failed too so exit */
                         fprintf(stderr, _("Couldn't open %s or /etc/gnokiirc. Exiting now...\n"), rcfile);
                         return -1;
                 }
         }
 
-        (char *)*model = CFG_Get(cfg_info, "global", "model");
+        (char *)*model = CFG_Get(CFG_Info, "global", "model");
         if (!*model) {
                 fprintf(stderr, _("Config error - no model specified. Exiting now...\n"));
                 return -2;
         }
 
-        (char *)*port = CFG_Get(cfg_info, "global", "port");
+        (char *)*port = CFG_Get(CFG_Info, "global", "port");
         if (!*port) {
                 fprintf(stderr, _("Config error - no port specified. Exiting now...\n"));
                 return -3;
         }
 
-        (char *)*initlength = CFG_Get(cfg_info, "global", "initlength");
+        (char *)*initlength = CFG_Get(CFG_Info, "global", "initlength");
         if (!*initlength) (char *)*initlength = "default";
 
-        (char *)*connection = CFG_Get(cfg_info, "global", "connection");
+        (char *)*connection = CFG_Get(CFG_Info, "global", "connection");
         if (!*connection) (char *)*connection = DefaultConnection;
 
-        (char *)*bindir = CFG_Get(cfg_info, "global", "bindir");
+        (char *)*bindir = CFG_Get(CFG_Info, "global", "bindir");
         if (!*bindir) (char *)*bindir = DefaultBindir;
 
         return 0;
diff --git a/common/cimd.c b/common/cimd.c
new file mode 100644 (file)
index 0000000..e213170
--- /dev/null
@@ -0,0 +1,1636 @@
+/*
+
+  $Id$
+  
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Copyright (C) 2001 Jan Kratochvil,
+  based on code by Hugh Blemings & Pavel Janík ml.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+  This file provides an API for accessing SMS centers by CIMD and related protococols.
+  See README-CIMD for more details on supported protocols.
+
+  The various routines are prefixed by CIMD.
+
+  $Log$
+  Revision 1.1.1.1  2002/04/03 00:08:03  short
+  Found in "gnokii-working" directory, some November-patches version
+
+
+*/
+
+#define CIMD_DEBUG 1
+
+/* System header files */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#ifdef WIN32
+
+#include <windows.h>
+#include "win32/winserial.h"
+
+#undef IN
+#undef OUT
+
+#define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
+#define sleep(x) Sleep((x) * 1000)
+#define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
+extern HANDLE hPhone;
+
+#else
+
+#define WRITEPHONE(a, b, c) device_write(b, c)
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <errno.h>
+#include "device.h"
+#include "devices/unixserial.h"
+
+#endif
+
+/* Various header file */
+
+#include "config.h"
+#include "misc.h"
+#include "gsm-common.h"
+#include "cfgreader.h"
+
+/* Global variables used by code in gsm-api.c to expose the functions
+   supported by this model of phone. */
+
+
+#if __unices__
+/* fd opened in device.c */
+extern int device_portfd;
+#endif
+
+/* Our private defines */
+
+/* Define if plain (non-Bin) Submit is needed for 7bit messages
+ */
+#define CIMD_SUBMIT_7BIT 1
+
+/* When now catchbuffer was provided and must have some space to decode
+ * OK/ERROR/... result codes.
+ */
+#define CIMD_CATCHBUFFER_LENGTH 0x400
+
+/* +1 of maximum position number of CIMD protocol parameter */
+#define CIMD_PARAMSLOTS (0x10)
+
+/* We assume the 'right one' class is 1 (Mobile Equipment specific) */
+#define DEFAULT_CLASS 1
+
+/* CIMD_Param_Nak_Error codes:
+ */
+#define CIMD_NAK_NO_SMS (0x5001)
+#define CIMD_NAK_KEEPALIVE_REPLY (0x9998)
+#define CIMD_NAK_RESEND (0x9999)
+
+/* Local variables */
+
+#ifndef WIN32
+static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
+#endif
+static bool RequestTerminate;
+
+
+static u8 CIMD_CatchBuffer[CIMD_CATCHBUFFER_LENGTH];
+static u8 *CIMD_CatchBufferPtr=CIMD_CatchBuffer;       /* current destination writing ptr */
+static GSM_Error *CIMD_CatchBufferErrorP;
+
+static void CIMD_CatchBufferStart(GSM_Error *errorcodep);
+
+
+#define CIMD_MARK_START (0x02)
+#define CIMD_MARK_STOP  (0x03)
+#define CIMD_MARK_SEP   (0x09)
+#define CIMD_MARK_SEPS  "\x09"
+
+/* ELF=ELement Format */
+enum CIMD_ELF {
+       CIMD_ELF_HexByte,
+       CIMD_ELF_HexWord,
+       CIMD_ELF_Decimal,
+       CIMD_ELF_StringZ,
+       CIMD_ELF_HexBlock,
+       CIMD_ELF_Empty,
+       };
+
+struct CIMD_Param {
+       u16 code;
+       enum CIMD_ELF elf;
+       };
+
+enum CIMD_Param_SAMPLE {               /* we need some universal enum for typecasting all params */
+       CIMD_Param_SAMPLE_dummy,
+       };
+
+/* CIMD_Cmd_Ack:
+ */
+enum CIMD_Param_Ack {
+       CIMD_Param_Ack_Cmd,
+       CIMD_Param_Ack_Error,
+       CIMD_Param_Ack_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Ack[]={
+       { /* CIMD_Param_Ack_Cmd   */ 1,CIMD_ELF_HexByte },
+       { /* CIMD_Param_Ack_Error */ 2,CIMD_ELF_StringZ },      /* protocol version on Cmd_Login, otherwise 0x0000 */
+       };
+
+/* CIMD_Cmd_Nak:
+ */
+enum CIMD_Param_Nak {
+       CIMD_Param_Nak_Cmd,
+       CIMD_Param_Nak_Error,
+       CIMD_Param_Nak_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Nak[]={
+       { /* CIMD_Param_Nak_Cmd   */ 1,CIMD_ELF_HexByte },
+       { /* CIMD_Param_Nak_Error */ 2,CIMD_ELF_HexWord },
+       };
+
+/* CIMD_Cmd_Login:
+ */
+enum CIMD_Param_Login {
+       CIMD_Param_Login_ID,
+       CIMD_Param_Login_PWD,
+       CIMD_Param_Login_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Login[]={
+       { /* CIMD_Param_Login_ID  */ 1,CIMD_ELF_StringZ },
+       { /* CIMD_Param_Login_PWD */ 2,CIMD_ELF_StringZ },
+       };
+
+/* CIMD_Cmd_Logout:
+ */
+enum CIMD_Param_Logout {
+       CIMD_Param_Logout_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Logout[]={
+       };
+
+/* CIMD_Cmd_Retrieve:
+ */
+enum CIMD_Param_Retrieve {
+       CIMD_Param_Retrieve_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Retrieve[]={
+       };
+
+/* CIMD_Cmd_RetrieveReply:
+ */
+enum CIMD_Param_RetrieveReply {
+       CIMD_Param_RetrieveReply_Destination,
+       CIMD_Param_RetrieveReply_SourceApplication,     /* ??? */
+       CIMD_Param_RetrieveReply_Text,
+       CIMD_Param_RetrieveReply_Timestamp,
+       CIMD_Param_RetrieveReply_Is8bit,                /* protocol version 1.14+, CIMD_Param_DCSEnable_Type_*DCS* reqd */
+       CIMD_Param_RetrieveReply_PID_DCS,               /* protocol version 1.15+, CIMD_Param_DCSEnable_Type_*PID_DCS* reqd */
+       CIMD_Param_RetrieveReply_SPEC,                  /* protocol version 1.16+, CIMD_Param_DCSEnable_Type_*SPEC* reqd */
+       CIMD_Param_RetrieveReply_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_RetrieveReply[]={
+       { /* CIMD_Param_RetrieveReply_Destination       */ 1,CIMD_ELF_StringZ },
+       { /* CIMD_Param_RetrieveReply_SourceApplication */ 2,CIMD_ELF_StringZ },
+       { /* CIMD_Param_RetrieveReply_Text              */ 3,CIMD_ELF_StringZ },
+       { /* CIMD_Param_RetrieveReply_Timestamp         */ 4,CIMD_ELF_StringZ },
+       { /* CIMD_Param_RetrieveReply_Is8bit            */ 5,CIMD_ELF_Decimal },
+       { /* CIMD_Param_RetrieveReply_PID_DCS           */ 6,CIMD_ELF_Decimal }, /* upper=PID, lower=DCS, FIXME: should be HexWord! */
+       { /* CIMD_Param_RetrieveReply_SPEC              */ 7,CIMD_ELF_Decimal }, /* FIXME: should be HexByte! */
+       };
+
+/* CIMD_Cmd_Count:
+ */
+enum CIMD_Param_Count {
+       CIMD_Param_Count_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Count[]={
+       };
+
+/* CIMD_Cmd_CountReply:
+ */
+enum CIMD_Param_CountReply {
+       CIMD_Param_CountReply_Used,             /* Used==NotRead==Slots */
+       CIMD_Param_CountReply_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_CountReply[]={
+       { /* CIMD_Param_CountReply_Used */ 1,CIMD_ELF_Decimal },
+       };
+
+/* CIMD_Cmd_Submit:
+ */
+enum CIMD_Param_Submit {
+       CIMD_Param_Submit_Destination,
+       CIMD_Param_Submit_Text,
+       CIMD_Param_Submit_ValidityPeriod,
+       CIMD_Param_Submit_AUX,          /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
+       CIMD_Param_Submit_DCS,          /* protocol version 1.14+ */
+       CIMD_Param_Submit_PID,          /* protocol version 1.15+ */
+       CIMD_Param_Submit_SPEC,         /* protocol version 1.16+ */
+       CIMD_Param_Submit_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_Submit[]={
+       { /* CIMD_Param_Submit_Destination    */ 1,CIMD_ELF_StringZ },
+       { /* CIMD_Param_Submit_Text           */ 2,CIMD_ELF_StringZ },
+       { /* CIMD_Param_Submit_ValidityPeriod */ 3,CIMD_ELF_HexByte },
+       { /* CIMD_Param_Submit_AUX            */ 4,CIMD_ELF_Empty   },
+       { /* CIMD_Param_Submit_DCS            */ 5,CIMD_ELF_Decimal },
+       { /* CIMD_Param_Submit_PID            */ 6,CIMD_ELF_HexByte },
+       { /* CIMD_Param_Submit_SPEC           */ 7,CIMD_ELF_HexByte },
+       };
+
+/* CIMD_Cmd_SubmitBin:         protocol version 1.12+
+ */
+enum CIMD_Param_SubmitBin {
+       CIMD_Param_SubmitBin_Destination,
+       CIMD_Param_SubmitBin_Text,
+       CIMD_Param_SubmitBin_ValidityPeriod,
+       CIMD_Param_SubmitBin_AUX,               /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
+       CIMD_Param_SubmitBin_DCS,               /* protocol version 1.14+ */
+       CIMD_Param_SubmitBin_PID,               /* protocol version 1.15+ */
+       CIMD_Param_SubmitBin_SPEC,              /* protocol version 1.16+ */
+       CIMD_Param_SubmitBin_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_SubmitBin[]={
+       { /* CIMD_Param_SubmitBin_Destination    */ 1,CIMD_ELF_StringZ },
+       { /* CIMD_Param_SubmitBin_Text           */ 2,CIMD_ELF_StringZ },
+       { /* CIMD_Param_SubmitBin_ValidityPeriod */ 3,CIMD_ELF_HexByte },
+       { /* CIMD_Param_SubmitBin_AUX            */ 4,CIMD_ELF_Empty   },
+       { /* CIMD_Param_SubmitBin_DCS            */ 5,CIMD_ELF_Decimal },
+       { /* CIMD_Param_SubmitBin_PID            */ 6,CIMD_ELF_HexByte },
+       { /* CIMD_Param_SubmitBin_SPEC           */ 7,CIMD_ELF_HexByte },
+       };
+
+/* CIMD_Cmd_DCSEnable:         protocol version 1.14+
+ */
+enum CIMD_Param_DCSEnable {
+       CIMD_Param_DCSEnable_Type,
+       CIMD_Param_DCSEnable_NULL
+       };
+static const struct CIMD_Param CIMD_Param_BIP_DCSEnable[]={
+       { /* CIMD_Param_DCSEnable_Type */ 1,CIMD_ELF_HexWord },
+       };
+
+enum CIMD_Param_DCSEnable_Type {
+       CIMD_Param_DCSEnable_Type_None        =0x0000,  /* protocol version 1.14+ */
+       CIMD_Param_DCSEnable_Type_DCS         =0x0001,  /* protocol version 1.14+ */
+       CIMD_Param_DCSEnable_Type_PID_DCS     =0x0002,  /* protocol version 1.15+ */
+       CIMD_Param_DCSEnable_Type_PID_DCS_SPEC=0x0003,  /* protocol version 1.16+ */
+       };
+
+#define CIMD_PARAM_ENTRY(param) (param),ARRAY_LEN((param))
+
+struct CIMD_Cmd {
+       u8 code;
+       const struct CIMD_Param *param;
+       unsigned paramcnt;
+       };
+
+enum CIMD_Cmd_Type {
+       CIMD_Cmd_Ack,
+       CIMD_Cmd_Nak,
+       CIMD_Cmd_Login,
+       CIMD_Cmd_Logout,
+       CIMD_Cmd_Submit,
+       CIMD_Cmd_Retrieve,
+       CIMD_Cmd_RetrieveReply,
+       CIMD_Cmd_Count,
+       CIMD_Cmd_CountReply,
+       CIMD_Cmd_SubmitBin,             /* protocol version 1.12+ */
+       CIMD_Cmd_DCSEnable,             /* protocol version 1.14+ */
+       CIMD_Cmd_NULL           /* stdarg termination, MUST be last! */
+       };
+static const struct CIMD_Cmd CIMD_Cmd_BIP[]={
+       { /* CIMD_Cmd_Ack           */ 0x00,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Ack          ) },
+       { /* CIMD_Cmd_Nak           */ 0x99,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Nak          ) },
+       { /* CIMD_Cmd_Login         */ 0x01,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Login        ) },
+       { /* CIMD_Cmd_Logout        */ 0x02,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Logout       ) },
+       { /* CIMD_Cmd_Submit        */ 0x03,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Submit       ) },
+       { /* CIMD_Cmd_Retrieve      */ 0x05,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Retrieve     ) },
+       { /* CIMD_Cmd_RetrieveReply */ 0x06,CIMD_PARAM_ENTRY(CIMD_Param_BIP_RetrieveReply) },
+       { /* CIMD_Cmd_Count         */ 0x51,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Count        ) },
+       { /* CIMD_Cmd_CountReply    */ 0x61,CIMD_PARAM_ENTRY(CIMD_Param_BIP_CountReply   ) },
+       { /* CIMD_Cmd_SubmitBin     */ 0x31,CIMD_PARAM_ENTRY(CIMD_Param_BIP_SubmitBin    ) },
+       { /* CIMD_Cmd_DCSEnable     */ 0x53,CIMD_PARAM_ENTRY(CIMD_Param_BIP_DCSEnable    ) },
+       };
+
+static const struct CIMD_Cmd *CIMD_Cmd=CIMD_Cmd_BIP;   /* FIXME: When other protocols get supported... */
+static unsigned CIMD_Cmdcnt=ARRAY_LEN(CIMD_Cmd_BIP);   /* FIXME: When other protocols get supported... */
+
+struct CIMD_Param_Ack_Nak_Error {
+       u16 code;
+       const char *msg;
+       };
+
+static const struct CIMD_Param_Ack_Nak_Error CIMD_Param_Ack_Nak_Error[]={
+       /* CIMD_Cmd_Login */
+       { 0x1101,N_("User is already logged in") },
+       { 0x1001,N_("Logging in is currently disabled") },
+       { 0x9999,N_("Username not found") },
+               /* 0x000? can be bitmask combined from: */
+       { 0x0001,N_("Invalid password") },
+       { 0x0002,N_("User has forbidden access") },
+       { 0x0008,N_("User is already registered") },            /* ??? difference from 0x1101 ? */
+       /* CIMD_Cmd_Retrieve / CIMD_Cmd_Count */
+       { 0x5001,N_("No SMS found or not detectable") },        /* ==CIMD_NAK_NO_SMS */
+       { 0x5011,N_("Function not enabled") },
+       /* CIMD_Cmd_Retrieve */
+       { 0x5021,N_("Automatical retrieve is active") },
+       /* CIMD_Cmd_Submit / CIMD_Cmd_SubmitBin */
+       { 0x3001,N_("Error during processing SMS in BMG") },
+       { 0x3061,N_("Destination number has invalid format") },
+       /* CIMD_Cmd_SubmitBin */
+       { 0x3051,N_("Invalid DCS") },           /* protocol version 1.14+ */
+       { 0x3081,N_("Invalid Text") },          /* protocol version 1.14+ */
+       { 0x3091,N_("SMS is empty") },          /* protocol version 1.14+ */
+       { 0x3099,N_("SMS submit too fast") },   /* protocol version 1.16+ */
+       };
+
+
+static struct CIMD_paramslot CIMD_RX_Packet_slot[CIMD_PARAMSLOTS];
+#define CIMD_RX_PACKET_PARAMSLOT(paramsample) (CIMD_RX_Packet_slot+1+(unsigned)(paramsample))
+static enum CIMD_Cmd_Type CIMD_RX_Packet_Cmd;
+static unsigned CIMD_RX_Packet_slots;
+
+
+/* RETURNS: Processed */
+typedef bool (*CIMD_RX_PatrolFunc)(void);
+typedef void (*CIMD_RX_PatrolReset)(void);
+
+struct CIMD_RX_Patrol {
+       enum CIMD_Cmd_Type cmd;
+       CIMD_RX_PatrolFunc func;
+       CIMD_RX_PatrolReset reset;
+       };
+
+static const struct CIMD_RX_Patrol *CIMD_RX_Patrol_Current;
+
+static char *CIMD_RX_Patrol_Ack_Error;
+static u16 CIMD_RX_Patrol_Nak_Error;
+
+
+#ifndef WIN32
+
+static pthread_t Thread;
+# if __unices__
+static pthread_t selThread;
+# endif
+
+#endif
+
+/* Local variables used by get/set phonebook entry code. Buffer is used as a
+   source or destination for phonebook data and other functions... Error is
+   set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
+   handler routines as appropriate. */
+
+static GSM_SMSMessage     *CurrentSMSMessage;
+static GSM_Error          CurrentSMSMessageError;
+
+static GSM_SMSStatus      *CurrentSMSStatus;
+static GSM_Error          CurrentSMSStatusError;
+
+static unsigned char      Revision[GSM_MAX_REVISION_LENGTH];
+static unsigned char      Model   [GSM_MAX_MODEL_LENGTH];
+
+
+static u8 CIMD_TX_SendCommand_buf[sizeof(CIMD_CatchBuffer)];   /* sizeof() is not required to be == but it is appropriate */
+static u8 *CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
+
+struct CIMD_paramslot {
+       u16 code;
+       enum CIMD_ELF elf;
+       union {
+               struct { u8    i;               } HexByte;
+               struct { u16   i;               } HexWord;
+               struct { int   i;               } Decimal;
+               struct { char *s;               } StringZ;
+               struct { u8   *buf; size_t len; } HexBlock;
+               struct { int _dummy;            } Empty;
+               } u;
+       };
+
+/* slots are sorted by moving the whole blocks but they are small so it is OK
+ */
+static struct CIMD_paramslot CIMD_TX_SendCommand_paramslots[CIMD_PARAMSLOTS];
+static int CIMD_TX_SendCommand_paramslots_full;
+static enum CIMD_Cmd_Type CIMD_TX_SendCommand_Cmd;
+
+/* RETURNS: Success
+ */
+static bool CIMD_TX_SendCommand_vpushparam(u16 code,enum CIMD_ELF elf,va_list *app)
+{
+struct CIMD_paramslot *slot=CIMD_TX_SendCommand_paramslots+(CIMD_TX_SendCommand_paramslots_full++);
+
+       if (slot>=CIMD_TX_SendCommand_paramslots+ARRAY_LEN(CIMD_TX_SendCommand_paramslots)) {
+               /* assertion */
+               fprintf(stderr,"Out of param slots!\n");
+               return(false);
+               }
+       slot->code=code;
+       slot->elf=elf;
+       switch (elf) {
+               case CIMD_ELF_HexByte:
+                       slot->u.HexByte.i=va_arg((*app),int);
+                       break;
+               case CIMD_ELF_HexWord:
+                       slot->u.HexWord.i=va_arg((*app),int);
+                       break;
+               case CIMD_ELF_Decimal:
+                       slot->u.Decimal.i=va_arg((*app),int);
+                       break;
+               case CIMD_ELF_StringZ:
+                       slot->u.StringZ.s=va_arg((*app),char *);
+                       break;
+               case CIMD_ELF_HexBlock:
+                       slot->u.HexBlock.buf=va_arg((*app),u8 *);
+                       slot->u.HexBlock.len=va_arg((*app),size_t);
+                       break;
+               case CIMD_ELF_Empty:
+                       break;
+               }
+       return(true);
+}
+
+/* RETURNS: Success
+ */
+static bool CIMD_TX_SendCommand_pushparam(u16 code,enum CIMD_ELF elf,...)
+{
+va_list ap;
+bool r;
+
+       va_start(ap,elf);
+       r=CIMD_TX_SendCommand_vpushparam(code,elf,&ap);
+       va_end(ap);
+       return(r);
+}
+
+static int CIMD_TX_SendCommand_paramslots_compare
+       (const struct CIMD_paramslot *a,const struct CIMD_paramslot *b)
+{
+       return((b->code<a->code)-(a->code<b->code));
+}
+
+/* RETURNS: Success
+ */
+static bool CIMD_TX_SendCommand_storeparam(struct CIMD_paramslot *slot)
+{
+       switch (slot->elf) {
+               case CIMD_ELF_HexByte:
+                       CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",slot->u.HexByte.i);
+                       break;
+               case CIMD_ELF_HexWord:
+                       CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%04X",slot->u.HexWord.i);
+                       break;
+               case CIMD_ELF_Decimal:
+                       CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%d",slot->u.Decimal.i);
+                       break;
+               case CIMD_ELF_StringZ: {
+size_t len;
+                       if (!slot->u.StringZ.s)         /* NULL is interpreted as "" */
+                               break;
+                       len=strlen(slot->u.StringZ.s);
+                       if (CIMD_TX_SendCommand_buf_d+len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
+                               return(false);          /* error - overflow */
+                       memcpy(CIMD_TX_SendCommand_buf_d,slot->u.StringZ.s,len);
+                       CIMD_TX_SendCommand_buf_d+=len;
+                       } break;
+               case CIMD_ELF_HexBlock:
+                       if (CIMD_TX_SendCommand_buf_d+2*slot->u.HexBlock.len
+                                       > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
+                               return(false);          /* error - overflow */
+                       CIMD_TX_SendCommand_buf_d=SMS_BlockToHex(CIMD_TX_SendCommand_buf_d,
+                                       slot->u.HexBlock.buf,slot->u.HexBlock.len);     /* never fails */
+                       break;
+               case CIMD_ELF_Empty:
+                       break;
+               }
+       return(true);   /* success */
+}
+
+static unsigned CIMD_KeepAlives=0;
+static pthread_mutex_t CIMD_KeepAlivesLock;
+
+/* This function is NOT thread-safe!
+ */
+static void CIMD_KeepAlivesLockInit(void)
+{
+static bool done=false;
+
+       if (done)
+               return;
+       pthread_mutex_init(&CIMD_KeepAlivesLock,NULL);
+       done=true;
+}
+
+static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...);
+
+static void CIMD_KeepAlivesCheck(void)
+{
+unsigned alives;
+
+       CIMD_KeepAlivesLockInit();
+       pthread_mutex_lock(&CIMD_KeepAlivesLock);
+       alives=CIMD_KeepAlives;
+       CIMD_KeepAlives=0;
+       pthread_mutex_unlock(&CIMD_KeepAlivesLock);
+
+       while (alives) {
+               CIMD_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,
+                       CIMD_Cmd_Nak,
+                               CIMD_Param_Nak_Cmd,(CIMD_Cmd+(unsigned)CIMD_Cmd_Ack)->code,     /* ==0x00 */
+                               CIMD_Param_Nak_Error,CIMD_NAK_KEEPALIVE_REPLY,                  /* ==0x9998 */
+                               CIMD_Param_Nak_NULL);
+               alives--;
+               }
+}
+
+/* RETURNS: Success
+ */
+static bool CIMD_TX_SendCommandResend(boid)
+{
+int writephone_got;
+
+       writephone_got=WRITEPHONE(PortFD, CIMD_TX_SendCommand_buf, CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf);
+
+#ifdef CIMD_DEBUG
+       write(1,"CMD:<STX>",9);
+       write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1));
+       write(1,"<ETX>\n",6);
+#endif
+
+       return(writephone_got==(CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf));
+}
+
+static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...)
+{
+va_list ap;
+const struct CIMD_Cmd *cmdstruct;
+enum CIMD_Param_SAMPLE param;
+const struct CIMD_Param *paramstruct;
+u8 *u8s,xsum;
+struct CIMD_paramslot *slot;
+int parami;
+
+       CIMD_KeepAlivesCheck();
+
+       va_start(ap,cmd);
+       if (errorcodep)
+               *errorcodep=GE_BUSY;
+       CIMD_RX_Patrol_Current=patrol;
+
+       CIMD_TX_SendCommand_Cmd=cmd;
+       CIMD_TX_SendCommand_paramslots_full=0;
+       if ((unsigned)cmd>=CIMD_Cmdcnt)
+               goto fail;              /* assert */
+       cmdstruct=CIMD_Cmd+(unsigned)cmd;
+
+       if (!CIMD_TX_SendCommand_pushparam(0/*code*/,CIMD_ELF_HexByte/*elf*/,cmdstruct->code))
+               goto fail;
+       for (;;) {
+               param=va_arg(ap,enum CIMD_Param_SAMPLE);
+               if ((unsigned)param==cmdstruct->paramcnt)
+                       break;          /* CIMD_Param_*_NULL reached */
+               if ((unsigned)param>cmdstruct->paramcnt)
+                       goto fail;      /* assert */
+               paramstruct=(cmdstruct->param+(unsigned)param);
+               if (!CIMD_TX_SendCommand_vpushparam(paramstruct->code,paramstruct->elf,&ap))
+                       goto fail;
+               }
+       qsort(CIMD_TX_SendCommand_paramslots,CIMD_TX_SendCommand_paramslots_full,
+                       sizeof(*CIMD_TX_SendCommand_paramslots),
+                       (int (*)(const void *,const void *))CIMD_TX_SendCommand_paramslots_compare);
+
+       CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
+       *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_START;
+
+       slot=CIMD_TX_SendCommand_paramslots;
+       for (parami=0;slot<CIMD_TX_SendCommand_paramslots+CIMD_TX_SendCommand_paramslots_full;parami++) {
+               if (CIMD_TX_SendCommand_buf_d +32/*safety*/ > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) {
+                               /* error - overflow */
+fail:
+                       if (errorcodep)
+                               *errorcodep=GE_INTERNALERROR;
+                       return;
+                       }
+               if (slot->code==parami) {
+                       if (!CIMD_TX_SendCommand_storeparam(slot))      /* error */
+                               goto fail;
+                       }
+               if (slot->code>=parami)
+                       *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_SEP;
+               if (slot->code<=parami)
+                       slot++;
+               }
+       xsum=0;
+       for (u8s=CIMD_TX_SendCommand_buf;u8s<CIMD_TX_SendCommand_buf_d;u8s++)
+               xsum+=*u8s;
+       CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",(unsigned)xsum);
+       *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_STOP;
+
+       CIMD_CatchBufferStart(errorcodep);
+
+       if (!CIMD_TX_SendCommandResend())
+               goto fail;
+
+       /* success but we don't wait for the result code */
+}
+
+/* This function is used to get storage status from the phone. It currently
+   supports two different memory areas - internal and SIM. */
+
+static GSM_Error 
+wait_on(volatile GSM_Error *what, int timeout)
+{
+GSM_Error r=GE_TIMEOUT;        /* shut up GCC when (timeout==0) */
+
+       while (timeout && ((r=*what)==GE_BUSY)) {
+               if (!--timeout) {
+                       r=GE_TIMEOUT;
+                       break;
+                       }
+               CIMD_KeepAlivesCheck();
+               usleep(100000);
+       }
+       /* any specific patrollers are no longer valid */
+       CIMD_RX_Patrol_Current=NULL;
+
+#ifdef CIMD_DEBUG
+       printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
+#endif
+
+       return(r);
+}
+
+#define WAIT_ON(what, timeout) \
+       do { \
+               GSM_Error res = wait_on(what, timeout); \
+               if (res != GE_NONE) \
+                       return res; \
+        } while (0)
+
+/* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
+ */
+#define CIMD_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
+       do { \
+GSM_Error _CIMD_TX_SENDCOMMAND_WAIT_ON_err,*_CIMD_TX_SENDCOMMAND_WAIT_ON_errp; \
+ \
+               if (!(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
+                       _CIMD_TX_SENDCOMMAND_WAIT_ON_errp=&_CIMD_TX_SENDCOMMAND_WAIT_ON_err; \
+               CIMD_TX_SendCommand(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
+               WAIT_ON(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
+       } while (0)
+
+
+#define CIMD_ERR_WRAPPER(expr) \
+       do { \
+               GSM_Error err=(expr); \
+               if (err!=GE_NONE) \
+                       return(err); \
+       } while (0)
+
+/* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
+ */
+static unsigned char CIMD_SMStoSPEC_BIP_CIMD(GSM_SMSMessage *SMS)
+{
+       return(0
+                       /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
+                        */
+                       |((!!SMS->ReplyViaSameSMSC)<<1) /*ReplyPath*/
+                       |((!!SMS->UDHPresent)<<2) /*UDH set*/
+                       );
+}
+
+static unsigned char CIMD_SMStoDCS(GSM_SMSMessage *SMS)
+{
+int class=(SMS->Class==-1 ? DEFAULT_CLASS : SMS->Class);
+
+#if 0 /* NEVER send 0 for BIP CIMD as it would translate it to F6 !!!
+       */
+       if (!SMS->EightBit && class==DEFAULT_CLASS)
+               return(0x00);
+#endif
+       return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
+}
+
+static GSM_Error CIMD_PhoneSetup(void)
+{
+#if 1 /* HACK */
+       CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,100/*timeout*/,NULL/*patrol*/,
+               CIMD_Cmd_Login,
+                       /* NULLs will be interpreted as ""
+                       */
+                       CIMD_Param_Login_ID,CFG_Get(CFG_Info,"CIMD","name"),
+                       CIMD_Param_Login_PWD,CFG_Get(CFG_Info,"CIMD","password"),
+                       CIMD_Param_Login_NULL);
+       if (!CIMD_RX_Patrol_Ack_Error)
+               Revision[0]='\0';
+       else
+               SAFE_STRNCPY_SIZEOF(Revision,CIMD_RX_Patrol_Ack_Error);
+
+       CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
+               CIMD_Cmd_DCSEnable,
+                       CIMD_Param_DCSEnable_Type,(u16)CIMD_Param_DCSEnable_Type_PID_DCS_SPEC,
+                       CIMD_Param_DCSEnable_NULL);
+#endif
+
+       return(GE_NONE);
+}
+
+
+static void CIMD_RX_Char(char rx_byte);
+static void CIMD_SigHandler(int status);
+static bool CIMD_OpenSerial(void);
+
+GSM_Phone phone_cimd;  /* forward declaration */
+
+/* Initialise variables and state machine. */
+
+static GSM_Error CIMD_Init(GSM_Data *data, GSM_Statemachine *state)
+{
+       RequestTerminate = false;
+
+       SAFE_STRNCPY_SIZEOF(Model,data->Model);
+
+       /* Create and start main thread. */
+
+#ifdef WIN32
+{
+int rtn;
+       rtn = ! OpenConnection(State->Link.PortDevice,CIMD_RX_Char,CIMD_KeepAliveProc);
+       if (rtn != 0) {
+               return(GE_INTERNALERROR);
+}
+#else
+       SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
+       if (!CIMD_OpenSerial())
+               return(GE_INTERNALERROR);
+#endif
+
+        CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
+
+       return (GE_NONE);
+}
+
+#if __unices__
+/* thread for handling incoming data */
+void CIMD_SelectLoop()
+{
+       int err;
+       fd_set readfds;
+       struct timeval timeout;
+
+       FD_ZERO(&readfds);
+       FD_SET(device_portfd, &readfds);
+       /* set timeout to 15 seconds */
+       timeout.tv_sec=15;
+       timeout.tv_usec=0;
+       while (!RequestTerminate) {
+               err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
+               /* call singal handler to process incoming data */
+               if ( err > 0 ) CIMD_SigHandler(0);
+               else if (err == -1) perror("Error in SelectLoop");
+       }
+}
+#endif
+
+/* Applications should call CIMD_Terminate to shut down the CIMD thread and
+   close the serial port. */
+
+static GSM_Error CIMD_Terminate(GSM_Data *data, GSM_Statemachine *state)
+{
+GSM_Error err;
+
+#if 1 /* HACK */
+       /* Don't wait too much as we can have already broken link
+        */
+       CIMD_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
+               CIMD_Cmd_Logout,
+                       CIMD_Param_Logout_NULL);
+       wait_on(&err,20/*timeout*/);            /* errors ignored, of course */
+#endif
+
+       /* Request termination of thread */
+       RequestTerminate = true;
+
+#ifndef WIN32
+       /* Now wait for thread to terminate. */
+       pthread_join(Thread, NULL);
+
+       /* Close serial port. */
+       device_close();
+
+#else
+       CloseConnection();
+#endif
+
+       return(GE_NONE);
+}
+
+/* messagecenter->No" is NOT set as it is input argument */
+static void CIMD_MessageCenterClear(GSM_MessageCenter *messagecenter)
+{
+       messagecenter->Name[0]='\0';            /* not present up to Nokia 9110i */
+       messagecenter->Recipient[0]='\0';       /* not present up to Nokia 9110i */
+       messagecenter->Number[0]='\0';          /* default */
+       messagecenter->Format=GSMF_Text;        /* default */
+       messagecenter->Validity=GSMV_72_Hours;  /* default */
+}
+
+static bool CIMD_RX_Patrol_CountReply(void)
+{
+       CurrentSMSStatus->UnRead=/*FALLTHRU*/
+       CurrentSMSStatus->Used  =/*FALLTHRU*/
+       CurrentSMSStatus->Slots =/*FALLTHRU*/
+                       CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_CountReply_Used)->u.Decimal.i;
+
+       CurrentSMSStatusError=GE_NONE;
+       return(true);
+}
+
+static const struct CIMD_RX_Patrol CIMD_RX_Patrol_CountReply_struct=
+       { CIMD_Cmd_CountReply,CIMD_RX_Patrol_CountReply };
+
+static GSM_Error CIMD_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentSMSStatus = data->SMSStatus;
+       CurrentSMSStatus->UnRead=0;     /* default */
+       CurrentSMSStatus->Used  =0;     /* default */
+       CurrentSMSStatus->Slots =0;     /* default */
+#if 1 /* HACK */
+       CIMD_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,100/*timeout*/,&CIMD_RX_Patrol_CountReply_struct/*patrol*/,
+               CIMD_Cmd_Count,
+                       CIMD_Param_Count_NULL);
+#endif
+
+       return(GE_NONE);
+}
+
+static GSM_Error CIMD_GetImei(GSM_Data *data, GSM_Statemachine *state)
+{
+       /* not supported by the protocol */
+       data->Imei[0]='\0';
+       return (GE_NONE);
+}
+
+static GSM_Error CIMD_GetRevision(GSM_Data *data, GSM_Statemachine *state)
+{
+       if (*Revision) {
+               strcpy(data->Revision,Revision);
+               return (GE_NONE);
+       } else return (GE_TRYAGAIN);
+}
+
+static GSM_Error CIMD_GetModel(GSM_Data *data, GSM_Statemachine *state)
+{
+       /* not supported by the protocol */
+       strcpy(data->Model,Model);
+       return (GE_NONE);
+}
+
+static GSM_Error CIMD_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
+{
+       /* not supported by the protocol */
+       data->Imei[0]='\0';
+       return (GE_NONE);
+}
+
+static void CIMD_DateTimeSetCurrent(GSM_DateTime *datetime)
+{
+time_t current=time(NULL);
+struct tm *tm=localtime(&current);
+
+       datetime->AlarmEnabled=false;
+
+       datetime->Year  =tm->tm_year+1900;
+       datetime->Month =tm->tm_mon+1;
+       datetime->Day   =tm->tm_mday;
+       datetime->Hour  =tm->tm_hour;
+       datetime->Minute=tm->tm_min;
+       datetime->Second=tm->tm_sec;
+
+       datetime->Timezone=timezone;
+}
+
+/* scts=="18.06.1998 22:33:23"
+ */
+static bool CIMD_SCTStoSMS(GSM_SMSMessage *SMS,const char *scts)
+{
+GSM_DateTime *DateTime=&SMS->Time;
+const char *fmt="%2d.%2d.%4d %2d:%2d:%2d";     /* trailing '\0' IS used! */
+const char *fs,*ss;
+
+       fs=fmt;
+       ss=scts;
+       do {
+               while (*fs=='%') {
+int nums=(*++fs)-'0';
+                       while (nums--)
+                               if (!isdigit(*ss++))
+                                       return(false);
+                       }
+               if (*fs++!=*ss)
+                       return(false);
+               } while (fs[-1]);
+       /* string is completely valid now */
+
+       sscanf(scts,fmt,
+                       &DateTime->Day,
+                       &DateTime->Month,
+                       &DateTime->Year,
+                       &DateTime->Hour,
+                       &DateTime->Minute,
+                       &DateTime->Second);
+       DateTime->Timezone=0;
+
+       return(true);
+}
+
+static void CIMD_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
+{
+       switch ((dcs&0xF0)>>4) {
+               case 0x0:
+                       switch (dcs&0x0F) {
+                               case 0x0:
+                                       CurrentSMSMessage->EightBit=false;
+                                       break;
+                               }
+                       break;
+               case 0xF:
+                       CurrentSMSMessage->EightBit=!!(dcs&0x04);       /* bit 2 */
+                       CurrentSMSMessage->Class=(dcs&0x03);            /* bits 0 & 1 */
+                       break;
+               }
+}
+
+/* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
+ */
+static void CIMD_SPEC_BIP_CIMDtoSMS(GSM_SMSMessage *SMS,u8 spec)
+{
+       /* Such specification not supported by BIP:
+        */
+       CurrentSMSMessage->Type=GST_MT;
+       CurrentSMSMessage->Status=GSS_NOTSENTREAD;
+
+       /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
+        */
+
+       SMS->UDHPresent=!!(spec&(1<<2));
+
+       SMS->ReplyViaSameSMSC=!!(spec&(1<<1));
+}
+
+static bool CIMD_RX_Patrol_RetrieveReply(void)
+{
+u16 pid_dcs;   /* upper=PID, lower=DCS */
+
+       {
+const char *destination=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Destination)->u.StringZ.s;
+
+               if (strlen(destination)+1 > sizeof(CurrentSMSMessage->Sender)) {
+fail:
+                       CurrentSMSMessageError=GE_INTERNALERROR;
+                       return(true);           /* error */
+                       }
+               strcpy(CurrentSMSMessage->Sender,destination);
+               }
+
+       if ((CurrentSMSMessage->EightBit=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Is8bit)->u.Decimal.i)) {
+const char *hextext=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
+size_t hextextlen=strlen(hextext);
+
+               if ((hextextlen&1) || (hextextlen/2 > sizeof(CurrentSMSMessage->MessageText)))
+                       goto fail;              /* error - message too long */
+               if (!SMS_BlockFromHex(CurrentSMSMessage->MessageText/*d*/,hextext,hextextlen))
+                       goto fail;              /* error - parse error */
+               CurrentSMSMessage->MessageTextLength=hextextlen/2;
+               }
+       else {  /* 7bit */
+const char *text=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
+size_t textlen=strlen(text);
+
+               if (textlen+1 > sizeof(CurrentSMSMessage->MessageText))
+                       goto fail;              /* error - message too long */
+               strcpy(CurrentSMSMessage->MessageText,text);
+               CurrentSMSMessage->MessageTextLength=textlen;
+               }
+
+       /* errors ignored as it is not fatal */
+       CIMD_SCTStoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Timestamp)->u.StringZ.s);
+
+       pid_dcs=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_PID_DCS)->u.HexWord.i;
+
+       /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
+       CurrentSMSMessage->MessageCenter.Format=(GSM_SMSMessageFormat)(pid_dcs>>8U);    /* <pid> */
+       CIMD_DCStoSMS(CurrentSMSMessage,(pid_dcs&0x00FFU));
+       CIMD_SPEC_BIP_CIMDtoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_SPEC)->u.HexByte.i);
+
+       if (CurrentSMSMessage->UDHPresent) {
+u8 udhlen;
+               if (CurrentSMSMessage->MessageTextLength<=0)
+                       goto fail;
+               udhlen=1/*sizeof(udhlen)*/ +CurrentSMSMessage->MessageText[0];
+               if (udhlen > CurrentSMSMessage->MessageTextLength)
+                       goto fail;
+               memcpy(CurrentSMSMessage->UDH,CurrentSMSMessage->MessageText,udhlen);
+               memmove(CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageText+udhlen,
+                               CurrentSMSMessage->MessageTextLength-udhlen +1/*Trailing '\0'*/);
+               CurrentSMSMessage->MessageTextLength-=udhlen;
+               }
+
+       CurrentSMSMessageError=GE_NONE;
+       return(true);
+}
+
+static const struct CIMD_RX_Patrol CIMD_RX_Patrol_RetrieveReply_struct=
+       { CIMD_Cmd_RetrieveReply,CIMD_RX_Patrol_RetrieveReply };
+
+static GSM_Error CIMD_GetSMS(GSM_Data *data, GSM_Statemachine *state)
+{
+       CurrentSMSMessage = data->SMSMessage;
+
+       if (CurrentSMSMessage->MemoryType!=GMT_SM)
+               return(GE_INVALIDMEMORYTYPE);
+
+       CIMD_DateTimeSetCurrent(&CurrentSMSMessage->Time);              /* default */
+       CIMD_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime);          /* not present in the protocol */
+       CurrentSMSMessage->MessageTextLength=0;                         /* default */
+       CurrentSMSMessage->Validity=72/*hours*/*60;                     /* default */
+       CurrentSMSMessage->UDHPresent=false;                            /* default */
+       CurrentSMSMessage->MessageText[0]='\0';                         /* default */
+
+       CIMD_MessageCenterClear(&CurrentSMSMessage->MessageCenter);     /* default */
+       CurrentSMSMessage->MessageCenter.No=0;                          /* default - input for GetSMSCenter */
+
+       CurrentSMSMessage->Sender[0]='\0';                              /* default */
+       CurrentSMSMessage->Destination[0]='\0';                         /* default */
+       CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location;   /* default */
+       /* CurrentSMSMessage->MemoryType is input argument */
+       CurrentSMSMessage->Type=GST_UN;                                 /* default, detection of EMPTY SMSes! */
+       CurrentSMSMessage->Status=GSS_SENTREAD;                         /* default */
+       CurrentSMSMessage->Class=DEFAULT_CLASS;                         /* default */
+       CurrentSMSMessage->EightBit=false;                              /* default */
+       CurrentSMSMessage->Compression=false;                           /* default */
+       /* CurrentSMSMessage->Location is input argument */
+       CurrentSMSMessage->ReplyViaSameSMSC=false;                      /* default */
+
+       CIMD_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&CIMD_RX_Patrol_RetrieveReply_struct/*patrol*/,
+               CIMD_Cmd_Retrieve,
+                       CIMD_Param_Retrieve_NULL);
+       if (GE_NONE!=wait_on(&CurrentSMSMessageError,90/*timeout*/)) {
+               if (CIMD_RX_Patrol_Nak_Error!=CIMD_NAK_NO_SMS)
+                       return(CurrentSMSMessageError);
+               /* We don't return GE_EMPTYSMSLOCATION as when we have already eaten
+                * all the wating SMSes there cannot be any other one
+                */
+               return(GE_INVALIDSMSLOCATION);
+               }
+
+       return(GE_NONE);
+}
+
+static GSM_Error CIMD_SendSMS(GSM_Data *data, GSM_Statemachine *state)
+{
+u8 bintext[sizeof(CurrentSMSMessage->UDH)+sizeof(CurrentSMSMessage->MessageText)],*d;
+char hexbintext[sizeof(bintext) +1/*Trailing '\0'*/];
+
+       CurrentSMSMessage = data->SMSMessage;
+
+       CurrentSMSMessage->MessageNumber=0;     /* default */
+
+       d=bintext;
+       if (CurrentSMSMessage->UDHPresent) {
+size_t UDHlen=1+CurrentSMSMessage->UDH[0];
+
+               memcpy(d,CurrentSMSMessage->UDH,UDHlen);
+               d+=UDHlen;
+#ifdef CIMD_SUBMIT_7BIT
+               if (!CurrentSMSMessage->EightBit)       /* We are not able to send UDH by 7bit channel */
+                       return(GE_NOTSUPPORTED);
+#endif
+               }
+       if (
+#ifdef CIMD_SUBMIT_7BIT
+                       1       /* Never do 7bit->8bit encoding when CIMD_SUBMIT_7BIT */
+#else
+                       CurrentSMSMessage->EightBit
+#endif
+                       ) {
+               memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
+               d+=CurrentSMSMessage->MessageTextLength;
+       }
+#ifndef CIMD_SUBMIT_7BIT
+       else {
+size_t byteslen=PackSevenBitsToEight(
+                       /* check it out yourself, really the number of used bits for UDH header on the start
+                        * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
+                        */
+                       (7-(d-bintext))%7,
+                       CurrentSMSMessage->MessageText,d);
+               d+=byteslen;
+       }
+#endif
+
+       if (
+#ifdef CIMD_SUBMIT_7BIT
+                       CurrentSMSMessage->EightBit
+#else
+                       1       /* Never use plain Submit for 8bit messages */
+#endif
+                       ) {
+               *(SMS_BlockToHex(hexbintext,bintext,(d-bintext)/*len*/))='\0';
+
+               /* DANGER: Keep in sync with CIMD_Cmd_Submit !!!
+                */
+               CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
+                       CIMD_Cmd_SubmitBin,
+                               CIMD_Param_SubmitBin_Destination,CurrentSMSMessage->Destination,
+                               CIMD_Param_SubmitBin_Text,hexbintext,
+                               CIMD_Param_SubmitBin_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
+                               CIMD_Param_SubmitBin_AUX,               /* Empty */
+                               CIMD_Param_SubmitBin_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
+                               CIMD_Param_SubmitBin_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
+                               CIMD_Param_SubmitBin_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
+                               CIMD_Param_SubmitBin_NULL);
+               }
+#ifdef CIMD_SUBMIT_7BIT
+       else {
+               *d='\0';
+
+               /* DANGER: Keep in sync with CIMD_Cmd_SubmitBin !!!
+                */
+               CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
+                       CIMD_Cmd_Submit,
+                               CIMD_Param_Submit_Destination,CurrentSMSMessage->Destination,
+                               CIMD_Param_Submit_Text,bintext,
+                               CIMD_Param_Submit_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
+                               CIMD_Param_Submit_AUX,          /* Empty */
+                               CIMD_Param_Submit_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
+                               CIMD_Param_Submit_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
+                               CIMD_Param_Submit_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
+                               CIMD_Param_Submit_NULL);
+               }
+#endif
+
+       return(GE_SMSSENDOK);
+}
+
+static GSM_Error CIMD_Reset(GSM_Data *data, GSM_Statemachine *state)
+{
+       CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
+
+       return(GE_NONE);
+}
+
+#ifndef WIN32
+
+/* Called by initialisation code to open comm port in asynchronous mode. */
+
+static bool CIMD_OpenSerial(void)
+{
+       int result;
+  
+#if __unices__
+       int rtn;
+#else
+       struct sigaction sig_io;
+
+       /* Set up and install handler before enabling async IO on port. */
+
+       sig_io.sa_handler = CIMD_SigHandler;
+       sig_io.sa_flags = 0;
+       sigaction (SIGIO, &sig_io, NULL);
+#endif
+
+       /* Open device. */
+
+       result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, GCT_Serial);
+
+       if (!result) {
+               perror(_("Couldn't open CIMD device"));
+               return false;
+       }
+
+#if __unices__
+       /* create a thread to handle incoming data from mobile phone */
+       rtn = pthread_create(&selThread, NULL, (void*)CIMD_SelectLoop, (void*)NULL);
+       if (rtn != 0) return false;
+#endif
+
+       /* device_changespeed() not needed as device_open() now automatically
+        * sets the user-specified (or default) speed.
+        */
+       return (true);
+}
+
+static void CIMD_SigHandler(int status)
+{
+       unsigned char buffer[255];
+       int count, res;
+       res = device_read(buffer, 255);
+       for (count = 0; count < res ; count ++)
+               CIMD_RX_Char(buffer[count]);
+}
+#endif /* WIN32 */
+
+static bool CIMD_RX_Patrol_Ack(void)
+{
+       if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i) { /* ==0x99 */
+               /* We cannot send the keepalive here as some dangerous operation may be in progress
+                */
+               CIMD_KeepAlivesLockInit();
+               pthread_mutex_lock(&CIMD_KeepAlivesLock);
+               CIMD_KeepAlives++;
+               pthread_mutex_unlock(&CIMD_KeepAlivesLock);
+               return(false);
+               }
+
+       if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
+               return(false);          /* Ack for some unknown command */
+
+       free(CIMD_RX_Patrol_Ack_Error);
+       CIMD_RX_Patrol_Ack_Error=strdup(CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Error)->u.StringZ.s);
+
+       /* Some patrol may have already indicated some error!
+        */
+        if (CIMD_CatchBufferErrorP && *CIMD_CatchBufferErrorP==GE_BUSY)
+                *CIMD_CatchBufferErrorP=GE_NONE;
+
+       return(true);
+}
+
+static void CIMD_RX_Patrol_Ack_reset(void)
+{
+       free(CIMD_RX_Patrol_Ack_Error);
+       CIMD_RX_Patrol_Ack_Error=NULL;
+}
+
+static const char *CIMD_RX_Patrol_Nak_resolve(u16 errorcode)
+{
+const struct CIMD_Param_Ack_Nak_Error *errorp;
+
+       for (errorp=CIMD_Param_Ack_Nak_Error;errorp<CIMD_Param_Ack_Nak_Error+ARRAY_LEN(CIMD_Param_Ack_Nak_Error);errorp++)
+               if (errorcode==errorp->code)
+                       return(_(errorp->msg));
+       return(_("Unknown error code"));
+}
+
+static void CIMD_RX_Patrol_Nak_dump(u16 errorcode)
+{
+       fprintf(stderr,_("Got CIMD error code 0x%04X: "),errorcode);
+       if (!(errorcode & ~0x000F)) {   /* combined error code */
+u16 errormask;
+               fprintf(stderr,_("combined:"));
+               for (errormask=0x0001;errormask<=errorcode;errormask<<=1)
+                       if (errorcode&errormask)
+                               fprintf(stderr," +%s",CIMD_RX_Patrol_Nak_resolve(errormask));
+               fputc('\n',stderr);
+               return;
+               }
+       fprintf(stderr,"%s\n",CIMD_RX_Patrol_Nak_resolve(errorcode));
+}
+
+static bool CIMD_RX_Patrol_Nak(void)
+{
+       if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i        /* ==0x99 */
+               && CIMD_NAK_RESEND == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i) {    /* 0x9999 */
+               fprintf(stderr,_("WARNING: Requested to resend last packet!!\n"));
+               CIMD_TX_SendCommandResend();
+               return(true);
+               }
+
+       if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
+               return(false);          /* Nak for some unknown command */
+
+       CIMD_RX_Patrol_Nak_Error=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i;
+       if (CIMD_CatchBufferErrorP)
+               *CIMD_CatchBufferErrorP=GE_INTERNALERROR;
+
+       CIMD_RX_Patrol_Nak_dump(CIMD_RX_Patrol_Nak_Error);
+
+       return(true);
+}
+
+static void CIMD_RX_Patrol_Nak_reset(void)
+{
+       CIMD_RX_Patrol_Nak_Error=0;
+}
+
+static const struct CIMD_RX_Patrol CIMD_RX_Patrols[]={
+       { CIMD_Cmd_Ack,CIMD_RX_Patrol_Ack,CIMD_RX_Patrol_Ack_reset },
+       { CIMD_Cmd_Nak,CIMD_RX_Patrol_Nak,CIMD_RX_Patrol_Nak_reset },
+       };
+
+
+static void CIMD_CatchBufferReset(void)
+{
+       CIMD_CatchBufferPtr=CIMD_CatchBuffer;
+}
+
+static void CIMD_CatchBufferStart(GSM_Error *errorcodep)
+{
+static GSM_Error err_trashcan;
+const struct CIMD_RX_Patrol *patrol;
+
+       if (!errorcodep)
+               errorcodep=&err_trashcan;
+       CIMD_CatchBufferErrorP=errorcodep;
+
+       CIMD_CatchBufferReset();
+
+       for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
+               if (patrol->reset)
+                       (*patrol->reset)();
+}
+
+static u8 *CIMD_RX_ProcessRawPacket_parseparam_buf_s;
+
+/* RETURNS: Success
+ */
+static bool CIMD_RX_ProcessRawPacket_parseparam(struct CIMD_paramslot *slot)
+{
+       switch (slot->elf) {
+
+               case CIMD_ELF_HexByte: {
+unsigned parsedbyte;
+                       if (0   || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
+                               || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
+                               || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[2]
+                               )
+                               return(false);
+                       if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedbyte))
+                               return(false);
+                       slot->u.HexByte.i=parsedbyte;
+                       CIMD_RX_ProcessRawPacket_parseparam_buf_s+=2+1;
+                       } break;
+
+               case CIMD_ELF_HexWord: {
+unsigned parsedword;
+                       if (0   || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
+                               || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
+                               || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[2])
+                               || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[3])
+                               || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[4]
+                               )
+                               return(false);
+                       if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedword))
+                               return(false);
+                       slot->u.HexWord.i=parsedword;
+                       CIMD_RX_ProcessRawPacket_parseparam_buf_s+=4+1;
+                       } break;
+
+               case CIMD_ELF_Decimal: {
+int parsedint;
+u8 *start=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
+                       if (CIMD_MARK_SEP==*start)
+                               return(false);          /* empty Decimal not permitted */
+                       while (isdigit(*CIMD_RX_ProcessRawPacket_parseparam_buf_s))
+                               CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
+                       if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++)        /* skip CIMD_MARK_SEP */
+                               return(false);
+                       if (1!=sscanf(start,"%d" CIMD_MARK_SEPS,&parsedint))
+                               return(false);
+                       slot->u.Decimal.i=parsedint;
+                       } break;
+
+               case CIMD_ELF_StringZ:
+                       slot->u.StringZ.s=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
+                       while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
+                               CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
+                       *CIMD_RX_ProcessRawPacket_parseparam_buf_s++='\0';      /* skip CIMD_MARK_SEP */
+                       break;
+
+               case CIMD_ELF_HexBlock:
+                       slot->u.HexBlock.buf=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
+
+                       while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
+                               CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
+                       if ((CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf)&1)
+                               return(false);          /* odd number of xdigits */
+
+                       slot->u.HexBlock.len=(CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf);
+                       if (!SMS_BlockFromHex(slot->u.HexBlock.buf/*d*/,slot->u.HexBlock.buf/*s*/,slot->u.HexBlock.len*2))
+                               return(false);          /* parse error */
+
+                       CIMD_RX_ProcessRawPacket_parseparam_buf_s++;    /* skip CIMD_MARK_SEP */
+                       break;
+
+               case CIMD_ELF_Empty:
+                       if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
+                               return(false);          /* some content found */
+                       break;
+               }
+       return(true);   /* success */
+}
+
+
+static bool CIMD_RX_EvalPatrol(const struct CIMD_RX_Patrol *patrol)
+{
+       if (!patrol)
+               return(false);  /* not recognized */
+       if (CIMD_RX_Packet_Cmd!=patrol->cmd)
+               return(false);  /* not recognized */
+       return((*patrol->func)());
+}
+
+static inline void CIMD_RX_ProcessPacket(void)
+{
+const struct CIMD_RX_Patrol *patrol;
+
+       for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
+               if (CIMD_RX_EvalPatrol(patrol))
+                       return;         /* recognized and processed - whether successfuly is not interesting */
+       CIMD_RX_EvalPatrol(CIMD_RX_Patrol_Current);
+}
+
+static inline void CIMD_RX_ProcessRawPacket(void)
+{
+unsigned xsumgot;
+u8 *u8s,xsum;
+u16 position;
+const struct CIMD_Param *param;
+const struct CIMD_Cmd *cmdp;
+
+#ifdef CIMD_DEBUG
+       write(1,"GOT:<STX>",9);
+       write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1));
+       write(1,"<ETX>\n",6);
+#endif
+
+       if (CIMD_CatchBufferPtr<CIMD_CatchBuffer +1/*START*/ +1/*SEP*/ +2/*xsum*/ +1/*STOP*/)
+               return;         /* error - buffer too short */
+       if (0   || CIMD_CatchBufferPtr[-4]!=CIMD_MARK_SEP
+               || !isxdigit(CIMD_CatchBufferPtr[-3])
+               || !isxdigit(CIMD_CatchBufferPtr[-2])
+               )
+               return;         /* error - invalid buffer tail */
+
+       /* (*Ptr) will now point to the end of parameter part */
+       CIMD_CatchBufferPtr-=3;
+       if (1!=sscanf(CIMD_CatchBufferPtr,"%X" CIMD_MARK_SEPS,&xsumgot))
+               return;         /* INTERNAL - we have already checked the validity! */
+       xsum=0;
+       for (u8s=CIMD_CatchBuffer;u8s<CIMD_CatchBufferPtr;u8s++)
+               xsum+=*u8s;
+       if (xsum!=xsumgot)
+               return;         /* error - invalid checksum */
+
+       CIMD_RX_ProcessRawPacket_parseparam_buf_s=CIMD_CatchBuffer +1/*START*/;
+       CIMD_RX_Packet_slots=1;         /* just [0] now */
+       CIMD_RX_Packet_slot[0].code=0;  /* ignored now - position */
+       CIMD_RX_Packet_slot[0].elf=CIMD_ELF_HexByte;
+       if (!CIMD_RX_ProcessRawPacket_parseparam(CIMD_RX_Packet_slot+0))
+               return;         /* error - cmd code not parsed */
+
+       for (cmdp=CIMD_Cmd;cmdp<CIMD_Cmd+CIMD_Cmdcnt;cmdp++)
+               if (cmdp->code==CIMD_RX_Packet_slot[0].u.HexByte.i) break;
+       if (cmdp>=CIMD_Cmd+CIMD_Cmdcnt)
+               return;         /* error - unknown command */
+       CIMD_RX_Packet_Cmd=(enum CIMD_Cmd_Type)(cmdp-CIMD_Cmd);
+
+       /* "position" handling is BIP dependency! */
+       for (position=1;CIMD_RX_ProcessRawPacket_parseparam_buf_s<CIMD_CatchBufferPtr;position++) {
+struct CIMD_paramslot *slot;
+
+               /* This code search is a crude overhead just for BIP now :-)
+                */
+               for (param=cmdp->param;param<cmdp->param+cmdp->paramcnt;param++)
+                       if (position==param->code)
+                               break;
+               if (param>=cmdp->param+cmdp->paramcnt) {        /* not found - ignore */
+                       while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++);    /* skip CIMD_MARK_SEP */
+                       continue;
+                       }
+               slot=CIMD_RX_Packet_slot+1+(param-cmdp->param);
+               slot->code=param->code;
+               slot->elf=param->elf;
+               if (!CIMD_RX_ProcessRawPacket_parseparam(slot))
+                       return;         /* error - unable to parse required param */
+               /* CIMD_MARK_SEP is skipped by CIMD_RX_ProcessRawPacket_parseparam() automatically
+                */
+               CIMD_RX_Packet_slots++;
+               }
+       /* The packet is now parsed */
+
+       CIMD_RX_ProcessPacket();
+}
+
+/* RX_State machine for receive handling.  Called once for each character
+   received from the phone/phone. */
+
+static void CIMD_RX_Char(char rx_byte)
+{
+#ifdef CIMD_DEBUG
+#if 0
+       dprintf(_("Received character '%c' (0x%02X)\n"),
+               (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
+#endif
+       putchar(rx_byte);
+#endif
+
+       /* NEVER store '\0' as it would knock-out completely our patrolling system!
+        * It is invalid anyway
+        */
+       if (rx_byte=='\0') {
+               CIMD_CatchBufferReset();
+               return;
+               }
+
+       if (CIMD_CatchBufferPtr==CIMD_CatchBuffer && rx_byte!=CIMD_MARK_START) {
+               /* No haven't yet catched any START yet */
+               return;
+               }
+       if (CIMD_CatchBufferPtr>=CIMD_CatchBuffer+sizeof(CIMD_CatchBuffer)) {
+               fprintf(stderr,_("WARNING: Incoming CIMD packet too long to fit (>%d bytes)!\n"),sizeof(CIMD_CatchBuffer));
+               CIMD_CatchBufferReset();
+               }
+
+       *CIMD_CatchBufferPtr++=rx_byte;
+       if (rx_byte==CIMD_MARK_STOP) {
+               CIMD_RX_ProcessRawPacket();
+               CIMD_CatchBufferReset();
+               }
+}
+
+/* Here we initialise model specific functions. */
+
+#define CIMD_FUNCTIONS_ENTRY(name) \
+       case GOP_##name:        return(CIMD_##name(data,state));
+
+static GSM_Error CIMD_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
+{
+static GSM_Statemachine *catcher=NULL;
+
+       if (!catcher || catcher==state)
+               catcher=state;
+       else
+               *((char *)NULL)=1;
+
+       switch (op) {
+               CIMD_FUNCTIONS_ENTRY(Init)
+               CIMD_FUNCTIONS_ENTRY(Terminate)
+               CIMD_FUNCTIONS_ENTRY(GetSMSStatus)
+               CIMD_FUNCTIONS_ENTRY(GetSMS)
+               CIMD_FUNCTIONS_ENTRY(SendSMS)
+               CIMD_FUNCTIONS_ENTRY(GetImei)
+               CIMD_FUNCTIONS_ENTRY(GetRevision)
+               CIMD_FUNCTIONS_ENTRY(GetModel)
+               CIMD_FUNCTIONS_ENTRY(GetManufacturer)
+               CIMD_FUNCTIONS_ENTRY(Reset)
+
+               case GOP_Identify: {
+GSM_Error err,r=GE_NONE;
+                       if (GE_NONE!=(err=CIMD_GetImei        (data,state)))
+                               r=err;
+                       if (GE_NONE!=(err=CIMD_GetRevision    (data,state)))
+                               r=err;
+                       if (GE_NONE!=(err=CIMD_GetModel       (data,state)))
+                               r=err;
+                       if (GE_NONE!=(err=CIMD_GetManufacturer(data,state)))
+                               r=err;
+                       return(r);
+                       }
+
+               default:
+                       return(GE_NOTIMPLEMENTED);
+       }
+}
+
+GSM_Phone phone_cimd = {
+       UNIMPLEMENTED,  /* IncomingFunctions - we don't use default StateMachine */
+       UNIMPLEMENTED,  /* DefaultFunction   - we don't use default StateMachine */
+        /* Mobile phone information */
+       {
+               "BIP" /* |CIMD - not yet */,    /* Supported models */
+               100,                    /* Max RF Level (AT+CSQ) */
+               0,                      /* Min RF Level (AT+CSQ) */
+               GRF_Percentage,         /* RF level units */
+               100,                    /* Max Battery Level (AT+CBC) */
+               0,                      /* Min Battery Level (AT+CBC) */
+               GBU_Percentage,         /* Battery level units */
+               GDT_None,               /* Have date/time support */
+               GDT_None,               /* Alarm supports time only */
+               0,                      /* No alarm available */
+               48, 84,                 /* Startup logo size */
+               14, 72,                 /* Op logo size */
+               14, 72,                 /* Caller logo size */
+       },
+       CIMD_Functions,
+};
index 41445c8..3c2eb86 100644 (file)
@@ -6,12 +6,29 @@
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
   This file provides a virtual modem or "AT" interface to the GSM phone by
   calling code in gsm-api.c. Inspired by and in places copied from the Linux
   kernel AT Emulator IDSN code by Fritz Elfert and others.
   
+  $Log$
+  Revision 1.1.1.5  2002/04/03 00:08:04  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.5  2001/11/08 16:34:19  pkot
+  Updates to work with new libsms
+
+  Revision 1.4  2001/07/03 15:27:03  pkot
+  AT commands for SMS handling support (Tamas Bondar)
+  Small at-emulator code cleanup (me)
+
+  Revision 1.3  2001/02/21 19:56:59  chris
+  More fiddling with the directory layout
+
+
 */
 
 #define                __data_at_emulator_c
@@ -305,7 +322,7 @@ static GSM_Error ATEM_ReadSMS(int number, GSM_MemoryType type, GSM_SMSMessage *m
        GSM_Error error;
 
        message->MemoryType = type;
-       message->Location = number;
+       message->Number = number;
        error = GSM->GetSMSMessage(message);
 
        return error;
@@ -315,13 +332,14 @@ static void ATEM_PrintSMS(char *line, GSM_SMSMessage *message, int mode)
 {
        switch (mode) {
        case INTERACT_MODE:
-               gsprintf(line, MAX_LINE_LENGTH, _("\n\rDate/time: %d/%d/%d %d:%02d:%02d Sender: %s Msg Center: %s\n\rText: %s\n\r"), message->Time.Day, message->Time.Month, message->Time.Year, message->Time.Hour, message->Time.Minute, message->Time.Second, message->Sender, message->MessageCenter.Number, message->MessageText);
+               gsprintf(line, MAX_LINE_LENGTH, _("\n\rDate/time: %d/%d/%d %d:%02d:%02d Sender: %s Msg Center: %s\n\rText: %s\n\r"), message->Time.Day, message->Time.Month, message->Time.Year, message->Time.Hour, message->Time.Minute, message->Time.Second, message->RemoteNumber.number, message->MessageCenter.Number, message->MessageText);
                break;
        case TEXT_MODE:
-               if (message->Coding==GSM_Coding_8bit)
-                       gsprintf(line, MAX_LINE_LENGTH, _("\"%s\",\"%s\",,\"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"\n\r%s"), (message->Status ? _("REC READ") : _("REC UNREAD")), message->Sender, message->Time.Year, message->Time.Month, message->Time.Day, message->Time.Hour, message->Time.Minute, message->Time.Second, message->Time.Timezone, _("<Not implemented>"));
+               if ((message->DCS.Type == SMS_GeneralDataCoding) &&
+                   (message->DCS.u.General.Alphabet == SMS_8bit))
+                       gsprintf(line, MAX_LINE_LENGTH, _("\"%s\",\"%s\",,\"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"\n\r%s"), (message->Status ? _("REC READ") : _("REC UNREAD")), message->RemoteNumber.number, message->Time.Year, message->Time.Month, message->Time.Day, message->Time.Hour, message->Time.Minute, message->Time.Second, message->Time.Timezone, _("<Not implemented>"));
                else
-                       gsprintf(line, MAX_LINE_LENGTH, _("\"%s\",\"%s\",,\"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"\n\r%s"), (message->Status ? _("REC READ") : _("REC UNREAD")), message->Sender, message->Time.Year, message->Time.Month, message->Time.Day, message->Time.Hour, message->Time.Minute, message->Time.Second, message->Time.Timezone, message->MessageText);
+                       gsprintf(line, MAX_LINE_LENGTH, _("\"%s\",\"%s\",,\"%02d/%02d/%02d,%02d:%02d:%02d+%02d\"\n\r%s"), (message->Status ? _("REC READ") : _("REC UNREAD")), message->RemoteNumber.number, message->Time.Year, message->Time.Month, message->Time.Day, message->Time.Hour, message->Time.Minute, message->Time.Second, message->Time.Timezone, message->MessageText);
                break;
        case PDU_MODE:
                gsprintf(line, MAX_LINE_LENGTH, _("<Not implemented>"));
@@ -336,7 +354,7 @@ static void ATEM_EraseSMS(int number, GSM_MemoryType type)
 {
        GSM_SMSMessage message;
        message.MemoryType = type;
-       message.Location = number;
+       message.Number = number;
        if (GSM->DeleteSMSMessage(&message) == GE_NONE) {
                ATEM_ModemResult(MR_OK);
        } else {
@@ -552,12 +570,12 @@ bool      ATEM_CommandPlusC(char **buf)
                                strcasecmp(*buf, "3") == 0 ||
                                strcasecmp(*buf, "\"REC READ\"") == 0 ||
                                strcasecmp(*buf, "\"STO SENT\"") == 0) {
-                               status = GSS_SENTREAD;
+                               status = SMS_Sent;
                        } else if (strcasecmp(*buf, "0") == 0 ||
                                strcasecmp(*buf, "2") == 0 ||
                                strcasecmp(*buf, "\"REC UNREAD\"") == 0 ||
                                strcasecmp(*buf, "\"STO UNSENT\"") == 0) {
-                               status = GSS_NOTSENTREAD;
+                               status = SMS_Unsent;
                        } else if (strcasecmp(*buf, "4") == 0 ||
                                strcasecmp(*buf, "\"ALL\"") == 0) {
                                status = 4; /* ALL */
index 4eede53..134bb3c 100644 (file)
@@ -6,12 +6,25 @@
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
   This file provides routines to handle processing of data when connected in
   fax or data mode. Converts data from/to GSM phone to virtual modem
   interface.
 
+  $Log$
+  Revision 1.1.1.3  2002/04/03 00:08:04  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.3  2001/02/21 19:57:00  chris
+  More fiddling with the directory layout
+
+  Revision 1.2  2001/02/17 22:40:51  chris
+  ATA support
+
+
 */
 
 #define                __data_datapump_c
index 9c46e0d..e392661 100644 (file)
@@ -6,6 +6,8 @@
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+  
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
   The development of RLP protocol is sponsored by SuSE CR, s.r.o. (Pavel use
   Actual implementation of RLP protocol. Based on GSM 04.22 version 7.1.0,
   downloadable from www.etsi.org (if you register with them)
 
+  $Log$
+  Revision 1.1.1.3  2002/04/03 00:08:05  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.5  2001/03/26 23:39:36  pkot
+  Minor updates:
+   - Windows INLINE patch (Manfred Jonsson)
+   - patch to configure.in to compile under FreeBSD (Panagiotis Astithas)
+   - other cleanups (me)
+
+  Revision 1.4  2001/03/13 01:23:18  pkot
+  Windows updates (Manfred Jonsson)
+
+  Revision 1.3  2001/02/21 19:57:00  chris
+  More fiddling with the directory layout
+
+  Revision 1.2  2001/02/17 22:40:51  chris
+  ATA support
+
+
 */
 
 #include <stdio.h>
index cc8af90..ab975c1 100644 (file)
@@ -6,10 +6,20 @@
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
   CRC24 (aka FCS) implementation in RLP.
 
+  $Log$
+  Revision 1.1.1.3  2002/04/03 00:08:06  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.2  2001/02/21 19:57:02  chris
+  More fiddling with the directory layout
+
+
 */
 
 #include "data/rlp-crc24.h"
index 7c7f4e2..2c389f9 100644 (file)
@@ -5,6 +5,8 @@
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
   This file provides a virtual modem interface to the GSM phone by calling
   (AT-emulator) and "online" mode where the data pump code translates data
   from/to the GSM handset and the modem data/fax stream.
 
+  $Log$
+  Revision 1.1.1.3  2002/04/03 00:08:06  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.4  2001/04/14 23:23:43  pkot
+  Fixed problems with grantpt
+
+  Revision 1.3  2001/03/21 23:36:04  chris
+  Added the statemachine
+  This will break gnokii --identify and --monitor except for 6210/7110
+
+  Revision 1.2  2001/02/21 19:57:02  chris
+  More fiddling with the directory layout
+
+  Revision 1.1  2001/02/16 14:29:51  chris
+  Restructure of common/.  Fixed a problem in fbus-phonet.c
+  Lots of dprintfs for Marcin
+  Any size xpm can now be loaded (eg for 7110 startup logos)
+  nk7110 code detects 7110/6210 and alters startup logo size to suit
+  Moved Marcin's extended phonebook code into gnokii.c
+
+  Revision 1.5  2001/01/08 15:11:37  pkot
+  Documentation updates.
+  Fixed some bugs and removed FIXMEs.
+  We need to move some stuff from configure.in to aclocal.m4
+
+  Revision 1.4  2001/01/02 09:09:09  pkot
+  Misc fixes and updates.
+
+  Revision 1.3  2000/12/27 10:54:14  pkot
+  Added Unix98 PTYs support (Michael Mráka).
+
+  
 */
 
 #define                __virtmodem_c
@@ -49,7 +84,7 @@
 
        /* Global variables */
 
-//extern bool TerminateThread;
+extern bool TerminateThread;
 int ConnectCount;
 
        /* Local variables */
@@ -66,7 +101,7 @@ bool                 RequestTerminate;
 
        /* If initialised in debug mode, stdin/out is used instead
           of ptys for interface. */
-bool   VM_Initialise(char *model,char *port, char *initlength, GSM_ConnectionType connection, char *bindir, bool debug_mode, bool GSMInit,char *synchronizetime)
+bool   VM_Initialise(char *model,char *port, char *initlength, GSM_ConnectionType connection, char *bindir, bool debug_mode, bool GSMInit)
 {
        int             rtn;
 
@@ -85,10 +120,9 @@ bool        VM_Initialise(char *model,char *port, char *initlength, GSM_ConnectionType
 #ifdef DEBUG
          fprintf (stderr , "Initialising GSM\n");
 #endif /* DEBUG */
-         if ((VM_GSMInitialise(model, port, initlength, connection, synchronizetime) != GE_NONE)) {
+         if ((VM_GSMInitialise(model, port, initlength, connection) != GE_NONE)) {
                fprintf (stderr, _("VM_Initialise - VM_GSMInitialise failed!\n"));
                return (false);
-               
          }
        }
        GSMInit=false;
@@ -241,7 +275,7 @@ void    VM_CharHandler(void)
       /* Note that file closure etc. should have been dealt with in ThreadLoop */
       
       if (res < 0) {   
-//         TerminateThread=true;
+           TerminateThread=true;
            return;
       }
        
@@ -250,14 +284,15 @@ void    VM_CharHandler(void)
 }     
 
        /* Initialise GSM interface, returning GSM_Error as appropriate  */
-GSM_Error      VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection, char *synchronizetime)
+GSM_Error      VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection)
 {
        int             count=0;
        GSM_Error       error;
+       static GSM_Statemachine sm;
 
                /* Initialise the code for the GSM interface. */     
 
-       error = GSM_Initialise(model,port, initlength, connection, RLP_DisplayF96Frame, synchronizetime);
+       error = GSM_Initialise(model,port, initlength, connection, RLP_DisplayF96Frame, &sm);
 
        if (error != GE_NONE) {
                fprintf(stderr, _("GSM/FBUS init failed! (Unknown model ?). Quitting.\n"));
index 5da6097..413581c 100644 (file)
@@ -6,6 +6,9 @@
  *
  * A Linux/Unix toolset and driver for Nokia mobile phones.
  *
+ * Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+ * Copyright (C) 2000-2001  Marcel Holtmann <marcel@holtmann.org>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * License along with this library; if not, write to the Free
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * $Log$
+ * Revision 1.1.1.3  2002/04/03 00:08:06  short
+ * Found in "gnokii-working" directory, some November-patches version
+ *
+ * Revision 1.2  2001/02/21 19:57:03  chris
+ * More fiddling with the directory layout
+ *
+ * Revision 1.1  2001/02/16 14:29:51  chris
+ * Restructure of common/.  Fixed a problem in fbus-phonet.c
+ * Lots of dprintfs for Marcin
+ * Any size xpm can now be loaded (eg for 7110 startup logos)
+ * nk7110 code detects 7110/6210 and alters startup logo size to suit
+ * Moved Marcin's extended phonebook code into gnokii.c
+ *
+ * Revision 1.2  2001/02/12 15:13:46  chris
+ * Fixed my bug in xgnokii_contacts.c and added <string.h> to tekram.c
+ *
+ * Revision 1.1  2001/02/09 18:12:53  chris
+ * Marcel's tekram support
+ *
  */
 
 #include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <termios.h>
 #include <string.h>
 
 #ifndef WIN32
-  #include <fcntl.h>
-  #include <sys/ioctl.h>
-  #include <termios.h>
-  #include "devices/unixserial.h"
+#include "devices/unixserial.h"
 #else
-  #include <windows.h>
-  #include "devices/winserial.h"
+#include "winserial.h"
 #endif
 
 #include "devices/tekram.h"
 
+
+
+
 int tekram_open(__const char *__file) {
 
   return (serial_open(__file, O_RDWR | O_NOCTTY | O_NONBLOCK));
@@ -51,9 +76,18 @@ void tekram_close(int __fd) {
 
 void tekram_reset(int __fd) {
 
-  serial_setdtrrts(__fd, 0, 0); usleep(50000);
-  serial_setdtrrts(__fd, 1, 0); usleep(1000);
-  serial_setdtrrts(__fd, 1, 1); usleep(50);
+  serial_setdtrrts(__fd, 0, 0);
+
+  usleep(50000);
+
+  serial_setdtrrts(__fd, 1, 0);
+
+  usleep(1000);
+
+  serial_setdtrrts(__fd, 1, 1);
+
+  usleep(50);
+
 
   serial_changespeed(__fd, 9600);
 }
index cfbf39a..31beb66 100644 (file)
@@ -1,10 +1,14 @@
 /*
  * $Id$
  *
+ *
  * G N O K I I
  *
  * A Linux/Unix toolset and driver for Nokia mobile phones.
  *
+ * Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+ * Copyright (C) 2000-2001  Marcel Holtmann <marcel@holtmann.org>
+ *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * License along with this library; if not, write to the Free
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * $Log$
+ * Revision 1.1.1.3  2002/04/03 00:08:06  short
+ * Found in "gnokii-working" directory, some November-patches version
+ *
+ * Revision 1.7  2001/11/08 16:49:19  pkot
+ * Cleanups
+ *
+ * Revision 1.6  2001/08/17 00:18:12  pkot
+ * Removed recv() from IrDA initializing procedure (many people)
+ *
+ * Revision 1.5  2001/06/27 23:52:48  pkot
+ * 7110/6210 updates (Marian Jancar)
+ *
+ * Revision 1.4  2001/06/20 21:27:34  pkot
+ * IrDA patch (Marian Jancar)
+ *
+ * Revision 1.3  2001/02/21 19:57:04  chris
+ * More fiddling with the directory layout
+ *
+ * Revision 1.2  2001/02/20 21:55:10  pkot
+ * Small #include updates
+ *
+ * Revision 1.1  2001/02/16 14:29:52  chris
+ * Restructure of common/.  Fixed a problem in fbus-phonet.c
+ * Lots of dprintfs for Marcin
+ * Any size xpm can now be loaded (eg for 7110 startup logos)
+ * nk7110 code detects 7110/6210 and alters startup logo size to suit
+ * Moved Marcin's extended phonebook code into gnokii.c
+ *
+ * Revision 1.2  2001/02/06 21:15:35  chris
+ * Preliminary irda support for 7110 etc.  Not well tested!
+ *
+ * Revision 1.1  2001/02/03 23:56:17  chris
+ * Start of work on irda support (now we just need fbus-irda.c!)
+ * Proper unicode support in 7110 code (from pkot)
+ *
  */
 
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/poll.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-
 #include "devices/unixirda.h"
-#include "devices/linuxirda.h"
-
 
 #ifndef AF_IRDA
 #define AF_IRDA 23
 #define DISCOVERY_SLEEP                0.4
 
 static char *phone[] = {
-        "Nokia 5210",
-       "Nokia 6210", "Nokia 6250", "Nokia 6310",
-       "Nokia 7110",
-       "Nokia 8210", "Nokia 8310", "Nokia 8850"
+       "Nokia 7110", "Nokia 6210"
 };
 
 double d_time(void)
@@ -77,17 +101,18 @@ double d_sleep(double s)
        return time;
 }
 
-static int irda_discover_device(int fd)
+static int irda_discover_device(void)
 {
 
        struct irda_device_list *list;
        struct irda_device_info *dev;
        unsigned char           *buf;
-       int                     s, len, i, j;
-       int                     daddr = -1;
+       int                     s, len, i, j, daddr = -1, fd;
        double                  t1, t2;
        int phones = sizeof(phone) / sizeof(*phone);
        
+       fd = socket(AF_IRDA, SOCK_STREAM, 0);
+       
        len = sizeof(*list) + sizeof(*dev) * 10;        // 10 = max devices in discover
        buf = malloc(len);
        list = (struct irda_device_list *)buf;
@@ -104,15 +129,11 @@ static int irda_discover_device(int fd)
                                for (j = 0; (j < phones) && (daddr == -1); j++) {
                                        if (strncmp(dev[i].info, phone[j], INFO_LEN) == 0) {
                                                daddr = dev[i].daddr;
-#ifdef DEBUG
-                                               fprintf(stdout,_("%s\t%x\n"), dev[i].info, dev[i].daddr);
-#endif
+                                               dprintf("%s\t%x\n", dev[i].info, dev[i].daddr);
                                        }
                                }
                                if (daddr == -1) {
-#ifdef DEBUG
-                                       fprintf(stdout,_("unknown: %s\t%x\n"), dev[i].info, dev[i].daddr);
-#endif
+                                       dprintf("unknown: %s\t%x\n", dev[i].info, dev[i].daddr);
                                }
                        }
                }
@@ -126,6 +147,7 @@ static int irda_discover_device(int fd)
        } while ((t2 - t1 < DISCOVERY_TIMEOUT) && (daddr == -1));
        
        free(buf);
+       close(fd);
        
        return daddr;
 }
@@ -134,44 +156,24 @@ int irda_open(void)
 {
        struct sockaddr_irda    peer;
        int                     fd = -1, daddr;
-       int                     pgrp;         
        
+       daddr = irda_discover_device();                 /* discover the devices */
        
-       fd = socket(AF_IRDA, SOCK_STREAM, 0);   /* Create socket */
-       if (fd == -1) {
-               perror("socket");
-                       exit(1);
-                    }
-
-       /* discover the devices */ 
-       daddr = irda_discover_device(fd);
-       if (daddr == -1)  {
-                       printf("irda_discover: no nokia devices found");
-                       exit(1);
-                    }
-
-       /* Arrange for the current process to receive
-           SIGIO when the state of the socket changes. */
-       pgrp = getpid();
-       if (fcntl (fd, F_SETOWN, pgrp) < 0)
-       perror("F_SETOWN");
-
-       /*  Set the socket state for Asynchronous  */
-       if (fcntl (fd, F_SETFL, FASYNC) < 0) {
-               perror("fcntl");
-                       exit(1);
-                    }
-
-       peer.sir_family = AF_IRDA;
-       peer.sir_lsap_sel = LSAP_ANY;
-       peer.sir_addr = daddr;
-       strcpy(peer.sir_name, "Nokia:PhoNet");
+       if (daddr != -1)  {
+               fd = socket(AF_IRDA, SOCK_STREAM, 0);   /* Create socket */
+               peer.sir_family = AF_IRDA;
+               peer.sir_lsap_sel = LSAP_ANY;
+               peer.sir_addr = daddr;
+               strcpy(peer.sir_name, "Nokia:PhoNet");
                
-       if (connect(fd, (struct sockaddr *)&peer, sizeof(peer))) {      /* Connect to service "Nokia:PhoNet" */
-               perror("connect");
-               close(fd);
-               fd = -1;
+               if (connect(fd, (struct sockaddr *)&peer, sizeof(peer))) {      /* Connect to service "Nokia:PhoNet" */
+                       perror("connect");
+                       close(fd);
+                       fd = -1;
+/*             } else { FIXME: It does not work in most cases. Why? Or why it should work?
+                       recv(fd, NULL, 0, 0);            call recv first to make select work correctly */
                }
+       }
        
        return fd;
 }
@@ -184,20 +186,7 @@ int irda_close(int fd)
 
 int irda_write(int __fd, __const __ptr_t __bytes, int __size)
 {
-  int actual,ret;
-
-   actual = 0;
-   
-   do {
-    if ((ret = send(__fd, __bytes, __size - actual, 0)) < 0)
-       return(actual);
-
-       actual += ret;
-       __bytes += ret;
-
-    } while (actual < __size);
-
-    return (actual);
+       return (send(__fd, __bytes, __size, 0));
 }
 
 int irda_read(int __fd, __ptr_t __bytes, int __size)
index bc7e37e..9594d89 100644 (file)
@@ -6,11 +6,47 @@
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+
   Released under the terms of the GNU GPL, see file COPYING for more details.
 
+  $Log$
+  Revision 1.1.1.5  2002/04/03 00:08:07  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.9  2001/09/14 12:15:28  pkot
+  Cleanups from 0.3.3 (part1)
+
+  Revision 1.8  2001/08/20 23:27:37  pkot
+  Add hardware shakehand to the link layer (Manfred Jonsson)
+
+  Revision 1.7  2001/07/03 00:03:36  pkot
+  Small fixes to let gnokii compile and work under solaris (thanks to Artur Kubiak)
+
+  Revision 1.6  2001/03/21 23:36:04  chris
+  Added the statemachine
+  This will break gnokii --identify and --monitor except for 6210/7110
+
+  Revision 1.5  2001/03/19 23:43:46  pkot
+  Solaris/ *BSD '#if defined' cleanup
+
+  Revision 1.4  2001/03/13 01:21:38  pkot
+  *BSD updates (Bert Driehuis)
+
+  Revision 1.3  2001/03/06 22:27:46  pkot
+  Misc docs and Makefiles updates and cleanups
+
+  Revision 1.2  2001/02/21 19:57:05  chris
+  More fiddling with the directory layout
+
+
 */
 
+/* [global] option "serial_write_usleep" default: */
+#define SERIAL_WRITE_USLEEP_DEFAULT (-1)
+
 #include "misc.h"
+#include "cfgreader.h"
 
 /* Do not compile this file under Win32 systems. */
 
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
 
 #if __unices__
 #  include <sys/file.h>
 
 struct termios serial_termios;
 
+/* Script handling: */
+
+static void device_script_cfgfunc(const char *section,const char *key,const char *value)
+{
+  setenv(key,value,1/*overwrite*/);    /* errors ignored */
+}
+
+static int device_script(int fd, const char *section)
+{
+pid_t pid;
+const char *scriptname = CFG_Get(CFG_Info, "global", section);
+
+  if (!scriptname)
+    return(0);
+
+  errno=0;
+  switch ((pid=fork())) {
+    case -1:
+      fprintf(stderr,_("device_script(\"%s\"): fork() failure: %s!\n"),scriptname,strerror(errno));
+      return(-1);
+
+    default: { /* parent */
+int status;
+      if (pid==waitpid(pid,&status,0/*options*/) && WIFEXITED(status) && !WEXITSTATUS(status))
+       return(0);
+      fprintf(stderr,_("device_script(\"%s\"): child script failure: %s, exit code=%d\n"),scriptname,
+          (WIFEXITED(status) ? _("normal exit") : _("abnormal exit")),
+         (WIFEXITED(status) ? WEXITSTATUS(status) : -1));
+      errno=EIO;
+      return(-1);
+      }
+
+    case 0: {  /* child */
+      CFG_GetForeach(CFG_Info,section,device_script_cfgfunc);
+      errno=0;
+      if (0!=dup2(fd,0) || 1!=dup2(fd,1) || close(fd)) {
+       fprintf(stderr,_("device_script(\"%s\"): file descriptor prepare: %s\n"),scriptname,strerror(errno));
+       _exit(-1);
+       }
+      /* FIXME: close all open descriptors - how to track them?
+       */
+      execl("/bin/sh","sh","-c",scriptname,NULL);
+      fprintf(stderr,_("device_script(\"%s\"): execute script: %s\n"),scriptname,strerror(errno));
+      _exit(-1);
+      /* NOTREACHED */
+      }
+    }
+  /* NOTREACHED */
+}
+
+int serial_close_all_openfds[0x10];    /* -1 when entry not used, fd otherwise */
+int serial_close(int __fd);
+
+static void serial_close_all(void)
+{
+  int i;
+
+  dprintf("serial_close_all() executed\n");
+  for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
+    if (serial_close_all_openfds[i]!=-1)
+      serial_close(serial_close_all_openfds[i]);
+}
+
+static void unixserial_interrupted(int signo)
+{
+       exit(EXIT_FAILURE);
+       /* NOTREACHED */
+}
+
 /* Open the serial port and store the settings. */
 
 int serial_open(__const char *__file, int __oflag) {
 
   int __fd;
-  int retcode;
+  int retcode,i;
+  static bool atexit_registered=false;
+
+  if (!atexit_registered) {
+    memset(serial_close_all_openfds,-1,sizeof(serial_close_all_openfds));
+#if 0  /* Disabled for now as atexit() functions are then called multiple times for pthreads! */
+    signal(SIGINT,unixserial_interrupted);
+#endif
+    atexit(serial_close_all);
+    atexit_registered=true;
+    }
 
   __fd = open(__file, __oflag);
   if (__fd == -1) {
@@ -77,6 +198,12 @@ int serial_open(__const char *__file, int __oflag) {
     return (-1);
   }
 
+  for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
+    if (serial_close_all_openfds[i]==-1 || serial_close_all_openfds[i]==__fd) {
+      serial_close_all_openfds[i]=__fd;
+      break;
+      }
+
   retcode=tcgetattr(__fd, &serial_termios);
   if(retcode==-1) {
     perror("Gnokii serial_open:tcgetattr");
@@ -91,57 +218,75 @@ int serial_open(__const char *__file, int __oflag) {
 /* Close the serial port and restore old settings. */
 
 int serial_close(int __fd) {
+  int i;
+
+  for (i=0;i<ARRAY_LEN(serial_close_all_openfds);i++)
+    if (serial_close_all_openfds[i]==__fd)
+      serial_close_all_openfds[i]=-1;          /* fd closed */
+
+  /* handle config file disconnect_script:
+   */
+  if (-1 == device_script(__fd,"disconnect_script"))
+    fprintf(stderr,"Gnokii serial_close: disconnect_script\n");
+
+  if (__fd >= 0) {
+#if 1 /* HACK */
+    serial_termios.c_cflag |= HUPCL;   /* production == 1 */
+#else
+    serial_termios.c_cflag &= ~HUPCL;  /* debugging  == 0 */
+#endif
 
-  if (__fd >= 0)
     tcsetattr(__fd, TCSANOW, &serial_termios);
+    }
 
   return (close(__fd));
 }
 
-/* Open a device with standard options. */
-
+/* Open a device with standard options.
+ * Use value (-1) for "__with_hw_handshake" if its specification is required from the user
+ */
 int serial_opendevice(__const char *__file, int __with_odd_parity, int __with_async, int __with_hw_handshake) {
 
   int fd;
   int retcode;
   struct termios tp;
 
+  /* handle config file handshake override: */
+  {
+char *s=CFG_Get(CFG_Info, "global", "handshake");
+
+        if (s && (!strcasecmp(s,"software") || !strcasecmp(s,"rtscts")))
+      __with_hw_handshake=false;
+    else if (s && (!strcasecmp(s,"hardware") || !strcasecmp(s,"xonxoff")))
+      __with_hw_handshake=true;
+    else if (s)
+      fprintf(stderr,_("Unrecognized [%s] option \"%s\", use \"%s\" or \"%s\" value, ignoring!"),
+         "global","handshake","software","hardware");
+
+    if (__with_hw_handshake==-1) {
+      fprintf(stderr,_("[%s] option \"%s\" not found, trying to use \"%s\" value!"),
+         "global","handshake","software");
+      __with_hw_handshake=false;
+      }
+  }
+
   /* Open device */
 
+  /* O_NONBLOCK MUST be used here as the CLOCAL may be currently off
+   * and if DCD is down the "open" syscall would be stuck wating for DCD.
+   */
   fd = serial_open(__file, O_RDWR | O_NOCTTY | O_NONBLOCK);
 
   if (fd < 0) 
     return fd;
 
-  /* Allow process/thread to receive SIGIO */
-
-#if !(__unices__)
-  retcode = fcntl(fd, F_SETOWN, getpid());
-  if (retcode == -1){
-    perror("Gnokii serial_opendevice: fnctl(F_SETOWN)");
-    serial_close(fd);
-    return(-1);
-  }
-#endif
-
-  /* Make filedescriptor asynchronous. */
-
-  if (__with_async) {
-    retcode=fcntl(fd, F_SETFL, FASYNC);
-    if (retcode == -1){
-      perror("Gnokii serial_opendevice: fnctl(F_SETFL)");
-      serial_close(fd);
-      return(-1);
-    }
-  }
-  
   /* Initialise the port settings */
 
   memcpy(&tp, &serial_termios, sizeof(struct termios));
 
   /* Set port settings for canonical input processing */
 
-  tp.c_cflag = B0 | CS8 | CLOCAL | CREAD;
+  tp.c_cflag = B0 | CS8 | CLOCAL | CREAD | HUPCL;
   if (__with_odd_parity) {
     tp.c_cflag |= (PARENB | PARODD);
     tp.c_iflag = 0;
@@ -172,6 +317,61 @@ int serial_opendevice(__const char *__file, int __with_odd_parity, int __with_as
     return(-1);
   }
 
+  /* Set speed */
+  { 
+char *baudratestring=CFG_Get(CFG_Info, "global", "serial_baudrate");
+int baudrate=0;
+
+  if (baudratestring)
+    baudrate=atoi(baudratestring);
+  if (baudrate && !serial_changespeed(fd,baudrate))
+    baudrate=0;
+  if (!baudrate)
+    serial_changespeed(fd,19200/*default value*/);
+  }
+
+  /* We need to turn off O_NONBLOCK now (we have CLOCAL set so it is safe).
+   * When we run some device script it really doesn't expect NONBLOCK!
+   */
+
+  retcode=fcntl(fd, F_SETFL, 0);
+  if (retcode == -1){
+    perror("Gnokii serial_opendevice: fnctl(F_SETFL)");
+    serial_close(fd);
+    return(-1);
+  }
+
+  /* handle config file connect_script:
+   */
+  if (-1 == device_script(fd,"connect_script")) {
+    fprintf(stderr,"Gnokii serial_opendevice: connect_script\n");
+    serial_close(fd);
+    return(-1);
+  }
+
+  /* Allow process/thread to receive SIGIO */
+
+#if !(__unices__)
+  retcode = fcntl(fd, F_SETOWN, getpid());
+  if (retcode == -1){
+    perror("Gnokii serial_opendevice: fnctl(F_SETOWN)");
+    serial_close(fd);
+    return(-1);
+  }
+#endif
+
+  /* Make filedescriptor asynchronous. */
+
+  /* We need to supply FNONBLOCK (or O_NONBLOCK) again as it would get reset
+   * by F_SETFL as a side-effect!
+   */
+  retcode=fcntl(fd, F_SETFL, (__with_async ? FASYNC : 0) | FNONBLOCK);
+  if (retcode == -1){
+    perror("Gnokii serial_opendevice: fnctl(F_SETFL)");
+    serial_close(fd);
+    return(-1);
+  }
+  
   return fd;
 }
 
@@ -183,13 +383,17 @@ void serial_setdtrrts(int __fd, int __dtr, int __rts) {
 
   flags = TIOCM_DTR;
 
-  if (__dtr) ioctl(__fd, TIOCMBIS, &flags);
-        else ioctl(__fd, TIOCMBIC, &flags);
+  if (__dtr)
+    ioctl(__fd, TIOCMBIS, &flags);
+  else
+    ioctl(__fd, TIOCMBIC, &flags);
 
   flags = TIOCM_RTS;
 
-  if (__rts) ioctl(__fd, TIOCMBIS, &flags);
-        else ioctl(__fd, TIOCMBIC, &flags);
+  if (__rts)
+    ioctl(__fd, TIOCMBIS, &flags);
+  else
+    ioctl(__fd, TIOCMBIC, &flags);
 }
 
 
@@ -205,9 +409,12 @@ int serial_select(int fd, struct timeval *timeout) {
 }
 
 
-/* Change the speed of the serial device. */
+/* Change the speed of the serial device.
+ * RETURNS: Success
+ */
 
-void serial_changespeed(int __fd, int __speed) {
+bool serial_changespeed(int __fd, int __speed) {
+  bool retcode = true;
 
 #ifndef SGTTY
   struct termios t;
@@ -223,30 +430,36 @@ void serial_changespeed(int __fd, int __speed) {
     case 38400:  speed = B38400;  break;
     case 57600:  speed = B57600;  break;
     case 115200: speed = B115200; break;
+    default:
+      fprintf(stderr,_("Serial port speed %d not supported!\n"),__speed);
+      return(false);
   }
 
 #ifndef SGTTY
-  tcgetattr(__fd, &t);
+  if (tcgetattr(__fd, &t))
+    retcode = false;
 
   // This is not needed! We set up the speed via cfsetspeed
   //  t.c_cflag &= ~CBAUD;
   //  t.c_cflag |= speed;
-#ifdef DEBUG
-  if (cfsetspeed(&t, speed) == -1)
-       fprintf(stdout,_("Serial port speed setting failed\n"));
-#else
-  cfsetspeed(&t, speed);
-#endif
+  if (cfsetspeed(&t, speed) == -1) {
+    dprintf(_("Serial port speed setting failed\n"));
+    retcode = false;
+    }
 
   tcsetattr(__fd, TCSADRAIN, &t);
 #else
-  ioctl(__fd, TIOCGETP, &t);
+  if (ioctl(__fd, TIOCGETP, &t))
+    retcode = false;
 
   t.sg_ispeed = speed;
   t.sg_ospeed = speed;
 
-  ioctl(__fd, TIOCSETN, &t);
+  if (ioctl(__fd, TIOCSETN, &t))
+    retcode = false;
 #endif
+
+  return(retcode);
 }
 
 /* Read from serial device. */
@@ -256,11 +469,65 @@ size_t serial_read(int __fd, __ptr_t __buf, size_t __nbytes) {
   return (read(__fd, __buf, __nbytes));
 }
 
+#if !defined(TIOCMGET) && defined(TIOCMODG)
+#define TIOCMGET TIOCMODG
+#endif
+
+static void check_dcd(int __fd)
+{
+#ifdef TIOCMGET
+int mcs;
+
+       if (ioctl(__fd, TIOCMGET, &mcs) || !(mcs & TIOCM_CAR)) {
+               fprintf(stderr,_("ERROR: Modem DCD is down and global/require_dcd parameter is set!\n"));
+               exit(EXIT_FAILURE);             /* Hard quit of all threads */
+               }
+#else
+       /* Impossible!! (eg. Coherent) */
+#endif
+}
+
 /* Write to serial device. */
 
 size_t serial_write(int __fd, __const __ptr_t __buf, size_t __n) {
-       
-       return (write(__fd, __buf, __n));
+
+size_t r=0;
+ssize_t got;
+static long serial_write_usleep=LONG_MIN;
+static int require_dcd=-1;
+
+       if (serial_write_usleep==LONG_MIN) {
+char *s=CFG_Get(CFG_Info, "global", "serial_write_usleep");
+
+               serial_write_usleep=(!s ?
+                       SERIAL_WRITE_USLEEP_DEFAULT : atol(CFG_Get(CFG_Info, "global", "serial_write_usleep")));
+               }
+
+       if (require_dcd==-1) {
+               require_dcd=(!!CFG_Get(CFG_Info, "global", "require_dcd"));
+#ifndef TIOCMGET
+               if (require_dcd)
+                       fprintf(stderr,_("WARNING: global/require_dcd argument was set but it is not supported on this system!\n"));
+#endif
+               }
+
+       if (require_dcd)
+               check_dcd(__fd);
+
+       if (serial_write_usleep<0)
+               return(write(__fd, __buf, __n));
+
+       while (__n>0) {
+               got=write(__fd, __buf, 1);
+               if (got<=0)
+                       return((!r ? -1 : r));
+               __buf++;
+               __n--;
+               r++;
+               if (serial_write_usleep)
+                       usleep(serial_write_usleep);
+               }
+       return(r);
 }
 
 #endif  /* WIN32 */
index 0eacb4a..5e827ee 100644 (file)
@@ -17,8 +17,8 @@
   and 6110.
 
   $Log$
-  Revision 1.1.1.1  2001/11/25 21:59:03  short
-  :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
+  Revision 1.1.1.2  2002/04/03 00:07:54  short
+  Found in "gnokii-working" directory, some November-patches version
 
   Revision 1.136  2001/08/20 23:27:37  pkot
   Add hardware shakehand to the link layer (Manfred Jonsson)
@@ -206,7 +206,6 @@ GSM_Functions FB61_Functions = {
        FB61_GetBitmap,
        FB61_SetBitmap,
        FB61_SetRingTone,
-       FB61_SendRingTone,
        FB61_Reset,
        FB61_GetProfile,
        FB61_SetProfile,
@@ -240,31 +239,6 @@ GSM_Information FB61_Information = {
        14, 72                 /* Caller logo size */
 };
 
-unsigned char GSM_Default_Alphabet[] = {
-
-       /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */
-       /* Characters in hex position 10, [12 to 1a] and 24 are not present on
-          latin1 charset, so we cannot reproduce on the screen, however they are
-          greek symbol not present even on my Nokia */
-
-       '@',  0xa3, '$',  0xa5, 0xe8, 0xe9, 0xf9, 0xec, 
-       0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
-       '?',  '_',  '?',  '?',  '?',  '?',  '?',  '?',
-       '?',  '?',  '?',  '?',  0xc6, 0xe6, 0xdf, 0xc9,
-       ' ',  '!',  '\"', '#',  0xa4,  '%',  '&',  '\'',
-       '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
-       '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
-       '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
-       0xa1, 'A',  'B',  'C',  'D',  'E',  'F',  'G',
-       'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
-       'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
-       'X',  'Y',  'Z',  0xc4, 0xd6, 0xd1, 0xdc, 0xa7,
-       0xbf, 'a',  'b',  'c',  'd',  'e',  'f',  'g',
-       'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
-       'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
-       'x',  'y',  'z',  0xe4, 0xf6, 0xf1, 0xfc, 0xe0
-};
-
 const char *FB61_MemoryType_String [] = {
        "",     /* 0x00 */
        "MT", /* 0x01 */
@@ -331,7 +305,6 @@ static GSM_Error          CurrentSpeedDialError;
 
 static GSM_SMSMessage     *CurrentSMSMessage;
 static GSM_Error          CurrentSMSMessageError;
-static int                CurrentSMSPointer;
 
 static GSM_MemoryStatus   *CurrentMemoryStatus;
 static GSM_Error          CurrentMemoryStatusError;
@@ -1052,93 +1025,6 @@ void FB61_Terminate(void)
 #endif
 }
 
-#define ByteMask ((1 << Bits) - 1)
-
-int UnpackEightBitsToSeven(int offset, int in_length, int out_length,
-                           unsigned char *input, unsigned char *output)
-{
-        unsigned char *OUT = output; /* Current pointer to the output buffer */
-        unsigned char *IN  = input;  /* Current pointer to the input buffer */
-        unsigned char Rest = 0x00;
-        int Bits;
-
-        Bits = offset ? offset : 7;
-
-        while ((IN - input) < in_length) {
-
-                *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
-                Rest = *IN >> Bits;
-
-                /* If we don't start from 0th bit, we shouldn't go to the
-                   next char. Under *OUT we have now 0 and under Rest -
-                   _first_ part of the char. */
-                if ((IN != input) || (Bits == 7)) OUT++;
-                IN++;
-
-                if ((OUT - output) >= out_length) break;
-
-                /* After reading 7 octets we have read 7 full characters but
-                   we have 7 bits as well. This is the next character */
-                if (Bits == 1) {
-                        *OUT = Rest;
-                        OUT++;
-                        Bits = 7;
-                        Rest = 0x00;
-                } else {
-                        Bits--;
-                }
-        }
-
-        return OUT - output;
-}
-
-unsigned char GetAlphabetValue(unsigned char value)
-{
-        unsigned char i;
-  
-        if (value == '?') return 0x3f;
-  
-        for (i = 0 ; i < 128 ; i++)
-                if (GSM_Default_Alphabet[i] == value)
-                        return i;
-
-        return 0x3f; /* '?' */
-}
-
-int PackSevenBitsToEight(int offset, unsigned char *input, unsigned char *output)
-{
-        unsigned char *OUT = output; /* Current pointer to the output buffer */
-        unsigned char *IN  = input;  /* Current pointer to the input buffer */
-        int Bits;                    /* Number of bits directly copied to
-                                        the output buffer */
-        Bits = (7 + offset) % 8;
-
-        /* If we don't begin with 0th bit, we will write only a part of the
-           first octet */
-        if (offset) {
-                *OUT = 0x00;
-                OUT++;
-        }
-
-        while ((IN - input) < strlen(input)) {
-                unsigned char Byte = GetAlphabetValue(*IN);
-
-                *OUT = Byte >> (7 - Bits);
-                /* If we don't write at 0th bit of the octet, we should write
-                   a second part of the previous octet */
-                if (Bits != 7)
-                        *(OUT-1) |= (Byte & ((1 << (7-Bits)) - 1)) << (Bits+1);
-
-                Bits--;
-
-                if (Bits == -1) Bits = 7;
-                else OUT++;
-
-                IN++;
-        }
-        return (OUT - output);
-}
-
 GSM_Error FB61_GetRFLevel(GSM_RFUnits *units, float *level)
 {
        /* FIXME - these values are from 3810 code, may be incorrect.  Map from
@@ -1621,55 +1507,9 @@ GSM_Error FB61_DeleteSMSMessage(GSM_SMSMessage *message)
        return wait_on(&CurrentSMSMessageError, 50);
 }
 
-/* This function implements packing of numbers (SMS Center number and
-   destination number) for SMS sending function. */
-
-int SemiOctetPack(char *Number, unsigned char *Output)
-{
-       unsigned char *IN = Number;  /* Pointer to the input number */
-       unsigned char *OUT = Output; /* Pointer to the output */
-       int count = 0; /* This variable is used to notify us about count of already
-                         packed numbers. */
-
-       /* The first byte in the Semi-octet representation of the address field is
-          the Type-of-Address. This field is described in the official GSM
-          specification 03.40 version 5.3.0, section 9.1.2.5, page 33. We support
-          only international and unknown number. */
-  
-       if (*IN == '+') {
-               *OUT++ = GNT_INTERNATIONAL; /* International number */
-               IN++;
-       } else *OUT++ = GNT_UNKNOWN; /* Unknown number */
-
-       /* The next field is the number. It is in semi-octet representation - see
-          GSM scpecification 03.40 version 5.3.0, section 9.1.2.3, page 31. */
-
-       while (*IN) {
-               if (count & 0x01) {
-                       *OUT = *OUT | ((*IN - '0') << 4);
-                       OUT++;
-               }
-               else
-                       *OUT = *IN - '0';
-               count++; IN++;
-       }
-
-       /* We should also fill in the most significant bits of the last byte with
-          0x0f (1111 binary) if the number is represented with odd number of
-          digits. */
-
-       if (count & 0x01) {
-               *OUT=*OUT | 0xf0;
-               OUT++;
-               return (2 * (OUT - Output - 1) - 1);
-       }
-
-       return (2 * (OUT - Output - 1));
-}
-
 /* The second argument is the size of the data in octets,
    excluding User Data Header - important only for 8bit data */
-GSM_Error FB61_SendSMSMessage(GSM_SMSMessage *SMS, int data_size)
+GSM_Error FB61_SendSMSMessage(GSM_SMSMessage *SMS)
 {
        GSM_Error error;
 
@@ -1710,7 +1550,7 @@ GSM_Error FB61_SendSMSMessage(GSM_SMSMessage *SMS, int data_size)
        }
        dprintf(_("Sending SMS to %s via message center %s\n"), SMS->Destination, SMS->MessageCenter.Number);
 
-       if (SMS->UDHType) {
+       if (SMS->UDHPresent) {
                /* offset - length of the user data header */
                offset = 1 + SMS->UDH[0];
                /* we copy the udh and set the mask for the indicator */
@@ -1724,25 +1564,25 @@ GSM_Error FB61_SendSMSMessage(GSM_SMSMessage *SMS, int data_size)
        }
 
        /* size is the length of the data in octets including udh */
-       /* SMS->Length is:
+       /* req[22] is:
           - integer representation of the number od octets within the user data when UD is coded using 8bit data
           - the sum of the number of septets in UDH including any padding and number of septets in UD in other case
        */
+
+       size = offset;
   
        /* offset now denotes UDH length */
        if (SMS->EightBit) {
-               memcpy(req + 42 + offset, SMS->MessageText, data_size);
-               SMS->Length = size = data_size + offset;
+               memcpy(req + 42 + offset, SMS->MessageText, SMS->MessageTextLength);
                /* the mask for the 8-bit data */
                req[21] |= 0xf4;
+               size += SMS->MessageTextLength;
+               req[22] = size;
        } else {
-               size = PackSevenBitsToEight((7-offset)%7, SMS->MessageText, req + 42 + offset);
-               size += offset;
-               SMS->Length = (offset*8 + ((7-offset)%7)) / 7 + strlen(SMS->MessageText);
+               size += PackSevenBitsToEight((7-offset)%7, SMS->MessageText, req + 42 + offset);
+               req[22] = (offset*8 +6/*round-up*/)/7 +strlen(SMS->MessageText)*7;
        }
 
-       req[22] = SMS->Length;
-
        CurrentSMSMessageError=GE_BUSY;
 
        req[6] = SemiOctetPack(SMS->MessageCenter.Number, req+7);
@@ -1770,34 +1610,13 @@ GSM_Error FB61_SendSMSMessage(GSM_SMSMessage *SMS, int data_size)
        req[23] = SemiOctetPack(SMS->Destination, req+24);
 
        /* TP-Validity Period handling */
-
-       /* FIXME: error-checking for correct Validity - it should not be bigger then
-          63 weeks and smaller then 5minutes. We should also test intervals because
-          the SMS->Validity to TP-VP is not continuos. I think that the simplest
-          solution will be an array of correct values. We should parse it and if we
-          find the closest TP-VP value we should use it. Or is it good to take
-          closest smaller TP-VP as we do now? I think it is :-) */
-
-       /* 5 minutes intervals up to 12 hours = 720 minutes */
-       if (SMS->Validity <= 720)
-               req[35] = (unsigned char) (SMS->Validity/5)-1;
-
-       /* 30 minutes intervals up to 1 day */
-       else if ((SMS->Validity > 720) && (SMS->Validity <= 1440))
-               req[35] = (unsigned char) ((SMS->Validity-720)/30)+143;
-
-       /* 1 day intervals up to 30 days */
-       else if ((SMS->Validity > 1440) && (SMS->Validity <= 43200))
-               req[35] = (unsigned char) (SMS->Validity/1440)+166;
-
-       /* 1 week intervals up to 63 weeks */
-       else if ((SMS->Validity > 43200) && (SMS->Validity <= 635040))
-               req[35] = (unsigned char) (SMS->Validity/10080)+192;
+       req[35] = SMS_Validity_to_VP(SMS->Validity);
 
        FB61_TX_SendMessage(42+size, 0x02, req);
        return wait_on(&CurrentSMSMessageError, 70);
 }
 
+/* FIXME: UDH & EightBit not implemented yet */
 GSM_Error FB61_SaveSMSMessage(GSM_SMSMessage *SMS)
 {
        unsigned char req[256] = {
@@ -1840,7 +1659,7 @@ GSM_Error FB61_SaveSMSMessage(GSM_SMSMessage *SMS)
                return(GE_SMSTOOLONG);
 
        /* size is the length of the data in octets including udh */
-       /* SMS->Length is:
+       /* req[24] is:
           - integer representation of the number od octets within the user data when UD is coded using 8bit data
           - the sum of the number of septets in UDH including any padding and number of septets in UD in other case
        */
@@ -1848,9 +1667,9 @@ GSM_Error FB61_SaveSMSMessage(GSM_SMSMessage *SMS)
        /* offset now denotes UDH length */
        size = PackSevenBitsToEight((7-offset)%7, SMS->MessageText, req + 44 + offset);
        size += offset;
-       SMS->Length = (offset*8 + ((7-offset)%7)) / 7 + strlen(SMS->MessageText);
 
-       req[24] = SMS->Length;
+       req[24] = (offset*8 + ((7-offset)%7)) / 7 + strlen(SMS->MessageText);
+
        FB61_TX_SendMessage(44+size, 0x14, req);
        return wait_on(&CurrentSMSMessageError, 70);
 }
@@ -2066,56 +1885,6 @@ GSM_Error FB61_SetRingTone(GSM_Ringtone *ringtone)
        return (GE_NONE);
 }
 
-GSM_Error FB61_SendRingTone(GSM_Ringtone *ringtone, char *dest)
-{
-       GSM_SMSMessage SMS;
-       GSM_Error error;
-
-       int size = GSM_MAX_RINGTONE_PACKAGE_LENGTH;
-       char Package[GSM_MAX_RINGTONE_PACKAGE_LENGTH];
-       char udh[] = {
-               0x06,       /* User Data Header Length */
-               0x05,       /* IEI */
-               0x04,       /* IEDL */
-               0x15, 0x81, /* Destination port */
-               0x15, 0x81  /* Originator port, only
-                              to fill in the two
-                              bytes :-) */
-       };
-
-       /* Default settings for SMS message:
-          - no delivery report
-          - Class Message 1
-          - no compression
-          - 8 bit data
-          - SMSC no. 1
-          - validity 3 days
-          - set UserDataHeaderIndicator
-       */
-
-       SMS.Type = GST_MO;
-       SMS.Class = 1;
-       SMS.Compression = false;
-       SMS.EightBit = true;
-       SMS.MessageCenter.No = 1;
-       SMS.Validity = 4320; /* 4320 minutes == 72 hours */
-
-       SMS.UDHType = GSM_RingtoneUDH;
-
-       strcpy(SMS.Destination, dest);
-       GSM_PackRingtone(ringtone, Package, &size);
-       memcpy(SMS.UDH, udh, 7);
-       memcpy(SMS.MessageText, Package, size);
-
-       /* Send the message. */
-       error = FB61_SendSMSMessage(&SMS, size);
-
-       if (error == GE_SMSSENDOK) dprintf(_("Send succeeded!\n"));
-       else dprintf(_("SMS Send failed (error=%d)\n"), error);
-
-       return (GE_NONE);
-}
-
 GSM_Error FB61_Reset(unsigned char type)
 {
        unsigned char req[4] = { 0x00,0x01,0x64,0x03 };
@@ -2230,28 +1999,6 @@ void FB61_SigHandler(int status)
 }
 #endif /* WIN32 */
 
-char *FB61_GetBCDNumber(u8 *Number) 
-{
-       static char Buffer[20] = "";
-
-       /* This is the length of BCD coded number */
-       int length = Number[0];
-       int count;
-
-       if (Number[1] == 0x91) sprintf(Buffer, "+");
-       else Buffer[0] = '\0';
-
-       for (count = 0; count < length-1; count++) {
-               int Digit;
-               Digit = Number[count+2] & 0x0f;
-               if (Digit < 10) sprintf(Buffer, "%s%d", Buffer, Digit);
-               Digit = Number[count+2] >> 4;
-               if (Digit < 10) sprintf(Buffer, "%s%d", Buffer, Digit);
-       }
-
-       return Buffer;
-}
-
 char *FB61_GetPackedDateTime(u8 *Number)
 {
        static char Buffer[20] = "";
@@ -2414,11 +2161,11 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
 
                case 0x10:
                        dprintf(_("Message: SMS Message Received\n"));
-                       dprintf(_("   SMS center number: %s\n"), FB61_GetBCDNumber(MessageBuffer+7));
+                       dprintf(_("   SMS center number: %s\n"), SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer+7));
 
                        MessageBuffer[23] = (MessageBuffer[23]+1)/2+1;
 
-                       dprintf(_("   Remote number: %s\n"), FB61_GetBCDNumber(MessageBuffer+23));
+                       dprintf(_("   Remote number: %s\n"), SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer+23));
                        dprintf(_("   Date: %s\n"), FB61_GetPackedDateTime(MessageBuffer+35));
                        dprintf(_("   SMS: "));
 
@@ -2462,7 +2209,7 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                        CurrentMessageCenter->Format = MessageBuffer[6];
                        CurrentMessageCenter->Validity = MessageBuffer[8];
                        sprintf(CurrentMessageCenter->Name, "%s", MessageBuffer+33);
-                       sprintf(CurrentMessageCenter->Number, "%s", FB61_GetBCDNumber(MessageBuffer+21));
+                       sprintf(CurrentMessageCenter->Number, "%s", SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer+21));
 
                        dprintf(_("Message: SMS Center received:\n"));
                        dprintf(_("   %d. SMS Center name is %s\n"), CurrentMessageCenter->No, CurrentMessageCenter->Name);
@@ -3281,35 +3028,7 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                                CurrentSMSMessage->Status = GSS_SENTREAD;
 
                        off = 0;
-                       if (MessageBuffer[20] & 0x40) {
-                               switch (MessageBuffer[40+offset]) {
-                               case 0x00: /* concatenated messages */
-                                       dprintf(_("Concatenated message!!!\n"));
-                                       CurrentSMSMessage->UDHType = GSM_ConcatenatedMessages;
-                                       if (MessageBuffer[41+offset] != 0x03) {
-                               /* should be some error */
-                                       }
-                                       break;
-                               case 0x05: /* logos */
-                                       switch (MessageBuffer[43+offset]) {
-                                       case 0x82:
-                                               CurrentSMSMessage->UDHType = GSM_OpLogo;
-                                               break;
-                                       case 0x83:
-                                               CurrentSMSMessage->UDHType = GSM_CallerIDLogo;
-                                               break;
-                                       }
-                                       break;
-                               default:
-                                       break;
-                               }
-                               /* Skip user data header when reading data */
-                               off = (MessageBuffer[39+offset] + 1);
-                               for (i = 0; i < off; i++)
-                                       CurrentSMSMessage->UDH[i] = MessageBuffer[39+offset+i];
-                       } else {
-                               CurrentSMSMessage->UDHType = GSM_NoUDH;
-                       }
+                       CurrentSMSMessage->UDHPresent=!!(MessageBuffer[20] & 0x40);
       
                        MessageBuffer[20+offset] = (MessageBuffer[20+offset]+1)/2+1;
 
@@ -3359,8 +3078,8 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                                        }
                                        dprintf("\n");
                                }
-                               dprintf(_("   SMS center number: %s\n"), FB61_GetBCDNumber(MessageBuffer + 8));
-                               dprintf(_("   Remote number: %s\n"), FB61_GetBCDNumber(MessageBuffer + 20 + offset));
+                               dprintf(_("   SMS center number: %s\n"), SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer + 8));
+                               dprintf(_("   Remote number: %s\n"), SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer + 20 + offset));
                                break;
                        }
 
@@ -3380,28 +3099,29 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                                CurrentSMSMessage->Time.Timezone = (10 * (MessageBuffer[38+offset] & 0x07) + (MessageBuffer[38+offset] >> 4)) / 4;
                                if (MessageBuffer[38+offset] & 0x08)
                                        CurrentSMSMessage->Time.Timezone = -CurrentSMSMessage->Time.Timezone;
-                               strcpy(CurrentSMSMessage->Sender, FB61_GetBCDNumber(MessageBuffer + 20 + offset));
-                               strcpy(CurrentSMSMessage->MessageCenter.Number, FB61_GetBCDNumber(MessageBuffer + 8));
+                               strcpy(CurrentSMSMessage->Sender, SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer + 20 + offset));
+                               strcpy(CurrentSMSMessage->MessageCenter.Number, SEMIOCTETUNPACK_STATIC_BYTES(MessageBuffer + 8));
                        }
 
                        if (CurrentSMSMessage->Type != GST_DR) {
                                /* 8bit SMS */
                                if ((MessageBuffer[18+offset] & 0xf4) == 0xf4) {
                                        CurrentSMSMessage->EightBit = true;
-                                       tmp=CurrentSMSMessage->Length=MessageBuffer[19+offset];
+                                       tmp=MessageBuffer[19+offset];
                                        offset += off;
                                        memcpy(output, MessageBuffer - 39 - offset - 2, tmp - offset);
                                        /* 7bit SMS */
                                } else {
                                        CurrentSMSMessage->EightBit = false;
-                                       CurrentSMSMessage->Length=MessageBuffer[19+offset] - (off * 8 + ((7-off)%7)) / 7;
+                                       tmp=MessageBuffer[19+offset] - (off * 8 + ((7-off)%7)) / 7;
                                        offset += off;
-                                       tmp=UnpackEightBitsToSeven((7-off)%7, MessageLength - 39 - offset - 2, CurrentSMSMessage->Length, MessageBuffer + 39 + offset, output);
+                                       tmp=UnpackEightBitsToSeven((7-off)%7, MessageLength - 39 - offset - 2, tmp, MessageBuffer + 39 + offset, output);
                                }
                                for (i = 0; i < tmp;i++) {
                                        dprintf("%c", GSM_Default_Alphabet[output[i]]);
                                        CurrentSMSMessage->MessageText[i] = GSM_Default_Alphabet[output[i]];
                                }
+                               CurrentSMSMessage->MessageTextLength = tmp;
                        } else { /* CurrentSMSMessage->Type == GST_DR (Delivery Report) */
                                /* SMSC Response time */
                                CurrentSMSMessage->SMSCTime.Year = 10 * (MessageBuffer[39+offset] & 0x0f) + (MessageBuffer[39+offset] >> 4);
@@ -3425,7 +3145,6 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                                                dprintf(_("SM replaced by the SC"));
                                                break;
                                        }
-                                       CurrentSMSMessage->Length = tmp = 10;
                                } else if (MessageBuffer[22] & 0x40) {
 
                                        strcpy(CurrentSMSMessage->MessageText, _("Failed"));
@@ -3496,7 +3215,6 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                                                        break;
                                                }
                                        }
-                                       CurrentSMSMessage->Length = tmp = 6;
                                } else if (MessageBuffer[22] & 0x20) {
                                        strcpy(CurrentSMSMessage->MessageText, _("Pending"));
 
@@ -3525,20 +3243,17 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                                                dprintf(_("Reserved/Specific to SC: %x"), MessageBuffer[22]);
                                                break;
                                        }
-                                       CurrentSMSMessage->Length = tmp = 7;
                                } else {
                                        strcpy(CurrentSMSMessage->MessageText, _("Unknown"));
 
                                        /* more detailed reason only for debug */
                                        dprintf(_("Reserved/Specific to SC: %x"), MessageBuffer[22]);
-                                       CurrentSMSMessage->Length = tmp = 8;
                                }
+                               CurrentSMSMessage->MessageTextLength = strlen(CurrentSMSMessage->MessageText);
                        }
 
-                       CurrentSMSMessage->MessageText[CurrentSMSMessage->Length] = 0;
+                       CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength] = 0;
 
-                       CurrentSMSPointer = tmp;
-      
                        CurrentSMSMessage->MemoryType = MessageBuffer[5];
                        CurrentSMSMessage->MessageNumber = MessageBuffer[6];
  
@@ -3596,7 +3311,8 @@ enum FB61_RX_States FB61_RX_DispatchMessage(void)
                        dprintf(_("   The number of messages: %d\n"), MessageBuffer[10]);
                        dprintf(_("   Unread messages: %d\n"), MessageBuffer[11]);
                        CurrentSMSStatus->UnRead = MessageBuffer[11];
-                       CurrentSMSStatus->Number = MessageBuffer[10];
+                       CurrentSMSStatus->Used   = MessageBuffer[10];
+                       CurrentSMSStatus->Slots  = CurrentSMSStatus->Used; /* is it correct? */
                        CurrentSMSStatusError = GE_NONE;
                        break;
 
index 3c090a0..5f86eff 100644 (file)
@@ -1,9 +1,13 @@
 /*
 
+  $Id$
+  
   G N O K I I
 
   A Linux/Unix toolset and driver for Nokia mobile phones.
 
+  Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
+
   Released under the terms of the GNU GPL, see file COPYING for more details.
        
   Provides a generic API for accessing functions on the phone, wherever
   Unless otherwise noted, all functions herein block until they complete.  The
   functions themselves are defined in a structure in gsm-common.h.
 
+  $Log$
+  Revision 1.1.1.5  2002/04/03 00:07:54  short
+  Found in "gnokii-working" directory, some November-patches version
+
+  Revision 1.31  2001/11/08 16:45:58  pkot
+  Obsolete old structure, kill treads where possible and make shared library
+
+  Revision 1.30  2001/08/09 11:51:38  pkot
+  Generic AT support updates and cleanup (Manfred Jonsson)
+
+  Revision 1.29  2001/07/27 00:02:20  pkot
+  Generic AT support for the new structure (Manfred Jonsson)
+
+  Revision 1.28  2001/06/06 09:05:56  machek
+  Convert Grab/Release display to new structure.
+
+  Revision 1.27  2001/05/07 14:13:06  machek
+  nokia-2110 module converted to suit new API better. --identify now works.
+
+  Revision 1.26  2001/04/25 12:54:47  machek
+  Partly converted nokia 2110 to "new" form, and moved it to phone
+  directory.
+
+  Revision 1.25  2001/03/26 23:39:21  pkot
+  Minor updates:
+   - Windows INLINE patch (Manfred Jonsson)
+   - patch to configure.in to compile under FreeBSD (Panagiotis Astithas)
+   - other cleanups (me)
+
+  
 */
 
 #include <stdio.h>
 #include <string.h>
-#include <stdlib.h>
-#include <time.h>
 
-#ifdef WIN32
-  #include <windows.h>
-  #include "misc_win32.h"
-#endif
+#include "misc.h"
+#include "gsm-common.h"
+#include "data/rlp-common.h"
+#include "gsm-statemachine.h"
+#include "phones/nk7110.h"
+#include "phones/nk6100.h"
+#include "phones/nk3110.h"
+#include "phones/nk2110.h"
 
-#include "gsm-api.h"
-
-#include "newmodules/n3110.h"
-#include "newmodules/n6110.h"
-#include "newmodules/n7110.h"
-#include "newmodules/newat.h"
-#ifdef DEBUG
-  #include "newmodules/sniff/sniff.h"
-#endif
-#include "protocol/fbusirda.h"
-#include "protocol/fbus.h"
-#include "protocol/fbus3110.h"
-#include "protocol/mbus.h"
-#include "protocol/at.h"
-#include "files/cfgreader.h"
-
-#ifndef WIN32
-  #include "devices/device.h"
-#endif
-
-#ifdef VC6
-  /* for VC6 make scripts save VERSION constant in mversion.h file */
-  #include "mversion.h"
-#endif
 
 /* GSM_LinkOK is set to true once normal communications with the phone have
    been established. */
 
 bool *GSM_LinkOK;
+bool LinkAlwaysOK = true;
 
 /* Define pointer to the GSM_Functions structure used by external code to call
    relevant API functions. This structure is defined in gsm-common.h. */
@@ -69,773 +82,622 @@ GSM_Functions *GSM;
 
 GSM_Information                *GSM_Info;
 
-/* Initialise interface to the phone. Model number should be a string such as
-   3810, 5110, 6110 etc. Device is the serial port to use e.g. /dev/ttyS0, the
-   user must have write permission to the device. */
 
-GSM_Protocol *Protocol;
 
-/* Local variables used by get/set phonebook entry code. Buffer is used as a
-   source or destination for phonebook data and other functions... Error is
-   set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
-   handler routines as appropriate. */
-                                  
-GSM_PhonebookEntry *CurrentPhonebookEntry;
-GSM_Error          CurrentPhonebookError;
+/* From here starts the ugly compatibility cludge to complete WIPE OUT the
+ * insane "Sorry, phone has not yet been converted to new style.
+ * Phone.Functions == NULL!" message out of the Earth surface to make the world
+ * a happy place back again! Wiping out, wiping out, wiping out!
+ * The most World's evil GSM_Error code: GE_NOTIMPLEMENTED
+ * Completely non-systematic naming is not my souvenir - it is just copied
+ * from the existing include files. (Jan Kratochvil)
+ */
+
+static GSM_Statemachine *compat_Functions_Statemachine;
+
+#define INVOKE_COMPAT_FUNCTIONS_SM(gop) \
+       (compat_Functions_Statemachine->Phone.Functions((gop),&data,compat_Functions_Statemachine))
+#define RETURN_COMPAT_FUNCTIONS_SM(gop) \
+       return((INVOKE_COMPAT_FUNCTIONS_SM((gop))))
+#define BODY_COMPAT_FUNCTIONS_SM_CORE(core,gop) \
+       GSM_Data data; \
+       core \
+       RETURN_COMPAT_FUNCTIONS_SM((gop));
+#define BODY_COMPAT_FUNCTIONS_SM0(gop) \
+       BODY_COMPAT_FUNCTIONS_SM_CORE(  ,(gop))
+#define BODY_COMPAT_FUNCTIONS_SM1(field1,arg1,gop) \
+       BODY_COMPAT_FUNCTIONS_SM_CORE( data.field1=(arg1); ,(gop))
+#define BODY_COMPAT_FUNCTIONS_SM2(field1,arg1,field2,arg2,gop) \
+       BODY_COMPAT_FUNCTIONS_SM_CORE( data.field1=(arg1);data.field2=(arg2); ,(gop))
+#define BODY_COMPAT_FUNCTIONS_SM3(field1,arg1,field2,arg2,field3,arg3,gop) \
+       BODY_COMPAT_FUNCTIONS_SM_CORE( data.field1=(arg1);data.field2=(arg2);data.field3=(arg3); ,(gop))
+
+static GSM_Error compat_Functions_Initialise
+       (char *port_device, char *initlength, GSM_ConnectionType connection, void (*rlp_callback)(RLP_F96Frame *frame))
+{
+       fprintf(stderr,"FATAL: GSM->Initialise called for new style phone implementation!\n");
+       return(GE_NOTIMPLEMENTED);
+}
 
-GSM_SpeedDial      *CurrentSpeedDialEntry;
-GSM_Error          CurrentSpeedDialError;
+static void compat_Functions_Terminate(void)
+{
+GSM_Data data; /* void */
 
-unsigned char      Current_IMEI[GSM_MAX_IMEI_LENGTH];
-unsigned char      Current_Revision[GSM_MAX_REVISION_LENGTH];
-unsigned char      Current_Model[GSM_MAX_MODEL_LENGTH];
+       INVOKE_COMPAT_FUNCTIONS_SM(GOP_Terminate);
+}
 
-GSM_SMSMessage     *CurrentSMSMessage;
-GSM_Error          CurrentSMSMessageError;
-int                CurrentSMSPointer;
+static GSM_Error compat_Functions_GetMemoryLocation( GSM_PhonebookEntry *entry )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(PhonebookEntry,entry,GOP_ReadPhonebook);
+}
 
-GSM_SMSFolders      *CurrentSMSFolders;
-GSM_Error          CurrentSMSFoldersError;
-int                CurrentSMSFoldersCount;
+static GSM_Error compat_Functions_WritePhonebookLocation( GSM_PhonebookEntry *entry )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(PhonebookEntry,entry,GOP_WritePhonebook);
+}
 
-GSM_OneSMSFolder   CurrentSMSFolder;
-GSM_Error          CurrentSMSFolderError;
-int                CurrentSMSFolderID;
+static GSM_Error compat_Functions_GetSpeedDial( GSM_SpeedDial *entry )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SpeedDial,entry,GOP_GetSpeedDial);
+}
 
-GSM_MemoryStatus   *CurrentMemoryStatus;
-GSM_Error          CurrentMemoryStatusError;
+static GSM_Error compat_Functions_SetSpeedDial( GSM_SpeedDial *entry )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SpeedDial,entry,GOP_SetSpeedDial);
+}
 
-GSM_NetworkInfo    *CurrentNetworkInfo;
-GSM_Error          CurrentNetworkInfoError;
+static GSM_Error compat_Functions_GetMemoryStatus( GSM_MemoryStatus *Status )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(MemoryStatus,Status,GOP_GetMemoryStatus);
+}
 
-GSM_SMSStatus      *CurrentSMSStatus;
-GSM_Error          CurrentSMSStatusError;
+static GSM_Error compat_Functions_GetSMSStatus( GSM_SMSStatus *Status )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SMSStatus,Status,GOP_GetSMSStatus);
+}
 
-GSM_MessageCenter  *CurrentMessageCenter;
-GSM_Error          CurrentMessageCenterError;
+static GSM_Error compat_Functions_GetSMSCenter( GSM_MessageCenter *MessageCenter )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(MessageCenter,MessageCenter,GOP_GetSMSCenter);
+}
 
-int                *CurrentSecurityCodeStatus;
-GSM_Error          CurrentSecurityCodeError;
-GSM_SecurityCode   *CurrentSecurityCode;
+static GSM_Error compat_Functions_SetSMSCenter( GSM_MessageCenter *MessageCenter )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(MessageCenter,MessageCenter,GOP_SetSMSCenter);
+}
 
-GSM_DateTime       *CurrentDateTime;
-GSM_Error          CurrentDateTimeError;
+static GSM_Error compat_Functions_GetSMSMessage( GSM_SMSMessage *Message )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SMSMessage,Message,GOP_GetSMS);
+}
 
-GSM_DateTime       *CurrentAlarm;
-GSM_Error          CurrentAlarmError;
+static GSM_Error compat_Functions_DeleteSMSMessage( GSM_SMSMessage *Message )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SMSMessage,Message,GOP_DeleteSMS);
+}
 
-GSM_CalendarNote   *CurrentCalendarNote;
-GSM_Error          CurrentCalendarNoteError;
+static GSM_Error compat_Functions_SendSMSMessage( GSM_SMSMessage *Message )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SMSMessage,Message,GOP_SendSMS);
+}
 
-GSM_NotesInfo      CurrentCalendarNotesInfo,*CurrentCalendarNotesInfo2;
-GSM_Error          CurrentCalendarNotesInfoError;
+static GSM_Error compat_Functions_SaveSMSMessage( GSM_SMSMessage *Message )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SMSMessage,Message,GOP_SaveSMS);
+}
 
-GSM_Error          CurrentSetDateTimeError;
-GSM_Error          CurrentSetAlarmError;
+static GSM_Error compat_Functions_GetRFLevel( GSM_RFUnits *units, float *level )
+{
+       BODY_COMPAT_FUNCTIONS_SM2(RFUnits,units,RFLevel,level,GOP_GetRFLevel);
+}
 
-GSM_Error          CurrentEnableExtendedCommandsError;
+static GSM_Error compat_Functions_GetBatteryLevel( GSM_BatteryUnits *units, float *level )
+{
+       BODY_COMPAT_FUNCTIONS_SM2(BatteryUnits,units,BatteryLevel,level,GOP_GetBatteryLevel);
+}
 
-int                CurrentRFLevel,
-                   CurrentBatteryLevel,
-                   CurrentPowerSource;
+static GSM_Error compat_Functions_GetPowerSource( GSM_PowerSource *source )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(PowerSource,source,GOP_GetPowersource);
+}
 
-int                CurrentDisplayStatus;
-GSM_Error          CurrentDisplayStatusError;
+static GSM_Error compat_Functions_GetDisplayStatus( int *Status )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(DisplayStatus,Status,GOP_GetDisplayStatus);
+}
 
-GSM_Error          CurrentResetPhoneSettingsError;
+static GSM_Error compat_Functions_EnterSecurityCode( GSM_SecurityCode Code )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(SecurityCode,&Code,GOP_EnterSecurityCode);
+}
 
-char               *CurrentNetmonitor;
-GSM_Error          CurrentNetmonitorError;
+/* FIXME: GetSecurityCodeStatus() should have GSM_SecurityCodeType as its argument!
+ */
+static GSM_Error compat_Functions_GetSecurityCodeStatus( int *Status )
+{
+GSM_SecurityCodeType status_local;
+GSM_Data data;
+GSM_Error err;
+
+       data.SecurityCodeStatus=&status_local;
+       err=INVOKE_COMPAT_FUNCTIONS_SM(GOP_GetSecurityCodeStatus);
+       *Status=status_local;
+       return(err);
+}
 
-GSM_Bitmap         *CurrentGetBitmap=NULL;
-GSM_Error          CurrentGetBitmapError;
+static GSM_Error compat_Functions_GetIMEI( char *imei )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Imei,imei,GOP_GetImei);
+}
 
-GSM_Error          CurrentSetBitmapError;
+static GSM_Error compat_Functions_GetRevision( char *revision )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Revision,revision,GOP_GetRevision);
+}
 
-GSM_Error          CurrentSendDTMFError;
+static GSM_Error compat_Functions_GetModel( char *model )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Model,model,GOP_GetModel);
+}
 
-GSM_Profile        *CurrentProfile;
-GSM_Error          CurrentProfileError;
+static GSM_Error compat_Functions_GetManufacturer( char *manufacturer )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Manufacturer,manufacturer,GOP_GetManufacturer);
+}
 
-GSM_Error          CurrentDisplayOutputError;
+static GSM_Error compat_Functions_GetDateTime( GSM_DateTime *date_time)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(DateTime,date_time,GOP_GetDateTime);
+}
 
-GSM_CBMessage      *CurrentCBMessage;
-GSM_Error          CurrentCBError;
+static GSM_Error compat_Functions_SetDateTime( GSM_DateTime *date_time)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(DateTime,date_time,GOP_SetDateTime);
+}
 
-int                CurrentPressKeyEvent;
-GSM_Error          CurrentPressKeyError;
+static GSM_Error compat_Functions_GetAlarm( int alarm_number, GSM_DateTime *date_time )
+{
+       BODY_COMPAT_FUNCTIONS_SM2(AlarmNumber,&alarm_number,AlarmDateTime,date_time,GOP_GetAlarm);
+}
 
-GSM_Error          CurrentPlayToneError=GE_UNKNOWN;
+static GSM_Error compat_Functions_SetAlarm( int alarm_number, GSM_DateTime *date_time )
+{
+       BODY_COMPAT_FUNCTIONS_SM2(AlarmNumber,&alarm_number,AlarmDateTime,date_time,GOP_SetAlarm);
+}
+
+static GSM_Error compat_Functions_DialVoice( char *Number )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(VoiceNumber,Number,GOP_DialVoice);
+}
 
-GSM_Error          CurrentDialVoiceError;
+static GSM_Error compat_Functions_DialData( char *Number, char type, void (* callpassup)(char c) )
+{
+       BODY_COMPAT_FUNCTIONS_SM3(DataNumber,Number,DataType,&type,DataCallPassUp,callpassup,GOP_DialData);
+}
 
-GSM_Error          CurrentGetOperatorNameError;
-GSM_Network        *CurrentGetOperatorNameNetwork;
-GSM_Error          CurrentSetOperatorNameError;
+static GSM_Error compat_Functions_GetIncomingCallNr( char *Number )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(IncomingCallNr,Number,GOP_GetIncomingCallNr);
+}
 
-GSM_Error          CurrentGetIMEIError;
+static GSM_Error compat_Functions_GetNetworkInfo ( GSM_NetworkInfo *NetworkInfo )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(NetworkInfo,NetworkInfo,GOP_GetNetworkInfo);
+}
 
-GSM_Error          CurrentGetHWError;
+static GSM_Error compat_Functions_GetCalendarNote ( GSM_CalendarNote *CalendarNote)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(CalendarNote,CalendarNote,GOP_GetCalendarNote);
+}
 
-unsigned char      CurrentPPS[4];
-GSM_Error          CurrentProductProfileSettingsError;
+static GSM_Error compat_Functions_WriteCalendarNote ( GSM_CalendarNote *CalendarNote)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(CalendarNote,CalendarNote,GOP_WriteCalendarNote);
+}
 
-char               CurrentIncomingCall[20];
+static GSM_Error compat_Functions_DeleteCalendarNote ( GSM_CalendarNote *CalendarNote)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(CalendarNote,CalendarNote,GOP_DeleteCalendarNote);
+}
 
-GSM_Error          CurrentBinRingtoneError;
-GSM_BinRingtone    *CurrentGetBinRingtone=NULL;
+static GSM_Error compat_Functions_NetMonitor ( unsigned char mode, char *Screen )
+{
+       BODY_COMPAT_FUNCTIONS_SM2(NetMonitorMode,&mode,NetMonitorScreen,Screen,GOP_NetMonitor);
+}
 
-GSM_Error          CurrentRingtoneError;
+static GSM_Error compat_Functions_SendDTMF ( char *String )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(DTMF,String,GOP_SendDTMF);
+}
 
-GSM_Error          CurrentMagicError;
+static GSM_Error compat_Functions_GetBitmap ( GSM_Bitmap *Bitmap )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Bitmap,Bitmap,GOP_GetBitmap);
+}
+  
+static GSM_Error compat_Functions_SetBitmap ( GSM_Bitmap *Bitmap )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Bitmap,Bitmap,GOP_SetBitmap);
+}
 
-GSM_Error          CurrentSimlockInfoError;
-GSM_AllSimlocks    *CurrentSimLock;
+static GSM_Error compat_Functions_SetRingtone ( GSM_Ringtone *ringtone )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Ringtone,ringtone,GOP_SetRingtone);
+}
 
-GSM_Error          CurrentGetWAPBookmarkError;
-GSM_Error          CurrentSetWAPBookmarkError;
-GSM_WAPBookmark    *WAPBookmark;
+static GSM_Error compat_Functions_Reset ( unsigned char type )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(ResetType,&type,GOP_Reset);
+}
 
-GSM_Error          CurrentGetWAPSettingsError;
-GSM_WAPSettings    *WAPSettings;
+static GSM_Error compat_Functions_GetProfile ( GSM_Profile *Profile )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Profile,Profile,GOP_GetProfile);
+}
 
-GSM_Error          CurrentCallDivertError;
-GSM_CallDivert    *CurrentCallDivert;
+static GSM_Error compat_Functions_SetProfile ( GSM_Profile *Profile )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(Profile,Profile,GOP_SetProfile);
+}
 
-char              *CurrentManufacturer;
+/* return type "bool" is fortunately compatible with "GSM_Error"
+ */
+static bool      compat_Functions_SendRLPFrame ( RLP_F96Frame *frame, bool out_dtx )
+{
+       BODY_COMPAT_FUNCTIONS_SM2(RLPFrame,frame,RLPFrame_out_dtx,&out_dtx,GOP_SendRLPFrame);
+}
 
-/* This is the connection type used in gnokii. */
-GSM_ConnectionType CurrentConnectionType;
+static GSM_Error compat_Functions_CancelCall ( void )
+{
+       BODY_COMPAT_FUNCTIONS_SM0(GOP_CancelCall);
+}
+  
+static GSM_Error compat_Functions_EnableDisplayOutput ( void )
+{
+       BODY_COMPAT_FUNCTIONS_SM0(GOP_EnableDisplayOutput);
+}
+  
+static GSM_Error compat_Functions_DisableDisplayOutput ( void )
+{
+       BODY_COMPAT_FUNCTIONS_SM0(GOP_DisableDisplayOutput);
+}
+static GSM_Error compat_Functions_EnableCellBroadcast ( void )
+{
+       BODY_COMPAT_FUNCTIONS_SM0(GOP_EnableCellBroadcast);
+}
 
-/* Pointer to a callback function used to return changes to a calls status */
-/* This saves unreliable polling */
-void (*CurrentCallPassup)(char c);
+static GSM_Error compat_Functions_DisableCellBroadcast ( void )
+{
+       BODY_COMPAT_FUNCTIONS_SM0(GOP_DisableCellBroadcast);
+}
 
-/* Pointer to callback function in user code to be called when RLP frames
-   are received. */
-void (*CurrentRLP_RXCallback)(RLP_F96Frame *frame);
+static GSM_Error compat_Functions_ReadCellBroadcast ( GSM_CBMessage *Message )
+{
+       BODY_COMPAT_FUNCTIONS_SM1(CBMessage,Message,GOP_ReadCellBroadcast);
+}
+  
+static GSM_Error compat_Functions_SetKey (int c, int up)
+{
+       BODY_COMPAT_FUNCTIONS_SM2(SetKeyKey,&c,SetKeyUp,&up,GOP_SetKey);
+}
+  
+static GSM_Error compat_Functions_HandleString (char *s)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(HandleString,s,GOP_HandleString);
+}
+       
+static GSM_Error compat_Functions_AnswerCall (char s)
+{
+       BODY_COMPAT_FUNCTIONS_SM1(CallNo,&s,GOP_AnswerCall);
+}
 
-/* Used to disconnect the call */
-u8 CurrentCallSequenceNumber;
+static GSM_Functions compat_Functions={
+       compat_Functions_Initialise,
+       compat_Functions_Terminate,
+       compat_Functions_GetMemoryLocation,
+       compat_Functions_WritePhonebookLocation,
+       compat_Functions_GetSpeedDial,
+       compat_Functions_SetSpeedDial,
+       compat_Functions_GetMemoryStatus,
+       compat_Functions_GetSMSStatus,
+       compat_Functions_GetSMSCenter,
+       compat_Functions_SetSMSCenter,
+       compat_Functions_GetSMSMessage,
+       compat_Functions_DeleteSMSMessage,
+       compat_Functions_SendSMSMessage,
+       compat_Functions_SaveSMSMessage,
+       compat_Functions_GetRFLevel,
+       compat_Functions_GetBatteryLevel,
+       compat_Functions_GetPowerSource,
+       compat_Functions_GetDisplayStatus,
+       compat_Functions_EnterSecurityCode,
+       compat_Functions_GetSecurityCodeStatus,
+       compat_Functions_GetIMEI,
+       compat_Functions_GetRevision,
+       compat_Functions_GetModel,
+       compat_Functions_GetManufacturer,
+       compat_Functions_GetDateTime,
+       compat_Functions_SetDateTime,
+       compat_Functions_GetAlarm,
+       compat_Functions_SetAlarm,
+       compat_Functions_DialVoice,
+       compat_Functions_DialData,
+       compat_Functions_GetIncomingCallNr,
+       compat_Functions_GetNetworkInfo,
+       compat_Functions_GetCalendarNote,
+       compat_Functions_WriteCalendarNote,
+       compat_Functions_DeleteCalendarNote,
+       compat_Functions_NetMonitor,
+       compat_Functions_SendDTMF,
+       compat_Functions_GetBitmap,
+       compat_Functions_SetBitmap,
+       compat_Functions_SetRingtone,
+       compat_Functions_Reset,
+       compat_Functions_GetProfile,
+       compat_Functions_SetProfile,
+       compat_Functions_SendRLPFrame,
+       compat_Functions_CancelCall,
+       compat_Functions_EnableDisplayOutput,
+       compat_Functions_DisableDisplayOutput,
+       compat_Functions_EnableCellBroadcast,
+       compat_Functions_DisableCellBroadcast,
+       compat_Functions_ReadCellBroadcast,
+       compat_Functions_SetKey,
+       compat_Functions_HandleString,
+       compat_Functions_AnswerCall,
+       };
+
+GSM_Error compat_Phone_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
+{
+       switch (op) {
+       
+       case GOP_Init:
+               fprintf(stderr,"FATAL: compat_Phone_Functions(GOP_Init) called for old style phone implementation!\n");
+               return(GE_NOTIMPLEMENTED);
 
-bool CurrentLinkOK;
+       case GOP_Terminate:
+               GSM->Terminate();
+               return(GE_NONE);
 
-bool CurrentRequestTerminate;
+       case GOP_ReadPhonebook:
+               return(GSM->GetMemoryLocation(data->PhonebookEntry/*entry*/));
 
-bool CurrentDisableKeepAlive;
+       case GOP_WritePhonebook:
+               return(GSM->WritePhonebookLocation(data->PhonebookEntry/*entry*/));
 
-bool CheckModel (GSM_Information InfoToCheck, char *model, GSM_ConnectionType connection) {
+       case GOP_GetSpeedDial:
+               return(GSM->GetSpeedDial(data->SpeedDial/*entry*/));
 
-  bool found_match=false;
+       case GOP_SetSpeedDial:
+               return(GSM->SetSpeedDial(data->SpeedDial/*entry*/));
 
-  if (strstr(InfoToCheck.FBUSModels, model) != NULL) {
-    if (connection==GCT_FBUS) found_match=true;
-  }
-  if (strstr(InfoToCheck.MBUSModels, model) != NULL) {
-    if (connection==GCT_MBUS) found_match=true;
-  }
-  if (strstr(InfoToCheck.InfraredModels, model) != NULL) {
-    if (connection==GCT_Infrared) found_match=true;
-  }
-  if (strstr(InfoToCheck.DLR3Models, model) != NULL) {
-    if (connection==GCT_DLR3) found_match=true;
-  }
-  if (strstr(InfoToCheck.IrdaModels, model) != NULL) {
-    if (connection==GCT_Irda) found_match=true;
-  }
-  if (strstr(InfoToCheck.ATModels, model) != NULL) {
-    if (connection==GCT_AT) found_match=true;
-  }
-  if (strstr(InfoToCheck.TekramModels, model) != NULL) {
-    if (connection==GCT_Tekram) found_match=true;
-  }
-  if (strstr(InfoToCheck.FBUS3110Models, model) != NULL) {
-    if (connection==GCT_FBUS3110) found_match=true;
-  }
+       case GOP_GetMemoryStatus:
+               return(GSM->GetMemoryStatus(data->MemoryStatus/*Status*/));
 
-  return found_match;
-}
-GSM_Error TryNewNokia(char *model, char *device, char *initlength, GSM_ConnectionType connection, void (*rlp_callback)(RLP_F96Frame *frame)) {
-  int InitLength;
-  int count;
-  unsigned char init_char = N6110_SYNC_BYTE;
+       case GOP_GetSMSStatus:
+               return(GSM->GetSMSStatus(data->SMSStatus/*Status*/));
 
-  /* Hopefully is 64 larger as FB38_MAX* / N6110_MAX* */
-  char phonemodel[64];
+       case GOP_GetSMSCenter:
+               return(GSM->GetSMSCenter(data->MessageCenter/*MessageCenter*/));
 
-  if (Protocol->Initialise(device,initlength,connection,rlp_callback)!=GE_NONE)
-  {
-    return GE_NOTSUPPORTED; 
-  }
+       case GOP_SetSMSCenter:
+               return(GSM->SetSMSCenter(data->MessageCenter/*MessageCenter*/));
 
-  if (connection!=GCT_MBUS) {
-    InitLength = atoi(initlength);
+       case GOP_GetSMS:
+               return(GSM->GetSMSMessage(data->SMSMessage/*Message*/));
 
-    if ((strcmp(initlength, "default") == 0) || (InitLength == 0)) {
-      InitLength = 250;        /* This is the usual value, lower may work. */
-    }
+       case GOP_DeleteSMS:
+               return(GSM->DeleteSMSMessage(data->SMSMessage/*Message*/));
 
-#ifdef DEBUG
-    fprintf(stdout,_("Writing init chars...."));
-#endif
+       case GOP_SendSMS:
+               return(GSM->SendSMSMessage(data->SMSMessage/*Message*/));
 
-    /* Initialise link with phone or what have you */
-    /* Send init string to phone, this is a bunch of 0x55 characters. Timing is
-       empirical. */
-    for (count = 0; count < InitLength; count ++) {
-      usleep(100);
-      Protocol->WritePhone(1,&init_char);
-    }
+       case GOP_SaveSMS:
+               return(GSM->SaveSMSMessage(data->SMSMessage/*Message*/));
 
-#ifdef DEBUG
-    fprintf(stdout,_("Done\n"));  
-#endif
+       case GOP_GetRFLevel:
+               return(GSM->GetRFLevel(data->RFUnits/*units*/,data->RFLevel/*level*/));
 
-    N6110_SendStatusRequest();
-  }
+       case GOP_GetBatteryLevel:
+               return(GSM->GetBatteryLevel(data->BatteryUnits/*units*/,data->BatteryLevel/*level*/));
 
-  usleep(100);
+       case GOP_GetPowersource:
+               return(GSM->GetPowerSource(data->PowerSource/*source*/));
 
-  if (N6110_SendIDFrame()!=GE_NONE)
-    return GE_TIMEOUT;
-  while (N6110_GetModel(phonemodel) != GE_NONE)
-    sleep(1);
+       case GOP_GetDisplayStatus:
+               return(GSM->GetDisplayStatus(data->DisplayStatus/*Status*/));
 
-  if (!strcmp(phonemodel,"NPE-3") || !strcmp(phonemodel,"NSE-5") || 
-      !strcmp(phonemodel,"NHM-3"))
-  {
-    GSM->Terminate();      
-    
-    /* Set pointers to relevant addresses */
-    GSM = &N7110_Functions;
-    GSM_Info = &N7110_Information;
-    GSM_LinkOK = &CurrentLinkOK;
-    return GE_NONE;
-  }
+       case GOP_EnterSecurityCode:
+               return(GSM->EnterSecurityCode(*data->SecurityCode/*Code*/));
 
-  return GE_NONE;
-}
+       case GOP_GetSecurityCodeStatus: {
+int status_local=*data->SecurityCodeStatus;
+GSM_Error err;
 
-GSM_Error GSM_Initialise(char *model, char *device, char *initlength, GSM_ConnectionType connection, void (*rlp_callback)(RLP_F96Frame *frame), char* SynchronizeTime)
-{
-  bool found_match=false;
-  
-  GSM_ConnectionType connection2;
+               err=GSM->GetSecurityCodeStatus(&status_local);
+               *data->SecurityCodeStatus=status_local;
+               return(err);
+               }
 
-  struct tm *now;
-  time_t nowh;
-  GSM_DateTime Date;
-  GSM_Error error;
-  
-  connection2=connection;
-
-  CurrentRLP_RXCallback = rlp_callback;
-  CurrentCallPassup=NULL;
-  CurrentCallDivert=NULL;
-  CurrentPhonebookEntry=NULL;
-  CurrentNetworkInfo = NULL;
-  CurrentGetBitmap=NULL;
-  CurrentPlayToneError=GE_UNKNOWN;
-  strcpy(CurrentIncomingCall," ");
-  CurrentGetBinRingtone=NULL;
-  CurrentNetworkInfo=NULL;
-  CurrentRequestTerminate=false;
-  CurrentDisableKeepAlive=false;
-  CurrentCalendarNotesInfo.HowMany=2000;
-  CurrentSMSMessage=NULL;
-  CurrentMagicError = GE_BUSY;  
-  
-  if (!strcmp(model,"auto")) {
-
-    /* For now */
-    GSM = &N6110_Functions;
-    GSM_Info = &N6110_Information;
-    GSM_LinkOK = &CurrentLinkOK;
-#ifdef DEBUG
-    fprintf(stdout,_("Trying FBUS for new Nokia phones...\n"));
-#endif
-    /* Trying FBUS */
-    Protocol = &FB